Merge branch 'blockmap-links' into 'next'

Make objects able to collide with others if they occupy multiple blockmap cells (resolves #592)

Closes #592

See merge request STJr/SRB2!2267
This commit is contained in:
sphere 2024-02-08 23:16:59 +00:00
commit 41613d89a1
12 changed files with 595 additions and 297 deletions

View file

@ -34,46 +34,28 @@ typedef UINT8 (*blockmap_func)(lua_State *, INT32, INT32, mobj_t *);
static boolean blockfuncerror = false; // errors should only print once per search blockmap call static boolean blockfuncerror = false; // errors should only print once per search blockmap call
// Helper function for "objects" search // Helper function for "objects" search
static UINT8 lib_searchBlockmap_Objects(lua_State *L, INT32 x, INT32 y, mobj_t *thing) static UINT8 lib_searchBlockmap_Objects(lua_State *L, mobj_t *thing, mobj_t *mobj)
{ {
mobj_t *mobj, *bnext = NULL; lua_pushvalue(L, 1); // push function
LUA_PushUserdata(L, thing, META_MOBJ);
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) LUA_PushUserdata(L, mobj, META_MOBJ);
return 0; if (lua_pcall(gL, 2, 1, 0)) {
if (!blockfuncerror || cv_debug & DBG_LUA)
// Check interaction with the objects in the blockmap. CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
for (mobj = blocklinks[y*bmapwidth + x]; mobj; mobj = bnext)
{
P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed!
if (mobj == thing)
continue; // our thing just found itself, so move on
lua_pushvalue(L, 1); // push function
LUA_PushUserdata(L, thing, META_MOBJ);
LUA_PushUserdata(L, mobj, META_MOBJ);
if (lua_pcall(gL, 2, 1, 0)) {
if (!blockfuncerror || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
blockfuncerror = true;
P_SetTarget(&bnext, NULL);
return 0; // *shrugs*
}
if (!lua_isnil(gL, -1))
{ // if nil, continue
P_SetTarget(&bnext, NULL);
if (lua_toboolean(gL, -1))
return 2; // stop whole search
else
return 1; // stop block search
}
lua_pop(gL, 1); lua_pop(gL, 1);
if (P_MobjWasRemoved(thing) // func just popped our thing, cannot continue. blockfuncerror = true;
|| (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue. return 0; // *shrugs*
{
P_SetTarget(&bnext, NULL);
return (P_MobjWasRemoved(thing)) ? 2 : 1;
}
} }
if (!lua_isnil(gL, -1))
{ // if nil, continue
if (lua_toboolean(gL, -1))
return 2; // stop whole search
else
return 1; // stop block search
}
lua_pop(gL, 1);
if (P_MobjWasRemoved(thing)) // func just popped our thing, cannot continue.
return P_MobjWasRemoved(thing) ? 2 : 1;
return 0; return 0;
} }
@ -233,6 +215,7 @@ static int lib_searchBlockmap(lua_State *L)
boolean retval = true; boolean retval = true;
UINT8 funcret = 0; UINT8 funcret = 0;
blockmap_func searchFunc; blockmap_func searchFunc;
boolean searchObjects = false;
lua_remove(L, 1); // remove searchtype, stack is now function, mobj, [x1, x2, y1, y2] lua_remove(L, 1); // remove searchtype, stack is now function, mobj, [x1, x2, y1, y2]
luaL_checktype(L, 1, LUA_TFUNCTION); luaL_checktype(L, 1, LUA_TFUNCTION);
@ -241,7 +224,8 @@ static int lib_searchBlockmap(lua_State *L)
{ {
case 0: // "objects" case 0: // "objects"
default: default:
searchFunc = lib_searchBlockmap_Objects; searchObjects = true;
searchFunc = NULL;
break; break;
case 1: // "lines" case 1: // "lines"
searchFunc = lib_searchBlockmap_Lines; searchFunc = lib_searchBlockmap_Lines;
@ -286,24 +270,66 @@ static int lib_searchBlockmap(lua_State *L)
BMBOUNDFIX(xl, xh, yl, yh); BMBOUNDFIX(xl, xh, yl, yh);
blockfuncerror = false; // reset blockfuncerror = false; // reset
validcount++;
for (bx = xl; bx <= xh; bx++) if (!searchObjects) {
for (by = yl; by <= yh; by++) validcount++;
{
funcret = searchFunc(L, bx, by, mobj); for (bx = xl; bx <= xh; bx++)
// return value of searchFunc determines searchFunc's return value and/or when to stop for (by = yl; by <= yh; by++)
if (funcret == 2){ // stop whole search {
lua_pushboolean(L, false); // return false funcret = searchFunc(L, bx, by, mobj);
return 1;
// return value of searchFunc determines searchFunc's return value and/or when to stop
if (funcret == 2) { // stop whole search
lua_pushboolean(L, false); // return false
return 1;
}
else if (funcret == 1) // search was interrupted for this block
retval = false; // this changes the return value, but doesn't stop the whole search
// else don't do anything, continue as normal
if (P_MobjWasRemoved(mobj)) { // ...unless the original object was removed
lua_pushboolean(L, false); // in which case we have to stop now regardless
return 1;
}
} }
else if (funcret == 1) // search was interrupted for this block }
retval = false; // this changes the return value, but doesn't stop the whole search else {
// else don't do anything, continue as normal bthingit_t *it = P_NewBlockThingsIterator(xl, yl, xh, yh);
if (P_MobjWasRemoved(mobj)){ // ...unless the original object was removed if (!it) {
lua_pushboolean(L, false); // in which case we have to stop now regardless lua_pushboolean(L, false);
return 1; return 1;
}
mobj_t *itmobj = NULL;
do
{
itmobj = P_BlockThingsIteratorNext(it, false);
if (itmobj)
{
if (mobj == itmobj)
continue; // our thing just found itself, so move on
funcret = lib_searchBlockmap_Objects(L, mobj, itmobj);
if (funcret == 2) {
lua_pushboolean(L, false);
return 1;
}
else if (funcret == 1)
retval = false;
if (P_MobjWasRemoved(mobj)) {
retval = false;
break;
}
} }
} }
while (itmobj != NULL);
P_FreeBlockThingsIterator(it);
}
lua_pushboolean(L, retval); lua_pushboolean(L, retval);
return 1; return 1;
} }

View file

@ -355,8 +355,12 @@ static int mobj_get(lua_State *L)
lua_pushinteger(L, mo->blendmode); lua_pushinteger(L, mo->blendmode);
break; break;
case mobj_bnext: case mobj_bnext:
LUA_PushUserdata(L, mo->bnext, META_MOBJ); if (mo->blocknode && mo->blocknode->bnext) {
break; LUA_PushUserdata(L, mo->blocknode->bnext->mobj, META_MOBJ);
break;
}
else
return 0;
case mobj_bprev: case mobj_bprev:
// bprev -- same deal as sprev above, but for the blockmap. // bprev -- same deal as sprev above, but for the blockmap.
return UNIMPLEMENTED; return UNIMPLEMENTED;
@ -669,7 +673,6 @@ static int mobj_set(lua_State *L)
sector_list = NULL; sector_list = NULL;
} }
mo->snext = NULL, mo->sprev = NULL; mo->snext = NULL, mo->sprev = NULL;
mo->bnext = NULL, mo->bprev = NULL;
P_SetThingPosition(mo); P_SetThingPosition(mo);
} }
else else

View file

