Merge branch 'flat_sprites' into 'master'

Flat sprites and collision bounds, controllable sprite ordering, and allowing sprite flipping without gravity flipping!

Okay, so I'm silly. Red showed off some ACZ2 clips of the minecart and I was like "FUCK, THAT LOOKS TERRIBL- oh wait i drew that". But it wasn't the sprites that were bad, no - it was the fact that the sprites occupied a single position in 3d space and had pre-built-in-perspective, and THAT was terrible. So I basically over-engineered a solution instead.

FF_PAPERSPRITE (name voted on by the public! http://i.imgur.com/i6g5P73.png) has:
* Accurate perspective as if it were a linedef midtexture IN-MAP!!!!!!!!!!!!!!!!!!!!! might need a little bit of tweaking though, @RedEnchilada cough meow
* As good ordering as you can get without seperating the sprite down the middle whenever it intersects something
* Pretty dang accurate clipping - only exception is http://i.imgur.com/QNjbATB.png - where the MF_PAPER mobj has moved close to the wall and then turned, and you can't really solve that without cutting Sonic's feet off like in OGL <-- might be inaccurate, need to check with fixed ordering
* I suck at understanding the casting to INT64 stuff that's used for overflow prevention elsewhere in the code, so I just cleaned up the most obvious and predictable overflow glitches manually. Still needs tweaking, though.
* GFY one - the player rotating on the spot (recorded with the player having MF_PAPER): http://gfycat.com/WhichSpectacularDesertpupfish
* GFY two - a ring box stays static as the player turns (recorded with the MF_PAPER check turned off): http://gfycat.com/SimpleShallowDeviltasmanian
* GFY three - THE ACZ2 MINECART! What I MADE this for... http://gfycat.com/EsteemedPleasedDuck

Also, MF_PAPERCOLLISION!
* Collision bounds are limited to the surface of the MF_PAPERCOLLISION. These objects can kinda get stuck inside solid stuff when they turn, though - I tried to do some rudimentary pushing but it's glitchy as hell so it's put behind a #define for now, since stuck is generally better than overt glitching and can at least be user-mitigated.
* To make space for this, MF_AMBUSH is now MF2_AMBUSH. Because it's more like an MF2 in all its uses, honestly. Literally no object definitions in info.c use that flag, and it's always applied at runtime - like all the other MF2s! Heck, there's some precedent - the mapthing flag MTF_OBJECTFLIP applies MF2_OBJECTFLIP. It just makes sense, and prevents wastage of our precious MF_ resources which are already dwindling so.

Ultimately, I see several major uses of various combinations of these things:
* Walls (for boxes/other shapes) with perspective that can cross sector boundaries at the expense of not being as thoroughly visually handled as polyobjects.
* Flat, potentially moving hazards like Scrap Brain sawblades that are excessively simple to set up.
* Robo-Hood's arrows and other types of elongated missiles being possible to dodge based on their apparent collision bounds rather than a solid cuboid.

Nev3r is obviously of course already salivating thinking about what he can do with this.

Unfortunately, the collision issues with regards to solid stuff will probably prevent people from giving MF_PAPERCOLLISION to everything (sorry, no Paper Mario Koopa Blast for now) since it's way too easy to get stuck at the moment. Hopefully that can be ironed out down the line - but if that makes you think this branch is incomplete, keep in mind that that this level of development was never actually my intent. In the end, I just wanted flat sprites to make my shitty minecart better, and I got that - the sawblade style stuff working accurately is just a further bonus!

No relevant OpenGL code included because you know how I feel about that, but that should be easy enough for someone else to do at a later date considering the effect happens a lot in that renderer unintentionally. :V

ALSO: I added another feature which (ab)uses some structural changes I made for papersprites. It's MF2_LINKDRAW!

* Sets the sortscale of the mobj to that of its tracer.
* Basically, Smiles' tails won't clip through shields thanks to this.
* http://gfycat.com/GraveGlassEwe

ALSO ALSO: I added ANOTHER feature. FF_VERTICALFLIP allows sprite flipping without gravity flipping. No need to bullet point, it stacks properly with gravity. http://gfycat.com/FrailLongKusimanse

Test using <root>/toaster/flatminecart.wad (and <root>/!LatestSRB2Files/srb2win_branch_paper.exe if you can't compile!) MF2_LINKDRAW doesn't have a testwad but I'm doing a lot of Smiles stuff right now so it probably won't be too long.

See merge request !37
This commit is contained in:
Monster Iestyn 2016-10-02 14:47:01 -04:00
commit 22d772f08a
14 changed files with 311 additions and 94 deletions

View File

@ -6594,7 +6594,7 @@ static const char *const MOBJFLAG_LIST[] = {
"SHOOTABLE",
"NOSECTOR",
"NOBLOCKMAP",
"AMBUSH",
"PAPERCOLLISION",
"PUSHABLE",
"BOSS",
"SPAWNCEILING",
@ -6651,6 +6651,8 @@ static const char *const MOBJFLAG2_LIST[] = {
"BOSSNOTRAP", // No Egg Trap after boss
"BOSSFLEE", // Boss is fleeing!
"BOSSDEAD", // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
"AMBUSH", // Alternate behaviour typically set by MTF_AMBUSH
"LINKDRAW", // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
NULL
};
@ -6973,6 +6975,8 @@ struct {
// Frame settings
{"FF_FRAMEMASK",FF_FRAMEMASK},
{"FF_VERTICALFLIP",FF_VERTICALFLIP},
{"FF_PAPERSPRITE",FF_PAPERSPRITE},
{"FF_SPR2ENDSTATE",FF_SPR2ENDSTATE},
{"FF_MIDDLESTARTCHANCE",FF_MIDDLESTARTCHANCE},
{"FF_ANIMATE",FF_ANIMATE},

View File

@ -536,4 +536,7 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
/// \note You should leave this enabled unless you're working with a future SRB2 version.
#define MUSICSLOT_COMPATIBILITY
/// Experimental attempts at preventing MF_PAPERCOLLISION objects from getting stuck in walls.
//#define PAPER_COLLISIONCORRECTION
#endif // __DOOMDEF__

View File

@ -5040,6 +5040,8 @@ static void HWR_ProjectSprite(mobj_t *thing)
size_t lumpoff;
unsigned rot;
UINT8 flip;
boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP));
angle_t ang;
INT32 heightsec, phs;
@ -5139,7 +5141,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
tx += FIXED_TO_FLOAT(spritecachedinfo[lumpoff].width) * this_scale;
x2 = gr_windowcenterx + (tx * gr_centerx / tz);
if (thing->eflags & MFE_VERTICALFLIP)
if (vflip)
{
gz = FIXED_TO_FLOAT(thing->z+thing->height) - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale;
gzt = gz + FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height) * this_scale;
@ -5216,10 +5218,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
//CONS_Debug(DBG_RENDER, "------------------\nH: sprite : %d\nH: frame : %x\nH: type : %d\nH: sname : %s\n\n",
// thing->sprite, thing->frame, thing->type, sprnames[thing->sprite]);
if (thing->eflags & MFE_VERTICALFLIP)
vis->vflip = true;
else
vis->vflip = false;
vis->vflip = vflip;
vis->precip = false;
}

View File

@ -1349,7 +1349,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
UINT32 durs = spr->mobj->state->tics;
UINT32 tics = spr->mobj->tics;
md2_frame_t *curr, *next = NULL;
const UINT8 flip = (UINT8)((spr->mobj->eflags & MFE_VERTICALFLIP) == MFE_VERTICALFLIP);
const UINT8 flip = (UINT8)(!(spr->mobj->eflags & MFE_VERTICALFLIP) != !(spr->mobj->frame & FF_VERTICALFLIP));
spritedef_t *sprdef;
spriteframe_t *sprframe;
float finalscale;
@ -1464,7 +1464,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
p.x = FIXED_TO_FLOAT(spr->mobj->x);
p.y = FIXED_TO_FLOAT(spr->mobj->y)+md2->offset;
if (spr->mobj->eflags & MFE_VERTICALFLIP)
if (flip)
p.z = FIXED_TO_FLOAT(spr->mobj->z + spr->mobj->height);
else
p.z = FIXED_TO_FLOAT(spr->mobj->z);

View File

@ -1054,7 +1054,7 @@ void OP_NightsObjectplace(player_t *player)
if (!OP_HeightOkay(player, false))
return;
if (player->mo->target->flags & MF_AMBUSH)
if (player->mo->target->flags2 & MF2_AMBUSH)
angle = (UINT16)player->anotherflyangle;
else
{

View File

@ -3544,7 +3544,7 @@ void A_BubbleSpawn(mobj_t *actor)
}
actor->flags2 &= ~MF2_DONTDRAW;
if (!(actor->flags & MF_AMBUSH))
if (!(actor->flags2 & MF2_AMBUSH))
{
// Quick! Look through players!
// Don't spawn bubbles unless a player is relatively close by (var2).
@ -3592,7 +3592,7 @@ void A_FanBubbleSpawn(mobj_t *actor)
if (!(actor->eflags & MFE_UNDERWATER))
return;
if (!(actor->flags & MF_AMBUSH))
if (!(actor->flags2 & MF2_AMBUSH))
{
// Quick! Look through players!
// Don't spawn bubbles unless a player is relatively close by (var2).
@ -4159,7 +4159,7 @@ void A_JetChase(mobj_t *actor)
return;
#endif
if (actor->flags & MF_AMBUSH)
if (actor->flags2 & MF2_AMBUSH)
return;
if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
@ -5052,7 +5052,7 @@ void A_SlingAppear(mobj_t *actor)
if (firsttime)
{
// This is the outermost link in the chain
spawnee->flags |= MF_AMBUSH;
spawnee->flags2 |= MF2_AMBUSH;
firsttime = false;
}
@ -6035,7 +6035,7 @@ void A_Boss2Chase(mobj_t *actor)
{
actor->watertop = -actor->watertop;
actor->extravalue1 = 18;
if (actor->flags & MF_AMBUSH)
if (actor->flags2 & MF2_AMBUSH)
actor->extravalue1 -= (actor->info->spawnhealth - actor->health)*2;
actor->extravalue2 = actor->extravalue1;
}
@ -6061,7 +6061,7 @@ void A_Boss2Chase(mobj_t *actor)
else
{
// Only speed up if you have the 'Deaf' flag.
if (actor->flags & MF_AMBUSH)
if (actor->flags2 & MF2_AMBUSH)
speedvar = actor->health;
else
speedvar = actor->info->spawnhealth;
@ -6652,7 +6652,7 @@ void A_BuzzFly(mobj_t *actor)
if (LUA_CallAction("A_BuzzFly", actor))
return;
#endif
if (actor->flags & MF_AMBUSH)
if (actor->flags2 & MF2_AMBUSH)
return;
if (actor->reactiontime)
@ -6792,7 +6792,7 @@ void A_GuardChase(mobj_t *actor)
return; // got a new target
// chase towards player
if (--actor->movecount < 0 || !P_Move(actor, (actor->flags & MF_AMBUSH) ? actor->info->speed * 2 : actor->info->speed))
if (--actor->movecount < 0 || !P_Move(actor, (actor->flags2 & MF2_AMBUSH) ? actor->info->speed * 2 : actor->info->speed))
{
P_NewChaseDir(actor);
actor->movecount += 5; // Increase tics before change in direction allowed.

View File

@ -1336,7 +1336,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
case MT_SMALLMACECHAIN:
case MT_BIGMACECHAIN:
// Is this the last link in the chain?
if (toucher->momz > 0 || !(special->flags & MF_AMBUSH)
if (toucher->momz > 0 || !(special->flags2 & MF2_AMBUSH)
|| (player->powers[pw_carry]))
return;

View File

@ -501,6 +501,74 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
return true; // didn't hit it
if (thing->flags & MF_PAPERCOLLISION) // CAUTION! Very easy to get stuck inside MF_SOLID objects. Giving the player MF_PAPERCOLLISION is a bad idea unless you know what you're doing.
{
fixed_t cosradius, sinradius;
vertex_t v1, v2; // fake vertexes
line_t junk; // fake linedef
cosradius = FixedMul(thing->radius, FINECOSINE(thing->angle>>ANGLETOFINESHIFT));
sinradius = FixedMul(thing->radius, FINESINE(thing->angle>>ANGLETOFINESHIFT));
v1.x = thing->x - cosradius;
v1.y = thing->y - sinradius;
v2.x = thing->x + cosradius;
v2.y = thing->y + sinradius;
junk.v1 = &v1;
junk.v2 = &v2;
junk.dx = v2.x - v1.x;
junk.dy = v2.y - v1.y;
if (tmthing->flags & MF_PAPERCOLLISION) // more strenuous checking to prevent clipping issues
{
INT32 check1, check2, check3, check4;
cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
sinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
check1 = P_PointOnLineSide(tmx - cosradius, tmy - sinradius, &junk);
check2 = P_PointOnLineSide(tmx + cosradius, tmy + sinradius, &junk);
check3 = P_PointOnLineSide(tmx + tmthing->momx - cosradius, tmy + tmthing->momy - sinradius, &junk);
check4 = P_PointOnLineSide(tmx + tmthing->momx + cosradius, tmy + tmthing->momy + sinradius, &junk);
if ((check1 == check2) && (check2 == check3) && (check3 == check4))
return true; // the line doesn't cross between collider's start or end
}
else
{
if ((P_PointOnLineSide(tmx - tmthing->radius, tmy - tmthing->radius, &junk)
== P_PointOnLineSide(tmx + tmthing->radius, tmy + tmthing->radius, &junk))
&& (P_PointOnLineSide(tmx + tmthing->radius, tmy - tmthing->radius, &junk)
== P_PointOnLineSide(tmx - tmthing->radius, tmy + tmthing->radius, &junk)))
return true; // the line doesn't cross between either pair of opposite corners
}
}
else if (tmthing->flags & MF_PAPERCOLLISION)
{
fixed_t cosradius, sinradius;
vertex_t v1, v2; // fake vertexes
line_t junk; // fake linedef
cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
sinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
v1.x = tmx - cosradius;
v1.y = tmy - sinradius;
v2.x = tmx + cosradius;
v2.y = tmy + sinradius;
junk.v1 = &v1;
junk.v2 = &v2;
junk.dx = v2.x - v1.x;
junk.dy = v2.y - v1.y;
// no need to check whether thing has MF_PAPERCOLLISION, since checked above
if ((P_PointOnLineSide(thing->x - thing->radius, thing->y - thing->radius, &junk)
== P_PointOnLineSide(thing->x + thing->radius, thing->y + thing->radius, &junk))
&& (P_PointOnLineSide(thing->x + thing->radius, thing->y - thing->radius, &junk)
== P_PointOnLineSide(thing->x - thing->radius, thing->y + thing->radius, &junk)))
return true; // the line doesn't cross between either pair of opposite corners
}
#ifdef HAVE_BLUA
{
UINT8 shouldCollide = LUAh_MobjCollide(thing, tmthing); // checks hook for thing's type
@ -1179,6 +1247,32 @@ static boolean PIT_CheckLine(line_t *ld)
if (P_BoxOnLineSide(tmbbox, ld) != -1)
return true;
if (tmthing->flags & MF_PAPERCOLLISION) // Caution! Turning whilst up against a wall will get you stuck. You probably shouldn't give the player this flag.
{
fixed_t cosradius, sinradius;
cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
sinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
if (P_PointOnLineSide(tmx - cosradius, tmy - sinradius, ld)
== P_PointOnLineSide(tmx + cosradius, tmy + sinradius, ld))
return true; // the line doesn't cross between collider's start or end
#ifdef PAPER_COLLISIONCORRECTION
{
fixed_t dist;
vertex_t result;
angle_t langle;
P_ClosestPointOnLine(tmx, tmy, ld, &result);
langle = R_PointToAngle2(ld->v1->x, ld->v1->y, ld->v2->x, ld->v2->y);
langle += ANGLE_90*(P_PointOnLineSide(tmx, tmy, ld) ? -1 : 1);
dist = abs(FixedMul(tmthing->radius, FINECOSINE((tmthing->angle - langle)>>ANGLETOFINESHIFT)));
cosradius = FixedMul(dist, FINECOSINE(langle>>ANGLETOFINESHIFT));
sinradius = FixedMul(dist, FINESINE(langle>>ANGLETOFINESHIFT));
tmthing->flags |= MF_NOCLIP;
P_TeleportMove(tmthing, result.x + cosradius - tmthing->momx, result.y + sinradius - tmthing->momy, tmthing->z);
tmthing->flags &= ~MF_NOCLIP;
}
#endif
}
// A line has been hit
// The moving thing's destination position will cross

View File

@ -2826,7 +2826,7 @@ static boolean P_ZMovement(mobj_t *mo)
&& abs(mom.y) < FixedMul(STOPSPEED, mo->scale)
&& abs(mom.z) < FixedMul(STOPSPEED*3, mo->scale))
{
if (mo->flags & MF_AMBUSH)
if (mo->flags2 & MF2_AMBUSH)
{
// If deafed, give the tumbleweed another random kick if it runs out of steam.
mom.z += P_MobjFlip(mo)*FixedMul(6*FRACUNIT, mo->scale);
@ -6760,7 +6760,7 @@ void P_MobjThinker(mobj_t *mobj)
flame->angle = mobj->angle;
if (mobj->flags & MF_AMBUSH) // Wave up and down instead of side-to-side
if (mobj->flags2 & MF2_AMBUSH) // Wave up and down instead of side-to-side
flame->momz = mobj->fuse << (FRACBITS-2);
else
flame->angle += FixedAngle(mobj->fuse*FRACUNIT);
@ -6795,7 +6795,7 @@ void P_MobjThinker(mobj_t *mobj)
strength -= ((20*FRACUNIT)/16)*mobj->movedir;
// If deaf'd, the object spawns on the ceiling.
if (mobj->flags & MF_AMBUSH)
if (mobj->flags2 & MF2_AMBUSH)
{
mobj->z = mobj->ceilingz-mobj->height;
flame->momz = -strength;
@ -7596,7 +7596,7 @@ void P_MobjThinker(mobj_t *mobj)
{
// Special case for ALL monitors.
// If a box's speed is nonzero, it's allowed to respawn as a WRM/SRM.
if (mobj->info->speed != 0 && (mobj->flags & MF_AMBUSH || mobj->flags2 & MF2_STRONGBOX))
if (mobj->info->speed != 0 && (mobj->flags2 & MF2_AMBUSH|MF2_STRONGBOX))
{
mobjtype_t spawnchance[64];
INT32 numchoices = 0, i = 0;
@ -7623,21 +7623,12 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s
i = P_RandomKey(numchoices); // Gotta love those random numbers!
newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, spawnchance[i]);
// If the monitor respawns randomly, transfer the flag.
if (mobj->flags & MF_AMBUSH)
newmobj->flags |= MF_AMBUSH;
// Transfer flags2 (strongbox, objectflip)
newmobj->flags2 = mobj->flags2;
}
else
{
newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->type);
// Transfer flags2 (strongbox, objectflip)
newmobj->flags2 = mobj->flags2;
}
// Transfer flags2 (ambush, strongbox, objectflip)
newmobj->flags2 = mobj->flags2;
P_RemoveMobj(mobj); // make sure they disappear
return;
}
@ -9515,7 +9506,7 @@ ML_NOCLIMB : Direction not controllable
if (firsttime)
{
// This is the outermost link in the chain
spawnee->flags |= MF_AMBUSH;
spawnee->flags2 |= MF2_AMBUSH;
firsttime = false;
}
@ -9587,7 +9578,7 @@ ML_NOCLIMB : Direction not controllable
{
// Inverted if uppermost bit is set
if (mthing->angle & 16384)
mobj->flags |= MF_AMBUSH;
mobj->flags2 |= MF2_AMBUSH;
if (mthing->angle > 0)
mobj->radius = (mthing->angle & 16383)*FRACUNIT;
@ -9759,7 +9750,7 @@ ML_NOCLIMB : Direction not controllable
{
// flag for strong/weak random boxes
// any monitor with nonzero speed is allowed to respawn like this
mobj->flags |= MF_AMBUSH;
mobj->flags2 |= MF2_AMBUSH;
}
else if (mthing->type != mobjinfo[MT_AXIS].doomednum &&
@ -9767,7 +9758,7 @@ ML_NOCLIMB : Direction not controllable
mthing->type != mobjinfo[MT_AXISTRANSFERLINE].doomednum &&
mthing->type != mobjinfo[MT_NIGHTSBUMPER].doomednum &&
mthing->type != mobjinfo[MT_STARPOST].doomednum)
mobj->flags |= MF_AMBUSH;
mobj->flags2 |= MF2_AMBUSH;
}
if (mthing->options & MTF_OBJECTSPECIAL)
@ -10104,7 +10095,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
P_SetMobjState(mobj, mobj->info->seestate);
mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
mobj->flags |= MF_AMBUSH;
mobj->flags2 |= MF2_AMBUSH;
mthing->mobj = mobj;
}
// All manners of rings and coins
@ -10178,7 +10169,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
}
mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
mobj->flags |= MF_AMBUSH;
mobj->flags2 |= MF2_AMBUSH;
mthing->mobj = mobj;
}
// ***
@ -10234,7 +10225,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
if (mthing->options & MTF_AMBUSH)
mobj->flags |= MF_AMBUSH;
mobj->flags2 |= MF2_AMBUSH;
}
}
// Diagonal rings (handles both types)
@ -10292,7 +10283,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
if (mthing->options & MTF_AMBUSH)
mobj->flags |= MF_AMBUSH;
mobj->flags2 |= MF2_AMBUSH;
}
}
// Rings of items (all six of them)

