Added LookForEnemiesEx() for returning all enemies in an area. (#2753)
* Compartmentalized the LookForEnemiesInBlock checks * Added LookForEnemiesEx(). This function allows for ZScript code to get an array with all enemies of the caller found in range. Using similar sight logic as functions like LookForEnemies(). * Added noPlayers parameter to LookForEnemiesEx(). This parameter allows the function to also find players around it. * Added VM abort to LookForEnemiesEx(). Prevent crashes by passing a null array by reference.
This commit is contained in:
parent
c603494057
commit
9df656a9cf
2 changed files with 160 additions and 104 deletions
|
|
@ -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<AActor*>);
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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<Actor> 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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue