newlon/scripts/level/LevelRunner.cs
2025-07-22 19:24:34 +05:00

196 lines
5.1 KiB
C#

using Godot;
using Newlon.Components.Zombies;
using System.Collections.Generic;
namespace Newlon.Components.Level;
[GlobalClass]
public partial class LevelRunner : Node
{
private AdventureLevelResource resource;
private int waveIndex = -1;
private bool hugeWaveApproaching = false;
private bool rewardGiven = false;
public float waveHealth = 0;
public float waveHealthMax = 0;
public int aliveZombies = 0;
public List<RuntimeZombieData> zombies = [];
[Export] private MoneyReward defaultReward;
[Export] private RowSpawner rowSpawner;
[Export] private Timer waveTimer;
[Export] private float approachNotificationTime;
[Export] private AnimationPlayer player;
[Export] private Node rewardParent;
[Export] private AudioStream firstWaveSound;
[Signal] public delegate void ResourceChangedEventHandler(AdventureLevelResource resource);
[Signal] public delegate void WaveChangedEventHandler(int to);
[Signal] public delegate void HugeWaveApproachingCallbackEventHandler();
[Signal] public delegate void HugeWaveInitiatedEventHandler(int waveNumber);
[Signal] public delegate void FinalWaveInitiatedEventHandler();
public override void _Ready()
{
waveTimer.Timeout += SummonWave;
}
public override void _Process(double delta)
{
if (RuntimeLevelData.Instance.GetLevelState() != RuntimeLevelData.LevelStates.Game) return;
if (waveIndex == resource.waves.Count - 1) return;
if (waveTimer.TimeLeft < approachNotificationTime && resource.waves[waveIndex + 1].isHugeWave && hugeWaveApproaching == false)
{
hugeWaveApproaching = true;
EmitSignal(SignalName.HugeWaveApproachingCallback);
}
if (rewardGiven == false && waveIndex == resource.waves.Count && PoolContainer.Instance.Zombies.GetChildCount() == 0)
{
SpawnReward(GetViewport().GetCamera2D().GlobalPosition);
}
}
public void SetLevelResource(AdventureLevelResource data)
{
resource = data;
waveTimer.Stop();
waveTimer.WaitTime = resource.initialWaveDelay;
waveTimer.Start();
EmitSignal(SignalName.ResourceChanged, resource);
}
private void SummonWave()
{
waveIndex += 1;
hugeWaveApproaching = false;
EmitSignal(SignalName.WaveChanged, waveIndex);
if (resource.waves[waveIndex].isHugeWave) EmitSignal(SignalName.HugeWaveInitiated, waveIndex);
rowSpawner.Add(resource.waves[waveIndex].zombiesOrdered);
ClearZombies();
if (waveIndex == resource.waves.Count - 1)
{
EmitSignal(SignalName.FinalWaveInitiated);
return;
}
if (waveIndex == 0)
{
AudioSequencer.Play("fl_wave", firstWaveSound);
}
waveTimer.WaitTime = resource.waves[waveIndex].customWaveDelay > 0 ? resource.waves[waveIndex].customWaveDelay : resource.standardWaveDelay;
waveTimer.Start();
}
private void OnHPChanged(EntitySignalContext context)
{
waveHealth = 0;
foreach (var zombie in zombies)
{
if (zombie != null)
{
waveHealth += zombie.GetHPMixed();
}
}
if (waveIndex == resource.waves.Count - 1) return;
if (waveHealth / waveHealthMax <= resource.wavePercentage)
{
if (resource.waves[waveIndex + 1].isHugeWave && waveTimer.TimeLeft > 6)
{
waveTimer.WaitTime = 6;
waveTimer.Start();
ClearZombies();
}
else
{
waveTimer.Stop();
SummonWave();
}
}
}
public void ClearZombies()
{
foreach (var zom in zombies)
{
if (zom != null)
{
zom.HPChangedMixed -= OnHPChanged;
}
}
waveHealthMax = 0;
waveHealth = waveHealthMax;
zombies.Clear();
}
public void AddZombie(RuntimeZombieData zombie)
{
zombies.Add(zombie);
waveHealthMax += zombie.GetMaxHPMixed();
waveHealth = waveHealthMax;
zombie.HPChangedMixed += OnHPChanged;
if (waveIndex == resource.waves.Count - 1)
{
aliveZombies+=1;
zombie.HasBeenKilled += OnLastZombieKilled;
}
}
private void OnLastZombieKilled(RuntimeZombieData who)
{
aliveZombies -= 1;
if (aliveZombies > 0) return;
SpawnReward(who.GlobalPosition);
}
private void SpawnReward(Vector2 where)
{
rewardGiven = true;
RuntimeLevelData.Instance.SetLevelState(RuntimeLevelData.LevelStates.Win);
DroppableItem reward;
if (resource.reward.Redeem())
{
reward = resource.reward.Scene.Instantiate<DroppableItem>();
LevelController.Instance.SetReward(resource.reward);
}
else
{
defaultReward.Redeem();
reward = defaultReward.Scene.Instantiate<DroppableItem>();
LevelController.Instance.SetReward(defaultReward);
}
if (reward is DroppableSeedpacket seedpacket && resource.reward is PlantReward plantReward)
{
seedpacket.plant = plantReward.Plant;
}
Callable.From(() =>
{
rewardParent.AddChild(reward);
reward.GlobalPosition = where + Vector2.Up * FieldParams.TileHeight * 0.5f;
var dropmover = new DropMover();
reward.AddChild(dropmover);
}).CallDeferred();
reward.PickedUp += () =>
{
player.Play("win");
var tween = CreateTween();
var camera = GetViewport().GetCamera2D();
tween.TweenProperty(reward, "global_position", camera.GlobalPosition, 4.0);
tween.Parallel().TweenProperty(reward, "global_scale", new Vector2(3.0f, 3.0f), 4.0);
tween.TweenInterval(1.0);
tween.TweenCallback(Callable.From(LevelController.Instance.EndLevel));
reward.InputPickable = false;
};
}
}