diff --git a/HKMP/Fsm/FsmPatcher.cs b/HKMP/Fsm/FsmPatcher.cs index a34df89..9dac82d 100644 --- a/HKMP/Fsm/FsmPatcher.cs +++ b/HKMP/Fsm/FsmPatcher.cs @@ -1,5 +1,5 @@ -using Hkmp.Logging; using Hkmp.Util; +using Hkmp.Logging; using HutongGames.PlayMaker.Actions; namespace Hkmp.Fsm; @@ -36,5 +36,28 @@ private void OnFsmEnable(On.PlayMakerFSM.orig_OnEnable orig, PlayMakerFSM self) triggerAction.collideTag.Value = "Player"; triggerAction.collideTag.UseVariable = false; } + + // Specific patch for the Battle Control FSM in Fungus2_05 where the Shroomal Ogres are with the Charm Notch + if (self.name.Equals("Battle Scene v2") && + self.Fsm.Name.Equals("Battle Control") && + self.gameObject.scene.name.Equals("Fungus2_05")) { + var findBrawler1 = self.GetAction("Init", 6); + var findBrawler2 = self.GetAction("Init", 8); + + // With the way the entity system works, the Mushroom Brawlers might not be found with the existing actions + // We complement these actions by checking if the Brawlers were found and if not, find them another way + self.InsertMethod("Init", 7, () => { + if (findBrawler1.store.Value == null) { + var brawler1 = GameObjectUtil.FindInactiveGameObject("Mushroom Brawler 1"); + findBrawler1.store.Value = brawler1; + } + }); + self.InsertMethod("Init", 10, () => { + if (findBrawler2.store.Value == null) { + var brawler2 = GameObjectUtil.FindInactiveGameObject("Mushroom Brawler 2"); + findBrawler2.store.Value = brawler2; + } + }); + } } } diff --git a/HKMP/Game/Client/Entity/Action/EntityFsmActions.cs b/HKMP/Game/Client/Entity/Action/EntityFsmActions.cs index 080e261..5ec9eac 100644 --- a/HKMP/Game/Client/Entity/Action/EntityFsmActions.cs +++ b/HKMP/Game/Client/Entity/Action/EntityFsmActions.cs @@ -1782,11 +1782,15 @@ private static void ApplyNetworkDataFromAction(EntityNetworkData data, SendEvent if (action.delay.Value < 1.0 / 1000.0) { action.Fsm.Event(action.eventTarget, action.sendEvent.Value); } else { - action.Fsm.DelayedEvent( - action.eventTarget, - FsmEvent.GetFsmEvent(action.sendEvent.Value), - action.delay.Value - ); + // We need to delay the event sending ourselves, because the FSM that we are executing in is not enabled + // The usual implementation of SendEventByName will thus not work + MonoBehaviourUtil.Instance.StartCoroutine(DelayEvent()); + + IEnumerator DelayEvent() { + yield return new WaitForSeconds(action.delay.Value); + + action.Fsm.Event(action.eventTarget, action.sendEvent.Value); + } } } @@ -1806,11 +1810,15 @@ private static void ApplyNetworkDataFromAction(EntityNetworkData data, SendEvent if (action.delay.Value < 1.0 / 1000.0) { action.Fsm.Event(action.eventTarget, action.sendEvent.Value); } else { - action.Fsm.DelayedEvent( - action.eventTarget, - FsmEvent.GetFsmEvent(action.sendEvent.Value), - action.delay.Value - ); + // We need to delay the event sending ourselves, because the FSM that we are executing in is not enabled + // The usual implementation of SendEventByNameV2 will thus not work + MonoBehaviourUtil.Instance.StartCoroutine(DelayEvent()); + + IEnumerator DelayEvent() { + yield return new WaitForSeconds(action.delay.Value); + + action.Fsm.Event(action.eventTarget, action.sendEvent.Value); + } } } diff --git a/HKMP/Game/Client/Entity/EntityManager.cs b/HKMP/Game/Client/Entity/EntityManager.cs index 63a29ad..e0940f9 100644 --- a/HKMP/Game/Client/Entity/EntityManager.cs +++ b/HKMP/Game/Client/Entity/EntityManager.cs @@ -379,12 +379,15 @@ private void FindEntitiesInScene(Scene scene, bool lateLoad) { var objectsToCheck = Object.FindObjectsOfType() .Where(e => e.gameObject.scene == scene) .SelectMany(enemyDeathEffects => { - if (enemyDeathEffects == null) { + try { + enemyDeathEffects.PreInstantiate(); + } catch (Exception) { + // If we get an exception it might not be possible to pre-instantiate the enemy death effects + // This can happen when the object uses a PersonalObjectPool, which can't be pre-instantiated + // this early, so we return only the original gameobject return new[] { enemyDeathEffects.gameObject }; } - enemyDeathEffects.PreInstantiate(); - var corpse = ReflectionHelper.GetField( enemyDeathEffects, "corpse" @@ -448,4 +451,4 @@ private void FindEntitiesInScene(Scene scene, bool lateLoad) { }.Process(); } } -} \ No newline at end of file +} diff --git a/HKMP/Game/Client/Entity/EntityType.cs b/HKMP/Game/Client/Entity/EntityType.cs index 365b812..5258ab1 100644 --- a/HKMP/Game/Client/Entity/EntityType.cs +++ b/HKMP/Game/Client/Entity/EntityType.cs @@ -62,6 +62,7 @@ internal enum EntityType { FungifiedHusk, Shrumeling, ShrumalWarrior, + ShrumalOgre, MantisYouth, MantisWarrior, MantisThrone, diff --git a/HKMP/Resource/entity-registry.json b/HKMP/Resource/entity-registry.json index df05bb0..cbc9bef 100644 --- a/HKMP/Resource/entity-registry.json +++ b/HKMP/Resource/entity-registry.json @@ -357,6 +357,11 @@ "type": "ShrumalWarrior", "fsm_name": "Mush Roller" }, + { + "base_object_name": "Mushroom Brawler", + "type": "ShrumalOgre", + "fsm_name": "Shroom Brawler" + }, { "base_object_name": "Mantis Flyer Child", "type": "MantisYouth", diff --git a/HKMP/Util/GameObjectExtensions.cs b/HKMP/Util/GameObjectUtil.cs similarity index 54% rename from HKMP/Util/GameObjectExtensions.cs rename to HKMP/Util/GameObjectUtil.cs index 292b8ad..116365e 100644 --- a/HKMP/Util/GameObjectExtensions.cs +++ b/HKMP/Util/GameObjectUtil.cs @@ -4,9 +4,9 @@ namespace Hkmp.Util; /// -/// Class for GameObject extensions. +/// Class for GameObject utility methods and extensions. /// -internal static class GameObjectExtensions { +internal static class GameObjectUtil { /// /// Find a GameObject with the given name in the children of the given GameObject. /// @@ -30,6 +30,11 @@ string name return null; } + /// + /// Get a list of the children of the given GameObject. + /// + /// The GameObject to get the children for. + /// A list of the children of the GameObject. public static List GetChildren(this GameObject gameObject) { var children = new List(); for (var i = 0; i < gameObject.transform.childCount; i++) { @@ -38,4 +43,22 @@ public static List GetChildren(this GameObject gameObject) { return children; } + + /// + /// Find an inactive GameObject with the given name. + /// + /// The name of the GameObject. + /// The GameObject is it exists, null otherwise. + public static GameObject FindInactiveGameObject(string name) { + var transforms = Resources.FindObjectsOfTypeAll(); + foreach (var transform in transforms) { + if (transform.hideFlags == HideFlags.None) { + if (transform.name == name) { + return transform.gameObject; + } + } + } + + return null; + } }