View File

@ -107,8 +107,8 @@ typedef enum
MF_NOSECTOR = 1<<3,
// Don't use the blocklinks (inert but displayable)
MF_NOBLOCKMAP = 1<<4,
// Not to be activated by sound, deaf monster.
MF_AMBUSH = 1<<5,
// Thin, paper-like collision bound (for visual equivalent, see FF_PAPERSPRITE)
MF_PAPERCOLLISION = 1<<5,
// You can push this object. It can activate switches and things by pushing it on top.
MF_PUSHABLE = 1<<6,
// Object is a boss.
@ -151,10 +151,9 @@ typedef enum
MF_PAIN = 1<<24,
// This mobj will stick to any surface or solid object it touches.
MF_STICKY = 1<<25,
// NiGHTS hidden item. Goes to seestate and turns MF_SPECIAL when paralooped.
// NiGHTS hidden item. Goes to seestate and turns MF_SPECIAL when paralooped.
MF_NIGHTSITEM = 1<<26,
// for chase camera, don't be blocked by things (partial clipping)
// (need comma at end of this for SOC editor)
MF_NOCLIPTHING = 1<<27,
// Missile bounces like a grenade.
MF_GRENADEBOUNCE = 1<<28,
@ -192,6 +191,8 @@ typedef enum
MF2_BOSSNOTRAP = 1<<24, // No Egg Trap after boss
MF2_BOSSFLEE = 1<<25, // Boss is fleeing!
MF2_BOSSDEAD = 1<<26, // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
MF2_AMBUSH = 1<<27, // Alternate behaviour typically set by MTF_AMBUSH
MF2_LINKDRAW = 1<<28, // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
// free: to and including 1<<31
} mobjflag2_t;

