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,19 +34,8 @@ typedef UINT8 (*blockmap_func)(lua_State *, INT32, INT32, mobj_t *);
static boolean blockfuncerror = false; // errors should only print once per search blockmap call
// 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;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
return 0;
// Check interaction with the objects in the blockmap.
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);
@ -55,25 +44,18 @@ static UINT8 lib_searchBlockmap_Objects(lua_State *L, INT32 x, INT32 y, mobj_t *
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);
if (P_MobjWasRemoved(thing) // func just popped our thing, cannot continue.
|| (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
{
P_SetTarget(&bnext, NULL);
return (P_MobjWasRemoved(thing)) ? 2 : 1;
}
}
if (P_MobjWasRemoved(thing)) // func just popped our thing, cannot continue.
return P_MobjWasRemoved(thing) ? 2 : 1;
return 0;
}
@ -233,6 +215,7 @@ static int lib_searchBlockmap(lua_State *L)
boolean retval = true;
UINT8 funcret = 0;
blockmap_func searchFunc;
boolean searchObjects = false;
lua_remove(L, 1); // remove searchtype, stack is now function, mobj, [x1, x2, y1, y2]
luaL_checktype(L, 1, LUA_TFUNCTION);
@ -241,7 +224,8 @@ static int lib_searchBlockmap(lua_State *L)
{
case 0: // "objects"
default:
searchFunc = lib_searchBlockmap_Objects;
searchObjects = true;
searchFunc = NULL;
break;
case 1: // "lines"
searchFunc = lib_searchBlockmap_Lines;
@ -286,11 +270,15 @@ static int lib_searchBlockmap(lua_State *L)
BMBOUNDFIX(xl, xh, yl, yh);
blockfuncerror = false; // reset
if (!searchObjects) {
validcount++;
for (bx = xl; bx <= xh; bx++)
for (by = yl; by <= yh; by++)
{
funcret = searchFunc(L, bx, by, mobj);
// 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
@ -298,12 +286,50 @@ static int lib_searchBlockmap(lua_State *L)
}
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 {
bthingit_t *it = P_NewBlockThingsIterator(xl, yl, xh, yh);
if (!it) {
lua_pushboolean(L, false);
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);
return 1;
}

View file

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

View file

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

View file

@ -462,7 +462,7 @@ extern INT32 bmapwidth;
extern INT32 bmapheight; // in mapblocks
extern fixed_t bmaporgx;
extern fixed_t bmaporgy; // origin of block map
extern mobj_t **blocklinks; // for thing chains
extern blocknode_t **blocklinks; // for thing chains
//
// 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);
}
//
// PIT_CheckThing
//
static boolean PIT_CheckThing(mobj_t *thing)
enum
{
CHECKTHING_NOCOLLIDE,
CHECKTHING_COLLIDE,
CHECKTHING_DONE,
CHECKTHING_IGNORE
};
static unsigned PIT_DoCheckThing(mobj_t *thing)
{
fixed_t blockdist;
// don't clip against self
if (thing == tmthing)
return true;
return CHECKTHING_IGNORE;
// Ignore... things.
if (!tmthing || !thing || P_MobjWasRemoved(thing))
return true;
return CHECKTHING_IGNORE;
I_Assert(!P_MobjWasRemoved(tmthing));
I_Assert(!P_MobjWasRemoved(thing));
@ -773,7 +778,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
// Ignore spectators
if ((tmthing->player && tmthing->player->spectator)
|| (thing->player && thing->player->spectator))
return true;
return CHECKTHING_IGNORE;
// Do name checks all the way up here
// 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.
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.
blockdist = thing->radius + tmthing->radius;
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
if (tmthing->z > thing->z + thing->height)
return true; // overhead
return CHECKTHING_NOCOLLIDE; // overhead
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))
return false;
return CHECKTHING_DONE;
seenplayer = thing->player;
return false;
return CHECKTHING_DONE;
}
// Metal Sonic destroys tiny baby objects.
@ -808,15 +813,15 @@ static boolean PIT_CheckThing(mobj_t *thing)
|| thing->type == MT_WALLSPIKE)))
{
if ((thing->flags & (MF_ENEMY|MF_BOSS)) && (thing->health <= 0 || !(thing->flags & MF_SHOOTABLE)))
return true;
return CHECKTHING_IGNORE;
blockdist = thing->radius + tmthing->radius;
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
if (tmthing->z > thing->z + thing->height)
return true; // overhead
return CHECKTHING_NOCOLLIDE; // overhead
if (tmthing->z + tmthing->height < thing->z)
return true; // underneath
return CHECKTHING_NOCOLLIDE; // underneath
if (thing->type == MT_SPIKE
|| thing->type == MT_WALLSPIKE)
{
@ -832,7 +837,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
thing->health = 0;
P_KillMobj(thing, tmthing, tmthing, 0);
}
return true;
return CHECKTHING_COLLIDE;
}
// STR_SPIKE users destroy spikes
@ -841,19 +846,19 @@ static boolean PIT_CheckThing(mobj_t *thing)
mobj_t *iter;
blockdist = thing->radius + tmthing->radius;
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
if (tmthing->z > thing->z + thing->height)
return true; // overhead
return CHECKTHING_NOCOLLIDE; // overhead
if (tmthing->z + tmthing->height < thing->z)
return true; // underneath
return CHECKTHING_NOCOLLIDE; // underneath
if (thing->flags & MF_SOLID)
S_StartSound(tmthing, thing->info->deathsound);
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))
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
@ -865,30 +870,30 @@ static boolean PIT_CheckThing(mobj_t *thing)
blockdist = thing->radius + tmthing->radius;
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)
return true; // overhead
return CHECKTHING_NOCOLLIDE; // overhead
if (tmthing->z + tmthing->height < thing->z)
return true; // underneath
return CHECKTHING_NOCOLLIDE; // underneath
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)))
return true;
return CHECKTHING_IGNORE;
// Don't collide with your buddies while NiGHTS-flying.
if (tmthing->player && thing->player && (maptol & TOL_NIGHTS)
&& ((tmthing->player->powers[pw_carry] == CR_NIGHTSMODE) || (thing->player->powers[pw_carry] == CR_NIGHTSMODE)))
return true;
return CHECKTHING_IGNORE;
blockdist = thing->radius + tmthing->radius;
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.
{
@ -915,23 +920,23 @@ static boolean PIT_CheckThing(mobj_t *thing)
fixed_t tmcosradius = FixedMul(tmthing->radius, FINECOSINE(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)))
return true; // didn't hit it
return CHECKTHING_NOCOLLIDE; // didn't hit it
check1 = 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);
check4 = P_PointOnLineSide(tmx + tmthing->momx + tmcosradius, tmy + tmthing->momy + tmsinradius, &junk);
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
{
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)
== 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)
@ -944,7 +949,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
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)))
return true; // didn't hit it
return CHECKTHING_NOCOLLIDE; // didn't hit it
v1.x = tmx - tmcosradius;
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)))
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
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)
return false; // force collide
return CHECKTHING_DONE; // force collide
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
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)
return false; // force collide
return CHECKTHING_DONE; // force collide
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))
{
//height check
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);
}
@ -995,7 +1000,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
{
//height check
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)
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);
S_StartSound(tmthing, thing->info->activesound);
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))
|| (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 (tmthing->player->powers[pw_carry] == CR_ROLLOUT)
{
return true;
return CHECKTHING_NOCOLLIDE;
}
if ((thing->flags & MF_PUSHABLE) // not carrying a player
&& (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);
if (!P_IsObjectOnGround(thing))
thing->momz += tmthing->momz;
return true;
return CHECKTHING_COLLIDE;
}
}
else if (tmthing->type == MT_ROLLOUTROCK)
{
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
return true;
return CHECKTHING_IGNORE;
if (thing->flags & MF_SPRING) // bounce on springs
{
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
{
P_KillMobj(thing, tmthing, tmthing->tracer, 0);
return true;
return CHECKTHING_COLLIDE;
}
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 (thing->z > tmthing->z + tmthing->height)
return true; // overhead
return CHECKTHING_NOCOLLIDE; // overhead
if (thing->z + thing->height < tmthing->z)
return true; // underneath
return CHECKTHING_NOCOLLIDE; // underneath
if (!thing->tracer || !thing->tracer->tracer)
return true;
return CHECKTHING_IGNORE;
P_SlapStick(tmthing, thing);
// 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?
@ -1123,13 +1128,13 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->type == MT_BIGMINE)
{
if (!tmthing->momx && !tmthing->momy)
return true;
return CHECKTHING_IGNORE;
if ((statenum_t)(thing->state-states) >= thing->info->meleestate)
return true;
return CHECKTHING_IGNORE;
if (thing->z > tmthing->z + tmthing->height)
return true; // overhead
return CHECKTHING_NOCOLLIDE; // overhead
if (thing->z + thing->height < tmthing->z)
return true; // underneath
return CHECKTHING_NOCOLLIDE; // underneath
thing->momx = tmthing->momx/3;
thing->momy = tmthing->momy/3;
@ -1141,18 +1146,18 @@ static boolean PIT_CheckThing(mobj_t *thing)
S_StartSound(thing, thing->info->activesound);
P_SetMobjState(thing, thing->info->meleestate);
P_SetTarget(&thing->tracer, tmthing->tracer);
return true;
return CHECKTHING_COLLIDE;
}
else if (tmthing->type == MT_CRUSHCLAW)
{
if (tmthing->extravalue1 <= 0)
return true;
return CHECKTHING_IGNORE;
if ((statenum_t)(thing->state-states) >= thing->info->meleestate)
return true;
return CHECKTHING_IGNORE;
if (thing->z > tmthing->z + tmthing->height)
return true; // overhead
return CHECKTHING_NOCOLLIDE; // overhead
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->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);
if (tmthing->tracer)
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 (thing->z > tmthing->z + tmthing->height)
return true; // overhead
return CHECKTHING_NOCOLLIDE; // overhead
if (thing->z + thing->height < tmthing->z)
return true; // underneath
return CHECKTHING_NOCOLLIDE; // underneath
if (tmthing->eflags & MFE_VERTICALFLIP)
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));
if (thing->flags & MF_SHOOTABLE)
P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
return true;
return CHECKTHING_COLLIDE;
}
if (thing->flags & MF_PAIN && tmthing->player)
{ // Player touches painful thing sitting on the floor
// see if it went over / under
if (thing->z > tmthing->z + tmthing->height)
return true; // overhead
return CHECKTHING_NOCOLLIDE; // overhead
if (thing->z + thing->height < tmthing->z)
return true; // underneath
return CHECKTHING_NOCOLLIDE; // underneath
if (tmthing->flags & MF_SHOOTABLE && thing->health > 0)
{
UINT32 damagetype = (thing->info->mass & 0xFF);
@ -1196,16 +1201,17 @@ static boolean PIT_CheckThing(mobj_t *thing)
damagetype = DMG_FIRE;
if (P_DamageMobj(tmthing, thing, thing, 1, damagetype) && (damagetype = (thing->info->mass>>8)))
S_StartSound(thing, damagetype);
return CHECKTHING_COLLIDE;
}
return true;
return CHECKTHING_NOCOLLIDE;
}
else if (tmthing->flags & MF_PAIN && thing->player)
{ // Painful thing splats player in the face
// see if it went over / under
if (tmthing->z > thing->z + thing->height)
return true; // overhead
return CHECKTHING_NOCOLLIDE; // overhead
if (tmthing->z + tmthing->height < thing->z)
return true; // underneath
return CHECKTHING_NOCOLLIDE; // underneath
if (thing->flags & MF_SHOOTABLE && tmthing->health > 0)
{
UINT32 damagetype = (tmthing->info->mass & 0xFF);
@ -1213,32 +1219,33 @@ static boolean PIT_CheckThing(mobj_t *thing)
damagetype = DMG_FIRE;
if (P_DamageMobj(thing, tmthing, tmthing, 1, damagetype) && (damagetype = (tmthing->info->mass>>8)))
S_StartSound(tmthing, damagetype);
return CHECKTHING_COLLIDE;
}
return true;
return CHECKTHING_NOCOLLIDE;
}
if (thing->type == MT_HOOPCOLLIDE && thing->flags & MF_SPECIAL && tmthing->player)
{
P_TouchSpecialThing(thing, tmthing, true);
return true;
return CHECKTHING_COLLIDE;
}
// check for skulls slamming into things
if (tmthing->flags2 & MF2_SKULLFLY)
{
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
{
// see if it went over / under
if (tmthing->z > thing->z + thing->height)
return true; // overhead
return CHECKTHING_NOCOLLIDE; // overhead
if (tmthing->z + tmthing->height < thing->z)
return true; // underneath
return CHECKTHING_NOCOLLIDE; // underneath
tmthing->flags2 &= ~MF2_SKULLFLY;
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))
{
P_DoSpring(thing, tmthing);
return true;
return CHECKTHING_COLLIDE;
}
else if (tmz > thzh - sprarea && tmz < thzh) // Don't damage people springing up / down
return true;
return CHECKTHING_NOCOLLIDE;
}
// missiles can hit other things
@ -1265,31 +1272,31 @@ static boolean PIT_CheckThing(mobj_t *thing)
{
// see if it went over / under
if (tmthing->z > thing->z + thing->height)
return true; // overhead
return CHECKTHING_NOCOLLIDE; // overhead
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)
{
// Don't hit same species as originator.
if (thing == tmthing->target)
return true;
return CHECKTHING_IGNORE;
if (thing->type != MT_PLAYER)
{
// Explode, but do no damage.
// Let players missile other players.
return false;
return CHECKTHING_DONE;
}
}
// 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)
return true;
return CHECKTHING_IGNORE;
// Missiles ignore Brak's helper.
if (thing->type == MT_BLACKEGGMAN_HELPER)
return true;
return CHECKTHING_IGNORE;
// Hurting Brak
if (tmthing->type == MT_BLACKEGGMAN_MISSILE
@ -1298,19 +1305,22 @@ static boolean PIT_CheckThing(mobj_t *thing)
// Not if Brak's already in pain
if (!(thing->state >= &states[S_BLACKEGG_PAIN1] && thing->state <= &states[S_BLACKEGG_PAIN35]))
P_SetMobjState(thing, thing->info->painstate);
return false;
return CHECKTHING_DONE;
}
if (!(thing->flags & MF_SHOOTABLE) && !(thing->type == MT_EGGSHIELD))
{
// 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
&& thing->player->ctfteam == tmthing->target->player->ctfteam
&& 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)
{
@ -1318,7 +1328,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (angle < ANGLE_180) // hit shield from behind, shield is destroyed!
P_KillMobj(thing, tmthing, tmthing, 0);
return false;
return CHECKTHING_DONE;
}
// damage / explode
@ -1344,7 +1354,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (!demoplayback || P_ControlStyle(thing->player) == CS_LMAOGALOG)
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)))
{
@ -1371,11 +1381,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
P_KillMobj(tmthing, NULL, NULL, 0);
// don't traverse any more
if (tmthing->type == MT_SHELL)
return true;
return CHECKTHING_COLLIDE;
else
return false;
return CHECKTHING_DONE;
}
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)
{
P_TouchSpecialThing(thing, tmthing, true); // can remove thing
return true;
return CHECKTHING_COLLIDE;
}
// check again for special pickup
if (tmthing->flags & MF_SPECIAL && thing->player)
{
P_TouchSpecialThing(tmthing, thing, true); // can remove thing
return true;
return CHECKTHING_COLLIDE;
}
// Sprite Spikes!
@ -1544,7 +1553,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (playerangle > ANGLE_180)
playerangle = InvAngle(playerangle);
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;
@ -1578,15 +1587,15 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (thing->type == MT_FAN || thing->type == MT_STEAM)
{
P_DoFanAndGasJet(thing, tmthing);
return true;
return CHECKTHING_COLLIDE;
}
else if (thing->flags & MF_SPRING)
{
if ( thing->z <= tmthing->z + tmthing->height
&& tmthing->z <= thing->z + thing->height)
if (P_DoSpring(thing, tmthing))
return false;
return true;
return CHECKTHING_DONE;
return CHECKTHING_COLLIDE;
}
}
@ -1595,7 +1604,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
{
if ((thing->z + thing->height >= tmthing->z)
&& (tmthing->z + tmthing->height >= thing->z))
return false;
return CHECKTHING_DONE;
}
// Damage other players when invincible
@ -1630,7 +1639,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->player && thing->player)
{
P_DoTailsCarry(thing->player, tmthing->player);
return true;
return CHECKTHING_COLLIDE;
}
}
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->health)
return true;
return CHECKTHING_IGNORE;
if (thing->type == MT_FAN || thing->type == MT_STEAM)
P_DoFanAndGasJet(thing, tmthing);
@ -1686,14 +1695,16 @@ static boolean PIT_CheckThing(mobj_t *thing)
if ( thing->z <= tmthing->z + tmthing->height
&& tmthing->z <= thing->z + thing->height)
if (P_DoSpring(thing, tmthing))
return false;
return true;
return CHECKTHING_DONE;
return CHECKTHING_COLLIDE;
}
// 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->flags & MF_SOLID) || P_PlayerCanDamage(tmthing->player, thing)))
{
unsigned collide = CHECKTHING_NOCOLLIDE;
if (thing->z - thing->scale <= tmthing->z + tmthing->height
&& thing->z + thing->height + thing->scale >= tmthing->z)
{
@ -1725,21 +1736,25 @@ static boolean PIT_CheckThing(mobj_t *thing)
*momz /= 2;
*momz -= (*momz/(underwater ? 8 : 4)); // Cap the height!
}
collide = CHECKTHING_COLLIDE;
}
if (!(elementalpierce == 1 && thing->flags & MF_GRENADEBOUNCE)) // prevent gold monitor clipthrough.
{
if (player->pflags & PF_BOUNCING)
P_DoAbilityBounce(player, false);
return false;
collide = CHECKTHING_DONE;
}
else
*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))
; // springs, gas jets and springs should never be able to step up onto a player
// z checking at last
@ -1762,25 +1777,27 @@ static boolean PIT_CheckThing(mobj_t *thing)
tmfloorrover = 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
// block only when jumping not high enough,
// (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
if (tmthing->player && tmthing->z + tmthing->height > topz
&& tmthing->z + tmthing->height < tmthing->ceilingz)
{
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
tmceilingrover = NULL;
tmceilingslope = NULL;
tmfloorthing = thing; // needed for side collision
collide = CHECKTHING_COLLIDE;
}
else if (topz < tmceilingz && tmthing->z <= thing->z+thing->height)
{
@ -1788,6 +1805,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
tmceilingrover = NULL;
tmceilingslope = NULL;
tmfloorthing = thing; // thing we may stand on
collide = CHECKTHING_COLLIDE;
}
}
else
@ -1803,25 +1822,27 @@ static boolean PIT_CheckThing(mobj_t *thing)
tmceilingrover = 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
// block only when jumping not high enough,
// (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
if (tmthing->player && tmthing->z < topz
&& tmthing->z > tmthing->floorz)
{
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
tmfloorrover = NULL;
tmfloorslope = NULL;
tmfloorthing = thing; // needed for side collision
collide = CHECKTHING_COLLIDE;
}
else if (topz > tmfloorz && tmthing->z+tmthing->height >= thing->z)
{
@ -1829,12 +1850,18 @@ static boolean PIT_CheckThing(mobj_t *thing)
tmfloorrover = NULL;
tmfloorslope = NULL;
tmfloorthing = thing; // thing we may stand on
collide = CHECKTHING_COLLIDE;
}
}
}
// not solid not blocked
return true;
return collide;
}
static boolean PIT_CheckThing(mobj_t *thing)
{
return PIT_DoCheckThing(thing) != CHECKTHING_DONE;
}
// PIT_CheckCameraLine
@ -2010,38 +2037,6 @@ static boolean PIT_CheckLine(line_t *ld)
// =========================================================================
// 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)
{
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)
return true;
// Check things first, possibly picking things up.
// MF_NOCLIPTHING: used by camera to not be blocked by things
// Check things first.
if (!(thing->flags & MF_NOCLIPTHING))
{
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.
if (thing->flags & MF_PUSHABLE)
{
INT32 bx, by, xl, xh, yl, yh;
INT32 xl, xh, yl, yh;
yh = (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;
standy = y;
for (by = yl; by <= yh; by++)
for (bx = xl; bx <= xh; bx++)
P_BlockThingsIterator(bx, by, PIT_PushableMoved);
P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushableMoved);
}
// 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)
{
INT32 x, y;
INT32 xl, xh, yl, yh;
fixed_t dist;
@ -4235,9 +4225,7 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama
bombdamagetype = damagetype;
bombsightcheck = sightcheck;
for (y = yl; y <= yh; y++)
for (x = xl; x <= xh; x++)
P_BlockThingsIterator(x, y, PIT_RadiusAttack);
P_DoBlockThingsIterate(xl, yl, xh, yh, 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)
{
mobj_t *mo;
blocknode_t *block;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
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))
continue;

View file

@ -680,6 +680,35 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
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
@ -730,20 +759,20 @@ void P_UnsetThingPosition(mobj_t *thing)
if (!(thing->flags & MF_NOBLOCKMAP))
{
/* inert things don't need to be in blockmap
*
* 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.
*/
// [RH] Unlink from all blocks this actor uses
blocknode_t *block = thing->blocknode;
mobj_t *bnext, **bprev = thing->bprev;
if (bprev && (*bprev = bnext = thing->bnext) != NULL) // unlink from block map
bnext->bprev = bprev;
while (block != NULL)
{
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))
{
// inert things don't need to be in blockmap
const INT32 blockx = (unsigned)(thing->x - bmaporgx)>>MAPBLOCKSHIFT;
const INT32 blocky = (unsigned)(thing->y - bmaporgy)>>MAPBLOCKSHIFT;
if (blockx >= 0 && blockx < bmapwidth
&& blocky >= 0 && blocky < bmapheight)
{
// killough 8/11/98: simpler scheme using
// pointer-to-pointer prev pointers --
// allows head nodes to be treated like everything else
INT32 x1 = (unsigned)(thing->x - thing->radius - bmaporgx)>>MAPBLOCKSHIFT;
INT32 y1 = (unsigned)(thing->y - thing->radius - bmaporgy)>>MAPBLOCKSHIFT;
INT32 x2 = (unsigned)(thing->x + thing->radius - bmaporgx)>>MAPBLOCKSHIFT;
INT32 y2 = (unsigned)(thing->y + thing->radius - bmaporgy)>>MAPBLOCKSHIFT;
mobj_t **link = &blocklinks[blocky*bmapwidth + blockx];
mobj_t *bnext = *link;
if ((thing->bnext = bnext) != NULL)
bnext->bprev = &thing->bnext;
thing->bprev = link;
*link = thing;
thing->blocknode = NULL;
blocknode_t **alink = &thing->blocknode;
if (!(x1 >= bmapwidth || x2 < 0 || y1 >= bmapheight || y2 < 0))
{
// [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
@ -971,34 +1021,220 @@ boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *))
return true; // Everything was checked.
}
//
// P_BlockThingsIterator
//
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)
return true;
// 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))
{
P_SetTarget(&bnext, NULL);
return false;
if (P_MobjWasRemoved(tmthing)) // func just broke blockmap chain, cannot continue.
return true;
}
if (P_MobjWasRemoved(tmthing) // func just popped our tmthing, cannot continue.
|| (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
return true;
}
boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *))
{
P_SetTarget(&bnext, NULL);
return true;
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;
}
}
return true;
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;
}
//

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_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_ADDTHINGS 2
#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)
{
INT32 xl, xh, yl, yh, bx, by;
INT32 xl, xh, yl, yh;
fixed_t radius;
if (!mobj->spawnpoint)
@ -9470,9 +9470,8 @@ static void P_PointPushThink(mobj_t *mobj)
xh = (unsigned)(mobj->x + radius - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
yl = (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_BlockThingsIterator(bx, by, PIT_PushThing);
P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushThing);
}
static boolean P_MobjRegularThink(mobj_t *mobj)

View file

@ -273,6 +273,19 @@ typedef enum {
PCF_THUNK = 32,
} 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.
typedef struct mobj_s
{
@ -344,8 +357,7 @@ typedef struct mobj_s
// Interaction info, by BLOCKMAP.
// Links in blocks (if needed).
struct mobj_s *bnext;
struct mobj_s **bprev; // killough 8/11/98: change to ptr-to-ptr
blocknode_t *blocknode;
// Additional pointers for NiGHTS hoops
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)
{
mobj_t *mo;
blocknode_t *block;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
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)
continue;
@ -937,10 +940,12 @@ static INT32 Polyobj_clipThings(polyobj_t *po, line_t *line)
{
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
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)
{
mobj_t *mo;
blocknode_t *block;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
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)
continue;

View file

@ -138,7 +138,7 @@ INT32 *blockmaplump; // Big blockmap
// origin of block map
fixed_t bmaporgx, bmaporgy;
// for thing chains
mobj_t **blocklinks;
blocknode_t **blocklinks;
// REJECT
// For fast sight rejection.
@ -7869,6 +7869,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
Z_Free(ss->attachedsolid);
}
P_ClearBlockNodes();
// Clear pointers that would be left dangling by the purge
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)
return true; // didn't hit it
highesttop = INT32_MIN;
if (teeterer->eflags & MFE_VERTICALFLIP)
{
if (thingtop < teeterer->z)
@ -4110,13 +4112,8 @@ static void P_DoTeeter(player_t *player)
teeteryl = teeteryh = player->mo->y;
couldteeter = false;
solidteeter = teeter;
for (by = yl; by <= yh; by++)
for (bx = xl; bx <= xh; bx++)
{
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
}
if (!P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_CheckSolidsTeeter))
goto teeterdone; // we've found something that stops us teetering at all
teeterdone:
teeter = solidteeter;
P_SetTarget(&tmthing, oldtmthing); // restore old tmthing, goodness knows what the game does with this before mobj thinkers