From 756e9d0d48d5ef0924039d81c52e4903d4ca63d3 Mon Sep 17 00:00:00 2001 From: Lactozilla Date: Wed, 17 Jan 2024 19:10:19 -0300 Subject: [PATCH] Link objects into multiple blockmap sectors Ported from ZDoom --- src/lua_blockmaplib.c | 132 ++++++++++------- src/lua_mobjlib.c | 9 +- src/p_enemy.c | 16 +-- src/p_local.h | 2 +- src/p_map.c | 324 ++++++++++++++++++++---------------------- src/p_maputl.c | 318 +++++++++++++++++++++++++++++++++++------ src/p_maputl.h | 33 +++++ src/p_mobj.c | 7 +- src/p_mobj.h | 16 ++- src/p_polyobj.c | 20 ++- src/p_setup.c | 4 +- src/p_user.c | 11 +- 12 files changed, 595 insertions(+), 297 deletions(-) diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c index 8d47f3dc1..6b4b6229f 100644 --- a/src/lua_blockmaplib.c +++ b/src/lua_blockmaplib.c @@ -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 // 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); - 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_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); - 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; - } + blockfuncerror = true; + return 0; // *shrugs* } + 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; } @@ -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,24 +270,66 @@ static int lib_searchBlockmap(lua_State *L) BMBOUNDFIX(xl, xh, yl, yh); blockfuncerror = false; // reset - 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 - return 1; + + 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 + 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 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; } diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 85e4590c5..5a3f8ad11 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -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); - break; + 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 diff --git a/src/p_enemy.c b/src/p_enemy.c index 4d268117f..5314de8ab 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -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; diff --git a/src/p_local.h b/src/p_local.h index 6126dedbe..672dcbf4b 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -460,7 +460,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 diff --git a/src/p_map.c b/src/p_map.c index 21582bbd7..10a32d0ea 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -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,28 +837,28 @@ 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 if ((tmthing->player) && ((tmthing->player->powers[pw_strong] & STR_SPIKE) && (thing->type == MT_SPIKE || thing->type == MT_WALLSPIKE))) { 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 - // see if it went over / under - if (tmthing->z > thing->z + thing->height) - return true; // overhead - if (tmthing->z + tmthing->height < thing->z) - return true; // underneath + blockdist = thing->radius + tmthing->radius; + if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist) + return CHECKTHING_NOCOLLIDE; // didn't hit it + // see if it went over / under + if (tmthing->z > thing->z + thing->height) + return CHECKTHING_NOCOLLIDE; // overhead + if (tmthing->z + tmthing->height < thing->z) + 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; diff --git a/src/p_maputl.c b/src/p_maputl.c index 9c30d3ead..758a71ca3 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -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,36 +1021,222 @@ 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 popped our tmthing, cannot continue. - || (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue. - { - P_SetTarget(&bnext, NULL); + if (P_MobjWasRemoved(tmthing)) // func just broke blockmap chain, cannot continue. 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 // diff --git a/src/p_maputl.h b/src/p_maputl.h index 08de0cb0b..e894c08a2 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -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 diff --git a/src/p_mobj.c b/src/p_mobj.c index f16fef2f0..28dd9304f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -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) diff --git a/src/p_mobj.h b/src/p_mobj.h index f2e4cbf3d..f833415ed 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -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; diff --git a/src/p_polyobj.c b/src/p_polyobj.c index 331bc5c7f..e779956e8 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -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; diff --git a/src/p_setup.c b/src/p_setup.c index d2e4f50f7..cf54f37bf 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -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(); diff --git a/src/p_user.c b/src/p_user.c index a5918f695..0ce08812d 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3806,6 +3806,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) @@ -4109,13 +4111,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