@ -5821,15 +5821,12 @@ void A_MinusDigging(mobj_t *actor)
fixed_t yl = (unsigned)(actor->y - radius - bmaporgy) >> MAPBLOCKSHIFT; fixed_t yl = (unsigned)(actor->y - radius - bmaporgy) >> MAPBLOCKSHIFT;
fixed_t xh = (unsigned)(actor->x + radius - bmaporgx) >> MAPBLOCKSHIFT; fixed_t xh = (unsigned)(actor->x + radius - bmaporgx) >> MAPBLOCKSHIFT;
fixed_t xl = (unsigned)(actor->x - radius - bmaporgx) >> MAPBLOCKSHIFT; fixed_t xl = (unsigned)(actor->x - radius - bmaporgx) >> MAPBLOCKSHIFT;
fixed_t bx, by;
BMBOUNDFIX(xl, xh, yl, yh); BMBOUNDFIX(xl, xh, yl, yh);
minus = actor; minus = actor;
for (bx = xl; bx <= xh; bx++) P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_MinusCarry);
for (by = yl; by <= yh; by++)
P_BlockThingsIterator(bx, by, PIT_MinusCarry);
} }
else else
{ {
@ -13889,7 +13886,7 @@ void A_DustDevilThink(mobj_t *actor)
{ {
fixed_t scale = actor->scale; fixed_t scale = actor->scale;
mobj_t *layer = actor->tracer; mobj_t *layer = actor->tracer;
INT32 bx, by, xl, xh, yl, yh; INT32 xl, xh, yl, yh;
fixed_t radius = actor->radius; fixed_t radius = actor->radius;
if (LUA_CallAction(A_DUSTDEVILTHINK, actor)) if (LUA_CallAction(A_DUSTDEVILTHINK, actor))
@ -13953,9 +13950,7 @@ void A_DustDevilThink(mobj_t *actor)
dustdevil = actor; dustdevil = actor;
for (bx = xl; bx <= xh; bx++) P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_DustDevilLaunch);
for (by = yl; by <= yh; by++)
P_BlockThingsIterator(bx, by, PIT_DustDevilLaunch);
//Whirlwind sound effect. //Whirlwind sound effect.
if (leveltime % 70 == 0) if (leveltime % 70 == 0)
@ -14035,7 +14030,6 @@ static boolean PIT_TNTExplode(mobj_t *nearby)
void A_TNTExplode(mobj_t *actor) void A_TNTExplode(mobj_t *actor)
{ {
INT32 locvar1 = var1; INT32 locvar1 = var1;
INT32 x, y;
INT32 xl, xh, yl, yh; INT32 xl, xh, yl, yh;
static mappoint_t epicenter = {0,0,0}; static mappoint_t epicenter = {0,0,0};
@ -14072,9 +14066,7 @@ void A_TNTExplode(mobj_t *actor)
barrel = actor; barrel = actor;
for (x = xl; x <= xh; x++) P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_TNTExplode);
for (y = yl; y <= yh; y++)
P_BlockThingsIterator(x, y, PIT_TNTExplode);
// cause a quake -- P_StartQuake does not exist yet // cause a quake -- P_StartQuake does not exist yet
epicenter.x = actor->x; epicenter.x = actor->x;

View file

@ -462,7 +462,7 @@ extern INT32 bmapwidth;
extern INT32 bmapheight; // in mapblocks extern INT32 bmapheight; // in mapblocks
extern fixed_t bmaporgx; extern fixed_t bmaporgx;
extern fixed_t bmaporgy; // origin of block map extern fixed_t bmaporgy; // origin of block map
extern mobj_t **blocklinks; // for thing chains extern blocknode_t **blocklinks; // for thing chains
// //
// P_INTER // P_INTER

View file

@ -752,20 +752,25 @@ static void P_PlayerBarrelCollide(mobj_t *toucher, mobj_t *barrel)
P_DamageMobj(barrel, toucher, toucher, 1, 0); P_DamageMobj(barrel, toucher, toucher, 1, 0);
} }
// enum
// PIT_CheckThing {
// CHECKTHING_NOCOLLIDE,
static boolean PIT_CheckThing(mobj_t *thing) CHECKTHING_COLLIDE,
CHECKTHING_DONE,
CHECKTHING_IGNORE
};
static unsigned PIT_DoCheckThing(mobj_t *thing)
{ {
fixed_t blockdist; fixed_t blockdist;
// don't clip against self // don't clip against self
if (thing == tmthing) if (thing == tmthing)
return true; return CHECKTHING_IGNORE;
// Ignore... things. // Ignore... things.
if (!tmthing || !thing || P_MobjWasRemoved(thing)) if (!tmthing || !thing || P_MobjWasRemoved(thing))
return true; return CHECKTHING_IGNORE;
I_Assert(!P_MobjWasRemoved(tmthing)); I_Assert(!P_MobjWasRemoved(tmthing));
I_Assert(!P_MobjWasRemoved(thing)); I_Assert(!P_MobjWasRemoved(thing));
@ -773,7 +778,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
// Ignore spectators // Ignore spectators
if ((tmthing->player && tmthing->player->spectator) if ((tmthing->player && tmthing->player->spectator)
|| (thing->player && thing->player->spectator)) || (thing->player && thing->player->spectator))
return true; return CHECKTHING_IGNORE;
// Do name checks all the way up here // Do name checks all the way up here
// So that NOTHING ELSE can see MT_NAMECHECK because it is client-side. // So that NOTHING ELSE can see MT_NAMECHECK because it is client-side.
@ -781,24 +786,24 @@ static boolean PIT_CheckThing(mobj_t *thing)
{ {
// Ignore things that aren't players, ignore spectators, ignore yourself. // Ignore things that aren't players, ignore spectators, ignore yourself.
if (!thing->player || !(tmthing->target && tmthing->target->player) || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player)) if (!thing->player || !(tmthing->target && tmthing->target->player) || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player))
return true; return CHECKTHING_IGNORE;
// Now check that you actually hit them. // Now check that you actually hit them.
blockdist = thing->radius + tmthing->radius; blockdist = thing->radius + tmthing->radius;
if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
return true; // didn't hit it return CHECKTHING_NOCOLLIDE; // didn't hit it
// see if it went over / under // see if it went over / under
if (tmthing->z > thing->z + thing->height) if (tmthing->z > thing->z + thing->height)
return true; // overhead return CHECKTHING_NOCOLLIDE; // overhead
if (tmthing->z + tmthing->height < thing->z) if (tmthing->z + tmthing->height < thing->z)
return true; // underneath return CHECKTHING_NOCOLLIDE; // underneath
// REX HAS SEEN YOU // Call any SeenPlayer Lua hooks
if (!LUA_HookSeenPlayer(tmthing->target->player, thing->player)) if (!LUA_HookSeenPlayer(tmthing->target->player, thing->player))
return false; return CHECKTHING_DONE;
seenplayer = thing->player; seenplayer = thing->player;
return false; return CHECKTHING_DONE;
} }
// Metal Sonic destroys tiny baby objects. // Metal Sonic destroys tiny baby objects.
@ -808,15 +813,15 @@ static boolean PIT_CheckThing(mobj_t *thing)
|| thing->type == MT_WALLSPIKE))) || thing->type == MT_WALLSPIKE)))
{ {
if ((thing->flags & (MF_ENEMY|MF_BOSS)) && (thing->health <= 0 || !(thing->flags & MF_SHOOTABLE))) if ((thing->flags & (MF_ENEMY|MF_BOSS)) && (thing->health <= 0 || !(thing->flags & MF_SHOOTABLE)))
return true; return CHECKTHING_IGNORE;
blockdist = thing->radius + tmthing->radius; blockdist = thing->radius + tmthing->radius;
if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
return true; // didn't hit it return CHECKTHING_NOCOLLIDE; // didn't hit it
// see if it went over / under // see if it went over / under
if (tmthing->z > thing->z + thing->height) if (tmthing->z > thing->z + thing->height)
return true; // overhead return CHECKTHING_NOCOLLIDE; // overhead
if (tmthing->z + tmthing->height < thing->z) if (tmthing->z + tmthing->height < thing->z)
return true; // underneath return CHECKTHING_NOCOLLIDE; // underneath
if (thing->type == MT_SPIKE if (thing->type == MT_SPIKE
|| thing->type == MT_WALLSPIKE) || thing->type == MT_WALLSPIKE)
{ {
@ -832,28 +837,28 @@ static boolean PIT_CheckThing(mobj_t *thing)
thing->health = 0; thing->health = 0;
P_KillMobj(thing, tmthing, tmthing, 0); P_KillMobj(thing, tmthing, tmthing, 0);
} }
return true; return CHECKTHING_COLLIDE;
} }
// STR_SPIKE users destroy spikes // STR_SPIKE users destroy spikes
if ((tmthing->player) && ((tmthing->player->powers[pw_strong] & STR_SPIKE) && (thing->type == MT_SPIKE || thing->type == MT_WALLSPIKE))) if ((tmthing->player) && ((tmthing->player->powers[pw_strong] & STR_SPIKE) && (thing->type == MT_SPIKE || thing->type == MT_WALLSPIKE)))
{ {
mobj_t *iter; mobj_t *iter;
blockdist = thing->radius + tmthing->radius; blockdist = thing->radius + tmthing->radius;
if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
return true; // didn't hit it return CHECKTHING_NOCOLLIDE; // didn't hit it
// see if it went over / under // see if it went over / under
if (tmthing->z > thing->z + thing->height) if (tmthing->z > thing->z + thing->height)
return true; // overhead return CHECKTHING_NOCOLLIDE; // overhead
if (tmthing->z + tmthing->height < thing->z) if (tmthing->z + tmthing->height < thing->z)
return true; // underneath return CHECKTHING_NOCOLLIDE; // underneath
if (thing->flags & MF_SOLID) if (thing->flags & MF_SOLID)
S_StartSound(tmthing, thing->info->deathsound); S_StartSound(tmthing, thing->info->deathsound);
for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext) for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext)
if (iter->type == thing->type && iter->health > 0 && iter->flags & MF_SOLID && (iter == thing || P_AproxDistance(P_AproxDistance(thing->x - iter->x, thing->y - iter->y), thing->z - iter->z) < 56*thing->scale))//FixedMul(56*FRACUNIT, thing->scale)) if (iter->type == thing->type && iter->health > 0 && iter->flags & MF_SOLID && (iter == thing || P_AproxDistance(P_AproxDistance(thing->x - iter->x, thing->y - iter->y), thing->z - iter->z) < 56*thing->scale))//FixedMul(56*FRACUNIT, thing->scale))
P_KillMobj(iter, tmthing, tmthing, 0); P_KillMobj(iter, tmthing, tmthing, 0);
return true; return CHECKTHING_COLLIDE;
} }
// vectorise metal - done in a special case as at this point neither has the right flags for touching // vectorise metal - done in a special case as at this point neither has the right flags for touching
@ -865,30 +870,30 @@ static boolean PIT_CheckThing(mobj_t *thing)
blockdist = thing->radius + tmthing->radius; blockdist = thing->radius + tmthing->radius;
if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
return true; // didn't hit it return CHECKTHING_NOCOLLIDE; // didn't hit it
if (tmthing->z > thing->z + thing->height) if (tmthing->z > thing->z + thing->height)
return true; // overhead return CHECKTHING_NOCOLLIDE; // overhead
if (tmthing->z + tmthing->height < thing->z) if (tmthing->z + tmthing->height < thing->z)
return true; // underneath return CHECKTHING_NOCOLLIDE; // underneath
thing->flags2 |= MF2_CLASSICPUSH; thing->flags2 |= MF2_CLASSICPUSH;
return true; return CHECKTHING_COLLIDE;
} }
if ((thing->flags & MF_NOCLIPTHING) || !(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING))) if ((thing->flags & MF_NOCLIPTHING) || !(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING)))
return true; return CHECKTHING_IGNORE;
// Don't collide with your buddies while NiGHTS-flying. // Don't collide with your buddies while NiGHTS-flying.
if (tmthing->player && thing->player && (maptol & TOL_NIGHTS) if (tmthing->player && thing->player && (maptol & TOL_NIGHTS)
&& ((tmthing->player->powers[pw_carry] == CR_NIGHTSMODE) || (thing->player->powers[pw_carry] == CR_NIGHTSMODE))) && ((tmthing->player->powers[pw_carry] == CR_NIGHTSMODE) || (thing->player->powers[pw_carry] == CR_NIGHTSMODE)))
return true; return CHECKTHING_IGNORE;
blockdist = thing->radius + tmthing->radius; blockdist = thing->radius + tmthing->radius;
if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
return true; // didn't hit it return CHECKTHING_NOCOLLIDE; // 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. 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.
{ {
@ -915,23 +920,23 @@ static boolean PIT_CheckThing(mobj_t *thing)
fixed_t tmcosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT)); fixed_t tmcosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
fixed_t tmsinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT)); fixed_t tmsinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
if (abs(thing->x - tmx) >= (abs(tmcosradius) + abs(cosradius)) || abs(thing->y - tmy) >= (abs(tmsinradius) + abs(sinradius))) if (abs(thing->x - tmx) >= (abs(tmcosradius) + abs(cosradius)) || abs(thing->y - tmy) >= (abs(tmsinradius) + abs(sinradius)))
return true; // didn't hit it return CHECKTHING_NOCOLLIDE; // didn't hit it
check1 = P_PointOnLineSide(tmx - tmcosradius, tmy - tmsinradius, &junk); check1 = P_PointOnLineSide(tmx - tmcosradius, tmy - tmsinradius, &junk);
check2 = P_PointOnLineSide(tmx + tmcosradius, tmy + tmsinradius, &junk); check2 = P_PointOnLineSide(tmx + tmcosradius, tmy + tmsinradius, &junk);
check3 = P_PointOnLineSide(tmx + tmthing->momx - tmcosradius, tmy + tmthing->momy - tmsinradius, &junk); check3 = P_PointOnLineSide(tmx + tmthing->momx - tmcosradius, tmy + tmthing->momy - tmsinradius, &junk);
check4 = P_PointOnLineSide(tmx + tmthing->momx + tmcosradius, tmy + tmthing->momy + tmsinradius, &junk); check4 = P_PointOnLineSide(tmx + tmthing->momx + tmcosradius, tmy + tmthing->momy + tmsinradius, &junk);
if ((check1 == check2) && (check2 == check3) && (check3 == check4)) if ((check1 == check2) && (check2 == check3) && (check3 == check4))
return true; // the line doesn't cross between collider's start or end return CHECKTHING_NOCOLLIDE; // the line doesn't cross between collider's start or end
} }
else else
{ {
if (abs(thing->x - tmx) >= (tmthing->radius + abs(cosradius)) || abs(thing->y - tmy) >= (tmthing->radius + abs(sinradius))) if (abs(thing->x - tmx) >= (tmthing->radius + abs(cosradius)) || abs(thing->y - tmy) >= (tmthing->radius + abs(sinradius)))
return true; // didn't hit it return CHECKTHING_NOCOLLIDE; // didn't hit it
if ((P_PointOnLineSide(tmx - tmthing->radius, tmy - tmthing->radius, &junk) 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) && (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 return CHECKTHING_NOCOLLIDE; // the line doesn't cross between either pair of opposite corners
} }
} }
else if (tmthing->flags & MF_PAPERCOLLISION) else if (tmthing->flags & MF_PAPERCOLLISION)
@ -944,7 +949,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
tmsinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT)); tmsinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
if (abs(thing->x - tmx) >= (thing->radius + abs(tmcosradius)) || abs(thing->y - tmy) >= (thing->radius + abs(tmsinradius))) if (abs(thing->x - tmx) >= (thing->radius + abs(tmcosradius)) || abs(thing->y - tmy) >= (thing->radius + abs(tmsinradius)))
return true; // didn't hit it return CHECKTHING_NOCOLLIDE; // didn't hit it
v1.x = tmx - tmcosradius; v1.x = tmx - tmcosradius;
v1.y = tmy - tmsinradius; v1.y = tmy - tmsinradius;
@ -961,32 +966,32 @@ static boolean PIT_CheckThing(mobj_t *thing)
== 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)
== 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 return CHECKTHING_NOCOLLIDE; // the line doesn't cross between either pair of opposite corners
} }
{ {
UINT8 shouldCollide = LUA_Hook2Mobj(thing, tmthing, MOBJ_HOOK(MobjCollide)); // checks hook for thing's type UINT8 shouldCollide = LUA_Hook2Mobj(thing, tmthing, MOBJ_HOOK(MobjCollide)); // checks hook for thing's type
if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing)) if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing))
return true; // one of them was removed??? return CHECKTHING_NOCOLLIDE; // one of them was removed???
if (shouldCollide == 1) if (shouldCollide == 1)
return false; // force collide return CHECKTHING_DONE; // force collide
else if (shouldCollide == 2) else if (shouldCollide == 2)
return true; // force no collide return CHECKTHING_NOCOLLIDE; // force no collide
shouldCollide = LUA_Hook2Mobj(tmthing, thing, MOBJ_HOOK(MobjMoveCollide)); // checks hook for tmthing's type shouldCollide = LUA_Hook2Mobj(tmthing, thing, MOBJ_HOOK(MobjMoveCollide)); // checks hook for tmthing's type
if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing)) if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing))
return true; // one of them was removed??? return CHECKTHING_NOCOLLIDE; // one of them was removed???
if (shouldCollide == 1) if (shouldCollide == 1)
return false; // force collide return CHECKTHING_DONE; // force collide
else if (shouldCollide == 2) else if (shouldCollide == 2)
return true; // force no collide return CHECKTHING_NOCOLLIDE; // force no collide
} }
if (tmthing->type == MT_LAVAFALL_LAVA && (thing->type == MT_RING || thing->type == MT_REDTEAMRING || thing->type == MT_BLUETEAMRING || thing->type == MT_FLINGRING)) if (tmthing->type == MT_LAVAFALL_LAVA && (thing->type == MT_RING || thing->type == MT_REDTEAMRING || thing->type == MT_BLUETEAMRING || thing->type == MT_FLINGRING))
{ {
//height check //height check
if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !(thing->health)) if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !(thing->health))
return true; return CHECKTHING_NOCOLLIDE;
P_KillMobj(thing, tmthing, tmthing, DMG_FIRE); P_KillMobj(thing, tmthing, tmthing, DMG_FIRE);
} }
@ -995,7 +1000,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
{ {
//height check //height check
if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !(thing->health)) if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !(thing->health))
return true; return CHECKTHING_NOCOLLIDE;
if (thing->type == MT_TNTBARREL) if (thing->type == MT_TNTBARREL)
P_KillMobj(thing, tmthing, tmthing->target, 0); P_KillMobj(thing, tmthing, tmthing->target, 0);
@ -1018,7 +1023,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
fixed_t s = FINESINE((ang >> ANGLETOFINESHIFT) & FINEMASK); fixed_t s = FINESINE((ang >> ANGLETOFINESHIFT) & FINEMASK);
S_StartSound(tmthing, thing->info->activesound); S_StartSound(tmthing, thing->info->activesound);
thing->extravalue2 += 2*FixedMul(s, dm)/3; thing->extravalue2 += 2*FixedMul(s, dm)/3;
return true; return CHECKTHING_COLLIDE;
} }
} }
@ -1026,14 +1031,14 @@ static boolean PIT_CheckThing(mobj_t *thing)
{ {
if (((thing->flags2 & MF2_AMBUSH) && (tmthing->z <= thing->z + thing->height) && (tmthing->z + tmthing->height >= thing->z)) if (((thing->flags2 & MF2_AMBUSH) && (tmthing->z <= thing->z + thing->height) && (tmthing->z + tmthing->height >= thing->z))
|| (tmthing->player->powers[pw_carry] == CR_MINECART && tmthing->tracer && !P_MobjWasRemoved(tmthing->tracer))) || (tmthing->player->powers[pw_carry] == CR_MINECART && tmthing->tracer && !P_MobjWasRemoved(tmthing->tracer)))
return true; return CHECKTHING_COLLIDE;
} }
if (thing->type == MT_ROLLOUTROCK && tmthing->player && tmthing->health) if (thing->type == MT_ROLLOUTROCK && tmthing->player && tmthing->health)
{ {
if (tmthing->player->powers[pw_carry] == CR_ROLLOUT) if (tmthing->player->powers[pw_carry] == CR_ROLLOUT)
{ {
return true; return CHECKTHING_NOCOLLIDE;
} }
if ((thing->flags & MF_PUSHABLE) // not carrying a player if ((thing->flags & MF_PUSHABLE) // not carrying a player
&& (tmthing->player->powers[pw_carry] == CR_NONE) // player is not already riding something && (tmthing->player->powers[pw_carry] == CR_NONE) // player is not already riding something
@ -1052,26 +1057,26 @@ static boolean PIT_CheckThing(mobj_t *thing)
P_SetTarget(&tmthing->tracer, thing); P_SetTarget(&tmthing->tracer, thing);
if (!P_IsObjectOnGround(thing)) if (!P_IsObjectOnGround(thing))
thing->momz += tmthing->momz; thing->momz += tmthing->momz;
return true; return CHECKTHING_COLLIDE;
} }
} }
else if (tmthing->type == MT_ROLLOUTROCK) else if (tmthing->type == MT_ROLLOUTROCK)
{ {
if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !thing->health) if (tmthing->z > thing->z + thing->height || thing->z > tmthing->z + tmthing->height || !thing->health)
return true; return CHECKTHING_NOCOLLIDE;
if (thing == tmthing->tracer) // don't collide with rider if (thing == tmthing->tracer) // don't collide with rider
return true; return CHECKTHING_IGNORE;
if (thing->flags & MF_SPRING) // bounce on springs if (thing->flags & MF_SPRING) // bounce on springs
{ {
P_DoSpring(thing, tmthing); P_DoSpring(thing, tmthing);
return true; return CHECKTHING_COLLIDE;
} }
else if ((thing->flags & (MF_MONITOR|MF_SHOOTABLE)) == (MF_MONITOR|MF_SHOOTABLE) && !(tmthing->flags & MF_PUSHABLE)) // pop monitors while carrying a player else if ((thing->flags & (MF_MONITOR|MF_SHOOTABLE)) == (MF_MONITOR|MF_SHOOTABLE) && !(tmthing->flags & MF_PUSHABLE)) // pop monitors while carrying a player
{ {
P_KillMobj(thing, tmthing, tmthing->tracer, 0); P_KillMobj(thing, tmthing, tmthing->tracer, 0);
return true; return CHECKTHING_COLLIDE;
} }
if (thing->type == tmthing->type // bounce against other rollout rocks if (thing->type == tmthing->type // bounce against other rollout rocks
@ -1106,11 +1111,11 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->type == MT_FANG && thing->type == MT_FSGNB) if (tmthing->type == MT_FANG && thing->type == MT_FSGNB)
{ {
if (thing->z > tmthing->z + tmthing->height) if (thing->z > tmthing->z + tmthing->height)
return true; // overhead return CHECKTHING_NOCOLLIDE; // overhead
if (thing->z + thing->height < tmthing->z) if (thing->z + thing->height < tmthing->z)
return true; // underneath return CHECKTHING_NOCOLLIDE; // underneath
if (!thing->tracer || !thing->tracer->tracer) if (!thing->tracer || !thing->tracer->tracer)
return true; return CHECKTHING_IGNORE;
P_SlapStick(tmthing, thing); P_SlapStick(tmthing, thing);
// no return value was used in the original prototype script at this point, // no return value was used in the original prototype script at this point,
// so I'm assuming we fall back on the solid code to determine how it all ends? // so I'm assuming we fall back on the solid code to determine how it all ends?
@ -1123,13 +1128,13 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->type == MT_BIGMINE) if (tmthing->type == MT_BIGMINE)
{ {
if (!tmthing->momx && !tmthing->momy) if (!tmthing->momx && !tmthing->momy)
return true; return CHECKTHING_IGNORE;
if ((statenum_t)(thing->state-states) >= thing->info->meleestate) if ((statenum_t)(thing->state-states) >= thing->info->meleestate)
return true; return CHECKTHING_IGNORE;
if (thing->z > tmthing->z + tmthing->height) if (thing->z > tmthing->z + tmthing->height)
return true; // overhead return CHECKTHING_NOCOLLIDE; // overhead
if (thing->z + thing->height < tmthing->z) if (thing->z + thing->height < tmthing->z)
return true; // underneath return CHECKTHING_NOCOLLIDE; // underneath
thing->momx = tmthing->momx/3; thing->momx = tmthing->momx/3;
thing->momy = tmthing->momy/3; thing->momy = tmthing->momy/3;
@ -1141,18 +1146,18 @@ static boolean PIT_CheckThing(mobj_t *thing)
S_StartSound(thing, thing->info->activesound); S_StartSound(thing, thing->info->activesound);
P_SetMobjState(thing, thing->info->meleestate); P_SetMobjState(thing, thing->info->meleestate);
P_SetTarget(&thing->tracer, tmthing->tracer); P_SetTarget(&thing->tracer, tmthing->tracer);
return true; return CHECKTHING_COLLIDE;
} }
else if (tmthing->type == MT_CRUSHCLAW) else if (tmthing->type == MT_CRUSHCLAW)
{ {
if (tmthing->extravalue1 <= 0) if (tmthing->extravalue1 <= 0)
return true; return CHECKTHING_IGNORE;
if ((statenum_t)(thing->state-states) >= thing->info->meleestate) if ((statenum_t)(thing->state-states) >= thing->info->meleestate)
return true; return CHECKTHING_IGNORE;
if (thing->z > tmthing->z + tmthing->height) if (thing->z > tmthing->z + tmthing->height)
return true; // overhead return CHECKTHING_NOCOLLIDE; // overhead
if (thing->z + thing->height < tmthing->z) if (thing->z + thing->height < tmthing->z)
return true; // underneath return CHECKTHING_NOCOLLIDE; // underneath
thing->momx = P_ReturnThrustX(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3); thing->momx = P_ReturnThrustX(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3);
thing->momy = P_ReturnThrustY(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3); thing->momy = P_ReturnThrustY(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3);
@ -1161,7 +1166,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
P_SetMobjState(thing, thing->info->meleestate); P_SetMobjState(thing, thing->info->meleestate);
if (tmthing->tracer) if (tmthing->tracer)
P_SetTarget(&thing->tracer, tmthing->tracer->target); P_SetTarget(&thing->tracer, tmthing->tracer->target);
return false; return CHECKTHING_DONE;
} }
} }
@ -1169,9 +1174,9 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->type == MT_SPIKE && (thing->flags & MF_SOLID) && (tmthing->flags & MF_SOLID)) if (tmthing->type == MT_SPIKE && (thing->flags & MF_SOLID) && (tmthing->flags & MF_SOLID))
{ {
if (thing->z > tmthing->z + tmthing->height) if (thing->z > tmthing->z + tmthing->height)
return true; // overhead return CHECKTHING_NOCOLLIDE; // overhead
if (thing->z + thing->height < tmthing->z) if (thing->z + thing->height < tmthing->z)
return true; // underneath return CHECKTHING_NOCOLLIDE; // underneath
if (tmthing->eflags & MFE_VERTICALFLIP) if (tmthing->eflags & MFE_VERTICALFLIP)
P_SetOrigin(thing, thing->x, thing->y, tmthing->z - thing->height - FixedMul(FRACUNIT, tmthing->scale)); P_SetOrigin(thing, thing->x, thing->y, tmthing->z - thing->height - FixedMul(FRACUNIT, tmthing->scale));
@ -1179,16 +1184,16 @@ static boolean PIT_CheckThing(mobj_t *thing)
P_SetOrigin(thing, thing->x, thing->y, tmthing->z + tmthing->height + FixedMul(FRACUNIT, tmthing->scale)); P_SetOrigin(thing, thing->x, thing->y, tmthing->z + tmthing->height + FixedMul(FRACUNIT, tmthing->scale));
if (thing->flags & MF_SHOOTABLE) if (thing->flags & MF_SHOOTABLE)
P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE); P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
return true; return CHECKTHING_COLLIDE;
} }
if (thing->flags & MF_PAIN && tmthing->player) if (thing->flags & MF_PAIN && tmthing->player)
{ // Player touches painful thing sitting on the floor { // Player touches painful thing sitting on the floor
// see if it went over / under // see if it went over / under
if (thing->z > tmthing->z + tmthing->height) if (thing->z > tmthing->z + tmthing->height)
return true; // overhead return CHECKTHING_NOCOLLIDE; // overhead
if (thing->z + thing->height < tmthing->z) if (thing->z + thing->height < tmthing->z)
return true; // underneath return CHECKTHING_NOCOLLIDE; // underneath
if (tmthing->flags & MF_SHOOTABLE && thing->health > 0) if (tmthing->flags & MF_SHOOTABLE && thing->health > 0)
{ {
UINT32 damagetype = (thing->info->mass & 0xFF); UINT32 damagetype = (thing->info->mass & 0xFF);
@ -1196,16 +1201,17 @@ static boolean PIT_CheckThing(mobj_t *thing)
damagetype = DMG_FIRE; damagetype = DMG_FIRE;
if (P_DamageMobj(tmthing, thing, thing, 1, damagetype) && (damagetype = (thing->info->mass>>8))) if (P_DamageMobj(tmthing, thing, thing, 1, damagetype) && (damagetype = (thing->info->mass>>8)))
S_StartSound(thing, damagetype); S_StartSound(thing, damagetype);
return CHECKTHING_COLLIDE;
} }
return true; return CHECKTHING_NOCOLLIDE;
} }
else if (tmthing->flags & MF_PAIN && thing->player) else if (tmthing->flags & MF_PAIN && thing->player)
{ // Painful thing splats player in the face { // Painful thing splats player in the face
// see if it went over / under // see if it went over / under
if (tmthing->z > thing->z + thing->height) if (tmthing->z > thing->z + thing->height)
return true; // overhead return CHECKTHING_NOCOLLIDE; // overhead
if (tmthing->z + tmthing->height < thing->z) if (tmthing->z + tmthing->height < thing->z)
return true; // underneath return CHECKTHING_NOCOLLIDE; // underneath
if (thing->flags & MF_SHOOTABLE && tmthing->health > 0) if (thing->flags & MF_SHOOTABLE && tmthing->health > 0)
{ {
UINT32 damagetype = (tmthing->info->mass & 0xFF); UINT32 damagetype = (tmthing->info->mass & 0xFF);
@ -1213,32 +1219,33 @@ static boolean PIT_CheckThing(mobj_t *thing)
damagetype = DMG_FIRE; damagetype = DMG_FIRE;
if (P_DamageMobj(thing, tmthing, tmthing, 1, damagetype) && (damagetype = (tmthing->info->mass>>8))) if (P_DamageMobj(thing, tmthing, tmthing, 1, damagetype) && (damagetype = (tmthing->info->mass>>8)))
S_StartSound(tmthing, damagetype); S_StartSound(tmthing, damagetype);
return CHECKTHING_COLLIDE;
} }
return true; return CHECKTHING_NOCOLLIDE;
} }
if (thing->type == MT_HOOPCOLLIDE && thing->flags & MF_SPECIAL && tmthing->player) if (thing->type == MT_HOOPCOLLIDE && thing->flags & MF_SPECIAL && tmthing->player)
{ {
P_TouchSpecialThing(thing, tmthing, true); P_TouchSpecialThing(thing, tmthing, true);
return true; return CHECKTHING_COLLIDE;
} }
// check for skulls slamming into things // check for skulls slamming into things
if (tmthing->flags2 & MF2_SKULLFLY) if (tmthing->flags2 & MF2_SKULLFLY)
{ {
if (tmthing->type == MT_EGGMOBILE) // Don't make Eggman stop! if (tmthing->type == MT_EGGMOBILE) // Don't make Eggman stop!
return true; // Let him RUN YOU RIGHT OVER. >:3 return CHECKTHING_COLLIDE; // Let him RUN YOU RIGHT OVER. >:3
else else
{ {
// see if it went over / under // see if it went over / under
if (tmthing->z > thing->z + thing->height) if (tmthing->z > thing->z + thing->height)
return true; // overhead return CHECKTHING_NOCOLLIDE; // overhead
if (tmthing->z + tmthing->height < thing->z) if (tmthing->z + tmthing->height < thing->z)
return true; // underneath return CHECKTHING_NOCOLLIDE; // underneath
tmthing->flags2 &= ~MF2_SKULLFLY; tmthing->flags2 &= ~MF2_SKULLFLY;
tmthing->momx = tmthing->momy = tmthing->momz = 0; tmthing->momx = tmthing->momy = tmthing->momz = 0;
return false; // stop moving return CHECKTHING_DONE; // stop moving
} }
} }
@ -1254,10 +1261,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
if ((tmznext <= thzh && tmz > thzh) || (tmznext > thzh - sprarea && tmznext < thzh)) if ((tmznext <= thzh && tmz > thzh) || (tmznext > thzh - sprarea && tmznext < thzh))
{ {
P_DoSpring(thing, tmthing); P_DoSpring(thing, tmthing);
return true; return CHECKTHING_COLLIDE;
} }
else if (tmz > thzh - sprarea && tmz < thzh) // Don't damage people springing up / down else if (tmz > thzh - sprarea && tmz < thzh) // Don't damage people springing up / down
return true; return CHECKTHING_NOCOLLIDE;
} }
// missiles can hit other things // missiles can hit other things
@ -1265,31 +1272,31 @@ static boolean PIT_CheckThing(mobj_t *thing)
{ {
// see if it went over / under // see if it went over / under
if (tmthing->z > thing->z + thing->height) if (tmthing->z > thing->z + thing->height)
return true; // overhead return CHECKTHING_NOCOLLIDE; // overhead
if (tmthing->z + tmthing->height < thing->z) if (tmthing->z + tmthing->height < thing->z)
return true; // underneath return CHECKTHING_NOCOLLIDE; // underneath
if (tmthing->type != MT_SHELL && tmthing->target && tmthing->target->type == thing->type) if (tmthing->type != MT_SHELL && tmthing->target && tmthing->target->type == thing->type)
{ {
// Don't hit same species as originator. // Don't hit same species as originator.
if (thing == tmthing->target) if (thing == tmthing->target)
return true; return CHECKTHING_IGNORE;
if (thing->type != MT_PLAYER) if (thing->type != MT_PLAYER)
{ {
// Explode, but do no damage. // Explode, but do no damage.
// Let players missile other players. // Let players missile other players.
return false; return CHECKTHING_DONE;
} }
} }
// Special case for bounce rings so they don't get caught behind solid objects. // Special case for bounce rings so they don't get caught behind solid objects.
if ((tmthing->type == MT_THROWNBOUNCE && tmthing->fuse > 8*TICRATE) && thing->flags & MF_SOLID) if ((tmthing->type == MT_THROWNBOUNCE && tmthing->fuse > 8*TICRATE) && thing->flags & MF_SOLID)
return true; return CHECKTHING_IGNORE;
// Missiles ignore Brak's helper. // Missiles ignore Brak's helper.
if (thing->type == MT_BLACKEGGMAN_HELPER) if (thing->type == MT_BLACKEGGMAN_HELPER)
return true; return CHECKTHING_IGNORE;
// Hurting Brak // Hurting Brak
if (tmthing->type == MT_BLACKEGGMAN_MISSILE if (tmthing->type == MT_BLACKEGGMAN_MISSILE
@ -1298,19 +1305,22 @@ static boolean PIT_CheckThing(mobj_t *thing)
// Not if Brak's already in pain // Not if Brak's already in pain
if (!(thing->state >= &states[S_BLACKEGG_PAIN1] && thing->state <= &states[S_BLACKEGG_PAIN35])) if (!(thing->state >= &states[S_BLACKEGG_PAIN1] && thing->state <= &states[S_BLACKEGG_PAIN35]))
P_SetMobjState(thing, thing->info->painstate); P_SetMobjState(thing, thing->info->painstate);
return false; return CHECKTHING_DONE;
} }
if (!(thing->flags & MF_SHOOTABLE) && !(thing->type == MT_EGGSHIELD)) if (!(thing->flags & MF_SHOOTABLE) && !(thing->type == MT_EGGSHIELD))
{ {
// didn't do any damage // didn't do any damage
return !(thing->flags & MF_SOLID); if (thing->flags & MF_SOLID)
return CHECKTHING_COLLIDE;
else
return CHECKTHING_NOCOLLIDE;
} }
if (tmthing->flags & MF_MISSILE && thing->player && tmthing->target && tmthing->target->player if (tmthing->flags & MF_MISSILE && thing->player && tmthing->target && tmthing->target->player
&& thing->player->ctfteam == tmthing->target->player->ctfteam && thing->player->ctfteam == tmthing->target->player->ctfteam
&& thing->player->powers[pw_carry] == CR_PLAYER && thing->tracer == tmthing->target) && thing->player->powers[pw_carry] == CR_PLAYER && thing->tracer == tmthing->target)
return true; // Don't give rings to your carry player by accident. return CHECKTHING_IGNORE; // Don't give rings to your carry player by accident.
if (thing->type == MT_EGGSHIELD) if (thing->type == MT_EGGSHIELD)
{ {
@ -1318,7 +1328,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (angle < ANGLE_180) // hit shield from behind, shield is destroyed! if (angle < ANGLE_180) // hit shield from behind, shield is destroyed!
P_KillMobj(thing, tmthing, tmthing, 0); P_KillMobj(thing, tmthing, tmthing, 0);
return false; return CHECKTHING_DONE;
} }
// damage / explode // damage / explode
@ -1344,7 +1354,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (!demoplayback || P_ControlStyle(thing->player) == CS_LMAOGALOG) if (!demoplayback || P_ControlStyle(thing->player) == CS_LMAOGALOG)
P_SetPlayerAngle(thing->player, thing->angle); P_SetPlayerAngle(thing->player, thing->angle);
return true; return CHECKTHING_COLLIDE;
} }
else if (tmthing->type == MT_BLACKEGGMAN_MISSILE && thing->player && ((thing->player->powers[pw_carry] == CR_GENERIC) || (thing->player->pflags & PF_JUMPED))) else if (tmthing->type == MT_BLACKEGGMAN_MISSILE && thing->player && ((thing->player->powers[pw_carry] == CR_GENERIC) || (thing->player->pflags & PF_JUMPED)))
{ {
@ -1371,11 +1381,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
P_KillMobj(tmthing, NULL, NULL, 0); P_KillMobj(tmthing, NULL, NULL, 0);
// don't traverse any more // don't traverse any more
if (tmthing->type == MT_SHELL) if (tmthing->type == MT_SHELL)
return true; return CHECKTHING_COLLIDE;
else else
return false; return CHECKTHING_DONE;
} }
if (thing->flags & MF_PUSHABLE && (tmthing->player || tmthing->flags & MF_PUSHABLE) if (thing->flags & MF_PUSHABLE && (tmthing->player || tmthing->flags & MF_PUSHABLE)
@ -1478,13 +1487,13 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (thing->flags & MF_SPECIAL && tmthing->player) if (thing->flags & MF_SPECIAL && tmthing->player)
{ {
P_TouchSpecialThing(thing, tmthing, true); // can remove thing P_TouchSpecialThing(thing, tmthing, true); // can remove thing
return true; return CHECKTHING_COLLIDE;
} }
// check again for special pickup // check again for special pickup
if (tmthing->flags & MF_SPECIAL && thing->player) if (tmthing->flags & MF_SPECIAL && thing->player)
{ {
P_TouchSpecialThing(tmthing, thing, true); // can remove thing P_TouchSpecialThing(tmthing, thing, true); // can remove thing
return true; return CHECKTHING_COLLIDE;
} }
// Sprite Spikes! // Sprite Spikes!
@ -1544,7 +1553,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (playerangle > ANGLE_180) if (playerangle > ANGLE_180)
playerangle = InvAngle(playerangle); playerangle = InvAngle(playerangle);
if (playerangle < ANGLE_90) if (playerangle < ANGLE_90)
return true; // Yes, this is intentionally outside the z-height check. No standing on spikes whilst moving away from them. return CHECKTHING_IGNORE; // Yes, this is intentionally outside the z-height check. No standing on spikes whilst moving away from them.
} }
bottomz = thing->z; bottomz = thing->z;
@ -1578,15 +1587,15 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (thing->type == MT_FAN || thing->type == MT_STEAM) if (thing->type == MT_FAN || thing->type == MT_STEAM)
{ {
P_DoFanAndGasJet(thing, tmthing); P_DoFanAndGasJet(thing, tmthing);
return true; return CHECKTHING_COLLIDE;
} }
else if (thing->flags & MF_SPRING) else if (thing->flags & MF_SPRING)
{ {
if ( thing->z <= tmthing->z + tmthing->height if ( thing->z <= tmthing->z + tmthing->height
&& tmthing->z <= thing->z + thing->height) && tmthing->z <= thing->z + thing->height)
if (P_DoSpring(thing, tmthing)) if (P_DoSpring(thing, tmthing))
return false; return CHECKTHING_DONE;
return true; return CHECKTHING_COLLIDE;
} }
} }
@ -1595,7 +1604,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
{ {
if ((thing->z + thing->height >= tmthing->z) if ((thing->z + thing->height >= tmthing->z)
&& (tmthing->z + tmthing->height >= thing->z)) && (tmthing->z + tmthing->height >= thing->z))
return false; return CHECKTHING_DONE;
} }
// Damage other players when invincible // Damage other players when invincible
@ -1630,7 +1639,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->player && thing->player) if (tmthing->player && thing->player)
{ {
P_DoTailsCarry(thing->player, tmthing->player); P_DoTailsCarry(thing->player, tmthing->player);
return true; return CHECKTHING_COLLIDE;
} }
} }
else if (thing->player) { else if (thing->player) {
@ -1677,7 +1686,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->player) // Is the moving/interacting object the player? if (tmthing->player) // Is the moving/interacting object the player?
{ {
if (!tmthing->health) if (!tmthing->health)
return true; return CHECKTHING_IGNORE;
if (thing->type == MT_FAN || thing->type == MT_STEAM) if (thing->type == MT_FAN || thing->type == MT_STEAM)
P_DoFanAndGasJet(thing, tmthing); P_DoFanAndGasJet(thing, tmthing);
@ -1686,14 +1695,16 @@ static boolean PIT_CheckThing(mobj_t *thing)
if ( thing->z <= tmthing->z + tmthing->height if ( thing->z <= tmthing->z + tmthing->height
&& tmthing->z <= thing->z + thing->height) && tmthing->z <= thing->z + thing->height)
if (P_DoSpring(thing, tmthing)) if (P_DoSpring(thing, tmthing))
return false; return CHECKTHING_DONE;
return true; return CHECKTHING_COLLIDE;
} }
// Monitor? // Monitor?
else if (thing->flags & MF_MONITOR else if (thing->flags & MF_MONITOR
&& !((thing->type == MT_RING_REDBOX && tmthing->player->ctfteam != 1) || (thing->type == MT_RING_BLUEBOX && tmthing->player->ctfteam != 2)) && !((thing->type == MT_RING_REDBOX && tmthing->player->ctfteam != 1) || (thing->type == MT_RING_BLUEBOX && tmthing->player->ctfteam != 2))
&& (!(thing->flags & MF_SOLID) || P_PlayerCanDamage(tmthing->player, thing))) && (!(thing->flags & MF_SOLID) || P_PlayerCanDamage(tmthing->player, thing)))
{ {
unsigned collide = CHECKTHING_NOCOLLIDE;
if (thing->z - thing->scale <= tmthing->z + tmthing->height if (thing->z - thing->scale <= tmthing->z + tmthing->height
&& thing->z + thing->height + thing->scale >= tmthing->z) && thing->z + thing->height + thing->scale >= tmthing->z)
{ {
@ -1725,21 +1736,25 @@ static boolean PIT_CheckThing(mobj_t *thing)
*momz /= 2; *momz /= 2;
*momz -= (*momz/(underwater ? 8 : 4)); // Cap the height! *momz -= (*momz/(underwater ? 8 : 4)); // Cap the height!
} }
collide = CHECKTHING_COLLIDE;
} }
if (!(elementalpierce == 1 && thing->flags & MF_GRENADEBOUNCE)) // prevent gold monitor clipthrough. if (!(elementalpierce == 1 && thing->flags & MF_GRENADEBOUNCE)) // prevent gold monitor clipthrough.
{ {
if (player->pflags & PF_BOUNCING) if (player->pflags & PF_BOUNCING)
P_DoAbilityBounce(player, false); P_DoAbilityBounce(player, false);
return false; collide = CHECKTHING_DONE;
} }
else else
*z -= *momz; // to ensure proper collision. *z -= *momz; // to ensure proper collision.
} }
return true; return collide;
} }
} }
// not solid not blocked
unsigned collide = CHECKTHING_NOCOLLIDE;
if ((tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM || tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) && (thing->player)) if ((tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM || tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) && (thing->player))
; // springs, gas jets and springs should never be able to step up onto a player ; // springs, gas jets and springs should never be able to step up onto a player
// z checking at last // z checking at last
@ -1762,25 +1777,27 @@ static boolean PIT_CheckThing(mobj_t *thing)
tmfloorrover = NULL; tmfloorrover = NULL;
tmfloorslope = NULL; tmfloorslope = NULL;
} }
return true; return CHECKTHING_COLLIDE;
} }
topz = thing->z - thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways topz = thing->z - thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways
// block only when jumping not high enough, // block only when jumping not high enough,
// (dont climb max. 24units while already in air) // (dont climb max. 24units while already in air)
// since return false doesn't handle momentum properly, // since return CHECKTHING_DONE doesn't handle momentum properly,
// we lie to P_TryMove() so it's always too high // we lie to P_TryMove() so it's always too high
if (tmthing->player && tmthing->z + tmthing->height > topz if (tmthing->player && tmthing->z + tmthing->height > topz
&& tmthing->z + tmthing->height < tmthing->ceilingz) && tmthing->z + tmthing->height < tmthing->ceilingz)
{ {
if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->info->flags & MF_MONITOR)) // Gold monitor hack... if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->info->flags & MF_MONITOR)) // Gold monitor hack...
return false; return CHECKTHING_DONE;
tmfloorz = tmceilingz = topz; // block while in air tmfloorz = tmceilingz = topz; // block while in air
tmceilingrover = NULL; tmceilingrover = NULL;
tmceilingslope = NULL; tmceilingslope = NULL;
tmfloorthing = thing; // needed for side collision tmfloorthing = thing; // needed for side collision
collide = CHECKTHING_COLLIDE;
} }
else if (topz < tmceilingz && tmthing->z <= thing->z+thing->height) else if (topz < tmceilingz && tmthing->z <= thing->z+thing->height)
{ {
@ -1788,6 +1805,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
tmceilingrover = NULL; tmceilingrover = NULL;
tmceilingslope = NULL; tmceilingslope = NULL;
tmfloorthing = thing; // thing we may stand on tmfloorthing = thing; // thing we may stand on
collide = CHECKTHING_COLLIDE;
} }
} }
else else
@ -1803,25 +1822,27 @@ static boolean PIT_CheckThing(mobj_t *thing)
tmceilingrover = NULL; tmceilingrover = NULL;
tmceilingslope = NULL; tmceilingslope = NULL;
} }
return true; return CHECKTHING_COLLIDE;
} }
topz = thing->z + thing->height + thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways topz = thing->z + thing->height + thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways
// block only when jumping not high enough, // block only when jumping not high enough,
// (dont climb max. 24units while already in air) // (dont climb max. 24units while already in air)
// since return false doesn't handle momentum properly, // since return CHECKTHING_DONE doesn't handle momentum properly,
// we lie to P_TryMove() so it's always too high // we lie to P_TryMove() so it's always too high
if (tmthing->player && tmthing->z < topz if (tmthing->player && tmthing->z < topz
&& tmthing->z > tmthing->floorz) && tmthing->z > tmthing->floorz)
{ {
if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->info->flags & MF_MONITOR)) // Gold monitor hack... if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->info->flags & MF_MONITOR)) // Gold monitor hack...
return false; return CHECKTHING_DONE;
tmfloorz = tmceilingz = topz; // block while in air tmfloorz = tmceilingz = topz; // block while in air
tmfloorrover = NULL; tmfloorrover = NULL;
tmfloorslope = NULL; tmfloorslope = NULL;
tmfloorthing = thing; // needed for side collision tmfloorthing = thing; // needed for side collision
collide = CHECKTHING_COLLIDE;
} }
else if (topz > tmfloorz && tmthing->z+tmthing->height >= thing->z) else if (topz > tmfloorz && tmthing->z+tmthing->height >= thing->z)
{ {
@ -1829,12 +1850,18 @@ static boolean PIT_CheckThing(mobj_t *thing)
tmfloorrover = NULL; tmfloorrover = NULL;
tmfloorslope = NULL; tmfloorslope = NULL;
tmfloorthing = thing; // thing we may stand on tmfloorthing = thing; // thing we may stand on
collide = CHECKTHING_COLLIDE;
} }
} }
} }
// not solid not blocked return collide;
return true; }
static boolean PIT_CheckThing(mobj_t *thing)
{
return PIT_DoCheckThing(thing) != CHECKTHING_DONE;
} }
// PIT_CheckCameraLine // PIT_CheckCameraLine
@ -2010,38 +2037,6 @@ static boolean PIT_CheckLine(line_t *ld)
// ========================================================================= // =========================================================================
// MOVEMENT CLIPPING // MOVEMENT CLIPPING
// ========================================================================= // =========================================================================
//
// P_CheckPosition
// This is purely informative, nothing is modified
// (except things picked up).
//
// in:
// a mobj_t (can be valid or invalid)
// a position to be checked
// (doesn't need to be related to the mobj_t->x,y)
//
// during:
// special things are touched if MF_PICKUP
// early out on solid lines?
//
// out:
// newsubsec
// tmfloorz
// tmceilingz
// tmdropoffz
// tmdrpoffceilz
// the lowest point contacted
// (monsters won't move to a dropoff)
// speciallines[]
// numspeciallines
//
// tmfloorz
// the nearest floor or thing's top under tmthing
// tmceilingz
// the nearest ceiling or thing's bottom over tmthing
//
boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
{ {
INT32 xl, xh, yl, yh, bx, by; INT32 xl, xh, yl, yh, bx, by;
@ -2282,9 +2277,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
if (tmflags & MF_NOCLIP) if (tmflags & MF_NOCLIP)
return true; return true;
// Check things first, possibly picking things up. // Check things first.
// MF_NOCLIPTHING: used by camera to not be blocked by things
if (!(thing->flags & MF_NOCLIPTHING)) if (!(thing->flags & MF_NOCLIPTHING))
{ {
for (bx = xl; bx <= xh; bx++) for (bx = xl; bx <= xh; bx++)
@ -2893,7 +2886,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
// standing on top and move it, too. // standing on top and move it, too.
if (thing->flags & MF_PUSHABLE) if (thing->flags & MF_PUSHABLE)
{ {
INT32 bx, by, xl, xh, yl, yh; INT32 xl, xh, yl, yh;
yh = (unsigned)(thing->y + MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT; yh = (unsigned)(thing->y + MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT;
yl = (unsigned)(thing->y - MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT; yl = (unsigned)(thing->y - MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT;
@ -2906,9 +2899,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
standx = x; standx = x;
standy = y; standy = y;
for (by = yl; by <= yh; by++) P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushableMoved);
for (bx = xl; bx <= xh; bx++)
P_BlockThingsIterator(bx, by, PIT_PushableMoved);
} }
// Link the thing into its new position // Link the thing into its new position
@ -4217,7 +4208,6 @@ static boolean PIT_RadiusAttack(mobj_t *thing)
// //
void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 damagetype, boolean sightcheck) void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 damagetype, boolean sightcheck)
{ {
INT32 x, y;
INT32 xl, xh, yl, yh; INT32 xl, xh, yl, yh;
fixed_t dist; fixed_t dist;
@ -4235,9 +4225,7 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama
bombdamagetype = damagetype; bombdamagetype = damagetype;
bombsightcheck = sightcheck; bombsightcheck = sightcheck;
for (y = yl; y <= yh; y++) P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_RadiusAttack);
for (x = xl; x <= xh; x++)
P_BlockThingsIterator(x, y, PIT_RadiusAttack);
} }
// //
@ -4392,16 +4380,18 @@ static boolean P_CheckSectorPolyObjects(sector_t *sector, boolean realcrush, boo
for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x) for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x)
{ {
mobj_t *mo; mobj_t *mo;
blocknode_t *block;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
continue; continue;
mo = blocklinks[y * bmapwidth + x]; block = blocklinks[y * bmapwidth + x];
for (; mo; mo = mo->bnext) for (; block; block = block->mnext)
{ {
// Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect mo = block->mobj;
// Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect
if (!P_MobjInsidePolyobj(po, mo)) if (!P_MobjInsidePolyobj(po, mo))
continue; continue;

View file

@ -680,6 +680,35 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
openrange = opentop - openbottom; openrange = opentop - openbottom;
} }
static blocknode_t *freeblocks;
static blocknode_t *P_CreateBlockNode(mobj_t *thing, int x, int y)
{
blocknode_t *block;
if (freeblocks != NULL)
{
block = freeblocks;
freeblocks = block->bnext;
}
else
block = Z_Malloc(sizeof(blocknode_t), PU_LEVEL, NULL);
block->blockindex = x + y*bmapwidth;
block->mobj = thing;
block->mnext = NULL;
block->mprev = NULL;
block->bprev = NULL;
block->bnext = NULL;
return block;
}
static void P_ReleaseBlockNode(blocknode_t *node)
{
node->bnext = freeblocks;
freeblocks = node;
}
// //
// THING POSITION SETTING // THING POSITION SETTING
@ -730,20 +759,20 @@ void P_UnsetThingPosition(mobj_t *thing)
if (!(thing->flags & MF_NOBLOCKMAP)) if (!(thing->flags & MF_NOBLOCKMAP))
{ {
/* inert things don't need to be in blockmap // [RH] Unlink from all blocks this actor uses
* blocknode_t *block = thing->blocknode;
* killough 8/11/98: simpler scheme using pointers-to-pointers for prev
* pointers, allows head node pointers to be treated like everything else
*
* Also more robust, since it doesn't depend on current position for
* unlinking. Old method required computing head node based on position
* at time of unlinking, assuming it was the same position as during
* linking.
*/
mobj_t *bnext, **bprev = thing->bprev; while (block != NULL)
if (bprev && (*bprev = bnext = thing->bnext) != NULL) // unlink from block map {
bnext->bprev = bprev; if (block->mnext != NULL)
block->mnext->mprev = block->mprev;
*(block->mprev) = block->mnext;
blocknode_t *next = block->bnext;
P_ReleaseBlockNode(block);
block = next;
}
thing->blocknode = NULL;
} }
} }
@ -814,24 +843,45 @@ void P_SetThingPosition(mobj_t *thing)
if (!(thing->flags & MF_NOBLOCKMAP)) if (!(thing->flags & MF_NOBLOCKMAP))
{ {
// inert things don't need to be in blockmap // inert things don't need to be in blockmap
const INT32 blockx = (unsigned)(thing->x - bmaporgx)>>MAPBLOCKSHIFT; INT32 x1 = (unsigned)(thing->x - thing->radius - bmaporgx)>>MAPBLOCKSHIFT;
const INT32 blocky = (unsigned)(thing->y - bmaporgy)>>MAPBLOCKSHIFT; INT32 y1 = (unsigned)(thing->y - thing->radius - bmaporgy)>>MAPBLOCKSHIFT;
if (blockx >= 0 && blockx < bmapwidth INT32 x2 = (unsigned)(thing->x + thing->radius - bmaporgx)>>MAPBLOCKSHIFT;
&& blocky >= 0 && blocky < bmapheight) INT32 y2 = (unsigned)(thing->y + thing->radius - bmaporgy)>>MAPBLOCKSHIFT;
{
// killough 8/11/98: simpler scheme using
// pointer-to-pointer prev pointers --
// allows head nodes to be treated like everything else
mobj_t **link = &blocklinks[blocky*bmapwidth + blockx]; thing->blocknode = NULL;
mobj_t *bnext = *link;
if ((thing->bnext = bnext) != NULL) blocknode_t **alink = &thing->blocknode;
bnext->bprev = &thing->bnext;
thing->bprev = link; if (!(x1 >= bmapwidth || x2 < 0 || y1 >= bmapheight || y2 < 0))
*link = thing; {
// [RH] Link into every block this actor touches, not just the center one
x1 = max(0, x1);
y1 = max(0, y1);
x2 = min(bmapwidth - 1, x2);
y2 = min(bmapheight - 1, y2);
for (int y = y1; y <= y2; ++y)
{
for (int x = x1; x <= x2; ++x)
{
blocknode_t **link = &blocklinks[y*bmapwidth + x];
blocknode_t *node = P_CreateBlockNode(thing, x, y);
// Link in to block
if ((node->mnext = *link) != NULL)
{
(*link)->mprev = &node->mnext;
}
node->mprev = link;
*link = node;
// Link in to actor
node->bprev = alink;
node->bnext = NULL;
(*alink) = node;
alink = &node->bnext;
}
}
} }
else // thing is off the map
thing->bnext = NULL, thing->bprev = NULL;
} }
// Allows you to 'step' on a new linedef exec when the previous // Allows you to 'step' on a new linedef exec when the previous
@ -971,36 +1021,222 @@ boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *))
return true; // Everything was checked. return true; // Everything was checked.
} }
// //
// P_BlockThingsIterator // P_BlockThingsIterator
// //
boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *)) boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *))
{ {
mobj_t *mobj, *bnext = NULL; mobj_t *mobj;
blocknode_t *block;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
return true; return true;
// Check interaction with the objects in the blockmap. // Check interaction with the objects in the blockmap.
for (mobj = blocklinks[y*bmapwidth + x]; mobj; mobj = bnext) for (block = blocklinks[y*bmapwidth + x]; block; block = block->mnext)
{ {
P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed! mobj = block->mobj;
if (!func(mobj)) if (!func(mobj))
{
P_SetTarget(&bnext, NULL);
return false; return false;
} if (P_MobjWasRemoved(tmthing)) // func just broke blockmap chain, cannot continue.
if (P_MobjWasRemoved(tmthing) // func just popped our tmthing, cannot continue.
|| (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
{
P_SetTarget(&bnext, NULL);
return true; return true;
}
} }
return true; return true;
} }
boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *))
{
boolean status = true;
for (INT32 bx = x1; bx <= x2; bx++)
for (INT32 by = y1; by <= y2; by++)
if (!P_BlockThingsIterator(bx, by, func))
status = false;
return status;
}
static bthingit_hash_entry_t *GetHashEntryForIterator(bthingit_t *it, int i)
{
if (i < NUM_BTHINGIT_FIXEDHASH)
return &it->fixedhash[i];
else
return &it->dynhash[i - NUM_BTHINGIT_FIXEDHASH];
}
static blocknode_t *GetBlockmapBlock(int x, int y)
{
if (x >= 0 && y >= 0 && x < bmapwidth && y < bmapheight)
{
return blocklinks[y*bmapwidth + x];
}
else
{
// invalid block
return NULL;
}
}
static bthingit_t *freeiters;
bthingit_t *P_NewBlockThingsIterator(int x1, int y1, int x2, int y2)
{
bthingit_t *it;
blocknode_t *block;
x1 = max(0, x1);
y1 = max(0, y1);
x2 = min(bmapwidth - 1, x2);
y2 = min(bmapheight - 1, y2);
if (x1 > x2 || y1 > y2)
return NULL;
block = GetBlockmapBlock(x1, y1);
if (!block)
return NULL;
if (freeiters != NULL)
{
it = freeiters;
freeiters = it->freechain;
}
else
it = Z_Calloc(sizeof(bthingit_t), PU_LEVEL, NULL);
it->x1 = x1;
it->y1 = y1;
it->x2 = x2;
it->y2 = y2;
it->curx = x1;
it->cury = y1;
it->block = block;
it->freechain = NULL;
it->numfixedhash = 0;
it->dynhashcount = 0;
for (size_t i = 0; i < NUM_BTHINGIT_BUCKETS; i++)
it->buckets[i] = -1;
return it;
}
mobj_t *P_BlockThingsIteratorNext(bthingit_t *it, boolean centeronly)
{
for (;;)
{
while (it->block != NULL)
{
mobj_t *mobj = it->block->mobj;
blocknode_t *node = it->block;
it->block = it->block->mnext;
// Don't recheck things that were already checked
if (node->bnext == NULL && node->bprev == &mobj->blocknode)
{
// This actor doesn't span blocks, so we know it can only ever be checked once.
return mobj;
}
else
{
// Block boundaries for compatibility mode
if (centeronly)
{
fixed_t blockleft = (it->curx * MAPBLOCKUNITS) + bmaporgx;
fixed_t blockright = blockleft + MAPBLOCKUNITS;
fixed_t blockbottom = (it->cury * MAPBLOCKUNITS) + bmaporgy;
fixed_t blocktop = blockbottom + MAPBLOCKUNITS;
// only return actors with the center in this block
if (mobj->x >= blockleft && mobj->x < blockright &&
mobj->y >= blockbottom && mobj->y < blocktop)
{
return mobj;
}
}
else
{
bthingit_hash_entry_t *entry;
int i;
size_t hash = ((size_t)mobj >> 3) % NUM_BTHINGIT_BUCKETS;
for (i = it->buckets[hash]; i >= 0; )
{
entry = GetHashEntryForIterator(it, i);
if (entry->mobj == mobj)
{
// I've already been checked. Skip to the next mobj.
break;
}
i = entry->next;
}
if (i < 0)
{
// Add mobj to the hash table and return it.
if (it->numfixedhash < NUM_BTHINGIT_FIXEDHASH)
{
entry = &it->fixedhash[it->numfixedhash];
entry->next = it->buckets[hash];
it->buckets[hash] = it->numfixedhash++;
}
else
{
if (!it->dynhash)
{
it->dynhashcapacity = 50;
Z_Calloc(it->dynhashcapacity * sizeof(it->dynhashcapacity), PU_LEVEL, &it->dynhash);
}
if (it->dynhashcount == it->dynhashcapacity)
{
it->dynhashcapacity *= 2;
it->dynhash = Z_Realloc(it->dynhash, it->dynhashcapacity * sizeof(it->dynhashcapacity), PU_LEVEL, &it->dynhash);
}
i = (int)it->dynhashcount;
it->dynhashcount++;
entry = &it->dynhash[i];
entry->next = it->buckets[hash];
it->buckets[hash] = i + NUM_BTHINGIT_FIXEDHASH;
}
entry->mobj = mobj;
return mobj;
}
}
}
}
if (++it->curx > it->x2)
{
it->curx = it->x1;
if (++it->cury > it->y2)
return NULL;
}
it->block = GetBlockmapBlock(it->curx, it->cury);
}
return NULL;
}
void P_FreeBlockThingsIterator(bthingit_t *it)
{
if (it)
{
it->freechain = freeiters;
freeiters = it;
}
}
void P_ClearBlockNodes(void)
{
freeblocks = NULL;
freeiters = NULL;
}
// //
// INTERCEPT ROUTINES // INTERCEPT ROUTINES
// //

