From 5d65ab6e6c21e1efeffe1ad41783ed3bcb17b557 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 13 Jun 2011 10:22:47 +0000 Subject: [PATCH] - cleaned up setPointer interface. - ZDoom part of setPointer/setActivator, submitted by FDARI. SVN r3230 (trunk) --- src/CMakeLists.txt | 1 + src/actor.h | 6 + src/actorptrselect.cpp | 179 ++++++++++++++++++++++++++++++ src/actorptrselect.h | 80 +++++-------- src/p_acs.cpp | 37 ++++-- src/thingdef/thingdef_codeptr.cpp | 120 ++------------------ zdoom.vcproj | 4 + 7 files changed, 257 insertions(+), 170 deletions(-) create mode 100644 src/actorptrselect.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4072e56fd..d1b302ef6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -616,6 +616,7 @@ add_executable( zdoom WIN32 ${SYSTEM_SOURCES} ${X86_SOURCES} x86.cpp + actorptrselect.cpp am_map.cpp b_bot.cpp b_func.cpp diff --git a/src/actor.h b/src/actor.h index bcb894071..fecd11a73 100644 --- a/src/actor.h +++ b/src/actor.h @@ -715,6 +715,11 @@ public: int SpawnHealth(); int GibHealth(); + inline bool isMissile(bool precise=true) + { + return (flags&MF_MISSILE) || (precise && GetDefault()->flags&MF_MISSILE); + } + // Check for monsters that count as kill but excludes all friendlies. bool CountsAsKill() const { @@ -1046,6 +1051,7 @@ inline T *Spawn (fixed_t x, fixed_t y, fixed_t z, replace_t allowreplacement) return static_cast(AActor::StaticSpawn (RUNTIME_CLASS(T), x, y, z, allowreplacement)); } + void PrintMiscActorInfo(AActor * query); #define S_FREETARGMOBJ 1 diff --git a/src/actorptrselect.cpp b/src/actorptrselect.cpp new file mode 100644 index 000000000..f1f50eeb5 --- /dev/null +++ b/src/actorptrselect.cpp @@ -0,0 +1,179 @@ +#include "actorptrselect.h" +#include "actor.h" +#include "d_player.h" +#include "p_pspr.h" + +//========================================================================== +// +// Standard pointer acquisition functions +// +// Possible effective results at run-time +// assigntovariable = NULL (or a RETURN statement is issued) +// P_BulletSlope(pointer_owner, &temporary), assigntovariable = temporary +// assigntovariable = pointer_owner->target or ...->master or ...->tracer +// +//========================================================================== + + +/* + COPY_AAPTR + + Result overview in order of priority: + + 1. Caller is player and a player specific selector is specified: Player specific selector is used. + 2. Caller is non-null and a general actor selector is specified: General actor selector is used. + 3. A static actor selector is specified: Static actor selector is used. + 4. The origin actor is used. + + Only one selector of each type can be used. +*/ + +#define AAPTR_RESOLVE_PLAYERNUM(playernum) (playeringame[playernum] ? players[playernum].mo : NULL) + +AActor *COPY_AAPTR(AActor *origin, int selector) +{ + if (origin) + { + if (origin->player) + { + switch (selector & AAPTR_PLAYER_SELECTORS) + { + case AAPTR_PLAYER_GETTARGET: + { + AActor *gettarget = NULL; + P_BulletSlope(origin, &gettarget); + return gettarget; + } + case AAPTR_PLAYER_GETCONVERSATION: + return origin->player->ConversationNPC; + } + } + + switch (selector & AAPTR_GENERAL_SELECTORS) + { + case AAPTR_TARGET: return origin->target; + case AAPTR_MASTER: return origin->master; + case AAPTR_TRACER: return origin->tracer; + case AAPTR_FRIENDPLAYER: + return origin->FriendPlayer ? AAPTR_RESOLVE_PLAYERNUM(origin->FriendPlayer - 1) : NULL; + } + } + + switch (selector & AAPTR_STATIC_SELECTORS) + { + case AAPTR_PLAYER1: return AAPTR_RESOLVE_PLAYERNUM(0); + case AAPTR_PLAYER2: return AAPTR_RESOLVE_PLAYERNUM(1); + case AAPTR_PLAYER3: return AAPTR_RESOLVE_PLAYERNUM(2); + case AAPTR_PLAYER4: return AAPTR_RESOLVE_PLAYERNUM(3); + case AAPTR_PLAYER5: return AAPTR_RESOLVE_PLAYERNUM(4); + case AAPTR_PLAYER6: return AAPTR_RESOLVE_PLAYERNUM(5); + case AAPTR_PLAYER7: return AAPTR_RESOLVE_PLAYERNUM(6); + case AAPTR_PLAYER8: return AAPTR_RESOLVE_PLAYERNUM(7); + case AAPTR_NULL: return NULL; + } + + return origin; +} + + +// [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. + + +//========================================================================== +// +// 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 +// +//========================================================================== + +void VerifyTargetChain(AActor *self, bool preciseMissileCheck) +{ + if (!self || !self->isMissile(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 && next->isMissile(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 + for (;;) + { + 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; + } + + 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; + for (;;) + { + if (compare == next) + { + self->master = NULL; + return; + } + if (compare == origin) break; + compare = compare->master; + } + + origin = next; + next = next->master; + } +} + +//========================================================================== +// +// 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 +// +//========================================================================== + +void ASSIGN_AAPTR(AActor *toActor, int toSlot, AActor *ptr, int flags) +{ + switch (toSlot) + { + case AAPTR_TARGET: + toActor->target = ptr; + if (!(PTROP_UNSAFETARGET & (flags))) VerifyTargetChain(toActor); + break; + + case AAPTR_MASTER: + toActor->master = ptr; + if (!(PTROP_UNSAFEMASTER & (flags))) VerifyMasterChain(toActor); + break; + + case AAPTR_TRACER: + toActor->tracer = ptr; + break; + } +} \ No newline at end of file diff --git a/src/actorptrselect.h b/src/actorptrselect.h index 8cc05f2da..46e1aa54d 100644 --- a/src/actorptrselect.h +++ b/src/actorptrselect.h @@ -1,14 +1,9 @@ #pragma once -#include "p_pspr.h" - //========================================================================== // // Standard pointer acquisition functions // -// Use COPY_AAPTR(pointer_owner, AActor *assigntovariable, AAPTR selector) -// Use COPY_AAPTR_NOT_NULL to return from a function if the pointer is NULL -// // Possible effective results at run-time // assigntovariable = NULL (or a RETURN statement is issued) // P_BulletSlope(pointer_owner, &temporary), assigntovariable = temporary @@ -16,6 +11,7 @@ // //========================================================================== +class AActor; // Pointer selectors (enum) @@ -55,7 +51,7 @@ enum AAPTR }; /* - PROCESS_AAPTR + COPY_AAPTR Result overview in order of priority: @@ -67,52 +63,36 @@ enum AAPTR Only one selector of each type can be used. */ -#define AAPTR_RESOLVE_PLAYERNUM(playernum) (playeringame[playernum] ? players[playernum].mo : NULL) +AActor *COPY_AAPTR(AActor *origin, int selector); -static AActor *PROCESS_AAPTR(AActor *origin, int selector) -{ - if (origin) - { - if (origin->player) - { - switch (selector & AAPTR_PLAYER_SELECTORS) - { - case AAPTR_PLAYER_GETTARGET: - { - AActor *gettarget = NULL; - P_BulletSlope(origin, &gettarget); - return gettarget; - } - case AAPTR_PLAYER_GETCONVERSATION: - return origin->player->ConversationNPC; - } - } +// Use COPY_AAPTR_NOT_NULL to return from a function if the pointer is NULL +#define COPY_AAPTR_NOT_NULL(source, destination, selector) { destination = COPY_AAPTR(source, selector); if (!destination) return; } - switch (selector & AAPTR_GENERAL_SELECTORS) - { - case AAPTR_TARGET: return origin->target; - case AAPTR_MASTER: return origin->master; - case AAPTR_TRACER: return origin->tracer; - case AAPTR_FRIENDPLAYER: - return origin->FriendPlayer ? AAPTR_RESOLVE_PLAYERNUM(origin->FriendPlayer - 1) : NULL; - } - } - switch (selector & AAPTR_STATIC_SELECTORS) - { - case AAPTR_PLAYER1: return AAPTR_RESOLVE_PLAYERNUM(0); - case AAPTR_PLAYER2: return AAPTR_RESOLVE_PLAYERNUM(1); - case AAPTR_PLAYER3: return AAPTR_RESOLVE_PLAYERNUM(2); - case AAPTR_PLAYER4: return AAPTR_RESOLVE_PLAYERNUM(3); - case AAPTR_PLAYER5: return AAPTR_RESOLVE_PLAYERNUM(4); - case AAPTR_PLAYER6: return AAPTR_RESOLVE_PLAYERNUM(5); - case AAPTR_PLAYER7: return AAPTR_RESOLVE_PLAYERNUM(6); - case AAPTR_PLAYER8: return AAPTR_RESOLVE_PLAYERNUM(7); - case AAPTR_NULL: return NULL; - } - return origin; -} +enum PTROP + { + PTROP_UNSAFETARGET = 1, + PTROP_UNSAFEMASTER = 2, + PTROP_NOSAFEGUARDS = PTROP_UNSAFETARGET|PTROP_UNSAFEMASTER +}; + + +// [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. + + +//========================================================================== +// +// 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 +// +//========================================================================== + +void VerifyTargetChain(AActor *self, bool preciseMissileCheck=true); +void VerifyMasterChain(AActor *self); +void ASSIGN_AAPTR(AActor *toActor, int toSlot, AActor *ptr, int flags) ; -#define COPY_AAPTR_NOT_NULL(source, destination, selector) { destination = PROCESS_AAPTR(source, selector); if (!destination) return; } -#define COPY_AAPTR(source, destination, selector) { destination = PROCESS_AAPTR(source, selector); } \ No newline at end of file diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 14a87231b..ac7e94fa5 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -71,6 +71,7 @@ #include "m_png.h" #include "p_setup.h" #include "po_man.h" +#include "actorptrselect.h" #include "g_shared/a_pickups.h" @@ -3114,6 +3115,7 @@ enum EACSFunctions ACSF_CheckSight, ACSF_SpawnForced, ACSF_AnnouncerSound, // Skulltag + ACSF_SetPointer, }; int DLevelScript::SideFromID(int id, int side) @@ -3248,8 +3250,29 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args) actor = SingleActorFromTID(args[0], activator); return actor != NULL? actor->velz : 0; + case ACSF_SetPointer: + if (activator) + { + AActor *ptr = SingleActorFromTID(args[1], activator); + if (argCount > 2) + { + ptr = COPY_AAPTR(ptr, args[2]); + } + if (ptr == activator) ptr = NULL; + ASSIGN_AAPTR(activator, args[0], ptr, (argCount > 3) ? args[3] : 0); + return ptr != NULL; + } + return 0; + case ACSF_SetActivator: - activator = SingleActorFromTID(args[0], NULL); + if (argCount > 1 && args[1] != AAPTR_DEFAULT) // condition (x != AAPTR_DEFAULT) is essentially condition (x). + { + activator = COPY_AAPTR(SingleActorFromTID(args[0], activator), args[1]); + } + else + { + activator = SingleActorFromTID(args[0], NULL); + } return activator != NULL; case ACSF_SetActivatorToTarget: @@ -3265,16 +3288,16 @@ int DLevelScript::CallFunction(int argCount, int funcIndex, SDWORD *args) { actor = actor->target; } - } - if (actor != NULL) - { - activator = actor; - return 1; + if (actor != NULL) // [FDARI] moved this (actor != NULL)-branch inside the other, so that it is only tried when it can be true + { + activator = actor; + return 1; + } } return 0; case ACSF_GetActorViewHeight: - actor = SingleActorFromTID(args[0], NULL); + actor = SingleActorFromTID(args[0], activator); if (actor != NULL) { if (actor->player != NULL) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index f0098f102..06f8bbc96 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -137,99 +137,6 @@ 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 PTROP - { - PTROP_UNSAFETARGET = 1, - PTROP_UNSAFEMASTER = 2, - PTROP_NOSAFEGUARDS = PTROP_UNSAFETARGET|PTROP_UNSAFEMASTER -}; - - -// [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 - for (;;) - { - 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; - } - - 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; - for (;;) - { - if (compare == next) - { - self->master = NULL; - return; - } - if (compare == origin) break; - compare = compare->master; - } - - origin = next; - next = next->master; - } -} - //========================================================================== // // A_RearrangePointers @@ -308,7 +215,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RearrangePointers) // A_TransferPointer // // Copy one pointer (MASTER, TARGET or TRACER) from this actor (SELF), -// or from an this actor's MASTER, TARGET or TRACER. +// or from this actor's MASTER, TARGET or TRACER. // // You can copy any one of that actor's pointers // @@ -333,31 +240,18 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TransferPointer) // Exchange pointers with actors to whom you have pointers (or with yourself, if you must) - COPY_AAPTR(self, source, ptr_source); + source = COPY_AAPTR(self, ptr_source); COPY_AAPTR_NOT_NULL(self, recepient, ptr_recepient); // pick an actor to store the provided pointer value // convert source from dataprovider to data - COPY_AAPTR(source, source, ptr_sourcefield); + source = COPY_AAPTR(source, ptr_sourcefield); 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; - } + ASSIGN_AAPTR(recepient, ptr_recepientfield, source, flags); } //========================================================================== @@ -1033,10 +927,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CustomMissile) // handle projectile shooting projectiles - track the // links back to a real owner - if (isMissile(self, !!(flags & CMF_TRACKOWNER))) + if (self->isMissile(!!(flags & CMF_TRACKOWNER))) { AActor * owner=self ;//->target; - while (isMissile(owner, !!(flags & CMF_TRACKOWNER)) && owner->target) owner=owner->target; + while (owner->isMissile(!!(flags & CMF_TRACKOWNER)) && owner->target) owner=owner->target; targ=owner; missile->target=owner; // automatic handling of seeker missiles @@ -1777,7 +1671,7 @@ static bool InitSpawnedItem(AActor *self, AActor *mo, int flags) mo->angle=self->angle; if (flags & SIXF_TRANSFERPITCH) mo->pitch = self->pitch; - while (originator && isMissile(originator)) originator = originator->target; + while (originator && originator->isMissile()) originator = originator->target; if (flags & SIXF_TELEFRAG) { diff --git a/zdoom.vcproj b/zdoom.vcproj index d12e2df1b..3b59d535d 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -472,6 +472,10 @@ Name="!Source Files" Filter="c;cpp" > + +