View File

@ -37,6 +37,10 @@
/// \brief Frame flags: only the frame number - 0 to 511 (Frames from 0 to 63, Sprite2 number uses full range)
#define FF_FRAMEMASK 0x1ff
/// \brief Frame flags: Flip sprite vertically (relative to what it should be for its gravity)
#define FF_VERTICALFLIP 0x400
/// \brief Frame flags: Thin, paper-like sprite (for collision equivalent, see MF_PAPERCOLLISION)
#define FF_PAPERSPRITE 0x800
/// \brief Frame flags: A change of state at the end of Sprite2 animation
#define FF_SPR2ENDSTATE 0x1000
/// \brief Frame flags: 50% of starting in middle of animation (Sprite2 and FF_ANIMATE)

View File

@ -625,7 +625,7 @@ static void P_DeNightserizePlayer(player_t *player)
if (!(mo2->type == MT_NIGHTSDRONE))
continue;
if (mo2->flags & MF_AMBUSH)
if (mo2->flags2 & MF2_AMBUSH)
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL);
break;
@ -5003,7 +5003,7 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
boolean transfer1last = false;
boolean transfer2last = false;
vertex_t vertices[4];
fixed_t truexspeed = xspeed*(!(player->pflags & PF_TRANSFERTOCLOSEST) && player->mo->target->flags & MF_AMBUSH ? -1 : 1);
fixed_t truexspeed = xspeed*(!(player->pflags & PF_TRANSFERTOCLOSEST) && player->mo->target->flags2 & MF2_AMBUSH ? -1 : 1);
// Find next waypoint
for (th = thinkercap.next; th != &thinkercap; th = th->next)
@ -5680,7 +5680,7 @@ static void P_NiGHTSMovement(player_t *player)
// The 'ambush' flag says you should rotate
// the other way around the axis.
if (player->mo->target->flags & MF_AMBUSH)
if (player->mo->target->flags2 & MF2_AMBUSH)
backwardaxis = true;
player->angle_pos = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y);
@ -7971,7 +7971,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
}
else if (player->mo->target)
{
if (player->mo->target->flags & MF_AMBUSH)
if (player->mo->target->flags2 & MF2_AMBUSH)
angle = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y);
else
angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->target->x, player->mo->target->y);