View file

@ -63,6 +63,39 @@ void P_LineOpening(line_t *plinedef, mobj_t *mobj);
boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean(*func)(line_t *)); boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean(*func)(line_t *));
boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean(*func)(mobj_t *)); boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean(*func)(mobj_t *));
void P_ClearBlockNodes(void);
typedef struct
{
mobj_t *mobj;
int next;
} bthingit_hash_entry_t;
#define NUM_BTHINGIT_BUCKETS 32
#define NUM_BTHINGIT_FIXEDHASH 10
typedef struct bthingit_s
{
int x1, y1, x2, y2;
int curx, cury;
blocknode_t *block;
int buckets[NUM_BTHINGIT_BUCKETS];
bthingit_hash_entry_t fixedhash[NUM_BTHINGIT_FIXEDHASH];
int numfixedhash;
bthingit_hash_entry_t *dynhash;
size_t dynhashcount;
size_t dynhashcapacity;
struct bthingit_s *freechain;
} bthingit_t;
bthingit_t *P_NewBlockThingsIterator(int x1, int y1, int x2, int y2);
mobj_t *P_BlockThingsIteratorNext(bthingit_t *it, boolean centeronly);
void P_FreeBlockThingsIterator(bthingit_t *it);
boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *));
#define PT_ADDLINES 1 #define PT_ADDLINES 1
#define PT_ADDTHINGS 2 #define PT_ADDTHINGS 2
#define PT_EARLYOUT 4 #define PT_EARLYOUT 4

