using GameCore.ECS; using GameCore.ECS.Interfaces; using GameCore.Events; using GameCore.Input; using GameCore.Interaction.Interfaces; using GameCore.Player; namespace GameCore.Interaction; public class InteractionSystem(float interactionRange) : ISystem { public void Update(World world, float deltaTime) { var playerEntities = world.GetEntitiesWith(); var players = playerEntities.ToList(); if (!players.Any()) return; var player = players.First(); var input = world.GetComponent(player); if (input == null) return; world.RemoveComponent(player); var from = input.MuzzlePosition; var to = from + input.MuzzleDirection * interactionRange; var hit = world.WorldQuery.Raycast(from, to, player); if (hit.DidHit && hit.HitEntity.HasValue) { var targetEntity = hit.HitEntity.Value; var door = world.GetComponent(targetEntity); if (door != null) { world.AddComponent(player, new IsLookingAtInteractableComponent(hit.HitEntity.Value)); if (input.IsInteracting) TryInteractWithDoor(world, player, hit.HitEntity.Value, door); return; } var button = world.GetComponent(targetEntity); if (button != null) { world.AddComponent(player, new IsLookingAtInteractableComponent(targetEntity)); if (input.IsInteracting) TryInteractWithButton(world, player, targetEntity, button); } } } private void TryInteractWithDoor(World world, Entity interactor, Entity doorEntity, DoorComponent door) { switch (door.CurrentState) { case DoorComponent.DoorState.Locked: if (door.Requirements.Count > 0 && CheckRequirements(world, interactor, door.Requirements)) { world.Logger.Info($"Door {doorEntity.Id} requirements met. Unlocking door."); door.CurrentState = DoorComponent.DoorState.Opening; if (door.IsOneTimeUnlock) door.Requirements.Clear(); world.PublishEvent(new DoorStateChangedEvent(doorEntity, door.CurrentState)); } else { world.Logger.Info($"Door {doorEntity.Id} requirements not met. Cannot unlock door."); world.PublishEvent(new DoorLockedEvent(doorEntity, interactor)); } break; case DoorComponent.DoorState.Closed: door.CurrentState = DoorComponent.DoorState.Opening; world.PublishEvent(new DoorStateChangedEvent(doorEntity, door.CurrentState)); break; case DoorComponent.DoorState.Open: door.CurrentState = DoorComponent.DoorState.Closing; world.PublishEvent(new DoorStateChangedEvent(doorEntity, door.CurrentState)); break; } } private void TryInteractWithButton(World world, Entity interactor, Entity buttonEntity, ButtonComponent button) { if (button.IsOneTimeUse && button.HasBeenUsed) { world.Logger.Info($"Button {buttonEntity.Id} is one-time-use and has already been used."); return; } if (CheckRequirements(world, interactor, button.Requirements)) { if (button.IsToggle) button.IsPressed = !button.IsPressed; else button.IsPressed = true; button.HasBeenUsed = true; world.Logger.Info( $"Button {buttonEntity.Id} pressed. Channel: {button.ChannelId}, NewState: {button.IsPressed}"); world.PublishEvent(new ButtonPressedEvent(button.ChannelId, button.IsPressed)); } else { world.Logger.Info($"Button {buttonEntity.Id} requirements not met."); } } private bool CheckRequirements(World world, Entity interactor, List requirements) { foreach (var req in requirements) if (!req.IsMet(interactor, world)) return false; foreach (var req in requirements) req.ApplySideEffects(interactor, world); return true; } }