First builds and stuff done yipewaho
This commit is contained in:
@@ -11,7 +11,7 @@ public partial class CameraController : Camera2D
|
||||
[Export] private float maxX = 0f;
|
||||
[Export] private float minY = 0f;
|
||||
[Export] private float maxY = 0f;
|
||||
|
||||
|
||||
private bool _dragging;
|
||||
private Vector2 _grabWorldPos;
|
||||
|
||||
|
||||
@@ -6,13 +6,90 @@ public partial class GameController : Node
|
||||
public static GameController Instance;
|
||||
|
||||
[Export] private float _ShrineStartHP;
|
||||
[Export] private Label _CurrencyLabel;
|
||||
[Export] private Label _ShrineHealthLabel;
|
||||
|
||||
public int Wave { get; private set; }
|
||||
public int Currency { get; private set; }
|
||||
public float ShrineHealth { get; private set; }
|
||||
|
||||
private bool _waveOnGoing = false;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
GameController.Instance = this;
|
||||
Wave = 0;
|
||||
Currency = 300;
|
||||
ShrineHealth = 100;
|
||||
|
||||
_CurrencyLabel.Text = Currency.ToString();
|
||||
_ShrineHealthLabel.Text = $"{(100f * (ShrineHealth /_ShrineStartHP)):F1}%";
|
||||
|
||||
CallDeferred(nameof(DelayNextWave));
|
||||
}
|
||||
|
||||
private void StartWave()
|
||||
{
|
||||
if (_waveOnGoing) return;
|
||||
Wave++;
|
||||
_waveOnGoing = true;
|
||||
|
||||
int amountToSpawn = (int)Math.Round(5 * Math.Pow(Wave, 1.05));
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
// GD.Print((int)Math.Round(5 * Math.Pow(i + 1, 1.05)));
|
||||
}
|
||||
|
||||
EnemySpawner.Instance.EnemiesSpawned = amountToSpawn;
|
||||
|
||||
float baseDelay = 0.67f;
|
||||
float minDelay = 0.12f; // never go below this
|
||||
|
||||
float delay = Math.Max(minDelay, baseDelay * (float)Math.Pow(0.96f, Wave));
|
||||
|
||||
for (int i = 0; i < amountToSpawn; i++)
|
||||
{
|
||||
EnemySpawner.Instance.SpawnWithDelay(i * delay);
|
||||
}
|
||||
}
|
||||
|
||||
private async void DelayNextWave()
|
||||
{
|
||||
await ToSignal(GetTree().CreateTimer(7.5f), SceneTreeTimer.SignalName.Timeout);
|
||||
CallDeferred(nameof(StartWave));
|
||||
}
|
||||
|
||||
public void WaveComplete()
|
||||
{
|
||||
_waveOnGoing = false;
|
||||
CallDeferred(nameof(DelayNextWave));
|
||||
}
|
||||
|
||||
public void DamageShrine(float v)
|
||||
{
|
||||
v = Math.Abs(v);
|
||||
ShrineHealth -= v;
|
||||
_ShrineHealthLabel.Text = $"{(100f * (ShrineHealth /_ShrineStartHP)):F1}%";
|
||||
if (ShrineHealth <= 0)
|
||||
{
|
||||
// TODO LOSE SCENARIO
|
||||
}
|
||||
}
|
||||
|
||||
public void AddCurrency(int v)
|
||||
{
|
||||
v = Math.Abs(v);
|
||||
Currency += v;
|
||||
_CurrencyLabel.Text = Currency.ToString();
|
||||
}
|
||||
|
||||
public bool TryRemoveCurrency(int v)
|
||||
{
|
||||
v = Math.Abs(v);
|
||||
if (Currency < v) return false;
|
||||
Currency -= v;
|
||||
_CurrencyLabel.Text = Currency.ToString();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,15 +11,20 @@ public partial class Health : Node
|
||||
[Signal]
|
||||
public delegate void DeathEventHandler(Node2D node);
|
||||
|
||||
[Signal]
|
||||
public delegate void DamagedEventHandler();
|
||||
|
||||
public override void _EnterTree()
|
||||
{
|
||||
CurHP = MaxHP = _baseMaxHp * Mathf.Pow(GameController.Instance.Wave, 1.067f);
|
||||
// CurHP = MaxHP = _baseMaxHp * Mathf.Pow(GameController.Instance.Wave, 1.067f);
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Substract(float v)
|
||||
{
|
||||
v = Mathf.Abs(v);
|
||||
CurHP -= v;
|
||||
EmitSignalDamaged();
|
||||
if (CurHP <= 0)
|
||||
{
|
||||
EmitSignalDeath(_parent);
|
||||
@@ -34,6 +39,8 @@ public partial class Health : Node
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
CurHP = MaxHP = _baseMaxHp * Mathf.Pow(GameController.Instance.Wave, 1.067f);
|
||||
CurHP = MaxHP = _baseMaxHp * Mathf.Pow(1.075f, GameController.Instance.Wave);
|
||||
GD.Print("Curhp");
|
||||
GD.Print(CurHP);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,21 @@ public partial class Enemy : PathFollow2D
|
||||
{
|
||||
[Export] public EnemyType Type;
|
||||
[Export] public Health Health;
|
||||
[Export] private AudioStreamPlayer2D _audio;
|
||||
|
||||
public event Action<Enemy> Died;
|
||||
public event Action<Enemy> ReachedShrine;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
Health.Death += _ => Died?.Invoke(this);
|
||||
Health.Damaged += () =>
|
||||
{
|
||||
_audio?.SetPitchScale(RandomHelper.Float(0.8f, 1.2f));
|
||||
_audio?.Play();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public void ResetEnemy()
|
||||
{
|
||||
GlobalPosition = Vector2.Zero;
|
||||
@@ -23,4 +30,9 @@ public partial class Enemy : PathFollow2D
|
||||
Health.Reset();
|
||||
// reset velocity, animation, AI, etc here
|
||||
}
|
||||
|
||||
public void Despawn()
|
||||
{
|
||||
ReachedShrine?.Invoke(this);
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,12 @@ using System;
|
||||
|
||||
public partial class EnemyMovement : Node
|
||||
{
|
||||
[Export] private Enemy _enemy;
|
||||
[Export] private PathFollow2D _pathFollow2D;
|
||||
[Export] private float _speed;
|
||||
|
||||
private float _finalSpeed => _speed + 0.15f * (float)Math.Log(GameController.Instance.Wave + 1);
|
||||
|
||||
private double _time = 0;
|
||||
|
||||
public override void _EnterTree()
|
||||
@@ -15,6 +18,11 @@ public partial class EnemyMovement : Node
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
_pathFollow2D.ProgressRatio += (float)delta * (_speed / 1000f);
|
||||
_pathFollow2D.ProgressRatio += (float)delta * (_finalSpeed / 1000f);
|
||||
|
||||
if (_pathFollow2D.ProgressRatio >= 1.0)
|
||||
{
|
||||
_enemy.Despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ public partial class EnemyPool : Node
|
||||
private Enemy CreateEnemy(EnemyType type)
|
||||
{
|
||||
var e = _scenes[type].Instantiate<Enemy>();
|
||||
e.Died += ReturnToPool;
|
||||
e.Died += Died;
|
||||
e.ReachedShrine += DamageShrine;
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -44,8 +45,25 @@ public partial class EnemyPool : Node
|
||||
return e;
|
||||
}
|
||||
|
||||
private void DamageShrine(Enemy e)
|
||||
{
|
||||
GameController.Instance.DamageShrine(5);
|
||||
ReturnToPool(e);
|
||||
}
|
||||
|
||||
private void Died(Enemy e)
|
||||
{
|
||||
GameController.Instance.AddCurrency(25);
|
||||
ReturnToPool(e);
|
||||
}
|
||||
|
||||
private void ReturnToPool(Enemy e)
|
||||
{
|
||||
EnemySpawner.Instance.EnemiesSpawned--;
|
||||
if (EnemySpawner.Instance.EnemiesSpawned <= 0)
|
||||
{
|
||||
GameController.Instance.WaveComplete();
|
||||
}
|
||||
e.GetParent()?.RemoveChild(e);
|
||||
e.ProcessMode = ProcessModeEnum.Disabled;
|
||||
_pool[e.Type].Enqueue(e);
|
||||
|
||||
@@ -3,22 +3,28 @@ using System.Collections.Generic;
|
||||
|
||||
public partial class EnemySpawner : Node
|
||||
{
|
||||
public static EnemySpawner Instance;
|
||||
|
||||
[Export] private Node _pathParent;
|
||||
[Export] private EnemyPool _pool;
|
||||
|
||||
private readonly List<Path2D> _paths = new();
|
||||
|
||||
public int EnemiesSpawned = 0;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
foreach (var c in _pathParent.GetChildren())
|
||||
if (c is Path2D p)
|
||||
_paths.Add(p);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
SpawnWithDelay(i * 0.33f);
|
||||
// for (int i = 0; i < 10; i++)
|
||||
// SpawnWithDelay(i * 0.33f);
|
||||
}
|
||||
|
||||
private async void SpawnWithDelay(float t)
|
||||
public async void SpawnWithDelay(float t)
|
||||
{
|
||||
await ToSignal(GetTree().CreateTimer(t), SceneTreeTimer.SignalName.Timeout);
|
||||
Spawn();
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
extends Node2D
|
||||
|
||||
@export var float_height := 2.0
|
||||
@export var float_speed := 1.25
|
||||
|
||||
var start_y := 0.0
|
||||
var time := 0.0
|
||||
var phase := 0.0
|
||||
|
||||
func _ready():
|
||||
start_y = position.y
|
||||
phase = randf() * TAU # Random value between 0–2π
|
||||
|
||||
func _process(delta):
|
||||
time += delta * float_speed
|
||||
position.y = start_y + sin(time + phase) * float_height
|
||||
@@ -0,0 +1 @@
|
||||
uid://dxcke7geb20dm
|
||||
@@ -26,14 +26,16 @@ public partial class ProjectilePool : Node
|
||||
|
||||
public Projectile Get()
|
||||
{
|
||||
var p = _pool.Count > 0 ? _pool.Dequeue() : CreateProjectile();
|
||||
return p;
|
||||
// var p = _pool.Count > 0 ? _pool.Dequeue() : CreateProjectile();
|
||||
// return p;
|
||||
return CreateProjectile();
|
||||
}
|
||||
|
||||
private void ReturnToPool(Projectile p)
|
||||
{
|
||||
p.GetParent()?.RemoveChild(p);
|
||||
p.ProcessMode = ProcessModeEnum.Disabled;
|
||||
_pool.Enqueue(p);
|
||||
p.QueueFree();
|
||||
// p.ProcessMode = ProcessModeEnum.Disabled;
|
||||
// _pool.Enqueue(p);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Godot;
|
||||
|
||||
public partial class DragArea : Area2D
|
||||
{
|
||||
public override void _InputEvent(Viewport viewport, InputEvent @event, int shapeIdx)
|
||||
{
|
||||
if (@event is InputEventMouseButton mb && mb.Pressed)
|
||||
{
|
||||
GD.Print("Area clicked");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://bwgxw6ox8b1tq
|
||||
@@ -0,0 +1,76 @@
|
||||
using Godot;
|
||||
|
||||
public partial class DragMover : Node
|
||||
{
|
||||
[Export] public Node2D Parent; // The node that will move
|
||||
[Export] public Area2D DragArea; // Click detection
|
||||
[Export] public Area2D CheckArea; // Overlap validation
|
||||
[Export] public Area2D ShootArea; // This gets disabled while dragging
|
||||
|
||||
private bool _dragging;
|
||||
private Vector2 _dragOffset;
|
||||
private Vector2 _originalPosition;
|
||||
|
||||
private bool _canMove = true;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
if (Parent == null || DragArea == null || CheckArea == null)
|
||||
{
|
||||
GD.PushError("DragMover exports are not assigned.");
|
||||
return;
|
||||
}
|
||||
|
||||
DragArea.InputEvent += OnDragAreaInputEvent;
|
||||
_originalPosition = Parent.GlobalPosition;
|
||||
}
|
||||
|
||||
private void OnDragAreaInputEvent(Node viewport, InputEvent @event, long shapeIdx)
|
||||
{
|
||||
if (!_canMove) return;
|
||||
if (@event is not InputEventMouseButton mb) return;
|
||||
if (mb.ButtonIndex != MouseButton.Left) return;
|
||||
|
||||
if (mb.Pressed)
|
||||
{
|
||||
_dragging = true;
|
||||
ShootArea.Monitoring = false;
|
||||
_originalPosition = Parent.GlobalPosition;
|
||||
|
||||
_dragOffset = Parent.GlobalPosition - Parent.GetGlobalMousePosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
_dragging = false;
|
||||
ShootArea.Monitoring = true;
|
||||
|
||||
// Check overlaps when released
|
||||
if (CheckArea.HasOverlappingAreas() || CheckArea.HasOverlappingBodies())
|
||||
{
|
||||
Parent.GlobalPosition = _originalPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
MovementDelay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (!_dragging) return;
|
||||
|
||||
Parent.GlobalPosition = Parent.GetGlobalMousePosition() + _dragOffset;
|
||||
}
|
||||
|
||||
private async void MovementDelay()
|
||||
{
|
||||
ShootArea.Monitoring = false;
|
||||
_canMove = false;
|
||||
Parent.SetModulate(Color.FromHtml("ffffff20"));
|
||||
await ToSignal(GetTree().CreateTimer(4), SceneTreeTimer.SignalName.Timeout);
|
||||
Parent.SetModulate(Color.FromHtml("ffffffff"));
|
||||
ShootArea.Monitoring = true;
|
||||
_canMove = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://bpqof3jshxnug
|
||||
@@ -11,6 +11,7 @@ public partial class Turret : Node
|
||||
[Export] private float _fireRate;
|
||||
[Export] private Node2D _parent;
|
||||
[Export] private ProjectilePool _projectilePool;
|
||||
[Export] private AudioStreamPlayer2D _audio;
|
||||
private float _fireTimer = 0f;
|
||||
|
||||
private HashSet<Enemy> _enemiesInRange = new HashSet<Enemy>();
|
||||
@@ -26,7 +27,6 @@ public partial class Turret : Node
|
||||
if (enemyHitBox is EnemyArea earea)
|
||||
{
|
||||
_enemiesInRange.Add(earea.Enemy);
|
||||
GD.Print(earea.Name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ public partial class Turret : Node
|
||||
if (enemyHitBox is EnemyArea earea)
|
||||
{
|
||||
_enemiesInRange.Remove(earea.Enemy);
|
||||
GD.Print(earea.Name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +44,8 @@ public partial class Turret : Node
|
||||
_fireTimer += (float)delta;
|
||||
if (!(_fireTimer >= 1f / _fireRate)) return;
|
||||
_fireTimer = 0;
|
||||
_audio?.SetPitchScale(RandomHelper.Float(0.8f, 1.2f));
|
||||
_audio?.Play();
|
||||
var t = Helpers.GetClosest(_parent,_enemiesInRange.ToArray());
|
||||
var dir = (t.GlobalPosition - _parent.GlobalPosition).Normalized();
|
||||
var proj = _projectilePool.Get();
|
||||
|
||||
@@ -1,8 +1,78 @@
|
||||
using System;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace fgj26.Scripts.Turrets;
|
||||
public enum TurretType
|
||||
{
|
||||
Kitsune,
|
||||
Oni,
|
||||
Tengu
|
||||
}
|
||||
|
||||
public partial class TurretController : Node2D
|
||||
{
|
||||
|
||||
}
|
||||
[Export] private Dictionary<TurretType, PackedScene> _typeToPrefab;
|
||||
[Export] private Dictionary<TurretType, PackedScene> _typeToPlaceholder;
|
||||
[Export] private Node2D _turretParent;
|
||||
|
||||
[Signal]
|
||||
public delegate void PlaceTurretEventHandler(TurretType t);
|
||||
|
||||
private bool _placingTurret = false;
|
||||
|
||||
private TurretPlaceholder _turretPlaceholder;
|
||||
private TurretType _currentlyPlacingType;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
PlaceTurret += StartPlaceingTurret;
|
||||
}
|
||||
|
||||
public override void _Input(InputEvent @event)
|
||||
{
|
||||
if (_turretPlaceholder == null) return;
|
||||
if (!_placingTurret) return;
|
||||
var mpos = GetGlobalMousePosition();
|
||||
_turretPlaceholder.GlobalPosition = mpos;
|
||||
if (@event is InputEventMouseButton mb)
|
||||
{
|
||||
switch (mb.ButtonIndex)
|
||||
{
|
||||
case MouseButton.Left:
|
||||
if (!_turretPlaceholder.CanPlace) return;
|
||||
if (!GameController.Instance.TryRemoveCurrency(_turretPlaceholder.Cost)) return;
|
||||
var newT = _typeToPrefab[_currentlyPlacingType].Instantiate() as Node2D;
|
||||
_turretParent.AddChild(newT);
|
||||
newT.GlobalPosition = mpos;
|
||||
break;
|
||||
case MouseButton.Right:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
StopPlacingTurret();
|
||||
}
|
||||
}
|
||||
|
||||
private void StartPlaceingTurret(TurretType t)
|
||||
{
|
||||
if (_turretPlaceholder != null)
|
||||
{
|
||||
_turretPlaceholder.GetParent()?.RemoveChild(_turretPlaceholder);
|
||||
_turretPlaceholder.QueueFree();
|
||||
}
|
||||
|
||||
_turretPlaceholder = _typeToPlaceholder[t].Instantiate() as TurretPlaceholder;
|
||||
_turretParent.AddChild(_turretPlaceholder);
|
||||
_currentlyPlacingType = t;
|
||||
_placingTurret = true;
|
||||
}
|
||||
|
||||
private void StopPlacingTurret()
|
||||
{
|
||||
_turretPlaceholder.GetParent()?.RemoveChild(_turretPlaceholder);
|
||||
_turretPlaceholder.QueueFree();
|
||||
_turretPlaceholder = null;
|
||||
_placingTurret = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using Godot;
|
||||
|
||||
public partial class TurretPlaceholder : Node2D
|
||||
{
|
||||
[Export] private Area2D _placementChecker;
|
||||
[Export] public int Cost = 75;
|
||||
|
||||
public bool CanPlace { get; private set; }
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
CanPlace = !_placementChecker.HasOverlappingAreas() && !_placementChecker.HasOverlappingBodies();
|
||||
SetModulate(CanPlace ? Color.FromHtml("ffffff80") : Color.FromHtml("ff000080"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://c4764ubjc3tan
|
||||
Reference in New Issue
Block a user