file refactor

This commit is contained in:
Rendo 2025-07-11 22:35:36 +05:00
commit bffb012a26
175 changed files with 1086 additions and 1107 deletions

View file

@ -0,0 +1,12 @@
using Godot;
using Newlon;
public partial class LeftBoundaryMarker : Marker2D
{
public override void _Ready()
{
Utility.LeftFieldBoundary = (Vector2I)GlobalPosition;
QueueFree();
}
}

View file

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

View file

@ -0,0 +1,31 @@
using Godot;
using Newlon.Components.Zombies;
namespace Newlon.Components.Level;
public partial class LoseCheckbox : Area2D
{
[Export] private CanvasLayer gameOverLayer;
[Export] private AnimationPlayer fadeAnimation;
public override void _Ready()
{
AreaEntered += OnAreaEntered;
}
private void OnAreaEntered(Area2D area)
{
var parent = area.GetParent();
if (parent != null && parent is RuntimeZombieData zombieData)
{
Engine.TimeScale = 1.0;
fadeAnimation.Play("fade");
GetTree().Paused = true;
PhysicsServer2D.SetActive(true);
Callable.From(()=>
{
zombieData.Reparent(gameOverLayer);
}).CallDeferred();
}
}
}

View file

@ -0,0 +1 @@
uid://812ldoyxd5n5

110
scripts/level/PlantField.cs Normal file
View file

@ -0,0 +1,110 @@
using Godot;
using Newlon.Components.GUI.Seedpackets;
using Newlon.Components.Plants;
namespace Newlon.Components.Level;
public partial class PlantField : Node2D
{
private Node2D _plantSetter;
private DisplayResource _resource;
private Seedpacket _slot;
private bool _previousCanPlace;
private ChannelPlayer player;
[Export] private PackedScene particles;
public static PlantField Instance {get; private set;}
public override void _Ready()
{
Instance = this;
_plantSetter = GetChild<Node2D>(0);
player = GetNode<ChannelPlayer>("PlantPlayer");
}
public void SetPlant(Seedpacket slot, DisplayResource resource)
{
_resource = resource;
_slot = slot;
if (resource == null)
{
foreach(var child in _plantSetter.GetChildren())
child.QueueFree();
}
else
{
var scene = resource.Scene.Instantiate<Node2D>();
_plantSetter.AddChild(scene);
scene.UseParentMaterial = true;
}
}
public void ResetPlant()
{
SetPlant(null,null);
}
public override void _Process(double delta)
{
// Getting and storing global mouse position, setting plant-poiner to it
var mouse_pos = GetGlobalMousePosition();
_plantSetter.GlobalPosition = mouse_pos;
// Getting position in grid coordinates
var expected_pos = (_plantSetter.GlobalPosition / Utility.Tile).Ceil() * Utility.Tile - new Vector2(20, 14);
// Checking for boundaries
bool inBoundary = expected_pos.X > Utility.LeftFieldBoundary.X && expected_pos.X < Utility.RightFieldBoundary.X && expected_pos.Y > Utility.LeftFieldBoundary.Y && expected_pos.Y < Utility.RightFieldBoundary.Y;
bool canPlace = _resource != null
&& inBoundary
&& PoolContainer.Instance.EntityField[_resource.Layer].ContainsKey(expected_pos) == false
&& RuntimeLevelData.Instance.CheckSpendSun(_resource.Cost);
// Setting visuals
if (_previousCanPlace != canPlace)
{
if (canPlace)
{
Material.Set("shader_parameter/amount", 0);
}
else
{
Material.Set("shader_parameter/amount", 1);
}
Cursor.Instance.plant = canPlace;
Cursor.Instance.UpdateCursor();
}
_previousCanPlace = canPlace;
if (canPlace)
_plantSetter.GlobalPosition = expected_pos;
}
public override void _UnhandledInput(InputEvent @event)
{
if (@event.IsActionPressed("cancel_plant") && _slot != null)
{
_slot.ReleaseFocus();
}
if (@event.IsActionPressed("primary_action") && _previousCanPlace)
{
var plant = _resource.Scene.Instantiate<RuntimePlantData>();
PoolContainer.Instance.Plants.AddChild(plant);
plant.GlobalPosition = (_plantSetter.GlobalPosition / Utility.Tile).Ceil() * Utility.Tile - new Vector2(20, 14);
plant.Resource = (PlantResource)_resource;
PoolContainer.Instance.EntityField[_resource.Layer].Add(plant.GlobalPosition, plant);
RuntimeLevelData.Instance.SpendSun(_resource.Cost);
PoolContainer.Instance.SpawnParticles(particles, plant.GlobalPosition + Vector2.Down * Utility.TileHeight/2.0f);
player.Play();
// Unfocusing and recharging slot
_slot.Recharge();
}
}
}

