mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-10 14:51:51 +00:00
- added FDARI's pointer operation submission.
SVN r3167 (trunk)
This commit is contained in:
parent
c52d9ec1b9
commit
820554d636
5 changed files with 348 additions and 23 deletions
|
@ -683,7 +683,7 @@ public:
|
|||
void ConversationAnimation (int animnum);
|
||||
|
||||
// Make this actor hate the same things as another actor
|
||||
void CopyFriendliness (AActor *other, bool changeTarget);
|
||||
void CopyFriendliness (AActor *other, bool changeTarget, bool resetHealth=true);
|
||||
|
||||
// Moves the other actor's inventory to this one
|
||||
void ObtainInventory (AActor *other);
|
||||
|
|
|
@ -770,7 +770,7 @@ bool AActor::GiveAmmo (const PClass *type, int amount)
|
|||
//
|
||||
//============================================================================
|
||||
|
||||
void AActor::CopyFriendliness (AActor *other, bool changeTarget)
|
||||
void AActor::CopyFriendliness (AActor *other, bool changeTarget, bool resetHealth)
|
||||
{
|
||||
level.total_monsters -= CountsAsKill();
|
||||
TIDtoHate = other->TIDtoHate;
|
||||
|
@ -786,7 +786,7 @@ void AActor::CopyFriendliness (AActor *other, bool changeTarget)
|
|||
// LastHeard must be set as well so that A_Look can react to the new target if called
|
||||
LastHeard = target = other->target;
|
||||
}
|
||||
health = SpawnHealth();
|
||||
if (resetHealth) health = SpawnHealth();
|
||||
level.total_monsters += CountsAsKill();
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,6 @@ static FRandom pr_burst ("Burst");
|
|||
static FRandom pr_monsterrefire ("MonsterRefire");
|
||||
static FRandom pr_teleport("A_Teleport");
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ACustomInventory :: CallStateChain
|
||||
|
@ -137,6 +136,291 @@ bool ACustomInventory::CallStateChain (AActor *actor, FState * State)
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Checks whether this actor is a missile
|
||||
// Unfortunately this was buggy in older versions of the code and many
|
||||
// released DECORATE monsters rely on this bug so it can only be fixed
|
||||
// with an optional flag
|
||||
//
|
||||
//==========================================================================
|
||||
inline static bool isMissile(AActor * self, bool precise=true)
|
||||
{
|
||||
return self->flags&MF_MISSILE || (precise && self->GetDefault()->flags&MF_MISSILE);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Pointer-based operations
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
|
||||
enum AAPTR
|
||||
{
|
||||
AAPTR_DEFAULT = 0,
|
||||
AAPTR_NULL = 0x1,
|
||||
AAPTR_TARGET = 0x2,
|
||||
AAPTR_MASTER = 0x4,
|
||||
AAPTR_TRACER = 0x8
|
||||
};
|
||||
|
||||
// [FDARI] Exported logic for guarding against loops in Target (for missiles) and Master (for all) chains.
|
||||
// It is called from multiple locations.
|
||||
// The code may be in need of optimisation.
|
||||
|
||||
void VerifyTargetChain(AActor *self, bool preciseMissileCheck=true)
|
||||
{
|
||||
if (!(self && isMissile(self, preciseMissileCheck))) return;
|
||||
AActor *origin = self;
|
||||
AActor *next = origin->target;
|
||||
|
||||
// origin: the most recent actor that has been verified as appearing only once
|
||||
// next: the next actor to be verified; will be "origin" in the next iteration
|
||||
|
||||
while (next && isMissile(next, preciseMissileCheck)) // we only care when there are missiles involved
|
||||
{
|
||||
AActor *compare = self;
|
||||
// every new actor must prove not to be the first actor in the chain, or any subsequent actor
|
||||
// any actor up to and including "origin" has only appeared once
|
||||
do
|
||||
{
|
||||
if (compare == next)
|
||||
{
|
||||
// if any of the actors from self to (inclusive) origin match the next actor,
|
||||
// self has reached/created a loop
|
||||
self->target = NULL;
|
||||
return;
|
||||
}
|
||||
if (compare == origin) break; // when "compare" = origin, we know that the next actor is, and should be "next"
|
||||
compare = compare->target;
|
||||
} while (true); // we're never leaving the loop here
|
||||
|
||||
origin = next;
|
||||
next = next->target;
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyMasterChain(AActor *self)
|
||||
{
|
||||
// See VerifyTargetChain for detailed comments.
|
||||
|
||||
if (!self) return;
|
||||
AActor *origin = self;
|
||||
AActor *next = origin->master;
|
||||
while (next) // We always care (See "VerifyTargetChain")
|
||||
{
|
||||
AActor *compare = self;
|
||||
do
|
||||
{
|
||||
if (compare == next)
|
||||
{
|
||||
self->master = NULL;
|
||||
return;
|
||||
}
|
||||
if (compare == origin) break;
|
||||
compare = compare->master;
|
||||
} while (true); // we're never leaving the loop here
|
||||
|
||||
origin = next;
|
||||
next = next->master;
|
||||
}
|
||||
}
|
||||
|
||||
enum PTROP
|
||||
{
|
||||
PTROP_UNSAFETARGET = 1,
|
||||
PTROP_UNSAFEMASTER = 2,
|
||||
PTROP_NOSAFEGUARDS = PTROP_UNSAFETARGET|PTROP_UNSAFEMASTER
|
||||
};
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// A_RearrangePointers
|
||||
//
|
||||
// Allow an actor to change its relationship to other actors by
|
||||
// copying pointers freely between TARGET MASTER and TRACER.
|
||||
// Can also assign null value, but does not duplicate A_ClearTarget.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
|
||||
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RearrangePointers)
|
||||
{
|
||||
ACTION_PARAM_START(4);
|
||||
ACTION_PARAM_INT(ptr_target, 0);
|
||||
ACTION_PARAM_INT(ptr_master, 1);
|
||||
ACTION_PARAM_INT(ptr_tracer, 2);
|
||||
ACTION_PARAM_INT(flags, 3);
|
||||
|
||||
// Rearrange pointers internally
|
||||
|
||||
// Fetch all values before modification, so that all fields can get original values
|
||||
AActor
|
||||
*gettarget = self->target,
|
||||
*getmaster = self->master,
|
||||
*gettracer = self->tracer;
|
||||
|
||||
switch (ptr_target) // pick the new target
|
||||
{
|
||||
case AAPTR_MASTER:
|
||||
self->target = getmaster;
|
||||
if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(self);
|
||||
break;
|
||||
case AAPTR_TRACER:
|
||||
self->target = gettracer;
|
||||
if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(self);
|
||||
break;
|
||||
case AAPTR_NULL:
|
||||
self->target = NULL;
|
||||
// THIS IS NOT "A_ClearTarget", so no other targeting info is removed
|
||||
break;
|
||||
}
|
||||
|
||||
// presently permitting non-monsters to set master
|
||||
switch (ptr_master) // pick the new master
|
||||
{
|
||||
case AAPTR_TARGET:
|
||||
self->master = gettarget;
|
||||
if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(self);
|
||||
break;
|
||||
case AAPTR_TRACER:
|
||||
self->master = gettracer;
|
||||
if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(self);
|
||||
break;
|
||||
case AAPTR_NULL:
|
||||
self->master = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ptr_tracer) // pick the new tracer
|
||||
{
|
||||
case AAPTR_TARGET:
|
||||
self->tracer = gettarget;
|
||||
break; // no verification deemed necessary; the engine never follows a tracer chain(?)
|
||||
case AAPTR_MASTER:
|
||||
self->tracer = getmaster;
|
||||
break; // no verification deemed necessary; the engine never follows a tracer chain(?)
|
||||
case AAPTR_NULL:
|
||||
self->tracer = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// A_TransferPointer
|
||||
//
|
||||
// Copy one pointer (MASTER, TARGET or TRACER) from this actor (SELF),
|
||||
// or from an this actor's MASTER, TARGET or TRACER.
|
||||
//
|
||||
// You can copy any one of that actor's pointers
|
||||
//
|
||||
// Assign the copied pointer to any one pointer in SELF,
|
||||
// MASTER, TARGET or TRACER.
|
||||
//
|
||||
// Any attempt to make an actor point to itself will replace the pointer
|
||||
// with a null value.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TransferPointer)
|
||||
{
|
||||
ACTION_PARAM_START(5);
|
||||
ACTION_PARAM_INT(ptr_source, 0);
|
||||
ACTION_PARAM_INT(ptr_recepient, 1);
|
||||
ACTION_PARAM_INT(ptr_sourcefield, 2);
|
||||
ACTION_PARAM_INT(ptr_recepientfield, 3);
|
||||
ACTION_PARAM_INT(flags, 4);
|
||||
|
||||
AActor *source, *recepient;
|
||||
|
||||
// Exchange pointers with actors to whom you have pointers (or with yourself, if you must)
|
||||
|
||||
switch (ptr_source) // pick an actor to provide a pointer
|
||||
{
|
||||
case AAPTR_DEFAULT: source = self; break;
|
||||
case AAPTR_TARGET: source = self->target; break;
|
||||
case AAPTR_MASTER: source = self->master; break;
|
||||
case AAPTR_TRACER: source = self->tracer; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
if (!source) return; // you must pick somebody. MAYBE we should make a null assignment instead of just returning.
|
||||
|
||||
switch (ptr_recepient) // pick an actor to store the provided pointer value
|
||||
{
|
||||
case AAPTR_DEFAULT: recepient = self; break;
|
||||
case AAPTR_TARGET: recepient = self->target; break;
|
||||
case AAPTR_MASTER: recepient = self->master; break;
|
||||
case AAPTR_TRACER: recepient = self->tracer; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
if (!recepient) return; // you must pick somebody. No way we can even make a null assignment here.
|
||||
|
||||
switch (ptr_sourcefield) // convert source from dataprovider to data
|
||||
{
|
||||
case AAPTR_TARGET: source = source->target; break; // now we don't care where the data comes from anymore
|
||||
case AAPTR_MASTER: source = source->master; break; // so we reassign source; it now holds the data itself
|
||||
case AAPTR_TRACER: source = source->tracer; break;
|
||||
default: source = NULL;
|
||||
}
|
||||
|
||||
if (source == recepient) source = NULL; // The recepient should not acquire a pointer to itself; will write NULL
|
||||
|
||||
if (ptr_recepientfield == AAPTR_DEFAULT) ptr_recepientfield = ptr_sourcefield; // If default: Write to same field as data was read from
|
||||
|
||||
switch (ptr_recepientfield) // assignment and safeguards (optional)
|
||||
{
|
||||
case AAPTR_TARGET:
|
||||
recepient->target = source;
|
||||
if (!(PTROP_UNSAFETARGET & flags)) VerifyTargetChain(recepient);
|
||||
break;
|
||||
case AAPTR_MASTER:
|
||||
recepient->master = source;
|
||||
if (!(PTROP_UNSAFEMASTER & flags)) VerifyMasterChain(recepient);
|
||||
break;
|
||||
case AAPTR_TRACER:
|
||||
recepient->tracer = source;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// A_CopyFriendliness
|
||||
//
|
||||
// Join forces with one of the actors you are pointing to (MASTER by default)
|
||||
//
|
||||
// Normal CopyFriendliness reassigns health. This function will not.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CopyFriendliness)
|
||||
{
|
||||
ACTION_PARAM_START(1);
|
||||
ACTION_PARAM_INT(ptr_source, 0);
|
||||
|
||||
if (self->player) return;
|
||||
|
||||
AActor *source;
|
||||
|
||||
switch (ptr_source)
|
||||
{
|
||||
case AAPTR_TARGET: source = self->target; break;
|
||||
case AAPTR_MASTER: source = self->master; break;
|
||||
case AAPTR_TRACER: source = self->tracer; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
if (source) self->CopyFriendliness(source, false, false); // Overriding default behaviour: No modification of health
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Simple flag changers
|
||||
|
@ -676,19 +960,6 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CallSpecial)
|
|||
ACTION_SET_RESULT(res);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Checks whether this actor is a missile
|
||||
// Unfortunately this was buggy in older versions of the code and many
|
||||
// released DECORATE monsters rely on this bug so it can only be fixed
|
||||
// with an optional flag
|
||||
//
|
||||
//==========================================================================
|
||||
inline static bool isMissile(AActor * self, bool precise=true)
|
||||
{
|
||||
return self->flags&MF_MISSILE || (precise && self->GetDefault()->flags&MF_MISSILE);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// The ultimate code pointer: Fully customizable missiles!
|
||||
|
@ -1382,10 +1653,28 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomRailgun)
|
|||
|
||||
static void DoGiveInventory(AActor * receiver, DECLARE_PARAMINFO)
|
||||
{
|
||||
ACTION_PARAM_START(2);
|
||||
ACTION_PARAM_START(3); // param count up
|
||||
ACTION_PARAM_CLASS(mi, 0);
|
||||
ACTION_PARAM_INT(amount, 1);
|
||||
|
||||
// [FDARI] Modified code: Allow any pointer to be used for receiver
|
||||
ACTION_PARAM_INT(setreceiver, 2);
|
||||
|
||||
switch (setreceiver)
|
||||
{
|
||||
case AAPTR_TARGET:
|
||||
if (receiver->target) { receiver = receiver->target; break; }
|
||||
return;
|
||||
case AAPTR_MASTER:
|
||||
if (receiver->master) { receiver = receiver->master; break; }
|
||||
return;
|
||||
case AAPTR_TRACER:
|
||||
if (receiver->tracer) { receiver = receiver->tracer; break; }
|
||||
return;
|
||||
}
|
||||
|
||||
// FDARI: End of modified code
|
||||
|
||||
bool res=true;
|
||||
if (receiver == NULL) return;
|
||||
|
||||
|
@ -1413,7 +1702,7 @@ static void DoGiveInventory(AActor * receiver, DECLARE_PARAMINFO)
|
|||
else res = false;
|
||||
ACTION_SET_RESULT(res);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveInventory)
|
||||
{
|
||||
|
@ -1438,10 +1727,28 @@ enum
|
|||
|
||||
void DoTakeInventory(AActor * receiver, DECLARE_PARAMINFO)
|
||||
{
|
||||
ACTION_PARAM_START(3);
|
||||
ACTION_PARAM_START(4); // param count up
|
||||
ACTION_PARAM_CLASS(item, 0);
|
||||
ACTION_PARAM_INT(amount, 1);
|
||||
ACTION_PARAM_INT(flags, 2);
|
||||
|
||||
// [FDARI] Modified code: Allow any pointer to be used for receiver
|
||||
ACTION_PARAM_INT(setreceiver, 3);
|
||||
|
||||
switch (setreceiver)
|
||||
{
|
||||
case AAPTR_TARGET:
|
||||
if (receiver->target) { receiver = receiver->target; break; }
|
||||
return;
|
||||
case AAPTR_MASTER:
|
||||
if (receiver->master) { receiver = receiver->master; break; }
|
||||
return;
|
||||
case AAPTR_TRACER:
|
||||
if (receiver->tracer) { receiver = receiver->tracer; break; }
|
||||
return;
|
||||
}
|
||||
|
||||
// FDARI: End of modified code
|
||||
|
||||
if (item == NULL || receiver == NULL) return;
|
||||
|
||||
|
@ -1471,7 +1778,7 @@ void DoTakeInventory(AActor * receiver, DECLARE_PARAMINFO)
|
|||
else inv->Amount-=amount;
|
||||
}
|
||||
ACTION_SET_RESULT(res);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeInventory)
|
||||
{
|
||||
|
|
|
@ -198,8 +198,8 @@ ACTOR Actor native //: Thinker
|
|||
action native A_JumpIfMasterCloser(float distance, state label);
|
||||
action native A_JumpIfInventory(class<Inventory> itemtype, int itemamount, state label);
|
||||
action native A_JumpIfArmorType(string Type, state label, int amount = 1);
|
||||
action native A_GiveInventory(class<Inventory> itemtype, int amount = 0);
|
||||
action native A_TakeInventory(class<Inventory> itemtype, int amount = 0, int flags = 0);
|
||||
action native A_GiveInventory(class<Inventory> itemtype, int amount = 0, int giveto = AAPTR_DEFAULT);
|
||||
action native A_TakeInventory(class<Inventory> itemtype, int amount = 0, int flags = 0, int giveto = AAPTR_DEFAULT);
|
||||
action native A_SpawnItem(class<Actor> itemtype = "Unknown", float distance = 0, float zheight = 0, bool useammo = true, bool transfer_translation = false);
|
||||
action native A_SpawnItemEx(class<Actor> itemtype, float xofs = 0, float yofs = 0, float zofs = 0, float xvel = 0, float yvel = 0, float zvel = 0, float angle = 0, int flags = 0, int failchance = 0);
|
||||
action native A_Print(string whattoprint, float time = 0, string fontname = "");
|
||||
|
@ -284,6 +284,10 @@ ACTOR Actor native //: Thinker
|
|||
|
||||
action native A_CheckSightOrRange(float distance, state label);
|
||||
|
||||
action native A_RearrangePointers(int newtarget, int newmaster = AAPTR_DEFAULT, int newtracer = AAPTR_DEFAULT, int flags=0);
|
||||
action native A_TransferPointer(int ptr_source, int ptr_recepient, int sourcefield, int recepientfield=AAPTR_DEFAULT, int flags=0);
|
||||
action native A_CopyFriendliness(int ptr_source = AAPTR_MASTER);
|
||||
|
||||
States
|
||||
{
|
||||
Spawn:
|
||||
|
|
|
@ -197,6 +197,20 @@ Const Int BLOCKF_EVERYTHING = 32;
|
|||
Const Int BLOCKF_RAILING = 64;
|
||||
Const Int BLOCKF_USE = 128;
|
||||
|
||||
// Pointer constants, bitfield-enabled
|
||||
|
||||
Const Int AAPTR_DEFAULT = 0;
|
||||
Const Int AAPTR_NULL = 1;
|
||||
Const Int AAPTR_TARGET = 2;
|
||||
Const Int AAPTR_MASTER = 4;
|
||||
Const Int AAPTR_TRACER = 8;
|
||||
|
||||
// Pointer operation flags
|
||||
|
||||
Const Int PTROP_UNSAFETARGET = 1;
|
||||
Const Int PTROP_UNSAFEMASTER = 2;
|
||||
Const Int PTROP_NOSAFEGUARDS = PTROP_UNSAFETARGET|PTROP_UNSAFEMASTER;
|
||||
|
||||
|
||||
|
||||
// This is only here to provide one global variable for testing.
|
||||
|
|
Loading…
Reference in a new issue