View File

@ -808,11 +808,18 @@ static void R_DrawVisSprite(vissprite_t *vis)
if (overflow_test < 0) overflow_test = -overflow_test;
if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) return; // fixed point mult would overflow
if (vis->scalestep) // handles right edge too
{
overflow_test = (INT64)centeryfrac - (((INT64)vis->texturemid*(vis->scale + (vis->scalestep*(vis->x2 - vis->x1))))>>FRACBITS);
if (overflow_test < 0) overflow_test = -overflow_test;
if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) return; // ditto
}
colfunc = basecolfunc; // hack: this isn't resetting properly somewhere.
dc_colormap = vis->colormap;
if ((vis->mobj->flags & MF_BOSS) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash"
{
// translate green skin to another color
// translate certain pixels to white
colfunc = transcolfunc;
if (vis->mobj->type == MT_CYBRAKDEMON)
dc_translation = R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE);
@ -868,13 +875,10 @@ static void R_DrawVisSprite(vissprite_t *vis)
if (!dc_colormap)
dc_colormap = colormaps;
dc_iscale = FixedDiv(FRACUNIT, vis->scale);
dc_texturemid = vis->texturemid;
dc_texheight = 0;
frac = vis->startfrac;
spryscale = vis->scale;
sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
windowtop = windowbottom = sprbotscreen = INT32_MAX;
if (vis->mobj->skin && ((skin_t *)vis->mobj->skin)->flags & SF_HIRES)
@ -886,28 +890,29 @@ static void R_DrawVisSprite(vissprite_t *vis)
if (!vis->isScaled)
{
vis->scale = FixedMul(vis->scale, this_scale);
spryscale = vis->scale;
dc_iscale = FixedDiv(FRACUNIT, vis->scale);
vis->scalestep = FixedMul(vis->scalestep, this_scale);
vis->xiscale = FixedDiv(vis->xiscale,this_scale);
vis->isScaled = true;
}
dc_texturemid = FixedDiv(dc_texturemid,this_scale);
}
//Oh lordy, mercy me. Don't freak out if sprites go offscreen!
/*if (vis->xiscale > 0)
frac = FixedDiv(frac, this_scale);
else if (vis->x1 <= 0)
frac = (vis->x1 - vis->x2) * vis->xiscale;*/
spryscale = vis->scale;
if (!(vis->scalestep))
{
sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
//dc_hires = 1;
dc_iscale = FixedDiv(FRACUNIT, vis->scale);
}
x1 = vis->x1;
x2 = vis->x2;
if (vis->x1 < 0)
{
spryscale += vis->scalestep*(-vis->x1);
vis->x1 = 0;
}
if (vis->x2 >= vid.width)
vis->x2 = vid.width-1;
@ -915,6 +920,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale)
{
#ifdef RANGECHECK
texturecolumn = frac>>FRACBITS;
if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width))
@ -923,10 +929,16 @@ static void R_DrawVisSprite(vissprite_t *vis)
#else
column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[frac>>FRACBITS]));
#endif
if (vis->scalestep)
{
sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
dc_iscale = (0xffffffffu / (unsigned)spryscale);
}
if (vis->vflip)
R_DrawFlippedMaskedColumn(column, patch->height);
else
R_DrawMaskedColumn(column);
spryscale += vis->scalestep;
}
colfunc = basecolfunc;
@ -1021,7 +1033,7 @@ static void R_SplitSprite(vissprite_t *sprite, mobj_t *thing)
if (testheight <= sprite->gz)
return;
cutfrac = (INT16)((centeryfrac - FixedMul(testheight - viewz, sprite->scale))>>FRACBITS);
cutfrac = (INT16)((centeryfrac - FixedMul(testheight - viewz, sprite->sortscale))>>FRACBITS);
if (cutfrac < 0)
continue;
if (cutfrac > viewheight)
@ -1094,7 +1106,7 @@ static void R_ProjectSprite(mobj_t *thing)
fixed_t tr_x, tr_y;
fixed_t gxt, gyt;
fixed_t tx, tz;
fixed_t xscale, yscale; //added : 02-02-98 : aaargll..if I were a math-guy!!!
fixed_t xscale, yscale, sortscale; //added : 02-02-98 : aaargll..if I were a math-guy!!!
INT32 x1, x2;
@ -1104,13 +1116,19 @@ static void R_ProjectSprite(mobj_t *thing)
size_t rot;
UINT8 flip;
boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP));
INT32 lindex;
vissprite_t *vis;
angle_t ang;
angle_t ang = 0; // compiler complaints
fixed_t iscale;
fixed_t scalestep;
fixed_t offset, offset2;
boolean papersprite = !!(thing->frame & FF_PAPERSPRITE);
INT32 dispoffset = thing->info->dispoffset;
//SoM: 3/17/2000
fixed_t gz, gzt;
@ -1118,6 +1136,8 @@ static void R_ProjectSprite(mobj_t *thing)
INT32 light = 0;
fixed_t this_scale = thing->scale;
fixed_t ang_scale = FRACUNIT;
// transform the origin point
tr_x = thing->x - viewx;
tr_y = thing->y - viewy;
@ -1128,7 +1148,7 @@ static void R_ProjectSprite(mobj_t *thing)
tz = gxt-gyt;
// thing is behind view plane?
if (tz < FixedMul(MINZ, this_scale))
if (!(papersprite) && (tz < FixedMul(MINZ, this_scale))) // papersprite clipping is handled later
return;
gxt = -FixedMul(tr_x, viewsin);
@ -1141,7 +1161,7 @@ static void R_ProjectSprite(mobj_t *thing)
// aspect ratio stuff
xscale = FixedDiv(projection, tz);
yscale = FixedDiv(projectiony, tz);
sortscale = FixedDiv(projectiony, tz);
// decide which patch to use for sprite relative to player
#ifdef RANGECHECK
@ -1188,6 +1208,13 @@ static void R_ProjectSprite(mobj_t *thing)
I_Error("R_ProjectSprite: sprframes NULL for sprite %d\n", thing->sprite);
#endif
if (sprframe->rotate != SRF_SINGLE || papersprite)
{
ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
if (papersprite)
ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT));
}
if (sprframe->rotate == SRF_SINGLE)
{
// use single rotation for all views
@ -1198,7 +1225,7 @@ static void R_ProjectSprite(mobj_t *thing)
else
{
// choose a different rotation based on player view
ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
//ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
if ((sprframe->rotate & SRF_RIGHT) && (ang < ANGLE_180)) // See from right
rot = 6; // F7 slot
@ -1219,22 +1246,112 @@ static void R_ProjectSprite(mobj_t *thing)
// calculate edges of the shape
if (flip)
tx -= FixedMul(spritecachedinfo[lump].width-spritecachedinfo[lump].offset, this_scale);
offset = spritecachedinfo[lump].offset - spritecachedinfo[lump].width;
else
tx -= FixedMul(spritecachedinfo[lump].offset, this_scale);
offset = -spritecachedinfo[lump].offset;
offset = FixedMul(offset, this_scale);
tx += FixedMul(offset, ang_scale);
x1 = (centerxfrac + FixedMul (tx,xscale)) >>FRACBITS;
// off the right side?
if (x1 > viewwidth)
return;
tx += FixedMul(spritecachedinfo[lump].width, this_scale);
offset2 = FixedMul(spritecachedinfo[lump].width, this_scale);
tx += FixedMul(offset2, ang_scale);
x2 = ((centerxfrac + FixedMul (tx,xscale)) >>FRACBITS) - 1;
// off the left side
if (x2 < 0)
return;
if (papersprite)
{
fixed_t yscale2, cosmul, sinmul, tz2;
INT32 range;
if (ang >= ANGLE_180)
{
offset *= -1;
offset2 *= -1;
}
cosmul = FINECOSINE(thing->angle>>ANGLETOFINESHIFT);
sinmul = FINESINE(thing->angle>>ANGLETOFINESHIFT);
tr_x += FixedMul(offset, cosmul);
tr_y += FixedMul(offset, sinmul);
gxt = FixedMul(tr_x, viewcos);
gyt = -FixedMul(tr_y, viewsin);
tz = gxt-gyt;
yscale = FixedDiv(projectiony, tz);
if (yscale < 64) return; // Fix some funky visuals
tr_x += FixedMul(offset2, cosmul);
tr_y += FixedMul(offset2, sinmul);
gxt = FixedMul(tr_x, viewcos);
gyt = -FixedMul(tr_y, viewsin);
tz2 = gxt-gyt;
yscale2 = FixedDiv(projectiony, tz2);
if (yscale2 < 64) return; // ditto
if (max(tz, tz2) < FixedMul(MINZ, this_scale)) // non-papersprite clipping is handled earlier
return;
if (x2 > x1)
range = (x2 - x1);
else
range = 1;
scalestep = (yscale2 - yscale)/range;
// The following two are alternate sorting methods which might be more applicable in some circumstances. TODO - maybe enable via MF2?
// sortscale = max(yscale, yscale2);
// sortscale = min(yscale, yscale2);
}
else
{
scalestep = 0;
yscale = sortscale;
}
xscale = FixedMul(xscale, ang_scale);
if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) // toast 16/09/16 (SYMMETRY)
{
fixed_t linkscale;
#if 0 // support for chains of linkdraw - probably not network safe to modify mobjs during rendering
mobj_t *link, *link2;
for (link = thing->tracer; (link->tracer && (link->flags2 & MF2_LINKDRAW)); link = link->tracer)
link->flags2 &= ~MF2_LINKDRAW; // to prevent infinite loops, otherwise would just be a ;
for (link2 = thing->tracer; (link2->tracer && (link2 != link)); link2 = link2->tracer)
link->flags2 |= MF2_LINKDRAW; // only needed for correction of the above
if (link->flags2 & MF2_LINKDRAW)
link->flags2 &= ~MF2_LINKDRAW; // let's try and make sure this doesn't happen again...
tr_x = link->x - viewx;
tr_y = link->y - viewy;
#else
tr_x = thing->tracer->x - viewx;
tr_y = thing->tracer->y - viewy;
#endif
gxt = FixedMul(tr_x, viewcos);
gyt = -FixedMul(tr_y, viewsin);
tz = gxt-gyt;
linkscale = FixedDiv(projectiony, tz);
if (tz < FixedMul(MINZ, this_scale))
return;
if (sortscale < linkscale)
dispoffset *= -1; // if it's physically behind, make sure it's ordered behind (if dispoffset > 0)
sortscale = linkscale; // now make sure it's linked
}
// PORTAL SPRITE CLIPPING
if (portalrender)
{
@ -1246,7 +1363,7 @@ static void R_ProjectSprite(mobj_t *thing)
}
//SoM: 3/17/2000: Disregard sprites that are out of view..
if (thing->eflags & MFE_VERTICALFLIP)
if (vflip)
{
// When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned.
// sprite height - sprite topoffset is the proper inverse of the vertical offset, of course.
@ -1316,7 +1433,8 @@ static void R_ProjectSprite(mobj_t *thing)
vis->heightsec = heightsec; //SoM: 3/17/2000
vis->mobjflags = thing->flags;
vis->scale = yscale; //<<detailshift;
vis->dispoffset = thing->info->dispoffset; // Monster Iestyn: 23/11/15
vis->sortscale = sortscale;
vis->dispoffset = dispoffset; // Monster Iestyn: 23/11/15
vis->gx = thing->x;
vis->gy = thing->y;
vis->gz = gz;
@ -1325,6 +1443,7 @@ static void R_ProjectSprite(mobj_t *thing)
vis->pz = thing->z;
vis->pzt = vis->pz + vis->thingheight;
vis->texturemid = vis->gzt - viewz;
vis->scalestep = scalestep;
vis->mobj = thing; // Easy access! Tails 06-07-2002
@ -1342,8 +1461,8 @@ static void R_ProjectSprite(mobj_t *thing)
vis->xscale = xscale; //SoM: 4/17/2000
vis->sector = thing->subsector->sector;
vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, yscale))>>FRACBITS);
vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, yscale))>>FRACBITS);
vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, sortscale))>>FRACBITS);
vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, sortscale))>>FRACBITS);
vis->cut = SC_NONE;
if (thing->subsector->sector->numlights)
vis->extra_colormap = thing->subsector->sector->lightlist[light].extra_colormap;
@ -1364,7 +1483,10 @@ static void R_ProjectSprite(mobj_t *thing)
}
if (vis->x1 > x1)
{
vis->startfrac += FixedDiv(vis->xiscale, this_scale)*(vis->x1-x1);
vis->scale += scalestep*(vis->x1 - x1);
}
//Fab: lumppat is the lump number of the patch to use, this is different
// than lumpid for sprites-in-pwad : the graphics are patched
@ -1402,10 +1524,7 @@ static void R_ProjectSprite(mobj_t *thing)
vis->precip = false;
if (thing->eflags & MFE_VERTICALFLIP)
vis->vflip = true;
else
vis->vflip = false;
vis->vflip = vflip;
vis->isScaled = false;
@ -1523,7 +1642,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
// store information in a vissprite
vis = R_NewVisSprite();
vis->scale = yscale; //<<detailshift;
vis->scale = vis->sortscale = yscale; //<<detailshift;
vis->dispoffset = 0; // Monster Iestyn: 23/11/15
vis->gx = thing->x;
vis->gy = thing->y;
@ -1533,6 +1652,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
vis->pz = thing->z;
vis->pzt = vis->pz + vis->thingheight;
vis->texturemid = vis->gzt - viewz;
vis->scalestep = 0;
vis->x1 = x1 < 0 ? 0 : x1;
vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2;
@ -1711,14 +1831,14 @@ void R_SortVisSprites(void)
bestscale = bestdispoffset = INT32_MAX;
for (ds = unsorted.next; ds != &unsorted; ds = ds->next)
{
if (ds->scale < bestscale)
if (ds->sortscale < bestscale)
{
bestscale = ds->scale;
bestscale = ds->sortscale;
bestdispoffset = ds->dispoffset;
best = ds;
}
// order visprites of same scale by dispoffset, smallest first
else if (ds->scale == bestscale && ds->dispoffset < bestdispoffset)
else if (ds->sortscale == bestscale && ds->dispoffset < bestdispoffset)
{
bestdispoffset = ds->dispoffset;
best = ds;
@ -1880,7 +2000,7 @@ static void R_CreateDrawNodes(void)
for (i = x1; i <= x2; i++)
{
if (r2->seg->frontscale[i] > rover->scale)
if (r2->seg->frontscale[i] > rover->sortscale)
break;
}
if (i > x2)
@ -1899,10 +2019,10 @@ static void R_CreateDrawNodes(void)
continue;
scale = r2->thickseg->scale1 > r2->thickseg->scale2 ? r2->thickseg->scale1 : r2->thickseg->scale2;
if (scale <= rover->scale)
if (scale <= rover->sortscale)
continue;
scale = r2->thickseg->scale1 + (r2->thickseg->scalestep * (sintersect - r2->thickseg->x1));
if (scale <= rover->scale)
if (scale <= rover->sortscale)
continue;
#ifdef ESLOPE
@ -1952,11 +2072,11 @@ static void R_CreateDrawNodes(void)
continue;
scale = r2->seg->scale1 > r2->seg->scale2 ? r2->seg->scale1 : r2->seg->scale2;
if (scale <= rover->scale)
if (scale <= rover->sortscale)
continue;
scale = r2->seg->scale1 + (r2->seg->scalestep * (sintersect - r2->seg->x1));
if (rover->scale < scale)
if (rover->sortscale < scale)
{
entry = R_CreateDrawNode(NULL);
(entry->prev = r2->prev)->next = entry;
@ -1972,8 +2092,8 @@ static void R_CreateDrawNodes(void)
if (r2->sprite->szt > rover->sz || r2->sprite->sz < rover->szt)
continue;
if (r2->sprite->scale > rover->scale
|| (r2->sprite->scale == rover->scale && r2->sprite->dispoffset > rover->dispoffset))
if (r2->sprite->sortscale > rover->sortscale
|| (r2->sprite->sortscale == rover->sortscale && r2->sprite->dispoffset > rover->dispoffset))
{
entry = R_CreateDrawNode(NULL);
(entry->prev = r2->prev)->next = entry;
@ -2126,8 +2246,8 @@ void R_ClipSprites(void)
scale = ds->scale2;
}
if (scale < spr->scale ||
(lowscale < spr->scale &&
if (scale < spr->sortscale ||
(lowscale < spr->sortscale &&
!R_PointOnSegSide (spr->gx, spr->gy, ds->curline)))
{
// masked mid texture?
@ -2178,7 +2298,7 @@ void R_ClipSprites(void)
fixed_t mh, h;
INT32 phs = viewplayer->mo->subsector->sector->heightsec;
if ((mh = sectors[spr->heightsec].floorheight) > spr->gz &&
(h = centeryfrac - FixedMul(mh -= viewz, spr->scale)) >= 0 &&
(h = centeryfrac - FixedMul(mh -= viewz, spr->sortscale)) >= 0 &&
(h >>= FRACBITS) < viewheight)
{
if (mh <= 0 || (phs != -1 && viewz > sectors[phs].floorheight))
@ -2196,7 +2316,7 @@ void R_ClipSprites(void)
}
if ((mh = sectors[spr->heightsec].ceilingheight) < spr->gzt &&
(h = centeryfrac - FixedMul(mh-viewz, spr->scale)) >= 0 &&
(h = centeryfrac - FixedMul(mh-viewz, spr->sortscale)) >= 0 &&
(h >>= FRACBITS) < viewheight)
{
if (phs != -1 && viewz >= sectors[phs].ceilingheight)

View File

@ -149,7 +149,8 @@ typedef struct vissprite_s
fixed_t pz, pzt; // physical bottom/top for sorting with 3D floors
fixed_t startfrac; // horizontal position of x1
fixed_t scale;
fixed_t scale, sortscale; // sortscale only differs from scale for paper sprites and MF2_LINKDRAW
fixed_t scalestep; // only for paper sprites, 0 otherwise
fixed_t xiscale; // negative if flipped
fixed_t texturemid;