From ebc36650a4152545a480f8acc2b885940d2c3814 Mon Sep 17 00:00:00 2001 From: Major Cooke Date: Tue, 19 Nov 2024 14:26:59 -0600 Subject: [PATCH 1/2] Added WorldThingGoalReached(WorldEvent e). Called whenever an actor chasing a goal reaches it. - `Thing`: actor that reached the goal - `Inflictor`: The previous goal it just reached. - The new goal is stored in actor's `goal`. --- src/events.cpp | 26 ++++++++++++++++++++++++++ src/events.h | 5 ++++- src/playsim/p_enemy.cpp | 8 ++++++-- wadsrc/static/zscript/events.zs | 5 +++-- 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/events.cpp b/src/events.cpp index a4773d13326..5c19cd12b6d 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -737,6 +737,18 @@ void EventManager::WorldThingDamaged(AActor* actor, AActor* inflictor, AActor* s handler->WorldThingDamaged(actor, inflictor, source, damage, mod, flags, angle); } +void EventManager::WorldThingGoalReached(AActor* actor, AActor* oldgoal) +{ + // don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever. + if (actor->ObjectFlags & OF_EuthanizeMe) + return; + + if (ShouldCallStatic(true)) staticEventManager.WorldThingGoalReached(actor, oldgoal); + + for (DStaticEventHandler* handler = FirstEventHandler; handler; handler = handler->next) + handler->WorldThingGoalReached(actor, oldgoal); +} + void EventManager::WorldThingDestroyed(AActor* actor) { // don't call anything if actor was destroyed on PostBeginPlay/BeginPlay/whatever. @@ -1776,6 +1788,20 @@ void DStaticEventHandler::WorldThingDamaged(AActor* actor, AActor* inflictor, AA } } +void DStaticEventHandler::WorldThingGoalReached(AActor* actor, AActor* oldgoal) +{ + IFVIRTUAL(DStaticEventHandler, WorldThingGoalReached) + { + // don't create excessive DObjects if not going to be processed anyway + if (isEmpty(func)) return; + FWorldEvent e = owner->SetupWorldEvent(); + e.Thing = actor; + e.Inflictor = oldgoal; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; + VMCall(func, params, 2, nullptr, 0); + } +} + void DStaticEventHandler::WorldThingDestroyed(AActor* actor) { IFVIRTUAL(DStaticEventHandler, WorldThingDestroyed) diff --git a/src/events.h b/src/events.h index d71922b9a8b..749a574918c 100755 --- a/src/events.h +++ b/src/events.h @@ -310,6 +310,7 @@ class DStaticEventHandler : public DObject // make it a part of normal GC proces void WorldThingGround(AActor* actor, FState* st); void WorldThingRevived(AActor* actor); void WorldThingDamaged(AActor* actor, AActor* inflictor, AActor* source, int damage, FName mod, int flags, DAngle angle); + void WorldThingGoalReached(AActor* actor, AActor* oldgoal); void WorldThingDestroyed(AActor* actor); void WorldLinePreActivated(line_t* line, AActor* actor, int activationType, bool* shouldactivate); void WorldLineActivated(line_t* line, AActor* actor, int activationType); @@ -471,8 +472,10 @@ struct EventManager void WorldThingGround(AActor* actor, FState* st); // called after AActor::Revive. void WorldThingRevived(AActor* actor); - // called before P_DamageMobj and before AActor::DamageMobj virtuals. + // called after damage has been dealt void WorldThingDamaged(AActor* actor, AActor* inflictor, AActor* source, int damage, FName mod, int flags, DAngle angle); + // called when the actor changes goals. Goal is the OLD goal. + void WorldThingGoalReached(AActor* actor, AActor* oldgoal); // called before AActor::Destroy of each actor. void WorldThingDestroyed(AActor* actor); // called in P_ActivateLine before executing special, set shouldactivate to false to prevent activation. diff --git a/src/playsim/p_enemy.cpp b/src/playsim/p_enemy.cpp index 120f37231e0..454af40dd5f 100644 --- a/src/playsim/p_enemy.cpp +++ b/src/playsim/p_enemy.cpp @@ -51,6 +51,7 @@ #include "actorinlines.h" #include "a_ceiling.h" #include "shadowinlines.h" +#include "events.h" #include "gi.h" @@ -2549,7 +2550,8 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi // [RH] Don't attack if just moving toward goal if (actor->target == actor->goal || (actor->flags5&MF5_CHASEGOAL && actor->goal != NULL)) { - AActor * savedtarget = actor->target; + AActor *savedtarget = actor->target; + AActor *savedgoal = actor->goal; actor->target = actor->goal; bool result = P_CheckMeleeRange(actor); actor->target = savedtarget; @@ -2572,7 +2574,7 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi DAngle lastgoalang = actor->goal->Angles.Yaw; int delay; AActor * newgoal = iterator.Next (); - if (newgoal != NULL && actor->goal == actor->target) + if (newgoal != nullptr && actor->goal == actor->target) { delay = newgoal->args[1]; actor->reactiontime = delay * TICRATE + actor->Level->maptime; @@ -2592,6 +2594,8 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi } actor->flags7 &= ~MF7_INCHASE; actor->goal = newgoal; + if (actor->goal != savedgoal) + actor->Level->localEventManager->WorldThingGoalReached(actor, savedgoal); return; } if (actor->goal == actor->target) goto nomissile; diff --git a/wadsrc/static/zscript/events.zs b/wadsrc/static/zscript/events.zs index 52cf3a7eb23..82bee47963c 100644 --- a/wadsrc/static/zscript/events.zs +++ b/wadsrc/static/zscript/events.zs @@ -74,9 +74,9 @@ struct WorldEvent native play version("2.4") native readonly bool IsReopen; // for unloaded, name of next map (if any) native readonly String NextMap; - // for thingspawned/thingdied/thingdestroyed/thingground + // for thingspawned/thingdied/thingdestroyed/thingground/thinggoalreached native readonly Actor Thing; - // for thingdied. can be null + // for thingdied. can be null. [MC] Also used for ThingGoalReached, inflictor is the old goal. native readonly Actor Inflictor; // for thingdamaged, line/sector damaged native readonly int Damage; @@ -154,6 +154,7 @@ class StaticEventHandler : Object native play version("2.4") virtual void WorldThingGround(WorldEvent e) {} virtual void WorldThingRevived(WorldEvent e) {} virtual void WorldThingDamaged(WorldEvent e) {} + virtual void WorldThingGoalReached(WorldEvent e) {} virtual void WorldThingDestroyed(WorldEvent e) {} virtual void WorldLinePreActivated(WorldEvent e) {} virtual void WorldLineActivated(WorldEvent e) {} From 895462c46ae44046db1c91436c3c346c7f53b817 Mon Sep 17 00:00:00 2001 From: Major Cooke Date: Tue, 19 Nov 2024 19:22:17 -0600 Subject: [PATCH 2/2] Added GoalReached(Actor oldgoal) function to actors. --- src/playsim/p_enemy.cpp | 17 +++++++++++++++-- wadsrc/static/zscript/actors/actor.zs | 3 +++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/playsim/p_enemy.cpp b/src/playsim/p_enemy.cpp index 454af40dd5f..3d4d874ccb4 100644 --- a/src/playsim/p_enemy.cpp +++ b/src/playsim/p_enemy.cpp @@ -2371,6 +2371,20 @@ DEFINE_ACTION_FUNCTION(AActor, A_Look2) return 0; } +void GoalReached(AActor* self, AActor* oldgoal) +{ + if (self && self->Level) + self->Level->localEventManager->WorldThingGoalReached(self, oldgoal); +} + +DEFINE_ACTION_FUNCTION(AActor, GoalReached) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_OBJECT(oldgoal, AActor); + GoalReached(self, oldgoal); + return 0; +} + //============================================================================= // // A_Chase @@ -2594,8 +2608,7 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi } actor->flags7 &= ~MF7_INCHASE; actor->goal = newgoal; - if (actor->goal != savedgoal) - actor->Level->localEventManager->WorldThingGoalReached(actor, savedgoal); + GoalReached(actor, savedgoal); return; } if (actor->goal == actor->target) goto nomissile; diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index 37b36e9e7c7..b9255dc7e9f 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -813,6 +813,9 @@ class Actor : Thinker native movecount = random[TryWalk](0, 15); return true; } + + // Calls WorldThingGoalReached. + native void GoalReached(Actor oldgoal); native bool TryMove(vector2 newpos, int dropoff, bool missilecheck = false, FCheckPosition tm = null); native bool CheckMove(vector2 newpos, int flags = 0, FCheckPosition tm = null);