diff --git a/HKMP/Game/Client/Entity/Component/ChallengePromptComponent.cs b/HKMP/Game/Client/Entity/Component/ChallengePromptComponent.cs index a33b0ad..5e808a6 100644 --- a/HKMP/Game/Client/Entity/Component/ChallengePromptComponent.cs +++ b/HKMP/Game/Client/Entity/Component/ChallengePromptComponent.cs @@ -44,7 +44,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { var type = data.Packet.ReadByte(); // If the player is a scene client we destroy the prompt, otherwise we start the fight by progressing the FSM diff --git a/HKMP/Game/Client/Entity/Component/ChildrenActivationComponent.cs b/HKMP/Game/Client/Entity/Component/ChildrenActivationComponent.cs index 08644aa..4ca4b4f 100644 --- a/HKMP/Game/Client/Entity/Component/ChildrenActivationComponent.cs +++ b/HKMP/Game/Client/Entity/Component/ChildrenActivationComponent.cs @@ -61,7 +61,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { if (!IsControlled) { return; } diff --git a/HKMP/Game/Client/Entity/Component/ClimberComponent.cs b/HKMP/Game/Client/Entity/Component/ClimberComponent.cs index 08840c8..2df0a2c 100644 --- a/HKMP/Game/Client/Entity/Component/ClimberComponent.cs +++ b/HKMP/Game/Client/Entity/Component/ClimberComponent.cs @@ -30,10 +30,10 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { } /// public override void Destroy() { } -} \ No newline at end of file +} diff --git a/HKMP/Game/Client/Entity/Component/ColliderComponent.cs b/HKMP/Game/Client/Entity/Component/ColliderComponent.cs index 26b8545..ec18f45 100644 --- a/HKMP/Game/Client/Entity/Component/ColliderComponent.cs +++ b/HKMP/Game/Client/Entity/Component/ColliderComponent.cs @@ -61,7 +61,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { Logger.Info($"Received collider update for {GameObject.Client.name}"); if (!IsControlled) { @@ -80,4 +80,4 @@ public override void Update(EntityNetworkData data) { public override void Destroy() { MonoBehaviourUtil.Instance.OnUpdateEvent -= OnUpdateCollider; } -} \ No newline at end of file +} diff --git a/HKMP/Game/Client/Entity/Component/ComponentFactory.cs b/HKMP/Game/Client/Entity/Component/ComponentFactory.cs index d08d9ae..18b29e5 100644 --- a/HKMP/Game/Client/Entity/Component/ComponentFactory.cs +++ b/HKMP/Game/Client/Entity/Component/ComponentFactory.cs @@ -73,6 +73,8 @@ HostClientPair objects return null; case EntityComponentType.FlipPlatform: return new FlipPlatformComponent(netClient, entityId, objects); + case EntityComponentType.DreamPlatform: + return new DreamPlatformComponent(netClient, entityId, objects); default: throw new ArgumentOutOfRangeException(nameof(type), type, $"Could not instantiate entity component for type: {type}"); } diff --git a/HKMP/Game/Client/Entity/Component/DamageHeroComponent.cs b/HKMP/Game/Client/Entity/Component/DamageHeroComponent.cs index 2ff7bcf..9fbe3c8 100644 --- a/HKMP/Game/Client/Entity/Component/DamageHeroComponent.cs +++ b/HKMP/Game/Client/Entity/Component/DamageHeroComponent.cs @@ -60,7 +60,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { var damageDealt = data.Packet.ReadByte(); _damageHero.Host.damageDealt = damageDealt; _damageHero.Client.damageDealt = damageDealt; @@ -70,4 +70,4 @@ public override void Update(EntityNetworkData data) { public override void Destroy() { MonoBehaviourUtil.Instance.OnUpdateEvent -= OnUpdate; } -} \ No newline at end of file +} diff --git a/HKMP/Game/Client/Entity/Component/DreamPlatformComponent.cs b/HKMP/Game/Client/Entity/Component/DreamPlatformComponent.cs new file mode 100644 index 0000000..34ecad0 --- /dev/null +++ b/HKMP/Game/Client/Entity/Component/DreamPlatformComponent.cs @@ -0,0 +1,196 @@ +using Hkmp.Networking.Client; +using Hkmp.Networking.Packet.Data; +using UnityEngine; +using Logger = Hkmp.Logging.Logger; + +namespace Hkmp.Game.Client.Entity.Component; + +/// +/// This component manages the platforms that (dis)appear in dream sequences. +internal class DreamPlatformComponent : EntityComponent { + /// + /// Host-client pair of the DreamPlatform components. + /// + private readonly HostClientPair _platform; + + /// + /// The number of players currently in range of the platform. + /// + private ushort _numInRange; + /// + /// Whether the local player is in range of the platform. + /// + private bool _isInRange; + + public DreamPlatformComponent( + NetClient netClient, + ushort entityId, + HostClientPair gameObject + ) : base(netClient, entityId, gameObject) { + _platform = new HostClientPair { + Client = gameObject.Client.GetComponent(), + Host = gameObject.Host.GetComponent() + }; + + if (!_platform.Client.showOnEnable) { + _platform.Client.outerCollider.OnTriggerExited += OuterColliderOnTriggerExited; + _platform.Host.outerCollider.OnTriggerExited += OuterColliderOnTriggerExited; + + _platform.Client.innerCollider.OnTriggerEntered += InnerColliderOnTriggerEntered; + _platform.Host.innerCollider.OnTriggerEntered += InnerColliderOnTriggerEntered; + + On.DreamPlatform.Start += DreamPlatformOnStart; + } + } + + /// + /// Hook for the Start method of DreamPlatform. Used to prevent the original method from registering event + /// handlers to the trigger enter/exit. + /// + private void DreamPlatformOnStart(On.DreamPlatform.orig_Start orig, DreamPlatform self) { + if (self == _platform.Client || self == _platform.Host) { + return; + } + + orig(self); + } + + /// + /// Show the correct platform based on whether the entity is controlled or not. + /// + private void Show() { + if (IsControlled) { + _platform.Client.Show(); + } else { + _platform.Host.Show(); + } + } + + /// + /// Hide the correct platform based on whether the entity is controlled or not. + /// + private void Hide() { + if (IsControlled) { + _platform.Client.Hide(); + } else { + _platform.Host.Hide(); + } + } + + /// + /// Event handler for when the trigger for the outer collider of the platform is exited. + /// + private void OuterColliderOnTriggerExited(Collider2D collider, GameObject sender) { + // If we haven't been in range of the platform but trigger the exit, we do not want to update anything + if (!_isInRange) { + return; + } + + _isInRange = false; + + _numInRange--; + + // If the number of players in range is now 0 (or lower), we can hide the platform + if (_numInRange == 0) { + Hide(); + } + + var data = new EntityNetworkData { + Type = EntityComponentType.DreamPlatform + }; + + data.Packet.Write(_numInRange); + data.Packet.Write(0); + + SendData(data); + } + + /// + /// Event handler for when the trigger for the inner collider of the platform is entered. + /// + private void InnerColliderOnTriggerEntered(Collider2D collider, GameObject sender) { + _isInRange = true; + + EnterPlatform(); + + var data = new EntityNetworkData { + Type = EntityComponentType.DreamPlatform + }; + + data.Packet.Write(_numInRange); + data.Packet.Write(1); + + SendData(data); + } + + /// + /// Exit the platform and decrease the number of players in range. If the number hits zero, the platform will be + /// hidden. + /// + private void ExitPlatform() { + _numInRange--; + + // If the number of players in range is now 0 (or lower), we can hide the platform + if (_numInRange == 0) { + Hide(); + } + } + + /// + /// Enter the platform and increase the number of players in range. If the number is exactly 1, the platform will + /// be shown. + /// + private void EnterPlatform() { + _numInRange++; + + // If the number of players in range is exactly 1 now, we show the platform + if (_numInRange == 1) { + Show(); + } + } + + /// + public override void InitializeHost() { + } + + /// + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { + var numInRange = data.Packet.ReadUShort(); + var action = data.Packet.ReadByte(); + + if (alreadyInSceneUpdate) { + _numInRange = numInRange; + + if (_numInRange > 0) { + Show(); + } + + return; + } + + if (action == 0) { + // Action 0 is exited collider + ExitPlatform(); + } else if (action == 1) { + // Action 1 is entered collider + EnterPlatform(); + } else { + Logger.Error($"Could not process unknown action for DreamPlatformComponent update: {action}"); + } + } + + /// + public override void Destroy() { + if (_platform.Client != null) { + _platform.Client.outerCollider.OnTriggerExited -= OuterColliderOnTriggerExited; + _platform.Client.innerCollider.OnTriggerEntered -= InnerColliderOnTriggerEntered; + } + + if (_platform.Host != null) { + _platform.Host.outerCollider.OnTriggerExited -= OuterColliderOnTriggerExited; + _platform.Host.innerCollider.OnTriggerEntered -= InnerColliderOnTriggerEntered; + } + + On.DreamPlatform.Start -= DreamPlatformOnStart; + } +} diff --git a/HKMP/Game/Client/Entity/Component/EnemySpawnerComponent.cs b/HKMP/Game/Client/Entity/Component/EnemySpawnerComponent.cs index efe7a69..9b8f8e7 100644 --- a/HKMP/Game/Client/Entity/Component/EnemySpawnerComponent.cs +++ b/HKMP/Game/Client/Entity/Component/EnemySpawnerComponent.cs @@ -63,7 +63,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { iTween.MoveBy(_spawner.Client.gameObject, new Hashtable { { "amount", @@ -89,4 +89,4 @@ public override void Destroy() { _spawner.Host.OnEnemySpawned -= OnEnemySpawned; } } -} \ No newline at end of file +} diff --git a/HKMP/Game/Client/Entity/Component/EntityComponent.cs b/HKMP/Game/Client/Entity/Component/EntityComponent.cs index c8d69c7..e12c9a4 100644 --- a/HKMP/Game/Client/Entity/Component/EntityComponent.cs +++ b/HKMP/Game/Client/Entity/Component/EntityComponent.cs @@ -54,11 +54,13 @@ protected void SendData(EntityNetworkData data) { /// Initializes the entity component when the client user is the scene host. /// public abstract void InitializeHost(); + /// /// Update the entity component with the given data. /// /// The data to update with. - public abstract void Update(EntityNetworkData data); + /// Whether this data is from an already in scene packet. + public abstract void Update(EntityNetworkData data, bool alreadyInSceneUpdate); /// /// Destroy the entity component. /// @@ -87,5 +89,6 @@ internal enum EntityComponentType : ushort { SpriteRenderer, ChallengePrompt, Music, - FlipPlatform + FlipPlatform, + DreamPlatform } diff --git a/HKMP/Game/Client/Entity/Component/FlipPlatformComponent.cs b/HKMP/Game/Client/Entity/Component/FlipPlatformComponent.cs index 6a18c5e..a1d88c1 100644 --- a/HKMP/Game/Client/Entity/Component/FlipPlatformComponent.cs +++ b/HKMP/Game/Client/Entity/Component/FlipPlatformComponent.cs @@ -88,7 +88,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { var platform = IsControlled ? _platform.Client : _platform.Host; var type = data.Packet.ReadByte(); diff --git a/HKMP/Game/Client/Entity/Component/GravityScaleComponent.cs b/HKMP/Game/Client/Entity/Component/GravityScaleComponent.cs index 4d0b862..cb705dc 100644 --- a/HKMP/Game/Client/Entity/Component/GravityScaleComponent.cs +++ b/HKMP/Game/Client/Entity/Component/GravityScaleComponent.cs @@ -71,7 +71,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { if (!IsControlled) { return; } @@ -83,4 +83,4 @@ public override void Update(EntityNetworkData data) { public override void Destroy() { MonoBehaviourUtil.Instance.OnUpdateEvent -= OnUpdate; } -} \ No newline at end of file +} diff --git a/HKMP/Game/Client/Entity/Component/HealthManagerComponent.cs b/HKMP/Game/Client/Entity/Component/HealthManagerComponent.cs index 31eab34..52aa1c3 100644 --- a/HKMP/Game/Client/Entity/Component/HealthManagerComponent.cs +++ b/HKMP/Game/Client/Entity/Component/HealthManagerComponent.cs @@ -137,7 +137,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { Logger.Info("Received health manager update"); if (!IsControlled) { @@ -173,4 +173,4 @@ public override void Destroy() { On.HealthManager.Die -= HealthManagerOnDie; MonoBehaviourUtil.Instance.OnUpdateEvent -= OnUpdate; } -} \ No newline at end of file +} diff --git a/HKMP/Game/Client/Entity/Component/MeshRendererComponent.cs b/HKMP/Game/Client/Entity/Component/MeshRendererComponent.cs index db80132..2043faf 100644 --- a/HKMP/Game/Client/Entity/Component/MeshRendererComponent.cs +++ b/HKMP/Game/Client/Entity/Component/MeshRendererComponent.cs @@ -63,7 +63,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { var enabled = data.Packet.ReadBool(); _meshRenderer.Host.enabled = enabled; _meshRenderer.Client.enabled = enabled; @@ -73,4 +73,4 @@ public override void Update(EntityNetworkData data) { public override void Destroy() { MonoBehaviourUtil.Instance.OnUpdateEvent -= OnUpdate; } -} \ No newline at end of file +} diff --git a/HKMP/Game/Client/Entity/Component/MusicComponent.cs b/HKMP/Game/Client/Entity/Component/MusicComponent.cs index 0b5584c..536daee 100644 --- a/HKMP/Game/Client/Entity/Component/MusicComponent.cs +++ b/HKMP/Game/Client/Entity/Component/MusicComponent.cs @@ -180,7 +180,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { Logger.Debug("Update MusicComponent"); if (!IsControlled) { diff --git a/HKMP/Game/Client/Entity/Component/RotationComponent.cs b/HKMP/Game/Client/Entity/Component/RotationComponent.cs index b911480..ca4c973 100644 --- a/HKMP/Game/Client/Entity/Component/RotationComponent.cs +++ b/HKMP/Game/Client/Entity/Component/RotationComponent.cs @@ -53,7 +53,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { var rotation = data.Packet.ReadFloat(); SetRotation(GameObject.Host); @@ -74,4 +74,4 @@ void SetRotation(GameObject obj) { public override void Destroy() { MonoBehaviourUtil.Instance.OnUpdateEvent -= OnUpdateRotation; } -} \ No newline at end of file +} diff --git a/HKMP/Game/Client/Entity/Component/SpawnJarComponent.cs b/HKMP/Game/Client/Entity/Component/SpawnJarComponent.cs index e761f60..26e2536 100644 --- a/HKMP/Game/Client/Entity/Component/SpawnJarComponent.cs +++ b/HKMP/Game/Client/Entity/Component/SpawnJarComponent.cs @@ -119,7 +119,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { Logger.Debug("Received SpawnJarComponent data"); MonoBehaviourUtil.Instance.StartCoroutine(Behaviour()); IEnumerator Behaviour() { @@ -177,4 +177,4 @@ public override void Destroy() { SpawnJarControlOnBehaviour ); } -} \ No newline at end of file +} diff --git a/HKMP/Game/Client/Entity/Component/SpriteRendererComponent.cs b/HKMP/Game/Client/Entity/Component/SpriteRendererComponent.cs index 17d1c21..e4e9424 100644 --- a/HKMP/Game/Client/Entity/Component/SpriteRendererComponent.cs +++ b/HKMP/Game/Client/Entity/Component/SpriteRendererComponent.cs @@ -60,7 +60,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { var enabled = data.Packet.ReadBool(); _spriteRenderer.Host.enabled = enabled; _spriteRenderer.Client.enabled = enabled; @@ -70,4 +70,4 @@ public override void Update(EntityNetworkData data) { public override void Destroy() { MonoBehaviourUtil.Instance.OnUpdateEvent -= OnUpdate; } -} \ No newline at end of file +} diff --git a/HKMP/Game/Client/Entity/Component/VelocityComponent.cs b/HKMP/Game/Client/Entity/Component/VelocityComponent.cs index 0003989..a3a2d76 100644 --- a/HKMP/Game/Client/Entity/Component/VelocityComponent.cs +++ b/HKMP/Game/Client/Entity/Component/VelocityComponent.cs @@ -72,7 +72,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { if (!IsControlled) { return; } @@ -88,4 +88,4 @@ public override void Update(EntityNetworkData data) { public override void Destroy() { MonoBehaviourUtil.Instance.OnUpdateEvent -= OnUpdate; } -} \ No newline at end of file +} diff --git a/HKMP/Game/Client/Entity/Component/ZPositionComponent.cs b/HKMP/Game/Client/Entity/Component/ZPositionComponent.cs index deeff4f..d7adea5 100644 --- a/HKMP/Game/Client/Entity/Component/ZPositionComponent.cs +++ b/HKMP/Game/Client/Entity/Component/ZPositionComponent.cs @@ -54,7 +54,7 @@ public override void InitializeHost() { } /// - public override void Update(EntityNetworkData data) { + public override void Update(EntityNetworkData data, bool alreadyInSceneUpdate) { if (!IsControlled) { return; } @@ -78,4 +78,4 @@ void SetZ(GameObject gameObject) { public override void Destroy() { MonoBehaviourUtil.Instance.OnUpdateEvent -= OnUpdate; } -} \ No newline at end of file +} diff --git a/HKMP/Game/Client/Entity/Entity.cs b/HKMP/Game/Client/Entity/Entity.cs index a5cfffd..5b2bebb 100644 --- a/HKMP/Game/Client/Entity/Entity.cs +++ b/HKMP/Game/Client/Entity/Entity.cs @@ -1235,7 +1235,8 @@ public void UpdateIsActive(bool active) { /// Updates generic data for the client entity. /// /// A list of data to update the client entity with. - public void UpdateData(List entityNetworkData) { + /// Whether this data is from an already in scene update. + public void UpdateData(List entityNetworkData, bool alreadyInSceneUpdate) { foreach (var data in entityNetworkData) { if (data.Type == EntityComponentType.Fsm) { PlayMakerFSM fsm; @@ -1276,7 +1277,7 @@ public void UpdateData(List entityNetworkData) { } if (_components.TryGetValue(data.Type, out var component)) { - component.Update(data); + component.Update(data, alreadyInSceneUpdate); } } } diff --git a/HKMP/Game/Client/Entity/EntityManager.cs b/HKMP/Game/Client/Entity/EntityManager.cs index e3fa5f0..8501c90 100644 --- a/HKMP/Game/Client/Entity/EntityManager.cs +++ b/HKMP/Game/Client/Entity/EntityManager.cs @@ -227,7 +227,7 @@ public bool HandleReliableEntityUpdate(ReliableEntityUpdate entityUpdate, bool a } if (entityUpdate.UpdateTypes.Contains(EntityUpdateType.Data)) { - entity.UpdateData(entityUpdate.GenericData); + entity.UpdateData(entityUpdate.GenericData, alreadyInSceneUpdate); } return true; @@ -449,6 +449,8 @@ private void FindEntitiesInScene(Scene scene, bool lateLoad) { .Concat(Object.FindObjectsOfType(true).Select(cameraLockArea => cameraLockArea.gameObject)) // Concatenate all GameObjects for FlipPlatform components .Concat(Object.FindObjectsOfType(true).Select(flipPlatform => flipPlatform.gameObject)) + // Concatenate all GameObjects for DreamPlatform components + .Concat(Object.FindObjectsOfType(true).Select(dreamPlatform => dreamPlatform.gameObject)) // Filter out GameObjects not in the current scene .Where(obj => obj.scene == scene) .Distinct(); diff --git a/HKMP/Game/Client/Entity/EntityRegistry.cs b/HKMP/Game/Client/Entity/EntityRegistry.cs index d925904..2cb1392 100644 --- a/HKMP/Game/Client/Entity/EntityRegistry.cs +++ b/HKMP/Game/Client/Entity/EntityRegistry.cs @@ -84,7 +84,11 @@ out EntityRegistryEntry foundEntry // Specifically check for entries that don't have a defined FSM whether they contain the // correct component(s) - if (entry.Type == EntityType.Tiktik) { + if (entry.Type == EntityType.DreamPlatform) { + if (gameObject.GetComponent() == null) { + continue; + } + } else if (entry.Type == EntityType.Tiktik) { if (gameObject.GetComponent() == null) { continue; } diff --git a/HKMP/Game/Client/Entity/EntityType.cs b/HKMP/Game/Client/Entity/EntityType.cs index 63c3d5f..6831d32 100644 --- a/HKMP/Game/Client/Entity/EntityType.cs +++ b/HKMP/Game/Client/Entity/EntityType.cs @@ -8,6 +8,7 @@ internal enum EntityType { CameraLockArea, CityElevator, CrystalPeakPlatform, + DreamPlatform, Crawlid, Tiktik, Vengefly, diff --git a/HKMP/Resource/entity-registry.json b/HKMP/Resource/entity-registry.json index f66cc96..f81c45d 100644 --- a/HKMP/Resource/entity-registry.json +++ b/HKMP/Resource/entity-registry.json @@ -25,6 +25,20 @@ "FlipPlatform" ] }, + { + "base_object_name": "F Plat", + "type": "DreamPlatform", + "components": [ + "DreamPlatform" + ] + }, + { + "base_object_name": "dream_plat_", + "type": "DreamPlatform", + "components": [ + "DreamPlatform" + ] + }, { "base_object_name": "Crawler", "type": "Crawlid",