diff --git a/src/p_floor.c b/src/p_floor.c index 7fbc1ad4c..551469a29 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -1272,216 +1272,198 @@ void T_NoEnemiesSector(noenemies_t *nobaddies) P_RemoveThinker(&nobaddies->thinker); } -// -// P_IsObjectOnRealGround -// -// Helper function for T_EachTimeThinker -// Like P_IsObjectOnGroundIn, except ONLY THE REAL GROUND IS CONSIDERED, NOT FOFS -// I'll consider whether to make this a more globally accessible function or whatever in future -// -- Monster Iestyn -// -static boolean P_IsObjectOnRealGround(mobj_t *mo, sector_t *sec) +static sector_t *P_Check3DFloorTriggers(player_t *player, sector_t *sector, line_t *sourceline) { - // Is the object in reverse gravity? - if (mo->eflags & MFE_VERTICALFLIP) + ffloor_t *rover; + + for (rover = sector->ffloors; rover; rover = rover->next) { - // Detect if the player is on the ceiling. - if (mo->z+mo->height >= P_GetSpecialTopZ(mo, sec, sec)) - return true; + if (GETSECSPECIAL(rover->master->frontsector->special, 2) < 2 || GETSECSPECIAL(rover->master->frontsector->special, 2) > 7) + continue; + + if (!(rover->flags & FF_EXISTS)) + continue; + + if (!Tag_Find(&sourceline->tags, Tag_FGet(&rover->master->frontsector->tags))) + return false; + + if (!P_IsMobjTouching3DFloor(player->mo, rover, sector)) + continue; + + // This FOF has the special we're looking for, but are we allowed to touch it? + if (sector == player->mo->subsector->sector + || (rover->master->frontsector->flags & SF_TRIGGERSPECIAL_TOUCH)) + return rover->master->frontsector; } - // Nope! - else - { - // Detect if the player is on the floor. - if (mo->z <= P_GetSpecialBottomZ(mo, sec, sec)) - return true; - } - return false; + + return NULL; } -static boolean P_IsMobjTouchingSector(mobj_t *mo, sector_t *sec) +static sector_t *P_CheckPolyobjTriggers(player_t *player, line_t *sourceline) { - msecnode_t *node; + polyobj_t *po; + sector_t *polysec; + boolean touching = false; + boolean inside = false; - if (mo->subsector->sector == sec) - return true; + for (po = player->mo->subsector->polyList; po; po = (polyobj_t *)(po->link.next)) //TODO + { + if (po->flags & POF_NOSPECIALS) + continue; - if (!(sec->flags & SF_TRIGGERSPECIAL_TOUCH)) + polysec = po->lines[0]->backsector; + + if (GETSECSPECIAL(polysec->special, 2) < 2 || GETSECSPECIAL(polysec->special, 2) > 7) + continue; + + if (!Tag_Find(&sourceline->tags, Tag_FGet(&polysec->tags))) + return false; + + touching = (polysec->flags & SF_TRIGGERSPECIAL_TOUCH) && P_MobjTouchingPolyobj(po, player->mo); + inside = P_MobjInsidePolyobj(po, player->mo); + + if (!(inside || touching)) + continue; + + if (!P_IsMobjTouchingPolyobj(player->mo, po, polysec)) + continue; + + return polysec; + } + + return NULL; +} + +static boolean P_CheckSectorTriggers(player_t *player, sector_t *sector, line_t *sourceline) +{ + if (GETSECSPECIAL(sector->special, 2) < 2 || GETSECSPECIAL(sector->special, 2) > 7) return false; - for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next) + if (!Tag_Find(&sourceline->tags, Tag_FGet(§or->tags))) + return false; + + if (GETSECSPECIAL(sector->special, 2) != 3 && GETSECSPECIAL(sector->special, 2) != 5) + return true; // "Anywhere in sector" types + + return P_IsMobjTouchingSectorPlane(player->mo, sector); + +} + +static sector_t *P_FindPlayerTrigger(player_t *player, line_t *sourceline) +{ + sector_t *originalsector; + sector_t *loopsector; + msecnode_t *node; + sector_t *caller; + + if (!player->mo) + return NULL; + + originalsector = player->mo->subsector->sector; + + caller = P_Check3DFloorTriggers(player, originalsector, sourceline); // Handle FOFs first. + + if (caller) + return caller; + + // Allow sector specials to be applied to polyobjects! + caller = P_CheckPolyobjTriggers(player, sourceline); + + if (caller) + return caller; + + if (P_CheckSectorTriggers(player, originalsector, sourceline)) + return originalsector; + + // Iterate through touching_sectorlist for SF_TRIGGERSPECIAL_TOUCH + for (node = player->mo->touching_sectorlist; node; node = node->m_sectorlist_next) { - if (node->m_sector == sec) - return true; + loopsector = node->m_sector; + + if (loopsector == originalsector) // Don't duplicate + continue; + + // Check 3D floors... + caller = P_Check3DFloorTriggers(player, loopsector, sourceline); // Handle FOFs first. + + if (caller) + return caller; + + //TODO: Check polyobjects in loopsector + + if (!(loopsector->flags & SF_TRIGGERSPECIAL_TOUCH)) + continue; + + if (P_CheckSectorTriggers(player, loopsector, sourceline)) + return loopsector; } return false; } -// -// T_EachTimeThinker -// -// Runs a linedef exec whenever a player enters an area. -// Keeps track of players currently in the area and notices any changes. -// -// \sa P_AddEachTimeThinker -// +static boolean P_CheckAllTrigger(eachtime_t *eachtime) +{ + size_t i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (P_IsPlayerValid(i) && !eachtime->playersInArea[i]) + return false; + } + + return true; +} + void T_EachTimeThinker(eachtime_t *eachtime) { - size_t i, j; - sector_t *sec = NULL; - sector_t *targetsec = NULL; - INT32 secnum = -1; + size_t i; boolean oldPlayersInArea[MAXPLAYERS]; - boolean oldPlayersOnArea[MAXPLAYERS]; - boolean *oldPlayersArea; - boolean *playersArea; - boolean FOFsector = false; - boolean floortouch = false; - fixed_t bottomheight, topheight; - ffloor_t *rover; + sector_t *caller[MAXPLAYERS]; + boolean allPlayersChecked = false; + boolean allPlayersTrigger = false; mtag_t tag = Tag_FGet(&eachtime->sourceline->tags); for (i = 0; i < MAXPLAYERS; i++) { oldPlayersInArea[i] = eachtime->playersInArea[i]; - oldPlayersOnArea[i] = eachtime->playersOnArea[i]; - eachtime->playersInArea[i] = false; - eachtime->playersOnArea[i] = false; - } - - TAG_ITER_SECTORS(tag, secnum) - { - sec = §ors[secnum]; - - FOFsector = false; - - if (GETSECSPECIAL(sec->special, 2) == 3 || GETSECSPECIAL(sec->special, 2) == 5) - floortouch = true; - else if (GETSECSPECIAL(sec->special, 2) >= 1 && GETSECSPECIAL(sec->special, 2) <= 8) - floortouch = false; - else - continue; - - // Check the lines of this sector, to see if it is a FOF control sector. - for (i = 0; i < sec->linecount; i++) - { - INT32 targetsecnum = -1; - - if (sec->lines[i]->special < 100 || sec->lines[i]->special >= 300) - continue; - - FOFsector = true; - - TAG_ITER_SECTORS(sec->lines[i]->args[0], targetsecnum) - { - targetsec = §ors[targetsecnum]; - - // Find the FOF corresponding to the control linedef - for (rover = targetsec->ffloors; rover; rover = rover->next) - { - if (rover->master == sec->lines[i]) - break; - } - - if (!rover) // This should be impossible, but don't complain if it is the case somehow - continue; - - if (!(rover->flags & FF_EXISTS)) // If the FOF does not "exist", we pretend that nobody's there - continue; - - for (j = 0; j < MAXPLAYERS; j++) - { - if (!P_IsPlayerValid(j)) - continue; - - if (!P_IsMobjTouchingSector(players[j].mo, targetsec)) - continue; - - topheight = P_GetSpecialTopZ(players[j].mo, sec, targetsec); - bottomheight = P_GetSpecialBottomZ(players[j].mo, sec, targetsec); - - if (players[j].mo->z > topheight) - continue; - - if (players[j].mo->z + players[j].mo->height < bottomheight) - continue; - - if (floortouch && P_IsObjectOnGroundIn(players[j].mo, targetsec)) - eachtime->playersOnArea[j] = true; - else - eachtime->playersInArea[j] = true; - } - } - } - - if (!FOFsector) - { - for (i = 0; i < MAXPLAYERS; i++) - { - if (!P_IsPlayerValid(i)) - continue; - - if (!P_IsMobjTouchingSector(players[i].mo, sec)) - continue; - - if (!(players[i].mo->subsector->sector == sec - || P_PlayerTouchingSectorSpecial(&players[i], 2, (GETSECSPECIAL(sec->special, 2))) == sec)) - continue; - - if (floortouch && P_IsObjectOnRealGround(players[i].mo, sec)) - eachtime->playersOnArea[i] = true; - else - eachtime->playersInArea[i] = true; - } - } - } - - // Check if a new player entered. - // If not, check if a player hit the floor. - // If either condition is true, execute. - if (floortouch) - { - playersArea = eachtime->playersOnArea; - oldPlayersArea = oldPlayersOnArea; - } - else - { - playersArea = eachtime->playersInArea; - oldPlayersArea = oldPlayersInArea; + caller[i] = P_IsPlayerValid(i) ? P_FindPlayerTrigger(&players[i], eachtime->sourceline) : NULL; + eachtime->playersInArea[i] = caller[i] != NULL; } // Easy check... nothing has changed - if (!memcmp(playersArea, oldPlayersArea, sizeof(boolean)*MAXPLAYERS)) + if (!memcmp(eachtime->playersInArea, oldPlayersInArea, sizeof(boolean)*MAXPLAYERS)) return; - // If sector has an "all players" trigger type, all players need to be in area - if (GETSECSPECIAL(sec->special, 2) == 2 || GETSECSPECIAL(sec->special, 2) == 3) - { - for (i = 0; i < MAXPLAYERS; i++) - { - if (P_IsPlayerValid(i) && !playersArea[i]) - return; - } - } - // Trigger for every player who has entered (and exited, if triggerOnExit) for (i = 0; i < MAXPLAYERS; i++) { - if (playersArea[i] == oldPlayersArea[i]) + if (eachtime->playersInArea[i] == oldPlayersInArea[i]) continue; // If player has just left, check if still valid - if (!playersArea[i] && (!eachtime->triggerOnExit || !P_IsPlayerValid(i))) + if (!eachtime->playersInArea[i] && (!eachtime->triggerOnExit || !P_IsPlayerValid(i))) continue; + // If sector has an "all players" trigger type, all players need to be in area + if (caller[i] && (GETSECSPECIAL(caller[i]->special, 2) == 2 || GETSECSPECIAL(caller[i]->special, 2) == 3)) + { + if (!allPlayersChecked) + { + allPlayersChecked = true; + allPlayersTrigger = P_CheckAllTrigger(eachtime); + } + + if (!allPlayersTrigger) + continue; + } + CONS_Debug(DBG_GAMELOGIC, "Trying to activate each time executor with tag %d\n", tag); // 03/08/14 -Monster Iestyn // No more stupid hacks involving changing eachtime->sourceline's tag or special or whatever! // This should now run ONLY the stuff for eachtime->sourceline itself, instead of all trigger linedefs sharing the same tag. // Makes much more sense doing it this way, honestly. - P_RunTriggerLinedef(eachtime->sourceline, players[i].mo, sec); + P_RunTriggerLinedef(eachtime->sourceline, players[i].mo, caller[i]); if (!eachtime->sourceline->special) // this happens only for "Trigger on X calls" linedefs P_RemoveThinker(&eachtime->thinker); diff --git a/src/p_local.h b/src/p_local.h index 1fcd3050d..bbcd86b39 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -148,7 +148,6 @@ boolean P_PlayerShouldUseSpinHeight(player_t *player); boolean P_IsObjectInGoop(mobj_t *mo); boolean P_IsObjectOnGround(mobj_t *mo); -boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec); boolean P_InSpaceSector(mobj_t *mo); boolean P_InQuicksand(mobj_t *mo); boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff); diff --git a/src/p_saveg.c b/src/p_saveg.c index b17011837..bc9c31d64 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2012,7 +2012,6 @@ static void SaveEachTimeThinker(const thinker_t *th, const UINT8 type) for (i = 0; i < MAXPLAYERS; i++) { WRITECHAR(save_p, ht->playersInArea[i]); - WRITECHAR(save_p, ht->playersOnArea[i]); } WRITECHAR(save_p, ht->triggerOnExit); } @@ -3130,7 +3129,6 @@ static thinker_t* LoadEachTimeThinker(actionf_p1 thinker) for (i = 0; i < MAXPLAYERS; i++) { ht->playersInArea[i] = READCHAR(save_p); - ht->playersOnArea[i] = READCHAR(save_p); } ht->triggerOnExit = READCHAR(save_p); return &ht->thinker; diff --git a/src/p_spec.c b/src/p_spec.c index 02e48fab2..feab9f2b6 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3886,12 +3886,12 @@ static boolean P_IsMobjTouchingPlane(mobj_t *mo, sector_t *sec, fixed_t floorz, return (floorallowed || ceilingallowed); } -static boolean P_IsMobjTouchingSectorPlane(mobj_t *mo, sector_t *sec) +boolean P_IsMobjTouchingSectorPlane(mobj_t *mo, sector_t *sec) { return P_IsMobjTouchingPlane(mo, sec, P_GetSpecialBottomZ(mo, sec, sec), P_GetSpecialTopZ(mo, sec, sec)); } -static boolean P_IsMobjTouching3DFloor(mobj_t *mo, ffloor_t *ffloor, sector_t *sec) +boolean P_IsMobjTouching3DFloor(mobj_t *mo, ffloor_t *ffloor, sector_t *sec) { fixed_t topheight = P_GetSpecialTopZ(mo, sectors + ffloor->secnum, sec); fixed_t bottomheight = P_GetSpecialBottomZ(mo, sectors + ffloor->secnum, sec); @@ -3909,7 +3909,7 @@ static boolean P_IsMobjTouching3DFloor(mobj_t *mo, ffloor_t *ffloor, sector_t *s } } -static boolean P_IsMobjTouchingPolyobj(mobj_t *mo, polyobj_t *po, sector_t *polysec) +boolean P_IsMobjTouchingPolyobj(mobj_t *mo, polyobj_t *po, sector_t *polysec) { if (!(po->flags & POF_TESTHEIGHT)) // Don't do height checking return true; diff --git a/src/p_spec.h b/src/p_spec.h index 0fdc216b9..5e79e9737 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -338,6 +338,10 @@ INT32 P_FindMinSurroundingLight(sector_t *sector, INT32 max); void P_SetupSignExit(player_t *player); boolean P_IsFlagAtBase(mobjtype_t flag); +boolean P_IsMobjTouchingSectorPlane(mobj_t *mo, sector_t *sec); +boolean P_IsMobjTouching3DFloor(mobj_t *mo, ffloor_t *ffloor, sector_t *sec); +boolean P_IsMobjTouchingPolyobj(mobj_t *mo, polyobj_t *po, sector_t *polysec); + void P_SwitchWeather(INT32 weathernum); boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller); @@ -680,7 +684,6 @@ typedef struct thinker_t thinker; line_t *sourceline; // Source line of the thinker boolean playersInArea[MAXPLAYERS]; - boolean playersOnArea[MAXPLAYERS]; boolean triggerOnExit; } eachtime_t; diff --git a/src/p_user.c b/src/p_user.c index 9921ed078..4858c6ddd 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1728,89 +1728,6 @@ boolean P_IsObjectOnGround(mobj_t *mo) return false; } -// -// P_IsObjectOnGroundIn -// -// Returns true if the player is -// on the ground in a specific sector. Takes reverse -// gravity and FOFs into account. -// -boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec) -{ - ffloor_t *rover; - - // Is the object in reverse gravity? - if (mo->eflags & MFE_VERTICALFLIP) - { - // Detect if the player is on the ceiling. - if (mo->z+mo->height >= P_GetSpecialTopZ(mo, sec, sec)) - return true; - // Otherwise, detect if the player is on the bottom of a FOF. - else - { - for (rover = sec->ffloors; rover; rover = rover->next) - { - // If the FOF doesn't exist, continue. - if (!(rover->flags & FF_EXISTS)) - continue; - - // If the FOF is configured to let the object through, continue. - if (!((rover->flags & FF_BLOCKPLAYER && mo->player) - || (rover->flags & FF_BLOCKOTHERS && !mo->player))) - continue; - - // If the the platform is intangible from below, continue. - if (rover->flags & FF_PLATFORM) - continue; - - // If the FOF is a water block, continue. (Unnecessary check?) - if (rover->flags & FF_SWIMMABLE) - continue; - - // Actually check if the player is on the suitable FOF. - if (mo->z+mo->height == P_GetSpecialBottomZ(mo, sectors + rover->secnum, sec)) - return true; - } - } - } - // Nope! - else - { - // Detect if the player is on the floor. - if (mo->z <= P_GetSpecialBottomZ(mo, sec, sec)) - return true; - // Otherwise, detect if the player is on the top of a FOF. - else - { - for (rover = sec->ffloors; rover; rover = rover->next) - { - // If the FOF doesn't exist, continue. - if (!(rover->flags & FF_EXISTS)) - continue; - - // If the FOF is configured to let the object through, continue. - if (!((rover->flags & FF_BLOCKPLAYER && mo->player) - || (rover->flags & FF_BLOCKOTHERS && !mo->player))) - continue; - - // If the the platform is intangible from above, continue. - if (rover->flags & FF_REVERSEPLATFORM) - continue; - - // If the FOF is a water block, continue. (Unnecessary check?) - if (rover->flags & FF_SWIMMABLE) - continue; - - // Actually check if the player is on the suitable FOF. - if (mo->z == P_GetSpecialTopZ(mo, sectors + rover->secnum, sec)) - return true; - } - } - } - - return false; -} - // // P_SetObjectMomZ //