mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-04-11 04:22:40 +00:00
Added initial Behavior API
Allows for arbitrary behaviors to be attached to Actors in a way that doesn't require the use of Inventory tokens. This list can't be freely modified nor can it have duplicates meaning it has far better deterministic behavior than using Inventory items for this purpose.
This commit is contained in:
parent
7d180069e3
commit
bd60fd7546
6 changed files with 308 additions and 1 deletions
src
wadsrc/static/zscript/actors
|
@ -452,6 +452,7 @@ xx(Readthismenu)
|
|||
xx(Playermenu)
|
||||
|
||||
// more stuff
|
||||
xx(Behavior)
|
||||
xx(ColorSet)
|
||||
xx(NeverSwitchOnPickup)
|
||||
xx(MoveBob)
|
||||
|
|
|
@ -793,6 +793,7 @@ public:
|
|||
|
||||
virtual void OnDestroy() override;
|
||||
virtual void Serialize(FSerializer &arc) override;
|
||||
virtual size_t PropagateMark() override;
|
||||
virtual void PostSerialize() override;
|
||||
virtual void PostBeginPlay() override; // Called immediately before the actor's first tick
|
||||
virtual void Tick() override;
|
||||
|
@ -1373,6 +1374,7 @@ public:
|
|||
// landing speed from a jump with normal gravity (squats the player's view)
|
||||
// (note: this is put into AActor instead of the PlayerPawn because non-players also use the value)
|
||||
double LandingSpeed;
|
||||
TMap<FName, DObject*> Behaviors;
|
||||
|
||||
|
||||
// ThingIDs
|
||||
|
@ -1434,6 +1436,17 @@ public:
|
|||
return GetClass()->FindState(numnames, names, exact);
|
||||
}
|
||||
|
||||
DObject* FindBehavior(const PClass& type) const
|
||||
{
|
||||
auto b = Behaviors.CheckKey(type.TypeName);
|
||||
return b != nullptr && *b != nullptr && !((*b)->ObjectFlags & OF_EuthanizeMe) ? *b : nullptr;
|
||||
}
|
||||
DObject* AddBehavior(const PClass& type);
|
||||
bool RemoveBehavior(const PClass& type);
|
||||
void TickBehaviors();
|
||||
void MoveBehaviors(AActor& from);
|
||||
void ClearBehaviors();
|
||||
|
||||
bool HasSpecialDeathStates () const;
|
||||
|
||||
double X() const
|
||||
|
|
|
@ -197,6 +197,22 @@ void AActor::EnableNetworking(const bool enable)
|
|||
Super::EnableNetworking(true);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// AActor :: PropagateMark
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t AActor::PropagateMark()
|
||||
{
|
||||
TMap<FName, DObject*>::Iterator it = { Behaviors };
|
||||
TMap<FName, DObject*>::Pair* pair = nullptr;
|
||||
while (it.NextPair(pair))
|
||||
GC::Mark(pair->Value);
|
||||
|
||||
return Super::PropagateMark();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// AActor :: Serialize
|
||||
|
@ -402,7 +418,8 @@ void AActor::Serialize(FSerializer &arc)
|
|||
("morphflags", MorphFlags)
|
||||
("premorphproperties", PremorphProperties)
|
||||
("morphexitflash", MorphExitFlash)
|
||||
("damagesource", damagesource);
|
||||
("damagesource", damagesource)
|
||||
("behaviors", Behaviors);
|
||||
|
||||
|
||||
SerializeTerrain(arc, "floorterrain", floorterrain, &def->floorterrain);
|
||||
|
@ -444,6 +461,229 @@ void AActor::PostSerialize()
|
|||
UpdateWaterLevel(false);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Behaviors allow for actions to be defined on Actors not coupled to
|
||||
// specific inventory tokens. Only one can be attached at a time.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool AActor::RemoveBehavior(const PClass& type)
|
||||
{
|
||||
if (Behaviors.CheckKey(type.TypeName))
|
||||
{
|
||||
auto b = Behaviors[type.TypeName];
|
||||
|
||||
Behaviors.Remove(type.TypeName);
|
||||
if (b != nullptr && !(b->ObjectFlags & OF_EuthanizeMe))
|
||||
{
|
||||
b->Destroy();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int RemoveBehavior(AActor* self, PClass* type)
|
||||
{
|
||||
return self->RemoveBehavior(*type);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, RemoveBehavior, RemoveBehavior)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_CLASS_NOT_NULL(type, DObject);
|
||||
ACTION_RETURN_BOOL(self->RemoveBehavior(*type));
|
||||
}
|
||||
|
||||
DObject* AActor::AddBehavior(const PClass& type)
|
||||
{
|
||||
if (type.bAbstract || !type.IsDescendantOf(NAME_Behavior))
|
||||
return nullptr;
|
||||
|
||||
auto b = FindBehavior(type);
|
||||
if (b == nullptr)
|
||||
{
|
||||
b = Create<DObject>();
|
||||
if (b == nullptr)
|
||||
return nullptr;
|
||||
|
||||
auto& owner = b->PointerVar<AActor>(NAME_Owner);
|
||||
owner = this;
|
||||
|
||||
Behaviors[type.TypeName] = b;
|
||||
IFOVERRIDENVIRTUALPTRNAME(b, NAME_Behavior, Initialize)
|
||||
{
|
||||
VMValue params[] = { b };
|
||||
VMCall(func, params, 1, nullptr, 0);
|
||||
|
||||
if (b->ObjectFlags & OF_EuthanizeMe)
|
||||
{
|
||||
RemoveBehavior(type);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IFOVERRIDENVIRTUALPTRNAME(b, NAME_Behavior, Readded)
|
||||
{
|
||||
VMValue params[] = { b };
|
||||
VMCall(func, params, 1, nullptr, 0);
|
||||
|
||||
if (b->ObjectFlags & OF_EuthanizeMe)
|
||||
{
|
||||
RemoveBehavior(type);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
static DObject* AddBehavior(AActor* self, PClass* type)
|
||||
{
|
||||
return self->AddBehavior(*type);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, AddBehavior, AddBehavior)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_CLASS_NOT_NULL(type, DObject);
|
||||
ACTION_RETURN_OBJECT(self->AddBehavior(*type));
|
||||
}
|
||||
|
||||
void AActor::TickBehaviors()
|
||||
{
|
||||
TArray<FName> toRemove = {};
|
||||
|
||||
TMap<FName, DObject*>::Iterator it = { Behaviors };
|
||||
TMap<FName, DObject*>::Pair* pair = nullptr;
|
||||
while (it.NextPair(pair))
|
||||
{
|
||||
auto b = pair->Value;
|
||||
if (b == nullptr || (b->ObjectFlags & OF_EuthanizeMe))
|
||||
{
|
||||
toRemove.Push(pair->Key);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& owner = b->PointerVar<AActor>(NAME_Owner);
|
||||
if (owner != this)
|
||||
{
|
||||
toRemove.Push(pair->Key);
|
||||
continue;
|
||||
}
|
||||
|
||||
IFOVERRIDENVIRTUALPTRNAME(b, NAME_Behavior, Tick)
|
||||
{
|
||||
VMValue params[] = { b };
|
||||
VMCall(func, params, 1, nullptr, 0);
|
||||
|
||||
if (b->ObjectFlags & OF_EuthanizeMe)
|
||||
toRemove.Push(pair->Key);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& type : toRemove)
|
||||
RemoveBehavior(*PClass::FindClass(type));
|
||||
}
|
||||
|
||||
static void TickBehaviors(AActor* self)
|
||||
{
|
||||
self->TickBehaviors();
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, TickBehaviors, TickBehaviors)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
self->TickBehaviors();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DObject* FindBehavior(AActor* self, PClass* type)
|
||||
{
|
||||
return self->FindBehavior(*type);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, FindBehavior, FindBehavior)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_CLASS_NOT_NULL(type, DObject);
|
||||
ACTION_RETURN_OBJECT(self->FindBehavior(*type));
|
||||
}
|
||||
|
||||
void AActor::MoveBehaviors(AActor& from)
|
||||
{
|
||||
// Clean these up properly before transferring.
|
||||
ClearBehaviors();
|
||||
|
||||
Behaviors.TransferFrom(from.Behaviors);
|
||||
|
||||
TArray<FName> toRemove = {};
|
||||
|
||||
// Clean up any empty behaviors that remained as well while
|
||||
// changing the owner.
|
||||
TMap<FName, DObject*>::Iterator it = { Behaviors };
|
||||
TMap<FName, DObject*>::Pair* pair = nullptr;
|
||||
while (it.NextPair(pair))
|
||||
{
|
||||
auto b = pair->Value;
|
||||
if (b == nullptr || (b->ObjectFlags & OF_EuthanizeMe))
|
||||
{
|
||||
toRemove.Push(pair->Key);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto owner = b->PointerVar<AActor>(NAME_Owner);
|
||||
owner = this;
|
||||
}
|
||||
|
||||
for (auto& type : toRemove)
|
||||
RemoveBehavior(*PClass::FindClass(type));
|
||||
}
|
||||
|
||||
static void MoveBehaviors(AActor* self, AActor* from)
|
||||
{
|
||||
self->MoveBehaviors(*from);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, MoveBehaviors, MoveBehaviors)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_OBJECT_NOT_NULL(from, AActor);
|
||||
self->MoveBehaviors(*from);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AActor::ClearBehaviors()
|
||||
{
|
||||
TArray<FName> toRemove = {};
|
||||
|
||||
TMap<FName, DObject*>::Iterator it = { Behaviors };
|
||||
TMap<FName, DObject*>::Pair* pair = nullptr;
|
||||
while (it.NextPair(pair))
|
||||
toRemove.Push(pair->Key);
|
||||
|
||||
for (auto& type : toRemove)
|
||||
RemoveBehavior(*PClass::FindClass(type));
|
||||
|
||||
Behaviors.Clear();
|
||||
}
|
||||
|
||||
static void ClearBehaviors(AActor* self)
|
||||
{
|
||||
self->ClearBehaviors();
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, ClearBehaviors, ClearBehaviors)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
self->ClearBehaviors();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
@ -3931,6 +4171,11 @@ void AActor::Tick ()
|
|||
return;
|
||||
}
|
||||
|
||||
// These should always tick regardless of prediction or not (let the behavior itself
|
||||
// handle this).
|
||||
if (!isFrozen())
|
||||
TickBehaviors();
|
||||
|
||||
if (flags5 & MF5_NOINTERACTION)
|
||||
{
|
||||
// only do the minimally necessary things here to save time:
|
||||
|
@ -5251,6 +5496,9 @@ void AActor::OnDestroy ()
|
|||
Level->localEventManager->WorldThingDestroyed(this);
|
||||
}
|
||||
|
||||
|
||||
ClearBehaviors();
|
||||
|
||||
DeleteAttachedLights();
|
||||
ClearRenderSectorList();
|
||||
ClearRenderLineList();
|
||||
|
@ -5497,6 +5745,8 @@ int MorphPointerSubstitution(AActor* from, AActor* to)
|
|||
VMCall(func, params, 2, nullptr, 0);
|
||||
}
|
||||
|
||||
to->MoveBehaviors(*from);
|
||||
|
||||
// Go through player infos.
|
||||
for (int i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
|
|
|
@ -226,6 +226,33 @@ FSerializer& FDoomSerializer::StatePointer(const char* key, void* ptraddr, bool
|
|||
}
|
||||
|
||||
|
||||
FSerializer& Serialize(FSerializer& arc, const char* key, TMap<FName, DObject*>& value, TMap<FName, DObject*>* def)
|
||||
{
|
||||
if (!arc.BeginObject(key))
|
||||
return arc;
|
||||
|
||||
if (arc.isWriting())
|
||||
{
|
||||
TMap<FName, DObject*>::Iterator it = { value };
|
||||
TMap<FName, DObject*>::Pair* pair = nullptr;
|
||||
while (it.NextPair(pair))
|
||||
arc(pair->Key.GetChars(), pair->Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* k = nullptr;
|
||||
while ((k = arc.GetKey()) != nullptr)
|
||||
{
|
||||
DObject* obj = nullptr;
|
||||
arc(k, obj);
|
||||
value[k] = obj;
|
||||
}
|
||||
}
|
||||
|
||||
arc.EndObject();
|
||||
return arc;
|
||||
}
|
||||
|
||||
|
||||
template<> FSerializer &Serialize(FSerializer &ar, const char *key, FPolyObj *&value, FPolyObj **defval)
|
||||
{
|
||||
|
|
|
@ -43,6 +43,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, ticcmd_t &sid, ticcmd_
|
|||
FSerializer &Serialize(FSerializer &arc, const char *key, usercmd_t &cmd, usercmd_t *def);
|
||||
FSerializer &Serialize(FSerializer &arc, const char *key, FInterpolator &rs, FInterpolator *def);
|
||||
FSerializer& Serialize(FSerializer& arc, const char* key, struct FStandaloneAnimation& value, struct FStandaloneAnimation* defval);
|
||||
FSerializer& Serialize(FSerializer& arc, const char* key, TMap<FName, DObject*>& value, TMap<FName, DObject*>* def);
|
||||
|
||||
template<> FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval);
|
||||
template<> FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval);
|
||||
|
|
|
@ -73,6 +73,14 @@ class ViewPosition native
|
|||
native readonly int Flags;
|
||||
}
|
||||
|
||||
class Behavior play abstract
|
||||
{
|
||||
readonly Actor Owner;
|
||||
|
||||
virtual void Initialize() {}
|
||||
virtual void Readded() {}
|
||||
virtual void Tick() {}
|
||||
}
|
||||
|
||||
class Actor : Thinker native
|
||||
{
|
||||
|
@ -500,6 +508,13 @@ class Actor : Thinker native
|
|||
return sin(fb * (180./32)) * 8;
|
||||
}
|
||||
|
||||
native clearscope Behavior FindBehavior(class<Behavior> type) const;
|
||||
native bool RemoveBehavior(class<Behavior> type);
|
||||
native Behavior AddBehavior(class<Behavior> type);
|
||||
native void TickBehaviors();
|
||||
native void ClearBehaviors();
|
||||
native void MoveBehaviors(Actor from);
|
||||
|
||||
native clearscope bool isFrozen() const;
|
||||
virtual native void BeginPlay();
|
||||
virtual native void Activate(Actor activator);
|
||||
|
|
Loading…
Reference in a new issue