diff --git a/Cirno.sln.DotSettings.user b/Cirno.sln.DotSettings.user index 99e56389..30725965 100644 --- a/Cirno.sln.DotSettings.user +++ b/Cirno.sln.DotSettings.user @@ -1,2 +1,5 @@  - ForceIncluded \ No newline at end of file + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded \ No newline at end of file diff --git a/Scenes/Actors/Enemy_Base.tscn b/Scenes/Actors/Enemy_Base.tscn new file mode 100644 index 00000000..3c7df8af --- /dev/null +++ b/Scenes/Actors/Enemy_Base.tscn @@ -0,0 +1,84 @@ +[gd_scene load_steps=11 format=3 uid="uid://b2gnndqesf4kb"] + +[ext_resource type="Script" path="res://Scripts/Enemy.cs" id="1_h0ey4"] +[ext_resource type="Texture2D" uid="uid://b4ynnb14mb4uq" path="res://Sprites/Reisen.png" id="2_ocghb"] +[ext_resource type="PackedScene" uid="uid://crry0rgk7a8sm" path="res://Scenes/Weapons/BaseWeapon.tscn" id="3_ihce0"] +[ext_resource type="Script" path="res://Scripts/Components/ProximityPlayerDetection.cs" id="3_udny0"] +[ext_resource type="PackedScene" uid="uid://cuixq5ex0j40h" path="res://Scenes/enemyBullet.tscn" id="4_iis8b"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_2brqc"] +atlas = ExtResource("2_ocghb") +region = Rect2(0, 0, 8, 16) + +[sub_resource type="CircleShape2D" id="CircleShape2D_8gtts"] +radius = 7.0 + +[sub_resource type="CircleShape2D" id="CircleShape2D_cacb5"] +radius = 4.0 + +[sub_resource type="CircleShape2D" id="CircleShape2D_v711r"] +radius = 85.0529 + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_m1rsg"] +size = Vector2(8, 12) + +[node name="Enemy" type="CharacterBody2D" node_paths=PackedStringArray("EquippedWeapon") groups=["Destroyable"]] +collision_layer = 16 +collision_mask = 9 +script = ExtResource("1_h0ey4") +EquippedWeapon = NodePath("Weapon") +NavigationEnabled = true +metadata/_edit_group_ = true + +[node name="Sprite2D" type="Sprite2D" parent="."] +texture = SubResource("AtlasTexture_2brqc") + +[node name="Damage_HitBox" type="CollisionShape2D" parent="."] +visible = false +shape = SubResource("CircleShape2D_8gtts") + +[node name="RigidBody2D" type="RigidBody2D" parent="."] +collision_layer = 16 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="RigidBody2D"] +visible = false +position = Vector2(0, 5) +shape = SubResource("CircleShape2D_cacb5") + +[node name="PlayerDetection" type="Area2D" parent="."] +visible = false +collision_layer = 16 +collision_mask = 2 +script = ExtResource("3_udny0") + +[node name="PlayerDetectionArea" type="CollisionShape2D" parent="PlayerDetection"] +shape = SubResource("CircleShape2D_v711r") + +[node name="ShootTimer" type="Timer" parent="."] +wait_time = 0.4 +one_shot = true + +[node name="Weapon" parent="." instance=ExtResource("3_ihce0")] +BulletScene = ExtResource("4_iis8b") +BulletCapacity = 4 +BulletSpeed = 50.0 + +[node name="NavigationAgent2D" type="NavigationAgent2D" parent="."] +target_desired_distance = 64.0 +path_max_distance = 800.0 +path_postprocessing = 1 +avoidance_enabled = true +debug_enabled = true +debug_path_custom_color = Color(1, 0, 0, 1) + +[node name="DamageHitbox" type="Area2D" parent="."] +collision_layer = 16 +collision_mask = 9 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="DamageHitbox"] +shape = SubResource("RectangleShape2D_m1rsg") + +[connection signal="area_entered" from="PlayerDetection" to="PlayerDetection" method="_on_area_entered"] +[connection signal="area_exited" from="PlayerDetection" to="." method="_on_area_exited"] +[connection signal="velocity_computed" from="NavigationAgent2D" to="." method="_on_navigation_agent_2d_velocity_computed"] +[connection signal="area_entered" from="DamageHitbox" to="." method="_on_damage_hitbox_area_entered"] diff --git a/Scenes/Enemy.tscn b/Scenes/Enemy.tscn index 2bf0e438..d51de564 100644 --- a/Scenes/Enemy.tscn +++ b/Scenes/Enemy.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=10 format=3 uid="uid://v8s3kubgb2qg"] +[gd_scene load_steps=11 format=3 uid="uid://v8s3kubgb2qg"] [ext_resource type="Texture2D" uid="uid://b4ynnb14mb4uq" path="res://Sprites/Reisen.png" id="1_4w8mj"] [ext_resource type="Script" path="res://Scripts/Enemy.cs" id="1_lpwdj"] [ext_resource type="PackedScene" uid="uid://cuixq5ex0j40h" path="res://Scenes/enemyBullet.tscn" id="2_ogldd"] +[ext_resource type="Script" path="res://Scripts/Components/ProximityPlayerDetection.cs" id="3_1nqn5"] [ext_resource type="PackedScene" uid="uid://crry0rgk7a8sm" path="res://Scenes/Weapons/BaseWeapon.tscn" id="4_2k1dv"] [sub_resource type="AtlasTexture" id="AtlasTexture_2brqc"] @@ -21,11 +22,12 @@ radius = 85.0529 [sub_resource type="RectangleShape2D" id="RectangleShape2D_m1rsg"] size = Vector2(8, 12) -[node name="Enemy" type="CharacterBody2D" node_paths=PackedStringArray("EquippedWeapon") groups=["Destroyable"]] +[node name="Enemy" type="CharacterBody2D" node_paths=PackedStringArray("EquippedWeapon", "_playerDetection") groups=["Destroyable"]] collision_layer = 16 collision_mask = 9 script = ExtResource("1_lpwdj") EquippedWeapon = NodePath("Weapon") +_playerDetection = NodePath("PlayerDetection") NavigationEnabled = true metadata/_edit_group_ = true @@ -48,6 +50,7 @@ shape = SubResource("CircleShape2D_cacb5") visible = false collision_layer = 16 collision_mask = 2 +script = ExtResource("3_1nqn5") [node name="PlayerDetectionArea" type="CollisionShape2D" parent="PlayerDetection"] shape = SubResource("CircleShape2D_v711r") @@ -76,7 +79,7 @@ collision_mask = 9 [node name="CollisionShape2D" type="CollisionShape2D" parent="DamageHitbox"] shape = SubResource("RectangleShape2D_m1rsg") -[connection signal="area_entered" from="PlayerDetection" to="." method="_on_player_detection_area_entered"] -[connection signal="area_exited" from="PlayerDetection" to="." method="_on_player_detection_area_exited"] +[connection signal="area_entered" from="PlayerDetection" to="PlayerDetection" method="_on_area_entered"] +[connection signal="area_exited" from="PlayerDetection" to="PlayerDetection" method="_on_area_exited"] [connection signal="velocity_computed" from="NavigationAgent2D" to="." method="_on_navigation_agent_2d_velocity_computed"] [connection signal="area_entered" from="DamageHitbox" to="." method="_on_damage_hitbox_area_entered"] diff --git a/Scenes/HUD/HUD.tscn b/Scenes/HUD/HUD.tscn index dd7e4d00..5e6cb120 100644 --- a/Scenes/HUD/HUD.tscn +++ b/Scenes/HUD/HUD.tscn @@ -26,8 +26,10 @@ animations = [{ "speed": 5.0 }] -[node name="HUD" type="CanvasLayer"] +[node name="HUD" type="CanvasLayer" node_paths=PackedStringArray("_healthLabel", "_itemsContainer")] script = ExtResource("1_m0hb0") +_healthLabel = NodePath("VBoxContainer/HealthLabel") +_itemsContainer = NodePath("VBoxContainer/ItemsContainer") [node name="GameOver" type="Label" parent="."] visible = false diff --git a/Scenes/player.tscn b/Scenes/player.tscn index 78c2f6dc..b8973852 100644 --- a/Scenes/player.tscn +++ b/Scenes/player.tscn @@ -185,6 +185,7 @@ animation = &"walk_left" z_index = 100 [node name="HitboxSprite" type="Sprite2D" parent="Smoothing2D"] +visible = false texture = ExtResource("7_msn8i") [node name="InteractionController" type="Area2D" parent="."] diff --git a/Scripts/Components/PlayerDetection.cs b/Scripts/Components/PlayerDetection.cs new file mode 100644 index 00000000..912f1c22 --- /dev/null +++ b/Scripts/Components/PlayerDetection.cs @@ -0,0 +1,17 @@ +using Godot; +using Godot.Collections; + +namespace Cirno.Scripts.Components; + +public partial class PlayerDetection : Area2D +{ + public InteractionController CachedPlayer => _cachedPlayer; + + protected InteractionController _cachedPlayer; + public virtual bool IsPlayerInRange { get; set; } + + public virtual bool IsPlayerInSight(uint collisionMask) + { + return false; + } +} \ No newline at end of file diff --git a/Scripts/Components/ProximityPlayerDetection.cs b/Scripts/Components/ProximityPlayerDetection.cs new file mode 100644 index 00000000..1958daea --- /dev/null +++ b/Scripts/Components/ProximityPlayerDetection.cs @@ -0,0 +1,45 @@ +using System.Diagnostics; +using Godot; +using Godot.Collections; + +namespace Cirno.Scripts.Components; + +public partial class ProximityPlayerDetection : PlayerDetection +{ + public override bool IsPlayerInRange { get; set; } + + public override bool IsPlayerInSight(uint collisionMask) + { + if (_cachedPlayer == null) return false; + + var spaceState = GetWorld2D().DirectSpaceState; + + // I have no idea why I need to use the parent's collision mask instead of the local one but it doesn't detect the player otherwise + + var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, _cachedPlayer.GlobalPosition, collisionMask, new Array { GetRid() }); + + // var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, _cachedPlayer.GlobalPosition, CollisionMask, new Array { GetRid() }); + var result = spaceState.IntersectRay(query); + + // If count is 0 then the player is in sight, otherwise there is level geometry in the way + return result.Count == 0; + } + + private void _on_area_entered(Area2D area) + { + // Assume area is player for now + if (area is not InteractionController player) return; + + Debug.WriteLine("Enemy detection area Entered by interaction controller"); + + _cachedPlayer = player; + + IsPlayerInRange = true; + } + + private void _on_area_exited(Area2D area) + { + if (area is not InteractionController player) return; + IsPlayerInRange = false; + } +} \ No newline at end of file diff --git a/Scripts/Enemy.cs b/Scripts/Enemy.cs index 1ccf6c68..9c9aed05 100644 --- a/Scripts/Enemy.cs +++ b/Scripts/Enemy.cs @@ -2,6 +2,8 @@ using Cirno.Scripts; using Godot; using System; using System.Diagnostics; +using Cirno.Scripts.Components; +using Godot.Collections; public partial class Enemy : CharacterBody2D { @@ -21,10 +23,13 @@ public partial class Enemy : CharacterBody2D private bool _isDestroyed = false; private NavigationAgent2D _navigationAgent; - - private bool _isPlayerInRange = false; - + + private bool IsPlayerInRange => _playerDetection is { IsPlayerInRange: true }; + private Vector2? _lastPlayerPosition = null; + + [Export] + private PlayerDetection _playerDetection; [Export] public bool NavigationEnabled { get; set; } = false; @@ -35,6 +40,10 @@ public partial class Enemy : CharacterBody2D _navigationAgent = GetNode("NavigationAgent2D"); + //var asdf = GetNode("PlayerDetection"); + + //_playerDetection = GetNode("PlayerDetection"); + //CallDeferred("Setup"); } @@ -67,15 +76,19 @@ public partial class Enemy : CharacterBody2D switch (_currentState) { case EnemyState.Idle: - HandlePlayerDetection(); + if (_playerDetection != null && _playerDetection.IsPlayerInSight(CollisionMask)) + { + _currentState = EnemyState.Alert; + } + //HandlePlayerDetection(); break; case EnemyState.Alert: // Update last known player position if it's in range - if (_isPlayerInRange) + if (IsPlayerInRange) { - _lastPlayerPosition = _cachedPlayer.GlobalPosition; + _lastPlayerPosition = _playerDetection.CachedPlayer.GlobalPosition; } if (_lastPlayerPosition.HasValue) @@ -127,25 +140,25 @@ public partial class Enemy : CharacterBody2D } - private void HandlePlayerDetection() - { - if (_cachedPlayer == null) - { - return; - } - - if (IsPlayerInSight()) - { - // Update player position only if player is in sight - // if (NavigationEnabled) - // { - // _navigationAgent.SetTargetPosition(_cachedPlayer.GlobalPosition); - // } - //Shoot(); - - _currentState = EnemyState.Alert; - } - } + // private void HandlePlayerDetection() + // { + // if (_cachedPlayer == null) + // { + // return; + // } + // + // if (IsPlayerInSight()) + // { + // // Update player position only if player is in sight + // // if (NavigationEnabled) + // // { + // // _navigationAgent.SetTargetPosition(_cachedPlayer.GlobalPosition); + // // } + // //Shoot(); + // + // _currentState = EnemyState.Alert; + // } + // } private void Shoot() { @@ -178,45 +191,45 @@ public partial class Enemy : CharacterBody2D // } } - private bool IsPlayerInSight() - { - var spaceState = GetWorld2D().DirectSpaceState; - var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, _cachedPlayer.GlobalPosition, CollisionMask, new Godot.Collections.Array { GetRid() }); - var result = spaceState.IntersectRay(query); + // private bool IsPlayerInSight() + // { + // var spaceState = GetWorld2D().DirectSpaceState; + // var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, _cachedPlayer.GlobalPosition, CollisionMask, new Godot.Collections.Array { GetRid() }); + // var result = spaceState.IntersectRay(query); + // + // // If count is 0 then the player is in sight, otherwise there is level geometry in the way + // return result.Count == 0; + // + // // if (result.Count > 0) + // // GD.Print("Hit at point: ", result["position"]); + // } - // If count is 0 then the player is in sight, otherwise there is level geometry in the way - return result.Count == 0; - - // if (result.Count > 0) - // GD.Print("Hit at point: ", result["position"]); - } - - private void _on_player_detection_area_entered(Area2D area) - { - // Assume area is player for now - if (area is InteractionController player) - { - Debug.WriteLine("Enemy detection area Entered by interaction controller"); - - _cachedPlayer = player; - - _isPlayerInRange = true; - // if (_currentState is EnemyState.Idle) - // { - // _currentState = EnemyState.Primed; - // } - } - } - - private void _on_player_detection_area_exited(Area2D area) - { - _isPlayerInRange = false; - - // if (_currentState is EnemyState.Primed) - // { - // _currentState = EnemyState.Idle; - // } - } + // private void _on_player_detection_area_entered(Area2D area) + // { + // // Assume area is player for now + // if (area is InteractionController player) + // { + // Debug.WriteLine("Enemy detection area Entered by interaction controller"); + // + // _cachedPlayer = player; + // + // _isPlayerInRange = true; + // // if (_currentState is EnemyState.Idle) + // // { + // // _currentState = EnemyState.Primed; + // // } + // } + // } + // + // private void _on_player_detection_area_exited(Area2D area) + // { + // _isPlayerInRange = false; + // + // // if (_currentState is EnemyState.Primed) + // // { + // // _currentState = EnemyState.Idle; + // // } + // } private void _on_damage_hitbox_area_entered(Area2D area) {