View file

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

View file

@ -0,0 +1,66 @@
using Godot;
using System.Collections.Generic;
namespace Newlon.Components.Level;
//
// PoolContainer contains nodes that contain different elemnts that generate during runtime
// Is not pool in traditional sense, but named like that to prevent repetition
//
public partial class PoolContainer : Node2D
{
[Export]
public Node2D Zombies { get; private set; }
[Export]
public Node2D Plants { get; private set; }
[Export]
public Node2D Projectiles { get; private set; }
[Export]
public Node2D Structures { get; private set; }
[Export]
public Node2D Particles { get; private set; }
public static PoolContainer Instance { get; private set; }
public Dictionary<Vector2, Entity>[] EntityField = { new(), new(), new() };
public override void _Ready()
{
Instance = this;
}
public bool TryGetEntity(Vector2 key, out Entity result, int layer = 1)
{
if (EntityField[layer].ContainsKey(key))
{
result = EntityField[layer][key];
}
else
{
result = null;
}
return EntityField[layer].ContainsKey(key)
&& EntityField[layer][key] != null;
}
public bool TryGetEntity<T>(Vector2 key, out T result, int layer = 1) where T : class
{
if (EntityField[layer].ContainsKey(key))
{
result = EntityField[layer][key] as T;
}
else
{
result = null;
}
return EntityField[layer].ContainsKey(key)
&& EntityField[layer][key] != null
&& result != null;
}
public void SpawnParticles(PackedScene particles, Vector2 position)
{
var emitter = particles.Instantiate<StandardParticles>();
Instance.Particles.AddChild(emitter);
emitter.GlobalPosition = position;
}
}

View file

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

View file

@ -0,0 +1,12 @@
using Godot;
using Newlon;
public partial class RightBoundaryMarker : Marker2D
{
public override void _Ready()
{
Utility.RightFieldBoundary = (Vector2I)GlobalPosition;
QueueFree();
}
}

View file

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

View file

@ -0,0 +1,61 @@
using System;
using Godot;
namespace Newlon.Components.Level;
public partial class RuntimeLevelData : Node
{
public enum LevelStates
{
ChooseYourSeeds,
Pregame,
Game,
Win,
Loose
}
[Export]
public float SunCount { get; private set; } = 0;
public event Action<LevelStates> OnLevelStateChanged;
public static RuntimeLevelData Instance { get; private set; }
private LevelStates _currentState = LevelStates.ChooseYourSeeds;
public override void _Ready()
{
Instance = this;
GetTree().Paused = true;
}
#region Sun
public void AddSun(float amount)
{
SunCount += amount;
}
public void SpendSun(float amount)
{
SunCount -= amount;
}
public bool CheckSpendSun(float amount)
{
if (SunCount - amount < 0) return false;
return true;
}
#endregion
public void SetLevelState(LevelStates state)
{
OnLevelStateChanged(state);
_currentState = state;
}
//private Array<PlantResource> _selectedPlants;
//private bool _selected;
//public Array<PlantResource> GetSelectedPlants();
//public bool TrySelectPlants(Array<PlantResource>);
//public void ResetSelection();
}