View file

@ -9455,7 +9455,7 @@ static inline boolean PIT_PushThing(mobj_t *thing)
static void P_PointPushThink(mobj_t *mobj) static void P_PointPushThink(mobj_t *mobj)
{ {
INT32 xl, xh, yl, yh, bx, by; INT32 xl, xh, yl, yh;
fixed_t radius; fixed_t radius;
if (!mobj->spawnpoint) if (!mobj->spawnpoint)
@ -9470,9 +9470,8 @@ static void P_PointPushThink(mobj_t *mobj)
xh = (unsigned)(mobj->x + radius - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; xh = (unsigned)(mobj->x + radius - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
yl = (unsigned)(mobj->y - radius - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; yl = (unsigned)(mobj->y - radius - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
yh = (unsigned)(mobj->y + radius - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; yh = (unsigned)(mobj->y + radius - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
for (bx = xl; bx <= xh; bx++)
for (by = yl; by <= yh; by++) P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushThing);
P_BlockThingsIterator(bx, by, PIT_PushThing);
} }
static boolean P_MobjRegularThink(mobj_t *mobj) static boolean P_MobjRegularThink(mobj_t *mobj)

View file

@ -273,6 +273,19 @@ typedef enum {
PCF_THUNK = 32, PCF_THUNK = 32,
} precipflag_t; } precipflag_t;
// [RH] Like msecnode_t, but for the blockmap
typedef struct blocknode_s
{
struct mobj_s *mobj;
int blockindex; // index into blocklinks for the block this node is in
struct blocknode_s **mprev; // previous actor in this block
struct blocknode_s *mnext; // next actor in this block
struct blocknode_s **bprev; // previous block this actor is in
struct blocknode_s *bnext; // next block this actor is in
} blocknode_t;
// Map Object definition. // Map Object definition.
typedef struct mobj_s typedef struct mobj_s
{ {
@ -344,8 +357,7 @@ typedef struct mobj_s
// Interaction info, by BLOCKMAP. // Interaction info, by BLOCKMAP.
// Links in blocks (if needed). // Links in blocks (if needed).
struct mobj_s *bnext; blocknode_t *blocknode;
struct mobj_s **bprev; // killough 8/11/98: change to ptr-to-ptr
// Additional pointers for NiGHTS hoops // Additional pointers for NiGHTS hoops
struct mobj_s *hnext; struct mobj_s *hnext;

View file

@ -877,14 +877,17 @@ static void Polyobj_carryThings(polyobj_t *po, fixed_t dx, fixed_t dy)
for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x) for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x)
{ {
mobj_t *mo; mobj_t *mo;
blocknode_t *block;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
continue; continue;
mo = blocklinks[y * bmapwidth + x]; block = blocklinks[y * bmapwidth + x];
for (; mo; mo = mo->bnext) for (; block; block = block->mnext)
{ {
mo = block->mobj;
if (mo->lastlook == pomovecount) if (mo->lastlook == pomovecount)
continue; continue;
@ -937,10 +940,12 @@ static INT32 Polyobj_clipThings(polyobj_t *po, line_t *line)
{ {
if (!(x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)) if (!(x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight))
{ {
mobj_t *mo = blocklinks[y * bmapwidth + x]; mobj_t *mo = NULL;
blocknode_t *block = blocklinks[y * bmapwidth + x];
for (; mo; mo = mo->bnext) for (; block; block = block->mnext)
{ {
mo = block->mobj;
// Don't scroll objects that aren't affected by gravity // Don't scroll objects that aren't affected by gravity
if (mo->flags & MF_NOGRAVITY) if (mo->flags & MF_NOGRAVITY)
@ -1109,14 +1114,17 @@ static void Polyobj_rotateThings(polyobj_t *po, vector2_t origin, angle_t delta,
for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x) for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x)
{ {
mobj_t *mo; mobj_t *mo;
blocknode_t *block;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
continue; continue;
mo = blocklinks[y * bmapwidth + x]; block = blocklinks[y * bmapwidth + x];
for (; mo; mo = mo->bnext) for (; block; block = block->mnext)
{ {
mo = block->mobj;
if (mo->lastlook == pomovecount) if (mo->lastlook == pomovecount)
continue; continue;

View file

@ -138,7 +138,7 @@ INT32 *blockmaplump; // Big blockmap
// origin of block map // origin of block map
fixed_t bmaporgx, bmaporgy; fixed_t bmaporgx, bmaporgy;
// for thing chains // for thing chains
mobj_t **blocklinks; blocknode_t **blocklinks;
// REJECT // REJECT
// For fast sight rejection. // For fast sight rejection.
@ -7869,6 +7869,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
Z_Free(ss->attachedsolid); Z_Free(ss->attachedsolid);
} }
P_ClearBlockNodes();
// Clear pointers that would be left dangling by the purge // Clear pointers that would be left dangling by the purge
R_FlushTranslationColormapCache(); R_FlushTranslationColormapCache();

View file

@ -3807,6 +3807,8 @@ static boolean PIT_CheckSolidsTeeter(mobj_t *thing)
if (abs(thing->x - teeterer->x) >= blockdist || abs(thing->y - teeterer->y) >= blockdist) if (abs(thing->x - teeterer->x) >= blockdist || abs(thing->y - teeterer->y) >= blockdist)
return true; // didn't hit it return true; // didn't hit it
highesttop = INT32_MIN;
if (teeterer->eflags & MFE_VERTICALFLIP) if (teeterer->eflags & MFE_VERTICALFLIP)
{ {
if (thingtop < teeterer->z) if (thingtop < teeterer->z)
@ -4110,13 +4112,8 @@ static void P_DoTeeter(player_t *player)
teeteryl = teeteryh = player->mo->y; teeteryl = teeteryh = player->mo->y;
couldteeter = false; couldteeter = false;
solidteeter = teeter; solidteeter = teeter;
for (by = yl; by <= yh; by++) if (!P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_CheckSolidsTeeter))
for (bx = xl; bx <= xh; bx++) goto teeterdone; // we've found something that stops us teetering at all
{
highesttop = INT32_MIN;
if (!P_BlockThingsIterator(bx, by, PIT_CheckSolidsTeeter))
goto teeterdone; // we've found something that stops us teetering at all, how about we stop already
}
teeterdone: teeterdone:
teeter = solidteeter; teeter = solidteeter;
P_SetTarget(&tmthing, oldtmthing); // restore old tmthing, goodness knows what the game does with this before mobj thinkers P_SetTarget(&tmthing, oldtmthing); // restore old tmthing, goodness knows what the game does with this before mobj thinkers