diff --git a/Cirno.csproj b/Cirno.csproj index 3ccbb866..bdc02c87 100644 --- a/Cirno.csproj +++ b/Cirno.csproj @@ -14,7 +14,4 @@ - - - \ No newline at end of file diff --git a/Dialogue/Timelines/Mission1_Briefing.dtl b/Dialogue/Timelines/Mission1_Briefing.dtl index acbe58cd..7cd19296 100644 --- a/Dialogue/Timelines/Mission1_Briefing.dtl +++ b/Dialogue/Timelines/Mission1_Briefing.dtl @@ -10,15 +10,15 @@ Colonel: You were found by one of our teams in the middle of a recovery mission Colonel: It is unlikely we'll be able to have access to it in the short term for any experimentation. This means you are, for the moment, stranded in our world. Colonel: But most importantly of all, I'm glad you decided to help our cause, our objectives coincide and collaboration will be instrumental in reaching them. Colonel: I know you are eager to prove yourself, and we have the perfect chance to show what you're worth. -Colonel: Because of your magical ability to regenerate from death we have assigned one mission that would be too dangerous for our human operatives. +Colonel: Because of your magical ability to regenerate from death we have assigned you one mission that would be too dangerous for our human operatives. update Colonel left [move_time="0.4"] [image scene="res://Dialogue/CustomScenes/dialogic_windowed_image.tscn" arg="res://Sprites/Briefing/RefinerySmall.png" fade="0.0"] -Colonel: Our spies have reported to us that this refinery has been emitting unusual amounts of pollutants, even more powerful than what they usually emit. +Colonel: Our spies have reported to us that this refinery has been emitting unusual amounts of pollutants, even more powerful than what they usually emit. Colonel: This leads us to believe that they are making use of an artifact of extradimentional origin. [image scene="res://Dialogue/CustomScenes/dialogic_windowed_image.tscn" arg="res://Sprites/Briefing/WireframeYinYang.png" fade="0.0"] Colonel: We believe the enemy to have one of these misterious artifacts in their possession and are using it to enhance their fuel production. Colonel: Your mission is to retrieve this artifact, it will cripple enemy operations and allow us to study it's magical effects. -Colonel: And perhaps there will be a chance to use it to give you new abilities that will make you more effective in the field. +Colonel: And perhaps there will be a chance to use it to give you new abilities that will make you more effective in the field. [image scene="res://Dialogue/CustomScenes/dialogic_windowed_image.tscn" arg="res://Sprites/Expression/ICE/ICE00.png" fade="0.0"] Colonel: You will be assigned an AI companion who will help you in your mission, you will get acquainted during VR training. [image fade="0.0"] diff --git a/Scenes/Activable/FloorEmitter.tscn b/Scenes/Activable/FloorEmitter.tscn index a7932f1d..2e5d4e2a 100644 --- a/Scenes/Activable/FloorEmitter.tscn +++ b/Scenes/Activable/FloorEmitter.tscn @@ -37,7 +37,6 @@ shape = SubResource("CircleShape2D_4awvh") [node name="Health" type="Node2D" parent="DamageReceiver"] script = ExtResource("6_6dpsy") ResourceName = "Health" -MaxResource = 4.0 [connection signal="StateChanged" from="." to="Sprite2D" method="ChangeState"] [connection signal="StateChanged" from="." to="DamageReceiver" method="ChangeState"] diff --git a/Scenes/Actors/FairyGuard_New.tscn b/Scenes/Actors/FairyGuard_New.tscn index 8825560b..a8016c0e 100644 --- a/Scenes/Actors/FairyGuard_New.tscn +++ b/Scenes/Actors/FairyGuard_New.tscn @@ -129,7 +129,6 @@ EquippedWeapon = NodePath("../EnemyWeapon") _playerDetection = NodePath("../PlayerDetection") [node name="PlayerDetection" type="Area2D" parent="."] -visible = false collision_layer = 0 collision_mask = 2 script = ExtResource("8_m5ma3") diff --git a/Scenes/Actors/Fairy_FSM.tscn b/Scenes/Actors/Fairy_FSM.tscn new file mode 100644 index 00000000..0572e5d6 --- /dev/null +++ b/Scenes/Actors/Fairy_FSM.tscn @@ -0,0 +1,87 @@ +[gd_scene load_steps=16 format=3 uid="uid://clieeuln36a7a"] + +[ext_resource type="Script" uid="uid://dn6dbog1s2818" path="res://Scripts/Components/FSM/Enemy/EnemyStateMachine.cs" id="1_27djw"] +[ext_resource type="SpriteFrames" uid="uid://bcc5mlwwnkvri" path="res://Resources/Sprites/Fairy.tres" id="1_ho0th"] +[ext_resource type="Script" uid="uid://bi2edpdosngll" path="res://Scripts/Components/FSM/Enemy/EnemyFSMProxy.cs" id="1_jgko8"] +[ext_resource type="Resource" uid="uid://cocl3qontm3be" path="res://Resources/Enemies/Base_Fairy.tres" id="2_rkav6"] +[ext_resource type="Script" uid="uid://rrelumir3g6n" path="res://Scripts/Components/FSM/Enemy/Init.cs" id="4_f112g"] +[ext_resource type="Script" uid="uid://bjrh5q24nuoec" path="res://Scripts/Components/FSM/Enemy/Idle.cs" id="4_kjmts"] +[ext_resource type="Script" uid="uid://dbmc3klko5x18" path="res://Scripts/Components/FSM/Enemy/Alert.cs" id="5_f112g"] +[ext_resource type="Script" uid="uid://mb4ugq74a17c" path="res://Scripts/Components/FSM/Enemy/PlayerDetectionModule.cs" id="5_rkav6"] +[ext_resource type="Script" uid="uid://bflvr26h52c55" path="res://Scripts/Components/FSM/Enemy/EnemyStorageModule.cs" id="8_fu65u"] +[ext_resource type="Script" uid="uid://cq3hkweplldbr" path="res://Scripts/Components/Actors/GenericDamageReceiver.cs" id="10_l7aey"] +[ext_resource type="PackedScene" uid="uid://dmumxecckh42r" path="res://Scenes/Activable/BrokenFloorEmitter.tscn" id="11_br0mr"] +[ext_resource type="Script" uid="uid://cqwvssstkrdmw" path="res://Scripts/Components/Actors/ActorResourceProvider.cs" id="12_w08b8"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_pnkma"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_5wstg"] +radius = 29.0 + +[sub_resource type="CircleShape2D" id="CircleShape2D_6x22m"] +radius = 5.0 + +[node name="FairyFsm" type="CharacterBody2D" node_paths=PackedStringArray("EnemyFSM")] +collision_layer = 16 +collision_mask = 33 +script = ExtResource("1_jgko8") +EnemyFSM = NodePath("StateMachine") +EnemyResource = ExtResource("2_rkav6") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("CircleShape2D_pnkma") + +[node name="StateMachine" type="Node2D" parent="."] +script = ExtResource("1_27djw") + +[node name="Init" type="Node2D" parent="StateMachine" node_paths=PackedStringArray("DamageReceiver", "StorageModule")] +script = ExtResource("4_f112g") +DamageReceiver = NodePath("../../DamageReceiver") +StorageModule = NodePath("../../Storage") + +[node name="Idle" type="Node2D" parent="StateMachine" node_paths=PackedStringArray("StorageModule", "PlayerDetection", "DamageReceiver")] +script = ExtResource("4_kjmts") +StorageModule = NodePath("../../Storage") +PlayerDetection = NodePath("../../PlayerDetection") +DamageReceiver = NodePath("../../DamageReceiver") + +[node name="Alert" type="Node2D" parent="StateMachine"] +script = ExtResource("5_f112g") + +[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."] +sprite_frames = ExtResource("1_ho0th") +animation = &"down" + +[node name="PlayerDetection" type="Area2D" parent="."] +collision_layer = 0 +collision_mask = 2 +script = ExtResource("5_rkav6") +ObstaclesCollisionMask = 81 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="PlayerDetection"] +shape = SubResource("CircleShape2D_5wstg") + +[node name="Storage" type="Node2D" parent="." node_paths=PackedStringArray("Root")] +script = ExtResource("8_fu65u") +Root = NodePath("..") + +[node name="DamageReceiver" type="Area2D" parent="." node_paths=PackedStringArray("HealthProvider")] +collision_layer = 64 +collision_mask = 136 +script = ExtResource("10_l7aey") +HealthProvider = NodePath("Health") +BulletGroup = 2 +Debris = ExtResource("11_br0mr") +DeleteParentOnDeath = false + +[node name="CollisionShape2D" type="CollisionShape2D" parent="DamageReceiver"] +position = Vector2(0, 3.865) +shape = SubResource("CircleShape2D_6x22m") + +[node name="Health" type="Node2D" parent="DamageReceiver"] +script = ExtResource("12_w08b8") +ResourceName = "Health" + +[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="area_entered" from="DamageReceiver" to="DamageReceiver" method="_on_damage_hitbox_area_entered"] diff --git a/Scenes/Actors/Fairy_New.tscn b/Scenes/Actors/Fairy_New.tscn index df5bd0c5..87d668c4 100644 --- a/Scenes/Actors/Fairy_New.tscn +++ b/Scenes/Actors/Fairy_New.tscn @@ -1,10 +1,8 @@ -[gd_scene load_steps=20 format=3 uid="uid://dfat0erkvb513"] +[gd_scene load_steps=18 format=3 uid="uid://dfat0erkvb513"] [ext_resource type="Script" uid="uid://c2mo5hc1qb6kf" path="res://Scripts/Components/Actors/Actor.cs" id="1_p2pib"] [ext_resource type="Resource" uid="uid://cocl3qontm3be" path="res://Resources/Enemies/Base_Fairy.tres" id="2_oync8"] [ext_resource type="SpriteFrames" uid="uid://bcc5mlwwnkvri" path="res://Resources/Sprites/Fairy.tres" id="2_ycldt"] -[ext_resource type="Script" uid="uid://tk6ytw246ubg" path="res://Scripts/Components/Actors/EnemyPossessionMovement.cs" id="3_bwdr1"] -[ext_resource type="Script" uid="uid://dq338w2lw5phl" path="res://Scripts/Components/Actors/KeyboardInputProvider.cs" id="4_5uoep"] [ext_resource type="Script" uid="uid://pfxrvd1fofnj" path="res://Scripts/Components/Actors/FourWayAnimationHandler.cs" id="5_6u5xg"] [ext_resource type="Script" uid="uid://ghq0lmohdvqf" path="res://Scripts/Components/Actors/ActorAi.cs" id="6_3slbl"] [ext_resource type="Script" uid="uid://dsu7k7ds0cnba" path="res://Scripts/Components/Actors/EnemyNavigationMovement.cs" id="7_vtg4i"] @@ -36,12 +34,6 @@ EnemyData = ExtResource("2_oync8") sprite_frames = ExtResource("2_ycldt") animation = &"down" -[node name="MovementProvider" type="Node2D" parent="."] -script = ExtResource("3_bwdr1") - -[node name="InputProvider" type="Node2D" parent="MovementProvider"] -script = ExtResource("4_5uoep") - [node name="CollisionShape2D" type="CollisionShape2D" parent="."] shape = SubResource("CircleShape2D_2b36v") diff --git a/Scenes/Maps/PlayerFSMTest.tscn b/Scenes/Maps/PlayerFSMTest.tscn index 2d76e403..2a2bdc85 100644 --- a/Scenes/Maps/PlayerFSMTest.tscn +++ b/Scenes/Maps/PlayerFSMTest.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=72 format=4 uid="uid://dqyfnby0t7gu1"] +[gd_scene load_steps=73 format=4 uid="uid://dqyfnby0t7gu1"] [ext_resource type="Script" uid="uid://doxmbokehw8ci" path="res://Scripts/GameManager.cs" id="1_c3v4x"] [ext_resource type="Resource" uid="uid://cs3ihltcn2166" path="res://Resources/Items/IcicleGun.tres" id="3_6314l"] @@ -16,6 +16,7 @@ [ext_resource type="Script" uid="uid://c5nxsq3tyxcx6" path="res://Scripts/InventoryManager.cs" id="10_qmakk"] [ext_resource type="PackedScene" uid="uid://dkwi1hu1bixoe" path="res://Scenes/HUD/HUD.tscn" id="11_hmqao"] [ext_resource type="Script" uid="uid://bdshph801ac2i" path="res://Scenes/CameraTarget.gd" id="12_8to53"] +[ext_resource type="PackedScene" uid="uid://clieeuln36a7a" path="res://Scenes/Actors/Fairy_FSM.tscn" id="12_64bae"] [ext_resource type="Script" uid="uid://cnkipcolyj61w" path="res://Scripts/AlarmManager.cs" id="13_8fnge"] [ext_resource type="PackedScene" uid="uid://b0gpbkxdfbnjh" path="res://Scenes/Actors/ForceField_Horizontal.tscn" id="13_mljl7"] [ext_resource type="PackedScene" uid="uid://d0yes7huiyisw" path="res://Scenes/Items/Blue_Keycard.tscn" id="14_0knpf"] @@ -226,6 +227,9 @@ texture = SubResource("GradientTexture1D_4gtx8") position = Vector2(385, 111) scale = Vector2(0.66, 2.04) +[node name="FairyFsm" parent="Tilemaps/AcidHitboxes" instance=ExtResource("12_64bae")] +position = Vector2(544, 160) + [node name="Actors" type="Node2D" parent="Tilemaps"] y_sort_enabled = true metadata/_edit_lock_ = true @@ -363,19 +367,16 @@ position = Vector2(436, 161) position = Vector2(639.074, 158.001) Script = ExtResource("45_4mkc6") InvertSignal = true -EmitOnStart = true [node name="FloorEmitter3" parent="Tilemaps/Actors" instance=ExtResource("44_b8ffn")] position = Vector2(613, 186) Script = ExtResource("45_4mkc6") InvertSignal = true -EmitOnStart = true [node name="FloorEmitter2" parent="Tilemaps/Actors" instance=ExtResource("44_b8ffn")] position = Vector2(601, 147) Script = SubResource("Resource_64bae") InvertSignal = true -EmitOnStart = true [node name="ScriptableAreaTrigger" parent="Tilemaps" instance=ExtResource("28_fuxq3")] position = Vector2(230, 250) diff --git a/Scripts/Components/Actors/ActorAi.cs b/Scripts/Components/Actors/ActorAi.cs index ad71b145..5f8b85c6 100644 --- a/Scripts/Components/Actors/ActorAi.cs +++ b/Scripts/Components/Actors/ActorAi.cs @@ -1,4 +1,5 @@ using Cirno.Scripts.Components.Actors; +using Cirno.Scripts.Enums; using Godot; public partial class ActorAi : ActorModule diff --git a/Scripts/Components/Actors/EnemyNavigationMovement.cs b/Scripts/Components/Actors/EnemyNavigationMovement.cs index bcee4ddc..ecc9f9f7 100644 --- a/Scripts/Components/Actors/EnemyNavigationMovement.cs +++ b/Scripts/Components/Actors/EnemyNavigationMovement.cs @@ -4,6 +4,7 @@ using Cirno.Scripts.Components; using System.Collections.Generic; using System.Linq; using Cirno.Scripts.Components.Actors; +using Cirno.Scripts.Enums; public partial class EnemyNavigationMovement : MovementHandler { diff --git a/Scripts/Components/Actors/EnemyTurretRotationMovement.cs b/Scripts/Components/Actors/EnemyTurretRotationMovement.cs index 386536a6..e48b3dfe 100644 --- a/Scripts/Components/Actors/EnemyTurretRotationMovement.cs +++ b/Scripts/Components/Actors/EnemyTurretRotationMovement.cs @@ -1,4 +1,5 @@ -using Godot; +using Cirno.Scripts.Enums; +using Godot; namespace Cirno.Scripts.Components.Actors; diff --git a/Scripts/Components/Actors/GenericDamageReceiver.cs b/Scripts/Components/Actors/GenericDamageReceiver.cs index af8b0663..0deadfe2 100644 --- a/Scripts/Components/Actors/GenericDamageReceiver.cs +++ b/Scripts/Components/Actors/GenericDamageReceiver.cs @@ -16,6 +16,10 @@ public partial class GenericDamageReceiver : Area2D, IHittable [Export] public PackedScene Debris { get; set; } [Export] public Array DamageResistances { get; set; } = []; + + [Export] public bool DeleteParentOnDeath { get; private set; } = true; + + //[Signal] public delegate void DeathEventHandler(); private Node2D _parent; @@ -69,6 +73,12 @@ public partial class GenericDamageReceiver : Area2D, IHittable _parent.CreateSibling(Debris); } - _parent.QueueFree(); + // Not needed because the health provider is accessible + //EmitSignal(SignalName.Death); + + if (DeleteParentOnDeath) + { + _parent.QueueFree(); + } } } \ No newline at end of file diff --git a/Scripts/Components/FSM/BaseState.cs b/Scripts/Components/FSM/BaseState.cs index 9f118a53..94c51dad 100644 --- a/Scripts/Components/FSM/BaseState.cs +++ b/Scripts/Components/FSM/BaseState.cs @@ -19,7 +19,7 @@ public abstract partial class BaseState : Node2D, IState _moduleNodes = []; - private readonly List> _modules = []; + protected readonly List> _modules = []; public virtual void Init(IStateMachine machine) { diff --git a/Scripts/Components/FSM/Enemy/Alert.cs b/Scripts/Components/FSM/Enemy/Alert.cs new file mode 100644 index 00000000..043476ad --- /dev/null +++ b/Scripts/Components/FSM/Enemy/Alert.cs @@ -0,0 +1,105 @@ +using Cirno.Scripts.Components.Actors; +using Cirno.Scripts.Enums; +using Godot; + +namespace Cirno.Scripts.Components.FSM.Enemy; + +public partial class Alert : EnemyStateBase +{ + public override EnemyState StateId => EnemyState.Alert; + + [Export] + public EnemyStorageModule StorageModule { get; private set; } + + [Export] + public PlayerDetectionModule PlayerDetection { get; private set; } + + [Export] + public GenericDamageReceiver DamageReceiver { get; private set; } + + private bool _isPlayerInRange = false; + + public override void EnterState() + { + base.EnterState(); + GD.Print("Entered Idle"); + PlayerDetection.SetRange(StorageModule.Root.EnemyResource.PlayerDetectionRange); + + _isPlayerInRange = PlayerDetection.IsPlayerInRange(StorageModule.Root.EnemyResource.PlayerDetectionRange); + + PlayerDetection.PlayerInRange += PlayerDetectionOnPlayerInRange; + + PlayerDetection.PlayerOutOfRange += PlayerDetectionOnPlayerOutOfRange; + + DamageReceiver.ChangeState(true); + + DamageReceiver.HealthProvider.ResourceDepleted += HealthProviderOnResourceDepleted; + + } + + + private void HealthProviderOnResourceDepleted() + { + ChangeState(EnemyState.Dead); + } + + private void PlayerDetectionOnPlayerOutOfRange() + { + _isPlayerInRange = false; + GD.Print("Player out of range"); + } + + public override void ExitState() + { + base.ExitState(); + GD.Print("Exited Idle"); + PlayerDetection.PlayerInRange -= PlayerDetectionOnPlayerInRange; + + PlayerDetection.PlayerOutOfRange -= PlayerDetectionOnPlayerOutOfRange; + + DamageReceiver.HealthProvider.ResourceDepleted -= HealthProviderOnResourceDepleted; + DamageReceiver.ChangeState(false); + + } + + private void PlayerDetectionOnPlayerInRange() + { + _isPlayerInRange = true; + GD.Print("Player In Range"); + } + + public override void PhysicsProcessState(double delta) + { + base.PhysicsProcessState(delta); + if (_isPlayerInRange && PlayerDetection.IsPlayerInSight()) + { + StateMachine.SetState(EnemyState.Shooting); + return; + } + + // if player is outside disengage range, change to idle (later on, search) + if (this.GlobalPosition.DistanceTo(GameManager.Instance.PlayerPosition.Value) >= + StorageModule.Root.EnemyResource.PlayerDisengageRange) + { + StateMachine.SetState(EnemyState.Idle); + } + + // Move towards last known position + if (PlayerDetection.LastKnownPlayerPosition.HasValue) + { + MoveTowardsPosition(PlayerDetection.LastKnownPlayerPosition.Value); + } + + + } + + private void MoveTowardsPosition(Vector2 position) + { + + } + + public override void ProcessState(double delta) + { + base.ProcessState(delta); + } +} \ No newline at end of file diff --git a/Scripts/Components/FSM/Enemy/Alert.cs.uid b/Scripts/Components/FSM/Enemy/Alert.cs.uid new file mode 100644 index 00000000..5f471d34 --- /dev/null +++ b/Scripts/Components/FSM/Enemy/Alert.cs.uid @@ -0,0 +1 @@ +uid://dbmc3klko5x18 diff --git a/Scripts/Components/FSM/Enemy/EnemyDamageReceiver.cs b/Scripts/Components/FSM/Enemy/EnemyDamageReceiver.cs new file mode 100644 index 00000000..13d9767b --- /dev/null +++ b/Scripts/Components/FSM/Enemy/EnemyDamageReceiver.cs @@ -0,0 +1,15 @@ +using Cirno.Scripts.Components.Actors; +using Godot; + +namespace Cirno.Scripts.Components.FSM.Enemy; + +public partial class EnemyDamageReceiver : Area2D, IHittable +{ + [Export] + public ActorResourceProvider HealthProvider { get; private set; } + + public void Hit(float damage, DamageType damageType = DamageType.Neutral) + { + + } +} \ No newline at end of file diff --git a/Scripts/Components/FSM/Enemy/EnemyDamageReceiver.cs.uid b/Scripts/Components/FSM/Enemy/EnemyDamageReceiver.cs.uid new file mode 100644 index 00000000..fe9b1bbb --- /dev/null +++ b/Scripts/Components/FSM/Enemy/EnemyDamageReceiver.cs.uid @@ -0,0 +1 @@ +uid://vkmg3pggt8h5 diff --git a/Scripts/Components/FSM/Enemy/EnemyFSMProxy.cs b/Scripts/Components/FSM/Enemy/EnemyFSMProxy.cs new file mode 100644 index 00000000..2e715d1d --- /dev/null +++ b/Scripts/Components/FSM/Enemy/EnemyFSMProxy.cs @@ -0,0 +1,24 @@ +using Cirno.Scripts.Enums; +using Cirno.Scripts.Resources; +using Cirno.Scripts.Resources.Loot; +using Godot; +using Godot.Collections; + +namespace Cirno.Scripts.Components.FSM.Enemy; + +public partial class EnemyFSMProxy : CharacterBody2D +{ + [Export] public EnemyStateMachine EnemyFSM { get; private set; } + + [Export] public EnemyResource EnemyResource { get; private set; } + + [Export] public Array ExtraLoot { get; private set; } + + [Export] + public AiState StartingAiState { get; private set; } + + [ExportCategory("Defeat Script")] + [Export] public Node2D DefeatScript { get; set; } + + [Export] public ActivationType ActivationType { get; private set; } = ActivationType.Toggle; +} \ No newline at end of file diff --git a/Scripts/Components/FSM/Enemy/EnemyFSMProxy.cs.uid b/Scripts/Components/FSM/Enemy/EnemyFSMProxy.cs.uid new file mode 100644 index 00000000..d7731a87 --- /dev/null +++ b/Scripts/Components/FSM/Enemy/EnemyFSMProxy.cs.uid @@ -0,0 +1 @@ +uid://bi2edpdosngll diff --git a/Scripts/Components/FSM/Enemy/EnemyStateBase.cs b/Scripts/Components/FSM/Enemy/EnemyStateBase.cs new file mode 100644 index 00000000..2941747e --- /dev/null +++ b/Scripts/Components/FSM/Enemy/EnemyStateBase.cs @@ -0,0 +1,9 @@ +using Cirno.Scripts.Enums; +using Godot; + +namespace Cirno.Scripts.Components.FSM.Enemy; + +public abstract partial class EnemyStateBase : BaseState +{ + +} \ No newline at end of file diff --git a/Scripts/Components/FSM/Enemy/EnemyStateBase.cs.uid b/Scripts/Components/FSM/Enemy/EnemyStateBase.cs.uid new file mode 100644 index 00000000..ab962c2e --- /dev/null +++ b/Scripts/Components/FSM/Enemy/EnemyStateBase.cs.uid @@ -0,0 +1 @@ +uid://cljaa768xq70j diff --git a/Scripts/Components/FSM/Enemy/EnemyStateMachine.cs b/Scripts/Components/FSM/Enemy/EnemyStateMachine.cs new file mode 100644 index 00000000..fe5d559d --- /dev/null +++ b/Scripts/Components/FSM/Enemy/EnemyStateMachine.cs @@ -0,0 +1,10 @@ +using Cirno.Scripts.Enums; +using Godot; + +namespace Cirno.Scripts.Components.FSM.Enemy; + +public partial class EnemyStateMachine : StateMachineBase +{ + [Export] public override EnemyState InitialState { get; protected set; } = EnemyState.Init; + +} \ No newline at end of file diff --git a/Scripts/Components/FSM/Enemy/EnemyStateMachine.cs.uid b/Scripts/Components/FSM/Enemy/EnemyStateMachine.cs.uid new file mode 100644 index 00000000..9b30b3b0 --- /dev/null +++ b/Scripts/Components/FSM/Enemy/EnemyStateMachine.cs.uid @@ -0,0 +1 @@ +uid://dn6dbog1s2818 diff --git a/Scripts/Components/FSM/Enemy/EnemyStorageModule.cs b/Scripts/Components/FSM/Enemy/EnemyStorageModule.cs new file mode 100644 index 00000000..3ea07e1e --- /dev/null +++ b/Scripts/Components/FSM/Enemy/EnemyStorageModule.cs @@ -0,0 +1,19 @@ +using Godot; + +namespace Cirno.Scripts.Components.FSM.Enemy; + +public partial class EnemyStorageModule : Node2D +{ + [Export] + public EnemyFSMProxy Root { get; private set; } + + public Vector2 MovementDirection { get; set; } + public Vector2 FacingDirection { get; set; } + + public float MovementSpeed => Root.EnemyResource.MovementSpeed; + + + + + +} \ No newline at end of file diff --git a/Scripts/Components/FSM/Enemy/EnemyStorageModule.cs.uid b/Scripts/Components/FSM/Enemy/EnemyStorageModule.cs.uid new file mode 100644 index 00000000..e95a3ba3 --- /dev/null +++ b/Scripts/Components/FSM/Enemy/EnemyStorageModule.cs.uid @@ -0,0 +1 @@ +uid://bflvr26h52c55 diff --git a/Scripts/Components/FSM/Enemy/Idle.cs b/Scripts/Components/FSM/Enemy/Idle.cs new file mode 100644 index 00000000..f12d2121 --- /dev/null +++ b/Scripts/Components/FSM/Enemy/Idle.cs @@ -0,0 +1,102 @@ +using Cirno.Scripts.Components.Actors; +using Cirno.Scripts.Enums; +using Godot; + +namespace Cirno.Scripts.Components.FSM.Enemy; + +public partial class Idle : EnemyStateBase +{ + public override EnemyState StateId => EnemyState.Idle; + + // Scan for player, move to alert if found + // Receive damage, move to alert if received + + [Export] + public EnemyStorageModule StorageModule { get; private set; } + + [Export] + public PlayerDetectionModule PlayerDetection { get; private set; } + + [Export] + public GenericDamageReceiver DamageReceiver { get; private set; } + + // public override void Init(IStateMachine machine) + // { + // base.Init(machine); + // } + private bool _isPlayerInRange = false; + + public override void EnterState() + { + base.EnterState(); + GD.Print("Entered Idle"); + PlayerDetection.SetRange(StorageModule.Root.EnemyResource.PlayerDetectionRange); + + _isPlayerInRange = PlayerDetection.IsPlayerInRange(StorageModule.Root.EnemyResource.PlayerDetectionRange); + + PlayerDetection.PlayerInRange += PlayerDetectionOnPlayerInRange; + + PlayerDetection.PlayerOutOfRange += PlayerDetectionOnPlayerOutOfRange; + + DamageReceiver.ChangeState(true); + + DamageReceiver.HealthProvider.ResourceDepleted += HealthProviderOnResourceDepleted; + + DamageReceiver.HealthProvider.ResourceDecreased += HealthProviderOnResourceDecreased; + } + + private void HealthProviderOnResourceDecreased(float oldvalue, float newvalue, float maxvalue) + { + ChangeState(EnemyState.Alert); + } + + private void HealthProviderOnResourceDepleted() + { + ChangeState(EnemyState.Dead); + } + + private void PlayerDetectionOnPlayerOutOfRange() + { + _isPlayerInRange = false; + GD.Print("Player out of range"); + } + + public override void ExitState() + { + base.ExitState(); + GD.Print("Exited Idle"); + PlayerDetection.PlayerInRange -= PlayerDetectionOnPlayerInRange; + + PlayerDetection.PlayerOutOfRange -= PlayerDetectionOnPlayerOutOfRange; + + DamageReceiver.HealthProvider.ResourceDepleted -= HealthProviderOnResourceDepleted; + + DamageReceiver.HealthProvider.ResourceDecreased -= HealthProviderOnResourceDecreased; + DamageReceiver.ChangeState(false); + + } + + private void PlayerDetectionOnPlayerInRange() + { + _isPlayerInRange = true; + GD.Print("Player In Range"); + } + + public override void PhysicsProcessState(double delta) + { + base.PhysicsProcessState(delta); + if (_isPlayerInRange) + { + if (PlayerDetection.IsPlayerInSight()) + { + StateMachine.SetState(EnemyState.Alert); + } + } + } + + public override void ProcessState(double delta) + { + base.ProcessState(delta); + } + +} \ No newline at end of file diff --git a/Scripts/Components/FSM/Enemy/Idle.cs.uid b/Scripts/Components/FSM/Enemy/Idle.cs.uid new file mode 100644 index 00000000..cef553cb --- /dev/null +++ b/Scripts/Components/FSM/Enemy/Idle.cs.uid @@ -0,0 +1 @@ +uid://bjrh5q24nuoec diff --git a/Scripts/Components/FSM/Enemy/Init.cs b/Scripts/Components/FSM/Enemy/Init.cs new file mode 100644 index 00000000..5bb14f99 --- /dev/null +++ b/Scripts/Components/FSM/Enemy/Init.cs @@ -0,0 +1,35 @@ +using Cirno.Scripts.Components.Actors; +using Cirno.Scripts.Enums; +using Godot; + +namespace Cirno.Scripts.Components.FSM.Enemy; + +public partial class Init : EnemyStateBase +{ + public override EnemyState StateId => EnemyState.Init; + + [Export] + public GenericDamageReceiver DamageReceiver { get; private set; } + + [Export] + public EnemyStorageModule StorageModule { get; private set; } + + public override void EnterState() + { + GD.Print("Enemy init"); + DamageReceiver.HealthProvider.MaxResource = StorageModule.Root.EnemyResource.MaxHealth; + + StateMachine.SetState(EnemyState.Idle); + } + + public override void PhysicsProcessState(double delta) + { + + } + + public override void ProcessState(double delta) + { + + } + +} \ No newline at end of file diff --git a/Scripts/Components/FSM/Enemy/Init.cs.uid b/Scripts/Components/FSM/Enemy/Init.cs.uid new file mode 100644 index 00000000..78a559b3 --- /dev/null +++ b/Scripts/Components/FSM/Enemy/Init.cs.uid @@ -0,0 +1 @@ +uid://rrelumir3g6n diff --git a/Scripts/Components/FSM/Enemy/PlayerDetectionModule.cs b/Scripts/Components/FSM/Enemy/PlayerDetectionModule.cs new file mode 100644 index 00000000..9f2c141d --- /dev/null +++ b/Scripts/Components/FSM/Enemy/PlayerDetectionModule.cs @@ -0,0 +1,83 @@ +using Cirno.Scripts.Enums; +using Godot; +using Godot.Collections; + +namespace Cirno.Scripts.Components.FSM.Enemy; + +public partial class PlayerDetectionModule : Area2D +{ + [Signal] + public delegate void PlayerInRangeEventHandler(); + + [Signal] + public delegate void PlayerOutOfRangeEventHandler(); + + [Export(PropertyHint.Layers2DPhysics)] + public uint ObstaclesCollisionMask { get; private set; } + + public Vector2? LastKnownPlayerPosition { get; private set; } + + //public bool PlayerInActiveArea { get; private set; } + private CollisionShape2D _collisionShape2D; + public override void _Ready() + { + + } + + public void SetRange(float range) + { + _collisionShape2D ??= this.GetNode("CollisionShape2D"); + if (_collisionShape2D.Shape is CircleShape2D shape2D) + { + shape2D.Radius = range; + } + } + + public bool IsPlayerInRange(float range) + { + if (!GameManager.Instance?.PlayerPosition.HasValue ?? false) + { + return false; + } + + return this.GlobalPosition.DistanceTo(GameManager.Instance.PlayerPosition.Value) < range; + } + + public bool IsPlayerInSight() + { + //if (_cachedPlayer == null) return false; + if (!GameManager.Instance?.PlayerPosition.HasValue ?? false) return false; + + var spaceState = GetWorld2D().DirectSpaceState; + + // It needs to use its own collision mask because it's detecting obstacles rather than the player + var query = PhysicsRayQueryParameters2D.Create(this.GlobalPosition, GameManager.Instance.PlayerPosition.Value, ObstaclesCollisionMask, + [GetRid()]); + //query.CollideWithBodies = true; + //query.CollideWithAreas = true; + // 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 + var found = result.Count == 0; + if (found) + { + LastKnownPlayerPosition = GameManager.Instance.PlayerPosition; + } + return found; + } + + private void _on_area_entered(Area2D area) + { + if (area is not InteractionController player) return; + EmitSignal(SignalName.PlayerInRange); + //PlayerInActiveArea = true; + } + + private void _on_area_exited(Area2D area) + { + if (area is not InteractionController player) return; + EmitSignal(SignalName.PlayerOutOfRange); + //PlayerInActiveArea = false; + } +} \ No newline at end of file diff --git a/Scripts/Components/FSM/Enemy/PlayerDetectionModule.cs.uid b/Scripts/Components/FSM/Enemy/PlayerDetectionModule.cs.uid new file mode 100644 index 00000000..534d9f0d --- /dev/null +++ b/Scripts/Components/FSM/Enemy/PlayerDetectionModule.cs.uid @@ -0,0 +1 @@ +uid://mb4ugq74a17c diff --git a/Scripts/Components/FSM/EnemyFSMState.cs b/Scripts/Components/FSM/EnemyFSMState.cs index 4358c30c..02d8ddd1 100644 --- a/Scripts/Components/FSM/EnemyFSMState.cs +++ b/Scripts/Components/FSM/EnemyFSMState.cs @@ -1,4 +1,5 @@ -using Godot; +using Cirno.Scripts.Enums; +using Godot; namespace Cirno.Scripts.Components.FSM; diff --git a/Scripts/Components/PlayerDetection.cs b/Scripts/Components/PlayerDetection.cs index 912f1c22..1c56b103 100644 --- a/Scripts/Components/PlayerDetection.cs +++ b/Scripts/Components/PlayerDetection.cs @@ -8,6 +8,7 @@ public partial class PlayerDetection : Area2D public InteractionController CachedPlayer => _cachedPlayer; protected InteractionController _cachedPlayer; + public virtual bool IsPlayerInRange { get; set; } public virtual bool IsPlayerInSight(uint collisionMask) diff --git a/Scripts/Enemy.cs b/Scripts/Enemy.cs index 50c99166..9203d7bb 100644 --- a/Scripts/Enemy.cs +++ b/Scripts/Enemy.cs @@ -3,6 +3,7 @@ using Godot; using System; using System.Diagnostics; using Cirno.Scripts.Components; +using Cirno.Scripts.Enums; using Godot.Collections; public partial class Enemy : CharacterBody2D @@ -319,14 +320,6 @@ public partial class Enemy : CharacterBody2D } } -public enum EnemyState -{ - Idle, - Alert, - Patrolling, - Shooting -} - public enum AiState { Enabled, diff --git a/Scripts/Enums/EnemyState.cs b/Scripts/Enums/EnemyState.cs new file mode 100644 index 00000000..f28a87bf --- /dev/null +++ b/Scripts/Enums/EnemyState.cs @@ -0,0 +1,12 @@ +namespace Cirno.Scripts.Enums; + +public enum EnemyState +{ + Init, + Idle, + Alert, + Patrolling, + Shooting, + Dead, + Controlled +} \ No newline at end of file diff --git a/Scripts/Resources/EnemyResource.cs b/Scripts/Resources/EnemyResource.cs index 2c631596..246af448 100644 --- a/Scripts/Resources/EnemyResource.cs +++ b/Scripts/Resources/EnemyResource.cs @@ -16,8 +16,8 @@ public partial class EnemyResource : Resource [Export] public WeaponResource Weapon { get; private set; } [Export] public Array LootDrops { get; private set; } - - [ExportCategory("AI")] + + [ExportCategory("AI")] [Export] public float PlayerDetectionRange { get; private set; } = 90f; [Export] public float AlarmReactRange { get; private set; } [Export] public float PlayerDisengageRange { get; private set; } } \ No newline at end of file diff --git a/addons/dialogic/Core/DialogicGameHandler.gd b/addons/dialogic/Core/DialogicGameHandler.gd index 43c25574..d1a6c28c 100644 --- a/addons/dialogic/Core/DialogicGameHandler.gd +++ b/addons/dialogic/Core/DialogicGameHandler.gd @@ -149,6 +149,9 @@ var VAR := preload("res://addons/dialogic/Modules/Variable/subsystem_variables.g var Voice := preload("res://addons/dialogic/Modules/Voice/subsystem_voice.gd").new(): get: return get_subsystem("Voice") +var ShowImage := preload("res://addons/dialogic_additions/ShowImage/subsystem_show_image.gd").new(): + get: return get_subsystem("ShowImage") + #endregion diff --git a/addons/dialogic_additions/ShowImage/event_show_image.gd b/addons/dialogic_additions/ShowImage/event_show_image.gd index 38259398..74ccd626 100644 --- a/addons/dialogic_additions/ShowImage/event_show_image.gd +++ b/addons/dialogic_additions/ShowImage/event_show_image.gd @@ -54,7 +54,7 @@ func _execute() -> void: if dialogic.Inputs.auto_skip.enabled: var time_per_event: float = dialogic.Inputs.auto_skip.time_per_event final_fade_duration = min(fade, time_per_event) - + dialogic.ShowImage dialogic.get_subsystem("ShowImage").update_image(scene, argument, final_fade_duration, transition) finish()