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"]