diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index a3288db87..04d54bde1 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -67,6 +67,7 @@ #include "v_palette.h" #include "g_shared/a_specialspot.h" #include "actorptrselect.h" +#include "m_bbox.h" static FRandom pr_camissile ("CustomActorfire"); @@ -4235,3 +4236,153 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedTerminate) ACTION_SET_RESULT(res); } + + +//========================================================================== +// +// A_RadiusGive +// +// Uses code roughly similar to A_Explode (but without all the compatibility +// baggage and damage computation code to give an item to all eligible mobjs +// in range. +// +//========================================================================== +enum RadiusGiveFlags +{ + RGF_GIVESELF = 1, + RGF_PLAYERS = 2, + RGF_MONSTERS = 4, + RGF_OBJECTS = 8, + RGF_VOODOO = 16, + RGF_CORPSES = 32, + RGF_MASK = 63, + RGF_NOTARGET = 64, + RGF_NOTRACER = 128, + RGF_NOMASTER = 256, + RGF_CUBE = 512, +}; + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) +{ + ACTION_PARAM_START(7); + ACTION_PARAM_CLASS(item, 0); + ACTION_PARAM_FIXED(distance, 1); + ACTION_PARAM_INT(flags, 2); + ACTION_PARAM_INT(amount, 3); + + // We need a valid item, valid targets, and a valid range + if (item == NULL || (flags & RGF_MASK) == 0 || distance <= 0) + { + return; + } + if (amount == 0) + { + amount = 1; + } + FBlockThingsIterator it(FBoundingBox(self->x, self->y, distance)); + double distsquared = double(distance) * double(distance); + + AActor *thing; + while ((thing = it.Next())) + { + // Don't give to inventory items + if (thing->flags & MF_SPECIAL) + { + continue; + } + // Avoid giving to self unless requested + if (thing == self && !(flags & RGF_GIVESELF)) + { + continue; + } + // Avoiding special pointers if requested + if (((thing == self->target) && (flags & RGF_NOTARGET)) || + ((thing == self->tracer) && (flags & RGF_NOTRACER)) || + ((thing == self->master) && (flags & RGF_NOMASTER))) + { + continue; + } + // Don't give to dead thing unless requested + if (thing->flags & MF_CORPSE) + { + if (!(flags & RGF_CORPSES)) + { + continue; + } + } + else if (thing->health <= 0 || thing->flags6 & MF6_KILLED) + { + continue; + } + // Players, monsters, and other shootable objects + if (thing->player) + { + if ((thing->player->mo == thing) && !(flags & RGF_PLAYERS)) + { + continue; + } + if ((thing->player->mo != thing) && !(flags & RGF_VOODOO)) + { + continue; + } + } + else if (thing->flags3 & MF3_ISMONSTER) + { + if (!(flags & RGF_MONSTERS)) + { + continue; + } + } + else if (thing->flags & MF_SHOOTABLE || thing->flags6 & MF6_VULNERABLE) + { + if (!(flags & RGF_OBJECTS)) + { + continue; + } + } + else + { + continue; + } + + if (flags & RGF_CUBE) + { // check if inside a cube + if (abs(thing->x - self->x) > distance || + abs(thing->y - self->y) > distance || + abs((thing->z + thing->height/2) - (self->z + self->height/2)) > distance) + { + continue; + } + } + else + { // check if inside a sphere + TVector3<double> tpos(thing->x, thing->y, thing->z + thing->height/2); + TVector3<double> spos(self->x, self->y, self->z + self->height/2); + if ((tpos - spos).LengthSquared() > distsquared) + { + continue; + } + } + fixed_t dz = abs ((thing->z + thing->height/2) - (self->z + self->height/2)); + + if (P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) + { // OK to give; target is in direct path + AInventory *gift = static_cast<AInventory *>(Spawn (item, 0, 0, 0, NO_REPLACE)); + if (gift->IsKindOf(RUNTIME_CLASS(AHealth))) + { + gift->Amount *= amount; + } + else + { + gift->Amount = amount; + } + gift->flags |= MF_DROPPED; + gift->ClearCounters(); + if (!gift->CallTryPickup (thing)) + { + gift->Destroy (); + } + } + } +} + diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index f48aeacc8..7e7a591e1 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -247,6 +247,7 @@ ACTOR Actor native //: Thinker action native A_JumpIfInTargetInventory(class<Inventory> itemtype, int amount, state label, int forward_ptr = AAPTR_DEFAULT); action native A_GiveToTarget(class<Inventory> itemtype, int amount = 0, int forward_ptr = AAPTR_DEFAULT); action native A_TakeFromTarget(class<Inventory> itemtype, int amount = 0, int flags = 0, int forward_ptr = AAPTR_DEFAULT); + action native A_RadiusGive(class<Inventory> itemtype, int distance, int flags, int amount = 0); action native A_CountdownArg(int argnum, state targstate = ""); action native A_CustomMeleeAttack(int damage = 0, sound meleesound = "", sound misssound = "", name damagetype = "none", bool bleed = true); action native A_CustomComboAttack(class<Actor> missiletype, float spawnheight, int damage, sound meleesound = "", name damagetype = "none", bool bleed = true); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 42dca0e3e..94ca8f9e6 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -139,6 +139,21 @@ const int TF_RANDOMDECIDE = 2; const int WAF_NORANDOM = 1; const int WAF_USEPUFF = 2; +// Flags for A_RadiusGive +enum +{ + RGF_GIVESELF = 1, + RGF_PLAYERS = 2, + RGF_MONSTERS = 4, + RGF_OBJECTS = 8, + RGF_VOODOO = 16, + RGF_CORPSES = 32, + RGF_NOTARGET = 64, + RGF_NOTRACER = 128, + RGF_NOMASTER = 256, + RGF_CUBE = 512, +}; + // Activation flags enum {