diff --git a/src/playsim/p_enemy.cpp b/src/playsim/p_enemy.cpp index 55f501488..120f37231 100644 --- a/src/playsim/p_enemy.cpp +++ b/src/playsim/p_enemy.cpp @@ -1309,6 +1309,161 @@ int P_IsVisible(AActor *lookee, AActor *other, INTBOOL allaround, FLookExParams return P_CheckSight(lookee, other, SF_SEEPASTSHOOTABLELINES); } +bool isTargetablePlayer(AActor *actor, player_t *player, INTBOOL allaround, void* lookparams) +{ + FLookExParams* params = (FLookExParams*)lookparams; + + if (!(player->mo->flags & MF_SHOOTABLE)) + return false; // not shootable (observer or dead) + + if (actor->IsFriend(player->mo)) + return false; // same +MF_FRIENDLY, ignore + + if (player->cheats & CF_NOTARGET) + return false; // no target + + if (player->health <= 0) + return false; // dead + + if (!P_IsVisible(actor, player->mo, allaround, params)) + return false; // out of sight + + // [RC] Well, let's let special monsters with this flag active be able to see + // the player then, eh? + if (!(actor->flags6 & MF6_SEEINVISIBLE)) + { + if ((player->mo->flags & MF_SHADOW && !(actor->Level->i_compatflags & COMPATF_INVISIBILITY)) || + player->mo->flags3 & MF3_GHOST) + { + if (player->mo->Distance2D(actor) > 128 && player->mo->Vel.XY().LengthSquared() < 5 * 5) + { // Player is sneaking - can't detect + return false; + } + if (pr_lookforplayers() < 225) + { // Player isn't sneaking, but still didn't detect + return false; + } + } + } + + return true; +} + +bool ValidEnemyInBlock(AActor* lookee, AActor* other, void* lookparams) +{ + FLookExParams* params = (FLookExParams*)lookparams; + + if (!(other->flags & MF_SHOOTABLE)) + return false; // not shootable (observer or dead) + + if (other == lookee) + return false; // is self + + if (other->health <= 0) + return false; // dead + + if (other->flags2 & MF2_DORMANT) + return false; // don't target dormant things + + if (!(other->flags3 & MF3_ISMONSTER)) + return false; // don't target it if it isn't a monster (could be a barrel) + + if (other->flags7 & MF7_NEVERTARGET) + return false; + + bool keepChecking = false; + if (lookee->flags & MF_FRIENDLY) + { + if (other->flags & MF_FRIENDLY) + { + if (!lookee->IsFriend(other)) + { + // This is somebody else's friend, so go after it + keepChecking = true; + } + else if (other->target != NULL && !(other->target->flags & MF_FRIENDLY)) + { + other = other->target; + if (!(other->flags & MF_SHOOTABLE) || + other->health <= 0 || + (other->flags2 & MF2_DORMANT)) + { + return false; + } + } + } + else + { + keepChecking = true; + } + } + else if (lookee->flags8 & MF8_SEEFRIENDLYMONSTERS && other->flags & MF_FRIENDLY) + { + keepChecking = true; + } + + // [MBF] If the monster is already engaged in a one-on-one attack + // with a healthy friend, don't attack around 60% the time. + + // [GrafZahl] This prevents friendlies from attacking all the same + // target. + + if (keepChecking) + { + AActor* targ = other->target; + if (targ && targ->target == other && pr_skiptarget() > 100 && lookee->IsFriend(targ) && + targ->health * 2 >= targ->SpawnHealth()) + { + return false; + } + } + + // [KS] Hey, shouldn't there be a check for MF3_NOSIGHTCHECK here? + + if (!keepChecking || !P_IsVisible(lookee, other, true, params)) + return false; // out of sight + + return true; +} + +//============================================================================ +// +// LookForEnemiesEx +// +// [inkoalawetrust] Return a script array of all valid enemies of the caller +// in range. For ZScript. +// +//============================================================================ + +DEFINE_ACTION_FUNCTION(AActor, LookForEnemiesEx) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OUTPOINTER(targets,TArray); + PARAM_FLOAT(range); + PARAM_BOOL(noPlayers); + PARAM_BOOL(allaround); + PARAM_POINTER(params, FLookExParams); + + if (targets == nullptr) + ThrowAbortException(X_WRITE_NIL,"No targets array passed"); + + if (range == -1) + range = self->friendlyseeblocks * FBlockmap::MAPBLOCKUNITS; + + FPortalGroupArray check(FPortalGroupArray::PGA_Full3d); + FMultiBlockThingsIterator it(check, self, range, false); + FMultiBlockThingsIterator::CheckResult cres; + + while (it.Next(&cres)) + { + if (cres.thing->player == nullptr && ValidEnemyInBlock(cres.thing, self, params) || + !noPlayers && cres.thing->player && isTargetablePlayer(self, cres.thing->player, allaround, params)) + targets->Push(cres.thing); + } + + ACTION_RETURN_INT(targets->Size()); +} + //--------------------------------------------------------------------------- // // FUNC P_LookForMonsters @@ -1552,80 +1707,10 @@ AActor *LookForEnemiesInBlock (AActor *lookee, int index, void *extparam) for (block = lookee->Level->blockmap.blocklinks[index]; block != NULL; block = block->NextActor) { - link = block->Me; - - if (!(link->flags & MF_SHOOTABLE)) - continue; // not shootable (observer or dead) - - if (link == lookee) + if (!ValidEnemyInBlock(lookee, block->Me, params)) continue; - if (link->health <= 0) - continue; // dead - - if (link->flags2 & MF2_DORMANT) - continue; // don't target dormant things - - if (!(link->flags3 & MF3_ISMONSTER)) - continue; // don't target it if it isn't a monster (could be a barrel) - - if (link->flags7 & MF7_NEVERTARGET) - continue; - - other = NULL; - if (lookee->flags & MF_FRIENDLY) - { - if (link->flags & MF_FRIENDLY) - { - if (!lookee->IsFriend(link)) - { - // This is somebody else's friend, so go after it - other = link; - } - else if (link->target != NULL && !(link->target->flags & MF_FRIENDLY)) - { - other = link->target; - if (!(other->flags & MF_SHOOTABLE) || - other->health <= 0 || - (other->flags2 & MF2_DORMANT)) - { - other = NULL;; - } - } - } - else - { - other = link; - } - } - else if (lookee->flags8 & MF8_SEEFRIENDLYMONSTERS && link->flags & MF_FRIENDLY) - { - other = link; - } - - // [MBF] If the monster is already engaged in a one-on-one attack - // with a healthy friend, don't attack around 60% the time. - - // [GrafZahl] This prevents friendlies from attacking all the same - // target. - - if (other) - { - AActor *targ = other->target; - if (targ && targ->target == other && pr_skiptarget() > 100 && lookee->IsFriend (targ) && - targ->health*2 >= targ->SpawnHealth()) - { - continue; - } - } - - // [KS] Hey, shouldn't there be a check for MF3_NOSIGHTCHECK here? - - if (other == NULL || !P_IsVisible (lookee, other, true, params)) - continue; // out of sight - - - return other; + return block->Me; } return NULL; } @@ -1825,38 +1910,8 @@ int P_LookForPlayers (AActor *actor, INTBOOL allaround, FLookExParams *params) player = actor->Level->Players[pnum]; - if (!(player->mo->flags & MF_SHOOTABLE)) - continue; // not shootable (observer or dead) - - if (actor->IsFriend(player->mo)) - continue; // same +MF_FRIENDLY, ignore - - if (player->cheats & CF_NOTARGET) - continue; // no target - - if (player->health <= 0) - continue; // dead - - if (!P_IsVisible (actor, player->mo, allaround, params)) - continue; // out of sight - - // [RC] Well, let's let special monsters with this flag active be able to see - // the player then, eh? - if(!(actor->flags6 & MF6_SEEINVISIBLE)) - { - if ((player->mo->flags & MF_SHADOW && !(actor->Level->i_compatflags & COMPATF_INVISIBILITY)) || - player->mo->flags3 & MF3_GHOST) - { - if (player->mo->Distance2D (actor) > 128 && player->mo->Vel.XY().LengthSquared() < 5*5) - { // Player is sneaking - can't detect - continue; - } - if (pr_lookforplayers() < 225) - { // Player isn't sneaking, but still didn't detect - continue; - } - } - } + if (!isTargetablePlayer(actor, player, allaround, params)) + continue; // [RH] Need to be sure the reactiontime is 0 if the monster is // leaving its goal to go after a player. diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index 7d24c36fe..970b23405 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -851,6 +851,7 @@ class Actor : Thinker native native bool LookForTid(bool allaround, LookExParams params = null); native bool LookForEnemies(bool allaround, LookExParams params = null); native bool LookForPlayers(bool allaround, LookExParams params = null); + native int LookForEnemiesEx(out Array targets, double range = -1, bool noPlayers = true, bool allaround = false, LookExParams params = null); native bool TeleportMove(Vector3 pos, bool telefrag, bool modifyactor = true); native clearscope double DistanceBySpeed(Actor other, double speed) const; native name GetSpecies();