View file

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

View file

@ -0,0 +1,29 @@
using Godot;
namespace Newlon.Components.Level;
public partial class SunSpawner : Node
{
[Export]
public int MinSun = 25;
[Export]
public int MaxSun = 25;
[Export]
private PackedScene SunScene;
public void Spawn()
{
float x = GD.Randf()*9*Utility.TileWidth;
uint y = GD.Randi() % 5;
var sun = SunScene.Instantiate<Sun>();
PoolContainer.Instance.Projectiles.AddChild(sun);
sun.GlobalPosition = new Vector2(Utility.LeftFieldBoundary.X + x, -90);
var moveTween = CreateTween();
moveTween.TweenProperty(sun,"global_position", new Vector2(Utility.LeftFieldBoundary.X + x,
Utility.LeftFieldBoundary.Y + Utility.TileHeight * y + Utility.TileHeight/2.0f),9-y);
}
}

View file

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

View file

@ -0,0 +1,170 @@
using System.Collections.Generic;
using Godot;
using Godot.Collections;
public partial class SurvivalZombieSpawner : Node
{
// Zombie pools
// Points from pools go in that order: support -> tank -> horde -> bank
// Horde pool is allowed to shrink during runtime
[Export] private Array<string> supportPool;
[Export] private Array<string> tankPool;
[Export] private Array<string> hordePool;
private List<ZombieResource> cachedSupportPool;
private List<ZombieResource> cachedTankPool;
private List<ZombieResource> cachedHordePool;
private float minSupportPoints;
private float minTankPoints;
private float minHordePoints;
[Export] private float points = 0;
[Export] private float huge_wave_points;
[Export] private Curve velocity_curve;
private float velocity = 0;
[Export] private double time = 0.0;
private float fin_a;
private RandomNumberGenerator rng = new();
public override void _Ready()
{
rng.Randomize();
cachedSupportPool = LoadFromRegistry(supportPool);
cachedTankPool = LoadFromRegistry(tankPool);
cachedHordePool = LoadFromRegistry(hordePool);
cachedTankPool.Sort((x, y) =>
{
return (int)(x.Cost - y.Cost);
});
cachedHordePool.Sort((x, y) =>
{
return (int)(x.Cost - y.Cost);
});
minSupportPoints = cachedSupportPool[0].Cost;
minTankPoints = cachedTankPool[0].Cost;
minHordePoints = cachedHordePool[0].Cost;
fin_a = (velocity_curve.Sample(velocity_curve.MaxDomain) - velocity_curve.Sample(velocity_curve.MaxDomain - 0.001f)) / 0.001f;
}
public override void _Process(double delta)
{
points += velocity * (float)delta;
if (time > velocity_curve.MaxDomain)
{
velocity += fin_a * (float)delta;
}
else
{
velocity = velocity_curve.Sample((float)time);
}
time += delta;
}
public void SummonWave()
{
bool big_wave = false;
if ((int)time % 300 == 0)
{
points += velocity/velocity_curve.Sample((float)time)*huge_wave_points;
big_wave = true;
}
float support_points = points * 0.2f;
float tank_points = points * 0.5f;
float horde_points = points * 0.3f;
points = 0;
List<ZombieResource> wave = [];
var remaining = SummonTank(tank_points, wave);
support_points += remaining * 0.8f;
horde_points += remaining * 0.2f;
horde_points += SummonSupport(support_points, wave);
points += SummonHorde(horde_points, wave, big_wave);
wave.Sort((x, y) => { return rng.RandiRange(-1, 1); });
foreach (var zom in wave)
{
ZombieSequencer.Instance.Add(zom.internal_id);
}
big_wave = false;
}
private float SummonSupport(float given_points, List<ZombieResource> wave)
{
if (cachedSupportPool.Count == 0)
{
return given_points;
}
while (given_points >= minSupportPoints)
{
var chosen_zombie = cachedSupportPool[rng.RandiRange(0, cachedSupportPool.Count - 1)];
if (given_points - chosen_zombie.Cost >= 0)
{
wave.Add(chosen_zombie);
given_points -= chosen_zombie.Cost;
}
}
return given_points;
}
private float SummonTank(float given_points, List<ZombieResource> wave)
{
if (cachedTankPool.Count == 0)
{
return given_points;
}
int zombieIndex = cachedTankPool.Count - 1;
while (given_points >= minSupportPoints && zombieIndex > -1)
{
if (cachedTankPool[zombieIndex].Cost > given_points)
{
zombieIndex--;
continue;
}
var chosen_zombie = cachedTankPool[zombieIndex];
wave.Add(chosen_zombie);
given_points -= chosen_zombie.Cost;
}
return given_points;
}
private float SummonHorde(float given_points, List<ZombieResource> wave, bool is_big)
{
if (cachedHordePool.Count == 0)
{
return given_points;
}
while (is_big == false && cachedHordePool.Count > 1 && cachedHordePool[1].Cost * 15 <= given_points)
{
cachedHordePool.RemoveAt(0);
minHordePoints = cachedHordePool[0].Cost;
}
while (given_points >= minHordePoints)
{
var chosen_zombie = cachedHordePool[rng.RandiRange(0, cachedHordePool.Count - 1)];
if (given_points - chosen_zombie.Cost >= 0)
{
wave.Add(chosen_zombie);
given_points -= chosen_zombie.Cost;
}
}
return given_points;
}
private List<ZombieResource> LoadFromRegistry(Array<string> pool)
{
List<ZombieResource> list = [];
foreach (var res in pool)
{
list.Add(GameRegistry.GetZombieByName(res));
}
return list;
}
}

