Add NotificationManager and NotificationLabel for age advancement notifications; refactor scripts into Components directory

This commit is contained in:
2025-08-23 04:54:45 +02:00
parent 5719c3f920
commit 9cf707945b
26 changed files with 96 additions and 68 deletions

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using Godot;
using ParasiticGod.Scripts;
using ParasiticGod.Scripts.Core.Effects;
using ParasiticGod.Scripts.Singletons;
namespace ParasiticGod.Scripts.Components;
[GlobalClass]
public partial class ActiveBuffsManager : Node
{
[Export] private PackedScene _activeBuffScene;
private readonly Dictionary<Guid, ActiveBuffUi> _activeBuffUis = new();
public override void _Ready()
{
GameBus.Instance.BuffAdded += OnBuffAdded;
GameBus.Instance.BuffRemoved += OnBuffRemoved;
}
public override void _ExitTree()
{
if (GameBus.Instance == null) return;
GameBus.Instance.BuffAdded -= OnBuffAdded;
GameBus.Instance.BuffRemoved -= OnBuffRemoved;
}
private void OnBuffAdded(Buff buff)
{
var buffInstance = _activeBuffScene.Instantiate<ActiveBuffUi>();
AddChild(buffInstance);
buffInstance.SetBuff(buff);
_activeBuffUis.Add(buff.Id, buffInstance);
}
private void OnBuffRemoved(Buff buff)
{
if (_activeBuffUis.TryGetValue(buff.Id, out var buffUi))
{
buffUi.QueueFree();
_activeBuffUis.Remove(buff.Id);
}
}
}

View File

@@ -0,0 +1 @@
uid://ddshg236tlltt

View File

@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Godot;
using ParasiticGod.Scripts.Core;
using ParasiticGod.Scripts.Singletons;
namespace ParasiticGod.Scripts.Components;
[GlobalClass]
public partial class ForestVisualizer : Node
{
[Export] private Node2D _treesContainer;
private List<Node2D> _trees = [];
private int _lastKnownTreesToShow = -1;
private bool _isUpdating = false;
public override void _Ready()
{
foreach (var child in _treesContainer.GetChildren())
{
if (child is Node2D tree)
{
_trees.Add(tree);
}
}
var rng = new RandomNumberGenerator();
rng.Randomize();
_trees = _trees.OrderBy(_ => Guid.NewGuid()).ToList();
GameBus.Instance.StateChanged += OnStateChanged;
}
public override void _ExitTree()
{
GameBus.Instance.StateChanged -= OnStateChanged;
}
private void OnStateChanged(GameState newState)
{
if (_isUpdating) return;
var corruptionRatio = newState.Get(Stat.Corruption) / 100.0;
var treesToShow = (int)(_trees.Count * (1.0 - corruptionRatio));
if (treesToShow != _lastKnownTreesToShow)
{
UpdateForestProgressively(treesToShow);
}
}
private async void UpdateForestProgressively(int treesToShow)
{
_isUpdating = true;
for (var i = 0; i < _trees.Count; i++)
{
var tree = _trees[i];
var shouldBeVisible = i < treesToShow;
var needsChange = tree.Visible != shouldBeVisible;
if (needsChange)
{
tree.Visible = shouldBeVisible;
await ToSignal(GetTree().CreateTimer(0.01f), SceneTreeTimer.SignalName.Timeout);
}
}
_lastKnownTreesToShow = treesToShow;
_isUpdating = false;
}
}

View File

@@ -0,0 +1 @@
uid://furbvcmw31bx

View File

@@ -0,0 +1,31 @@
using Godot;
using ParasiticGod.Scripts;
using ParasiticGod.Scripts.Singletons;
namespace ParasiticGod.Scripts.Components;
[GlobalClass]
public partial class NotificationManager : CanvasLayer
{
[Export] private PackedScene _notificationLabelScene;
public override void _Ready()
{
GameBus.Instance.AgeAdvanced += OnAgeAdvanced;
}
public override void _ExitTree()
{
if (GameBus.Instance != null)
{
GameBus.Instance.AgeAdvanced -= OnAgeAdvanced;
}
}
private void OnAgeAdvanced(string ageName)
{
var notification = _notificationLabelScene.Instantiate<NotificationLabel>();
AddChild(notification);
notification.ShowNotification($"You have entered\n{ageName}!");
}
}

View File

