fix: plan being considered empty if the last state is still active

This commit is contained in:
Sara 2025-03-10 23:34:02 +01:00
parent d7dadac070
commit 9b27fbaabf
37 changed files with 354 additions and 220 deletions

View file

@ -7,8 +7,7 @@
[sub_resource type="SphereShape3D" id="SphereShape3D_5pqvg"]
radius = 15.6018
[sub_resource type="SphereShape3D" id="SphereShape3D_drlm2"]
radius = 1.0
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_wwkx3"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ss47r"]
albedo_color = Color(1, 0.24, 0.24, 1)
@ -32,7 +31,7 @@ available_goals_inspector = [ExtResource("1_jwvis"), ExtResource("1_b1qo1")]
unique_name_in_owner = true
[node name="Planner" type="Planner" parent="."]
actions_inspector = [3, 2, 4, 5, 1]
actions_inspector = [3, 2, 4, 1]
unique_name_in_owner = true
[node name="EntityHealth" type="EntityHealth" parent="."]
@ -41,7 +40,7 @@ unique_name_in_owner = true
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="."]
unique_name_in_owner = true
path_desired_distance = 0.5
path_desired_distance = 0.1
target_desired_distance = 0.2
path_height_offset = 0.5
avoidance_enabled = true
@ -77,7 +76,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.31501, 0)
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.999222, -0.0394342, 0, 0.0394342, 0.999222, 0, 1.013, 0)
shape = SubResource("SphereShape3D_drlm2")
shape = SubResource("CapsuleShape3D_wwkx3")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)

View file

@ -3,7 +3,6 @@
[ext_resource type="AnimationLibrary" uid="uid://crkh5gahl2ci6" path="res://Animation/bean_characters.res" id="1_70627"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_q7mxo"]
radius = 0.586696
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_n4q15"]
specular_mode = 1
@ -40,7 +39,7 @@ unique_name_in_owner = true
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="."]
unique_name_in_owner = true
path_desired_distance = 0.2
path_desired_distance = 0.1
target_desired_distance = 0.5
path_height_offset = 0.5
avoidance_enabled = true

View file