View file

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

View file

@ -0,0 +1,90 @@
using System.Collections.Generic;
using Godot;
using Newlon;
using Newlon.Components.Level;
using Newlon.Components.Zombies;
public partial class ZombieSequencer : Node2D
{
public static ZombieSequencer Instance { get; private set; }
private Queue<string> queue = [];
private RandomNumberGenerator rng = new();
private bool turbo = false;
[Export] private Timer spawnTimer;
[Export] private Timer waveTimer;
private double startSpawnTime;
public override void _Ready()
{
rng.Randomize();
Instance = this;
startSpawnTime = spawnTimer.WaitTime;
}
private void FormSquad()
{
if (queue.Count == 0) return;
int count = rng.RandiRange(1, queue.Count > 5 ? 5 : queue.Count);
if (count == 5)
{
Spawn(queue.Dequeue(), 1);
Spawn(queue.Dequeue(), 2);
Spawn(queue.Dequeue(), 3);
Spawn(queue.Dequeue(), 4);
Spawn(queue.Dequeue(), 5);
}
List<int> list = [];
while (list.Count < count)
{
int lane = rng.RandiRange(1, 5);
if (list.Contains(lane) == true)
{
continue;
}
list.Add(lane);
}
foreach (int lane in list)
{
if (queue.Count > 0)
Spawn(queue.Dequeue(), lane);
}
}
private void Spawn(string id, int lane)
{
RuntimeZombieData zombie = GameRegistry.GetZombieByName(id).Scene.Instantiate<RuntimeZombieData>();
PoolContainer.Instance.Zombies.AddChild(zombie);
zombie.GlobalPosition = new Vector2(GlobalPosition.X, Utility.RightFieldBoundary.Y - (lane - 1) * Utility.TileHeight);
}
public void Add(string id)
{
queue.Enqueue(id);
}
public override void _Process(double delta)
{
if (turbo == false && queue.Count > waveTimer.WaitTime / startSpawnTime * 5.0)
{
spawnTimer.WaitTime = waveTimer.WaitTime / queue.Count;
turbo = true;
}
else if(turbo && queue.Count == 0)
{
spawnTimer.WaitTime = startSpawnTime;
turbo = false;
}
}
public void DebugClearQueue()
{
queue.Clear();
}
}

View file

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