@@ -0,0 +1 @@
uid://c6uh5h3sdlg7n

View File

@@ -0,0 +1,116 @@
using System.Collections.Generic;
using Godot;
using Godot.Collections;
using ParasiticGod.Scripts.Core;
using ParasiticGod.Scripts.Singletons;
namespace ParasiticGod.Scripts.Components;
[GlobalClass]
public partial class PopulationVisualizer : Node
{
[Export] private Node2D _markersContainer;
[Export] private int _unitsPerMarker = 5;
[Export] private Array<TierDefinition> _tiers;
private readonly List<FollowerMarker> _markers = [];
private long _lastKnownUnitCount = -1;
private int _lastKnownTierIndex = -1;
private bool _isUpdating = false;
public override void _Ready()
{
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;
var currentUnitCount = (long)newState.Get(Stat.Followers);
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)
{
if (!marker.IsOccupied || _lastKnownTierIndex != newTierIndex)
{
if (marker.IsOccupied) marker.RemoveFollower();
var followerInstance = currentTier.Scene.Instantiate<Follower>();
marker.PlaceFollower(followerInstance);
needsChange = true;
}
}
else
{
if (marker.IsOccupied)
{
marker.RemoveFollower();
needsChange = true;
}
}
if (needsChange)
{
await ToSignal(GetTree().CreateTimer(0.1f), SceneTreeTimer.SignalName.Timeout);
}
}
_lastKnownUnitCount = currentUnitCount;
_lastKnownTierIndex = newTierIndex;
_isUpdating = false;
GameBus.Instance.NotifyPopulationVisualsUpdated();
}
}

View File

@@ -0,0 +1 @@
uid://dj2wyrq07gfp2

View File

@@ -0,0 +1 @@
uid://m3qqshwpk16h

View File

@@ -0,0 +1,101 @@
using System.Collections.Generic;
using System.Linq;
using Godot;
using ParasiticGod.Scripts.Singletons;
namespace ParasiticGod.Scripts.Components;
[GlobalClass]
public partial class RoadManager : Node2D
{
[Export] private Node2D _markersContainer;
[Export] private float _roadWidth = 4.0f;
[Export] private Color _roadColor = new("saddlebrown");
[Export] private Follower.FollowerTier _minimumTierForRoads = Follower.FollowerTier.Tier2;
private Line2D _roadNetwork;
public override void _Ready()
{
_roadNetwork = new Line2D
{
Width = _roadWidth,
DefaultColor = _roadColor,
};
AddChild(_roadNetwork);
Callable.From(() =>
{
GenerateRoads();
GameBus.Instance.PopulationVisualsUpdated += GenerateRoads;
}).CallDeferred();
}
public override void _ExitTree()
{
if (GameBus.Instance != null)
{
GameBus.Instance.PopulationVisualsUpdated -= GenerateRoads;
}
}
private void GenerateRoads()
{
_roadNetwork.ClearPoints();
var activeMarkers = _markersContainer.GetChildren()
.OfType<FollowerMarker>()
.Where(m => m.IsOccupied && m.FollowerInstance != null &&
m.FollowerInstance.Tier >= _minimumTierForRoads)
.ToList();
if (activeMarkers.Count < 2) return;
var treeNodes = new HashSet<FollowerMarker>();
var remainingNodes = new List<FollowerMarker>(activeMarkers);
var edges = new List<(Vector2, Vector2)>();
var startNode = remainingNodes[0];
treeNodes.Add(startNode);
remainingNodes.RemoveAt(0);
while (remainingNodes.Any())
{
FollowerMarker bestSource = null;
FollowerMarker bestDest = null;
var minDistanceSq = float.MaxValue;
foreach (var source in treeNodes)
{
foreach (var dest in remainingNodes)
{
var distSq = source.GlobalPosition.DistanceSquaredTo(dest.GlobalPosition);
if (distSq < minDistanceSq)
{
minDistanceSq = distSq;
bestSource = source;
bestDest = dest;
}
}
}
if (bestDest != null)
{
treeNodes.Add(bestDest);
remainingNodes.Remove(bestDest);
edges.Add((bestSource.Position, bestDest.Position));
}
else
{
break;
}
}
foreach (var (start, end) in edges)
{
_roadNetwork.AddPoint(start);
_roadNetwork.AddPoint(end);
}
}
}

View File

@@ -0,0 +1 @@
uid://cw8gpeaq3yfjn