From 1898d91a2854883147853b75a4b3ffcaf0388155 Mon Sep 17 00:00:00 2001 From: Minimata Date: Sat, 16 May 2026 19:48:48 +0200 Subject: [PATCH] wave behavior and fixed explosion --- forge/calculators/ForgeDamageExecution.cs | 4 + .../ability_datas/explosion_hit.tres | 4 +- managers/{ => Inventory}/InventoryManager.cs | 0 .../{ => Inventory}/InventoryManager.cs.uid | 0 managers/{ => Inventory}/WeaponInventory.cs | 0 .../{ => Inventory}/WeaponInventory.cs.uid | 0 managers/TokenManager.cs | 3 +- managers/Wave/EnemyDescription.cs | 21 ++++ managers/Wave/EnemyDescription.cs.uid | 1 + managers/Wave/SingleWave.cs | 13 +++ managers/Wave/SingleWave.cs.uid | 1 + managers/Wave/WaveContent.cs | 11 ++ managers/Wave/WaveContent.cs.uid | 1 + managers/Wave/WaveManager.cs | 100 ++++++++++++++++++ managers/Wave/WaveManager.cs.uid | 1 + .../Wave/resources/flying_enemy_desc.tres | 9 ++ .../Wave/resources/grounded_enemy_desc.tres | 9 ++ managers/Wave/resources/one_of_each_wave.tres | 17 +++ .../Wave/resources/projectile_enemy_desc.tres | 10 ++ maps/_templates/MainSceneTemplate.cs | 22 +++- maps/levels/4 - tuto_waves.tscn | 54 ++++++++++ scenes/enemies/Enemy.cs | 15 +-- scenes/explosion/Explosion.cs | 3 +- scenes/explosion/explosion.tscn | 7 ++ scenes/spawners/Spawner.cs | 50 +++++++-- tools/IterableUtils.cs | 19 ++++ tools/IterableUtils.cs.uid | 1 + 27 files changed, 355 insertions(+), 21 deletions(-) rename managers/{ => Inventory}/InventoryManager.cs (100%) rename managers/{ => Inventory}/InventoryManager.cs.uid (100%) rename managers/{ => Inventory}/WeaponInventory.cs (100%) rename managers/{ => Inventory}/WeaponInventory.cs.uid (100%) create mode 100644 managers/Wave/EnemyDescription.cs create mode 100644 managers/Wave/EnemyDescription.cs.uid create mode 100644 managers/Wave/SingleWave.cs create mode 100644 managers/Wave/SingleWave.cs.uid create mode 100644 managers/Wave/WaveContent.cs create mode 100644 managers/Wave/WaveContent.cs.uid create mode 100644 managers/Wave/WaveManager.cs create mode 100644 managers/Wave/WaveManager.cs.uid create mode 100644 managers/Wave/resources/flying_enemy_desc.tres create mode 100644 managers/Wave/resources/grounded_enemy_desc.tres create mode 100644 managers/Wave/resources/one_of_each_wave.tres create mode 100644 managers/Wave/resources/projectile_enemy_desc.tres create mode 100644 tools/IterableUtils.cs create mode 100644 tools/IterableUtils.cs.uid diff --git a/forge/calculators/ForgeDamageExecution.cs b/forge/calculators/ForgeDamageExecution.cs index 4b907f29..18de2328 100644 --- a/forge/calculators/ForgeDamageExecution.cs +++ b/forge/calculators/ForgeDamageExecution.cs @@ -30,6 +30,7 @@ public class DamageExecution : CustomExecution public DamageExecution(DamageType damageType, ForgeTagContainer? damageDealerEventTags, ForgeTagContainer? damageReceiverEventTags) { + // Capture target mana and magic resistance TargetHealth = new AttributeCaptureDefinition( "CharacterAttributeSet.Health", @@ -60,6 +61,8 @@ public class DamageExecution : CustomExecution target, effectEvaluatedData); + GD.Print(targetIncomingDamage); + if (targetIncomingDamage <= 0) { return [.. results]; @@ -70,6 +73,7 @@ public class DamageExecution : CustomExecution targetIncomingDamage *= hitEffectData.Magnitude; } + // Apply health reduction to target if attribute exists if (TargetHealth.TryGetAttribute(target, out EntityAttribute? targetHealthAttribute)) { diff --git a/forge/resources/ability_datas/explosion_hit.tres b/forge/resources/ability_datas/explosion_hit.tres index a7fd6185..2f5aefd5 100644 --- a/forge/resources/ability_datas/explosion_hit.tres +++ b/forge/resources/ability_datas/explosion_hit.tres @@ -6,6 +6,7 @@ [ext_resource type="Script" uid="uid://cfx62w40nd84v" path="res://forge/calculators/ForgeDamageExecution.cs" id="4_xfamx"] [ext_resource type="Script" uid="uid://b44cse62qru7j" path="res://scenes/components/knockback/RKnockback.cs" id="5_tw4gc"] [ext_resource type="Resource" uid="uid://bhn27s8ne0uyg" path="res://forge/resources/tag_containers/on_knockback_dealt.tres" id="6_nq3a0"] +[ext_resource type="Resource" uid="uid://5obdxlcpw8qt" path="res://forge/resources/tag_containers/knockback_immune.tres" id="7_3ma4g"] [ext_resource type="Resource" uid="uid://bkr6uu57wm3o3" path="res://forge/resources/tag_containers/on_knockback_received.tres" id="7_3utx7"] [ext_resource type="Resource" uid="uid://45l7vnfs72b" path="res://forge/resources/tag_containers/knockbackable_tag.tres" id="8_7hfxb"] [ext_resource type="Script" uid="uid://diondfg5xp78h" path="res://forge/calculators/ForgeKnockbackExecution.cs" id="9_lysxe"] @@ -32,6 +33,7 @@ metadata/_custom_type_script = "uid://b44cse62qru7j" [sub_resource type="Resource" id="Resource_dy671"] script = ExtResource("9_lysxe") KnockbackableTag = ExtResource("8_7hfxb") +KnockbackImmuneTag = ExtResource("7_3ma4g") Knockback = SubResource("Resource_ndb8p") KnockbackDealerEventTags = ExtResource("6_nq3a0") KnockbackReceiverEventTags = ExtResource("7_3utx7") @@ -89,7 +91,7 @@ BaseValue = 1 script = ExtResource("13_c85am") Name = "Explosion hit" Modifiers = Array[Object]([SubResource("Resource_xwtie")]) -Components = Array[Object]([ExtResource("1_w36j6")]) +Components = [ExtResource("1_w36j6")] Executions = Array[Object]([SubResource("Resource_vy8wr"), SubResource("Resource_dy671")]) StackLimit = SubResource("Resource_ikb7l") InitialStack = SubResource("Resource_i0sj3") diff --git a/managers/InventoryManager.cs b/managers/Inventory/InventoryManager.cs similarity index 100% rename from managers/InventoryManager.cs rename to managers/Inventory/InventoryManager.cs diff --git a/managers/InventoryManager.cs.uid b/managers/Inventory/InventoryManager.cs.uid similarity index 100% rename from managers/InventoryManager.cs.uid rename to managers/Inventory/InventoryManager.cs.uid diff --git a/managers/WeaponInventory.cs b/managers/Inventory/WeaponInventory.cs similarity index 100% rename from managers/WeaponInventory.cs rename to managers/Inventory/WeaponInventory.cs diff --git a/managers/WeaponInventory.cs.uid b/managers/Inventory/WeaponInventory.cs.uid similarity index 100% rename from managers/WeaponInventory.cs.uid rename to managers/Inventory/WeaponInventory.cs.uid diff --git a/managers/TokenManager.cs b/managers/TokenManager.cs index 758428d1..5d441b55 100644 --- a/managers/TokenManager.cs +++ b/managers/TokenManager.cs @@ -36,7 +36,8 @@ public partial class TokenManager : Node if (RequestQueue.First() != owner.GetInstanceId()) return null; // Next in line is not the requester RequestQueue.RemoveAt(0); - var token = new Token(owner.GetInstanceId(), () => UseToken(owner.GetInstanceId())); + var ownerInstanceId = owner.GetInstanceId(); + var token = new Token(ownerInstanceId, () => UseToken(ownerInstanceId)); Tokens.Add(token.InstanceId, token); return token; } diff --git a/managers/Wave/EnemyDescription.cs b/managers/Wave/EnemyDescription.cs new file mode 100644 index 00000000..2ee902d3 --- /dev/null +++ b/managers/Wave/EnemyDescription.cs @@ -0,0 +1,21 @@ +using Godot; + +namespace Movementtests.managers; + +[GlobalClass] +public partial class EnemyDescription(PackedScene scene, EnemyDescription.EnemyType type, RMovement? movementOverride) : Resource +{ + public enum EnemyType + { + Normal, + Projectile, + } + + [Export(PropertyHint.NodeType)] public required PackedScene Scene { get; set; } = scene; + + [Export] public required EnemyType Type { get; set; } = type; + + [Export] public RMovement? MovementOverride { get; set; } = movementOverride; + + public EnemyDescription() : this(ResourceLoader.Load("uid://dxt0e2ugmttqq"), EnemyType.Normal, null) {} +} \ No newline at end of file diff --git a/managers/Wave/EnemyDescription.cs.uid b/managers/Wave/EnemyDescription.cs.uid new file mode 100644 index 00000000..7bc2a6cb --- /dev/null +++ b/managers/Wave/EnemyDescription.cs.uid @@ -0,0 +1 @@ +uid://rhdkfi7nuvu1 diff --git a/managers/Wave/SingleWave.cs b/managers/Wave/SingleWave.cs new file mode 100644 index 00000000..161fd7f4 --- /dev/null +++ b/managers/Wave/SingleWave.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Godot; + +namespace Movementtests.managers; + + +[GlobalClass] +public partial class SingleWave(EnemyDescription[] enemies) : Resource +{ + [Export] public Godot.Collections.Dictionary EnemiesToSpawn { get; set; } = []; + + public SingleWave() : this([]) {} +} \ No newline at end of file diff --git a/managers/Wave/SingleWave.cs.uid b/managers/Wave/SingleWave.cs.uid new file mode 100644 index 00000000..183cd5f0 --- /dev/null +++ b/managers/Wave/SingleWave.cs.uid @@ -0,0 +1 @@ +uid://cr8wog705ane6 diff --git a/managers/Wave/WaveContent.cs b/managers/Wave/WaveContent.cs new file mode 100644 index 00000000..ef510dca --- /dev/null +++ b/managers/Wave/WaveContent.cs @@ -0,0 +1,11 @@ +using Godot; + +namespace Movementtests.managers; + +[GlobalClass] +public partial class WaveContent(SingleWave[] waves) : Resource +{ + [Export] public SingleWave[] Waves { get; set; } = waves; + + public WaveContent() : this([]) {} +} \ No newline at end of file diff --git a/managers/Wave/WaveContent.cs.uid b/managers/Wave/WaveContent.cs.uid new file mode 100644 index 00000000..3a0842f5 --- /dev/null +++ b/managers/Wave/WaveContent.cs.uid @@ -0,0 +1 @@ +uid://dijmv0wqc1xuv diff --git a/managers/Wave/WaveManager.cs b/managers/Wave/WaveManager.cs new file mode 100644 index 00000000..64a41ac2 --- /dev/null +++ b/managers/Wave/WaveManager.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using System.Linq; +using Chickensoft.AutoInject; +using Chickensoft.Collections; +using Chickensoft.Introspection; +using Godot; +using Movementtests.tools; + +namespace Movementtests.managers; + + +[GlobalClass, Meta(typeof(IAutoNode))] +public partial class WaveManager : Node +{ + public override void _Notification(int what) => this.Notify(what); + + public WaveContent WaveContent { get; set; } + + public int CurrentWaveIndex { get; set; } + public int CurrentWaveCount => WaveContent.Waves.Length; + + public Godot.Collections.Dictionary CurrentWave => + WaveContent.Waves[CurrentWaveIndex].EnemiesToSpawn; + + public Dictionary CurrentSpawnedEnemies { get; set; } = []; + public Set SpawnersInUse { get; set; } = []; + + public List Spawners { get; set; } = []; + public void RegisterSpawner(Spawner spawner) => Spawners.Add(spawner); + + public void InitializeFromResource(WaveContent waveContent) => WaveContent = waveContent; + + public void StartWaves() + { + CurrentWaveIndex = 0; + StartNextWave(); + } + + public void StartNextWave() + { + if (CurrentWave.Count == 0) + { + GD.PrintErr("Wave has no enemies"); + return; + } + + if (Spawners.Count == 0) + { + GD.PrintErr("No spawners registered"); + return; + } + + SpawnEnemiesAsAvailable(); + } + + public void SpawnEnemiesAsAvailable() + { + var randomizedSpawners = Spawners.ToArray(); + randomizedSpawners.Shuffle(); + + foreach (var (enemyDescription, numberRemaining) in CurrentWave) + { + if (numberRemaining <= 0) continue; + foreach (var spawner in randomizedSpawners) + { + if (!spawner.SupportedEnemyTypes.Contains(enemyDescription.Type)) continue; + if (SpawnersInUse.Contains(spawner)) continue; + + var spawnedEnemy = spawner.SpawnEnemy(enemyDescription); + if (spawnedEnemy == null) continue; + + CurrentSpawnedEnemies[spawnedEnemy.GetInstanceId()] = spawnedEnemy; + SpawnersInUse.Add(spawner); + + spawnedEnemy.OnKilled += instanceId=> SpawnedEnemyDied(instanceId, spawner); + + CurrentWave[enemyDescription]--; + break; + } + } + + var remainingEnemiesToSpawn = CurrentWave.Values.Sum(); + if (remainingEnemiesToSpawn <= 0) return; // Wave is fully spawned + GetTree().CreateTimer(1.0f).Timeout += SpawnEnemiesAsAvailable; // Call back the same function later to try and spawn the rest + } + + public void SpawnedEnemyDied(ulong instanceId, Spawner spawner) + { + CurrentSpawnedEnemies.Remove(instanceId); + SpawnersInUse.Remove(spawner); + if (CurrentSpawnedEnemies.Count == 0) FinishWave(); + } + + public void FinishWave() + { + if (CurrentWaveIndex >= CurrentWaveCount) return; // All waves finished + CurrentWaveIndex++; + GetTree().CreateTimer(1.0f).Timeout += StartNextWave; // Start next wave in 1s + } +} \ No newline at end of file diff --git a/managers/Wave/WaveManager.cs.uid b/managers/Wave/WaveManager.cs.uid new file mode 100644 index 00000000..8d894281 --- /dev/null +++ b/managers/Wave/WaveManager.cs.uid @@ -0,0 +1 @@ +uid://cprjrwfk8d0vf diff --git a/managers/Wave/resources/flying_enemy_desc.tres b/managers/Wave/resources/flying_enemy_desc.tres new file mode 100644 index 00000000..adedc484 --- /dev/null +++ b/managers/Wave/resources/flying_enemy_desc.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EnemyDescription" format=3 uid="uid://cfyafss8ncbhh"] + +[ext_resource type="PackedScene" uid="uid://cmlud1hwkd6sv" path="res://scenes/enemies/flying_enemy/flying_enemy.tscn" id="1_yvgr4"] +[ext_resource type="Script" uid="uid://rhdkfi7nuvu1" path="res://managers/Wave/EnemyDescription.cs" id="2_hsb6g"] + +[resource] +script = ExtResource("2_hsb6g") +Scene = ExtResource("1_yvgr4") +metadata/_custom_type_script = "uid://rhdkfi7nuvu1" diff --git a/managers/Wave/resources/grounded_enemy_desc.tres b/managers/Wave/resources/grounded_enemy_desc.tres new file mode 100644 index 00000000..83fe4f09 --- /dev/null +++ b/managers/Wave/resources/grounded_enemy_desc.tres @@ -0,0 +1,9 @@ +[gd_resource type="Resource" script_class="EnemyDescription" format=3 uid="uid://3clksludry8g"] + +[ext_resource type="PackedScene" uid="uid://dxt0e2ugmttqq" path="res://scenes/enemies/grounded_enemy/grounded_enemy.tscn" id="1_wxdbf"] +[ext_resource type="Script" uid="uid://rhdkfi7nuvu1" path="res://managers/Wave/EnemyDescription.cs" id="2_gk6ig"] + +[resource] +script = ExtResource("2_gk6ig") +Scene = ExtResource("1_wxdbf") +metadata/_custom_type_script = "uid://rhdkfi7nuvu1" diff --git a/managers/Wave/resources/one_of_each_wave.tres b/managers/Wave/resources/one_of_each_wave.tres new file mode 100644 index 00000000..dbb34627 --- /dev/null +++ b/managers/Wave/resources/one_of_each_wave.tres @@ -0,0 +1,17 @@ +[gd_resource type="Resource" script_class="SingleWave" format=3 uid="uid://dm71u0ryn0w1o"] + +[ext_resource type="Resource" uid="uid://cfyafss8ncbhh" path="res://managers/Wave/resources/flying_enemy_desc.tres" id="1_bcssi"] +[ext_resource type="Script" uid="uid://rhdkfi7nuvu1" path="res://managers/Wave/EnemyDescription.cs" id="2_3r6ce"] +[ext_resource type="Resource" uid="uid://3clksludry8g" path="res://managers/Wave/resources/grounded_enemy_desc.tres" id="2_mmsra"] +[ext_resource type="Resource" uid="uid://lnturc3ibr5c" path="res://managers/Wave/resources/projectile_enemy_desc.tres" id="3_3r6ce"] +[ext_resource type="Script" uid="uid://cr8wog705ane6" path="res://managers/Wave/SingleWave.cs" id="5_qckro"] + +[resource] +script = ExtResource("5_qckro") +Enemies = Array[Object]([ExtResource("1_bcssi"), ExtResource("2_mmsra"), ExtResource("3_3r6ce")]) +EnemiesToSpawn = Dictionary[ExtResource("2_3r6ce"), int]({ +ExtResource("1_bcssi"): 1, +ExtResource("2_mmsra"): 1, +ExtResource("3_3r6ce"): 1 +}) +metadata/_custom_type_script = "uid://cr8wog705ane6" diff --git a/managers/Wave/resources/projectile_enemy_desc.tres b/managers/Wave/resources/projectile_enemy_desc.tres new file mode 100644 index 00000000..c71932f4 --- /dev/null +++ b/managers/Wave/resources/projectile_enemy_desc.tres @@ -0,0 +1,10 @@ +[gd_resource type="Resource" script_class="EnemyDescription" format=3 uid="uid://lnturc3ibr5c"] + +[ext_resource type="PackedScene" uid="uid://dx3y8sjftqk8f" path="res://scenes/enemies/projectile_enemy/projectile_enemy.tscn" id="1_2nepo"] +[ext_resource type="Script" uid="uid://rhdkfi7nuvu1" path="res://managers/Wave/EnemyDescription.cs" id="2_bn1dh"] + +[resource] +script = ExtResource("2_bn1dh") +Scene = ExtResource("1_2nepo") +Type = 1 +metadata/_custom_type_script = "uid://rhdkfi7nuvu1" diff --git a/maps/_templates/MainSceneTemplate.cs b/maps/_templates/MainSceneTemplate.cs index 7f7f8ab6..5e6b935b 100644 --- a/maps/_templates/MainSceneTemplate.cs +++ b/maps/_templates/MainSceneTemplate.cs @@ -13,7 +13,12 @@ using Movementtests.systems; typeof(IAutoConnect), typeof(IProvider) )] -public partial class MainSceneTemplate : Node3D, IProvide, IProvide, IProvide, IProvide +public partial class MainSceneTemplate : Node3D, + IProvide, + IProvide, + IProvide, + IProvide, + IProvide { public override void _Notification(int what) => this.Notify(what); @@ -32,6 +37,7 @@ public partial class MainSceneTemplate : Node3D, IProvide, IPr [Export] public WeaponInventory? InitialWeaponInventory { get; set; } [Export] public int MaxNumberOfProjectiles { get; set; } = 3; + [Export] public WaveContent? WaveContent { get; set; } #endregion @@ -44,6 +50,9 @@ public partial class MainSceneTemplate : Node3D, IProvide, IPr public required TokenManager TokenManager { get; set; } TokenManager IProvide.Value() => TokenManager; + public required WaveManager WaveManager { get; set; } + WaveManager IProvide.Value() => WaveManager; + public void OnReady() { PlayerFellPlane.BodyEntered += StartResetPlayerAnimation; @@ -57,10 +66,21 @@ public partial class MainSceneTemplate : Node3D, IProvide, IPr TokenManager = new TokenManager(); TokenManager.Initialize(MaxNumberOfProjectiles); AddChild(TokenManager); + + WaveManager = new WaveManager(); + if (WaveContent != null) + WaveManager.InitializeFromResource(WaveContent); + AddChild(WaveManager); this.Provide(); } + public void OnProvided() + { + if (WaveContent != null) + GetTree().CreateTimer(3).Timeout += WaveManager.StartWaves; + } + public void ResetPlayerPosition() { if (Respawnabble == null || PlayerRespawnMarker == null) throw new Exception("Player or respawn marker is null"); diff --git a/maps/levels/4 - tuto_waves.tscn b/maps/levels/4 - tuto_waves.tscn index 65e5539d..89158e85 100644 --- a/maps/levels/4 - tuto_waves.tscn +++ b/maps/levels/4 - tuto_waves.tscn @@ -1,10 +1,34 @@ [gd_scene format=3 uid="uid://b2g2gys4dopmn"] [ext_resource type="PackedScene" uid="uid://55wehh6xombr" path="res://maps/_templates/main_scene_template.tscn" id="1_pxwoj"] +[ext_resource type="Resource" uid="uid://dm71u0ryn0w1o" path="res://managers/Wave/resources/one_of_each_wave.tres" id="2_a6jwd"] [ext_resource type="PackedScene" uid="uid://y77cdg7gg3y7" path="res://maps/levels/_arenas/playtest_1.tscn" id="2_apgv3"] [ext_resource type="Material" uid="uid://31aulub2nqov" path="res://assets/materials/greybox/m_greybox.tres" id="3_452yy"] +[ext_resource type="Script" uid="uid://rhdkfi7nuvu1" path="res://managers/Wave/EnemyDescription.cs" id="3_a6jwd"] +[ext_resource type="Resource" uid="uid://cfyafss8ncbhh" path="res://managers/Wave/resources/flying_enemy_desc.tres" id="3_re07p"] +[ext_resource type="PackedScene" uid="uid://c305mfrtumcyq" path="res://scenes/spawners/spawner.tscn" id="4_6q0yp"] +[ext_resource type="Resource" uid="uid://3clksludry8g" path="res://managers/Wave/resources/grounded_enemy_desc.tres" id="4_7ijxg"] +[ext_resource type="Script" uid="uid://cr8wog705ane6" path="res://managers/Wave/SingleWave.cs" id="5_7ijxg"] +[ext_resource type="Resource" uid="uid://lnturc3ibr5c" path="res://managers/Wave/resources/projectile_enemy_desc.tres" id="5_hm1dp"] +[ext_resource type="Script" uid="uid://dijmv0wqc1xuv" path="res://managers/Wave/WaveContent.cs" id="6_hm1dp"] + +[sub_resource type="Resource" id="Resource_wllel"] +script = ExtResource("5_7ijxg") +EnemiesToSpawn = Dictionary[ExtResource("3_a6jwd"), int]({ +ExtResource("4_7ijxg"): 4, +ExtResource("5_hm1dp"): 4, +ExtResource("3_re07p"): 4 +}) +metadata/_custom_type_script = "uid://cr8wog705ane6" + +[sub_resource type="Resource" id="Resource_e88eg"] +script = ExtResource("6_hm1dp") +Waves = Array[Object]([SubResource("Resource_wllel"), ExtResource("2_a6jwd")]) +metadata/_custom_type_script = "uid://dijmv0wqc1xuv" [node name="Main" unique_id=955321579 instance=ExtResource("1_pxwoj")] +MaxNumberOfProjectiles = 2 +WaveContent = SubResource("Resource_e88eg") [node name="PlaytestArena" parent="." index="12" unique_id=664535670 instance=ExtResource("2_apgv3")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -16, 0, 9.5) @@ -14,3 +38,33 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.5, 8.75, 2.25) use_collision = true size = Vector3(1, 17.5, 9.5) material = ExtResource("3_452yy") + +[node name="Spawner" parent="." index="13" unique_id=580981173 node_paths=PackedStringArray("Target") instance=ExtResource("4_6q0yp")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -12, 1, -4) +SupportedEnemyTypes = Array[int]([0]) +Target = NodePath("../Player") +IsActiveOnStart = false + +[node name="Spawner2" parent="." index="14" unique_id=1982641431 node_paths=PackedStringArray("Target") instance=ExtResource("4_6q0yp")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -8.5, 1, -4) +SupportedEnemyTypes = Array[int]([0]) +Target = NodePath("../Player") +IsActiveOnStart = false + +[node name="Spawner3" parent="." index="15" unique_id=1743153579 node_paths=PackedStringArray("Target") instance=ExtResource("4_6q0yp")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.5, 1, -4) +SupportedEnemyTypes = Array[int]([1]) +Target = NodePath("../Player") +IsActiveOnStart = false + +[node name="Spawner4" parent="." index="16" unique_id=536869732 node_paths=PackedStringArray("Target") instance=ExtResource("4_6q0yp")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8, 1, -4) +SupportedEnemyTypes = Array[int]([1, 0]) +Target = NodePath("../Player") +IsActiveOnStart = false + +[node name="Spawner5" parent="." index="17" unique_id=275129467 node_paths=PackedStringArray("Target") instance=ExtResource("4_6q0yp")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13, 1, -4) +SupportedEnemyTypes = Array[int]([1, 0]) +Target = NodePath("../Player") +IsActiveOnStart = false diff --git a/scenes/enemies/Enemy.cs b/scenes/enemies/Enemy.cs index a349ba18..ca43b543 100644 --- a/scenes/enemies/Enemy.cs +++ b/scenes/enemies/Enemy.cs @@ -42,6 +42,12 @@ public partial class Enemy : CharacterBody3D, [Dependency] public TokenManager TokenManager => this.DependOn(); + #endregion + + #region Signals + + [Signal] public delegate void OnKilledEventHandler(ulong instanceId); + #endregion #region Inspector @@ -188,12 +194,7 @@ public partial class Enemy : CharacterBody3D, public void ProcessGameplay(double delta) { - if (IsStunned()) - { - GD.Print("Cannot attack, stunned!"); - return; - } - // if (_hitAbilityHandle == null) return; + if (IsStunned()) return; var bodies = DamageBox.GetOverlappingBodies(); foreach (var body in bodies) @@ -253,6 +254,8 @@ public partial class Enemy : CharacterBody3D, { killable.Kill(); } + + EmitSignalOnKilled(GetInstanceId()); CallDeferred(Node.MethodName.QueueFree); } diff --git a/scenes/explosion/Explosion.cs b/scenes/explosion/Explosion.cs index 05d303f9..8c729d9b 100644 --- a/scenes/explosion/Explosion.cs +++ b/scenes/explosion/Explosion.cs @@ -50,7 +50,8 @@ public partial class Explosion : Area3D, IProvide foreach (var body in bodies) { if (body is not IForgeEntity target) continue; - foreach (var ability in ForgeEntityNode.Abilities.GrantedAbilities.Where(ability => ability.CanActivate(out _, target))) + foreach (var ability in ForgeEntityNode.Abilities.GrantedAbilities.Where(ability => + ability.CanActivate(out _, target))) ability.Activate(out _, target, Damage); } QueueFree(); diff --git a/scenes/explosion/explosion.tscn b/scenes/explosion/explosion.tscn index 43893466..d15796f7 100644 --- a/scenes/explosion/explosion.tscn +++ b/scenes/explosion/explosion.tscn @@ -1,6 +1,7 @@ [gd_scene format=3 uid="uid://duju3atqgltkg"] [ext_resource type="Script" uid="uid://cnlu64l7oxvv3" path="res://scenes/explosion/Explosion.cs" id="1_82hkh"] +[ext_resource type="Script" uid="uid://cw525n4mjqgw0" path="res://addons/forge/resources/ForgeTagContainer.cs" id="3_1ve7p"] [ext_resource type="Script" uid="uid://rpcbb54q4atx" path="res://forge/ForgeEntityNode.cs" id="3_wikc1"] [ext_resource type="Script" uid="uid://dps0oef50noil" path="res://addons/forge/nodes/ForgeEffect.cs" id="4_f5lqq"] [ext_resource type="Resource" uid="uid://nns16d5uhtl8" path="res://forge/resources/ability_datas/explosion_hit.tres" id="5_nphml"] @@ -21,6 +22,11 @@ transparency = 1 cull_mode = 2 albedo_color = Color(0.9607843, 0.27058825, 0, 0.7176471) +[sub_resource type="Resource" id="Resource_nqbbv"] +script = ExtResource("3_1ve7p") +ContainerTags = [] +metadata/_custom_type_script = "uid://cw525n4mjqgw0" + [sub_resource type="Resource" id="Resource_5c2oj"] script = ExtResource("5_nqbbv") BaseValue = 1 @@ -71,6 +77,7 @@ surface_material_override/0 = SubResource("StandardMaterial3D_hys74") [node name="ForgeEntityNode" type="Node3D" parent="." unique_id=806020391] script = ExtResource("3_wikc1") +BaseTags = SubResource("Resource_nqbbv") metadata/_custom_type_script = "uid://rpcbb54q4atx" [node name="ForgeEffect" type="Node" parent="ForgeEntityNode" unique_id=2068515708] diff --git a/scenes/spawners/Spawner.cs b/scenes/spawners/Spawner.cs index 073730ce..a4005685 100644 --- a/scenes/spawners/Spawner.cs +++ b/scenes/spawners/Spawner.cs @@ -1,11 +1,20 @@ using Godot; using System; +using Chickensoft.AutoInject; +using Chickensoft.Introspection; +using Movementtests.managers; -[GlobalClass, Icon("res://assets/ui/IconGodotNode/node_3D/icon_duplicate.png")] +[GlobalClass, Icon("res://assets/ui/IconGodotNode/node_3D/icon_duplicate.png"), Meta(typeof(IAutoNode))] public partial class Spawner : Node3D { - [Export(PropertyHint.NodeType)] + public override void _Notification(int what) => this.Notify(what); + + [Dependency] public WaveManager WaveManager => this.DependOn(); + + [Export(PropertyHint.NodeType)] public PackedScene? EnemyToSpawn { get; set; } + + [Export] public Godot.Collections.Array SupportedEnemyTypes { get; set; } = []; [Export] public RMovement? MovementInputs { get; set; } @@ -18,37 +27,56 @@ public partial class Spawner : Node3D [Export] public bool IsActiveOnStart { get; set; } = true; - private Timer _timer; + [Node("Timer")] private Timer SpawnTimer { get; set; } - public override void _Ready() + public void OnReady() { - _timer = GetNode("Timer"); - _timer.WaitTime = SpawnInterval; - _timer.Timeout += Spawn; + SpawnTimer.WaitTime = SpawnInterval; + SpawnTimer.Timeout += Spawn; if (IsActiveOnStart) StartSpawning(); } + public void OnResolved() + { + WaveManager.RegisterSpawner(this); + } + public void Spawn() { if (EnemyToSpawn == null || !EnemyToSpawn.CanInstantiate()) return; if (EnemyToSpawn.Instantiate() is not Enemy spawnedInstance) return; - spawnedInstance.RequestReady(); + spawnedInstance.Target = Target; - spawnedInstance.RMovement = MovementInputs; + if (MovementInputs != null) + spawnedInstance.RMovement = MovementInputs; spawnedInstance.Init(); GetTree().GetCurrentScene().AddChild(spawnedInstance); spawnedInstance.GlobalPosition = GlobalPosition; } + + public Enemy? SpawnEnemy(EnemyDescription description) + { + if (description.Scene.Instantiate() is not Enemy spawnedInstance) return null; + + spawnedInstance.Target = Target; + if (description.MovementOverride is not null) + spawnedInstance.RMovement = description.MovementOverride; + spawnedInstance.Init(); + + GetTree().GetCurrentScene().AddChild(spawnedInstance); + spawnedInstance.GlobalPosition = GlobalPosition; + return spawnedInstance; + } public void StartSpawning() { - _timer.Start(); + SpawnTimer.Start(); } public void StopSpawning() { - _timer.Stop(); + SpawnTimer.Stop(); } } diff --git a/tools/IterableUtils.cs b/tools/IterableUtils.cs new file mode 100644 index 00000000..9e1efcea --- /dev/null +++ b/tools/IterableUtils.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace Movementtests.tools; + +public static class IterableUtils +{ + private static readonly Random Rng = new Random(); + + public static void Shuffle(this IList list) + { + var n = list.Count; + while (n > 1) { + n--; + var k = Rng.Next(n + 1); + (list[k], list[n]) = (list[n], list[k]); + } + } +} \ No newline at end of file diff --git a/tools/IterableUtils.cs.uid b/tools/IterableUtils.cs.uid new file mode 100644 index 00000000..16280831 --- /dev/null +++ b/tools/IterableUtils.cs.uid @@ -0,0 +1 @@ +uid://bn1ps81lsbmk0