using System.Collections.Generic; using Godot; using ParasiticGod.Scripts.Core; using ParasiticGod.Scripts.Singletons; namespace ParasiticGod.Scripts.Components; [GlobalClass] public partial class PopulationVisualizer : Node { public enum VisualCategory { Followers, Huts, Temples } [Export] private Node2D _markersContainer; [Export] private int _unitsPerMarker = 5; [Export] public VisualCategory Category { get; private set; } [Export] private PackedScene _moddableVisualScene; private List _tiers; private readonly List _markers = []; private long _lastKnownUnitCount = -1; private int _lastKnownTierIndex = -1; private bool _isUpdating = false; public override void _Ready() { switch (Category) { case VisualCategory.Followers: _tiers = GameBus.Instance.FollowerTiers; break; case VisualCategory.Huts: _tiers = GameBus.Instance.HutTiers; break; case VisualCategory.Temples: _tiers = GameBus.Instance.TempleTiers; break; default: GD.PushError($"PopulationVisualizer has an invalid category: {Category}"); return; } foreach (var child in _markersContainer.GetChildren()) { if (child is FollowerMarker marker) { _markers.Add(marker); } } GameBus.Instance.StateChanged += OnStateChanged; } public override void _ExitTree() { GameBus.Instance.StateChanged -= OnStateChanged; } private void OnStateChanged(GameState newState) { if (_isUpdating) return; long currentUnitCount = Category switch { VisualCategory.Followers => (long)newState.Get(Stat.Followers), VisualCategory.Huts => (long)newState.Get(Stat.Followers), VisualCategory.Temples => (long)newState.Get(Stat.Followers), _ => 0 }; var currentMarkersToShow = (int)currentUnitCount / _unitsPerMarker; var lastMarkersToShow = (int)_lastKnownUnitCount / _unitsPerMarker; var newTierIndex = GetTierIndex(currentUnitCount); if (currentMarkersToShow != lastMarkersToShow || newTierIndex != _lastKnownTierIndex) { UpdateVisualsProgressively(currentUnitCount, newTierIndex); } } private int GetTierIndex(long currentUnitCount) { for (var i = _tiers.Count - 1; i >= 0; i--) { if (currentUnitCount >= _tiers[i].Threshold) { return i; } } return -1; } private async void UpdateVisualsProgressively(long currentUnitCount, int newTierIndex) { _isUpdating = true; if (newTierIndex < 0) { _isUpdating = false; return; } var followersToShow = (int)currentUnitCount / _unitsPerMarker; var currentTier = _tiers[newTierIndex]; for (var i = 0; i < _markers.Count; i++) { var marker = _markers[i]; var needsChange = false; if (i < followersToShow) { var currentVisual = marker.GetChildOrNull(0); if (currentVisual == null || currentVisual.Tier != currentTier.TierEnum) { if (marker.GetChildCount() > 0) marker.GetChild(0).QueueFree(); var visualInstance = _moddableVisualScene.Instantiate(); visualInstance.Initialize(currentTier.TierEnum, currentTier.Texture, currentTier.Scale); marker.AddChild(visualInstance); needsChange = true; } } else { if (marker.GetChildCount() > 0) { marker.GetChild(0).QueueFree(); needsChange = true; } } if (needsChange) { await ToSignal(GetTree().CreateTimer(0.1f), SceneTreeTimer.SignalName.Timeout); } } _lastKnownUnitCount = currentUnitCount; _lastKnownTierIndex = newTierIndex; _isUpdating = false; GameBus.Instance.NotifyPopulationVisualsUpdated(); } }