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
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue