diff --git a/src/dehacked.c b/src/dehacked.c index 729e96311..aa5d84954 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -6962,6 +6962,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_PULL", "MT_GHOST", "MT_OVERLAY", + "MT_ANGLEMAN", "MT_POLYANCHOR", "MT_POLYSPAWN", "MT_POLYSPAWNCRUSH", diff --git a/src/info.c b/src/info.c index 3140bd7de..72abcede1 100644 --- a/src/info.c +++ b/src/info.c @@ -17814,6 +17814,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_ANGLEMAN + 758, // doomednum + S_INVISIBLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8, // radius + 8, // height + 0, // display offset + 10, // mass + 0, // damage + sfx_None, // activesound + MF_NOTHINK|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + { // MT_POLYANCHOR 760, // doomednum S_INVISIBLE, // spawnstate diff --git a/src/info.h b/src/info.h index b757deec0..c9de82541 100644 --- a/src/info.h +++ b/src/info.h @@ -4305,6 +4305,7 @@ typedef enum mobj_type MT_PULL, MT_GHOST, MT_OVERLAY, + MT_ANGLEMAN, MT_POLYANCHOR, MT_POLYSPAWN, MT_POLYSPAWNCRUSH, diff --git a/src/p_mobj.c b/src/p_mobj.c index 9189f50f7..3c1418252 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7436,6 +7436,48 @@ void P_MobjThinker(mobj_t *mobj) if ((mobj->flags & MF_ENEMY) && (mobj->state->nextstate == mobj->info->spawnstate && mobj->tics == 1)) mobj->flags2 &= ~MF2_FRET; + // Angle-to-tracer to trigger a linedef exec + // See Linedef Exec 457 (Track mobj angle to point) + if ((mobj->eflags & MFE_TRACERANGLE) && mobj->tracer && mobj->extravalue2) + { + // mobj->lastlook - Don't disable behavior after first failure + // mobj->extravalue1 - Angle tolerance + // mobj->extravalue2 - Exec tag upon failure + // mobj->cvval - Allowable failure delay + // mobj->cvmem - Failure timer + + angle_t ang = mobj->angle - R_PointToAngle2(mobj->x, mobj->y, mobj->tracer->x, mobj->tracer->y); + + // \todo account for distance between mobj and tracer + // Because closer mobjs can be facing beyond the angle tolerance + // yet tracer is still in the camera view + + // failure state: mobj is not facing tracer + // Reasaonable defaults: ANGLE_67h, ANGLE_292h + if (ang >= (UINT32)mobj->extravalue1 && ang <= ANGLE_MAX - (UINT32)mobj->extravalue1) + { + if (mobj->cvmem) + mobj->cvmem--; + else + { + INT32 exectag = mobj->extravalue2; // remember this before we erase the values + + if (mobj->lastlook) + mobj->cvmem = mobj->cusval; // reset timer for next failure + else + { + // disable after first failure + mobj->eflags &= ~MFE_TRACERANGLE; + mobj->lastlook = mobj->extravalue1 = mobj->extravalue2 = mobj->cvmem = mobj->cusval = 0; + } + + P_LinedefExecute(exectag, mobj, NULL); + } + } + else + mobj->cvmem = mobj->cusval; // reset failure timer + } + switch (mobj->type) { case MT_WALLSPIKEBASE: diff --git a/src/p_mobj.h b/src/p_mobj.h index dd4da765d..eac5118b8 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -239,6 +239,9 @@ typedef enum MFE_SPRUNG = 1<<8, // Platform movement MFE_APPLYPMOMZ = 1<<9, + // Compute and trigger on mobj angle relative to tracer + // See Linedef Exec 457 (Track mobj angle to point) + MFE_TRACERANGLE = 1<<10, // free: to and including 1<<15 } mobjeflag_t; diff --git a/src/p_spec.c b/src/p_spec.c index 9f9e80ecc..965e0cbe0 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3757,6 +3757,41 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) P_ResetColormapFader(§ors[secnum]); break; + case 457: // Track mobj angle to point + if (mo) + { + INT32 failureangle = min(max(abs(sides[line->sidenum[0]].textureoffset>>FRACBITS), 0), 360) * ANG1; + INT32 failuredelay = abs(sides[line->sidenum[0]].rowoffset>>FRACBITS); + INT32 failureexectag = line->sidenum[1] != 0xffff ? + (INT32)(sides[line->sidenum[1]].textureoffset>>FRACBITS) : 0; + boolean persist = (line->flags & ML_EFFECT2); + mobj_t *anchormo; + + if ((secnum = P_FindSectorFromLineTag(line, -1)) < 0) + return; + + anchormo = P_GetObjectTypeInSectorNum(MT_ANGLEMAN, secnum); + if (!anchormo) + return; + + mo->eflags |= MFE_TRACERANGLE; + P_SetTarget(&mo->tracer, anchormo); + mo->lastlook = persist; // don't disable behavior after first failure + mo->extravalue1 = failureangle; // angle to exceed for failure state + mo->extravalue2 = failureexectag; // exec tag for failure state (angle is not within range) + mo->cusval = mo->cvmem = failuredelay; // cusval = tics to allow failure before line trigger; cvmem = decrement timer + } + break; + + case 458: // Stop tracking mobj angle to point + if (mo && (mo->eflags & MFE_TRACERANGLE)) + { + mo->eflags &= ~MFE_TRACERANGLE; + P_SetTarget(&mo->tracer, NULL); + mo->lastlook = mo->cvmem = mo->cusval = mo->extravalue1 = mo->extravalue2 = 0; + } + break; + #ifdef POLYOBJECTS case 480: // Polyobj_DoorSlide case 481: // Polyobj_DoorSwing