diff --git a/DungeonShooting_Godot/prefab/role/Enemy0001.tscn b/DungeonShooting_Godot/prefab/role/Enemy0001.tscn
index 815c72f..923ba15 100644
--- a/DungeonShooting_Godot/prefab/role/Enemy0001.tscn
+++ b/DungeonShooting_Godot/prefab/role/Enemy0001.tscn
@@ -1,7 +1,7 @@
[gd_scene load_steps=12 format=3 uid="uid://b8s1dgu63fddf"]
-[ext_resource type="PackedScene" uid="uid://dbrig6dq441wo" path="res://prefab/role/template/AdvancedEnemyTemplate.tscn" id="1_2vqwe"]
-[ext_resource type="Script" path="res://src/game/activity/role/enemy/AdvancedEnemy.cs" id="2_thbey"]
+[ext_resource type="PackedScene" uid="uid://dbrig6dq441wo" path="res://prefab/role/template/EnemyTemplate.tscn" id="1_2vqwe"]
+[ext_resource type="Script" path="res://src/game/activity/role/enemy/Enemy.cs" id="2_0pcq3"]
[ext_resource type="Shader" path="res://resource/material/Blend.gdshader" id="3_x8agd"]
[ext_resource type="SpriteFrames" uid="uid://cnctpyrn02rhd" path="res://resource/spriteFrames/role/Enemy0001.tres" id="4_qv8w5"]
@@ -269,15 +269,11 @@
"query": SubResource("Animation_usfrh")
}
-[node name="Enemy0001" node_paths=PackedStringArray("ViewRay", "NavigationAgent2D", "NavigationPoint", "MountPoint", "BackMountPoint", "MeleeAttackArea", "MeleeAttackCollision", "HurtArea", "HurtCollision", "InteractiveArea", "InteractiveCollision", "TipRoot", "TipSprite", "AnimationPlayer", "ShadowSprite", "AnimatedSprite", "Collision") instance=ExtResource("1_2vqwe")]
-script = ExtResource("2_thbey")
+[node name="Enemy0001" node_paths=PackedStringArray("ViewRay", "NavigationAgent2D", "NavigationPoint", "HurtArea", "HurtCollision", "InteractiveArea", "InteractiveCollision", "TipRoot", "TipSprite", "AnimationPlayer", "MountPoint", "BackMountPoint", "MeleeAttackArea", "MeleeAttackCollision", "ShadowSprite", "AnimatedSprite", "Collision") instance=ExtResource("1_2vqwe")]
+script = ExtResource("2_0pcq3")
ViewRay = NodePath("ViewRay")
NavigationAgent2D = NodePath("NavigationPoint/NavigationAgent2D")
NavigationPoint = NodePath("NavigationPoint")
-MountPoint = NodePath("MountPoint")
-BackMountPoint = NodePath("BackMountPoint")
-MeleeAttackArea = NodePath("MountPoint/MeleeAttackArea")
-MeleeAttackCollision = NodePath("MountPoint/MeleeAttackArea/MeleeAttackCollision")
HurtArea = NodePath("HurtArea")
HurtCollision = NodePath("HurtArea/HurtCollision")
InteractiveArea = NodePath("InteractiveArea")
@@ -285,6 +281,10 @@
TipRoot = NodePath("TipRoot")
TipSprite = NodePath("TipRoot/TipSprite")
AnimationPlayer = NodePath("AnimationPlayer")
+MountPoint = NodePath("MountPoint")
+BackMountPoint = NodePath("BackMountPoint")
+MeleeAttackArea = NodePath("MountPoint/MeleeAttackArea")
+MeleeAttackCollision = NodePath("MountPoint/MeleeAttackArea/MeleeAttackCollision")
ShadowSprite = NodePath("ShadowSprite")
AnimatedSprite = NodePath("AnimatedSprite")
Collision = NodePath("Collision")
diff --git a/DungeonShooting_Godot/prefab/role/Enemy0002.tscn b/DungeonShooting_Godot/prefab/role/Enemy0002.tscn
deleted file mode 100644
index b7b5558..0000000
--- a/DungeonShooting_Godot/prefab/role/Enemy0002.tscn
+++ /dev/null
@@ -1,57 +0,0 @@
-[gd_scene load_steps=7 format=3 uid="uid://b5r3hd8kv2wmd"]
-
-[ext_resource type="PackedScene" uid="uid://dxeqcssparqoo" path="res://prefab/role/template/EnemyTemplate.tscn" id="1_rikvp"]
-[ext_resource type="Script" path="res://src/game/activity/role/enemy/Enemy.cs" id="2_wjtfl"]
-[ext_resource type="Shader" path="res://resource/material/Blend.gdshader" id="3_gr4gs"]
-[ext_resource type="SpriteFrames" uid="uid://ctpkpxgcwb583" path="res://resource/spriteFrames/role/Enemy0002.tres" id="4_ehtyi"]
-
-[sub_resource type="ShaderMaterial" id="ShaderMaterial_7theg"]
-resource_local_to_scene = true
-shader = ExtResource("3_gr4gs")
-shader_parameter/blend = Color(0, 0, 0, 0.470588)
-shader_parameter/schedule = 1.0
-shader_parameter/modulate = Color(1, 1, 1, 1)
-shader_parameter/show_outline = true
-shader_parameter/outline_color = Color(0, 0, 0, 1)
-shader_parameter/outline_rainbow = false
-shader_parameter/outline_use_blend = true
-
-[sub_resource type="ShaderMaterial" id="ShaderMaterial_ntjmx"]
-resource_local_to_scene = true
-shader = ExtResource("3_gr4gs")
-shader_parameter/blend = Color(1, 1, 1, 1)
-shader_parameter/schedule = 0.0
-shader_parameter/modulate = Color(1, 1, 1, 1)
-shader_parameter/show_outline = true
-shader_parameter/outline_color = Color(0, 0, 0, 1)
-shader_parameter/outline_rainbow = false
-shader_parameter/outline_use_blend = true
-
-[node name="Enemy0002" node_paths=PackedStringArray("ViewRay", "NavigationAgent2D", "NavigationPoint", "FirePoint", "HurtArea", "HurtCollision", "InteractiveArea", "InteractiveCollision", "TipRoot", "TipSprite", "AnimationPlayer", "ShadowSprite", "AnimatedSprite", "Collision") instance=ExtResource("1_rikvp")]
-script = ExtResource("2_wjtfl")
-ViewRay = NodePath("ViewRay")
-NavigationAgent2D = NodePath("NavigationPoint/NavigationAgent2D")
-NavigationPoint = NodePath("NavigationPoint")
-FirePoint = NodePath("AnimatedSprite/FirePoint")
-HurtArea = NodePath("HurtArea")
-HurtCollision = NodePath("HurtArea/HurtCollision")
-InteractiveArea = NodePath("InteractiveArea")
-InteractiveCollision = NodePath("InteractiveArea/InteractiveCollision")
-TipRoot = NodePath("TipRoot")
-TipSprite = NodePath("TipRoot/TipSprite")
-AnimationPlayer = NodePath("AnimationPlayer")
-ShadowSprite = NodePath("ShadowSprite")
-AnimatedSprite = NodePath("AnimatedSprite")
-Collision = NodePath("Collision")
-
-[node name="ShadowSprite" parent="." index="0"]
-material = SubResource("ShaderMaterial_7theg")
-
-[node name="AnimatedSprite" parent="." index="1"]
-material = SubResource("ShaderMaterial_ntjmx")
-position = Vector2(0, -10)
-sprite_frames = ExtResource("4_ehtyi")
-animation = &"attack"
-
-[node name="FirePoint" type="Marker2D" parent="AnimatedSprite" index="0"]
-position = Vector2(9, 4)
diff --git a/DungeonShooting_Godot/prefab/role/Role0001.tscn b/DungeonShooting_Godot/prefab/role/Role0001.tscn
index 24c63b1..43ea2ef 100644
--- a/DungeonShooting_Godot/prefab/role/Role0001.tscn
+++ b/DungeonShooting_Godot/prefab/role/Role0001.tscn
@@ -1,6 +1,6 @@
[gd_scene load_steps=7 format=3 uid="uid://cxhrcytrx0kcf"]
-[ext_resource type="PackedScene" uid="uid://cyrcv2jdgr8cf" path="res://prefab/role/template/AdvancedRoleTemplate.tscn" id="1_10c2n"]
+[ext_resource type="PackedScene" uid="uid://cyrcv2jdgr8cf" path="res://prefab/role/template/RoleTemplate.tscn" id="1_10c2n"]
[ext_resource type="Script" path="res://src/game/activity/role/player/Player.cs" id="2_6xwnt"]
[ext_resource type="Shader" path="res://resource/material/Blend.gdshader" id="3_rk4gg"]
[ext_resource type="SpriteFrames" uid="uid://n11thtali6es" path="res://resource/spriteFrames/role/Role0001.tres" id="4_galcc"]
@@ -27,13 +27,9 @@
shader_parameter/outline_rainbow = false
shader_parameter/outline_use_blend = true
-[node name="Role0001" node_paths=PackedStringArray("MountPoint", "BackMountPoint", "MeleeAttackArea", "MeleeAttackCollision", "HurtArea", "HurtCollision", "InteractiveArea", "InteractiveCollision", "TipRoot", "TipSprite", "AnimationPlayer", "ShadowSprite", "AnimatedSprite", "Collision") instance=ExtResource("1_10c2n")]
+[node name="Role0001" node_paths=PackedStringArray("HurtArea", "HurtCollision", "InteractiveArea", "InteractiveCollision", "TipRoot", "TipSprite", "AnimationPlayer", "MountPoint", "BackMountPoint", "MeleeAttackArea", "MeleeAttackCollision", "ShadowSprite", "AnimatedSprite", "Collision") instance=ExtResource("1_10c2n")]
collision_layer = 8
script = ExtResource("2_6xwnt")
-MountPoint = NodePath("MountPoint")
-BackMountPoint = NodePath("BackMountPoint")
-MeleeAttackArea = NodePath("MountPoint/MeleeAttackArea")
-MeleeAttackCollision = NodePath("MountPoint/MeleeAttackArea/MeleeAttackCollision")
HurtArea = NodePath("HurtArea")
HurtCollision = NodePath("HurtArea/HurtCollision")
InteractiveArea = NodePath("InteractiveArea")
@@ -41,6 +37,10 @@
TipRoot = NodePath("TipRoot")
TipSprite = NodePath("TipRoot/TipSprite")
AnimationPlayer = NodePath("AnimationPlayer")
+MountPoint = NodePath("MountPoint")
+BackMountPoint = NodePath("BackMountPoint")
+MeleeAttackArea = NodePath("MountPoint/MeleeAttackArea")
+MeleeAttackCollision = NodePath("MountPoint/MeleeAttackArea/MeleeAttackCollision")
ShadowSprite = NodePath("ShadowSprite")
AnimatedSprite = NodePath("AnimatedSprite")
Collision = NodePath("Collision")
diff --git a/DungeonShooting_Godot/prefab/role/template/AdvancedEnemyTemplate.tscn b/DungeonShooting_Godot/prefab/role/template/AdvancedEnemyTemplate.tscn
deleted file mode 100644
index b02e44a..0000000
--- a/DungeonShooting_Godot/prefab/role/template/AdvancedEnemyTemplate.tscn
+++ /dev/null
@@ -1,48 +0,0 @@
-[gd_scene load_steps=5 format=3 uid="uid://dbrig6dq441wo"]
-
-[ext_resource type="PackedScene" uid="uid://cyrcv2jdgr8cf" path="res://prefab/role/template/AdvancedRoleTemplate.tscn" id="1_5po38"]
-[ext_resource type="Shader" path="res://resource/material/Blend.gdshader" id="3_x8agd"]
-
-[sub_resource type="ShaderMaterial" id="ShaderMaterial_8vxx6"]
-resource_local_to_scene = true
-shader = ExtResource("3_x8agd")
-shader_parameter/blend = Color(0, 0, 0, 0.470588)
-shader_parameter/schedule = 1.0
-shader_parameter/modulate = Color(1, 1, 1, 1)
-shader_parameter/show_outline = true
-shader_parameter/outline_color = Color(0, 0, 0, 1)
-shader_parameter/outline_rainbow = false
-shader_parameter/outline_use_blend = true
-
-[sub_resource type="ShaderMaterial" id="ShaderMaterial_k8mt5"]
-resource_local_to_scene = true
-shader = ExtResource("3_x8agd")
-shader_parameter/blend = Color(1, 1, 1, 1)
-shader_parameter/schedule = 0.0
-shader_parameter/modulate = Color(1, 1, 1, 1)
-shader_parameter/show_outline = true
-shader_parameter/outline_color = Color(0, 0, 0, 1)
-shader_parameter/outline_rainbow = false
-shader_parameter/outline_use_blend = true
-
-[node name="AdvancedEnemyTemplate" instance=ExtResource("1_5po38")]
-collision_layer = 16
-collision_mask = 25
-
-[node name="ShadowSprite" parent="." index="0"]
-material = SubResource("ShaderMaterial_8vxx6")
-
-[node name="AnimatedSprite" parent="." index="2"]
-material = SubResource("ShaderMaterial_k8mt5")
-
-[node name="ViewRay" type="RayCast2D" parent="." index="6"]
-position = Vector2(0, -8)
-enabled = false
-
-[node name="NavigationPoint" type="Marker2D" parent="." index="8"]
-position = Vector2(0, -5)
-
-[node name="NavigationAgent2D" type="NavigationAgent2D" parent="NavigationPoint" index="0"]
-path_desired_distance = 3.0
-target_desired_distance = 3.0
-radius = 20.0
diff --git a/DungeonShooting_Godot/prefab/role/template/AdvancedRoleTemplate.tscn b/DungeonShooting_Godot/prefab/role/template/AdvancedRoleTemplate.tscn
deleted file mode 100644
index 7b79ea0..0000000
--- a/DungeonShooting_Godot/prefab/role/template/AdvancedRoleTemplate.tscn
+++ /dev/null
@@ -1,93 +0,0 @@
-[gd_scene load_steps=9 format=3 uid="uid://cyrcv2jdgr8cf"]
-
-[ext_resource type="Shader" path="res://resource/material/Blend.gdshader" id="1_xk5yk"]
-[ext_resource type="Script" path="res://src/game/activity/role/MountRotation.cs" id="2_5ddpw"]
-[ext_resource type="SpriteFrames" uid="uid://c8h5svp76h3kw" path="res://resource/spriteFrames/role/Role_tip.tres" id="3_bo78w"]
-
-[sub_resource type="ShaderMaterial" id="ShaderMaterial_v2kfw"]
-resource_local_to_scene = true
-shader = ExtResource("1_xk5yk")
-shader_parameter/blend = Color(0, 0, 0, 0.470588)
-shader_parameter/schedule = 0.0
-shader_parameter/modulate = Color(1, 1, 1, 1)
-shader_parameter/show_outline = true
-shader_parameter/outline_color = Color(0, 0, 0, 1)
-shader_parameter/outline_rainbow = false
-shader_parameter/outline_use_blend = true
-
-[sub_resource type="ShaderMaterial" id="ShaderMaterial_yif6x"]
-resource_local_to_scene = true
-shader = ExtResource("1_xk5yk")
-shader_parameter/blend = Color(1, 1, 1, 1)
-shader_parameter/schedule = 0.0
-shader_parameter/modulate = Color(1, 1, 1, 1)
-shader_parameter/show_outline = true
-shader_parameter/outline_color = Color(0, 0, 0, 1)
-shader_parameter/outline_rainbow = false
-shader_parameter/outline_use_blend = true
-
-[sub_resource type="CircleShape2D" id="CircleShape2D_5pj80"]
-radius = 4.0
-
-[sub_resource type="RectangleShape2D" id="RectangleShape2D_1eja2"]
-size = Vector2(12, 18)
-
-[sub_resource type="RectangleShape2D" id="RectangleShape2D_n68nu"]
-size = Vector2(10, 16.5)
-
-[node name="AdvancedRoleTemplate" type="CharacterBody2D"]
-collision_layer = 0
-
-[node name="ShadowSprite" type="Sprite2D" parent="."]
-z_index = -1
-material = SubResource("ShaderMaterial_v2kfw")
-
-[node name="BackMountPoint" type="Marker2D" parent="."]
-position = Vector2(0, -12)
-
-[node name="AnimatedSprite" type="AnimatedSprite2D" parent="."]
-material = SubResource("ShaderMaterial_yif6x")
-offset = Vector2(0, -12)
-
-[node name="Collision" type="CollisionShape2D" parent="."]
-position = Vector2(0, -4)
-shape = SubResource("CircleShape2D_5pj80")
-
-[node name="HurtArea" type="Area2D" parent="."]
-collision_layer = 0
-collision_mask = 0
-monitoring = false
-
-[node name="HurtCollision" type="CollisionShape2D" parent="HurtArea"]
-position = Vector2(0, -9)
-shape = SubResource("RectangleShape2D_1eja2")
-
-[node name="InteractiveArea" type="Area2D" parent="."]
-visible = false
-collision_layer = 0
-collision_mask = 4
-monitorable = false
-
-[node name="InteractiveCollision" type="CollisionShape2D" parent="InteractiveArea"]
-position = Vector2(0, -5)
-shape = SubResource("RectangleShape2D_n68nu")
-
-[node name="MountPoint" type="Marker2D" parent="."]
-position = Vector2(2, -8)
-script = ExtResource("2_5ddpw")
-
-[node name="MeleeAttackArea" type="Area2D" parent="MountPoint"]
-collision_layer = 0
-collision_mask = 0
-monitorable = false
-
-[node name="MeleeAttackCollision" type="CollisionPolygon2D" parent="MountPoint/MeleeAttackArea"]
-
-[node name="TipRoot" type="Node2D" parent="."]
-
-[node name="TipSprite" type="AnimatedSprite2D" parent="TipRoot"]
-visible = false
-position = Vector2(0, -22)
-sprite_frames = ExtResource("3_bo78w")
-
-[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
diff --git a/DungeonShooting_Godot/prefab/role/template/EnemyTemplate.tscn b/DungeonShooting_Godot/prefab/role/template/EnemyTemplate.tscn
index 8d83646..272b97a 100644
--- a/DungeonShooting_Godot/prefab/role/template/EnemyTemplate.tscn
+++ b/DungeonShooting_Godot/prefab/role/template/EnemyTemplate.tscn
@@ -1,11 +1,11 @@
-[gd_scene load_steps=5 format=3 uid="uid://dxeqcssparqoo"]
+[gd_scene load_steps=5 format=3 uid="uid://dbrig6dq441wo"]
-[ext_resource type="PackedScene" uid="uid://0uc4naitjprl" path="res://prefab/role/template/RoleTemplate.tscn" id="1_u04qy"]
-[ext_resource type="Shader" path="res://resource/material/Blend.gdshader" id="2_tedjs"]
+[ext_resource type="PackedScene" uid="uid://cyrcv2jdgr8cf" path="res://prefab/role/template/RoleTemplate.tscn" id="1_5po38"]
+[ext_resource type="Shader" path="res://resource/material/Blend.gdshader" id="3_x8agd"]
-[sub_resource type="ShaderMaterial" id="ShaderMaterial_fhls5"]
+[sub_resource type="ShaderMaterial" id="ShaderMaterial_8vxx6"]
resource_local_to_scene = true
-shader = ExtResource("2_tedjs")
+shader = ExtResource("3_x8agd")
shader_parameter/blend = Color(0, 0, 0, 0.470588)
shader_parameter/schedule = 1.0
shader_parameter/modulate = Color(1, 1, 1, 1)
@@ -14,9 +14,9 @@
shader_parameter/outline_rainbow = false
shader_parameter/outline_use_blend = true
-[sub_resource type="ShaderMaterial" id="ShaderMaterial_lnent"]
+[sub_resource type="ShaderMaterial" id="ShaderMaterial_k8mt5"]
resource_local_to_scene = true
-shader = ExtResource("2_tedjs")
+shader = ExtResource("3_x8agd")
shader_parameter/blend = Color(1, 1, 1, 1)
shader_parameter/schedule = 0.0
shader_parameter/modulate = Color(1, 1, 1, 1)
@@ -25,21 +25,21 @@
shader_parameter/outline_rainbow = false
shader_parameter/outline_use_blend = true
-[node name="EnemyTemplate" instance=ExtResource("1_u04qy")]
+[node name="AdvancedEnemyTemplate" instance=ExtResource("1_5po38")]
collision_layer = 16
collision_mask = 25
[node name="ShadowSprite" parent="." index="0"]
-material = SubResource("ShaderMaterial_fhls5")
+material = SubResource("ShaderMaterial_8vxx6")
-[node name="AnimatedSprite" parent="." index="1"]
-material = SubResource("ShaderMaterial_lnent")
+[node name="AnimatedSprite" parent="." index="2"]
+material = SubResource("ShaderMaterial_k8mt5")
-[node name="ViewRay" type="RayCast2D" parent="." index="5"]
+[node name="ViewRay" type="RayCast2D" parent="." index="6"]
position = Vector2(0, -8)
enabled = false
-[node name="NavigationPoint" type="Marker2D" parent="." index="6"]
+[node name="NavigationPoint" type="Marker2D" parent="." index="8"]
position = Vector2(0, -5)
[node name="NavigationAgent2D" type="NavigationAgent2D" parent="NavigationPoint" index="0"]
diff --git a/DungeonShooting_Godot/prefab/role/template/RoleTemplate.tscn b/DungeonShooting_Godot/prefab/role/template/RoleTemplate.tscn
index b8cb5a0..7b79ea0 100644
--- a/DungeonShooting_Godot/prefab/role/template/RoleTemplate.tscn
+++ b/DungeonShooting_Godot/prefab/role/template/RoleTemplate.tscn
@@ -1,7 +1,8 @@
-[gd_scene load_steps=8 format=3 uid="uid://0uc4naitjprl"]
+[gd_scene load_steps=9 format=3 uid="uid://cyrcv2jdgr8cf"]
[ext_resource type="Shader" path="res://resource/material/Blend.gdshader" id="1_xk5yk"]
-[ext_resource type="SpriteFrames" uid="uid://c8h5svp76h3kw" path="res://resource/spriteFrames/role/Role_tip.tres" id="2_1udon"]
+[ext_resource type="Script" path="res://src/game/activity/role/MountRotation.cs" id="2_5ddpw"]
+[ext_resource type="SpriteFrames" uid="uid://c8h5svp76h3kw" path="res://resource/spriteFrames/role/Role_tip.tres" id="3_bo78w"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_v2kfw"]
resource_local_to_scene = true
@@ -34,13 +35,16 @@
[sub_resource type="RectangleShape2D" id="RectangleShape2D_n68nu"]
size = Vector2(10, 16.5)
-[node name="RoleTemplate" type="CharacterBody2D"]
+[node name="AdvancedRoleTemplate" type="CharacterBody2D"]
collision_layer = 0
[node name="ShadowSprite" type="Sprite2D" parent="."]
z_index = -1
material = SubResource("ShaderMaterial_v2kfw")
+[node name="BackMountPoint" type="Marker2D" parent="."]
+position = Vector2(0, -12)
+
[node name="AnimatedSprite" type="AnimatedSprite2D" parent="."]
material = SubResource("ShaderMaterial_yif6x")
offset = Vector2(0, -12)
@@ -68,9 +72,22 @@
position = Vector2(0, -5)
shape = SubResource("RectangleShape2D_n68nu")
-[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
+[node name="MountPoint" type="Marker2D" parent="."]
+position = Vector2(2, -8)
+script = ExtResource("2_5ddpw")
+
+[node name="MeleeAttackArea" type="Area2D" parent="MountPoint"]
+collision_layer = 0
+collision_mask = 0
+monitorable = false
+
+[node name="MeleeAttackCollision" type="CollisionPolygon2D" parent="MountPoint/MeleeAttackArea"]
[node name="TipRoot" type="Node2D" parent="."]
[node name="TipSprite" type="AnimatedSprite2D" parent="TipRoot"]
-sprite_frames = ExtResource("2_1udon")
+visible = false
+position = Vector2(0, -22)
+sprite_frames = ExtResource("3_bo78w")
+
+[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
diff --git a/DungeonShooting_Godot/resource/sprite/role/common/Role_astonished.png b/DungeonShooting_Godot/resource/sprite/role/common/Role_astonished.png
index 6ba090b..8d232dd 100644
--- a/DungeonShooting_Godot/resource/sprite/role/common/Role_astonished.png
+++ b/DungeonShooting_Godot/resource/sprite/role/common/Role_astonished.png
Binary files differ
diff --git a/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs b/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs
index ff5d0c9..00e5e81 100644
--- a/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs
+++ b/DungeonShooting_Godot/src/framework/map/preinstall/RoomPreinstall.cs
@@ -431,7 +431,7 @@
else if (activityMark.ActivityType == ActivityType.Enemy) //敌人类型
{
var role = (Role)activityObject;
- if (role is AdvancedEnemy enemy && activityMark.Attr.TryGetValue("Weapon", out var weaponId)) //使用的武器
+ if (role is Enemy enemy && activityMark.Attr.TryGetValue("Weapon", out var weaponId)) //使用的武器
{
if (!string.IsNullOrEmpty(weaponId))
{
diff --git a/DungeonShooting_Godot/src/game/Cursor.cs b/DungeonShooting_Godot/src/game/Cursor.cs
index 7f8e528..aca4d11 100644
--- a/DungeonShooting_Godot/src/game/Cursor.cs
+++ b/DungeonShooting_Godot/src/game/Cursor.cs
@@ -13,7 +13,7 @@
///
/// 非GUI模式下鼠标指针所挂载的角色
///
- private AdvancedRole _mountRole;
+ private Role _mountRole;
private Sprite2D center;
private Sprite2D lt;
@@ -88,7 +88,7 @@
///
/// 设置非GUI模式下鼠标指针所挂载的角色
///
- public void SetMountRole(AdvancedRole role)
+ public void SetMountRole(Role role)
{
_mountRole = role;
}
@@ -96,7 +96,7 @@
///
/// 获取非GUI模式下鼠标指针所挂载的角色
///
- public AdvancedRole GetMountRole()
+ public Role GetMountRole()
{
return _mountRole;
}
diff --git a/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs b/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs
index 5491250..a516c2f 100644
--- a/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs
+++ b/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs
@@ -146,7 +146,7 @@
}
//造成伤害
- role.CallDeferred(nameof(AdvancedRole.Hurt), BulletData.TriggerRole.IsDestroyed ? null : BulletData.TriggerRole, BulletData.Harm, Rotation);
+ role.CallDeferred(nameof(Role.Hurt), BulletData.TriggerRole.IsDestroyed ? null : BulletData.TriggerRole, BulletData.Harm, Rotation);
//穿透次数
CurrentPenetration++;
diff --git a/DungeonShooting_Godot/src/game/activity/prop/Prop.cs b/DungeonShooting_Godot/src/game/activity/prop/Prop.cs
index 09ddfca..c79dfc0 100644
--- a/DungeonShooting_Godot/src/game/activity/prop/Prop.cs
+++ b/DungeonShooting_Godot/src/game/activity/prop/Prop.cs
@@ -32,7 +32,7 @@
/// 触发扔掉道具效果, 并不会管道具是否在道具背包中
///
/// 触发扔掉该道具的的角色
- public void ThrowProp(AdvancedRole master)
+ public void ThrowProp(Role master)
{
ThrowProp(master, master.GlobalPosition);
}
diff --git a/DungeonShooting_Godot/src/game/activity/prop/active/ActiveProp5001.cs b/DungeonShooting_Godot/src/game/activity/prop/active/ActiveProp5001.cs
index eb4662c..2bac5e6 100644
--- a/DungeonShooting_Godot/src/game/activity/prop/active/ActiveProp5001.cs
+++ b/DungeonShooting_Godot/src/game/activity/prop/active/ActiveProp5001.cs
@@ -16,24 +16,16 @@
public override bool OnCheckUse()
{
- if (Master is AdvancedRole advancedRole)
- {
- return advancedRole.WeaponPack.ActiveItem != null && !advancedRole.WeaponPack.ActiveItem.IsAmmoFull();
- }
-
- return false;
+ return Master.WeaponPack.ActiveItem != null && !Master.WeaponPack.ActiveItem.IsAmmoFull();
}
protected override int OnUse()
{
- if (Master is AdvancedRole advancedRole)
+ var weapon = Master.WeaponPack.ActiveItem;
+ if (weapon != null)
{
- var weapon = advancedRole.WeaponPack.ActiveItem;
- if (weapon != null)
- {
- weapon.SetTotalAmmo(weapon.Attribute.MaxAmmoCapacity);
- return 1;
- }
+ weapon.SetTotalAmmo(weapon.Attribute.MaxAmmoCapacity);
+ return 1;
}
return 0;
diff --git a/DungeonShooting_Godot/src/game/activity/prop/buff/BuffProp0010.cs b/DungeonShooting_Godot/src/game/activity/prop/buff/BuffProp0010.cs
index 6e92d10..6254213 100644
--- a/DungeonShooting_Godot/src/game/activity/prop/buff/BuffProp0010.cs
+++ b/DungeonShooting_Godot/src/game/activity/prop/buff/BuffProp0010.cs
@@ -52,9 +52,8 @@
private void CalcBulletRepelEvent(float originRepel, RefValue repel)
{
- if (repel.Value < 0 || (Master is AdvancedRole advancedRole &&
- advancedRole.WeaponPack.ActiveItem != null &&
- advancedRole.WeaponPack.ActiveItem.Attribute.IsMelee))
+ if (repel.Value < 0 || (Master.WeaponPack.ActiveItem != null &&
+ Master.WeaponPack.ActiveItem.Attribute.IsMelee))
{
return;
}
diff --git a/DungeonShooting_Godot/src/game/activity/prop/buff/BuffProp0013.cs b/DungeonShooting_Godot/src/game/activity/prop/buff/BuffProp0013.cs
index 05e0202..e43270a 100644
--- a/DungeonShooting_Godot/src/game/activity/prop/buff/BuffProp0013.cs
+++ b/DungeonShooting_Godot/src/game/activity/prop/buff/BuffProp0013.cs
@@ -8,17 +8,11 @@
{
public override void OnPickUpItem()
{
- if (Master is AdvancedRole advancedRole)
- {
- advancedRole.WeaponPack.SetCapacity(advancedRole.WeaponPack.Capacity + 1);
- }
+ Master.WeaponPack.SetCapacity(Master.WeaponPack.Capacity + 1);
}
public override void OnRemoveItem()
{
- if (Master is AdvancedRole advancedRole)
- {
- advancedRole.WeaponPack.SetCapacity(advancedRole.WeaponPack.Capacity - 1);
- }
+ Master.WeaponPack.SetCapacity(Master.WeaponPack.Capacity - 1);
}
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/AdvancedRole.cs b/DungeonShooting_Godot/src/game/activity/role/AdvancedRole.cs
deleted file mode 100644
index c1350c1..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/AdvancedRole.cs
+++ /dev/null
@@ -1,407 +0,0 @@
-using Godot;
-
-///
-/// 高级角色,可以拾起和使用武器
-///
-public abstract partial class AdvancedRole : Role
-{
- ///
- /// 角色携带的武器背包
- ///
- public Package WeaponPack { get; private set; }
-
- ///
- /// 武器挂载点
- ///
- [Export, ExportFillNode]
- public MountRotation MountPoint { get; set; }
- ///
- /// 背后武器的挂载点
- ///
- [Export, ExportFillNode]
- public Marker2D BackMountPoint { get; set; }
-
- ///
- /// 近战碰撞检测区域
- ///
- [Export, ExportFillNode]
- public Area2D MeleeAttackArea { get; set; }
-
- ///
- /// 近战碰撞检测区域的碰撞器
- ///
- [Export, ExportFillNode]
- public CollisionPolygon2D MeleeAttackCollision { get; set; }
-
- ///
- /// 近战攻击时挥动武器的角度
- ///
- [Export]
- public float MeleeAttackAngle { get; set; } = 120;
-
- ///
- /// 武器挂载点是否始终指向目标
- ///
- public bool MountLookTarget { get; set; } = true;
-
- ///
- /// 是否处于近战攻击中
- ///
- public bool IsMeleeAttack { get; private set; }
-
- //近战计时器
- private float _meleeAttackTimer = 0;
-
- ///
- /// 当拾起某个武器时调用
- ///
- protected virtual void OnPickUpWeapon(Weapon weapon)
- {
- }
-
- ///
- /// 当扔掉某个武器时调用
- ///
- protected virtual void OnThrowWeapon(Weapon weapon)
- {
- }
-
- ///
- /// 当切换到某个武器时调用
- ///
- protected virtual void OnExchangeWeapon(Weapon weapon)
- {
- }
-
- public override void OnInit()
- {
- base.OnInit();
- WeaponPack = AddComponent>();
- WeaponPack.SetCapacity(4);
-
- MountPoint.Master = this;
-
- MeleeAttackCollision.Disabled = true;
- //切换武器回调
- WeaponPack.ChangeActiveItemEvent += OnChangeActiveItem;
- //近战区域进入物体
- MeleeAttackArea.BodyEntered += OnMeleeAttackBodyEntered;
- }
-
- protected override void Process(float delta)
- {
- if (IsDie)
- {
- return;
- }
-
- if (_meleeAttackTimer > 0)
- {
- _meleeAttackTimer -= delta;
- }
-
- base.Process(delta);
- }
-
- ///
- /// 当武器放到后背时调用, 用于设置武器位置和角度
- ///
- /// 武器实例
- /// 放入武器背包的位置
- public virtual void OnPutBackMount(Weapon weapon, int index)
- {
- if (index < 8)
- {
- if (index % 2 == 0)
- {
- weapon.Position = new Vector2(-4, 3);
- weapon.RotationDegrees = 90 - (index / 2f) * 20;
- weapon.Scale = new Vector2(-1, 1);
- }
- else
- {
- weapon.Position = new Vector2(4, 3);
- weapon.RotationDegrees = 270 + (index - 1) / 2f * 20;
- weapon.Scale = new Vector2(1, 1);
- }
- }
- else
- {
- weapon.Visible = false;
- }
- }
-
- protected override void OnAffiliationChange(AffiliationArea prevArea)
- {
- //身上的武器的所属区域也得跟着变
- WeaponPack.ForEach((weapon, i) =>
- {
- if (AffiliationArea != null)
- {
- AffiliationArea.InsertItem(weapon);
- }
- else if (weapon.AffiliationArea != null)
- {
- weapon.AffiliationArea.RemoveItem(weapon);
- }
- });
- }
-
- public override void LookTargetPosition(Vector2 pos)
- {
- LookPosition = pos;
- if (MountLookTarget)
- {
- //脸的朝向
- var gPos = Position;
- if (pos.X > gPos.X && Face == FaceDirection.Left)
- {
- Face = FaceDirection.Right;
- }
- else if (pos.X < gPos.X && Face == FaceDirection.Right)
- {
- Face = FaceDirection.Left;
- }
- //枪口跟随目标
- MountPoint.SetLookAt(pos);
- }
- }
-
- ///
- /// 返回所有武器是否弹药都打光了
- ///
- public bool IsAllWeaponTotalAmmoEmpty()
- {
- foreach (var weapon in WeaponPack.ItemSlot)
- {
- if (weapon != null && !weapon.IsTotalAmmoEmpty())
- {
- return false;
- }
- }
-
- return true;
- }
-
- //-------------------------------------------------------------------------------------
-
- ///
- /// 拾起一个武器, 返回是否成功拾起, 如果不想立刻切换到该武器, exchange 请传 false
- ///
- /// 武器对象
- /// 是否立即切换到该武器, 默认 true
- public bool PickUpWeapon(Weapon weapon, bool exchange = true)
- {
- if (WeaponPack.PickupItem(weapon, exchange) != -1)
- {
- //从可互动队列中移除
- InteractiveItemList.Remove(weapon);
- OnPickUpWeapon(weapon);
- return true;
- }
-
- return false;
- }
-
- ///
- /// 切换到下一个武器
- ///
- public void ExchangeNextWeapon()
- {
- var weapon = WeaponPack.ActiveItem;
- WeaponPack.ExchangeNext();
- if (WeaponPack.ActiveItem != weapon)
- {
- OnExchangeWeapon(WeaponPack.ActiveItem);
- }
- }
-
- ///
- /// 切换到上一个武器
- ///
- public void ExchangePrevWeapon()
- {
- var weapon = WeaponPack.ActiveItem;
- WeaponPack.ExchangePrev();
- if (WeaponPack.ActiveItem != weapon)
- {
- OnExchangeWeapon(WeaponPack.ActiveItem);
- }
- }
-
- ///
- /// 扔掉当前使用的武器, 切换到上一个武器
- ///
- public void ThrowWeapon()
- {
- ThrowWeapon(WeaponPack.ActiveIndex);
- }
-
- ///
- /// 扔掉指定位置的武器
- ///
- /// 武器在武器背包中的位置
- public void ThrowWeapon(int index)
- {
- var weapon = WeaponPack.GetItem(index);
- if (weapon == null)
- {
- return;
- }
-
- var temp = weapon.AnimatedSprite.Position;
- if (Face == FaceDirection.Left)
- {
- temp.Y = -temp.Y;
- }
- //var pos = GlobalPosition + temp.Rotated(weapon.GlobalRotation);
- WeaponPack.RemoveItem(index);
- //播放抛出效果
- weapon.ThrowWeapon(this, GlobalPosition);
- }
-
- ///
- /// 切换到下一个武器
- ///
- public void ExchangeNextActiveProp()
- {
- var prop = ActivePropsPack.ActiveItem;
- ActivePropsPack.ExchangeNext();
- if (prop != ActivePropsPack.ActiveItem)
- {
- OnExchangeActiveProp(ActivePropsPack.ActiveItem);
- }
- }
-
- ///
- /// 切换到上一个武器
- ///
- public void ExchangePrevActiveProp()
- {
- var prop = ActivePropsPack.ActiveItem;
- ActivePropsPack.ExchangePrev();
- if (prop != ActivePropsPack.ActiveItem)
- {
- OnExchangeActiveProp(ActivePropsPack.ActiveItem);
- }
- }
-
- //-------------------------------------------------------------------------------------
-
-
- ///
- /// 触发换弹
- ///
- public virtual void Reload()
- {
- if (WeaponPack.ActiveItem != null)
- {
- WeaponPack.ActiveItem.Reload();
- }
- }
-
- public override void Attack()
- {
- if (!IsMeleeAttack && WeaponPack.ActiveItem != null)
- {
- WeaponPack.ActiveItem.Trigger(this);
- }
- }
-
- ///
- /// 触发近战攻击
- ///
- public virtual void MeleeAttack()
- {
- if (IsMeleeAttack || _meleeAttackTimer > 0)
- {
- return;
- }
-
- if (WeaponPack.ActiveItem != null && WeaponPack.ActiveItem.Attribute.CanMeleeAttack)
- {
- IsMeleeAttack = true;
- _meleeAttackTimer = RoleState.MeleeAttackTime;
- MountLookTarget = false;
-
- //WeaponPack.ActiveItem.TriggerMeleeAttack(this);
- //播放近战动画
- PlayAnimation_MeleeAttack(() =>
- {
- MountLookTarget = true;
- IsMeleeAttack = false;
- });
- }
- }
-
- ///
- /// 切换当前使用的武器的回调
- ///
- private void OnChangeActiveItem(Weapon weapon)
- {
- //这里处理近战区域
- if (weapon != null)
- {
- MeleeAttackCollision.Polygon = Utils.CreateSectorPolygon(
- Utils.ConvertAngle(-MeleeAttackAngle / 2f),
- (weapon.GetLocalFirePosition() - weapon.GripPoint.Position).Length() * 1.2f,
- MeleeAttackAngle,
- 6
- );
- MeleeAttackArea.CollisionMask = AttackLayer | PhysicsLayer.Bullet;
- }
- }
-
- ///
- /// 近战区域碰到敌人
- ///
- private void OnMeleeAttackBodyEntered(Node2D body)
- {
- var activeWeapon = WeaponPack.ActiveItem;
- if (activeWeapon == null)
- {
- return;
- }
- var activityObject = body.AsActivityObject();
- if (activityObject != null)
- {
- if (activityObject is AdvancedRole role) //攻击角色
- {
- var damage = Utils.Random.RandomConfigRange(activeWeapon.Attribute.MeleeAttackHarmRange);
- damage = RoleState.CalcDamage(damage);
-
- //击退
- if (role is not Player) //目标不是玩家才会触发击退
- {
- var attr = IsAi ? activeWeapon.AiUseAttribute : activeWeapon.PlayerUseAttribute;
- var repel = Utils.Random.RandomConfigRange(attr.MeleeAttackRepelRange);
- var position = role.GlobalPosition - MountPoint.GlobalPosition;
- var v2 = position.Normalized() * repel;
- role.MoveController.AddForce(v2);
- }
-
- role.CallDeferred(nameof(Hurt), this, damage, (role.GetCenterPosition() - GlobalPosition).Angle());
- }
- else if (activityObject is Bullet bullet) //攻击子弹
- {
- var attackLayer = bullet.AttackLayer;
- if (CollisionWithMask(attackLayer)) //是攻击玩家的子弹
- {
- bullet.PlayDisappearEffect();
- bullet.Destroy();
- }
- }
- }
- }
-
- public override float GetFirePointAltitude()
- {
- return -MountPoint.Position.Y;
- }
-
- protected override void OnDestroy()
- {
- base.OnDestroy();
- WeaponPack.Destroy();
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/AdvancedRole_Animation.cs b/DungeonShooting_Godot/src/game/activity/role/AdvancedRole_Animation.cs
deleted file mode 100644
index e829a1f..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/AdvancedRole_Animation.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-
-using System;
-using Godot;
-using Vector2 = Godot.Vector2;
-
-public partial class AdvancedRole
-{
- ///
- /// 播放近战攻击动画
- ///
- public virtual void PlayAnimation_MeleeAttack(Action finish)
- {
- var r = MountPoint.RotationDegrees;
- //var gp = MountPoint.GlobalPosition;
- var p1 = MountPoint.Position;
- var p2 = p1 + new Vector2(6, 0).Rotated(Mathf.DegToRad(r - MeleeAttackAngle / 2f));
- var p3 = p1 + new Vector2(6, 0).Rotated(Mathf.DegToRad(r + MeleeAttackAngle / 2f));
-
- var tween = CreateTween();
- tween.SetParallel();
-
- tween.TweenProperty(MountPoint, "rotation_degrees", r - MeleeAttackAngle / 2f, 0.1);
- tween.TweenProperty(MountPoint, "position", p2, 0.1);
- tween.TweenProperty(MountPoint, "position", p2, 0.1);
- tween.Chain();
-
- tween.TweenCallback(Callable.From(() =>
- {
- MountPoint.RotationDegrees = r + MeleeAttackAngle / 2f;
- MountPoint.Position = p3;
- //重新计算武器阴影位置
- var activeItem = WeaponPack.ActiveItem;
- activeItem.CalcShadowTransform();
- //创建屏幕抖动
- if (Face == FaceDirection.Right)
- {
- //GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(r - 90)) * 5);
- GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(r - 180)) * 6);
- }
- else
- {
- //GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(270 - r)) * 5);
- GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(-r)) * 6);
- }
- //播放特效
- var effect = ObjectManager.GetPoolItem(ResourcePath.prefab_effect_weapon_MeleeAttack1_tscn);
- var sprite = (Node2D)effect;
- var localFirePosition = activeItem.GetLocalFirePosition() - activeItem.GripPoint.Position;
- localFirePosition *= 0.9f;
- sprite.Position = p1 + localFirePosition.Rotated(Mathf.DegToRad(r));
- sprite.RotationDegrees = r;
- AddChild(sprite);
- effect.PlayEffect();
-
- //启用近战碰撞区域
- MeleeAttackCollision.Disabled = false;
- }));
- tween.Chain();
-
- tween.TweenInterval(0.1f);
- tween.Chain();
-
- tween.TweenCallback(Callable.From(() =>
- {
- //关闭近战碰撞区域
- MeleeAttackCollision.Disabled = true;
- }));
- tween.TweenProperty(MountPoint, "rotation_degrees", r, 0.2);
- tween.TweenProperty(MountPoint, "position", p1, 0.2);
- tween.Chain();
-
- tween.TweenCallback(Callable.From(() =>
- {
- finish();
- }));
- tween.Play();
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/MountRotation.cs b/DungeonShooting_Godot/src/game/activity/role/MountRotation.cs
index eca09e9..e265b10 100644
--- a/DungeonShooting_Godot/src/game/activity/role/MountRotation.cs
+++ b/DungeonShooting_Godot/src/game/activity/role/MountRotation.cs
@@ -15,7 +15,7 @@
///
/// 所在的角色
///
- public AdvancedRole Master { get; set; }
+ public Role Master { get; set; }
///
/// 当前节点真实的旋转角度, 角度制
diff --git a/DungeonShooting_Godot/src/game/activity/role/Role.cs b/DungeonShooting_Godot/src/game/activity/role/Role.cs
index fcea8b0..0a22232 100644
--- a/DungeonShooting_Godot/src/game/activity/role/Role.cs
+++ b/DungeonShooting_Godot/src/game/activity/role/Role.cs
@@ -95,6 +95,54 @@
///
public FaceDirection Face { get => _face; set => SetFace(value); }
private FaceDirection _face;
+
+ ///
+ /// 角色携带的武器背包
+ ///
+ public Package WeaponPack { get; private set; }
+
+ ///
+ /// 武器挂载点
+ ///
+ [Export, ExportFillNode]
+ public MountRotation MountPoint { get; set; }
+
+ ///
+ /// 背后武器的挂载点
+ ///
+ [Export, ExportFillNode]
+ public Marker2D BackMountPoint { get; set; }
+
+ ///
+ /// 近战碰撞检测区域
+ ///
+ [Export, ExportFillNode]
+ public Area2D MeleeAttackArea { get; set; }
+
+ ///
+ /// 近战碰撞检测区域的碰撞器
+ ///
+ [Export, ExportFillNode]
+ public CollisionPolygon2D MeleeAttackCollision { get; set; }
+
+ ///
+ /// 近战攻击时挥动武器的角度
+ ///
+ [Export]
+ public float MeleeAttackAngle { get; set; } = 120;
+
+ ///
+ /// 武器挂载点是否始终指向目标
+ ///
+ public bool MountLookTarget { get; set; } = true;
+
+ ///
+ /// 是否处于近战攻击中
+ ///
+ public bool IsMeleeAttack { get; private set; }
+
+ //近战计时器
+ private float _meleeAttackTimer = 0;
///
/// 是否死亡
@@ -372,14 +420,6 @@
{
}
-
- ///
- /// 触发攻击
- ///
- public virtual void Attack()
- {
- }
-
public override void OnInit()
{
ActivePropsPack = AddComponent>();
@@ -396,10 +436,33 @@
//连接互动物体信号
InteractiveArea.BodyEntered += _OnPropsEnter;
InteractiveArea.BodyExited += _OnPropsExit;
+
+ //------------------------
+
+ WeaponPack = AddComponent>();
+ WeaponPack.SetCapacity(4);
+
+ MountPoint.Master = this;
+
+ MeleeAttackCollision.Disabled = true;
+ //切换武器回调
+ WeaponPack.ChangeActiveItemEvent += OnChangeActiveItem;
+ //近战区域进入物体
+ MeleeAttackArea.BodyEntered += OnMeleeAttackBodyEntered;
}
protected override void Process(float delta)
- {
+ {
+ if (IsDie)
+ {
+ return;
+ }
+
+ if (_meleeAttackTimer > 0)
+ {
+ _meleeAttackTimer -= delta;
+ }
+
//检查可互动的物体
bool findFlag = false;
for (int i = 0; i < InteractiveItemList.Count; i++)
@@ -541,24 +604,6 @@
}
///
- /// 使角色看向指定的坐标的方向
- ///
- public virtual void LookTargetPosition(Vector2 pos)
- {
- LookPosition = pos;
- //脸的朝向
- var gPos = Position;
- if (pos.X > gPos.X && Face == FaceDirection.Left)
- {
- Face = FaceDirection.Right;
- }
- else if (pos.X < gPos.X && Face == FaceDirection.Right)
- {
- Face = FaceDirection.Left;
- }
- }
-
- ///
/// 判断指定坐标是否在角色视野方向
///
public bool IsPositionInForward(Vector2 pos)
@@ -884,7 +929,322 @@
///
public virtual float GetFirePointAltitude()
{
- return -AnimatedSprite.Position.Y;
+ return -MountPoint.Position.Y;
+ }
+
+ ///
+ /// 当拾起某个武器时调用
+ ///
+ protected virtual void OnPickUpWeapon(Weapon weapon)
+ {
+ }
+
+ ///
+ /// 当扔掉某个武器时调用
+ ///
+ protected virtual void OnThrowWeapon(Weapon weapon)
+ {
+ }
+
+ ///
+ /// 当切换到某个武器时调用
+ ///
+ protected virtual void OnExchangeWeapon(Weapon weapon)
+ {
+ }
+
+ ///
+ /// 当武器放到后背时调用, 用于设置武器位置和角度
+ ///
+ /// 武器实例
+ /// 放入武器背包的位置
+ public virtual void OnPutBackMount(Weapon weapon, int index)
+ {
+ if (index < 8)
+ {
+ if (index % 2 == 0)
+ {
+ weapon.Position = new Vector2(-4, 3);
+ weapon.RotationDegrees = 90 - (index / 2f) * 20;
+ weapon.Scale = new Vector2(-1, 1);
+ }
+ else
+ {
+ weapon.Position = new Vector2(4, 3);
+ weapon.RotationDegrees = 270 + (index - 1) / 2f * 20;
+ weapon.Scale = new Vector2(1, 1);
+ }
+ }
+ else
+ {
+ weapon.Visible = false;
+ }
+ }
+
+ protected override void OnAffiliationChange(AffiliationArea prevArea)
+ {
+ //身上的武器的所属区域也得跟着变
+ WeaponPack.ForEach((weapon, i) =>
+ {
+ if (AffiliationArea != null)
+ {
+ AffiliationArea.InsertItem(weapon);
+ }
+ else if (weapon.AffiliationArea != null)
+ {
+ weapon.AffiliationArea.RemoveItem(weapon);
+ }
+ });
+ }
+
+ public virtual void LookTargetPosition(Vector2 pos)
+ {
+ LookPosition = pos;
+ if (MountLookTarget)
+ {
+ //脸的朝向
+ var gPos = Position;
+ if (pos.X > gPos.X && Face == FaceDirection.Left)
+ {
+ Face = FaceDirection.Right;
+ }
+ else if (pos.X < gPos.X && Face == FaceDirection.Right)
+ {
+ Face = FaceDirection.Left;
+ }
+ //枪口跟随目标
+ MountPoint.SetLookAt(pos);
+ }
+ }
+
+ ///
+ /// 返回所有武器是否弹药都打光了
+ ///
+ public bool IsAllWeaponTotalAmmoEmpty()
+ {
+ foreach (var weapon in WeaponPack.ItemSlot)
+ {
+ if (weapon != null && !weapon.IsTotalAmmoEmpty())
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //-------------------------------------------------------------------------------------
+
+ ///
+ /// 拾起一个武器, 返回是否成功拾起, 如果不想立刻切换到该武器, exchange 请传 false
+ ///
+ /// 武器对象
+ /// 是否立即切换到该武器, 默认 true
+ public bool PickUpWeapon(Weapon weapon, bool exchange = true)
+ {
+ if (WeaponPack.PickupItem(weapon, exchange) != -1)
+ {
+ //从可互动队列中移除
+ InteractiveItemList.Remove(weapon);
+ OnPickUpWeapon(weapon);
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// 切换到下一个武器
+ ///
+ public void ExchangeNextWeapon()
+ {
+ var weapon = WeaponPack.ActiveItem;
+ WeaponPack.ExchangeNext();
+ if (WeaponPack.ActiveItem != weapon)
+ {
+ OnExchangeWeapon(WeaponPack.ActiveItem);
+ }
+ }
+
+ ///
+ /// 切换到上一个武器
+ ///
+ public void ExchangePrevWeapon()
+ {
+ var weapon = WeaponPack.ActiveItem;
+ WeaponPack.ExchangePrev();
+ if (WeaponPack.ActiveItem != weapon)
+ {
+ OnExchangeWeapon(WeaponPack.ActiveItem);
+ }
+ }
+
+ ///
+ /// 扔掉当前使用的武器, 切换到上一个武器
+ ///
+ public void ThrowWeapon()
+ {
+ ThrowWeapon(WeaponPack.ActiveIndex);
+ }
+
+ ///
+ /// 扔掉指定位置的武器
+ ///
+ /// 武器在武器背包中的位置
+ public void ThrowWeapon(int index)
+ {
+ var weapon = WeaponPack.GetItem(index);
+ if (weapon == null)
+ {
+ return;
+ }
+
+ var temp = weapon.AnimatedSprite.Position;
+ if (Face == FaceDirection.Left)
+ {
+ temp.Y = -temp.Y;
+ }
+ //var pos = GlobalPosition + temp.Rotated(weapon.GlobalRotation);
+ WeaponPack.RemoveItem(index);
+ //播放抛出效果
+ weapon.ThrowWeapon(this, GlobalPosition);
+ }
+
+ ///
+ /// 切换到下一个武器
+ ///
+ public void ExchangeNextActiveProp()
+ {
+ var prop = ActivePropsPack.ActiveItem;
+ ActivePropsPack.ExchangeNext();
+ if (prop != ActivePropsPack.ActiveItem)
+ {
+ OnExchangeActiveProp(ActivePropsPack.ActiveItem);
+ }
+ }
+
+ ///
+ /// 切换到上一个武器
+ ///
+ public void ExchangePrevActiveProp()
+ {
+ var prop = ActivePropsPack.ActiveItem;
+ ActivePropsPack.ExchangePrev();
+ if (prop != ActivePropsPack.ActiveItem)
+ {
+ OnExchangeActiveProp(ActivePropsPack.ActiveItem);
+ }
+ }
+
+ //-------------------------------------------------------------------------------------
+
+
+ ///
+ /// 触发换弹
+ ///
+ public virtual void Reload()
+ {
+ if (WeaponPack.ActiveItem != null)
+ {
+ WeaponPack.ActiveItem.Reload();
+ }
+ }
+
+ ///
+ /// 攻击函数
+ ///
+ public virtual void Attack()
+ {
+ if (!IsMeleeAttack && WeaponPack.ActiveItem != null)
+ {
+ WeaponPack.ActiveItem.Trigger(this);
+ }
+ }
+
+ ///
+ /// 触发近战攻击
+ ///
+ public virtual void MeleeAttack()
+ {
+ if (IsMeleeAttack || _meleeAttackTimer > 0)
+ {
+ return;
+ }
+
+ if (WeaponPack.ActiveItem != null && WeaponPack.ActiveItem.Attribute.CanMeleeAttack)
+ {
+ IsMeleeAttack = true;
+ _meleeAttackTimer = RoleState.MeleeAttackTime;
+ MountLookTarget = false;
+
+ //WeaponPack.ActiveItem.TriggerMeleeAttack(this);
+ //播放近战动画
+ PlayAnimation_MeleeAttack(() =>
+ {
+ MountLookTarget = true;
+ IsMeleeAttack = false;
+ });
+ }
+ }
+
+ ///
+ /// 切换当前使用的武器的回调
+ ///
+ private void OnChangeActiveItem(Weapon weapon)
+ {
+ //这里处理近战区域
+ if (weapon != null)
+ {
+ MeleeAttackCollision.Polygon = Utils.CreateSectorPolygon(
+ Utils.ConvertAngle(-MeleeAttackAngle / 2f),
+ (weapon.GetLocalFirePosition() - weapon.GripPoint.Position).Length() * 1.2f,
+ MeleeAttackAngle,
+ 6
+ );
+ MeleeAttackArea.CollisionMask = AttackLayer | PhysicsLayer.Bullet;
+ }
+ }
+
+ ///
+ /// 近战区域碰到敌人
+ ///
+ private void OnMeleeAttackBodyEntered(Node2D body)
+ {
+ var activeWeapon = WeaponPack.ActiveItem;
+ if (activeWeapon == null)
+ {
+ return;
+ }
+ var activityObject = body.AsActivityObject();
+ if (activityObject != null)
+ {
+ if (activityObject is Role role) //攻击角色
+ {
+ var damage = Utils.Random.RandomConfigRange(activeWeapon.Attribute.MeleeAttackHarmRange);
+ damage = RoleState.CalcDamage(damage);
+
+ //击退
+ if (role is not Player) //目标不是玩家才会触发击退
+ {
+ var attr = IsAi ? activeWeapon.AiUseAttribute : activeWeapon.PlayerUseAttribute;
+ var repel = Utils.Random.RandomConfigRange(attr.MeleeAttackRepelRange);
+ var position = role.GlobalPosition - MountPoint.GlobalPosition;
+ var v2 = position.Normalized() * repel;
+ role.MoveController.AddForce(v2);
+ }
+
+ role.CallDeferred(nameof(Hurt), this, damage, (role.GetCenterPosition() - GlobalPosition).Angle());
+ }
+ else if (activityObject is Bullet bullet) //攻击子弹
+ {
+ var attackLayer = bullet.AttackLayer;
+ if (CollisionWithMask(attackLayer)) //是攻击玩家的子弹
+ {
+ bullet.PlayDisappearEffect();
+ bullet.Destroy();
+ }
+ }
+ }
}
protected override void OnDestroy()
@@ -896,5 +1256,7 @@
}
BuffPropPack.Clear();
ActivePropsPack.Destroy();
+
+ WeaponPack.Destroy();
}
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/Role_Animation.cs b/DungeonShooting_Godot/src/game/activity/role/Role_Animation.cs
new file mode 100644
index 0000000..1dfcc68
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/role/Role_Animation.cs
@@ -0,0 +1,78 @@
+
+using System;
+using Godot;
+using Vector2 = Godot.Vector2;
+
+public partial class Role
+{
+ ///
+ /// 播放近战攻击动画
+ ///
+ public virtual void PlayAnimation_MeleeAttack(Action finish)
+ {
+ var r = MountPoint.RotationDegrees;
+ //var gp = MountPoint.GlobalPosition;
+ var p1 = MountPoint.Position;
+ var p2 = p1 + new Vector2(6, 0).Rotated(Mathf.DegToRad(r - MeleeAttackAngle / 2f));
+ var p3 = p1 + new Vector2(6, 0).Rotated(Mathf.DegToRad(r + MeleeAttackAngle / 2f));
+
+ var tween = CreateTween();
+ tween.SetParallel();
+
+ tween.TweenProperty(MountPoint, "rotation_degrees", r - MeleeAttackAngle / 2f, 0.1);
+ tween.TweenProperty(MountPoint, "position", p2, 0.1);
+ tween.TweenProperty(MountPoint, "position", p2, 0.1);
+ tween.Chain();
+
+ tween.TweenCallback(Callable.From(() =>
+ {
+ MountPoint.RotationDegrees = r + MeleeAttackAngle / 2f;
+ MountPoint.Position = p3;
+ //重新计算武器阴影位置
+ var activeItem = WeaponPack.ActiveItem;
+ activeItem.CalcShadowTransform();
+ //创建屏幕抖动
+ if (Face == FaceDirection.Right)
+ {
+ //GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(r - 90)) * 5);
+ GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(r - 180)) * 6);
+ }
+ else
+ {
+ //GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(270 - r)) * 5);
+ GameCamera.Main.DirectionalShake(Vector2.FromAngle(Mathf.DegToRad(-r)) * 6);
+ }
+ //播放特效
+ var effect = ObjectManager.GetPoolItem(ResourcePath.prefab_effect_weapon_MeleeAttack1_tscn);
+ var sprite = (Node2D)effect;
+ var localFirePosition = activeItem.GetLocalFirePosition() - activeItem.GripPoint.Position;
+ localFirePosition *= 0.9f;
+ sprite.Position = p1 + localFirePosition.Rotated(Mathf.DegToRad(r));
+ sprite.RotationDegrees = r;
+ AddChild(sprite);
+ effect.PlayEffect();
+
+ //启用近战碰撞区域
+ MeleeAttackCollision.Disabled = false;
+ }));
+ tween.Chain();
+
+ tween.TweenInterval(0.1f);
+ tween.Chain();
+
+ tween.TweenCallback(Callable.From(() =>
+ {
+ //关闭近战碰撞区域
+ MeleeAttackCollision.Disabled = true;
+ }));
+ tween.TweenProperty(MountPoint, "rotation_degrees", r, 0.2);
+ tween.TweenProperty(MountPoint, "position", p1, 0.2);
+ tween.Chain();
+
+ tween.TweenCallback(Callable.From(() =>
+ {
+ finish();
+ }));
+ tween.Play();
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/SubLine.cs b/DungeonShooting_Godot/src/game/activity/role/SubLine.cs
index 03039ac..5f9dd84 100644
--- a/DungeonShooting_Godot/src/game/activity/role/SubLine.cs
+++ b/DungeonShooting_Godot/src/game/activity/role/SubLine.cs
@@ -5,7 +5,7 @@
///
/// 瞄准辅助线
///
-public class SubLine : Component
+public class SubLine : Component
{
///
/// 是否正在播放警告闪烁动画
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/AIStateEnum.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/AIStateEnum.cs
new file mode 100644
index 0000000..9b923d5
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/role/enemy/AIStateEnum.cs
@@ -0,0 +1,40 @@
+
+public enum AIStateEnum
+{
+ ///
+ /// Ai 状态, 正常, 未发现目标
+ ///
+ AiNormal,
+ ///
+ /// 找到玩家,准备通知其他敌人
+ ///
+ AiNotify,
+ ///
+ /// 惊讶状态
+ ///
+ AiAstonished,
+ ///
+ /// 收到其他敌人通知, 前往发现目标的位置
+ ///
+ AiLeaveFor,
+ ///
+ /// 发现目标, 目标不在视野内, 但是知道位置
+ ///
+ AiTailAfter,
+ ///
+ /// 目标在视野内, 跟进目标, 如果距离在子弹有效射程内, 则开火
+ ///
+ AiFollowUp,
+ ///
+ /// 距离足够近, 在目标附近随机移动
+ ///
+ AiSurround,
+ ///
+ /// Ai 寻找弹药
+ ///
+ AiFindAmmo,
+ ///
+ /// Ai攻击
+ ///
+ AiAttack,
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/AdvancedEnemy.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/AdvancedEnemy.cs
deleted file mode 100644
index b2d184d..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/AdvancedEnemy.cs
+++ /dev/null
@@ -1,467 +0,0 @@
-#region 基础敌人设计思路
-/*
-敌人有三种状态:
-状态1: 未发现玩家, 视野不可穿墙, 该状态下敌人移动比较规律, 移动速度较慢, 一旦玩家进入视野或者听到玩家枪声, 立刻切换至状态3, 该房间的敌人不能再回到状态1
-状态2: 发现有玩家, 但不知道在哪, 视野不可穿墙, 该情况下敌人移动速度明显加快, 移动不规律, 一旦玩家进入视野或者听到玩家枪声, 立刻切换至状态3
-状态3: 明确知道玩家的位置, 视野允许穿墙, 移动速度与状态2一致, 进入该状态时, 敌人之间会相互告知玩家所在位置, 并朝着玩家位置开火,
- 如果有墙格挡, 则有一定概率继续开火, 一旦玩家立刻敌人视野超哥一段时间, 敌人自动切换为状态2
-
-敌人状态1只存在于少数房间内, 比如特殊房间, 大部分情况下敌人应该是状态2, 或者玩家进入房间时就被敌人发现
-*/
-#endregion
-
-
-using System;
-using AdvancedState;
-using Godot;
-
-///
-/// 高级敌人,可以携带武器
-///
-[Tool]
-public partial class AdvancedEnemy : AdvancedRole
-{
- ///
- /// 目标是否在视野内
- ///
- public bool TargetInView { get; set; } = true;
-
- ///
- /// 敌人身上的状态机控制器
- ///
- public StateController StateController { get; private set; }
-
- ///
- /// 视野半径, 单位像素, 发现玩家后改视野范围可以穿墙
- ///
- public float ViewRange { get; set; } = 250;
-
- ///
- /// 发现玩家后的视野半径
- ///
- public float TailAfterViewRange { get; set; } = 400;
-
- ///
- /// 背后的视野半径, 单位像素
- ///
- public float BackViewRange { get; set; } = 50;
-
- ///
- /// 视野检测射线, 朝玩家打射线, 检测是否碰到墙
- ///
- [Export, ExportFillNode]
- public RayCast2D ViewRay { get; private set; }
-
- ///
- /// 导航代理
- ///
- [Export, ExportFillNode]
- public NavigationAgent2D NavigationAgent2D { get; private set; }
-
- ///
- /// 导航代理中点
- ///
- [Export, ExportFillNode]
- public Marker2D NavigationPoint { get; private set; }
-
- ///
- /// 当前敌人所看向的对象, 也就是枪口指向的对象
- ///
- public ActivityObject LookTarget { get; set; }
-
- ///
- /// 锁定目标已经走过的时间
- ///
- public float LockTargetTime { get; set; } = 0;
-
- public override void OnInit()
- {
- base.OnInit();
- IsAi = true;
- StateController = AddComponent>();
-
- AttackLayer = PhysicsLayer.Wall | PhysicsLayer.Player;
- EnemyLayer = PhysicsLayer.Player;
- Camp = CampEnum.Camp2;
-
- RoleState.MoveSpeed = 20;
-
- MaxHp = 20;
- Hp = 20;
-
- //PathSign = new PathSign(this, PathSignLength, GameApplication.Instance.Node3D.Player);
-
- //注册Ai状态机
- StateController.Register(new AiNormalState());
- //StateController.Register(new AiProbeState());
- StateController.Register(new AiTailAfterState());
- StateController.Register(new AiFollowUpState());
- StateController.Register(new AiLeaveForState());
- StateController.Register(new AiSurroundState());
- StateController.Register(new AiFindAmmoState());
- StateController.Register(new AiAttackState());
- StateController.Register(new AiAstonishedState());
- StateController.Register(new AiNotifyState());
-
- //默认状态
- StateController.ChangeStateInstant(AIAdvancedStateEnum.AiNormal);
- }
-
- public override void EnterTree()
- {
- if (!World.Enemy_InstanceList.Contains(this))
- {
- World.Enemy_InstanceList.Add(this);
- }
- }
-
- public override void ExitTree()
- {
- World.Enemy_InstanceList.Remove(this);
- }
-
- protected override void OnDie()
- {
- //扔掉所有武器
- var weapons = WeaponPack.GetAndClearItem();
- for (var i = 0; i < weapons.Length; i++)
- {
- weapons[i].ThrowWeapon(this);
- }
-
- var effPos = Position + new Vector2(0, -Altitude);
- //血液特效
- var blood = ObjectManager.GetPoolItem(ResourcePath.prefab_effect_enemy_EnemyBloodEffect_tscn);
- blood.Position = effPos - new Vector2(0, 12);
- blood.AddToActivityRoot(RoomLayerEnum.NormalLayer);
- blood.PlayEffect();
-
- //创建敌人碎片
- var count = Utils.Random.RandomRangeInt(3, 6);
- for (var i = 0; i < count; i++)
- {
- var debris = Create(Ids.Id_effect0001);
- debris.PutDown(effPos, RoomLayerEnum.NormalLayer);
- debris.InheritVelocity(this);
- }
-
- //派发敌人死亡信号
- EventManager.EmitEvent(EventEnum.OnEnemyDie, this);
- Destroy();
- }
-
- protected override void Process(float delta)
- {
- base.Process(delta);
- if (IsDie)
- {
- return;
- }
-
- //看向目标
- if (LookTarget != null && MountLookTarget)
- {
- var pos = LookTarget.Position;
- LookPosition = pos;
- //脸的朝向
- var gPos = Position;
- if (pos.X > gPos.X && Face == FaceDirection.Left)
- {
- Face = FaceDirection.Right;
- }
- else if (pos.X < gPos.X && Face == FaceDirection.Right)
- {
- Face = FaceDirection.Left;
- }
- //枪口跟随目标
- MountPoint.SetLookAt(pos);
- }
-
- //拾起武器操作
- EnemyPickUpWeapon();
- }
-
- protected override void OnHit(ActivityObject target, int damage, float angle, bool realHarm)
- {
- //受到伤害
- var state = StateController.CurrState;
- if (state == AIAdvancedStateEnum.AiNormal || state == AIAdvancedStateEnum.AiLeaveFor) //|| state == AiStateEnum.AiProbe
- {
- LookTarget = target;
- StateController.ChangeState(AIAdvancedStateEnum.AiTailAfter);
- }
- }
-
- ///
- /// 返回地上的武器是否有可以拾取的, 也包含没有被其他敌人标记的武器
- ///
- public bool CheckUsableWeaponInUnclaimed()
- {
- foreach (var unclaimedWeapon in World.Weapon_UnclaimedWeapons)
- {
- //判断是否能拾起武器, 条件: 相同的房间
- if (unclaimedWeapon.AffiliationArea == AffiliationArea)
- {
- if (!unclaimedWeapon.IsTotalAmmoEmpty())
- {
- if (!unclaimedWeapon.HasSign(SignNames.AiFindWeaponSign))
- {
- return true;
- }
- else
- {
- //判断是否可以移除该标记
- var enemy = unclaimedWeapon.GetSign(SignNames.AiFindWeaponSign);
- if (enemy == null || enemy.IsDestroyed) //标记当前武器的敌人已经被销毁
- {
- unclaimedWeapon.RemoveSign(SignNames.AiFindWeaponSign);
- return true;
- }
- else if (!enemy.IsAllWeaponTotalAmmoEmpty()) //标记当前武器的敌人已经有新的武器了
- {
- unclaimedWeapon.RemoveSign(SignNames.AiFindWeaponSign);
- return true;
- }
- }
- }
- }
- }
-
- return false;
- }
-
- ///
- /// 寻找可用的武器
- ///
- public Weapon FindTargetWeapon()
- {
- Weapon target = null;
- var position = Position;
- foreach (var weapon in World.Weapon_UnclaimedWeapons)
- {
- //判断是否能拾起武器, 条件: 相同的房间, 或者当前房间目前没有战斗, 或者不在战斗房间
- if (weapon.AffiliationArea == AffiliationArea)
- {
- //还有弹药
- if (!weapon.IsTotalAmmoEmpty())
- {
- //查询是否有其他敌人标记要拾起该武器
- if (weapon.HasSign(SignNames.AiFindWeaponSign))
- {
- var enemy = weapon.GetSign(SignNames.AiFindWeaponSign);
- if (enemy == this) //就是自己标记的
- {
-
- }
- else if (enemy == null || enemy.IsDestroyed) //标记当前武器的敌人已经被销毁
- {
- weapon.RemoveSign(SignNames.AiFindWeaponSign);
- }
- else if (!enemy.IsAllWeaponTotalAmmoEmpty()) //标记当前武器的敌人已经有新的武器了
- {
- weapon.RemoveSign(SignNames.AiFindWeaponSign);
- }
- else //放弃这把武器
- {
- continue;
- }
- }
-
- if (target == null) //第一把武器
- {
- target = weapon;
- }
- else if (target.Position.DistanceSquaredTo(position) >
- weapon.Position.DistanceSquaredTo(position)) //距离更近
- {
- target = weapon;
- }
- }
- }
- }
-
- return target;
- }
-
- ///
- /// 获取武器攻击范围 (最大距离值与最小距离的中间值)
- ///
- /// 从最小到最大距离的过渡量, 0 - 1, 默认 0.5
- public float GetWeaponRange(float weight = 0.5f)
- {
- if (WeaponPack.ActiveItem != null)
- {
- var attribute = WeaponPack.ActiveItem.Attribute;
- return Mathf.Lerp(Utils.GetConfigRangeStart(attribute.Bullet.DistanceRange), Utils.GetConfigRangeEnd(attribute.Bullet.DistanceRange), weight);
- }
-
- return 0;
- }
-
- ///
- /// 返回目标点是否在视野范围内
- ///
- public bool IsInViewRange(Vector2 target)
- {
- var isForward = IsPositionInForward(target);
- if (isForward)
- {
- if (GlobalPosition.DistanceSquaredTo(target) <= ViewRange * ViewRange) //没有超出视野半径
- {
- return true;
- }
- }
-
- return false;
- }
-
- ///
- /// 返回目标点是否在跟随状态下的视野半径内
- ///
- public bool IsInTailAfterViewRange(Vector2 target)
- {
- var isForward = IsPositionInForward(target);
- if (isForward)
- {
- if (GlobalPosition.DistanceSquaredTo(target) <= TailAfterViewRange * TailAfterViewRange) //没有超出视野半径
- {
- return true;
- }
- }
-
- return false;
- }
-
- ///
- /// 调用视野检测, 如果被墙壁和其它物体遮挡, 则返回true
- ///
- public bool TestViewRayCast(Vector2 target)
- {
- ViewRay.Enabled = true;
- ViewRay.TargetPosition = ViewRay.ToLocal(target);
- ViewRay.ForceRaycastUpdate();
- return ViewRay.IsColliding();
- }
-
- ///
- /// 调用视野检测完毕后, 需要调用 TestViewRayCastOver() 来关闭视野检测射线
- ///
- public void TestViewRayCastOver()
- {
- ViewRay.Enabled = false;
- }
-
- ///
- /// AI 拾起武器操作
- ///
- private void EnemyPickUpWeapon()
- {
- //这几个状态不需要主动拾起武器操作
- var state = StateController.CurrState;
- if (state == AIAdvancedStateEnum.AiNormal)
- {
- return;
- }
-
- //拾起地上的武器
- if (InteractiveItem is Weapon weapon)
- {
- if (WeaponPack.ActiveItem == null) //手上没有武器, 无论如何也要拾起
- {
- TriggerInteractive();
- return;
- }
-
- //没弹药了
- if (weapon.IsTotalAmmoEmpty())
- {
- return;
- }
-
- var index = WeaponPack.FindIndex((we, i) => we.ActivityBase.Id == weapon.ActivityBase.Id);
- if (index != -1) //与武器背包中武器类型相同, 补充子弹
- {
- if (!WeaponPack.GetItem(index).IsAmmoFull())
- {
- TriggerInteractive();
- }
-
- return;
- }
-
- // var index2 = Holster.FindWeapon((we, i) =>
- // we.Attribute.WeightType == weapon.Attribute.WeightType && we.IsTotalAmmoEmpty());
- var index2 = WeaponPack.FindIndex((we, i) => we.IsTotalAmmoEmpty());
- if (index2 != -1) //扔掉没子弹的武器
- {
- ThrowWeapon(index2);
- TriggerInteractive();
- return;
- }
-
- // if (Holster.HasVacancy()) //有空位, 拾起武器
- // {
- // TriggerInteractive();
- // return;
- // }
- }
- }
-
- ///
- /// 获取锁定目标的剩余时间
- ///
- public float GetLockRemainderTime()
- {
- var weapon = WeaponPack.ActiveItem;
- if (weapon == null)
- {
- return 0;
- }
- return weapon.Attribute.AiAttackAttr.LockingTime - LockTargetTime;
- }
-
- ///
- /// 强制设置锁定目标时间
- ///
- public void SetLockTargetTime(float time)
- {
- LockTargetTime = time;
- }
-
- public override void LookTargetPosition(Vector2 pos)
- {
- LookTarget = null;
- base.LookTargetPosition(pos);
- }
-
- ///
- /// 执行移动操作
- ///
- public void DoMove()
- {
- AnimatedSprite.Play(AnimatorNames.Run);
- //计算移动
- var nextPos = NavigationAgent2D.GetNextPathPosition();
- BasisVelocity = (nextPos - Position - NavigationPoint.Position).Normalized() * RoleState.MoveSpeed;
- }
-
- ///
- /// 执行站立操作
- ///
- public void DoIdle()
- {
- AnimatedSprite.Play(AnimatorNames.Idle);
- BasisVelocity = Vector2.Zero;
- }
-
- ///
- /// 更新房间中标记的目标位置
- ///
- public void UpdateMarkTargetPosition()
- {
- if (LookTarget != null)
- {
- AffiliationArea.RoomInfo.MarkTargetPosition[LookTarget.Id] = LookTarget.Position;
- }
- }
-}
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs
index 1513d7c..3bb3691 100644
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs
+++ b/DungeonShooting_Godot/src/game/activity/role/enemy/Enemy.cs
@@ -1,11 +1,21 @@
-
-using System;
-using Config;
+#region 基础敌人设计思路
+/*
+敌人有三种状态:
+状态1: 未发现玩家, 视野不可穿墙, 该状态下敌人移动比较规律, 移动速度较慢, 一旦玩家进入视野或者听到玩家枪声, 立刻切换至状态3, 该房间的敌人不能再回到状态1
+状态2: 发现有玩家, 但不知道在哪, 视野不可穿墙, 该情况下敌人移动速度明显加快, 移动不规律, 一旦玩家进入视野或者听到玩家枪声, 立刻切换至状态3
+状态3: 明确知道玩家的位置, 视野允许穿墙, 移动速度与状态2一致, 进入该状态时, 敌人之间会相互告知玩家所在位置, 并朝着玩家位置开火,
+ 如果有墙格挡, 则有一定概率继续开火, 一旦玩家立刻敌人视野超哥一段时间, 敌人自动切换为状态2
+
+敌人状态1只存在于少数房间内, 比如特殊房间, 大部分情况下敌人应该是状态2, 或者玩家进入房间时就被敌人发现
+*/
+#endregion
+
+
+using EnemyState;
using Godot;
-using NnormalState;
///
-/// 基础敌人
+/// 高级敌人,可以携带武器
///
[Tool]
public partial class Enemy : Role
@@ -18,7 +28,7 @@
///
/// 敌人身上的状态机控制器
///
- public StateController StateController { get; private set; }
+ public StateController StateController { get; private set; }
///
/// 视野半径, 单位像素, 发现玩家后改视野范围可以穿墙
@@ -36,11 +46,6 @@
public float BackViewRange { get; set; } = 50;
///
- /// 攻击范围
- ///
- public float AttackRange { get; set; } = 200;
-
- ///
/// 视野检测射线, 朝玩家打射线, 检测是否碰到墙
///
[Export, ExportFillNode]
@@ -59,37 +64,20 @@
public Marker2D NavigationPoint { get; private set; }
///
- /// 开火位置
- ///
- [Export, ExportFillNode]
- public Marker2D FirePoint { get; private set; }
-
- ///
- /// 攻击时间间隔
- ///
- public float AttackInterval { get; set; } = 5;
-
- ///
/// 当前敌人所看向的对象, 也就是枪口指向的对象
///
public ActivityObject LookTarget { get; set; }
///
- /// Ai 攻击属性
+ /// 锁定目标已经走过的时间
///
- public ExcelConfig.AiAttackAttr AttackAttribute { get; private set; }
-
- //锁定目标时间
- private float _lockTargetTime = 0;
- //攻击冷却计时器
- private float _attackTimer = 0;
+ public float LockTargetTime { get; set; } = 0;
public override void OnInit()
{
base.OnInit();
- AttackAttribute = ExcelConfig.AiAttackAttr_Map["0001"];
IsAi = true;
- StateController = AddComponent>();
+ StateController = AddComponent>();
AttackLayer = PhysicsLayer.Wall | PhysicsLayer.Player;
EnemyLayer = PhysicsLayer.Player;
@@ -99,14 +87,23 @@
MaxHp = 20;
Hp = 20;
-
+
+ //PathSign = new PathSign(this, PathSignLength, GameApplication.Instance.Node3D.Player);
+
+ //注册Ai状态机
StateController.Register(new AiNormalState());
+ //StateController.Register(new AiProbeState());
StateController.Register(new AiTailAfterState());
StateController.Register(new AiFollowUpState());
- StateController.Register(new AiSurroundState());
StateController.Register(new AiLeaveForState());
+ StateController.Register(new AiSurroundState());
+ StateController.Register(new AiFindAmmoState());
StateController.Register(new AiAttackState());
- StateController.ChangeState(AINormalStateEnum.AiNormal);
+ StateController.Register(new AiAstonishedState());
+ StateController.Register(new AiNotifyState());
+
+ //默认状态
+ StateController.ChangeStateInstant(AIStateEnum.AiNormal);
}
public override void EnterTree()
@@ -122,79 +119,15 @@
World.Enemy_InstanceList.Remove(this);
}
- public override void Attack()
- {
- _attackTimer = AttackInterval;
- OnAttack();
- }
-
- ///
- /// 敌人发动攻击
- ///
- protected virtual void OnAttack()
- {
- Debug.Log("触发攻击");
- var bulletData = FireManager.GetBulletData(this, ConvertRotation(Position.AngleTo(LookPosition)), ExcelConfig.BulletBase_Map["0006"]);
- FireManager.ShootBullet(bulletData, AttackLayer);
- }
-
- protected override void Process(float delta)
- {
- base.Process(delta);
- if (IsDie)
- {
- return;
- }
-
- //看向目标
- if (LookTarget != null)
- {
- var pos = LookTarget.Position;
- LookPosition = pos;
- //脸的朝向
- var gPos = Position;
- if (pos.X > gPos.X && Face == FaceDirection.Left)
- {
- Face = FaceDirection.Right;
- }
- else if (pos.X < gPos.X && Face == FaceDirection.Right)
- {
- Face = FaceDirection.Left;
- }
- }
-
- if (_attackTimer > 0)
- {
- _attackTimer -= delta;
- }
- //目标在视野内的时间
- var currState = StateController.CurrState;
- if (currState == AINormalStateEnum.AiAttack && _attackTimer <= 0) //必须在可以开火时记录时间
- {
- _lockTargetTime += delta;
- }
- else
- {
- _lockTargetTime = 0;
- }
- }
-
- protected override void OnHit(ActivityObject target, int damage, float angle, bool realHarm)
- {
- //受到伤害
- var state = StateController.CurrState;
- // if (state == AINormalStateEnum.AiNormal)
- // {
- // StateController.ChangeState(AINormalStateEnum.AiLeaveFor, target);
- // }
- // else if (state == AINormalStateEnum.AiLeaveFor)
- // {
- //
- // }
- }
-
protected override void OnDie()
{
+ //扔掉所有武器
+ var weapons = WeaponPack.GetAndClearItem();
+ for (var i = 0; i < weapons.Length; i++)
+ {
+ weapons[i].ThrowWeapon(this);
+ }
+
var effPos = Position + new Vector2(0, -Altitude);
//血液特效
var blood = ObjectManager.GetPoolItem(ResourcePath.prefab_effect_enemy_EnemyBloodEffect_tscn);
@@ -215,8 +148,156 @@
EventManager.EmitEvent(EventEnum.OnEnemyDie, this);
Destroy();
}
+
+ protected override void Process(float delta)
+ {
+ base.Process(delta);
+ if (IsDie)
+ {
+ return;
+ }
+
+ //看向目标
+ if (LookTarget != null && MountLookTarget)
+ {
+ var pos = LookTarget.Position;
+ LookPosition = pos;
+ //脸的朝向
+ var gPos = Position;
+ if (pos.X > gPos.X && Face == FaceDirection.Left)
+ {
+ Face = FaceDirection.Right;
+ }
+ else if (pos.X < gPos.X && Face == FaceDirection.Right)
+ {
+ Face = FaceDirection.Left;
+ }
+ //枪口跟随目标
+ MountPoint.SetLookAt(pos);
+ }
+
+ //拾起武器操作
+ EnemyPickUpWeapon();
+ }
+
+ protected override void OnHit(ActivityObject target, int damage, float angle, bool realHarm)
+ {
+ //受到伤害
+ var state = StateController.CurrState;
+ if (state == AIStateEnum.AiNormal || state == AIStateEnum.AiLeaveFor) //|| state == AiStateEnum.AiProbe
+ {
+ LookTarget = target;
+ StateController.ChangeState(AIStateEnum.AiTailAfter);
+ }
+ }
+
+ ///
+ /// 返回地上的武器是否有可以拾取的, 也包含没有被其他敌人标记的武器
+ ///
+ public bool CheckUsableWeaponInUnclaimed()
+ {
+ foreach (var unclaimedWeapon in World.Weapon_UnclaimedWeapons)
+ {
+ //判断是否能拾起武器, 条件: 相同的房间
+ if (unclaimedWeapon.AffiliationArea == AffiliationArea)
+ {
+ if (!unclaimedWeapon.IsTotalAmmoEmpty())
+ {
+ if (!unclaimedWeapon.HasSign(SignNames.AiFindWeaponSign))
+ {
+ return true;
+ }
+ else
+ {
+ //判断是否可以移除该标记
+ var enemy = unclaimedWeapon.GetSign(SignNames.AiFindWeaponSign);
+ if (enemy == null || enemy.IsDestroyed) //标记当前武器的敌人已经被销毁
+ {
+ unclaimedWeapon.RemoveSign(SignNames.AiFindWeaponSign);
+ return true;
+ }
+ else if (!enemy.IsAllWeaponTotalAmmoEmpty()) //标记当前武器的敌人已经有新的武器了
+ {
+ unclaimedWeapon.RemoveSign(SignNames.AiFindWeaponSign);
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
///
+ /// 寻找可用的武器
+ ///
+ public Weapon FindTargetWeapon()
+ {
+ Weapon target = null;
+ var position = Position;
+ foreach (var weapon in World.Weapon_UnclaimedWeapons)
+ {
+ //判断是否能拾起武器, 条件: 相同的房间, 或者当前房间目前没有战斗, 或者不在战斗房间
+ if (weapon.AffiliationArea == AffiliationArea)
+ {
+ //还有弹药
+ if (!weapon.IsTotalAmmoEmpty())
+ {
+ //查询是否有其他敌人标记要拾起该武器
+ if (weapon.HasSign(SignNames.AiFindWeaponSign))
+ {
+ var enemy = weapon.GetSign(SignNames.AiFindWeaponSign);
+ if (enemy == this) //就是自己标记的
+ {
+
+ }
+ else if (enemy == null || enemy.IsDestroyed) //标记当前武器的敌人已经被销毁
+ {
+ weapon.RemoveSign(SignNames.AiFindWeaponSign);
+ }
+ else if (!enemy.IsAllWeaponTotalAmmoEmpty()) //标记当前武器的敌人已经有新的武器了
+ {
+ weapon.RemoveSign(SignNames.AiFindWeaponSign);
+ }
+ else //放弃这把武器
+ {
+ continue;
+ }
+ }
+
+ if (target == null) //第一把武器
+ {
+ target = weapon;
+ }
+ else if (target.Position.DistanceSquaredTo(position) >
+ weapon.Position.DistanceSquaredTo(position)) //距离更近
+ {
+ target = weapon;
+ }
+ }
+ }
+ }
+
+ return target;
+ }
+
+ ///
+ /// 获取武器攻击范围 (最大距离值与最小距离的中间值)
+ ///
+ /// 从最小到最大距离的过渡量, 0 - 1, 默认 0.5
+ public float GetWeaponRange(float weight = 0.5f)
+ {
+ if (WeaponPack.ActiveItem != null)
+ {
+ var attribute = WeaponPack.ActiveItem.Attribute;
+ return Mathf.Lerp(Utils.GetConfigRangeStart(attribute.Bullet.DistanceRange), Utils.GetConfigRangeEnd(attribute.Bullet.DistanceRange), weight);
+ }
+
+ return 0;
+ }
+
+ ///
/// 返回目标点是否在视野范围内
///
public bool IsInViewRange(Vector2 target)
@@ -251,7 +332,7 @@
}
///
- /// 调用视野检测, 如果被墙壁和其它物体遮挡, 则返回被挡住视野的物体对象, 视野无阻则返回 null
+ /// 调用视野检测, 如果被墙壁和其它物体遮挡, 则返回true
///
public bool TestViewRayCast(Vector2 target)
{
@@ -268,42 +349,82 @@
{
ViewRay.Enabled = false;
}
-
- ///
- /// 获取锁定目标的时间
- ///
- public float GetLockTime()
- {
- return _lockTargetTime;
- }
///
+ /// AI 拾起武器操作
+ ///
+ private void EnemyPickUpWeapon()
+ {
+ //这几个状态不需要主动拾起武器操作
+ var state = StateController.CurrState;
+ if (state == AIStateEnum.AiNormal)
+ {
+ return;
+ }
+
+ //拾起地上的武器
+ if (InteractiveItem is Weapon weapon)
+ {
+ if (WeaponPack.ActiveItem == null) //手上没有武器, 无论如何也要拾起
+ {
+ TriggerInteractive();
+ return;
+ }
+
+ //没弹药了
+ if (weapon.IsTotalAmmoEmpty())
+ {
+ return;
+ }
+
+ var index = WeaponPack.FindIndex((we, i) => we.ActivityBase.Id == weapon.ActivityBase.Id);
+ if (index != -1) //与武器背包中武器类型相同, 补充子弹
+ {
+ if (!WeaponPack.GetItem(index).IsAmmoFull())
+ {
+ TriggerInteractive();
+ }
+
+ return;
+ }
+
+ // var index2 = Holster.FindWeapon((we, i) =>
+ // we.Attribute.WeightType == weapon.Attribute.WeightType && we.IsTotalAmmoEmpty());
+ var index2 = WeaponPack.FindIndex((we, i) => we.IsTotalAmmoEmpty());
+ if (index2 != -1) //扔掉没子弹的武器
+ {
+ ThrowWeapon(index2);
+ TriggerInteractive();
+ return;
+ }
+
+ // if (Holster.HasVacancy()) //有空位, 拾起武器
+ // {
+ // TriggerInteractive();
+ // return;
+ // }
+ }
+ }
+
+ ///
/// 获取锁定目标的剩余时间
///
public float GetLockRemainderTime()
{
- return AttackAttribute.LockingTime - _lockTargetTime;
+ var weapon = WeaponPack.ActiveItem;
+ if (weapon == null)
+ {
+ return 0;
+ }
+ return weapon.Attribute.AiAttackAttr.LockingTime - LockTargetTime;
}
-
+
///
/// 强制设置锁定目标时间
///
public void SetLockTargetTime(float time)
{
- _lockTargetTime = time;
- }
-
- ///
- /// 获取攻击范围
- ///
- public float GetAttackRange()
- {
- return AttackRange;
- }
-
- public override float GetFirePointAltitude()
- {
- return -AnimatedSprite.Position.Y - FirePoint.Position.Y;
+ LockTargetTime = time;
}
public override void LookTargetPosition(Vector2 pos)
@@ -313,14 +434,6 @@
}
///
- /// 获取攻击冷却计时时间, 小于等于 0 表示冷却完成
- ///
- public float GetAttackTimer()
- {
- return _attackTimer;
- }
-
- ///
/// 执行移动操作
///
public void DoMove()
@@ -339,4 +452,15 @@
AnimatedSprite.Play(AnimatorNames.Idle);
BasisVelocity = Vector2.Zero;
}
-}
\ No newline at end of file
+
+ ///
+ /// 更新房间中标记的目标位置
+ ///
+ public void UpdateMarkTargetPosition()
+ {
+ if (LookTarget != null)
+ {
+ AffiliationArea.RoomInfo.MarkTargetPosition[LookTarget.Id] = LookTarget.Position;
+ }
+ }
+}
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/NoWeaponEnemy.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/NoWeaponEnemy.cs
new file mode 100644
index 0000000..b2da111
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/role/enemy/NoWeaponEnemy.cs
@@ -0,0 +1,11 @@
+
+using Godot;
+
+///
+/// 没有武器的敌人
+///
+[Tool]
+public partial class NoWeaponEnemy : Enemy
+{
+
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AIAdvancedStateEnum.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AIAdvancedStateEnum.cs
deleted file mode 100644
index c02b0d2..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AIAdvancedStateEnum.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-
-public enum AIAdvancedStateEnum
-{
- ///
- /// Ai 状态, 正常, 未发现目标
- ///
- AiNormal,
- ///
- /// 找到玩家,准备通知其他敌人
- ///
- AiNotify,
- ///
- /// 惊讶状态
- ///
- AiAstonished,
- ///
- /// 收到其他敌人通知, 前往发现目标的位置
- ///
- AiLeaveFor,
- ///
- /// 发现目标, 目标不在视野内, 但是知道位置
- ///
- AiTailAfter,
- ///
- /// 目标在视野内, 跟进目标, 如果距离在子弹有效射程内, 则开火
- ///
- AiFollowUp,
- ///
- /// 距离足够近, 在目标附近随机移动
- ///
- AiSurround,
- ///
- /// Ai 寻找弹药
- ///
- AiFindAmmo,
- ///
- /// Ai攻击
- ///
- AiAttack,
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiAstonishedState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiAstonishedState.cs
deleted file mode 100644
index dae6a74..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiAstonishedState.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using Godot;
-
-namespace AdvancedState;
-
-///
-/// 发现目标时的惊讶状态
-///
-public class AiAstonishedState : StateBase
-{
- ///
- /// 下一个状态
- ///
- public AIAdvancedStateEnum NextState;
-
- private object[] _args;
- private float _timer;
-
- public AiAstonishedState() : base(AIAdvancedStateEnum.AiAstonished)
- {
- }
-
- public override void Enter(AIAdvancedStateEnum prev, params object[] args)
- {
- if (args.Length == 0)
- {
- Debug.Log("进入 AINormalStateEnum.AiAstonished 状态必传入下一个状态做完参数!");
- ChangeState(prev);
- return;
- }
-
- NextState = (AIAdvancedStateEnum)args[0];
- _args = args;
- _timer = 0.6f;
-
- if (NextState == AIAdvancedStateEnum.AiLeaveFor)
- {
- var target = (ActivityObject)args[1];
- Master.LookTargetPosition(target.GetCenterPosition());
- }
-
- //播放惊讶表情
- Master.AnimationPlayer.Play(AnimatorNames.Astonished);
- }
-
- public override void Process(float delta)
- {
- Master.DoIdle();
- _timer -= delta;
- if (_timer <= 0)
- {
- if (_args.Length == 1)
- {
- ChangeState(NextState);
- }
- else if (_args.Length == 2)
- {
- ChangeState(NextState, _args[1]);
- }
- else if (_args.Length == 3)
- {
- ChangeState(NextState, _args[1], _args[2]);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiAttackState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiAttackState.cs
deleted file mode 100644
index f49c137..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiAttackState.cs
+++ /dev/null
@@ -1,268 +0,0 @@
-using System;
-using Godot;
-
-namespace AdvancedState;
-
-///
-/// ai 攻击状态
-///
-public class AiAttackState : StateBase
-{
- ///
- /// 上一个状态
- ///
- public AIAdvancedStateEnum PrevState;
-
- ///
- /// 攻击状态
- ///
- public AiAttackEnum AttackState;
-
- //是否移动结束
- private bool _isMoveOver;
-
- //移动停顿计时器
- private float _pauseTimer;
- private bool _moveFlag;
-
- //下一个移动点
- private Vector2 _nextPosition;
-
- //上一帧位置
- private Vector2 _prevPos;
- //卡在一个位置的时间
- private float _lockTimer;
-
- public AiAttackState() : base(AIAdvancedStateEnum.AiAttack)
- {
- }
-
- public override void Enter(AIAdvancedStateEnum prev, params object[] args)
- {
- if (Master.LookTarget == null)
- {
- throw new Exception("进入 AIAdvancedStateEnum.AiAttack 状态时角色没有攻击目标!");
- }
-
- var weapon = Master.WeaponPack.ActiveItem;
- if (weapon == null)
- {
- throw new Exception("进入 AIAdvancedStateEnum.AiAttack 状态时角色没有武器!");
-
- }
-
- if (!weapon.TriggerIsReady())
- {
- throw new Exception("进入 AIAdvancedStateEnum.AiAttack 状态时角色武器还玩法触动扳机!");
- }
-
- Master.BasisVelocity = Vector2.Zero;
- AttackState = AiAttackEnum.None;
- Master.LockTargetTime = 0;
- PrevState = prev;
-
- _isMoveOver = true;
- _pauseTimer = 0;
- _moveFlag = false;
- }
-
- public override void Exit(AIAdvancedStateEnum next)
- {
- Master.MountLookTarget = true;
- Master.LockTargetTime = 0;
- }
-
- public override void Process(float delta)
- {
- //更新标记位置
- Master.UpdateMarkTargetPosition();
-
- var weapon = Master.WeaponPack.ActiveItem;
- if (weapon == null)
- {
- //攻击结束
- ChangeState(PrevState);
- }
- else if (AttackState == AiAttackEnum.AttackInterval) //攻击完成
- {
- if (weapon.GetAttackTimer() <= 0) //攻击冷却完成
- {
- Master.MountLookTarget = true;
- //这里要做换弹判断, 还有上膛判断
- if (weapon.CurrAmmo <= 0) //换弹判断
- {
- if (!weapon.Reloading)
- {
- weapon.Reload();
- }
- }
- else if (weapon.GetBeLoadedStateState() != 2) //上膛
- {
- if (weapon.GetBeLoadedStateState() == 0)
- {
- weapon.BeLoaded();
- }
- }
- else
- {
- //攻击结束
- ChangeState(PrevState);
- }
- }
- MoveHandler(delta);
- }
- else //攻击状态
- {
- //触发扳机
- AttackState = Master.WeaponPack.ActiveItem.AiTriggerAttackState();
-
- if (AttackState == AiAttackEnum.LockingTime) //锁定玩家状态
- {
- Master.LockTargetTime += delta;
-
- var aiLockRemainderTime = Master.GetLockRemainderTime();
- Master.MountLookTarget = aiLockRemainderTime >= weapon.Attribute.AiAttackAttr.LockAngleTime;
- //更新瞄准辅助线
- if (weapon.Attribute.AiAttackAttr.ShowSubline)
- {
- if (Master.SubLine == null)
- {
- Master.InitSubLine();
- }
- else
- {
- Master.SubLine.Enable = true;
- }
-
- //播放警告删掉动画
- if (!Master.SubLine.IsPlayWarnAnimation && aiLockRemainderTime <= 0.5f)
- {
- Master.SubLine.PlayWarnAnimation(0.5f);
- }
- }
-
- if (weapon.Attribute.AiAttackAttr.LockingStand) //锁定目标时站立不动
- {
- Master.DoIdle();
- }
- else //正常移动
- {
- MoveHandler(delta);
- }
- }
- else
- {
- Master.LockTargetTime = 0;
- //关闭辅助线
- if (Master.SubLine != null)
- {
- Master.SubLine.Enable = false;
- }
-
- if (AttackState == AiAttackEnum.Attack || AttackState == AiAttackEnum.AttackInterval)
- {
- if (weapon.Attribute.AiAttackAttr.AttackLockAngle) //开火时锁定枪口角度
- {
- //连发状态锁定角度
- Master.MountLookTarget = !(weapon.GetContinuousCount() > 0 || weapon.GetAttackTimer() > 0);
- }
- else
- {
- Master.MountLookTarget = true;
- }
- }
- else
- {
- Master.MountLookTarget = true;
- }
-
- if (AttackState == AiAttackEnum.Attack && weapon.Attribute.AiAttackAttr.FiringStand) //开火时站立不动
- {
- Master.DoIdle();
- }
- else //正常移动
- {
- MoveHandler(delta);
- }
- }
- }
- }
-
- private void MoveHandler(float delta)
- {
-
- if (_pauseTimer >= 0)
- {
- Master.AnimatedSprite.Play(AnimatorNames.Idle);
- _pauseTimer -= delta;
- }
- else if (_isMoveOver) //移动已经完成
- {
- RunOver(Master.LookTarget.Position);
- _isMoveOver = false;
- }
- else
- {
- var masterPosition = Master.Position;
- if (_lockTimer >= 1) //卡在一个点超过一秒
- {
- RunOver(Master.LookTarget.Position);
- _isMoveOver = false;
- _lockTimer = 0;
- }
- else if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点
- {
- _pauseTimer = Utils.Random.RandomRangeFloat(0f, 0.5f);
- _isMoveOver = true;
- _moveFlag = false;
- //站立
- Master.DoIdle();
- }
- else if (!_moveFlag)
- {
- _moveFlag = true;
- //移动
- Master.DoMove();
- }
- else
- {
- var lastSlideCollision = Master.GetLastSlideCollision();
- if (lastSlideCollision != null && lastSlideCollision.GetCollider() is AdvancedRole) //碰到其他角色
- {
- _pauseTimer = Utils.Random.RandomRangeFloat(0f, 0.3f);
- _isMoveOver = true;
- _moveFlag = false;
- //站立
- Master.DoIdle();
- }
- else
- {
- //移动
- Master.DoMove();
- }
-
- if (_prevPos.DistanceSquaredTo(masterPosition) <= 1 * delta)
- {
- _lockTimer += delta;
- }
- else
- {
- _lockTimer = 0;
- _prevPos = masterPosition;
- }
- }
- }
- }
-
- private void RunOver(Vector2 targetPos)
- {
- var weapon = Master.WeaponPack.ActiveItem;
- var distance = (int)(weapon == null ? 150 : (Utils.GetConfigRangeStart(weapon.Attribute.Bullet.DistanceRange) * 0.7f));
- _nextPosition = new Vector2(
- targetPos.X + Utils.Random.RandomRangeInt(-distance, distance),
- targetPos.Y + Utils.Random.RandomRangeInt(-distance, distance)
- );
- Master.NavigationAgent2D.TargetPosition = _nextPosition;
- }
-
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiFindAmmoState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiFindAmmoState.cs
deleted file mode 100644
index 3857cea..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiFindAmmoState.cs
+++ /dev/null
@@ -1,151 +0,0 @@
-
-using System;
-using Godot;
-
-namespace AdvancedState;
-
-///
-/// Ai 寻找弹药, 进入该状态需要在参数中传入目标武器对象
-///
-public class AiFindAmmoState : StateBase
-{
-
- private Weapon _target;
-
- //导航目标点刷新计时器
- private float _navigationUpdateTimer = 0;
- private float _navigationInterval = 1f;
-
- private bool _isInTailAfterRange = false;
- private float _tailAfterTimer = 0;
-
- public AiFindAmmoState() : base(AIAdvancedStateEnum.AiFindAmmo)
- {
- }
-
- public override void Enter(AIAdvancedStateEnum prev, params object[] args)
- {
- if (Master.LookTarget == null)
- {
- throw new Exception("进入 AIAdvancedStateEnum.AiFindAmmo 状态时角色没有攻击目标!");
- }
-
- if (args.Length == 0)
- {
- throw new Exception("进入 AiStateEnum.AiFindAmmo 状态必须要把目标武器当成参数传过来");
- }
-
- SetTargetWeapon((Weapon)args[0]);
- _navigationUpdateTimer = 0;
- _isInTailAfterRange = false;
- _tailAfterTimer = 0;
-
- //标记武器
- _target.SetSign(SignNames.AiFindWeaponSign, Master);
- }
-
- public override void Process(float delta)
- {
- if (!Master.IsAllWeaponTotalAmmoEmpty()) //已经有弹药了
- {
- ChangeState(GetNextState());
- return;
- }
-
- //更新目标位置
- if (_navigationUpdateTimer <= 0)
- {
- //每隔一段时间秒更改目标位置
- _navigationUpdateTimer = _navigationInterval;
- var position = _target.GlobalPosition;
- Master.NavigationAgent2D.TargetPosition = position;
- }
- else
- {
- _navigationUpdateTimer -= delta;
- }
-
- if (_target.IsDestroyed || _target.IsTotalAmmoEmpty()) //已经被销毁, 或者弹药已经被其他角色捡走
- {
- //再去寻找其他武器
- SetTargetWeapon(Master.FindTargetWeapon());
-
- if (_target == null) //也没有其他可用的武器了
- {
- ChangeState(GetNextState());
- }
- }
- else if (_target.Master == Master) //已经被自己拾起
- {
- ChangeState(GetNextState());
- }
- else if (_target.Master != null) //武器已经被其他角色拾起!
- {
- //再去寻找其他武器
- SetTargetWeapon(Master.FindTargetWeapon());
-
- if (_target == null) //也没有其他可用的武器了
- {
- ChangeState(GetNextState());
- }
- }
- else
- {
- //检测目标没有超出跟随视野距离
- _isInTailAfterRange = Master.IsInTailAfterViewRange(Master.LookTarget.GetCenterPosition());
- if (_isInTailAfterRange)
- {
- _tailAfterTimer = 0;
- }
- else
- {
- _tailAfterTimer += delta;
- }
-
- //向武器移动
- if (!Master.NavigationAgent2D.IsNavigationFinished())
- {
- //移动
- Master.DoMove();
- }
- else
- {
- //站立
- Master.DoIdle();
- }
- }
- }
-
- private AIAdvancedStateEnum GetNextState()
- {
- return _tailAfterTimer > 10 ? AIAdvancedStateEnum.AiNormal : AIAdvancedStateEnum.AiTailAfter;
- }
-
- private void SetTargetWeapon(Weapon weapon)
- {
- _target = weapon;
- //设置目标点
- if (_target != null)
- {
- Master.NavigationAgent2D.TargetPosition = _target.GlobalPosition;
- }
- }
-
- public override void DebugDraw()
- {
- if (_target != null)
- {
- Master.DrawLine(Vector2.Zero, Master.ToLocal(_target.GlobalPosition), Colors.Purple);
-
- if (_tailAfterTimer <= 0)
- {
- Master.DrawLine(Vector2.Zero, Master.ToLocal(Master.LookTarget.GetCenterPosition()), Colors.Orange);
- }
- else if (_tailAfterTimer <= 10)
- {
- Master.DrawLine(Vector2.Zero, Master.ToLocal(Master.LookTarget.GetCenterPosition()), Colors.Blue);
- }
-
- }
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiFollowUpState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiFollowUpState.cs
deleted file mode 100644
index 4a602a8..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiFollowUpState.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-
-using System;
-using Godot;
-
-namespace AdvancedState;
-
-///
-/// 目标在视野内, 跟进目标, 如果距离在子弹有效射程内, 则开火
-///
-public class AiFollowUpState : StateBase
-{
- //导航目标点刷新计时器
- private float _navigationUpdateTimer = 0;
- private float _navigationInterval = 0.3f;
-
- public AiFollowUpState() : base(AIAdvancedStateEnum.AiFollowUp)
- {
- }
-
- public override void Enter(AIAdvancedStateEnum prev, params object[] args)
- {
- if (Master.LookTarget == null)
- {
- throw new Exception("进入 AIAdvancedStateEnum.AiFollowUp 状态时角色没有攻击目标!");
- }
-
- _navigationUpdateTimer = 0;
- Master.TargetInView = true;
- }
-
- public override void Process(float delta)
- {
- //先检查弹药是否打光
- if (Master.IsAllWeaponTotalAmmoEmpty())
- {
- //再寻找是否有可用的武器
- var targetWeapon = Master.FindTargetWeapon();
- if (targetWeapon != null)
- {
- ChangeState(AIAdvancedStateEnum.AiFindAmmo, targetWeapon);
- return;
- }
- else
- {
- //切换到随机移动状态
- ChangeState(AIAdvancedStateEnum.AiSurround);
- }
- }
-
- var playerPos = Master.LookTarget.GetCenterPosition();
-
- //更新玩家位置
- if (_navigationUpdateTimer <= 0)
- {
- //每隔一段时间秒更改目标位置
- _navigationUpdateTimer = _navigationInterval;
- Master.NavigationAgent2D.TargetPosition = playerPos;
- }
- else
- {
- _navigationUpdateTimer -= delta;
- }
-
- //是否在攻击范围内
- var inAttackRange = false;
-
- var weapon = Master.WeaponPack.ActiveItem;
- var distanceSquared = Master.Position.DistanceSquaredTo(playerPos);
- if (weapon != null)
- {
- inAttackRange = distanceSquared <= Mathf.Pow(Master.GetWeaponRange(0.7f), 2);
- }
-
- if (!Master.NavigationAgent2D.IsNavigationFinished())
- {
- //移动
- Master.DoMove();
- }
- else
- {
- //站立
- Master.DoIdle();
- }
-
- //检测玩家是否在视野内
- if (Master.IsInTailAfterViewRange(playerPos))
- {
- Master.TargetInView = !Master.TestViewRayCast(playerPos);
- //关闭射线检测
- Master.TestViewRayCastOver();
- }
- else
- {
- Master.TargetInView = false;
- }
-
- //在视野中
- if (Master.TargetInView)
- {
- //更新标记位置
- Master.UpdateMarkTargetPosition();
- if (inAttackRange) //在攻击范围内
- {
- //距离够近, 可以切换到环绕模式
- if (distanceSquared <= Mathf.Pow(Utils.GetConfigRangeStart(weapon.Attribute.Bullet.DistanceRange), 2) * 0.7f)
- {
- ChangeState(AIAdvancedStateEnum.AiSurround);
- }
- else if (weapon.TriggerIsReady()) //可以攻击
- {
- //攻击状态
- ChangeState(AIAdvancedStateEnum.AiAttack);
- }
- }
- }
- else //不在视野中
- {
- ChangeState(AIAdvancedStateEnum.AiTailAfter);
- }
- }
-
- public override void DebugDraw()
- {
- var playerPos = Master.LookTarget.GetCenterPosition();
- Master.DrawLine(new Vector2(0, -8), Master.ToLocal(playerPos), Colors.Red);
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiLeaveForState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiLeaveForState.cs
deleted file mode 100644
index 030736f..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiLeaveForState.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-
-using System;
-using Godot;
-
-namespace AdvancedState;
-
-///
-/// 收到其他敌人通知, 前往发现目标的位置
-///
-public class AiLeaveForState : StateBase
-{
- //导航目标点刷新计时器
- private float _navigationUpdateTimer = 0;
- private float _navigationInterval = 0.3f;
-
- private float _idleTimer = 0;
-
- //目标
- private ActivityObject _target;
- //目标点
- private Vector2 _targetPosition;
-
- public AiLeaveForState() : base(AIAdvancedStateEnum.AiLeaveFor)
- {
- }
-
- public override void Enter(AIAdvancedStateEnum prev, params object[] args)
- {
- if (args.Length == 0)
- {
- throw new Exception("进入 AINormalStateEnum.AiLeaveFor 状态必须带上目标对象");
- }
-
- _target = (ActivityObject)args[0];
-
- //先检查弹药是否打光
- if (Master.IsAllWeaponTotalAmmoEmpty())
- {
- //再寻找是否有可用的武器
- var targetWeapon = Master.FindTargetWeapon();
- if (targetWeapon != null)
- {
- Master.LookTarget = _target;
- ChangeState(AIAdvancedStateEnum.AiFindAmmo, targetWeapon);
- return;
- }
- }
-
- _idleTimer = 1;
- _targetPosition = _target.GetCenterPosition();
- Master.LookTargetPosition(_targetPosition);
- Master.AnimationPlayer.Play(AnimatorNames.Query);
- }
-
- public override void Exit(AIAdvancedStateEnum next)
- {
- Master.AnimationPlayer.Play(AnimatorNames.Reset);
- }
-
- public override void Process(float delta)
- {
- if (_idleTimer > 0)
- {
- _idleTimer -= delta;
- return;
- }
- //这个状态下不会有攻击事件, 所以没必要每一帧检查是否弹药耗尽
-
- //更新玩家位置
- if (_navigationUpdateTimer <= 0)
- {
- //每隔一段时间秒更改目标位置
- _navigationUpdateTimer = _navigationInterval;
- if (Master.AffiliationArea.RoomInfo.MarkTargetPosition.TryGetValue(_target.Id, out var pos))
- {
- _targetPosition = pos;
- }
- Master.NavigationAgent2D.TargetPosition = _targetPosition;
- }
- else
- {
- _navigationUpdateTimer -= delta;
- }
-
- if (!Master.NavigationAgent2D.IsNavigationFinished())
- {
- Master.LookTargetPosition(_targetPosition);
- //移动
- Master.DoMove();
- }
- else
- {
- //站立
- Master.DoIdle();
- }
-
- var playerPos = Player.Current.GetCenterPosition();
- //检测玩家是否在视野内, 如果在, 则切换到 AiTargetInView 状态
- if (Master.IsInTailAfterViewRange(playerPos))
- {
- if (!Master.TestViewRayCast(playerPos)) //看到玩家
- {
- //关闭射线检测
- Master.TestViewRayCastOver();
- //切换成发现目标状态
- Master.LookTarget = Player.Current;
- ChangeState(AIAdvancedStateEnum.AiFollowUp);
- return;
- }
- else
- {
- //关闭射线检测
- Master.TestViewRayCastOver();
- }
- }
-
- //移动到目标掉了, 还没发现目标
- if (Master.NavigationAgent2D.IsNavigationFinished())
- {
- ChangeState(AIAdvancedStateEnum.AiNormal);
- }
- }
-
- public override void DebugDraw()
- {
- Master.DrawLine(Vector2.Zero, Master.ToLocal(Master.NavigationAgent2D.TargetPosition), Colors.Yellow);
- }
-}
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiNormalState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiNormalState.cs
deleted file mode 100644
index 8a4858b..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiNormalState.cs
+++ /dev/null
@@ -1,164 +0,0 @@
-
-using Godot;
-
-namespace AdvancedState;
-
-///
-/// AI 正常状态
-///
-public class AiNormalState : StateBase
-{
- //下一个运动的坐标
- private Vector2 _nextPos;
-
- //是否移动结束
- private bool _isMoveOver;
-
- //上一次移动是否撞墙
- private bool _againstWall;
-
- //撞墙法线角度
- private float _againstWallNormalAngle;
-
- //移动停顿计时器
- private float _pauseTimer;
- private bool _moveFlag;
-
- //上一帧位置
- private Vector2 _prevPos;
- //卡在一个位置的时间
- private float _lockTimer;
-
- public AiNormalState() : base(AIAdvancedStateEnum.AiNormal)
- {
- }
-
- public override void Enter(AIAdvancedStateEnum prev, params object[] args)
- {
- _isMoveOver = true;
- _againstWall = false;
- _againstWallNormalAngle = 0;
- _pauseTimer = 0;
- _moveFlag = false;
- Master.LookTarget = null;
- }
-
- public override void Process(float delta)
- {
- //检测玩家
- var player = Player.Current;
- //玩家中心点坐标
- var playerPos = player.GetCenterPosition();
-
- if (Master.IsInViewRange(playerPos) && !Master.TestViewRayCast(playerPos)) //发现玩家
- {
- //发现玩家
- Master.LookTarget = player;
- //进入惊讶状态, 然后再进入通知状态
- ChangeState(AIAdvancedStateEnum.AiAstonished, AIAdvancedStateEnum.AiNotify);
- return;
- }
- else if (_pauseTimer >= 0)
- {
- Master.AnimatedSprite.Play(AnimatorNames.Idle);
- _pauseTimer -= delta;
- }
- else if (_isMoveOver) //没发现玩家, 且已经移动完成
- {
- RunOver();
- _isMoveOver = false;
- }
- else //移动中
- {
- if (_lockTimer >= 1) //卡在一个点超过一秒
- {
- RunOver();
- _isMoveOver = false;
- _lockTimer = 0;
- }
- else if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点
- {
- _pauseTimer = Utils.Random.RandomRangeFloat(0.3f, 2f);
- _isMoveOver = true;
- _moveFlag = false;
- //站立
- Master.DoIdle();
- }
- else if (!_moveFlag)
- {
- _moveFlag = true;
- var pos = Master.Position;
- //移动
- Master.DoMove();
- _prevPos = pos;
- }
- else
- {
- var pos = Master.Position;
- var lastSlideCollision = Master.GetLastSlideCollision();
- if (lastSlideCollision != null && lastSlideCollision.GetCollider() is AdvancedRole) //碰到其他角色
- {
- _pauseTimer = Utils.Random.RandomRangeFloat(0.1f, 0.5f);
- _isMoveOver = true;
- _moveFlag = false;
- //站立
- Master.DoIdle();
- }
- else
- {
- //移动
- Master.DoMove();
- }
-
- if (_prevPos.DistanceSquaredTo(pos) <= 0.01f)
- {
- _lockTimer += delta;
- }
- else
- {
- _prevPos = pos;
- }
- }
- }
-
- //关闭射线检测
- Master.TestViewRayCastOver();
- }
-
- //移动结束
- private void RunOver()
- {
- float angle;
- if (_againstWall)
- {
- angle = Utils.Random.RandomRangeFloat(_againstWallNormalAngle - Mathf.Pi * 0.5f,
- _againstWallNormalAngle + Mathf.Pi * 0.5f);
- }
- else
- {
- angle = Utils.Random.RandomRangeFloat(0, Mathf.Pi * 2f);
- }
-
- var len = Utils.Random.RandomRangeInt(30, 200);
- _nextPos = new Vector2(len, 0).Rotated(angle) + Master.GlobalPosition;
- //获取射线碰到的坐标
- if (Master.TestViewRayCast(_nextPos)) //碰到墙壁
- {
- _nextPos = Master.ViewRay.GetCollisionPoint();
- _againstWall = true;
- _againstWallNormalAngle = Master.ViewRay.GetCollisionNormal().Angle();
- }
- else
- {
- _againstWall = false;
- }
-
- Master.NavigationAgent2D.TargetPosition = _nextPos;
- Master.LookTargetPosition(_nextPos);
- }
-
- public override void DebugDraw()
- {
- Master.DrawLine(new Vector2(0, -8), Master.ToLocal(_nextPos), Colors.Green);
- }
-}
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiNotifyState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiNotifyState.cs
deleted file mode 100644
index f1f0c10..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiNotifyState.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System;
-
-namespace AdvancedState;
-
-///
-/// 发现目标, 通知其它敌人
-///
-public class AiNotifyState : StateBase
-{
- private float _timer;
-
- public AiNotifyState() : base(AIAdvancedStateEnum.AiNotify)
- {
- }
-
- public override void Enter(AIAdvancedStateEnum prev, params object[] args)
- {
- if (Master.LookTarget == null)
- {
- throw new Exception("进入 AIAdvancedStateEnum.AiNotify 没有攻击目标!");
- }
- _timer = 0.6f;
- //通知其它角色
- Master.World.NotifyEnemyTarget(Master, Master.LookTarget);
- Master.AnimationPlayer.Play(AnimatorNames.Notify);
- }
-
- public override void Process(float delta)
- {
- Master.DoIdle();
- _timer -= delta;
- if (_timer <= 0)
- {
- ChangeState(AIAdvancedStateEnum.AiTailAfter);
- }
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiSurroundState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiSurroundState.cs
deleted file mode 100644
index 875d365..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiSurroundState.cs
+++ /dev/null
@@ -1,175 +0,0 @@
-
-using System;
-using Godot;
-
-namespace AdvancedState;
-
-///
-/// 距离目标足够近, 在目标附近随机移动, 并开火
-///
-public class AiSurroundState : StateBase
-{
- //是否移动结束
- private bool _isMoveOver;
-
- //移动停顿计时器
- private float _pauseTimer;
- private bool _moveFlag;
-
- //下一个移动点
- private Vector2 _nextPosition;
-
- //上一帧位置
- private Vector2 _prevPos;
- //卡在一个位置的时间
- private float _lockTimer;
-
- public AiSurroundState() : base(AIAdvancedStateEnum.AiSurround)
- {
- }
-
- public override void Enter(AIAdvancedStateEnum prev, params object[] args)
- {
- if (Master.LookTarget == null)
- {
- throw new Exception("进入 AIAdvancedStateEnum.AiSurround 状态时角色没有攻击目标!");
- }
-
- Master.TargetInView = true;
- _isMoveOver = true;
- _pauseTimer = 0;
- _moveFlag = false;
- }
-
- public override void Process(float delta)
- {
- //先检查弹药是否打光
- if (Master.IsAllWeaponTotalAmmoEmpty())
- {
- //再寻找是否有可用的武器
- var targetWeapon = Master.FindTargetWeapon();
- if (targetWeapon != null)
- {
- ChangeState(AIAdvancedStateEnum.AiFindAmmo, targetWeapon);
- return;
- }
- }
-
- var playerPos = Master.LookTarget.GetCenterPosition();
- var weapon = Master.WeaponPack.ActiveItem;
-
- //检测玩家是否在视野内
- if (Master.IsInTailAfterViewRange(playerPos))
- {
- Master.TargetInView = !Master.TestViewRayCast(playerPos);
- //关闭射线检测
- Master.TestViewRayCastOver();
- }
- else
- {
- Master.TargetInView = false;
- }
-
- //在视野中
- if (Master.TargetInView)
- {
- //更新标记位置
- Master.UpdateMarkTargetPosition();
-
- if (_pauseTimer >= 0)
- {
- Master.AnimatedSprite.Play(AnimatorNames.Idle);
- _pauseTimer -= delta;
- }
- else if (_isMoveOver) //移动已经完成
- {
- RunOver(playerPos);
- _isMoveOver = false;
- }
- else
- {
- var masterPosition = Master.Position;
- if (_lockTimer >= 1) //卡在一个点超过一秒
- {
- RunOver(playerPos);
- _isMoveOver = false;
- _lockTimer = 0;
- }
- else if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点
- {
- _pauseTimer = Utils.Random.RandomRangeFloat(0f, 0.5f);
- _isMoveOver = true;
- _moveFlag = false;
- //站立
- Master.DoIdle();
- }
- else if (!_moveFlag)
- {
- _moveFlag = true;
- //移动
- Master.DoMove();
- }
- else
- {
- var lastSlideCollision = Master.GetLastSlideCollision();
- if (lastSlideCollision != null && lastSlideCollision.GetCollider() is AdvancedRole) //碰到其他角色
- {
- _pauseTimer = Utils.Random.RandomRangeFloat(0f, 0.3f);
- _isMoveOver = true;
- _moveFlag = false;
- //站立
- Master.DoIdle();
- }
- else
- {
- //移动
- Master.DoMove();
- }
-
- if (_prevPos.DistanceSquaredTo(masterPosition) <= 1 * delta)
- {
- _lockTimer += delta;
- }
- else
- {
- _lockTimer = 0;
- _prevPos = masterPosition;
- }
- }
-
- if (weapon != null)
- {
- if (masterPosition.DistanceSquaredTo(playerPos) > Mathf.Pow(Master.GetWeaponRange(0.7f), 2)) //玩家离开正常射击范围
- {
- ChangeState(AIAdvancedStateEnum.AiFollowUp);
- }
- else if (weapon.TriggerIsReady()) //可以攻击
- {
- //发起攻击
- ChangeState(AIAdvancedStateEnum.AiAttack);
- }
- }
- }
- }
- else //目标离开视野
- {
- ChangeState(AIAdvancedStateEnum.AiTailAfter);
- }
- }
-
- private void RunOver(Vector2 targetPos)
- {
- var weapon = Master.WeaponPack.ActiveItem;
- var distance = (int)(weapon == null ? 150 : (Utils.GetConfigRangeStart(weapon.Attribute.Bullet.DistanceRange) * 0.7f));
- _nextPosition = new Vector2(
- targetPos.X + Utils.Random.RandomRangeInt(-distance, distance),
- targetPos.Y + Utils.Random.RandomRangeInt(-distance, distance)
- );
- Master.NavigationAgent2D.TargetPosition = _nextPosition;
- }
-
- public override void DebugDraw()
- {
- Master.DrawLine(new Vector2(0, -8), Master.ToLocal(_nextPosition), Colors.White);
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiTailAfterState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiTailAfterState.cs
deleted file mode 100644
index a4bd0f6..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/advancedState/AiTailAfterState.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-
-using System;
-using Godot;
-
-namespace AdvancedState;
-
-///
-/// AI 发现玩家, 跟随玩家, 但是不在视野范围内
-///
-public class AiTailAfterState : StateBase
-{
- ///
- /// 目标是否在视野半径内
- ///
- private bool _isInViewRange;
-
- //导航目标点刷新计时器
- private float _navigationUpdateTimer = 0;
- private float _navigationInterval = 0.3f;
-
- //目标从视野消失时已经过去的时间
- private float _viewTimer;
-
- public AiTailAfterState() : base(AIAdvancedStateEnum.AiTailAfter)
- {
- }
-
- public override void Enter(AIAdvancedStateEnum prev, params object[] args)
- {
- if (Master.LookTarget == null)
- {
- throw new Exception("进入 AIAdvancedStateEnum.AiTailAfter 状态时角色没有攻击目标!");
- }
-
- _isInViewRange = true;
- _navigationUpdateTimer = 0;
- _viewTimer = 0;
-
- //先检查弹药是否打光
- if (Master.IsAllWeaponTotalAmmoEmpty())
- {
- //再寻找是否有可用的武器
- var targetWeapon = Master.FindTargetWeapon();
- if (targetWeapon != null)
- {
- ChangeState(AIAdvancedStateEnum.AiFindAmmo, targetWeapon);
- }
- }
- }
-
- public override void Process(float delta)
- {
- //这个状态下不会有攻击事件, 所以没必要每一帧检查是否弹药耗尽
-
- var playerPos = Master.LookTarget.GetCenterPosition();
-
- //更新玩家位置
- if (_navigationUpdateTimer <= 0)
- {
- //每隔一段时间秒更改目标位置
- _navigationUpdateTimer = _navigationInterval;
- Master.NavigationAgent2D.TargetPosition = playerPos;
- }
- else
- {
- _navigationUpdateTimer -= delta;
- }
-
- if (!Master.NavigationAgent2D.IsNavigationFinished())
- {
- //移动
- Master.DoMove();
- }
- else
- {
- //站立
- Master.DoIdle();
- }
- //检测玩家是否在视野内, 如果在, 则切换到 AiTargetInView 状态
- if (Master.IsInTailAfterViewRange(playerPos))
- {
- if (!Master.TestViewRayCast(playerPos)) //看到玩家
- {
- //关闭射线检测
- Master.TestViewRayCastOver();
- //切换成发现目标状态
- ChangeState(AIAdvancedStateEnum.AiFollowUp);
- return;
- }
- else
- {
- //关闭射线检测
- Master.TestViewRayCastOver();
- }
- }
-
- //检测玩家是否在穿墙视野范围内, 直接检测距离即可
- _isInViewRange = Master.IsInViewRange(playerPos);
- if (_isInViewRange)
- {
- _viewTimer = 0;
- }
- else //超出视野
- {
- if (_viewTimer > 10) //10秒
- {
- ChangeState(AIAdvancedStateEnum.AiNormal);
- }
- else
- {
- _viewTimer += delta;
- }
- }
- }
-
- public override void DebugDraw()
- {
- var playerPos = Master.LookTarget.GetCenterPosition();
- if (_isInViewRange)
- {
- Master.DrawLine(new Vector2(0, -8), Master.ToLocal(playerPos), Colors.Orange);
- }
- else
- {
- Master.DrawLine(new Vector2(0, -8), Master.ToLocal(playerPos), Colors.Blue);
- }
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AINormalStateEnum.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AINormalStateEnum.cs
deleted file mode 100644
index 35e56dd..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AINormalStateEnum.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-namespace NnormalState;
-
-public enum AINormalStateEnum
-{
- ///
- /// Ai 状态, 正常, 未发现目标
- ///
- AiNormal,
- ///
- /// 找到玩家,准备通知其他敌人
- ///
- AiNotify,
- ///
- /// 发现目标, 惊讶状态
- ///
- AiAstonished,
- ///
- /// 收到其他敌人通知, 前往发现目标的位置
- ///
- AiLeaveFor,
- ///
- /// 发现目标, 目标不在视野内, 但是知道位置
- ///
- AiTailAfter,
- ///
- /// 目标在视野内, 跟进目标, 如果距离在子弹有效射程内, 则开火
- ///
- AiFollowUp,
- ///
- /// 距离足够近, 在目标附近随机移动
- ///
- AiSurround,
- ///
- /// 攻击状态
- ///
- AiAttack,
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiAstonishedState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiAstonishedState.cs
deleted file mode 100644
index 74fd553..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiAstonishedState.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-namespace NnormalState;
-
-///
-/// 发现目标时的惊讶状态
-///
-public class AiAstonishedState : StateBase
-{
- ///
- /// 下一个状态
- ///
- public AINormalStateEnum NextState;
-
- private float _timer;
-
- public AiAstonishedState() : base(AINormalStateEnum.AiAstonished)
- {
- }
-
- public override void Enter(AINormalStateEnum prev, params object[] args)
- {
- if (args.Length == 0)
- {
- Debug.Log("进入 AINormalStateEnum.AiAstonished 状态必传入下一个状态做完参数!");
- ChangeState(prev);
- return;
- }
-
- NextState = (AINormalStateEnum)args[0];
- _timer = 1.5f;
- }
-
- public override void Process(float delta)
- {
- Master.DoIdle();
- _timer -= delta;
- if (_timer <= 0)
- {
- ChangeState(NextState);
- }
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiAttackState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiAttackState.cs
deleted file mode 100644
index 4fc5e3e..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiAttackState.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using Godot;
-
-namespace NnormalState;
-
-public class AiAttackState : StateBase
-{
- //上一个状态
- public AINormalStateEnum PrevState;
- //攻击步骤, 0.锁定目标, 1.执行攻击, 2.攻击完成
- private byte _attackStep = 0;
-
- public AiAttackState() : base(AINormalStateEnum.AiAttack)
- {
- }
-
- public override void Enter(AINormalStateEnum prev, params object[] args)
- {
- if (Master.GetAttackTimer() > 0)
- {
- Debug.LogError("攻击冷却还未完成就进入了 AINormalStateEnum.AiAttack 状态!");
- ChangeState(prev);
- return;
- }
- _attackStep = 0;
- PrevState = prev;
- Master.AnimationPlayer.AnimationFinished += OnAnimationFinished;
- }
-
- public override void Exit(AINormalStateEnum next)
- {
- Master.AnimationPlayer.AnimationFinished -= OnAnimationFinished;
- }
-
- public override void Process(float delta)
- {
- if (_attackStep == 0) //锁定目标步骤
- {
- if (Master.GetLockRemainderTime() > 0) //锁定目标时间
- {
- if (!Master.AttackAttribute.FiringStand && !Master.NavigationAgent2D.IsNavigationFinished())
- {
- //移动
- Master.DoMove();
- }
- else
- {
- //站立
- Master.DoIdle();
- }
- }
- else
- {
- _attackStep = 1;
- }
- }
- else if (_attackStep == 1) //攻击步骤
- {
- Master.BasisVelocity = Vector2.Zero;
- Master.AnimationPlayer.Play(AnimatorNames.Attack);
- _attackStep = 2;
- }
-
- }
-
- public void OnAnimationFinished(StringName name)
- {
- ChangeState(PrevState);
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiFollowUpState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiFollowUpState.cs
deleted file mode 100644
index 9ce25d8..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiFollowUpState.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-
-using Godot;
-
-namespace NnormalState;
-
-///
-/// 目标在视野内, 跟进目标, 如果距离在子弹有效射程内, 则开火
-///
-public class AiFollowUpState : StateBase
-{
- //导航目标点刷新计时器
- private float _navigationUpdateTimer = 0;
- private float _navigationInterval = 0.3f;
-
- public AiFollowUpState() : base(AINormalStateEnum.AiFollowUp)
- {
- }
-
- public override void Enter(AINormalStateEnum prev, params object[] args)
- {
- _navigationUpdateTimer = 0;
- Master.TargetInView = true;
- }
-
- public override void Exit(AINormalStateEnum next)
- {
- Master.LookTarget = null;
- }
-
- public override void Process(float delta)
- {
- var playerPos = Player.Current.GetCenterPosition();
-
- //更新玩家位置
- if (_navigationUpdateTimer <= 0)
- {
- //每隔一段时间秒更改目标位置
- _navigationUpdateTimer = _navigationInterval;
- Master.NavigationAgent2D.TargetPosition = playerPos;
- }
- else
- {
- _navigationUpdateTimer -= delta;
- }
-
- var distanceSquared = Master.GlobalPosition.DistanceSquaredTo(playerPos);
- //是否在攻击范围内
- var inAttackRange = distanceSquared <= Mathf.Pow(Master.GetAttackRange(), 2);
-
- //枪口指向玩家
- Master.LookTarget = Player.Current;
-
- if (!Master.NavigationAgent2D.IsNavigationFinished())
- {
- //移动
- Master.DoMove();
- }
- else
- {
- //站立
- Master.DoIdle();
- }
-
- //检测玩家是否在视野内
- if (Master.IsInTailAfterViewRange(playerPos))
- {
- Master.TargetInView = !Master.TestViewRayCast(playerPos);
- //关闭射线检测
- Master.TestViewRayCastOver();
- }
- else
- {
- Master.TargetInView = false;
- }
-
- //在视野中
- if (Master.TargetInView)
- {
- if (inAttackRange) //在攻击范围内
- {
- //距离够近, 可以切换到环绕模式
- if (distanceSquared <= Mathf.Pow(Master.GetAttackRange() * 0.7f, 2) * 0.7f)
- {
- ChangeState(AINormalStateEnum.AiSurround);
- }
- else if (Master.GetAttackTimer() <= 0) //攻击
- {
- ChangeState(AINormalStateEnum.AiAttack);
- }
- }
- }
- else //不在视野中
- {
- ChangeState(AINormalStateEnum.AiTailAfter);
- }
- }
-
- public override void DebugDraw()
- {
- var playerPos = Player.Current.GetCenterPosition();
- Master.DrawLine(new Vector2(0, -8), Master.ToLocal(playerPos), Colors.Red);
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiLeaveForState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiLeaveForState.cs
deleted file mode 100644
index 98163be..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiLeaveForState.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-
-using Godot;
-
-namespace NnormalState;
-
-///
-/// 收到其他敌人通知, 前往发现目标的位置
-///
-public class AiLeaveForState : StateBase
-{
- //导航目标点刷新计时器
- private float _navigationUpdateTimer = 0;
- private float _navigationInterval = 0.3f;
-
- public AiLeaveForState() : base(AINormalStateEnum.AiLeaveFor)
- {
- }
-
- public override void Enter(AINormalStateEnum prev, params object[] args)
- {
- // if (Master.World.Enemy_IsFindTarget)
- // {
- // Master.NavigationAgent2D.TargetPosition = Master.World.Enemy_FindTargetPosition;
- // }
- // else
- // {
- // ChangeState(prev);
- // }
- }
-
- public override void Process(float delta)
- {
- // //这个状态下不会有攻击事件, 所以没必要每一帧检查是否弹药耗尽
- //
- // //更新玩家位置
- // if (_navigationUpdateTimer <= 0)
- // {
- // //每隔一段时间秒更改目标位置
- // _navigationUpdateTimer = _navigationInterval;
- // Master.NavigationAgent2D.TargetPosition = Master.World.Enemy_FindTargetPosition;
- // }
- // else
- // {
- // _navigationUpdateTimer -= delta;
- // }
- //
- // if (!Master.NavigationAgent2D.IsNavigationFinished())
- // {
- // Master.LookTargetPosition(Master.World.Enemy_FindTargetPosition);
- // //移动
- // Master.DoMove();
- // }
- // else
- // {
- // //站立
- // Master.DoIdle();
- // }
- //
- // var playerPos = Player.Current.GetCenterPosition();
- // //检测玩家是否在视野内, 如果在, 则切换到 AiTargetInView 状态
- // if (Master.IsInTailAfterViewRange(playerPos))
- // {
- // if (!Master.TestViewRayCast(playerPos)) //看到玩家
- // {
- // //关闭射线检测
- // Master.TestViewRayCastOver();
- // //切换成发现目标状态
- // ChangeState(AINormalStateEnum.AiFollowUp);
- // return;
- // }
- // else
- // {
- // //关闭射线检测
- // Master.TestViewRayCastOver();
- // }
- // }
- //
- // //移动到目标掉了, 还没发现目标
- // if (Master.NavigationAgent2D.IsNavigationFinished())
- // {
- // ChangeState(AINormalStateEnum.AiNormal);
- // }
- }
-
- public override void DebugDraw()
- {
- Master.DrawLine(Vector2.Zero, Master.ToLocal(Master.NavigationAgent2D.TargetPosition), Colors.Yellow);
- }
-}
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiNormalState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiNormalState.cs
deleted file mode 100644
index 61469b8..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiNormalState.cs
+++ /dev/null
@@ -1,164 +0,0 @@
-using Godot;
-
-namespace NnormalState;
-
-///
-/// AI 正常状态
-///
-public class AiNormalState : StateBase
-{
- //是否发现玩家
- private bool _isFindPlayer;
-
- //下一个运动的坐标
- private Vector2 _nextPos;
-
- //是否移动结束
- private bool _isMoveOver;
-
- //上一次移动是否撞墙
- private bool _againstWall;
-
- //撞墙法线角度
- private float _againstWallNormalAngle;
-
- //移动停顿计时器
- private float _pauseTimer;
- private bool _moveFlag;
-
- //上一帧位置
- private Vector2 _prevPos;
- //卡在一个位置的时间
- private float _lockTimer;
-
- public AiNormalState() : base(AINormalStateEnum.AiNormal)
- {
- }
-
- public override void Enter(AINormalStateEnum prev, params object[] args)
- {
- _isFindPlayer = false;
- _isMoveOver = true;
- _againstWall = false;
- _againstWallNormalAngle = 0;
- _pauseTimer = 0;
- _moveFlag = false;
- }
-
- public override void Process(float delta)
- {
- if (_isFindPlayer) //已经找到玩家了
- {
- //现临时处理, 直接切换状态
- ChangeState(AINormalStateEnum.AiTailAfter);
- }
- else //没有找到玩家
- {
- //检测玩家
- var player = Player.Current;
- //玩家中心点坐标
- var playerPos = player.GetCenterPosition();
-
- if (Master.IsInViewRange(playerPos) && !Master.TestViewRayCast(playerPos)) //发现玩家
- {
- //发现玩家
- _isFindPlayer = true;
- }
- else if (_pauseTimer >= 0)
- {
- Master.AnimatedSprite.Play(AnimatorNames.Idle);
- _pauseTimer -= delta;
- }
- else if (_isMoveOver) //没发现玩家, 且已经移动完成
- {
- RunOver();
- _isMoveOver = false;
- }
- else //移动中
- {
- if (_lockTimer >= 1) //卡在一个点超过一秒
- {
- RunOver();
- _isMoveOver = false;
- _lockTimer = 0;
- }
- else if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点
- {
- _pauseTimer = Utils.Random.RandomRangeFloat(0.3f, 2f);
- _isMoveOver = true;
- _moveFlag = false;
- Master.DoIdle();
- }
- else if (!_moveFlag)
- {
- _moveFlag = true;
- var pos = Master.Position;
- //移动
- Master.DoMove();
- _prevPos = pos;
- }
- else
- {
- var pos = Master.Position;
- var lastSlideCollision = Master.GetLastSlideCollision();
- if (lastSlideCollision != null && lastSlideCollision.GetCollider() is AdvancedRole) //碰到其他角色
- {
- _pauseTimer = Utils.Random.RandomRangeFloat(0.1f, 0.5f);
- _isMoveOver = true;
- _moveFlag = false;
- Master.DoIdle();
- }
- else
- {
- //计算移动
- Master.DoMove();
- }
-
- if (_prevPos.DistanceSquaredTo(pos) <= 0.01f)
- {
- _lockTimer += delta;
- }
- else
- {
- _prevPos = pos;
- }
- }
- }
-
- //关闭射线检测
- Master.TestViewRayCastOver();
- }
- }
-
- //移动结束
- private void RunOver()
- {
- float angle;
- if (_againstWall)
- {
- angle = Utils.Random.RandomRangeFloat(_againstWallNormalAngle - Mathf.Pi * 0.5f,
- _againstWallNormalAngle + Mathf.Pi * 0.5f);
- }
- else
- {
- angle = Utils.Random.RandomRangeFloat(0, Mathf.Pi * 2f);
- }
-
- var len = Utils.Random.RandomRangeInt(30, 200);
- _nextPos = new Vector2(len, 0).Rotated(angle) + Master.GlobalPosition;
- //获取射线碰到的坐标
- if (Master.TestViewRayCast(_nextPos)) //碰到墙壁
- {
- _nextPos = Master.ViewRay.GetCollisionPoint();
- _againstWall = true;
- _againstWallNormalAngle = Master.ViewRay.GetCollisionNormal().Angle();
- }
- else
- {
- _againstWall = false;
- }
-
- Master.NavigationAgent2D.TargetPosition = _nextPos;
- Master.LookTargetPosition(_nextPos);
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiNotifyState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiNotifyState.cs
deleted file mode 100644
index 76e46c4..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiNotifyState.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-namespace NnormalState;
-
-///
-/// 发现目标, 通知其它敌人
-///
-public class AiNotifyState : StateBase
-{
-
- private float _timer;
-
- public AiNotifyState() : base(AINormalStateEnum.AiNotify)
- {
- }
-
- public override void Enter(AINormalStateEnum prev, params object[] args)
- {
- _timer = 1.5f;
- //通知其它角色
- Master.World.NotifyEnemyTarget(Master, Player.Current);
- }
-
- public override void Process(float delta)
- {
- _timer -= delta;
- if (_timer <= 0)
- {
- ChangeState(AINormalStateEnum.AiTailAfter);
- }
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiSurroundState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiSurroundState.cs
deleted file mode 100644
index 9e145c5..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiSurroundState.cs
+++ /dev/null
@@ -1,154 +0,0 @@
-
-using Godot;
-
-namespace NnormalState;
-
-///
-/// 距离目标足够近, 在目标附近随机移动, 并开火
-///
-public class AiSurroundState : StateBase
-{
- //是否移动结束
- private bool _isMoveOver;
-
- //移动停顿计时器
- private float _pauseTimer;
- private bool _moveFlag;
-
- //下一个移动点
- private Vector2 _nextPosition;
-
- //上一帧位置
- private Vector2 _prevPos;
- //卡在一个位置的时间
- private float _lockTimer;
-
- public AiSurroundState() : base(AINormalStateEnum.AiSurround)
- {
- }
-
- public override void Enter(AINormalStateEnum prev, params object[] args)
- {
- Master.TargetInView = true;
- _isMoveOver = true;
- _pauseTimer = 0;
- _moveFlag = false;
- }
-
- public override void Exit(AINormalStateEnum next)
- {
- Master.LookTarget = null;
- }
-
- public override void Process(float delta)
- {
- var playerPos = Player.Current.GetCenterPosition();
-
- //枪口指向玩家
- Master.LookTarget = Player.Current;
-
- //检测玩家是否在视野内
- if (Master.IsInTailAfterViewRange(playerPos))
- {
- Master.TargetInView = !Master.TestViewRayCast(playerPos);
- //关闭射线检测
- Master.TestViewRayCastOver();
- }
- else
- {
- Master.TargetInView = false;
- }
-
- //在视野中
- if (Master.TargetInView)
- {
- if (_pauseTimer >= 0)
- {
- Master.AnimatedSprite.Play(AnimatorNames.Idle);
- _pauseTimer -= delta;
- }
- else if (_isMoveOver) //移动已经完成
- {
- RunOver(playerPos);
- _isMoveOver = false;
- }
- else
- {
- var masterPosition = Master.Position;
- if (_lockTimer >= 1) //卡在一个点超过一秒
- {
- RunOver(playerPos);
- _isMoveOver = false;
- _lockTimer = 0;
- }
- else if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点
- {
- _pauseTimer = Utils.Random.RandomRangeFloat(0f, 0.5f);
- _isMoveOver = true;
- _moveFlag = false;
- Master.DoIdle();
- }
- else if (!_moveFlag)
- {
- _moveFlag = true;
- //计算移动
- Master.DoMove();
- }
- else
- {
- var lastSlideCollision = Master.GetLastSlideCollision();
- if (lastSlideCollision != null && lastSlideCollision.GetCollider() is AdvancedRole) //碰到其他角色
- {
- _pauseTimer = Utils.Random.RandomRangeFloat(0f, 0.3f);
- _isMoveOver = true;
- _moveFlag = false;
- Master.DoIdle();
- }
- else
- {
- //计算移动
- Master.DoMove();
- }
-
- if (_prevPos.DistanceSquaredTo(masterPosition) <= 1 * delta)
- {
- _lockTimer += delta;
- }
- else
- {
- _lockTimer = 0;
- _prevPos = masterPosition;
- }
- }
-
- if (masterPosition.DistanceSquaredTo(playerPos) > Mathf.Pow(Master.GetAttackRange() * 0.7f, 2)) //玩家离开正常射击范围
- {
- ChangeState(AINormalStateEnum.AiFollowUp);
- }
- else if (Master.GetAttackTimer() <= 0) //发起攻击
- {
- ChangeState(AINormalStateEnum.AiAttack);
- }
- }
- }
- else //目标离开视野
- {
- ChangeState(AINormalStateEnum.AiTailAfter);
- }
- }
-
- private void RunOver(Vector2 targetPos)
- {
- var distance = (int)(Master.GetAttackRange() * 0.7f);
- _nextPosition = new Vector2(
- targetPos.X + Utils.Random.RandomRangeInt(-distance, distance),
- targetPos.Y + Utils.Random.RandomRangeInt(-distance, distance)
- );
- Master.NavigationAgent2D.TargetPosition = _nextPosition;
- }
-
- public override void DebugDraw()
- {
- Master.DrawLine(new Vector2(0, -8), Master.ToLocal(_nextPosition), Colors.White);
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiTailAfterState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiTailAfterState.cs
deleted file mode 100644
index 864d094..0000000
--- a/DungeonShooting_Godot/src/game/activity/role/enemy/normalState/AiTailAfterState.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-
-using Godot;
-
-namespace NnormalState;
-
-///
-/// AI 发现玩家, 跟随玩家
-///
-public class AiTailAfterState : StateBase
-{
- ///
- /// 目标是否在视野半径内
- ///
- private bool _isInViewRange;
-
- //导航目标点刷新计时器
- private float _navigationUpdateTimer = 0;
- private float _navigationInterval = 0.3f;
-
- //目标从视野消失时已经过去的时间
- private float _viewTimer;
-
- public AiTailAfterState() : base(AINormalStateEnum.AiTailAfter)
- {
- }
-
- public override void Enter(AINormalStateEnum prev, params object[] args)
- {
- _isInViewRange = true;
- _navigationUpdateTimer = 0;
- _viewTimer = 0;
- }
-
- public override void Exit(AINormalStateEnum next)
- {
- Master.LookTarget = null;
- }
-
- public override void Process(float delta)
- {
- //这个状态下不会有攻击事件, 所以没必要每一帧检查是否弹药耗尽
-
- var playerPos = Player.Current.GetCenterPosition();
-
- //更新玩家位置
- if (_navigationUpdateTimer <= 0)
- {
- //每隔一段时间秒更改目标位置
- _navigationUpdateTimer = _navigationInterval;
- Master.NavigationAgent2D.TargetPosition = playerPos;
- }
- else
- {
- _navigationUpdateTimer -= delta;
- }
-
- //枪口指向玩家
- Master.LookTarget = Player.Current;
-
- if (!Master.NavigationAgent2D.IsNavigationFinished())
- {
- //移动
- Master.DoMove();
- }
- else
- {
- Master.DoIdle();
- }
- //检测玩家是否在视野内, 如果在, 则切换到 AiTargetInView 状态
- if (Master.IsInTailAfterViewRange(playerPos))
- {
- if (!Master.TestViewRayCast(playerPos)) //看到玩家
- {
- //关闭射线检测
- Master.TestViewRayCastOver();
- //切换成发现目标状态
- ChangeState(AINormalStateEnum.AiFollowUp);
- return;
- }
- else
- {
- //关闭射线检测
- Master.TestViewRayCastOver();
- }
- }
-
- //检测玩家是否在穿墙视野范围内, 直接检测距离即可
- _isInViewRange = Master.IsInViewRange(playerPos);
- if (_isInViewRange)
- {
- _viewTimer = 0;
- }
- else //超出视野
- {
- if (_viewTimer > 10) //10秒
- {
- ChangeState(AINormalStateEnum.AiNormal);
- }
- else
- {
- _viewTimer += delta;
- }
- }
- }
-
- public override void DebugDraw()
- {
- var playerPos = Player.Current.GetCenterPosition();
- if (_isInViewRange)
- {
- Master.DrawLine(new Vector2(0, -8), Master.ToLocal(playerPos), Colors.Orange);
- }
- else
- {
- Master.DrawLine(new Vector2(0, -8), Master.ToLocal(playerPos), Colors.Blue);
- }
- }
-}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiAstonishedState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiAstonishedState.cs
new file mode 100644
index 0000000..081f791
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiAstonishedState.cs
@@ -0,0 +1,65 @@
+using Godot;
+
+namespace EnemyState;
+
+///
+/// 发现目标时的惊讶状态
+///
+public class AiAstonishedState : StateBase
+{
+ ///
+ /// 下一个状态
+ ///
+ public AIStateEnum NextState;
+
+ private object[] _args;
+ private float _timer;
+
+ public AiAstonishedState() : base(AIStateEnum.AiAstonished)
+ {
+ }
+
+ public override void Enter(AIStateEnum prev, params object[] args)
+ {
+ if (args.Length == 0)
+ {
+ Debug.Log("进入 AINormalStateEnum.AiAstonished 状态必传入下一个状态做完参数!");
+ ChangeState(prev);
+ return;
+ }
+
+ NextState = (AIStateEnum)args[0];
+ _args = args;
+ _timer = 0.6f;
+
+ if (NextState == AIStateEnum.AiLeaveFor)
+ {
+ var target = (ActivityObject)args[1];
+ Master.LookTargetPosition(target.GetCenterPosition());
+ }
+
+ //播放惊讶表情
+ Master.AnimationPlayer.Play(AnimatorNames.Astonished);
+ }
+
+ public override void Process(float delta)
+ {
+ Master.DoIdle();
+ _timer -= delta;
+ if (_timer <= 0)
+ {
+ if (_args.Length == 1)
+ {
+ ChangeState(NextState);
+ }
+ else if (_args.Length == 2)
+ {
+ ChangeState(NextState, _args[1]);
+ }
+ else if (_args.Length == 3)
+ {
+ ChangeState(NextState, _args[1], _args[2]);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiAttackState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiAttackState.cs
new file mode 100644
index 0000000..2f0b18e
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiAttackState.cs
@@ -0,0 +1,268 @@
+using System;
+using Godot;
+
+namespace EnemyState;
+
+///
+/// ai 攻击状态
+///
+public class AiAttackState : StateBase
+{
+ ///
+ /// 上一个状态
+ ///
+ public AIStateEnum PrevState;
+
+ ///
+ /// 攻击状态
+ ///
+ public AiAttackEnum AttackState;
+
+ //是否移动结束
+ private bool _isMoveOver;
+
+ //移动停顿计时器
+ private float _pauseTimer;
+ private bool _moveFlag;
+
+ //下一个移动点
+ private Vector2 _nextPosition;
+
+ //上一帧位置
+ private Vector2 _prevPos;
+ //卡在一个位置的时间
+ private float _lockTimer;
+
+ public AiAttackState() : base(AIStateEnum.AiAttack)
+ {
+ }
+
+ public override void Enter(AIStateEnum prev, params object[] args)
+ {
+ if (Master.LookTarget == null)
+ {
+ throw new Exception("进入 AIAdvancedStateEnum.AiAttack 状态时角色没有攻击目标!");
+ }
+
+ var weapon = Master.WeaponPack.ActiveItem;
+ if (weapon == null)
+ {
+ throw new Exception("进入 AIAdvancedStateEnum.AiAttack 状态时角色没有武器!");
+
+ }
+
+ if (!weapon.TriggerIsReady())
+ {
+ throw new Exception("进入 AIAdvancedStateEnum.AiAttack 状态时角色武器还玩法触动扳机!");
+ }
+
+ Master.BasisVelocity = Vector2.Zero;
+ AttackState = AiAttackEnum.None;
+ Master.LockTargetTime = 0;
+ PrevState = prev;
+
+ _isMoveOver = true;
+ _pauseTimer = 0;
+ _moveFlag = false;
+ }
+
+ public override void Exit(AIStateEnum next)
+ {
+ Master.MountLookTarget = true;
+ Master.LockTargetTime = 0;
+ }
+
+ public override void Process(float delta)
+ {
+ //更新标记位置
+ Master.UpdateMarkTargetPosition();
+
+ var weapon = Master.WeaponPack.ActiveItem;
+ if (weapon == null)
+ {
+ //攻击结束
+ ChangeState(PrevState);
+ }
+ else if (AttackState == AiAttackEnum.AttackInterval) //攻击完成
+ {
+ if (weapon.GetAttackTimer() <= 0) //攻击冷却完成
+ {
+ Master.MountLookTarget = true;
+ //这里要做换弹判断, 还有上膛判断
+ if (weapon.CurrAmmo <= 0) //换弹判断
+ {
+ if (!weapon.Reloading)
+ {
+ weapon.Reload();
+ }
+ }
+ else if (weapon.GetBeLoadedStateState() != 2) //上膛
+ {
+ if (weapon.GetBeLoadedStateState() == 0)
+ {
+ weapon.BeLoaded();
+ }
+ }
+ else
+ {
+ //攻击结束
+ ChangeState(PrevState);
+ }
+ }
+ MoveHandler(delta);
+ }
+ else //攻击状态
+ {
+ //触发扳机
+ AttackState = Master.WeaponPack.ActiveItem.AiTriggerAttackState();
+
+ if (AttackState == AiAttackEnum.LockingTime) //锁定玩家状态
+ {
+ Master.LockTargetTime += delta;
+
+ var aiLockRemainderTime = Master.GetLockRemainderTime();
+ Master.MountLookTarget = aiLockRemainderTime >= weapon.Attribute.AiAttackAttr.LockAngleTime;
+ //更新瞄准辅助线
+ if (weapon.Attribute.AiAttackAttr.ShowSubline)
+ {
+ if (Master.SubLine == null)
+ {
+ Master.InitSubLine();
+ }
+ else
+ {
+ Master.SubLine.Enable = true;
+ }
+
+ //播放警告删掉动画
+ if (!Master.SubLine.IsPlayWarnAnimation && aiLockRemainderTime <= 0.5f)
+ {
+ Master.SubLine.PlayWarnAnimation(0.5f);
+ }
+ }
+
+ if (weapon.Attribute.AiAttackAttr.LockingStand) //锁定目标时站立不动
+ {
+ Master.DoIdle();
+ }
+ else //正常移动
+ {
+ MoveHandler(delta);
+ }
+ }
+ else
+ {
+ Master.LockTargetTime = 0;
+ //关闭辅助线
+ if (Master.SubLine != null)
+ {
+ Master.SubLine.Enable = false;
+ }
+
+ if (AttackState == AiAttackEnum.Attack || AttackState == AiAttackEnum.AttackInterval)
+ {
+ if (weapon.Attribute.AiAttackAttr.AttackLockAngle) //开火时锁定枪口角度
+ {
+ //连发状态锁定角度
+ Master.MountLookTarget = !(weapon.GetContinuousCount() > 0 || weapon.GetAttackTimer() > 0);
+ }
+ else
+ {
+ Master.MountLookTarget = true;
+ }
+ }
+ else
+ {
+ Master.MountLookTarget = true;
+ }
+
+ if (AttackState == AiAttackEnum.Attack && weapon.Attribute.AiAttackAttr.FiringStand) //开火时站立不动
+ {
+ Master.DoIdle();
+ }
+ else //正常移动
+ {
+ MoveHandler(delta);
+ }
+ }
+ }
+ }
+
+ private void MoveHandler(float delta)
+ {
+
+ if (_pauseTimer >= 0)
+ {
+ Master.AnimatedSprite.Play(AnimatorNames.Idle);
+ _pauseTimer -= delta;
+ }
+ else if (_isMoveOver) //移动已经完成
+ {
+ RunOver(Master.LookTarget.Position);
+ _isMoveOver = false;
+ }
+ else
+ {
+ var masterPosition = Master.Position;
+ if (_lockTimer >= 1) //卡在一个点超过一秒
+ {
+ RunOver(Master.LookTarget.Position);
+ _isMoveOver = false;
+ _lockTimer = 0;
+ }
+ else if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点
+ {
+ _pauseTimer = Utils.Random.RandomRangeFloat(0f, 0.5f);
+ _isMoveOver = true;
+ _moveFlag = false;
+ //站立
+ Master.DoIdle();
+ }
+ else if (!_moveFlag)
+ {
+ _moveFlag = true;
+ //移动
+ Master.DoMove();
+ }
+ else
+ {
+ var lastSlideCollision = Master.GetLastSlideCollision();
+ if (lastSlideCollision != null && lastSlideCollision.GetCollider() is Role) //碰到其他角色
+ {
+ _pauseTimer = Utils.Random.RandomRangeFloat(0f, 0.3f);
+ _isMoveOver = true;
+ _moveFlag = false;
+ //站立
+ Master.DoIdle();
+ }
+ else
+ {
+ //移动
+ Master.DoMove();
+ }
+
+ if (_prevPos.DistanceSquaredTo(masterPosition) <= 1 * delta)
+ {
+ _lockTimer += delta;
+ }
+ else
+ {
+ _lockTimer = 0;
+ _prevPos = masterPosition;
+ }
+ }
+ }
+ }
+
+ private void RunOver(Vector2 targetPos)
+ {
+ var weapon = Master.WeaponPack.ActiveItem;
+ var distance = (int)(weapon == null ? 150 : (Utils.GetConfigRangeStart(weapon.Attribute.Bullet.DistanceRange) * 0.7f));
+ _nextPosition = new Vector2(
+ targetPos.X + Utils.Random.RandomRangeInt(-distance, distance),
+ targetPos.Y + Utils.Random.RandomRangeInt(-distance, distance)
+ );
+ Master.NavigationAgent2D.TargetPosition = _nextPosition;
+ }
+
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiFindAmmoState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiFindAmmoState.cs
new file mode 100644
index 0000000..682aae1
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiFindAmmoState.cs
@@ -0,0 +1,151 @@
+
+using System;
+using Godot;
+
+namespace EnemyState;
+
+///
+/// Ai 寻找弹药, 进入该状态需要在参数中传入目标武器对象
+///
+public class AiFindAmmoState : StateBase
+{
+
+ private Weapon _target;
+
+ //导航目标点刷新计时器
+ private float _navigationUpdateTimer = 0;
+ private float _navigationInterval = 1f;
+
+ private bool _isInTailAfterRange = false;
+ private float _tailAfterTimer = 0;
+
+ public AiFindAmmoState() : base(AIStateEnum.AiFindAmmo)
+ {
+ }
+
+ public override void Enter(AIStateEnum prev, params object[] args)
+ {
+ if (Master.LookTarget == null)
+ {
+ throw new Exception("进入 AIAdvancedStateEnum.AiFindAmmo 状态时角色没有攻击目标!");
+ }
+
+ if (args.Length == 0)
+ {
+ throw new Exception("进入 AiStateEnum.AiFindAmmo 状态必须要把目标武器当成参数传过来");
+ }
+
+ SetTargetWeapon((Weapon)args[0]);
+ _navigationUpdateTimer = 0;
+ _isInTailAfterRange = false;
+ _tailAfterTimer = 0;
+
+ //标记武器
+ _target.SetSign(SignNames.AiFindWeaponSign, Master);
+ }
+
+ public override void Process(float delta)
+ {
+ if (!Master.IsAllWeaponTotalAmmoEmpty()) //已经有弹药了
+ {
+ ChangeState(GetNextState());
+ return;
+ }
+
+ //更新目标位置
+ if (_navigationUpdateTimer <= 0)
+ {
+ //每隔一段时间秒更改目标位置
+ _navigationUpdateTimer = _navigationInterval;
+ var position = _target.GlobalPosition;
+ Master.NavigationAgent2D.TargetPosition = position;
+ }
+ else
+ {
+ _navigationUpdateTimer -= delta;
+ }
+
+ if (_target.IsDestroyed || _target.IsTotalAmmoEmpty()) //已经被销毁, 或者弹药已经被其他角色捡走
+ {
+ //再去寻找其他武器
+ SetTargetWeapon(Master.FindTargetWeapon());
+
+ if (_target == null) //也没有其他可用的武器了
+ {
+ ChangeState(GetNextState());
+ }
+ }
+ else if (_target.Master == Master) //已经被自己拾起
+ {
+ ChangeState(GetNextState());
+ }
+ else if (_target.Master != null) //武器已经被其他角色拾起!
+ {
+ //再去寻找其他武器
+ SetTargetWeapon(Master.FindTargetWeapon());
+
+ if (_target == null) //也没有其他可用的武器了
+ {
+ ChangeState(GetNextState());
+ }
+ }
+ else
+ {
+ //检测目标没有超出跟随视野距离
+ _isInTailAfterRange = Master.IsInTailAfterViewRange(Master.LookTarget.GetCenterPosition());
+ if (_isInTailAfterRange)
+ {
+ _tailAfterTimer = 0;
+ }
+ else
+ {
+ _tailAfterTimer += delta;
+ }
+
+ //向武器移动
+ if (!Master.NavigationAgent2D.IsNavigationFinished())
+ {
+ //移动
+ Master.DoMove();
+ }
+ else
+ {
+ //站立
+ Master.DoIdle();
+ }
+ }
+ }
+
+ private AIStateEnum GetNextState()
+ {
+ return _tailAfterTimer > 10 ? AIStateEnum.AiNormal : AIStateEnum.AiTailAfter;
+ }
+
+ private void SetTargetWeapon(Weapon weapon)
+ {
+ _target = weapon;
+ //设置目标点
+ if (_target != null)
+ {
+ Master.NavigationAgent2D.TargetPosition = _target.GlobalPosition;
+ }
+ }
+
+ public override void DebugDraw()
+ {
+ if (_target != null)
+ {
+ Master.DrawLine(Vector2.Zero, Master.ToLocal(_target.GlobalPosition), Colors.Purple);
+
+ if (_tailAfterTimer <= 0)
+ {
+ Master.DrawLine(Vector2.Zero, Master.ToLocal(Master.LookTarget.GetCenterPosition()), Colors.Orange);
+ }
+ else if (_tailAfterTimer <= 10)
+ {
+ Master.DrawLine(Vector2.Zero, Master.ToLocal(Master.LookTarget.GetCenterPosition()), Colors.Blue);
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiFollowUpState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiFollowUpState.cs
new file mode 100644
index 0000000..e107a5e
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiFollowUpState.cs
@@ -0,0 +1,127 @@
+
+using System;
+using Godot;
+
+namespace EnemyState;
+
+///
+/// 目标在视野内, 跟进目标, 如果距离在子弹有效射程内, 则开火
+///
+public class AiFollowUpState : StateBase
+{
+ //导航目标点刷新计时器
+ private float _navigationUpdateTimer = 0;
+ private float _navigationInterval = 0.3f;
+
+ public AiFollowUpState() : base(AIStateEnum.AiFollowUp)
+ {
+ }
+
+ public override void Enter(AIStateEnum prev, params object[] args)
+ {
+ if (Master.LookTarget == null)
+ {
+ throw new Exception("进入 AIAdvancedStateEnum.AiFollowUp 状态时角色没有攻击目标!");
+ }
+
+ _navigationUpdateTimer = 0;
+ Master.TargetInView = true;
+ }
+
+ public override void Process(float delta)
+ {
+ //先检查弹药是否打光
+ if (Master.IsAllWeaponTotalAmmoEmpty())
+ {
+ //再寻找是否有可用的武器
+ var targetWeapon = Master.FindTargetWeapon();
+ if (targetWeapon != null)
+ {
+ ChangeState(AIStateEnum.AiFindAmmo, targetWeapon);
+ return;
+ }
+ else
+ {
+ //切换到随机移动状态
+ ChangeState(AIStateEnum.AiSurround);
+ }
+ }
+
+ var playerPos = Master.LookTarget.GetCenterPosition();
+
+ //更新玩家位置
+ if (_navigationUpdateTimer <= 0)
+ {
+ //每隔一段时间秒更改目标位置
+ _navigationUpdateTimer = _navigationInterval;
+ Master.NavigationAgent2D.TargetPosition = playerPos;
+ }
+ else
+ {
+ _navigationUpdateTimer -= delta;
+ }
+
+ //是否在攻击范围内
+ var inAttackRange = false;
+
+ var weapon = Master.WeaponPack.ActiveItem;
+ var distanceSquared = Master.Position.DistanceSquaredTo(playerPos);
+ if (weapon != null)
+ {
+ inAttackRange = distanceSquared <= Mathf.Pow(Master.GetWeaponRange(0.7f), 2);
+ }
+
+ if (!Master.NavigationAgent2D.IsNavigationFinished())
+ {
+ //移动
+ Master.DoMove();
+ }
+ else
+ {
+ //站立
+ Master.DoIdle();
+ }
+
+ //检测玩家是否在视野内
+ if (Master.IsInTailAfterViewRange(playerPos))
+ {
+ Master.TargetInView = !Master.TestViewRayCast(playerPos);
+ //关闭射线检测
+ Master.TestViewRayCastOver();
+ }
+ else
+ {
+ Master.TargetInView = false;
+ }
+
+ //在视野中
+ if (Master.TargetInView)
+ {
+ //更新标记位置
+ Master.UpdateMarkTargetPosition();
+ if (inAttackRange) //在攻击范围内
+ {
+ //距离够近, 可以切换到环绕模式
+ if (distanceSquared <= Mathf.Pow(Utils.GetConfigRangeStart(weapon.Attribute.Bullet.DistanceRange), 2) * 0.7f)
+ {
+ ChangeState(AIStateEnum.AiSurround);
+ }
+ else if (weapon.TriggerIsReady()) //可以攻击
+ {
+ //攻击状态
+ ChangeState(AIStateEnum.AiAttack);
+ }
+ }
+ }
+ else //不在视野中
+ {
+ ChangeState(AIStateEnum.AiTailAfter);
+ }
+ }
+
+ public override void DebugDraw()
+ {
+ var playerPos = Master.LookTarget.GetCenterPosition();
+ Master.DrawLine(new Vector2(0, -8), Master.ToLocal(playerPos), Colors.Red);
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiLeaveForState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiLeaveForState.cs
new file mode 100644
index 0000000..39d62aa
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiLeaveForState.cs
@@ -0,0 +1,128 @@
+
+using System;
+using Godot;
+
+namespace EnemyState;
+
+///
+/// 收到其他敌人通知, 前往发现目标的位置
+///
+public class AiLeaveForState : StateBase
+{
+ //导航目标点刷新计时器
+ private float _navigationUpdateTimer = 0;
+ private float _navigationInterval = 0.3f;
+
+ private float _idleTimer = 0;
+
+ //目标
+ private ActivityObject _target;
+ //目标点
+ private Vector2 _targetPosition;
+
+ public AiLeaveForState() : base(AIStateEnum.AiLeaveFor)
+ {
+ }
+
+ public override void Enter(AIStateEnum prev, params object[] args)
+ {
+ if (args.Length == 0)
+ {
+ throw new Exception("进入 AINormalStateEnum.AiLeaveFor 状态必须带上目标对象");
+ }
+
+ _target = (ActivityObject)args[0];
+
+ //先检查弹药是否打光
+ if (Master.IsAllWeaponTotalAmmoEmpty())
+ {
+ //再寻找是否有可用的武器
+ var targetWeapon = Master.FindTargetWeapon();
+ if (targetWeapon != null)
+ {
+ Master.LookTarget = _target;
+ ChangeState(AIStateEnum.AiFindAmmo, targetWeapon);
+ return;
+ }
+ }
+
+ _idleTimer = 1;
+ _targetPosition = _target.GetCenterPosition();
+ Master.LookTargetPosition(_targetPosition);
+ Master.AnimationPlayer.Play(AnimatorNames.Query);
+ }
+
+ public override void Exit(AIStateEnum next)
+ {
+ Master.AnimationPlayer.Play(AnimatorNames.Reset);
+ }
+
+ public override void Process(float delta)
+ {
+ if (_idleTimer > 0)
+ {
+ _idleTimer -= delta;
+ return;
+ }
+ //这个状态下不会有攻击事件, 所以没必要每一帧检查是否弹药耗尽
+
+ //更新玩家位置
+ if (_navigationUpdateTimer <= 0)
+ {
+ //每隔一段时间秒更改目标位置
+ _navigationUpdateTimer = _navigationInterval;
+ if (Master.AffiliationArea.RoomInfo.MarkTargetPosition.TryGetValue(_target.Id, out var pos))
+ {
+ _targetPosition = pos;
+ }
+ Master.NavigationAgent2D.TargetPosition = _targetPosition;
+ }
+ else
+ {
+ _navigationUpdateTimer -= delta;
+ }
+
+ if (!Master.NavigationAgent2D.IsNavigationFinished())
+ {
+ Master.LookTargetPosition(_targetPosition);
+ //移动
+ Master.DoMove();
+ }
+ else
+ {
+ //站立
+ Master.DoIdle();
+ }
+
+ var playerPos = Player.Current.GetCenterPosition();
+ //检测玩家是否在视野内, 如果在, 则切换到 AiTargetInView 状态
+ if (Master.IsInTailAfterViewRange(playerPos))
+ {
+ if (!Master.TestViewRayCast(playerPos)) //看到玩家
+ {
+ //关闭射线检测
+ Master.TestViewRayCastOver();
+ //切换成发现目标状态
+ Master.LookTarget = Player.Current;
+ ChangeState(AIStateEnum.AiFollowUp);
+ return;
+ }
+ else
+ {
+ //关闭射线检测
+ Master.TestViewRayCastOver();
+ }
+ }
+
+ //移动到目标掉了, 还没发现目标
+ if (Master.NavigationAgent2D.IsNavigationFinished())
+ {
+ ChangeState(AIStateEnum.AiNormal);
+ }
+ }
+
+ public override void DebugDraw()
+ {
+ Master.DrawLine(Vector2.Zero, Master.ToLocal(Master.NavigationAgent2D.TargetPosition), Colors.Yellow);
+ }
+}
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiNormalState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiNormalState.cs
new file mode 100644
index 0000000..f91594a
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiNormalState.cs
@@ -0,0 +1,164 @@
+
+using Godot;
+
+namespace EnemyState;
+
+///
+/// AI 正常状态
+///
+public class AiNormalState : StateBase
+{
+ //下一个运动的坐标
+ private Vector2 _nextPos;
+
+ //是否移动结束
+ private bool _isMoveOver;
+
+ //上一次移动是否撞墙
+ private bool _againstWall;
+
+ //撞墙法线角度
+ private float _againstWallNormalAngle;
+
+ //移动停顿计时器
+ private float _pauseTimer;
+ private bool _moveFlag;
+
+ //上一帧位置
+ private Vector2 _prevPos;
+ //卡在一个位置的时间
+ private float _lockTimer;
+
+ public AiNormalState() : base(AIStateEnum.AiNormal)
+ {
+ }
+
+ public override void Enter(AIStateEnum prev, params object[] args)
+ {
+ _isMoveOver = true;
+ _againstWall = false;
+ _againstWallNormalAngle = 0;
+ _pauseTimer = 0;
+ _moveFlag = false;
+ Master.LookTarget = null;
+ }
+
+ public override void Process(float delta)
+ {
+ //检测玩家
+ var player = Player.Current;
+ //玩家中心点坐标
+ var playerPos = player.GetCenterPosition();
+
+ if (Master.IsInViewRange(playerPos) && !Master.TestViewRayCast(playerPos)) //发现玩家
+ {
+ //发现玩家
+ Master.LookTarget = player;
+ //进入惊讶状态, 然后再进入通知状态
+ ChangeState(AIStateEnum.AiAstonished, AIStateEnum.AiNotify);
+ return;
+ }
+ else if (_pauseTimer >= 0)
+ {
+ Master.AnimatedSprite.Play(AnimatorNames.Idle);
+ _pauseTimer -= delta;
+ }
+ else if (_isMoveOver) //没发现玩家, 且已经移动完成
+ {
+ RunOver();
+ _isMoveOver = false;
+ }
+ else //移动中
+ {
+ if (_lockTimer >= 1) //卡在一个点超过一秒
+ {
+ RunOver();
+ _isMoveOver = false;
+ _lockTimer = 0;
+ }
+ else if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点
+ {
+ _pauseTimer = Utils.Random.RandomRangeFloat(0.3f, 2f);
+ _isMoveOver = true;
+ _moveFlag = false;
+ //站立
+ Master.DoIdle();
+ }
+ else if (!_moveFlag)
+ {
+ _moveFlag = true;
+ var pos = Master.Position;
+ //移动
+ Master.DoMove();
+ _prevPos = pos;
+ }
+ else
+ {
+ var pos = Master.Position;
+ var lastSlideCollision = Master.GetLastSlideCollision();
+ if (lastSlideCollision != null && lastSlideCollision.GetCollider() is Role) //碰到其他角色
+ {
+ _pauseTimer = Utils.Random.RandomRangeFloat(0.1f, 0.5f);
+ _isMoveOver = true;
+ _moveFlag = false;
+ //站立
+ Master.DoIdle();
+ }
+ else
+ {
+ //移动
+ Master.DoMove();
+ }
+
+ if (_prevPos.DistanceSquaredTo(pos) <= 0.01f)
+ {
+ _lockTimer += delta;
+ }
+ else
+ {
+ _prevPos = pos;
+ }
+ }
+ }
+
+ //关闭射线检测
+ Master.TestViewRayCastOver();
+ }
+
+ //移动结束
+ private void RunOver()
+ {
+ float angle;
+ if (_againstWall)
+ {
+ angle = Utils.Random.RandomRangeFloat(_againstWallNormalAngle - Mathf.Pi * 0.5f,
+ _againstWallNormalAngle + Mathf.Pi * 0.5f);
+ }
+ else
+ {
+ angle = Utils.Random.RandomRangeFloat(0, Mathf.Pi * 2f);
+ }
+
+ var len = Utils.Random.RandomRangeInt(30, 200);
+ _nextPos = new Vector2(len, 0).Rotated(angle) + Master.GlobalPosition;
+ //获取射线碰到的坐标
+ if (Master.TestViewRayCast(_nextPos)) //碰到墙壁
+ {
+ _nextPos = Master.ViewRay.GetCollisionPoint();
+ _againstWall = true;
+ _againstWallNormalAngle = Master.ViewRay.GetCollisionNormal().Angle();
+ }
+ else
+ {
+ _againstWall = false;
+ }
+
+ Master.NavigationAgent2D.TargetPosition = _nextPos;
+ Master.LookTargetPosition(_nextPos);
+ }
+
+ public override void DebugDraw()
+ {
+ Master.DrawLine(new Vector2(0, -8), Master.ToLocal(_nextPos), Colors.Green);
+ }
+}
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiNotifyState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiNotifyState.cs
new file mode 100644
index 0000000..d7c199e
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiNotifyState.cs
@@ -0,0 +1,37 @@
+using System;
+
+namespace EnemyState;
+
+///
+/// 发现目标, 通知其它敌人
+///
+public class AiNotifyState : StateBase
+{
+ private float _timer;
+
+ public AiNotifyState() : base(AIStateEnum.AiNotify)
+ {
+ }
+
+ public override void Enter(AIStateEnum prev, params object[] args)
+ {
+ if (Master.LookTarget == null)
+ {
+ throw new Exception("进入 AIAdvancedStateEnum.AiNotify 没有攻击目标!");
+ }
+ _timer = 0.6f;
+ //通知其它角色
+ Master.World.NotifyEnemyTarget(Master, Master.LookTarget);
+ Master.AnimationPlayer.Play(AnimatorNames.Notify);
+ }
+
+ public override void Process(float delta)
+ {
+ Master.DoIdle();
+ _timer -= delta;
+ if (_timer <= 0)
+ {
+ ChangeState(AIStateEnum.AiTailAfter);
+ }
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiSurroundState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiSurroundState.cs
new file mode 100644
index 0000000..c9a8f6e
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiSurroundState.cs
@@ -0,0 +1,175 @@
+
+using System;
+using Godot;
+
+namespace EnemyState;
+
+///
+/// 距离目标足够近, 在目标附近随机移动, 并开火
+///
+public class AiSurroundState : StateBase
+{
+ //是否移动结束
+ private bool _isMoveOver;
+
+ //移动停顿计时器
+ private float _pauseTimer;
+ private bool _moveFlag;
+
+ //下一个移动点
+ private Vector2 _nextPosition;
+
+ //上一帧位置
+ private Vector2 _prevPos;
+ //卡在一个位置的时间
+ private float _lockTimer;
+
+ public AiSurroundState() : base(AIStateEnum.AiSurround)
+ {
+ }
+
+ public override void Enter(AIStateEnum prev, params object[] args)
+ {
+ if (Master.LookTarget == null)
+ {
+ throw new Exception("进入 AIAdvancedStateEnum.AiSurround 状态时角色没有攻击目标!");
+ }
+
+ Master.TargetInView = true;
+ _isMoveOver = true;
+ _pauseTimer = 0;
+ _moveFlag = false;
+ }
+
+ public override void Process(float delta)
+ {
+ //先检查弹药是否打光
+ if (Master.IsAllWeaponTotalAmmoEmpty())
+ {
+ //再寻找是否有可用的武器
+ var targetWeapon = Master.FindTargetWeapon();
+ if (targetWeapon != null)
+ {
+ ChangeState(AIStateEnum.AiFindAmmo, targetWeapon);
+ return;
+ }
+ }
+
+ var playerPos = Master.LookTarget.GetCenterPosition();
+ var weapon = Master.WeaponPack.ActiveItem;
+
+ //检测玩家是否在视野内
+ if (Master.IsInTailAfterViewRange(playerPos))
+ {
+ Master.TargetInView = !Master.TestViewRayCast(playerPos);
+ //关闭射线检测
+ Master.TestViewRayCastOver();
+ }
+ else
+ {
+ Master.TargetInView = false;
+ }
+
+ //在视野中
+ if (Master.TargetInView)
+ {
+ //更新标记位置
+ Master.UpdateMarkTargetPosition();
+
+ if (_pauseTimer >= 0)
+ {
+ Master.AnimatedSprite.Play(AnimatorNames.Idle);
+ _pauseTimer -= delta;
+ }
+ else if (_isMoveOver) //移动已经完成
+ {
+ RunOver(playerPos);
+ _isMoveOver = false;
+ }
+ else
+ {
+ var masterPosition = Master.Position;
+ if (_lockTimer >= 1) //卡在一个点超过一秒
+ {
+ RunOver(playerPos);
+ _isMoveOver = false;
+ _lockTimer = 0;
+ }
+ else if (Master.NavigationAgent2D.IsNavigationFinished()) //到达终点
+ {
+ _pauseTimer = Utils.Random.RandomRangeFloat(0f, 0.5f);
+ _isMoveOver = true;
+ _moveFlag = false;
+ //站立
+ Master.DoIdle();
+ }
+ else if (!_moveFlag)
+ {
+ _moveFlag = true;
+ //移动
+ Master.DoMove();
+ }
+ else
+ {
+ var lastSlideCollision = Master.GetLastSlideCollision();
+ if (lastSlideCollision != null && lastSlideCollision.GetCollider() is Role) //碰到其他角色
+ {
+ _pauseTimer = Utils.Random.RandomRangeFloat(0f, 0.3f);
+ _isMoveOver = true;
+ _moveFlag = false;
+ //站立
+ Master.DoIdle();
+ }
+ else
+ {
+ //移动
+ Master.DoMove();
+ }
+
+ if (_prevPos.DistanceSquaredTo(masterPosition) <= 1 * delta)
+ {
+ _lockTimer += delta;
+ }
+ else
+ {
+ _lockTimer = 0;
+ _prevPos = masterPosition;
+ }
+ }
+
+ if (weapon != null)
+ {
+ if (masterPosition.DistanceSquaredTo(playerPos) > Mathf.Pow(Master.GetWeaponRange(0.7f), 2)) //玩家离开正常射击范围
+ {
+ ChangeState(AIStateEnum.AiFollowUp);
+ }
+ else if (weapon.TriggerIsReady()) //可以攻击
+ {
+ //发起攻击
+ ChangeState(AIStateEnum.AiAttack);
+ }
+ }
+ }
+ }
+ else //目标离开视野
+ {
+ ChangeState(AIStateEnum.AiTailAfter);
+ }
+ }
+
+ private void RunOver(Vector2 targetPos)
+ {
+ var weapon = Master.WeaponPack.ActiveItem;
+ var distance = (int)(weapon == null ? 150 : (Utils.GetConfigRangeStart(weapon.Attribute.Bullet.DistanceRange) * 0.7f));
+ _nextPosition = new Vector2(
+ targetPos.X + Utils.Random.RandomRangeInt(-distance, distance),
+ targetPos.Y + Utils.Random.RandomRangeInt(-distance, distance)
+ );
+ Master.NavigationAgent2D.TargetPosition = _nextPosition;
+ }
+
+ public override void DebugDraw()
+ {
+ Master.DrawLine(new Vector2(0, -8), Master.ToLocal(_nextPosition), Colors.White);
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiTailAfterState.cs b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiTailAfterState.cs
new file mode 100644
index 0000000..4fa2cc5
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/activity/role/enemy/state/AiTailAfterState.cs
@@ -0,0 +1,128 @@
+
+using System;
+using Godot;
+
+namespace EnemyState;
+
+///
+/// AI 发现玩家, 跟随玩家, 但是不在视野范围内
+///
+public class AiTailAfterState : StateBase
+{
+ ///
+ /// 目标是否在视野半径内
+ ///
+ private bool _isInViewRange;
+
+ //导航目标点刷新计时器
+ private float _navigationUpdateTimer = 0;
+ private float _navigationInterval = 0.3f;
+
+ //目标从视野消失时已经过去的时间
+ private float _viewTimer;
+
+ public AiTailAfterState() : base(AIStateEnum.AiTailAfter)
+ {
+ }
+
+ public override void Enter(AIStateEnum prev, params object[] args)
+ {
+ if (Master.LookTarget == null)
+ {
+ throw new Exception("进入 AIAdvancedStateEnum.AiTailAfter 状态时角色没有攻击目标!");
+ }
+
+ _isInViewRange = true;
+ _navigationUpdateTimer = 0;
+ _viewTimer = 0;
+
+ //先检查弹药是否打光
+ if (Master.IsAllWeaponTotalAmmoEmpty())
+ {
+ //再寻找是否有可用的武器
+ var targetWeapon = Master.FindTargetWeapon();
+ if (targetWeapon != null)
+ {
+ ChangeState(AIStateEnum.AiFindAmmo, targetWeapon);
+ }
+ }
+ }
+
+ public override void Process(float delta)
+ {
+ //这个状态下不会有攻击事件, 所以没必要每一帧检查是否弹药耗尽
+
+ var playerPos = Master.LookTarget.GetCenterPosition();
+
+ //更新玩家位置
+ if (_navigationUpdateTimer <= 0)
+ {
+ //每隔一段时间秒更改目标位置
+ _navigationUpdateTimer = _navigationInterval;
+ Master.NavigationAgent2D.TargetPosition = playerPos;
+ }
+ else
+ {
+ _navigationUpdateTimer -= delta;
+ }
+
+ if (!Master.NavigationAgent2D.IsNavigationFinished())
+ {
+ //移动
+ Master.DoMove();
+ }
+ else
+ {
+ //站立
+ Master.DoIdle();
+ }
+ //检测玩家是否在视野内, 如果在, 则切换到 AiTargetInView 状态
+ if (Master.IsInTailAfterViewRange(playerPos))
+ {
+ if (!Master.TestViewRayCast(playerPos)) //看到玩家
+ {
+ //关闭射线检测
+ Master.TestViewRayCastOver();
+ //切换成发现目标状态
+ ChangeState(AIStateEnum.AiFollowUp);
+ return;
+ }
+ else
+ {
+ //关闭射线检测
+ Master.TestViewRayCastOver();
+ }
+ }
+
+ //检测玩家是否在穿墙视野范围内, 直接检测距离即可
+ _isInViewRange = Master.IsInViewRange(playerPos);
+ if (_isInViewRange)
+ {
+ _viewTimer = 0;
+ }
+ else //超出视野
+ {
+ if (_viewTimer > 10) //10秒
+ {
+ ChangeState(AIStateEnum.AiNormal);
+ }
+ else
+ {
+ _viewTimer += delta;
+ }
+ }
+ }
+
+ public override void DebugDraw()
+ {
+ var playerPos = Master.LookTarget.GetCenterPosition();
+ if (_isInViewRange)
+ {
+ Master.DrawLine(new Vector2(0, -8), Master.ToLocal(playerPos), Colors.Orange);
+ }
+ else
+ {
+ Master.DrawLine(new Vector2(0, -8), Master.ToLocal(playerPos), Colors.Blue);
+ }
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/role/player/Player.cs b/DungeonShooting_Godot/src/game/activity/role/player/Player.cs
index 9c1e3b1..87cf9c4 100644
--- a/DungeonShooting_Godot/src/game/activity/role/player/Player.cs
+++ b/DungeonShooting_Godot/src/game/activity/role/player/Player.cs
@@ -6,7 +6,7 @@
/// 玩家角色基类, 所有角色都必须继承该类
///
[Tool]
-public partial class Player : AdvancedRole
+public partial class Player : Role
{
///
/// 获取当前操作的角色
@@ -190,7 +190,7 @@
var enemyList = AffiliationArea.FindIncludeItems(o => o.CollisionWithMask(PhysicsLayer.Enemy));
foreach (var enemy in enemyList)
{
- ((AdvancedEnemy)enemy).Hurt(this, 1000, 0);
+ ((Enemy)enemy).Hurt(this, 1000, 0);
}
}
// //测试用
diff --git a/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs b/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs
index 9a2fd78..5c9ffe8 100644
--- a/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs
+++ b/DungeonShooting_Godot/src/game/activity/weapon/Weapon.cs
@@ -6,7 +6,7 @@
///
/// 武器的基类
///
-public abstract partial class Weapon : ActivityObject, IPackageItem
+public abstract partial class Weapon : ActivityObject, IPackageItem
{
///
/// 武器使用的属性数据, 该属性会根据是否是玩家使用武器, 如果是Ai使用武器, 则会返回 AiUseAttribute 的属性对象
@@ -32,7 +32,7 @@
///
public CampEnum TargetCamp { get; set; }
- public AdvancedRole Master { get; set; }
+ public Role Master { get; set; }
public int PackageIndex { get; set; } = -1;
@@ -148,7 +148,7 @@
///
/// 上一次触发改武器开火的角色, 可能为 null
///
- public AdvancedRole TriggerRole { get; private set; }
+ public Role TriggerRole { get; private set; }
///
/// 上一次触发改武器开火的触发开火攻击的层级, 数据源自于:
@@ -397,7 +397,7 @@
/// 当武器被拾起时调用
///
/// 拾起该武器的角色
- protected virtual void OnPickUp(AdvancedRole master)
+ protected virtual void OnPickUp(Role master)
{
}
@@ -405,7 +405,7 @@
/// 当武器从武器背包中移除时调用
///
/// 移除该武器的角色
- protected virtual void OnRemove(AdvancedRole master)
+ protected virtual void OnRemove(Role master)
{
}
@@ -695,7 +695,7 @@
/// 扳机函数, 调用即视为按下扳机
///
/// 按下扳机的角色, 如果传 null, 则视为走火
- public void Trigger(AdvancedRole triggerRole)
+ public void Trigger(Role triggerRole)
{
//不能触发扳机
if (!NoMasterCanTrigger && Master == null) return;
@@ -1105,7 +1105,7 @@
///
/// 根据触扳机的角色对象判断该角色使用的武器数据
///
- public ExcelConfig.WeaponBase GetUseAttribute(AdvancedRole triggerRole)
+ public ExcelConfig.WeaponBase GetUseAttribute(Role triggerRole)
{
if (triggerRole == null || !triggerRole.IsAi)
{
@@ -1125,7 +1125,7 @@
{
return (uint)TriggerRoleAttackLayer;
}
- return Master != null ? Master.AttackLayer : AdvancedRole.DefaultAttackLayer;
+ return Master != null ? Master.AttackLayer : Role.DefaultAttackLayer;
}
///
@@ -1620,7 +1620,7 @@
{
var result = new CheckInteractiveResult(this);
- if (master is AdvancedRole roleMaster) //碰到角色
+ if (master is Role roleMaster) //碰到角色
{
if (Master == null)
{
@@ -1666,7 +1666,7 @@
public override void Interactive(ActivityObject master)
{
- if (master is AdvancedRole roleMaster) //与role互动
+ if (master is Role roleMaster) //与role互动
{
var holster = roleMaster.WeaponPack;
//查找是否有同类型武器
@@ -1741,7 +1741,7 @@
/// 触发扔掉武器时抛出的效果, 并不会管武器是否在武器背包中
///
/// 触发扔掉该武器的的角色
- public void ThrowWeapon(AdvancedRole master)
+ public void ThrowWeapon(Role master)
{
ThrowWeapon(master, master.GlobalPosition);
}
@@ -1751,7 +1751,7 @@
///
/// 触发扔掉该武器的的角色
/// 投抛起始位置
- public void ThrowWeapon(AdvancedRole master, Vector2 startPosition)
+ public void ThrowWeapon(Role master, Vector2 startPosition)
{
//阴影偏移
ShadowOffset = new Vector2(0, 2);
@@ -1968,7 +1968,7 @@
}
else
{
- var enemy = (AdvancedEnemy)Master;
+ var enemy = (Enemy)Master;
if (enemy.LockTargetTime >= Attribute.AiAttackAttr.LockingTime) //正常射击
{
if (GetDelayedAttackTime() > 0)
diff --git a/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs b/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs
index b5db0d6..ca56de3 100644
--- a/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs
+++ b/DungeonShooting_Godot/src/game/activity/weapon/knife/Knife.cs
@@ -127,7 +127,7 @@
var activityObject = body.AsActivityObject();
if (activityObject != null)
{
- if (activityObject is AdvancedRole role) //碰到角色
+ if (activityObject is Role role) //碰到角色
{
var damage = Utils.Random.RandomConfigRange(Attribute.Bullet.HarmRange);
//计算子弹造成的伤害
@@ -159,7 +159,7 @@
}
//造成伤害
- role.CallDeferred(nameof(AdvancedRole.Hurt), TriggerRole, damage, (role.GetCenterPosition() - GlobalPosition).Angle());
+ role.CallDeferred(nameof(Role.Hurt), TriggerRole, damage, (role.GetCenterPosition() - GlobalPosition).Angle());
}
else if (activityObject is Bullet bullet) //攻击子弹
{
diff --git a/DungeonShooting_Godot/src/game/camera/GameCamera.cs b/DungeonShooting_Godot/src/game/camera/GameCamera.cs
index fd48f9b..b35d63a 100644
--- a/DungeonShooting_Godot/src/game/camera/GameCamera.cs
+++ b/DungeonShooting_Godot/src/game/camera/GameCamera.cs
@@ -49,7 +49,7 @@
///
/// 相机跟随目标
///
- private AdvancedRole _followTarget;
+ private Role _followTarget;
///
/// SubViewportContainer 中的像素偏移, 因为游戏开启了完美像素, SubViewport 节点下的相机运动会造成非常大的抖动,
@@ -119,7 +119,7 @@
///
/// 设置相机跟随目标
///
- public void SetFollowTarget(AdvancedRole target)
+ public void SetFollowTarget(Role target)
{
_followTarget = target;
if (target != null)
@@ -132,7 +132,7 @@
///
/// 获取相机跟随目标
///
- public AdvancedRole GetFollowTarget()
+ public Role GetFollowTarget()
{
return _followTarget;
}
diff --git a/DungeonShooting_Godot/src/game/manager/FireManager.cs b/DungeonShooting_Godot/src/game/manager/FireManager.cs
index 727edf2..3f02409 100644
--- a/DungeonShooting_Godot/src/game/manager/FireManager.cs
+++ b/DungeonShooting_Godot/src/game/manager/FireManager.cs
@@ -218,18 +218,7 @@
Penetration = Utils.Random.RandomConfigRange(bullet.Penetration),
};
- if (role is AdvancedRole advancedRole)
- {
- data.Position = advancedRole.MountPoint.GlobalPosition;
- }
- else if (role is Enemy enemy)
- {
- data.Position = enemy.FirePoint.GlobalPosition;
- }
- else
- {
- data.Position = role.AnimatedSprite.GlobalPosition;
- }
+ data.Position = role.MountPoint.GlobalPosition;
var deviationAngle = Utils.Random.RandomConfigRange(bullet.DeviationAngleRange);
data.Altitude = role.GetFirePointAltitude();
diff --git a/DungeonShooting_Godot/src/game/manager/ResourcePath.cs b/DungeonShooting_Godot/src/game/manager/ResourcePath.cs
index 6914429..cd7de40 100644
--- a/DungeonShooting_Godot/src/game/manager/ResourcePath.cs
+++ b/DungeonShooting_Godot/src/game/manager/ResourcePath.cs
@@ -48,10 +48,7 @@
public const string prefab_prop_buff_BuffProp0013_tscn = "res://prefab/prop/buff/BuffProp0013.tscn";
public const string prefab_prop_buff_BuffProp0014_tscn = "res://prefab/prop/buff/BuffProp0014.tscn";
public const string prefab_role_Enemy0001_tscn = "res://prefab/role/Enemy0001.tscn";
- public const string prefab_role_Enemy0002_tscn = "res://prefab/role/Enemy0002.tscn";
public const string prefab_role_Role0001_tscn = "res://prefab/role/Role0001.tscn";
- public const string prefab_role_template_AdvancedEnemyTemplate_tscn = "res://prefab/role/template/AdvancedEnemyTemplate.tscn";
- public const string prefab_role_template_AdvancedRoleTemplate_tscn = "res://prefab/role/template/AdvancedRoleTemplate.tscn";
public const string prefab_role_template_EnemyTemplate_tscn = "res://prefab/role/template/EnemyTemplate.tscn";
public const string prefab_role_template_RoleTemplate_tscn = "res://prefab/role/template/RoleTemplate.tscn";
public const string prefab_shell_Shell0001_tscn = "res://prefab/shell/Shell0001.tscn";
diff --git a/DungeonShooting_Godot/src/game/room/DungeonManager.cs b/DungeonShooting_Godot/src/game/room/DungeonManager.cs
index 3cf058f..7e70905 100644
--- a/DungeonShooting_Godot/src/game/room/DungeonManager.cs
+++ b/DungeonShooting_Godot/src/game/room/DungeonManager.cs
@@ -3,7 +3,6 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
-using NnormalState;
///
/// 地牢管理器
@@ -566,23 +565,9 @@
//不与玩家处于同一个房间
if (!enemy.IsDestroyed && enemy.AffiliationArea != playerAffiliationArea)
{
- if (enemy is Enemy e)
+ if (enemy.StateController.CurrState != AIStateEnum.AiNormal)
{
- if (e.StateController.CurrState != AINormalStateEnum.AiNormal)
- {
- e.StateController.ChangeState(AINormalStateEnum.AiNormal);
- }
- }
- else if (enemy is AdvancedEnemy ae)
- {
- if (ae.StateController.CurrState != AIAdvancedStateEnum.AiNormal)
- {
- ae.StateController.ChangeState(AIAdvancedStateEnum.AiNormal);
- }
- }
- else
- {
- throw new Exception("World.Enemy_InstanceList 混入了非 Enemy 和 AdvancedEnemy 类型的对象!");
+ enemy.StateController.ChangeState(AIStateEnum.AiNormal);
}
}
}
diff --git a/DungeonShooting_Godot/src/game/room/World.cs b/DungeonShooting_Godot/src/game/room/World.cs
index 1ed2eab..e45156c 100644
--- a/DungeonShooting_Godot/src/game/room/World.cs
+++ b/DungeonShooting_Godot/src/game/room/World.cs
@@ -1,7 +1,6 @@
using System.Collections;
using System.Collections.Generic;
using Godot;
-using NnormalState;
///
/// 游戏世界
@@ -63,7 +62,7 @@
///
/// 记录所有存活的敌人
///
- public List Enemy_InstanceList { get; } = new List();
+ public List Enemy_InstanceList { get; } = new List();
private bool _pause = false;
private List _coroutineList;
@@ -113,14 +112,11 @@
{
if (role != self && !role.IsDestroyed && role.AffiliationArea == self.AffiliationArea)
{
- if (role is AdvancedEnemy advancedEnemy)
+ //将未发现目标的敌人状态置为惊讶状态
+ var controller = role.StateController;
+ if (controller.CurrState == AIStateEnum.AiNormal)
{
- //将未发现目标的敌人状态置为惊讶状态
- var controller = advancedEnemy.StateController;
- if (controller.CurrState == AIAdvancedStateEnum.AiNormal)
- {
- controller.ChangeState(AIAdvancedStateEnum.AiLeaveFor, target);
- }
+ controller.ChangeState(AIStateEnum.AiLeaveFor, target);
}
}
}
diff --git a/DungeonShooting_Godot/src/game/ui/roomUI/RoomUIPanel.cs b/DungeonShooting_Godot/src/game/ui/roomUI/RoomUIPanel.cs
index dbd01d3..9d8132b 100644
--- a/DungeonShooting_Godot/src/game/ui/roomUI/RoomUIPanel.cs
+++ b/DungeonShooting_Godot/src/game/ui/roomUI/RoomUIPanel.cs
@@ -71,10 +71,10 @@
{
foreach (var role in World.Current.Enemy_InstanceList)
{
- if (!role.IsDestroyed && role is AdvancedEnemy advancedEnemy)
+ if (!role.IsDestroyed)
{
- var position = GameApplication.Instance.ViewToGlobalPosition(advancedEnemy.Position);
- DrawString(ResourceManager.DefaultFont16Px, position, advancedEnemy.StateController.CurrState.ToString());
+ var position = GameApplication.Instance.ViewToGlobalPosition(role.Position);
+ DrawString(ResourceManager.DefaultFont16Px, position, role.StateController.CurrState.ToString());
}
}
}