From 3c9a51bd6a8d570c2fe5ac8abd0cf0ce197ba284 Mon Sep 17 00:00:00 2001 From: Conor Edmonds Date: Sun, 7 Jun 2026 21:14:46 -0400 Subject: [PATCH] working some more on enemy movement, they stilldon't always want to move to the sides when they reach the front so still some more work to do. also updated the aiming a bit to clamp the arc for firing and drawing, rewrote the draw arc function --- Commander.cs | 5 +-- Enemy.cs | 11 ++++++- EnemyController.cs | 78 +++++++++++++++++++++++++++++++++++----------- Main.cs | 7 +++++ Map.cs | 16 +++++++++- PlayArea.tscn | 7 +++-- Tower.cs | 48 ++++++++++++++++++++++------ main.tscn | 4 +-- project.godot | 5 +++ tower.tscn | 2 +- 10 files changed, 144 insertions(+), 39 deletions(-) diff --git a/Commander.cs b/Commander.cs index f2f5580..0248845 100644 --- a/Commander.cs +++ b/Commander.cs @@ -19,8 +19,7 @@ public partial class Commander : Sprite2D { if (_attack.Position.Y > GetViewportRect().Size.Y + 50) { - _attack.QueueFree(); - _attack = null; + UnloadAttack(); if (_actions <= 0) { EmitSignal(SignalName.ActionsUp); @@ -32,6 +31,7 @@ public partial class Commander : Sprite2D public void AttackEnteredBucket() { _actions += (_actions + 1) > _actionsMax ? 0 : 1; + UnloadAttack(); } public void LoadAttack(Vector2 OFFSET) @@ -65,5 +65,6 @@ public partial class Commander : Sprite2D public void UnloadAttack() { _attack.QueueFree(); + _attack = null; } } diff --git a/Enemy.cs b/Enemy.cs index fa2f0a0..26e403e 100644 --- a/Enemy.cs +++ b/Enemy.cs @@ -7,8 +7,10 @@ public partial class Enemy : StaticBody2D { [Signal] public delegate void DeathEventHandler(Enemy THIS); - public int _damage = 1, _health = 2, _speed, _visibilityRange = 10; + public bool _hovered = false; + public int _damage = 1, _health = 2, _speed, _speedRemaining, _visibilityRange = 4; public Vector2I _address, _range = Vector2I.Up; + public List _path = new(); public float _movement = 0; public EnemyController _enemyController; @@ -40,4 +42,11 @@ public partial class Enemy : StaticBody2D EmitSignal(SignalName.Death, this); } } + + public void OnMouseEntered(){ + _hovered = true; + } + public void OnMouseExited(){ + _hovered = true; + } } diff --git a/EnemyController.cs b/EnemyController.cs index e2825e3..e195ce4 100644 --- a/EnemyController.cs +++ b/EnemyController.cs @@ -35,6 +35,7 @@ public partial class EnemyController : TurnController newEnemy.Death += RemoveEnemy; newEnemy._speed = Globals._rng.Next(2,4+1); + newEnemy._speed = 3; newEnemy.Modulate = new Color(newEnemy._speed == 2 ? "#FF0000" : newEnemy._speed == 3 ? "#00FF00" : "#0000FF"); newEnemy._enemyController = this; @@ -56,33 +57,69 @@ public partial class EnemyController : TurnController { Tween tween = CreateTween(); tween.SetParallel(); - Dictionary> enemyPaths = new(); - Dictionary enemyOffsets = new(); - _enemies = [.. _enemies.OrderBy(e => e._address.Y).ThenBy(e => e._address.X)]; + for (int i = 0; i < _enemies.Count; i++) { Enemy enemy = _enemies[i]; - List path = _playArea._map.GetPath(enemy._address, new Vector2I(enemy._address.X, _playArea._map._minY)); - enemyPaths[enemy] = path; - enemyOffsets[enemy] = 0; + Vector2I goal = new(enemy._address.X, _playArea._map._minY); + if (Math.Abs(goal.Y - enemy._address.Y) <= enemy._visibilityRange) + { + // GD.Print(goal.Y, enemy._address.Y,enemy._visibilityRange); + while(_playArea._map.IsRowFull(goal.Y)) + { + goal.Y++; + } + // if (_playArea._map.GetOccupant(goal) != null) + // { + // goal.X = Math.Clamp(goal.X+1, _playArea._map._minX+1, _playArea._map._maxX-1); + // } + int loop = 0; + while (_playArea._map.GetOccupant(goal) != null) + { + goal.X = Math.Clamp(enemy._address.X + ((int)MathF.Floor(loop / 2) + 1) * (loop % 2 == 0 ? 1 : -1), _playArea._map._minX, _playArea._map._maxX); + loop++; + } + } + List path = _playArea._map.GetPath(enemy._address, goal); + enemy._path = path; + enemy._speedRemaining = enemy._address.Y <= _playArea._map._minY ? 0 : enemy._speed; } + _enemies = [.. _enemies.OrderByDescending(e => e._path.Count).ThenBy(e => e._address.Y).ThenBy(e => Math.Abs(e._address.X))]; + int maxSpeed = _enemies.Max(e => e._speedRemaining); - int maxSpeed = _enemies.Max(e => e._speed); + List remainingEnemies = [.. _enemies]; for (int i = 0; i < maxSpeed; i++) { - for (int j = 0; j < _enemies.Count; j++) + for (int j = 0; j < remainingEnemies.Count; j++) { - Enemy enemy = _enemies[j]; - int offset = enemyOffsets[enemy]; - if (i >= enemy._speed - offset) + Enemy enemy = remainingEnemies[j]; + if (enemy._speedRemaining <= 0) { + remainingEnemies.Remove(enemy); continue; } - List path = enemyPaths[enemy]; - Vector2I cell = path[i - offset]; + List path = enemy._path; + int pathIndex = enemy._speed - enemy._speedRemaining; + Vector2I cell = path[pathIndex]; + Enemy obstacle = _playArea._map.GetOccupant(cell); + if (obstacle != null) + { + int obstacleRemainingMoves = obstacle._speedRemaining; + if (obstacleRemainingMoves <= 0) + { + _playArea._map.EvaluateSolidCells(); + List newPath = _playArea._map.GetPath(enemy._address, new Vector2I(enemy._address.X, _playArea._map._minY)); + enemy._path = newPath; - if (_playArea._map.GetOccupant(cell) == null) + newPath.InsertRange(0, [.. Enumerable.Repeat(newPath[0], pathIndex)]); + + cell = enemy._path[pathIndex]; + obstacle = _playArea._map.GetOccupant(cell); + } + } + + if (obstacle == null) { _playArea._map.SetCellEnemy(enemy._address, null); enemy._address = cell; @@ -96,12 +133,14 @@ public partial class EnemyController : TurnController tween.Parallel(); } tween.TweenProperty(enemy, "global_position", _playArea._map.GetCellPositionFromAddress(enemy._address), .2f); - } - else - { - enemyOffsets[enemy]++; - } + if (enemy._address.Y <= _playArea._map._minY) + { + remainingEnemies.Remove(enemy); + continue; + } + enemy._speedRemaining--; + } } } @@ -111,6 +150,7 @@ public partial class EnemyController : TurnController public void RemoveEnemy(Enemy ENEMY_TO_REMOVE) { _enemies.Remove(ENEMY_TO_REMOVE); + _playArea._map.SetCellEnemy(ENEMY_TO_REMOVE._address, null); ENEMY_TO_REMOVE.QueueFree(); } diff --git a/Main.cs b/Main.cs index a488d79..ca4ab5a 100644 --- a/Main.cs +++ b/Main.cs @@ -38,6 +38,13 @@ public partial class Main : Node { GetTree().Quit(); } + if (Input.IsActionJustPressed("changeTurn")) + { + if(_turnController == _playerController) + { + ChangeTurn(); + } + } } public void ChangeTurn() diff --git a/Map.cs b/Map.cs index 221f0c4..75f87f3 100644 --- a/Map.cs +++ b/Map.cs @@ -17,11 +17,12 @@ public partial class Map : TileMapLayer { base._Ready(); _cellSize = TileSet.TileSize; - List cells = GetUsedCells().ToList(); + List cells = [.. GetUsedCells()]; _minX = cells.Min(c => c.X); _maxX = cells.Max(c => c.X); _minY = cells.Min(c => c.Y); _maxY = cells.Max(c => c.Y); + // GD.Print(_minX, _maxX, _minY, _maxY); _leftmostColumn = [.. cells.Where(c => c.X == _minX)]; _rightmostColumn = [.. cells.Where(c => c.X == _maxX)]; _topRow = [.. cells.Where(c => c.Y == _minY)]; @@ -51,6 +52,14 @@ public partial class Map : TileMapLayer return hasOccupant || isSolid; } + public bool IsRowFull(int ROW_TO_CHECK) + { + List cells = [.. GetUsedCells()]; + List rowCells = [.. cells.Where(c => c.Y == ROW_TO_CHECK)]; + + return rowCells.All(c => GetOccupant(c) != null); + } + public void SetCellEnemy(Vector2I ADDRESS, Enemy ENEMY) { _addressOccupants[ADDRESS] = ENEMY; @@ -89,4 +98,9 @@ public partial class Map : TileMapLayer return pathTaken; } + public void EvaluateSolidCells() + { + GetUsedCells().ToList().ForEach(c => IsCellSolid(c)); + } + } diff --git a/PlayArea.tscn b/PlayArea.tscn index 2598d0d..cb901b8 100644 --- a/PlayArea.tscn +++ b/PlayArea.tscn @@ -1,7 +1,7 @@ [gd_scene format=4 uid="uid://dumcridek4xy3"] [ext_resource type="Script" uid="uid://bnaxgcafcvtfv" path="res://PlayArea.cs" id="1_lq4m8"] -[ext_resource type="PackedScene" uid="uid://mjinvqj25wha" path="res://Map.tscn" id="2_wqv88"] +[ext_resource type="PackedScene" path="res://Map.tscn" id="2_wqv88"] [ext_resource type="Texture2D" uid="uid://cf554xlykq1o4" path="res://Art/tile_set.png" id="3_md2b6"] [ext_resource type="PackedScene" uid="uid://b2mb7mimdu5ad" path="res://bucket.tscn" id="5_vqhsw"] @@ -14,7 +14,7 @@ size = Vector2(400, 1000) [sub_resource type="RectangleShape2D" id="RectangleShape2D_7h2rc"] size = Vector2(400, 80) -[sub_resource type="TileMapPattern" id="TileMapPattern_vqhsw"] +[sub_resource type="TileMapPattern" id="TileMapPattern_xy0lb"] tile_data = PackedInt32Array(0, 0, 0, 65536, 0, 1, 1, 0, 1, 65537, 0, 0) [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_vqhsw"] @@ -38,7 +38,7 @@ tile_size = Vector2i(40, 40) custom_data_layer_0/name = "is_solid" custom_data_layer_0/type = 1 sources/0 = SubResource("TileSetAtlasSource_vqhsw") -pattern_0 = SubResource("TileMapPattern_vqhsw") +pattern_0 = SubResource("TileMapPattern_xy0lb") [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_xy0lb"] texture = ExtResource("3_md2b6") @@ -233,5 +233,6 @@ tile_set = SubResource("TileSet_he03c") position = Vector2(0, 440) [node name="InitialPositions" type="TileMapLayer" parent="." unique_id=555896823] +visible = false tile_map_data = PackedByteArray("AAD0//3/AAADAAEAAAD1//z/AAADAAEAAAD1//7/AAADAAEAAAD2//3/AAADAAEAAAD5/wMAAAADAAEAAAD4/wIAAAADAAEAAAD6/wQAAAADAAEAAAD7/wMAAAADAAEAAAD8/wQAAAADAAEAAAD9/wMAAAADAAEAAAD+/wQAAAADAAEAAAD//wMAAAADAAEAAAAAAAMAAAADAAEAAAABAAQAAAADAAEAAAACAAMAAAADAAEAAAADAAQAAAADAAEAAAAEAAMAAAADAAEAAAAFAAQAAAADAAEAAAAGAAMAAAADAAEAAAAHAAIAAAADAAEAAAD8////AAADAAEAAAD9//7/AAADAAEAAAD+//3/AAADAAEAAAD///7/AAADAAEAAAAAAP7/AAADAAEAAAABAP3/AAADAAEAAAACAP7/AAADAAEAAAADAP//AAADAAEAAAAJAP3/AAADAAEAAAAKAP7/AAADAAEAAAALAP3/AAADAAEAAAAKAPz/AAADAAEAAAA=") tile_set = SubResource("TileSet_vqhsw") diff --git a/Tower.cs b/Tower.cs index 5c3462a..931c350 100644 --- a/Tower.cs +++ b/Tower.cs @@ -29,7 +29,8 @@ public partial class Tower : Sprite2D { if (_arcEnd != GetGlobalMousePosition()) { - DrawArc(_attackSpawn.GlobalPosition, GetGlobalMousePosition()); + float arcAngle = CalculateLaunchAngle(_attackSpawn.GlobalPosition, GetGlobalMousePosition()); + DrawArc(arcAngle, _attackSpawn.GlobalPosition, GetGlobalMousePosition()); } if (Input.IsActionJustPressed("rightClick")) { @@ -39,7 +40,7 @@ public partial class Tower : Sprite2D else if (Input.IsActionJustPressed("leftClick")) { - _aimOffset = CalculateLaunchAngle(_attackSpawn.GlobalPosition, GetGlobalMousePosition()); + _aimOffset = ToLaunchVector(CalculateLaunchAngle(_attackSpawn.GlobalPosition, GetGlobalMousePosition())); _commander.ShootCurrentAttack(_aimOffset); @@ -58,7 +59,7 @@ public partial class Tower : Sprite2D } } - public Vector2 CalculateLaunchAngle(Vector2 START, Vector2 END, int MAX_ITERATIONS = 20) + public float CalculateLaunchAngle(Vector2 START, Vector2 END, int MAX_ITERATIONS = 20) { Vector2 offset = END - START; float baseAngle = offset.Angle(); @@ -95,8 +96,9 @@ public partial class Tower : Sprite2D bestAngle = testAngle; } + bestAngle = (float)Mathf.Clamp(bestAngle, Math.PI, Math.PI * 2); - return Vector2.FromAngle(bestAngle) * _launchSpeed; + return bestAngle; } public void ClearArc() @@ -105,16 +107,40 @@ public partial class Tower : Sprite2D path.Curve.ClearPoints(); } - public void DrawArc(Vector2 START, Vector2 END, int MAX_ITERATIONS = 20) + public void DrawArc(float ANGLE, Vector2 START, Vector2 END, int MAX_ITERATIONS = 20) { CalculateLaunchAngle(START, END); + float speed = _launchSpeed, gravity = Globals._gravity, drag = Globals._drag, delta = 1.0f / 60.0f; + Vector2 velocity = new Vector2(Mathf.Cos(ANGLE), Mathf.Sin(ANGLE)) * speed; + Vector2 position = START; + + float directionSign = Mathf.Sign(END.X - START.X); + float maxFlightTime = 6.0f; + int totalSteps = (int)(maxFlightTime / delta); + List arc = [START]; + + for (int step = 0; step < totalSteps; step++) + { + velocity.Y += gravity * delta; + velocity -= velocity * drag * delta; + position += velocity * delta; + arc.Add(position); + + bool reachedTargetX = (directionSign >= 0) ? (position.X >= END.X) : (position.X <= END.X); + + if (reachedTargetX) + { + break; + } + } + Path2D path = _commander._attack.GetNode("PredictedPath"); path.Curve.ClearPoints(); - for (int i = 0; i < _arc.Count; i++) + for (int i = 0; i < arc.Count; i++) { - path.Curve.AddPoint(_arc[i]); + path.Curve.AddPoint(arc[i]); } } @@ -127,14 +153,12 @@ public partial class Tower : Sprite2D float directionSign = Mathf.Sign(TARGET.X - START.X); float maxFlightTime = 6.0f; int totalSteps = (int)(maxFlightTime / delta); - _arc = [START]; for (int step = 0; step < totalSteps; step++) { velocity.Y += gravity * delta; velocity -= velocity * drag * delta; position += velocity * delta; - _arc.Add(position); bool reachedTargetX = (directionSign >= 0) ? (position.X >= TARGET.X) : (position.X <= TARGET.X); @@ -144,7 +168,6 @@ public partial class Tower : Sprite2D } } - _arcEnd = _arc[^1]; return (position.Y > TARGET.Y) ? 99999f : -99999f; } @@ -156,6 +179,11 @@ public partial class Tower : Sprite2D } } + public Vector2 ToLaunchVector(float ANGLE) + { + return Vector2.FromAngle(ANGLE) * _launchSpeed; + } + public void OnMouseEntered(){ _hovered = true; } diff --git a/main.tscn b/main.tscn index cd23f6b..ecd8c11 100644 --- a/main.tscn +++ b/main.tscn @@ -1,7 +1,7 @@ [gd_scene format=3 uid="uid://dcp7p6al4i0b7"] [ext_resource type="Script" uid="uid://cg1m762ed04kv" path="res://Main.cs" id="1_ig7tw"] -[ext_resource type="PackedScene" path="res://PlayArea.tscn" id="2_1bvp3"] +[ext_resource type="PackedScene" uid="uid://dumcridek4xy3" path="res://PlayArea.tscn" id="2_1bvp3"] [ext_resource type="PackedScene" uid="uid://c6b188d2a20eq" path="res://enemy_controller.tscn" id="4_1bvp3"] [ext_resource type="PackedScene" uid="uid://b7kvx7p0b2086" path="res://player_controller.tscn" id="4_lquwl"] @@ -9,7 +9,7 @@ script = ExtResource("1_ig7tw") [node name="PlayArea" parent="." unique_id=1123610167 instance=ExtResource("2_1bvp3")] -position = Vector2(960, 510) +position = Vector2(931, 542) [node name="EnemyController" parent="." unique_id=1894449838 instance=ExtResource("4_1bvp3")] diff --git a/project.godot b/project.godot index 80cda47..7ae815b 100644 --- a/project.godot +++ b/project.godot @@ -46,6 +46,11 @@ rightClick={ "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":2,"position":Vector2(109, 18),"global_position":Vector2(118, 66),"factor":1.0,"button_index":2,"canceled":false,"pressed":true,"double_click":false,"script":null) ] } +changeTurn={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":84,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} [physics] diff --git a/tower.tscn b/tower.tscn index dc1d649..bfcc8bd 100644 --- a/tower.tscn +++ b/tower.tscn @@ -22,7 +22,7 @@ debug_color = Color(0.6411928, 0.52469516, 0, 0.41960785) position = Vector2(25, -50) [node name="AttackSpawn" type="Marker2D" parent="." unique_id=715904909] -position = Vector2(0, 50) +position = Vector2(0, -24) [connection signal="mouse_entered" from="Area" to="." method="OnMouseEntered"] [connection signal="mouse_exited" from="Area" to="." method="OnMouseExited"]