diff --git a/Assets/Scripts/Core/GameManager.cs b/Assets/Scripts/Core/GameManager.cs index 65f1cada..53fe12f9 100644 --- a/Assets/Scripts/Core/GameManager.cs +++ b/Assets/Scripts/Core/GameManager.cs @@ -159,72 +159,10 @@ public class GameManager : MonoBehaviour { return DeveloperSettingsProvider.Instance?.GetSettings(); } - - // PLAYER & FOLLOWER SETTINGS - - // Player settings - public float MoveSpeed => GetSettings()?.MoveSpeed ?? 5f; - public float StopDistance => GetSettings()?.StopDistance ?? 0.1f; - public bool UseRigidbody => GetSettings()?.UseRigidbody ?? true; - public HoldMovementMode DefaultHoldMovementMode => - GetSettings()?.DefaultHoldMovementMode ?? HoldMovementMode.Pathfinding; - - // Follower settings - public float FollowDistance => GetSettings()?.FollowDistance ?? 1.5f; - public float ManualMoveSmooth => GetSettings()?.ManualMoveSmooth ?? 8f; - public float ThresholdFar => GetSettings()?.ThresholdFar ?? 2.5f; - public float ThresholdNear => GetSettings()?.ThresholdNear ?? 0.5f; - public float StopThreshold => GetSettings()?.StopThreshold ?? 0.1f; - public float FollowUpdateInterval => GetSettings()?.FollowUpdateInterval ?? 0.1f; - public float FollowerSpeedMultiplier => GetSettings()?.FollowerSpeedMultiplier ?? 1.2f; - public float HeldIconDisplayHeight => GetSettings()?.HeldIconDisplayHeight ?? 2.0f; - - // INTERACTION SETTINGS + // LEFTOVER LEGACY SETTINGS public float PlayerStopDistance => GetSettings()?.PlayerStopDistance ?? 6.0f; public float PlayerStopDistanceDirectInteraction => GetSettings()?.PlayerStopDistanceDirectInteraction ?? 2.0f; - public float FollowerPickupDelay => GetSettings()?.FollowerPickupDelay ?? 0.2f; - public LayerMask InteractableLayerMask => GetSettings()?.InteractableLayerMask ?? -1; - public GameObject BasePickupPrefab => GetSettings()?.BasePickupPrefab; - public GameObject LevelSwitchMenuPrefab => GetSettings()?.LevelSwitchMenuPrefab; - - // PUZZLE SETTINGS - public float DefaultPuzzlePromptRange => GetSettings()?.DefaultPuzzlePromptRange ?? 3.0f; - public GameObject DefaultPuzzleIndicatorPrefab => GetSettings()?.DefaultPuzzleIndicatorPrefab; - /// - /// Returns the combination rule for two items, if any. - /// - public CombinationRule GetCombinationRule(PickupItemData item1, PickupItemData item2) - { - var settings = GetSettings(); - if (settings == null || settings.CombinationRules == null) return null; - - foreach (var rule in settings.CombinationRules) - { - if ((PickupItemData.AreEquivalent(rule.itemA, item1) && PickupItemData.AreEquivalent(rule.itemB, item2)) || - (PickupItemData.AreEquivalent(rule.itemA, item2) && PickupItemData.AreEquivalent(rule.itemB, item1))) - { - return rule; - } - } - return null; - } - - /// - /// Returns the slot item config for a given slot item. - /// - public SlotItemConfig GetSlotItemConfig(PickupItemData slotItem) - { - var settings = GetSettings(); - if (settings == null || settings.SlotItemConfigs == null || slotItem == null) return null; - - foreach (var config in settings.SlotItemConfigs) - { - if (PickupItemData.AreEquivalent(slotItem, config.slotItem)) - return config; - } - return null; - } } diff --git a/Assets/Scripts/Core/Settings/InteractionSettings.cs b/Assets/Scripts/Core/Settings/InteractionSettings.cs index d4ae1bf7..4c5bf271 100644 --- a/Assets/Scripts/Core/Settings/InteractionSettings.cs +++ b/Assets/Scripts/Core/Settings/InteractionSettings.cs @@ -53,5 +53,38 @@ namespace AppleHills.Core.Settings followerPickupDelay = Mathf.Max(0f, followerPickupDelay); defaultPuzzlePromptRange = Mathf.Max(0.1f, defaultPuzzlePromptRange); } + + /// + /// Returns the combination rule for two items, if any. + /// + public CombinationRule GetCombinationRule(PickupItemData item1, PickupItemData item2) + { + if (combinationRules == null) return null; + + foreach (var rule in combinationRules) + { + if ((PickupItemData.AreEquivalent(rule.itemA, item1) && PickupItemData.AreEquivalent(rule.itemB, item2)) || + (PickupItemData.AreEquivalent(rule.itemA, item2) && PickupItemData.AreEquivalent(rule.itemB, item1))) + { + return rule; + } + } + return null; + } + + /// + /// Returns the slot item config for a given slot item. + /// + public SlotItemConfig GetSlotItemConfig(PickupItemData slotItem) + { + if (slotItemConfigs == null || slotItem == null) return null; + + foreach (var config in slotItemConfigs) + { + if (PickupItemData.AreEquivalent(slotItem, config.slotItem)) + return config; + } + return null; + } } } diff --git a/Assets/Scripts/Core/Settings/SettingsInterfaces.cs b/Assets/Scripts/Core/Settings/SettingsInterfaces.cs index 440594bd..4b48f197 100644 --- a/Assets/Scripts/Core/Settings/SettingsInterfaces.cs +++ b/Assets/Scripts/Core/Settings/SettingsInterfaces.cs @@ -43,6 +43,10 @@ namespace AppleHills.Core.Settings // Puzzle settings GameObject DefaultPuzzleIndicatorPrefab { get; } float DefaultPuzzlePromptRange { get; } + + // Methods to query item configurations + CombinationRule GetCombinationRule(PickupItemData item1, PickupItemData item2); + SlotItemConfig GetSlotItemConfig(PickupItemData slotItem); } /// diff --git a/Assets/Scripts/Input/InputManager.cs b/Assets/Scripts/Input/InputManager.cs index 0e1bbf9d..80d110ae 100644 --- a/Assets/Scripts/Input/InputManager.cs +++ b/Assets/Scripts/Input/InputManager.cs @@ -3,6 +3,7 @@ using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.InputSystem; using UnityEngine.SceneManagement; +using AppleHills.Core.Settings; // Added for IInteractionSettings namespace Input { @@ -44,6 +45,9 @@ namespace Input } } + // Settings reference + private IInteractionSettings _interactionSettings; + private PlayerInput playerInput; private InputAction tapMoveAction; private InputAction holdMoveAction; @@ -55,6 +59,10 @@ namespace Input { _instance = this; // DontDestroyOnLoad(gameObject); + + // Initialize settings reference + _interactionSettings = GameManager.GetSettingsObject(); + playerInput = GetComponent(); if (playerInput == null) { @@ -217,7 +225,7 @@ namespace Input /// private bool TryDelegateToInteractable(Vector2 worldPos) { - LayerMask mask = GameManager.Instance != null ? GameManager.Instance.InteractableLayerMask : -1; + LayerMask mask = _interactionSettings != null ? _interactionSettings.InteractableLayerMask : -1; Collider2D hit = Physics2D.OverlapPoint(worldPos, mask); if (hit != null) { diff --git a/Assets/Scripts/Interactions/ItemSlot.cs b/Assets/Scripts/Interactions/ItemSlot.cs index 2826d7a8..dfce4c21 100644 --- a/Assets/Scripts/Interactions/ItemSlot.cs +++ b/Assets/Scripts/Interactions/ItemSlot.cs @@ -3,6 +3,7 @@ using UnityEngine; using UnityEngine.Events; using System; // for Action using Core; // register with ItemManager +using AppleHills.Core.Settings; // Added for IInteractionSettings namespace Interactions { @@ -24,6 +25,10 @@ namespace Interactions // Tracks the current state of the slotted item private ItemSlotState _currentState = ItemSlotState.None; + // Settings reference + private IInteractionSettings _interactionSettings; + private IPlayerFollowerSettings _playerFollowerSettings; + /// /// Read-only access to the current slotted item state. /// @@ -64,13 +69,22 @@ namespace Interactions } } + public override void Awake() + { + base.Awake(); + + // Initialize settings references + _interactionSettings = GameManager.GetSettingsObject(); + _playerFollowerSettings = GameManager.GetSettingsObject(); + } + protected override void OnCharacterArrived() { Debug.Log("[ItemSlot] OnCharacterArrived"); var heldItemData = FollowerController.CurrentlyHeldItemData; var heldItemObj = FollowerController.GetHeldPickupObject(); - var config = GameManager.Instance.GetSlotItemConfig(itemData); + var config = _interactionSettings?.GetSlotItemConfig(itemData); var forbidden = config?.forbiddenItems ?? new List(); // Held item, slot empty -> try to slot item @@ -120,7 +134,7 @@ namespace Interactions { slottedItemRenderer.sprite = _currentlySlottedItemData.mapSprite; // Scale sprite to desired height, preserve aspect ratio, compensate for parent scale - float desiredHeight = GameManager.Instance.HeldIconDisplayHeight; + float desiredHeight = _playerFollowerSettings?.HeldIconDisplayHeight ?? 2.0f; var sprite = _currentlySlottedItemData.mapSprite; float spriteHeight = sprite.bounds.size.y; float spriteWidth = sprite.bounds.size.x; @@ -175,7 +189,7 @@ namespace Interactions // Once an item is slotted, we know it is not forbidden, so we can skip that check, but now check if it was // the correct item we're looking for - var config = GameManager.Instance.GetSlotItemConfig(itemData); + var config = _interactionSettings?.GetSlotItemConfig(itemData); var allowed = config?.allowedItems ?? new List(); if (itemToSlotData != null && PickupItemData.ListContainsEquivalent(allowed, itemToSlotData)) { diff --git a/Assets/Scripts/Interactions/Pickup.cs b/Assets/Scripts/Interactions/Pickup.cs index 065b6a86..d07237fe 100644 --- a/Assets/Scripts/Interactions/Pickup.cs +++ b/Assets/Scripts/Interactions/Pickup.cs @@ -26,7 +26,7 @@ namespace Interactions /// /// Unity Awake callback. Sets up icon, interactable, and event handlers. /// - void Awake() + public virtual void Awake() { if (iconRenderer == null) iconRenderer = GetComponent(); diff --git a/Assets/Scripts/LevelS/LevelSwitch.cs b/Assets/Scripts/LevelS/LevelSwitch.cs index 36d39fc5..b37f9725 100644 --- a/Assets/Scripts/LevelS/LevelSwitch.cs +++ b/Assets/Scripts/LevelS/LevelSwitch.cs @@ -2,6 +2,7 @@ using Input; using Interactions; using UnityEngine; +using AppleHills.Core.Settings; // Added for IInteractionSettings /// /// Handles level switching when interacted with. Applies switch data and triggers scene transitions. @@ -14,6 +15,9 @@ public class LevelSwitch : MonoBehaviour public LevelSwitchData switchData; private SpriteRenderer _iconRenderer; private Interactable _interactable; + + // Settings reference + private IInteractionSettings _interactionSettings; private bool _isActive = true; @@ -30,6 +34,10 @@ public class LevelSwitch : MonoBehaviour { _interactable.characterArrived.AddListener(OnCharacterArrived); } + + // Initialize settings reference + _interactionSettings = GameManager.GetSettingsObject(); + ApplySwitchData(); } @@ -78,10 +86,10 @@ public class LevelSwitch : MonoBehaviour if (switchData == null || string.IsNullOrEmpty(switchData.targetLevelSceneName) || !_isActive) return; - var menuPrefab = GameManager.Instance.LevelSwitchMenuPrefab; + var menuPrefab = _interactionSettings?.LevelSwitchMenuPrefab; if (menuPrefab == null) { - Debug.LogError("LevelSwitchMenu prefab not assigned in GameSettings!"); + Debug.LogError("LevelSwitchMenu prefab not assigned in InteractionSettings!"); return; } // Spawn the menu overlay (assume Canvas parent is handled in prefab setup) diff --git a/Assets/Scripts/Movement/FollowerController.cs b/Assets/Scripts/Movement/FollowerController.cs index 60bef864..d50d3344 100644 --- a/Assets/Scripts/Movement/FollowerController.cs +++ b/Assets/Scripts/Movement/FollowerController.cs @@ -3,6 +3,7 @@ using UnityEngine; using Pathfinding; using UnityEngine.SceneManagement; using Utils; +using AppleHills.Core.Settings; /// /// Controls the follower character, including following the player, handling pickups, and managing held items. @@ -20,6 +21,10 @@ public class FollowerController: MonoBehaviour /// public float manualMoveSmooth = 8f; + // Settings reference + private IPlayerFollowerSettings _settings; + private IInteractionSettings _interactionSettings; + private GameObject _playerRef; private Transform _playerTransform; private AIPath _playerAIPath; @@ -80,6 +85,10 @@ public class FollowerController: MonoBehaviour _animator = GetComponentInChildren(); // fallback _spriteRenderer = GetComponentInChildren(); } + + // Initialize settings references + _settings = GameManager.GetSettingsObject(); + _interactionSettings = GameManager.GetSettingsObject(); } void OnEnable() @@ -108,7 +117,7 @@ public class FollowerController: MonoBehaviour } _timer += Time.deltaTime; - if (_timer >= GameManager.Instance.FollowUpdateInterval) + if (_timer >= _settings.FollowUpdateInterval) { _timer = 0f; UpdateFollowTarget(); @@ -120,24 +129,24 @@ public class FollowerController: MonoBehaviour Vector2 target2D = new Vector2(_targetPoint.x, _targetPoint.y); float dist = Vector2.Distance(current2D, target2D); float minSpeed = _followerMaxSpeed * 0.3f; - float lerpFactor = GameManager.Instance.ManualMoveSmooth * Time.deltaTime; + float lerpFactor = _settings.ManualMoveSmooth * Time.deltaTime; float targetSpeed = 0f; - if (dist > GameManager.Instance.StopThreshold) + if (dist > _settings.StopThreshold) { - if (dist > GameManager.Instance.ThresholdFar) + if (dist > _settings.ThresholdFar) { targetSpeed = _followerMaxSpeed; } - else if (dist > GameManager.Instance.ThresholdNear && dist <= GameManager.Instance.ThresholdFar) + else if (dist > _settings.ThresholdNear && dist <= _settings.ThresholdFar) { targetSpeed = _followerMaxSpeed; } - else if (dist > GameManager.Instance.StopThreshold && dist <= GameManager.Instance.ThresholdNear) + else if (dist > _settings.StopThreshold && dist <= _settings.ThresholdNear) { targetSpeed = minSpeed; } _currentSpeed = Mathf.Lerp(_currentSpeed, targetSpeed, lerpFactor); - if (dist > GameManager.Instance.StopThreshold && dist <= GameManager.Instance.ThresholdNear) + if (dist > _settings.StopThreshold && dist <= _settings.ThresholdNear) { _currentSpeed = Mathf.Max(_currentSpeed, minSpeed); } @@ -215,7 +224,7 @@ public class FollowerController: MonoBehaviour { _playerMaxSpeed = _playerAIPath.maxSpeed; _defaultFollowerMaxSpeed = _playerMaxSpeed; - _followerMaxSpeed = _playerMaxSpeed * GameManager.Instance.FollowerSpeedMultiplier; + _followerMaxSpeed = _playerMaxSpeed * _settings.FollowerSpeedMultiplier; } } else @@ -258,8 +267,8 @@ public class FollowerController: MonoBehaviour { moveDir = _lastMoveDir; } - // Use GameSettings for followDistance - _targetPoint = playerPos - moveDir * GameManager.Instance.FollowDistance; + // Use settings for followDistance + _targetPoint = playerPos - moveDir * _settings.FollowDistance; _targetPoint.z = 0; if (_aiPath != null) { @@ -310,14 +319,14 @@ public class FollowerController: MonoBehaviour _aiPath.destination = new Vector3(itemPosition.x, itemPosition.y, 0); } // Wait until follower reaches item (2D distance) - while (Vector2.Distance(new Vector2(transform.position.x, transform.position.y), new Vector2(itemPosition.x, itemPosition.y)) > GameManager.Instance.StopThreshold) + while (Vector2.Distance(new Vector2(transform.position.x, transform.position.y), new Vector2(itemPosition.x, itemPosition.y)) > _settings.StopThreshold) { yield return null; } OnPickupArrived?.Invoke(); // Wait briefly, then return to player - yield return new WaitForSeconds(0.2f); + yield return new WaitForSeconds(_interactionSettings.FollowerPickupDelay); if (_aiPath != null && playerTransform != null) { _aiPath.maxSpeed = _followerMaxSpeed; @@ -325,7 +334,7 @@ public class FollowerController: MonoBehaviour } _isReturningToPlayer = true; // Wait until follower returns to player (2D distance) - while (playerTransform != null && Vector2.Distance(new Vector2(transform.position.x, transform.position.y), new Vector2(playerTransform.position.x, playerTransform.position.y)) > GameManager.Instance.StopThreshold) + while (playerTransform != null && Vector2.Distance(new Vector2(transform.position.x, transform.position.y), new Vector2(playerTransform.position.x, playerTransform.position.y)) > _settings.StopThreshold) { yield return null; } @@ -375,15 +384,18 @@ public class FollowerController: MonoBehaviour { return CombinationResult.NotApplicable; } - var rule = GameManager.Instance.GetCombinationRule(pickupA.itemData, pickupB.itemData); + + // Use the InteractionSettings directly instead of GameManager + CombinationRule matchingRule = _interactionSettings.GetCombinationRule(pickupA.itemData, pickupB.itemData); + Vector3 spawnPos = pickupA.gameObject.transform.position; - if (rule != null && rule.resultPrefab != null) + if (matchingRule != null && matchingRule.resultPrefab != null) { - newItem = Instantiate(rule.resultPrefab, spawnPos, Quaternion.identity); + newItem = Instantiate(matchingRule.resultPrefab, spawnPos, Quaternion.identity); PickupItemData itemData = newItem.GetComponent().itemData; Destroy(pickupA.gameObject); Destroy(pickupB.gameObject); - TryPickupItem(newItem,itemData); + TryPickupItem(newItem, itemData); return CombinationResult.Successful; } diff --git a/Assets/Scripts/PuzzleS/PuzzleManager.cs b/Assets/Scripts/PuzzleS/PuzzleManager.cs index 210f2bd5..f0678415 100644 --- a/Assets/Scripts/PuzzleS/PuzzleManager.cs +++ b/Assets/Scripts/PuzzleS/PuzzleManager.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.SceneManagement; +using AppleHills.Core.Settings; // Added for IInteractionSettings namespace PuzzleS { @@ -21,6 +22,9 @@ namespace PuzzleS private Transform _playerTransform; private Coroutine _proximityCheckCoroutine; + // Settings reference + private IInteractionSettings _interactionSettings; + /// /// Singleton instance of the PuzzleManager. /// @@ -58,6 +62,9 @@ namespace PuzzleS _instance = this; // DontDestroyOnLoad(gameObject); SceneManager.sceneLoaded += OnSceneLoaded; + + // Initialize settings reference + _interactionSettings = GameManager.GetSettingsObject(); } void Start() @@ -123,8 +130,8 @@ namespace PuzzleS { if (_playerTransform != null) { - // Get the proximity threshold from settings (half of the prompt range) - float proximityThreshold = GameManager.Instance.DefaultPuzzlePromptRange; + // Get the proximity threshold from settings directly using our settings reference + float proximityThreshold = _interactionSettings?.DefaultPuzzlePromptRange ?? 3.0f; // Check distance to each step behavior foreach (var kvp in _stepBehaviours)