@ -9,10 +9,11 @@
[ext_resource type="PackedScene" uid="uid://ulvv4o73s48a" path="res://Environments/Models/KenneyTrains/track.glb" id="7_8fuqb"]
[sub_resource type="NavigationMesh" id="NavigationMesh_8a2j6"]
vertices = PackedVector3Array(-5.65381, 0.408736, -1.70065, -5.40381, 0.408736, -2.70065, -8.40381, 0.408736, -2.95065, -9.15381, 0.408736, -1.70065, -8.15381, 0.408736, -6.70065, -9.15381, 0.408736, -9.20065, 6.09619, 0.408736, -6.70065, 6.34619, 0.408736, -4.70065, 9.34619, 0.408736, -4.70065, 9.34619, 0.408736, -9.20065, -7.40381, 4.65874, -5.70065, -7.40381, 4.65874, -3.70065, -1.65381, 4.65874, -3.70065, -1.65381, 4.65874, -5.70065, -0.403809, 4.65874, -5.70065, -0.403809, 4.65874, -3.70065, 5.34619, 4.65874, -3.70065, 5.34619, 4.65874, -5.70065, -7.15381, 0.408736, -5.45065, -7.15381, 0.408736, -3.95065, -1.90381, 0.408736, -3.95065, -1.90381, 0.408736, -5.45065, -0.153809, 0.408736, -5.45065, -0.153809, 0.408736, -3.95065, 5.09619, 0.408736, -3.95065, 5.09619, 0.408736, -5.45065, 1.84619, 0.408736, -2.70065, 2.09619, 0.408736, -1.70065, 4.84619, 0.408736, -1.45065, 6.09619, 0.408736, -2.70065, 4.84619, 0.408736, 0.799349, 3.09619, 0.408736, 1.04935, 3.34619, 0.408736, 5.29935, 7.09619, 0.408736, 5.29935, 7.34619, 0.408736, 8.04935, 9.34619, 0.408736, 8.04935, -3.90381, 0.408736, -1.45065, 0.346191, 0.408736, -1.45065, -3.90381, 0.408736, 0.799349, 0.346191, 0.408736, 0.799349, -5.90381, 0.408736, 1.04935, -5.40381, 0.408736, 5.29935, -29.1538, 0.408736, 1.04935, -29.1538, 0.408736, 3.04935, -27.1538, 0.408736, 3.04935, -26.9038, 0.408736, 1.29935, -12.6538, 0.408736, 1.29935, -7.40381, 0.408736, 5.54935, -12.4038, 0.408736, 4.79935, -12.1538, 0.408736, 8.04935, -7.40381, 0.408736, 8.04935, -26.1538, 4.65874, 2.29935, -26.1538, 4.65874, 4.29935, -20.4038, 4.65874, 4.29935, -20.4038, 4.65874, 2.29935, -19.1538, 4.65874, 2.29935, -19.1538, 4.65874, 4.29935, -13.4038, 4.65874, 4.29935, -13.4038, 4.65874, 2.29935, -25.9038, 0.408736, 2.54935, -25.9038, 0.408736, 4.04935, -20.6538, 0.408736, 4.04935, -20.6538, 0.408736, 2.54935, -18.9038, 0.408736, 2.54935, -18.9038, 0.408736, 4.04935, -13.6538, 0.408736, 4.04935, -13.6538, 0.408736, 2.54935, -12.6538, 0.408736, 5.29935, -26.9038, 0.408736, 5.29935, -29.1538, 0.408736, 8.04935, -6.40381, 4.65874, 6.29935, -6.40381, 4.65874, 8.04935, -0.653809, 4.65874, 8.04935, -0.653809, 4.65874, 6.29935, 0.596191, 4.65874, 6.29935, 0.596191, 4.65874, 8.04935, 6.34619, 4.65874, 8.04935, 6.34619, 4.65874, 6.29935, -6.15381, 0.408736, 6.54935, -6.15381, 0.408736, 8.04935, -0.903809, 0.408736, 8.04935, -0.903809, 0.408736, 6.54935, 0.846191, 0.408736, 6.54935, 0.846191, 0.408736, 8.04935, 6.09619, 0.408736, 8.04935, 6.09619, 0.408736, 6.54935)
polygons = [PackedInt32Array(1, 0, 2), PackedInt32Array(2, 0, 3), PackedInt32Array(2, 3, 4), PackedInt32Array(4, 3, 5), PackedInt32Array(7, 6, 8), PackedInt32Array(8, 6, 9), PackedInt32Array(4, 5, 6), PackedInt32Array(6, 5, 9), PackedInt32Array(13, 12, 10), PackedInt32Array(10, 12, 11), PackedInt32Array(17, 16, 14), PackedInt32Array(14, 16, 15), PackedInt32Array(21, 20, 18), PackedInt32Array(18, 20, 19), PackedInt32Array(25, 24, 22), PackedInt32Array(22, 24, 23), PackedInt32Array(27, 26, 28), PackedInt32Array(28, 26, 29), PackedInt32Array(31, 30, 32), PackedInt32Array(32, 30, 33), PackedInt32Array(35, 34, 33), PackedInt32Array(28, 29, 30), PackedInt32Array(30, 29, 8), PackedInt32Array(30, 8, 33), PackedInt32Array(33, 8, 35), PackedInt32Array(29, 7, 8), PackedInt32Array(0, 1, 36), PackedInt32Array(36, 1, 37), PackedInt32Array(37, 1, 26), PackedInt32Array(37, 26, 27), PackedInt32Array(37, 39, 36), PackedInt32Array(36, 39, 38), PackedInt32Array(40, 38, 41), PackedInt32Array(41, 38, 39), PackedInt32Array(41, 39, 31), PackedInt32Array(41, 31, 32), PackedInt32Array(44, 43, 45), PackedInt32Array(45, 43, 42), PackedInt32Array(45, 42, 46), PackedInt32Array(46, 42, 40), PackedInt32Array(41, 47, 40), PackedInt32Array(40, 47, 48), PackedInt32Array(40, 48, 46), PackedInt32Array(50, 49, 47), PackedInt32Array(47, 49, 48), PackedInt32Array(54, 53, 51), PackedInt32Array(51, 53, 52), PackedInt32Array(58, 57, 55), PackedInt32Array(55, 57, 56), PackedInt32Array(62, 61, 59), PackedInt32Array(59, 61, 60), PackedInt32Array(66, 65, 63), PackedInt32Array(63, 65, 64), PackedInt32Array(67, 48, 49), PackedInt32Array(44, 68, 43), PackedInt32Array(43, 68, 69), PackedInt32Array(67, 49, 68), PackedInt32Array(68, 49, 69), PackedInt32Array(73, 72, 70), PackedInt32Array(70, 72, 71), PackedInt32Array(77, 76, 74), PackedInt32Array(74, 76, 75), PackedInt32Array(81, 80, 78), PackedInt32Array(78, 80, 79), PackedInt32Array(85, 84, 82), PackedInt32Array(82, 84, 83)]
vertices = PackedVector3Array(-49.9439, 6.15874, -50.0795, -48.4439, 6.15874, -50.0795, -48.6939, 6.15874, -65.8295, -71.1939, 6.15874, -48.8295, -49.9439, 6.15874, -48.5795, -71.1939, 6.15874, -65.8295, 50.0561, 6.15874, -50.0795, 50.0561, 6.15874, -48.3295, 51.8061, 6.15874, -48.5795, 51.8061, 6.15874, -65.8295, -71.1939, 6.15874, 48.6705, -49.9439, 6.15874, 48.4205, 50.0561, 6.15874, 48.1705, 51.8061, 6.15874, 48.4205, 8.05612, 0.408735, -6.32945, 8.05612, 0.408735, -4.57945, 9.05612, 0.408735, -4.57945, 9.05612, 0.408735, -9.07945, -8.69388, 0.408735, -6.07945, -7.19388, 0.408735, -6.07945, -6.69388, 0.408735, -6.82945, -8.69388, 0.408735, -9.07945, 7.55612, 0.408735, -6.82945, 0.0561218, 0.408735, -1.32945, 1.55612, 0.408735, -1.82945, 1.55612, 0.408735, -2.57945, -6.69388, 0.408735, -2.57945, -4.19388, 0.408735, -1.82945, -7.19388, 0.408735, -3.07945, -8.69388, 0.408735, -1.82945, -3.69388, 0.408735, -1.32945, -3.69388, 0.408735, 0.920547, 0.0561218, 0.408735, 0.920547, 2.55612, 0.408735, 4.92055, 2.55612, 0.408735, 1.42055, 0.556122, 0.408735, 1.42055, -5.19388, 0.408735, 1.42055, -5.19388, 0.408735, 4.92055, -5.69388, 4.65874, -5.32945, -5.69388, 4.65874, -4.07945, -0.443878, 4.65874, -4.07945, -0.443878, 4.65874, -5.32945, 1.30612, 4.65874, -5.32945, 1.30612, 4.65874, -4.07945, 6.55612, 4.65874, -4.07945, 6.55612, 4.65874, -5.32945, -5.44388, 0.408735, -5.07945, -5.44388, 0.408735, -4.32945, -0.693878, 0.408735, -4.32945, -0.693878, 0.408735, -5.07945, 1.55612, 0.408735, -5.07945, 1.55612, 0.408735, -4.32945, 6.30612, 0.408735, -4.32945, 6.30612, 0.408735, -5.07945, 8.05612, 0.408735, -3.07945, 9.05612, 0.408735, 7.92055, 7.55612, 0.408735, 5.42055, 7.55612, 0.408735, 7.92055, 4.55612, 0.408735, -1.82945, 7.55612, 0.408735, -2.57945, 5.05612, 0.408735, 0.920547, 5.05612, 0.408735, -1.32945, 4.55612, 0.408735, 1.42055, 7.05612, 0.408735, 4.92055, -27.4439, 0.408735, 4.92055, -27.1939, 0.408735, 1.42055, -28.9439, 0.408735, 1.42055, -28.9439, 0.408735, 7.92055, -12.9439, 0.408735, 7.92055, -12.9439, 0.408735, 5.42055, -26.9439, 0.408735, 5.42055, -7.69388, 0.408735, 7.92055, -7.69388, 0.408735, 6.67055, -8.44388, 0.408735, 6.17055, -12.1939, 0.408735, 4.92055, -8.44388, 0.408735, 1.42055, -12.4439, 0.408735, 1.42055, -25.9439, 4.65874, 2.67055, -25.9439, 4.65874, 3.92055, -20.6939, 4.65874, 3.92055, -20.6939, 4.65874, 2.67055, -18.9439, 4.65874, 2.67055, -18.9439, 4.65874, 3.92055, -13.6939, 4.65874, 3.92055, -13.6939, 4.65874, 2.67055, -25.6939, 0.408735, 2.92055, -25.6939, 0.408735, 3.67055, -20.9439, 0.408735, 3.67055, -20.9439, 0.408735, 2.92055, -18.6939, 0.408735, 2.92055, -18.6939, 0.408735, 3.67055, -13.9439, 0.408735, 3.67055, -13.9439, 0.408735, 2.92055, -6.19388, 4.65874, 6.42055, -6.19388, 4.65874, 7.92055, -0.943878, 4.65874, 7.92055, -0.943878, 4.65874, 6.42055, 0.806122, 4.65874, 6.42055, 0.806122, 4.65874, 7.92055, 6.05612, 4.65874, 7.92055, 6.05612, 4.65874, 6.42055, -5.94388, 0.408735, 6.92055, -5.94388, 0.408735, 7.67055, -1.19388, 0.408735, 7.67055, -1.19388, 0.408735, 6.67055, 1.05612, 0.408735, 6.67055, 1.05612, 0.408735, 7.67055, 5.80612, 0.408735, 7.67055, 5.80612, 0.408735, 6.67055, 50.0561, 6.15874, 49.9205, 51.8061, 6.15874, 67.6705, -48.6939, 6.15874, 67.6705, -48.4439, 6.15874, 49.9205, -49.9439, 6.15874, 49.9205, -71.1939, 6.15874, 67.6705)
polygons = [PackedInt32Array(2, 1, 0), PackedInt32Array(0, 4, 3), PackedInt32Array(3, 5, 0), PackedInt32Array(0, 5, 2), PackedInt32Array(7, 6, 8), PackedInt32Array(8, 6, 9), PackedInt32Array(1, 2, 6), PackedInt32Array(6, 2, 9), PackedInt32Array(11, 10, 4), PackedInt32Array(4, 10, 3), PackedInt32Array(7, 8, 12), PackedInt32Array(12, 8, 13), PackedInt32Array(15, 14, 16), PackedInt32Array(16, 14, 17), PackedInt32Array(19, 18, 20), PackedInt32Array(20, 18, 21), PackedInt32Array(22, 20, 17), PackedInt32Array(17, 20, 21), PackedInt32Array(17, 14, 22), PackedInt32Array(24, 23, 25), PackedInt32Array(25, 23, 27), PackedInt32Array(25, 27, 26), PackedInt32Array(19, 28, 18), PackedInt32Array(18, 28, 29), PackedInt32Array(23, 32, 30), PackedInt32Array(30, 32, 31), PackedInt32Array(29, 28, 26), PackedInt32Array(27, 29, 26), PackedInt32Array(35, 34, 33), PackedInt32Array(32, 35, 31), PackedInt32Array(31, 35, 36), PackedInt32Array(36, 35, 37), PackedInt32Array(37, 35, 33), PackedInt32Array(23, 30, 27), PackedInt32Array(41, 40, 38), PackedInt32Array(38, 40, 39), PackedInt32Array(45, 44, 42), PackedInt32Array(42, 44, 43), PackedInt32Array(49, 48, 46), PackedInt32Array(46, 48, 47), PackedInt32Array(53, 52, 50), PackedInt32Array(50, 52, 51), PackedInt32Array(15, 16, 54), PackedInt32Array(54, 16, 55), PackedInt32Array(55, 57, 56), PackedInt32Array(24, 25, 58), PackedInt32Array(58, 25, 59), PackedInt32Array(59, 54, 60), PackedInt32Array(60, 54, 56), PackedInt32Array(56, 54, 55), PackedInt32Array(59, 61, 58), PackedInt32Array(34, 62, 33), PackedInt32Array(33, 62, 63), PackedInt32Array(62, 60, 63), PackedInt32Array(63, 60, 56), PackedInt32Array(60, 61, 59), PackedInt32Array(65, 64, 66), PackedInt32Array(66, 64, 67), PackedInt32Array(69, 68, 70), PackedInt32Array(70, 68, 67), PackedInt32Array(67, 64, 70), PackedInt32Array(72, 71, 73), PackedInt32Array(73, 71, 74), PackedInt32Array(74, 71, 69), PackedInt32Array(69, 71, 68), PackedInt32Array(76, 75, 74), PackedInt32Array(74, 75, 73), PackedInt32Array(80, 79, 77), PackedInt32Array(77, 79, 78), PackedInt32Array(84, 83, 81), PackedInt32Array(81, 83, 82), PackedInt32Array(88, 87, 85), PackedInt32Array(85, 87, 86), PackedInt32Array(92, 91, 89), PackedInt32Array(89, 91, 90), PackedInt32Array(96, 95, 93), PackedInt32Array(93, 95, 94), PackedInt32Array(100, 99, 97), PackedInt32Array(97, 99, 98), PackedInt32Array(102, 101, 103), PackedInt32Array(103, 101, 104), PackedInt32Array(108, 107, 105), PackedInt32Array(105, 107, 106), PackedInt32Array(12, 13, 109), PackedInt32Array(109, 13, 110), PackedInt32Array(112, 109, 111), PackedInt32Array(111, 109, 110), PackedInt32Array(113, 112, 111), PackedInt32Array(10, 11, 113), PackedInt32Array(10, 113, 114), PackedInt32Array(114, 113, 111)]
geometry_parsed_geometry_type = 1
geometry_collision_mask = 4294967289
agent_radius = 0.6
[sub_resource type="Goal" id="Goal_yju55"]
desired_state_dict = {
@ -54,47 +55,47 @@ game_mode_prototype = ExtResource("1_4nchg")
[node name="CoverMarker" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(0.999999, 0, -0.00101254, 0, 1, 0, 0.00101254, 0, 0.999999, 3.57243, 0.408736, -1.56782)
transform = Transform3D(0.999999, 0, -0.00101254, 0, 1, 0, 0.00101254, 0, 0.999999, 3.16586, 0.408736, -1.60341)
[node name="CoverMarker3" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(-0.00101258, 0, -0.999999, 0, 1, 0, 0.999999, 0, -0.00101258, 4.84619, 0.408736, -0.211887)
transform = Transform3D(-0.00101258, 0, -0.999999, 0, 1, 0, 0.999999, 0, -0.00101258, 5.05612, 0.408735, -0.198195)
[node name="CoverMarker4" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(-0.999999, 0, 0.00101263, 0, 1, 0, -0.00101263, 0, -0.999999, 3.55349, 0.408736, 0.984021)
transform = Transform3D(-0.999999, 0, 0.00101263, 0, 1, 0, -0.00101263, 0, -0.999999, 3.21966, 0.408735, 1.42055)
[node name="CoverMarker5" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(-0.999999, 0, 0.00101263, 0, 1, 0, -0.00101263, 0, -0.999999, 1.75757, 0.408736, 0.927656)
transform = Transform3D(-0.999999, 0, 0.00101263, 0, 1, 0, -0.00101263, 0, -0.999999, 1.32541, 0.408735, 1.42055)
[node name="CoverMarker6" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(0.00101267, 0, 0.999999, 0, 1, 0, -0.999999, 0, 0.00101267, 0.346191, 0.408736, -0.0968238)
transform = Transform3D(0.00101267, 0, 0.999999, 0, 1, 0, -0.999999, 0, 0.00101267, 0.0561218, 0.408735, -0.256456)
[node name="CoverMarker7" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(0.999999, 0, -0.00101271, 0, 1, 0, 0.00101271, 0, 0.999999, -5.38549, 0.408736, -1.66232)
transform = Transform3D(0.999999, 0, -0.00101271, 0, 1, 0, 0.00101271, 0, 0.999999, -6.17118, 0.408735, -1.82945)
[node name="CoverMarker8" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(-0.999999, 0, 0.00101263, 0, 1, 0, -0.00101263, 0, -0.999999, -5.39349, 0.408736, 0.98556)
transform = Transform3D(-0.999999, 0, 0.00101263, 0, 1, 0, -0.00101263, 0, -0.999999, -4.91025, 0.408735, 1.32601)
[node name="CoverMarker9" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(-0.00101258, 0, -0.999999, 0, 1, 0, 0.999999, 0, -0.00101258, 7.2385, 0.408736, 6.86474)
transform = Transform3D(-0.00101258, 0, -0.999999, 0, 1, 0, 0.999999, 0, -0.00101258, 7.55612, 0.408735, 7.19658)
[node name="CoverMarker16" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(0.00101267, 0, 0.999999, 0, 1, 0, -0.999999, 0, 0.00101267, -7.40381, 0.408736, 6.88524)
transform = Transform3D(0.00101267, 0, 0.999999, 0, 1, 0, -0.999999, 0, 0.00101267, -7.69388, 0.408735, 7.45576)
[node name="CoverMarker17" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(0.00101267, 0, 0.999999, 0, 1, 0, -0.999999, 0, 0.00101267, -27.1371, 0.408736, 3.19975)
transform = Transform3D(0.00101267, 0, 0.999999, 0, 1, 0, -0.999999, 0, 0.00101267, -27.3177, 0.408735, 3.15346)
[node name="CoverMarker18" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(-0.00101258, 0, -0.999999, 0, 1, 0, 0.999999, 0, -0.00101258, -12.5116, 0.408736, 3.28973)
transform = Transform3D(-0.00101258, 0, -0.999999, 0, 1, 0, 0.999999, 0, -0.00101258, -13.9439, 0.408735, 3.27543)
[node name="CoverMarker19" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
@ -102,7 +103,7 @@ transform = Transform3D(0.999999, 4.37114e-08, -0.00101254, -4.36671e-08, 1, 4.3
[node name="CoverMarker20" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(-0.999999, 4.37114e-08, 0.00101245, 4.36671e-08, 1, -4.37556e-08, -0.00101245, -4.37114e-08, -0.999999, -15.033, 0.408736, 5.29935)
transform = Transform3D(-0.999999, 4.37114e-08, 0.00101245, 4.36671e-08, 1, -4.37556e-08, -0.00101245, -4.37114e-08, -0.999999, -15.033, 0.408735, 5.42055)
[node name="CoverMarker21" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
@ -110,35 +111,31 @@ transform = Transform3D(0.999999, 4.37114e-08, -0.00101254, -4.36671e-08, 1, 4.3
[node name="CoverMarker22" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(-0.999999, 4.37114e-08, 0.00101245, 4.36671e-08, 1, -4.37556e-08, -0.00101245, -4.37114e-08, -0.999999, -24.9126, 0.408736, 5.29935)
transform = Transform3D(-0.999999, 4.37114e-08, 0.00101245, 4.36671e-08, 1, -4.37556e-08, -0.00101245, -4.37114e-08, -0.999999, -24.9126, 0.408735, 5.42055)
[node name="CoverMarker2" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(0.999999, 0, -0.00101254, 0, 1, 0, 0.00101254, 0, 0.999999, 1.73439, 0.408736, -1.68834)
transform = Transform3D(0.999999, 0, -0.00101254, 0, 1, 0, 0.00101254, 0, 0.999999, 1.95486, 0.408736, -1.68046)
[node name="CoverMarker10" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(0.999999, 0, -0.00101254, 0, 1, 0, 0.00101254, 0, 0.999999, 4.21452, 0.408736, -6.70065)
transform = Transform3D(0.999999, 0, -0.00101254, 0, 1, 0, 0.00101254, 0, 0.999999, 4.1476, 0.408735, -6.82945)
[node name="CoverMarker11" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(-0.999999, 0, 0.00101263, 0, 1, 0, -0.00101263, 0, -0.999999, 4.66545, 0.408736, -2.70065)
[node name="CoverMarker12" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(-0.999999, 0, 0.00101263, 0, 1, 0, -0.00101263, 0, -0.999999, -6.83875, 0.408736, -2.82023)
transform = Transform3D(-0.999999, 0, 0.00101263, 0, 1, 0, -0.00101263, 0, -0.999999, 4.47796, 0.408735, -2.57945)
[node name="CoverMarker13" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(0.999999, 0, -0.00101271, 0, 1, 0, 0.00101271, 0, 0.999999, -6.67478, 0.408736, -6.70065)
transform = Transform3D(0.999999, 0, -0.00101271, 0, 1, 0, 0.00101271, 0, 0.999999, -6.57121, 0.408735, -6.82945)
[node name="CoverMarker14" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(0.00101267, 0, 0.999999, 0, 1, 0, -0.999999, 0, 0.00101267, -8.28839, 0.408736, -4.682)
transform = Transform3D(0.00101267, 0, 0.999999, 0, 1, 0, -0.999999, 0, 0.00101267, -7.19388, 0.408735, -4.682)
[node name="CoverMarker15" parent="NavRoom" instance=ExtResource("5_ta2oq")]
marker_type = 1
transform = Transform3D(-0.00101276, 0, -0.999999, 0, 1, 0, 0.999999, 0, -0.00101276, 6.32876, 0.408736, -4.56123)
transform = Transform3D(-0.00101276, 0, -0.999999, 0, 1, 0, 0.999999, 0, -0.00101276, 8.05612, 0.408735, -4.59507)
[node name="GenericMarker" parent="NavRoom" instance=ExtResource("5_ta2oq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.44669, 0.408736, 3.42186)
@ -147,10 +144,10 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.44669, 0.408736, 3.42186)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6.55474, 0.408736, 3.32429)
[node name="GenericMarker3" parent="NavRoom" instance=ExtResource("5_ta2oq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.82326, 0.408736, -2.82315)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8.13664, 0.408735, -1.06315)
[node name="GenericMarker4" parent="NavRoom" instance=ExtResource("5_ta2oq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.17274, 0.408736, -7.57197)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.17274, 0.408735, -7.57197)
[node name="GenericMarker5" parent="NavRoom" instance=ExtResource("5_ta2oq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.210692, 0.408736, -7.8647)
@ -165,13 +162,13 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -9.41558, 0.408736, 3.0966)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -12.4156, 0.408736, 7.0966)
[node name="GenericMarker9" parent="NavRoom" instance=ExtResource("5_ta2oq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -19.3762, 0.408736, 7.35681)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -19.2634, 0.408735, 7.18221)
[node name="GenericMarker10" parent="NavRoom" instance=ExtResource("5_ta2oq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -27.5728, 0.408736, 6.3485)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -27.5644, 0.408735, 6.49595)
[node name="GenericMarker11" parent="NavRoom" instance=ExtResource("5_ta2oq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -28.1582, 0.408736, 2.25021)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -28.3032, 0.408735, 2.73795)
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = ExtResource("2_jq6bw")
@ -187,10 +184,10 @@ shadow_blur = 0.1
navigation_mesh = SubResource("NavigationMesh_8a2j6")
[node name="ModernTrain" parent="WorldEnvironment/NavigationRegion3D" instance=ExtResource("6_favl6")]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -4.39772, 0.268182, -4.7114)
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -2.84418, 0.268182, -4.7114)
[node name="ModernTrain2" parent="WorldEnvironment/NavigationRegion3D" instance=ExtResource("6_favl6")]
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 2.30532, 0.268182, -4.7114)
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 3.85886, 0.268182, -4.7114)
[node name="ModernTrain3" parent="WorldEnvironment/NavigationRegion3D" instance=ExtResource("6_favl6")]
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -3.38777, 0.268182, 7.24505)
@ -210,7 +207,7 @@ collision_layer = 7
[node name="CSGBox3D2" type="CSGBox3D" parent="WorldEnvironment/NavigationRegion3D/brushwork"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -9.70088, 2.69981, 0.976042)
size = Vector3(52.4059, 6.08215, 28.8534)
size = Vector3(124.486, 6.08215, 135.111)
[node name="CSGBox3D" type="CSGBox3D" parent="WorldEnvironment/NavigationRegion3D/brushwork"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.17791, 5.23133, -5.44301)
@ -271,6 +268,8 @@ shape = SubResource("BoxShape3D_3h2p0")
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0515832, 0.0826392, -2.12334)
height = 4.0
vertices = PackedVector3Array(-1, 0, 3, -1, 0, -3, 1, 0, -3, 1, 0, 3)
affect_navigation_mesh = true
carve_navigation_mesh = true
[node name="MeshInstance3D" type="MeshInstance3D" parent="WorldEnvironment/NavigationRegion3D/UtilityLock"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.136569, 0)
@ -420,10 +419,10 @@ transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 8.7278
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -8.12793, 1.8999e-07, -8.60757)
[node name="Player2" parent="." instance=ExtResource("3_wl7wm")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.7762, 1.8999e-07, -9.13169)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.74312, 1.8999e-07, -8.7408)
[node name="Tank" parent="." instance=ExtResource("4_0o33v")]
transform = Transform3D(-1, 0, 8.9407e-08, 0, 1, 0, -8.9407e-08, 0, -1, 7.84599, -7.63685e-07, 8.12034)
transform = Transform3D(-1, 0, 8.9407e-08, 0, 1, 0, -8.9407e-08, 0, -1, 7.88995, -7.63685e-07, 7.79155)
[node name="Tank2" parent="." instance=ExtResource("4_0o33v")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -10.4143, -7.63685e-07, 5.91707)

View file

@ -18,4 +18,3 @@ void CharacterUnit::use_weapon() {
this->inventory->get_weapon()->try_use_on(this, target);
}
}

View file

@ -1,15 +1,17 @@
#ifndef CHARACTER_UNIT_HPP
#define CHARACTER_UNIT_HPP
#include "unit.hpp"
#include "inventory.hpp"
#include "unit.hpp"
class CharacterUnit : public Unit {
GDCLASS(CharacterUnit, Unit);
static void _bind_methods();
public:
virtual void _ready() override;
virtual void use_weapon() override;
private:
Inventory *inventory{nullptr};
};

View file

@ -7,10 +7,12 @@
class CharacterWorldState : public UnitWorldState {
GDCLASS(CharacterWorldState, UnitWorldState);
static void _bind_methods();
public:
virtual void _enter_tree() override;
virtual gd::String get_weapon_animation() const override;
virtual bool get_is_in_range() const override;
private:
Inventory *inventory{nullptr};
};

View file

@ -1,10 +1,10 @@
#include "enemy_world_state.hpp"
#include "entity_health.hpp"
#include "unit.hpp"
#include "goap/goal.hpp"
#include "unit.hpp"
#include "utils/godot_macros.hpp"
#include <godot_cpp/variant/utility_functions.hpp>
#include <cmath>
#include <godot_cpp/variant/utility_functions.hpp>
void EnemyWorldState::_bind_methods() {
#define CLASSNAME EnemyWorldState
@ -20,33 +20,45 @@ void EnemyWorldState::_ready() {
}
void EnemyWorldState::_process(double) {
if(!this->health->is_conscious())
return;
if(this->select_and_set_target_on_process) {
gd::UtilityFunctions::print("!!! ", this->get_path(), " requested, select_and_set_target");
this->select_and_set_target_on_process = false;
this->select_and_set_target();
} else if(!this->parent_unit->has_plan()) {
gd::UtilityFunctions::print("!!! ", this->get_path(), " no plan, select_and_set_target");
this->select_and_set_target();
}
}
void EnemyWorldState::on_awareness_entered(gd::Node3D *node) {
if(Unit *unit{gd::Object::cast_to<Unit>(node)}) {
if(Unit * unit{gd::Object::cast_to<Unit>(node)}) {
this->add_aware_unit(unit);
}
}
void EnemyWorldState::on_awareness_exited(gd::Node3D *node) {
if(Unit *unit{gd::Object::cast_to<Unit>(node)})
if(Unit * unit{gd::Object::cast_to<Unit>(node)})
this->remove_aware_unit(unit);
}
void EnemyWorldState::on_damaged(EntityHealth *, int, Unit *source) {
if(source != nullptr && !this->known_enemies.has(source))
this->add_aware_unit(source);
else if(!this->parent_unit->has_plan())
if(!this->parent_unit->has_plan())
this->select_and_set_target();
}
Unit *EnemyWorldState::select_target_from_known_with_priority(float *out_priority) {
Unit *out{nullptr};
*out_priority = -INFINITY;
Unit *out{gd::Object::cast_to<Unit>(target_node)};
// prioritze current target if it is still valid
if(out != nullptr && this->known_enemies.has(out)) {
*out_priority = INFINITY;
return out;
} else {
*out_priority = -INFINITY;
}
for(Unit *unit : this->known_enemies) {
if(!this->get_can_see_node(unit))
continue;
@ -101,6 +113,7 @@ void EnemyWorldState::on_aware_unit_death(Unit *, Unit *dead_entity) {
}
void EnemyWorldState::select_and_set_target() {
gd::UtilityFunctions::print("!!! ", this->get_path(), " select_and_set_target");
this->try_set_target(this->select_target_from_known());
}
@ -130,14 +143,11 @@ gd::Ref<goap::Goal> EnemyWorldState::get_goal_for_target(Unit *unit) {
}
void EnemyWorldState::try_set_target(Unit *unit) {
if(unit == nullptr) {
this->target_node = nullptr;
this->parent_unit->stop_plan();
if(unit == nullptr)
return;
}
gd::Ref<goap::Goal> goal{this->get_goal_for_target(unit)};
this->last_goal = goal;
if(goal.is_valid())
if(goal.is_valid() && goal != this->parent_unit->get_current_goal())
this->parent_unit->set_target_goal(unit, goal);
}

View file

@ -1,8 +1,8 @@
#ifndef ENEMY_WORLD_STATE_HPP
#define ENEMY_WORLD_STATE_HPP
#include "unit_world_state.hpp"
#include "goap/goal.hpp"
#include "unit_world_state.hpp"
#include <godot_cpp/classes/area3d.hpp>
#include <godot_cpp/templates/vector.hpp>
#include <godot_cpp/variant/array.hpp>
@ -14,6 +14,7 @@ class EntityHealth;
class EnemyWorldState : public UnitWorldState {
GDCLASS(EnemyWorldState, UnitWorldState);
static void _bind_methods();
public:
virtual void _ready() override;
virtual void _process(double) override;
@ -23,10 +24,11 @@ public:
/*! Select the target with the highest priority and return it.
* Assigns the priority of found target to out_priority.
* \param out_priority Assigned to highest found priority, cannot be nullptr.
*/
*/
Unit *select_target_from_known_with_priority(float *out_priority);
//! Shorthand for select_target_from_known_with_priority(nullptr)
Unit *select_target_from_known();
private:
void queue_select_and_set_target();
void add_aware_unit(Unit *unit);
@ -38,6 +40,7 @@ private:
void try_set_target(Unit *target);
void set_available_goals_inspector(gd::Array array);
gd::Array get_available_goals_inspector() const;
private:
gd::Callable aware_unit_death{callable_mp(this, &EnemyWorldState::on_aware_unit_death)};
gd::Vector<gd::Ref<goap::Goal>> available_goals{};

View file

@ -10,22 +10,22 @@ void EntityHealth::_bind_methods() {
GDPROPERTY(wounds_max, gd::Variant::INT);
GDSIGNAL("damage",
gd::PropertyInfo(gd::Variant::OBJECT, "health_entity", gd::PROPERTY_HINT_NODE_TYPE, "EntityHealth"),
gd::PropertyInfo(gd::Variant::INT, "change"),
gd::PropertyInfo(gd::Variant::OBJECT, "source", gd::PROPERTY_HINT_NODE_TYPE, "Unit"));
gd::PropertyInfo(gd::Variant::OBJECT, "health_entity", gd::PROPERTY_HINT_NODE_TYPE, "EntityHealth"),
gd::PropertyInfo(gd::Variant::INT, "change"),
gd::PropertyInfo(gd::Variant::OBJECT, "source", gd::PROPERTY_HINT_NODE_TYPE, "Unit"));
GDSIGNAL("heal",
gd::PropertyInfo(gd::Variant::OBJECT, "health_entity", gd::PROPERTY_HINT_NODE_TYPE, "EntityHealth"),
gd::PropertyInfo(gd::Variant::INT, "change"),
gd::PropertyInfo(gd::Variant::OBJECT, "source", gd::PROPERTY_HINT_NODE_TYPE, "Unit"));
gd::PropertyInfo(gd::Variant::OBJECT, "health_entity", gd::PROPERTY_HINT_NODE_TYPE, "EntityHealth"),
gd::PropertyInfo(gd::Variant::INT, "change"),
gd::PropertyInfo(gd::Variant::OBJECT, "source", gd::PROPERTY_HINT_NODE_TYPE, "Unit"));
GDSIGNAL("health_changed",
gd::PropertyInfo(gd::Variant::OBJECT, "health_entity", gd::PROPERTY_HINT_NODE_TYPE, "EntityHealth"),
gd::PropertyInfo(gd::Variant::INT, "change"));
gd::PropertyInfo(gd::Variant::OBJECT, "health_entity", gd::PROPERTY_HINT_NODE_TYPE, "EntityHealth"),
gd::PropertyInfo(gd::Variant::INT, "change"));
GDSIGNAL("death", gd::PropertyInfo(gd::Variant::OBJECT, "source", gd::PROPERTY_HINT_NODE_TYPE, "Unit"));
GDSIGNAL("unconscious", gd::PropertyInfo(gd::Variant::OBJECT, "source", gd::PROPERTY_HINT_NODE_TYPE, "Unit"));
GDSIGNAL("revived",
gd::PropertyInfo(gd::Variant::OBJECT, "health_entity", gd::PROPERTY_HINT_NODE_TYPE, "Unit"),
gd::PropertyInfo(gd::Variant::INT, "healed"),
gd::PropertyInfo(gd::Variant::OBJECT, "source", gd::PROPERTY_HINT_NODE_TYPE, "Unit"));
gd::PropertyInfo(gd::Variant::OBJECT, "health_entity", gd::PROPERTY_HINT_NODE_TYPE, "Unit"),
gd::PropertyInfo(gd::Variant::INT, "healed"),
gd::PropertyInfo(gd::Variant::OBJECT, "source", gd::PROPERTY_HINT_NODE_TYPE, "Unit"));
}
void EntityHealth::_enter_tree() {
@ -71,24 +71,20 @@ void EntityHealth::receive_injury(int incoming_amount, Unit *source) {
float EntityHealth::get_wounds_damage_factor() const {
return this->injury_max == 0
? 1.f
: float(this->injury_current) / float(this->injury_max);
? 1.f
: float(this->injury_current) / float(this->injury_max);
}
bool EntityHealth::is_conscious() const {
return !this->is_dead()
&& (this->wounds_current > 0 || this->wounds_max <= 0);
return !this->is_dead() && (this->wounds_current > 0 || this->wounds_max <= 0);
}
bool EntityHealth::can_be_revived() const {
return this->wounds_current <= 0
&& this->injury_current > 0;
return this->wounds_current <= 0 && this->injury_current > 0;
}
bool EntityHealth::can_be_healed() const {
return this->injury_current > 0
&& this->wounds_current > 0
&& this->wounds_current < this->wounds_max;
return this->injury_current > 0 && this->wounds_current > 0 && this->wounds_current < this->wounds_max;
}
bool EntityHealth::is_dead() const {

View file

@ -11,15 +11,18 @@ class Unit;
class EntityHealth : public gd::Node {
GDCLASS(EntityHealth, gd::Node);
static void _bind_methods();
public:
virtual void _enter_tree() override;
void damaged_by(int amount, Unit *source);
void healed_by(int amount, Unit *source);
void heal_and_revive(int heal_amount, Unit *source);
private:
void receive_wounds(int incoming_amount, Unit *source);
void receive_injury(int incoming_amount, Unit *source);
float get_wounds_damage_factor() const;
public:
bool is_conscious() const;
bool can_be_revived() const;
@ -34,6 +37,7 @@ public:
void set_wounds_max(int max_health);
int get_wounds_max() const;
int get_wounds_current() const;
private:
Stats const *stats;
// long term health

View file

@ -10,10 +10,12 @@ namespace gd = godot;
class GoalMarker : public gd::StaticBody3D {
GDCLASS(GoalMarker, gd::StaticBody3D);
static void _bind_methods();
public:
void destroy_on_forgotten();
void set_goal(gd::Ref<goap::Goal> goal);
gd::Ref<goap::Goal> get_goal() const;
private:
gd::Ref<goap::Goal> goal{};
};

View file

@ -2,6 +2,7 @@
#include "../goap/action.hpp"
#include "../goap/actor_world_state.hpp"
#include "../utils/godot_macros.hpp"
#include "godot_cpp/variant/utility_functions.hpp"
namespace goap {
void State::_bind_methods() {
@ -21,11 +22,13 @@ void State::_process(double) {
}
void State::interrupt_state() {
gd::UtilityFunctions::print("!!! ", this->get_path(), " interrupt_state");
this->_end_state();
this->queue_free();
}
void State::end_state() {
gd::UtilityFunctions::print("!!! ", this->get_path(), " end_state");
this->interrupt_state();
this->emit_signal(this->is_action_done() ? "state_finished" : "state_failed");
this->emit_signal("end_state");

View file

@ -19,10 +19,14 @@ void Inventory::_ready() {
Stats Inventory::get_stats() const {
Stats stats{};
if(this->weapon != nullptr) this->weapon->apply_stats(stats);
if(this->utility != nullptr) this->utility->apply_stats(stats);
if(this->armour != nullptr) this->armour->apply_stats(stats);
if(this->helmet != nullptr) this->helmet->apply_stats(stats);
if(this->weapon != nullptr)
this->weapon->apply_stats(stats);
if(this->utility != nullptr)
this->utility->apply_stats(stats);
if(this->armour != nullptr)
this->armour->apply_stats(stats);
if(this->helmet != nullptr)
this->helmet->apply_stats(stats);
return stats;
}

View file

@ -6,8 +6,8 @@
#include "utils/godot_macros.hpp"
#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/classes/ref_counted.hpp>
#include <godot_cpp/templates/pair.hpp>
#include <godot_cpp/templates/hash_map.hpp>
#include <godot_cpp/templates/pair.hpp>
namespace gd = godot;
class Unit;
@ -15,6 +15,7 @@ class Unit;
class Inventory : public gd::Node {
GDCLASS(Inventory, gd::Node);
static void _bind_methods();
public:
virtual void _ready() override;
Stats get_stats() const;
@ -38,6 +39,7 @@ public:
int get_utility_id() const;
void set_inventory_inspector(gd::Dictionary dict);
gd::Dictionary get_inventory_inspector() const;
private:
Item const *weapon{nullptr};
Item const *utility{nullptr};

View file

@ -1,8 +1,8 @@
#ifndef ITEM_HPP
#define ITEM_HPP
#include "stats.hpp"
#include "goap/action.hpp"
#include "stats.hpp"
#include "utils/godot_macros.hpp"
#include <godot_cpp/templates/hash_set.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
@ -10,22 +10,22 @@
namespace gd = godot;
GDENUM(ItemCapability,
Heal,
Revive,
WeaponEquip,
WeaponRanged,
WeaponMelee,
ArmourEquip,
HeadEquip,
UtilityEquip,
Consume
);
Heal,
Revive,
WeaponEquip,
WeaponRanged,
WeaponMelee,
ArmourEquip,
HeadEquip,
UtilityEquip,
Consume);
typedef int ItemID;
class Unit;
class Item {
friend class ItemDB;
friend class ItemDB;
public:
static gd::StringName get_static_class() {
gd::UtilityFunctions::push_error("Calling Item::get_static_class() on an Item class that does not implement it. Use ITEM_CLASS(*) macro.");
@ -45,6 +45,7 @@ public:
}
Item() = default;
virtual ~Item();
public:
virtual bool can_use_on(Unit *used_by, gd::Object *used_on) const;
bool try_use_on(Unit *used_by, gd::Object *used_on) const;
@ -52,22 +53,27 @@ public:
bool has_capability(ItemCapability const &capability) const;
gd::StringName const &get_animation() const;
ItemID get_id() const;
protected:
virtual void use_on(Unit *used_by, gd::Object *used_on) const;
protected:
gd::StringName animation{"RESET"};
gd::HashSet<uint32_t> capabilities{};
private:
ItemID id{-1};
};
#define ITEM_CLASS(Class_, DisplayName_, Description_)\
friend class ItemDB;\
public: \
_FORCE_INLINE_ static godot::StringName get_static_class() { return #Class_; }\
_FORCE_INLINE_ virtual godot::StringName get_class() const override { return #Class_; }\
_FORCE_INLINE_ virtual godot::String get_display_name() const override { return DisplayName_; }\
_FORCE_INLINE_ virtual godot::String get_description() const override { return Description_; }\
#define ITEM_CLASS(Class_, DisplayName_, Description_) \
friend class ItemDB; \
\
public: \
_FORCE_INLINE_ static godot::StringName get_static_class() { return #Class_; } \
_FORCE_INLINE_ virtual godot::StringName get_class() const override { return #Class_; } \
_FORCE_INLINE_ virtual godot::String get_display_name() const override { return DisplayName_; } \
_FORCE_INLINE_ virtual godot::String get_description() const override { return Description_; } \
\
private:
#endif // !ITEM_HPP

View file

@ -22,7 +22,7 @@ gd::String &ItemDB::StaticData::get_array_hint() {
}
ItemID ItemDB::register_item(Item *item, gd::String item_name) {
item->id = ItemDB::data.items.size()+1;
item->id = ItemDB::data.items.size() + 1;
ItemDB::data.get_hint() += ",";
ItemDB::data.get_hint() += item_name;
ItemDB::data.get_array_hint() = gd::vformat("%s/%s:%s", gd::Variant::INT, gd::PROPERTY_HINT_ENUM, ItemDB::data.get_hint());

View file

@ -14,9 +14,10 @@ class ItemDB {
gd::String &get_array_hint();
gd::String *array_hint{};
gd::String *hint{};
gd::Vector<Item*> items{};
gd::Vector<Item *> items{};
};
static ItemID register_item(Item *item, gd::String item_name);
public:
static Item const *get_item(ItemID id);
static gd::String const &get_enum_hint();
@ -27,6 +28,7 @@ public:
_FORCE_INLINE_ static ItemID register_item() {
return ItemDB::register_item(new TItem(), TItem::get_static_class());
}
private:
static StaticData data;
};

View file

@ -7,19 +7,20 @@
namespace gd = godot;
GDENUM(MarkerType,
Generic,
Cover,
HalfCover
);
Generic,
Cover,
HalfCover);
class NavMarker : public gd::Marker3D {
GDCLASS(NavMarker, gd::Marker3D);
static void _bind_methods();
public:
virtual void _process(double) override;
void set_marker_type(int type);
int get_marker_type() const;
private:
MarkerType type{MarkerType::Generic};
};

View file

@ -1,8 +1,8 @@
#include "nav_room.hpp"
#include "nav_marker.hpp"
#include "utils/godot_macros.hpp"
#include <godot_cpp/classes/global_constants.hpp>
#include <cmath>
#include <godot_cpp/classes/global_constants.hpp>
void NavRoom::_bind_methods() {
#define CLASSNAME NavRoom
@ -32,15 +32,15 @@ void NavRoom::_exit_tree() {
}
void NavRoom::on_child_entered(gd::Node *child) {
if(NavMarker *marker{gd::Object::cast_to<NavMarker>(child)})
if(NavMarker * marker{gd::Object::cast_to<NavMarker>(child)})
this->markers.push_back(marker);
}
gd::Vector<NavRoom*> const &NavRoom::get_neighbours() const {
gd::Vector<NavRoom *> const &NavRoom::get_neighbours() const {
return this->neighbours;
}
gd::Vector<NavMarker*> const &NavRoom::get_markers() const {
gd::Vector<NavMarker *> const &NavRoom::get_markers() const {
return this->markers;
}
@ -53,8 +53,9 @@ gd::Array NavRoom::get_neighbours_inspector() const {
void NavRoom::set_neighbours_inspector(gd::Array array) {
this->neighbours.clear();
while(!array.is_empty()) if(NavRoom *room{gd::Object::cast_to<NavRoom>(array.pop_front())})
this->neighbours.push_back(room);
while(!array.is_empty())
if(NavRoom * room{gd::Object::cast_to<NavRoom>(array.pop_front())})
this->neighbours.push_back(room);
}
gd::Vector<NavRoom*> NavRoom::rooms{};
gd::Vector<NavRoom *> NavRoom::rooms{};

View file

@ -11,23 +11,26 @@ class NavMarker;
class NavRoom : public gd::Node3D {
GDCLASS(NavRoom, gd::Node3D);
static void _bind_methods();
public:
static NavRoom *get_closest_room(gd::Vector3 const &closest_to);
virtual void _enter_tree() override;
virtual void _exit_tree() override;
virtual void on_child_entered(gd::Node *child);
gd::Vector<NavRoom*> const &get_neighbours() const;
gd::Vector<NavMarker*> const &get_markers() const;
gd::Vector<NavRoom *> const &get_neighbours() const;
gd::Vector<NavMarker *> const &get_markers() const;
private:
void set_neighbours_inspector(gd::Array array);
gd::Array get_neighbours_inspector() const;
private:
float radius{1.f};
gd::Vector3 centre{0.f, 0.f, 0.f};
gd::Vector<NavMarker*> markers{};
gd::Vector<NavRoom*> neighbours{};
gd::Vector<NavMarker *> markers{};
gd::Vector<NavRoom *> neighbours{};
static gd::Vector<NavRoom*> rooms;
static gd::Vector<NavRoom *> rooms;
};
#endif // !NAV_ROOM_HPP

View file

@ -3,6 +3,12 @@
#include "character_world_state.hpp"
#include "enemy_world_state.hpp"
#include "entity_health.hpp"
#include "goap/action.hpp"
#include "goap/action_db.hpp"
#include "goap/actor_world_state.hpp"
#include "goap/goal.hpp"
#include "goap/planner.hpp"
#include "goap/state.hpp"
#include "inventory.hpp"
#include "item_db.hpp"
#include "nav_marker.hpp"
@ -16,12 +22,6 @@
#include "unit.hpp"
#include "unit_world_state.hpp"
#include "utility_lock.hpp"
#include "goap/action.hpp"
#include "goap/action_db.hpp"
#include "goap/actor_world_state.hpp"
#include "goap/goal.hpp"
#include "goap/planner.hpp"
#include "goap/state.hpp"
#include "utils/register_types.hpp"
#include <gdextension_interface.h>
#include <godot_cpp/core/class_db.hpp>
@ -30,9 +30,8 @@
namespace gd = godot;
void initialize_gdextension_types(gd::ModuleInitializationLevel p_level)
{
if (p_level != gd::MODULE_INITIALIZATION_LEVEL_SCENE) {
void initialize_gdextension_types(gd::ModuleInitializationLevel p_level) {
if(p_level != gd::MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
utils::godot_cpp_utils_register_types();
@ -78,15 +77,13 @@ void initialize_gdextension_types(gd::ModuleInitializationLevel p_level)
GDREGISTER_RUNTIME_CLASS(RTSPlayer);
}
extern "C"
{
// Initialization
GDExtensionBool GDE_EXPORT metro_rts_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization)
{
gd::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
init_obj.register_initializer(initialize_gdextension_types);
init_obj.set_minimum_library_initialization_level(gd::MODULE_INITIALIZATION_LEVEL_SCENE);
extern "C" {
// Initialization
GDExtensionBool GDE_EXPORT metro_rts_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) {
gd::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
init_obj.register_initializer(initialize_gdextension_types);
init_obj.set_minimum_library_initialization_level(gd::MODULE_INITIALIZATION_LEVEL_SCENE);
return init_obj.init();
}
return init_obj.init();
}
}

View file

@ -1,17 +1,21 @@
#include "rts_actions.hpp"
#include "nav_marker.hpp"
#include "nav_room.hpp"
#include "rts_states.hpp"
#include "utility_lock.hpp"
#include "goap/actor_world_state.hpp"
#include "goap/state.hpp"
#include "godot_cpp/classes/object.hpp"
#include "nav_marker.hpp"
#include "nav_room.hpp"
#include "rts_actions.hpp"
#include "rts_states.hpp"
#include "unit.hpp"
#include "unit_world_state.hpp"
#include "utility_lock.hpp"
#include <cmath>
#include <godot_cpp/core/memory.hpp>
#include <godot_cpp/variant/basis.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <cmath>
MoveToTarget::MoveToTarget()
: Action() {
MoveToTarget::MoveToTarget() :
Action() {
this->required.insert("can_reach_target", true);
this->effects.insert("is_at_target", true);
}
@ -26,8 +30,8 @@ goap::State *MoveToTarget::get_apply_state(goap::ActorWorldState *context) const
return state;
}
UseWeapon::UseWeapon()
: Action() {
UseWeapon::UseWeapon() :
Action() {
this->required.insert("can_see_target", true);
this->required.insert("is_in_range", true);
this->effects.insert("is_target_unconscious", true);
@ -39,8 +43,8 @@ goap::State *UseWeapon::get_apply_state(goap::ActorWorldState *context) const {
return state;
}
FindTarget::FindTarget()
: Action() {
FindTarget::FindTarget() :
Action() {
this->require_state_complete = false;
this->effects.insert("can_see_target", true);
}
@ -52,10 +56,10 @@ goap::State *FindTarget::get_apply_state(goap::ActorWorldState *context) const {
return state;
}
GetInRange::GetInRange()
: Action() {
GetInRange::GetInRange() :
Action() {
this->require_state_complete = false;
this->required.insert("can_see_target", true);
this->effects.insert("can_see_target", true);
this->effects.insert("is_in_range", true);
}
@ -66,9 +70,8 @@ goap::State *GetInRange::get_apply_state(goap::ActorWorldState *context) const {
return state;
}
TankSelfHeal::TankSelfHeal()
: Action() {
this->required.insert("can_see_target", false);
TankSelfHeal::TankSelfHeal() :
Action() {
this->effects.insert("is_health_safe", true);
}
@ -78,8 +81,8 @@ goap::State *TankSelfHeal::get_apply_state(goap::ActorWorldState *) const {
return state;
}
TakeCover::TakeCover()
: Action () {
TakeCover::TakeCover() :
Action() {
this->effects.insert("can_see_target", false);
}
@ -100,6 +103,7 @@ bool TakeCover::procedural_is_possible(goap::ActorWorldState *context) const {
}
goap::State *TakeCover::get_apply_state(goap::ActorWorldState *context) const {
UnitWorldState *unit_state{gd::Object::cast_to<UnitWorldState>(context)};
// positions of the context and the target to hide from
gd::Vector3 const context_position{context->get_world_property("parent_global_position")};
gd::Vector3 const target_position{context->get_world_property("target_global_position")};
@ -110,6 +114,10 @@ goap::State *TakeCover::get_apply_state(goap::ActorWorldState *context) const {
NavMarker *best_marker{nullptr}; // marker with the best score found so far
float best_score{0.f}; // best score found so far, starts at zero because negative values should not be considered
for(NavMarker *marker : room->get_markers()) {
bool can_reach{unit_state->can_reach_point(marker->get_global_position())};
if(!can_reach) {
continue; // skip if unreachable
}
float const score{this->score_cover_marker(marker, target_position, context_position)};
if(score > best_score) {
best_score = score;
@ -128,11 +136,10 @@ goap::State *TakeCover::get_apply_state(goap::ActorWorldState *context) const {
bool TakeCover::is_marker_cover_from(NavMarker *marker, gd::Vector3 const &target_position, gd::Vector3 const &context_position) {
gd::Vector3 const marker_position{marker->get_global_position()};
float const distance_to_target{target_position.distance_to(context_position)};
return marker->get_marker_type() == MarkerType::Cover
&& marker->get_global_basis().get_column(2).dot(marker_position - target_position) < -2.f;
return marker->get_marker_type() == MarkerType::Cover && marker->get_global_basis().get_column(2).dot(marker_position - target_position) < -2.f;
}
float TakeCover::score_cover_marker(class NavMarker* marker, const gd::Vector3& target_position, const gd::Vector3& context_position) const {
float TakeCover::score_cover_marker(class NavMarker *marker, const gd::Vector3 &target_position, const gd::Vector3 &context_position) const {
if(!TakeCover::is_marker_cover_from(marker, target_position, context_position))
return 0.f;
gd::Vector3 const marker_position{marker->get_global_position()}; // position of the marker being considered
@ -173,7 +180,7 @@ goap::State *ActivateLock::get_apply_state(goap::ActorWorldState *context) const
}
Activate *activate = this->create_state<Activate>();
activate->lock = lock;
if(Inventory *inventory{unit_context->get_node<Inventory>("%Inventory")})
if(Inventory * inventory{unit_context->get_node<Inventory>("%Inventory")})
activate->using_item = inventory->get_utility();
return activate;
}

View file

@ -6,6 +6,7 @@
class MoveToTarget : public goap::Action {
GOAP_ACTION(MoveToTarget);
public:
MoveToTarget();
virtual goap::State *get_apply_state(goap::ActorWorldState *context) const override;
@ -13,6 +14,7 @@ public:
class UseWeapon : public goap::Action {
GOAP_ACTION(UseWeapon);
public:
UseWeapon();
virtual goap::State *get_apply_state(goap::ActorWorldState *context) const override;
@ -20,6 +22,7 @@ public:
class FindTarget : public goap::Action {
GOAP_ACTION(FindTarget);
public:
FindTarget();
virtual goap::State *get_apply_state(goap::ActorWorldState *context) const override;
@ -27,6 +30,7 @@ public:
class GetInRange : public goap::Action {
GOAP_ACTION(GetInRange);
public:
GetInRange();
virtual goap::State *get_apply_state(goap::ActorWorldState *context) const override;
@ -34,6 +38,7 @@ public:
class TankSelfHeal : public goap::Action {
GOAP_ACTION(TankSelfHeal);
public:
TankSelfHeal();
virtual goap::State *get_apply_state(goap::ActorWorldState *) const override;
@ -41,10 +46,12 @@ public:
class TakeCover : public goap::Action {
GOAP_ACTION(TakeCover);
public:
TakeCover();
virtual bool procedural_is_possible(goap::ActorWorldState *context) const override;
virtual goap::State *get_apply_state(goap::ActorWorldState *context) const override;
private:
static bool is_marker_cover_from(class NavMarker *marker, gd::Vector3 const &target_position, gd::Vector3 const &context_position);
float score_cover_marker(class NavMarker *marker, gd::Vector3 const &target_position, gd::Vector3 const &context_position) const;
@ -52,6 +59,7 @@ private:
class ActivateLock : public goap::Action {
GOAP_ACTION(ActivateLock);
public:
ActivateLock();
virtual bool procedural_is_possible(goap::ActorWorldState *context) const override;

View file

@ -6,6 +6,7 @@
class RTSGameMode : public utils::GameMode {
GDCLASS(RTSGameMode, utils::GameMode);
static void _bind_methods();
public:
virtual void _ready() override;
};

View file

@ -1,9 +1,9 @@
#include "rts_items.hpp"
#include "unit.hpp"
#include "entity_health.hpp"
#include "unit.hpp"
Medkit::Medkit()
: Item() {
Medkit::Medkit() :
Item() {
this->capabilities.insert(ItemCapability::Consume);
this->capabilities.insert(ItemCapability::Heal);
this->capabilities.insert(ItemCapability::Revive);
@ -17,38 +17,38 @@ void Medkit::use_on(Unit *used_by, gd::Object *used_on) const {
gd::Object::cast_to<Unit>(used_on)->get_entity_health()->healed_by(3, used_by);
}
Handgun::Handgun()
: Item() {
Handgun::Handgun() :
Item() {
this->animation = "fire_weapon";
this->capabilities.insert(ItemCapability::WeaponEquip);
this->capabilities.insert(ItemCapability::WeaponRanged);
}
bool Handgun::can_use_on(Unit *used_by, gd::Object *used_on) const {
return gd::Object::cast_to<Unit>(used_on) != nullptr
&& used_by->get_world_state()->get_can_see_target();
return gd::Object::cast_to<Unit>(used_on) != nullptr && used_by->get_world_state()->get_can_see_target();
}
void Handgun::apply_stats(Stats &stats) const {
stats.weapon_range = 10.f;
}
void Handgun::use_on(Unit* used_by, gd::Object* used_on) const {
void Handgun::use_on(Unit *used_by, gd::Object *used_on) const {
Unit *target{gd::Object::cast_to<Unit>(used_on)};
used_by->aim_at(target);
EntityHealth *health{target->get_entity_health()};
health->damaged_by(4, used_by);
gd::Vector3 const owner_position{used_by->get_global_position()};
gd::Vector3 const target_position{target->get_global_position()};
used_by->look_at(owner_position-(target_position-owner_position));
if(owner_position != target_position)
used_by->look_at(owner_position - (target_position - owner_position));
}
Lasercutter::Lasercutter()
: Item() {
Lasercutter::Lasercutter() :
Item() {
this->capabilities.insert(ItemCapability::UtilityEquip);
}
Welder::Welder()
: Item() {
Welder::Welder() :
Item() {
this->capabilities.insert(ItemCapability::UtilityEquip);
}

View file

@ -5,32 +5,40 @@
class Medkit : public Item {
ITEM_CLASS(Medkit, "Medkit",
"Standard emergency home medical kit. Use to manage wounds and stabilize a person.");
"Standard emergency home medical kit. Use to manage wounds and stabilize a person.");
private:
Medkit();
public:
virtual bool can_use_on(Unit *used_by, gd::Object *used_on) const override;
protected:
virtual void use_on(Unit *used_by, gd::Object *used_on) const override;
};
class Handgun : public Item {
ITEM_CLASS(Handgun, "9mm handgun.",
"A standard issue police firearm.");
"A standard issue police firearm.");
private:
Handgun();
public:
virtual bool can_use_on(Unit *used_by, gd::Object *used_on) const override;
virtual void apply_stats(Stats &stats) const override;
protected:
virtual void use_on(Unit *used_by, gd::Object *used_on) const override;
};
class Lasercutter : public Item {
ITEM_CLASS(Lasercutter, "Lasercutter",
"A laser cutter, use to clear metal obstacles.");
"A laser cutter, use to clear metal obstacles.");
private:
Lasercutter();
public:
// virtual bool can_use_on(Unit *used_by, gd::Object *object) const override;
protected:
@ -39,9 +47,11 @@ protected:
class Welder : public Item {
ITEM_CLASS(Welder, "Welder",
"A welder with tanks intended to be carried on a person's back. Use to repair broken metal parts.");
"A welder with tanks intended to be carried on a person's back. Use to repair broken metal parts.");
private:
Welder();
public:
// virtual bool can_use_on(Unit *used_by, gd::Object *object) const override;
protected:

View file

@ -6,7 +6,6 @@
#include <godot_cpp/classes/curve.hpp>
#include <godot_cpp/classes/packed_scene.hpp>
#include <godot_cpp/classes/physics_direct_space_state3d.hpp>
#include <godot_cpp/classes/physics_direct_space_state3d.hpp>
#include <godot_cpp/classes/physics_ray_query_parameters3d.hpp>
#include <godot_cpp/classes/viewport.hpp>
#include <godot_cpp/classes/world3d.hpp>
@ -127,7 +126,6 @@ bool RTSPlayer::order_activate_object(gd::Node3D *node) {
return true;
}
return unit != nullptr;
}
void RTSPlayer::clear_selected_unit() {
@ -236,4 +234,3 @@ void RTSPlayer::DEBUG_enable_debug(gd::Ref<gd::InputEvent>, float value) {
}
}
#endif

View file

@ -17,6 +17,7 @@ class RTSPlayer : public gd::Node3D,
public utils::IPlayer {
GDCLASS(RTSPlayer, godot::Node3D);
static void _bind_methods();
public:
virtual void _ready() override;
virtual void _process(double delta_time) override;
@ -24,6 +25,7 @@ public:
virtual void setup_player_input(utils::PlayerInput *input) override;
virtual Node *to_node() override;
virtual void spawn_at_position(gd::Transform3D const &at) override;
private:
//! process any changes to mouse position
void process_mouse();
@ -61,10 +63,12 @@ private:
gd::Ref<gd::PackedScene> get_ground_marker_scene() const;
void set_zoom_curve(gd::Ref<gd::Curve> curve);
gd::Ref<gd::Curve> get_zoom_curve() const;
private:
gd::Callable const on_selected_unit_destroyed{callable_mp(this, &RTSPlayer::clear_selected_unit)};
private:
Unit* selected_unit{nullptr};
Unit *selected_unit{nullptr};
bool mmb_down{false};
bool lmb_down{false};

View file

@ -2,9 +2,9 @@
#include "unit.hpp"
#include "utility_lock.hpp"
#include "utils/util_functions.hpp"
#include <cmath>
#include <godot_cpp/core/math.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <cmath>
void MoveTo::_bind_methods() {}
@ -114,18 +114,34 @@ void Activate::_end_state() {
void Animate::_bind_methods() {}
void Animate::_ready() {
gd::UtilityFunctions::print("!!! ", this->get_path(), ": attempting to play animation ", this->animation);
this->anim = this->get_parent()->get_node<gd::AnimationPlayer>("%AnimationPlayer");
if(!this->anim->has_animation(this->animation))
if(!this->anim->has_animation(this->animation)) {
this->end_state();
else this->anim->play(this->animation);
gd::UtilityFunctions::print("!!! ", this->get_path(), ": failed to play animation ", this->animation);
} else {
this->anim->play(this->animation);
this->anim->advance(0);
this->anim->connect("animation_finished", callable_mp(this, &self_type::_on_animation_finished));
gd::UtilityFunctions::print("!!! ", this->get_path(), ": succeeded playing animation ", this->animation);
}
}
void Animate::_process(double) {
bool const animation_finished{!this->anim->is_playing() || this->anim->get_current_animation() != this->animation};
if(this->is_action_done_interrupt() || animation_finished)
if(this->is_action_done_interrupt() || this->is_finished) {
gd::UtilityFunctions::print("!!! animation " , this->animation,
" is done in ", this->get_path(),
" animation finished: ", this->is_finished,
" interrupted: ", this->is_action_done_interrupt());
this->end_state();
}
}
void Animate::_end_state() {
this->anim->stop();
gd::UtilityFunctions::print("!!! ", this->get_path(), " animation ", this->animation, " stopped");
}
void Animate::_on_animation_finished(gd::String new_anim) {
this->is_finished = true;
}

View file

@ -1,8 +1,8 @@
#ifndef RTS_STATES_HPP
#define RTS_STATES_HPP
#include "unit.hpp"
#include "goap/state.hpp"
#include "unit.hpp"
#include <godot_cpp/classes/animation_player.hpp>
#include <godot_cpp/classes/navigation_agent3d.hpp>
@ -13,6 +13,7 @@ class UtilityLock;
class MoveTo : public goap::State {
GDCLASS(MoveTo, goap::State);
static void _bind_methods();
public:
virtual void _ready() override;
virtual void _end_state() override;
@ -22,8 +23,10 @@ public:
float target_delta_position() const;
float distance_to_target() const;
double get_repath_interval() const;
public:
gd::Node3D *target_node{nullptr};
private:
gd::Callable nav_velocity_computed{callable_mp(this, &MoveTo::on_velocity_computed)};
Unit *parent_unit{nullptr};
@ -35,12 +38,14 @@ private:
class Activate : public goap::State {
GDCLASS(Activate, goap::State);
static void _bind_methods();
public:
virtual void _ready() override;
virtual void _process(double) override;
virtual void _end_state() override;
UtilityLock *lock{nullptr};
Item const *using_item{nullptr};
private:
gd::String animation{};
gd::AnimationPlayer *anim{nullptr};
@ -50,12 +55,16 @@ private:
class Animate : public goap::State {
GDCLASS(Animate, goap::State);
static void _bind_methods();
public:
virtual void _ready() override;
virtual void _process(double delta_time) override;
virtual void _end_state() override;
void _on_animation_finished(gd::String new_anim);
gd::StringName animation{};
private:
bool is_finished{false};
gd::AnimationPlayer *anim{nullptr};
};

View file

@ -6,6 +6,7 @@
class TankUnit : public Unit {
GDCLASS(TankUnit, Unit);
static void _bind_methods();
public:
virtual void use_weapon() override;
};

View file

@ -45,6 +45,7 @@ void Unit::_physics_process(double) {
}
void Unit::stop_plan() {
gd::UtilityFunctions::print("!!! ", this->get_path(), " stop_plan");
this->current_goal.unref();
this->current_plan.clear();
this->destroy_state();
@ -71,6 +72,7 @@ void Unit::begin_marker_temporary(GoalMarker *marker) {
}
void Unit::set_target_goal(gd::Node3D *target, gd::Ref<goap::Goal> goal) {
gd::UtilityFunctions::print("!!! ", this->get_path(), " set_target_goal");
this->destroy_state();
this->world_state->set_target_node(target);
this->begin_goal(goal);
@ -96,6 +98,7 @@ void Unit::aim_at(gd::Node3D *target) {
}
void Unit::on_unconscious(Unit *damage_source) {
gd::UtilityFunctions::print("!!! ", this->get_path(), " on_unconscious");
this->destroy_state();
this->emit_signal("plan_failed");
this->anim_player->stop();
@ -103,6 +106,7 @@ void Unit::on_unconscious(Unit *damage_source) {
}
void Unit::on_death(Unit *damage_source) {
gd::UtilityFunctions::print("!!! ", this->get_path(), " on_death");
this->destroy_state();
this->emit_signal("plan_failed");
if(this->anim_player->get_current_animation() != gd::StringName("death")) {
@ -113,13 +117,16 @@ void Unit::on_death(Unit *damage_source) {
void Unit::on_velocity_computed(gd::Vector3 vel) {
gd::Vector3 const pos{this->get_global_position()};
if(vel.x == 0 && vel.z == 0)
if(vel.x == 0 && vel.z == 0) {
this->set_velocity({0.f, 0.f, 0.f});
return;
}
this->set_velocity(vel.normalized() * this->movement_speed);
this->look_at(pos - gd::Vector3{vel.x, 0.f, vel.z});
}
void Unit::destroy_state() {
gd::UtilityFunctions::print("!!! ", this->get_path(), " destroy_state");
if(this->state == nullptr)
return;
if(!this->state->is_queued_for_deletion())
@ -143,8 +150,14 @@ void Unit::next_action() {
if(this->current_plan.is_empty())
return;
goap::Action const *action{this->current_plan.get(0)};
if(action == nullptr || !action->is_possible(this->world_state)) {
if(action == nullptr) {
gd::UtilityFunctions::push_error("Plan interrupted by invalid action in queue");
this->emit_signal("plan_interrupted");
return;
}
if(!action->is_possible(this->world_state)) {
this->emit_signal("plan_interrupted");
gd::UtilityFunctions::push_error("Plan interrupted because action ", action->get_class(), " is impossible");
return;
}
// get next action and apply state
@ -193,10 +206,14 @@ int Unit::get_configure_team() const {
}
bool Unit::has_plan() const {
return !this->current_plan.is_empty();
return !this->current_plan.is_empty() || this->state != nullptr;
}
EntityHealth *Unit::get_entity_health() {
gd::Ref<goap::Goal> Unit::get_current_goal() const {
return this->current_goal;
}
EntityHealth *Unit::get_entity_health() const {
return this->health;
}
@ -211,7 +228,8 @@ float Unit::get_movement_speed() const {
#ifdef DEBUG_ENABLED
gd::String Unit::DEBUG_print_debug_info() {
gd::String debug_info{};
debug_info += gd::String("health: ") + gd::vformat("%d", this->get_entity_health()->get_wounds_current());
debug_info += gd::String("wounds: ") + gd::vformat("%d", this->get_entity_health()->get_wounds_current());
debug_info += gd::String("\ninjury: ") + gd::vformat("%d", this->get_entity_health()->get_injury_current());
debug_info += "\ngoal: ";
if(!this->current_goal.is_valid()) {
debug_info += "No goal assigned";

View file

@ -2,9 +2,9 @@
#define RTS_UNIT_HPP
#include "goal_marker.hpp"
#include "unit_world_state.hpp"
#include "goap/goal.hpp"
#include "goap/planner.hpp"
#include "unit_world_state.hpp"
#include "utils/godot_macros.hpp"
#include <godot_cpp/classes/animation_player.hpp>
#include <godot_cpp/classes/character_body3d.hpp>
@ -15,15 +15,15 @@
namespace gd = godot;
GDENUM(UnitTeam,
Neutral,
Player,
Ally,
Enemy
);
Neutral,
Player,
Ally,
Enemy);
class Unit : public gd::CharacterBody3D {
GDCLASS(Unit, gd::CharacterBody3D);
static void _bind_methods();
public:
virtual void _enter_tree() override;
virtual void _physics_process(double) override;
@ -36,6 +36,7 @@ public:
void aim_at(gd::Node3D *node);
void on_unconscious(Unit *damage_source);
void on_death(Unit *damage_source);
private:
void on_velocity_computed(gd::Vector3 vel);
void destroy_state();
@ -43,18 +44,22 @@ private:
void next_action();
void replan_goal();
void set_goal_and_plan(gd::Ref<goap::Goal> goal);
public: // getter-setters
UnitWorldState *get_world_state() const;
UnitTeam get_team() const;
void set_configure_team(int value);
int get_configure_team() const;
bool has_plan() const;
EntityHealth *get_entity_health();
gd::Ref<goap::Goal> get_current_goal() const;
EntityHealth *get_entity_health() const;
void set_movement_speed(float speed);
float get_movement_speed() const;
private:
gd::Callable on_state_finished{callable_mp(this, &Unit::state_finished)};
gd::Callable on_plan_failed{callable_mp(this, &Unit::replan_goal)};
protected:
goap::Plan current_plan{};
gd::Ref<goap::Goal> current_goal{};

View file

@ -1,12 +1,14 @@
#include "unit_world_state.hpp"
#include "entity_health.hpp"
#include "godot_cpp/classes/navigation_server3d.hpp"
#include "godot_cpp/variant/packed_vector3_array.hpp"
#include "unit.hpp"
#include "utility_lock.hpp"
#include "utils/godot_macros.hpp"
#include "utils/util_functions.hpp"
#include <godot_cpp/classes/physics_direct_space_state3d.hpp>
#include <godot_cpp/classes/physics_ray_query_parameters3d.hpp>
#include <godot_cpp/classes/world3d.hpp>
#include <godot_cpp/classes/physics_direct_space_state3d.hpp>
#include <godot_cpp/variant/callable_method_pointer.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
@ -22,6 +24,7 @@ void UnitWorldState::_bind_methods() {
GDFUNCTION(get_is_target_enemy);
GDFUNCTION(get_is_target_activated);
GDFUNCTION(get_is_in_range);
GDFUNCTION(get_can_reach_target);
GDFUNCTION(get_is_health_safe);
GDFUNCTION(get_parent_global_position);
GDFUNCTION(get_target_global_position);
@ -61,19 +64,19 @@ bool UnitWorldState::get_can_see_node(gd::Node3D *node) const {
// construct list for ignored objects including target node and parent unit
gd::TypedArray<gd::RID> ignore_list{this->parent_unit->get_rid()};
// ignore target if it is a collision object that might obstruct it's own collision test
if(gd::CollisionObject3D *as_collision_object{gd::Object::cast_to<gd::CollisionObject3D>(node)})
if(gd::CollisionObject3D * as_collision_object{gd::Object::cast_to<gd::CollisionObject3D>(node)})
ignore_list.push_back(as_collision_object->get_rid());
// construct raycast query from unit's eye node to vision target. Ignoring parent unit and target if applicable
gd::Ref<gd::PhysicsRayQueryParameters3D> const query{gd::PhysicsRayQueryParameters3D::create(
eyes.origin,
target_position,
0x1 | 0x4, ignore_list
)};
eyes.origin,
target_position,
0x1 | 0x4, ignore_list)};
return this->parent_unit->get_world_3d()->get_direct_space_state()->intersect_ray(query).is_empty();
}
bool UnitWorldState::get_is_at_target() const {
if(this->target_node == nullptr) return true;
if(this->target_node == nullptr)
return true;
gd::Vector3 const target = this->target_node->get_global_position();
gd::Vector3 const current = this->parent_unit->get_global_position();
float const min_dist = this->agent->get_target_desired_distance();
@ -85,7 +88,7 @@ bool UnitWorldState::get_has_target() const {
}
bool UnitWorldState::get_is_target_unconscious() const {
if(EntityHealth *health{this->target_node->get_node<EntityHealth>("%EntityHealth")})
if(EntityHealth * health{this->target_node->get_node<EntityHealth>("%EntityHealth")})
return !health->is_conscious();
return false;
}
@ -100,9 +103,7 @@ bool UnitWorldState::get_is_target_enemy() const {
}
bool UnitWorldState::get_is_unit_enemy(Unit *unit) const {
return unit != nullptr
&& unit->get_team() != UnitTeam::Neutral
&& unit->get_team() != this->parent_unit->get_team();
return unit != nullptr && unit->get_team() != UnitTeam::Neutral && unit->get_team() != this->parent_unit->get_team();
}
bool UnitWorldState::get_is_target_activated() const {
@ -111,8 +112,21 @@ bool UnitWorldState::get_is_target_activated() const {
}
bool UnitWorldState::get_is_in_range() const {
return this->target_node != nullptr
&& this->target_node->get_global_position().distance_squared_to(this->parent_unit->get_global_position()) <= 2.f * 2.f;
return this->target_node != nullptr && this->target_node->get_global_position().distance_squared_to(this->parent_unit->get_global_position()) <= 2.f * 2.f;
}
bool UnitWorldState::can_reach_point(gd::Vector3 point) const {
gd::NavigationServer3D *server{gd::NavigationServer3D::get_singleton()};
godot::PackedVector3Array path{server->map_get_path(this->agent->get_navigation_map(),
this->parent_unit->get_global_position(), point, false, 0xF)};
gd::Vector3 const endpoint{path[path.size() - 1]};
bool const can_reach{!path.is_empty() && gd::Vector2{endpoint.x, endpoint.z}.is_equal_approx({point.x, point.z})};
gd::UtilityFunctions::print("target ", point, " reachable from ", this->get_parent_global_position(), ": ", can_reach, ", endpoint ", path[path.size()-1]);
return can_reach;
}
bool UnitWorldState::get_can_reach_target() const {
return this->can_reach_point(this->get_target_global_position());
}
bool UnitWorldState::get_is_health_safe() const {
@ -142,7 +156,7 @@ gd::Node3D *UnitWorldState::get_target_node() const {
}
void UnitWorldState::cache_property(gd::String property, gd::Variant value, double auto_invalidate_time) {
this->cache[property] = {.value=value, .auto_invalidate_time=auto_invalidate_time};
this->cache[property] = {.value = value, .auto_invalidate_time = auto_invalidate_time};
}
void UnitWorldState::target_destroyed(gd::Node3D *target) {

View file

@ -2,8 +2,8 @@
#define UNIT_WORLD_STATE_HPP
#include "entity_health.hpp"
#include "inventory.hpp"
#include "goap/actor_world_state.hpp"
#include "inventory.hpp"
#include <godot_cpp/classes/navigation_agent3d.hpp>
#include <godot_cpp/classes/node3d.hpp>
@ -19,6 +19,7 @@ struct CachedWorldProperty {
class UnitWorldState : public goap::ActorWorldState {
GDCLASS(UnitWorldState, goap::ActorWorldState);
static void _bind_methods();
public:
virtual void _enter_tree() override;
virtual gd::Variant get_world_property(gd::String property) override;
@ -34,16 +35,21 @@ public:
bool get_is_unit_enemy(Unit *unit) const;
bool get_is_target_activated() const;
virtual bool get_is_in_range() const;
bool can_reach_point(gd::Vector3 point) const;
bool get_can_reach_target() const;
bool get_is_health_safe() const;
gd::Vector3 get_parent_global_position() const;
gd::Vector3 get_target_global_position() const;
void set_target_node(gd::Node3D *node);
gd::Node3D *get_target_node() const;
private:
void cache_property(gd::String property, gd::Variant value, double auto_invalidate_time);
void target_destroyed(gd::Node3D *target);
private:
gd::Callable const target_node_exited_tree{callable_mp(this, &UnitWorldState::target_destroyed)};
protected:
Unit *parent_unit{nullptr};
EntityHealth *health{nullptr};

View file

@ -23,7 +23,8 @@ bool UtilityLock::begin_activate(Item const *with, Unit *by) {
this->_begin_activate(with, by);
this->emit_signal("begin_activate", with->get_id(), by);
return true;
} else return false;
} else
return false;
}
bool UtilityLock::finish_activate(Item const *with, Unit *by) {
@ -33,7 +34,8 @@ bool UtilityLock::finish_activate(Item const *with, Unit *by) {
this->_finish_activate(with, by);
this->emit_signal("finish_activate", with->get_id(), by);
return true;
} else return false;
} else
return false;
}
void UtilityLock::interrupt_activate() {

View file

@ -3,14 +3,15 @@
#include "goal_marker.hpp"
#include "item.hpp"
#include <godot_cpp/variant/utility_functions.hpp>
#include <godot_cpp/templates/hash_set.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
namespace gd = godot;
class UtilityLock : public GoalMarker {
GDCLASS(UtilityLock, GoalMarker)
static void _bind_methods();
public:
enum ActivationState {
Inactive,
@ -31,6 +32,7 @@ public:
gd::Array get_allowed_items() const;
void set_animation_name(gd::String animation);
gd::String get_animation_name() const;
private:
gd::HashSet<Item const *> allowed_items{};
Unit *user{nullptr};