Compare commits
33 commits
goap-plann
...
main
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a2c1cd5e57 | ||
![]() |
17c5d1510c | ||
![]() |
039c0c1ab5 | ||
![]() |
7796d67206 | ||
![]() |
9b57569394 | ||
![]() |
e882b63b28 | ||
![]() |
febfa76bb3 | ||
![]() |
36d681252b | ||
![]() |
ae8b76bd0c | ||
![]() |
aaf4177c16 | ||
![]() |
53a138459e | ||
![]() |
15f7f5e7c4 | ||
![]() |
1a85848528 | ||
![]() |
028aa48c3a | ||
![]() |
15f9451b92 | ||
![]() |
7b8ba8739e | ||
![]() |
675b2e53ec | ||
![]() |
51d07079b2 | ||
![]() |
9472109d6e | ||
![]() |
fd726c03b9 | ||
![]() |
83e0b16133 | ||
![]() |
1d440672f3 | ||
![]() |
58bf470a89 | ||
![]() |
f0185e64bc | ||
![]() |
7f2d67d9db | ||
![]() |
43728c7e83 | ||
![]() |
73ff6ead46 | ||
![]() |
8d3484163c | ||
![]() |
7d227d105b | ||
![]() |
1095ee24f8 | ||
![]() |
624989a7cd | ||
![]() |
5f076dfb63 | ||
![]() |
ba6bc4fd6d |
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -1,7 +1,7 @@
|
||||||
[submodule "godot-cpp"]
|
[submodule "godot-cpp"]
|
||||||
path = godot-cpp
|
path = godot-cpp
|
||||||
url = https://github.com/godotengine/godot-cpp.git
|
url = https://github.com/godotengine/godot-cpp.git
|
||||||
branch = 4.1
|
branch = 4.2
|
||||||
|
|
||||||
[submodule "src/utils"]
|
[submodule "src/utils"]
|
||||||
path = src/utils
|
path = src/utils
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 4b63d795e4279838d988399f008eec47eb2dcc7f
|
Subproject commit 51c752c46b44769d3b6c661526c364a18ea64781
|
BIN
godot/Environments/Non-Modular/whiteblock-station-level.glb
(Stored with Git LFS)
Normal file
BIN
godot/Environments/Non-Modular/whiteblock-station-level.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
|
@ -0,0 +1,41 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="scene"
|
||||||
|
importer_version=1
|
||||||
|
type="PackedScene"
|
||||||
|
uid="uid://bw3gqiinifef4"
|
||||||
|
path="res://.godot/imported/whiteblock-station-level.glb-0ec8b9e904d919ae6bf68126120ff1a4.scn"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Environments/Non-Modular/whiteblock-station-level.glb"
|
||||||
|
dest_files=["res://.godot/imported/whiteblock-station-level.glb-0ec8b9e904d919ae6bf68126120ff1a4.scn"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
nodes/root_type=""
|
||||||
|
nodes/root_name=""
|
||||||
|
nodes/apply_root_scale=true
|
||||||
|
nodes/root_scale=1.0
|
||||||
|
meshes/ensure_tangents=true
|
||||||
|
meshes/generate_lods=true
|
||||||
|
meshes/create_shadow_meshes=true
|
||||||
|
meshes/light_baking=2
|
||||||
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
|
skins/use_named_skins=true
|
||||||
|
animation/import=true
|
||||||
|
animation/fps=30
|
||||||
|
animation/trimming=false
|
||||||
|
animation/remove_immutable_tracks=true
|
||||||
|
import_script/path=""
|
||||||
|
_subresources={
|
||||||
|
"materials": {
|
||||||
|
"Shadow": {
|
||||||
|
"use_external/enabled": true,
|
||||||
|
"use_external/path": "res://Environments/Special Materials/shadow.tres"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gltf/naming_version=1
|
||||||
|
gltf/embedded_image_handling=1
|
Binary file not shown.
BIN
godot/Environments/Roots/roots_a.glb
(Stored with Git LFS)
Normal file
BIN
godot/Environments/Roots/roots_a.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
34
godot/Environments/Roots/roots_a.glb.import
Normal file
34
godot/Environments/Roots/roots_a.glb.import
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="scene"
|
||||||
|
importer_version=1
|
||||||
|
type="PackedScene"
|
||||||
|
uid="uid://d3apnkhay2yq3"
|
||||||
|
path="res://.godot/imported/roots_a.glb-3fef5f980b3fb4c8f2d3e000d80eb81e.scn"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Environments/Roots/roots_a.glb"
|
||||||
|
dest_files=["res://.godot/imported/roots_a.glb-3fef5f980b3fb4c8f2d3e000d80eb81e.scn"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
nodes/root_type=""
|
||||||
|
nodes/root_name=""
|
||||||
|
nodes/apply_root_scale=true
|
||||||
|
nodes/root_scale=1.0
|
||||||
|
meshes/ensure_tangents=true
|
||||||
|
meshes/generate_lods=true
|
||||||
|
meshes/create_shadow_meshes=true
|
||||||
|
meshes/light_baking=1
|
||||||
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
|
skins/use_named_skins=true
|
||||||
|
animation/import=true
|
||||||
|
animation/fps=30
|
||||||
|
animation/trimming=false
|
||||||
|
animation/remove_immutable_tracks=true
|
||||||
|
import_script/path=""
|
||||||
|
_subresources={}
|
||||||
|
gltf/naming_version=1
|
||||||
|
gltf/embedded_image_handling=1
|
5
godot/Environments/Special Materials/shadow.tres
Normal file
5
godot/Environments/Special Materials/shadow.tres
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[gd_resource type="StandardMaterial3D" format=3 uid="uid://ccujbp2eghtha"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
shading_mode = 0
|
||||||
|
albedo_color = Color(0, 0, 0, 1)
|
|
@ -8,7 +8,6 @@ point_count = 2
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
projectile_scene = ExtResource("1_h12ld")
|
projectile_scene = ExtResource("1_h12ld")
|
||||||
range = 15.0
|
range = 25.0
|
||||||
rounds_per_second = 10.0
|
rounds_per_second = 10.0
|
||||||
allow_automatic = true
|
|
||||||
projectile_speed = SubResource("Curve_tdh3d")
|
projectile_speed = SubResource("Curve_tdh3d")
|
||||||
|
|
|
@ -4,3 +4,7 @@
|
||||||
|
|
||||||
[node name="GameRoot" type="GameRoot3D"]
|
[node name="GameRoot" type="GameRoot3D"]
|
||||||
first_boot_level = ExtResource("1_4g2mr")
|
first_boot_level = ExtResource("1_4g2mr")
|
||||||
|
|
||||||
|
[node name="GlobalWorldState" type="GlobalWorldState" parent="."]
|
||||||
|
process_priority = -1
|
||||||
|
process_physics_priority = -1
|
||||||
|
|
|
@ -1,21 +1,36 @@
|
||||||
[gd_scene load_steps=3 format=3 uid="uid://cqkbxe758jr7p"]
|
[gd_scene load_steps=4 format=3 uid="uid://cqkbxe758jr7p"]
|
||||||
|
|
||||||
[sub_resource type="Curve" id="Curve_bxjan"]
|
[sub_resource type="Curve" id="Curve_bxjan"]
|
||||||
_data = [Vector2(0, 0), 0.0, 3.33407, 0, 0, Vector2(0.430894, 0.692308), 0.730621, 0.730621, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
|
_data = [Vector2(0, 0), 0.0, 3.33407, 0, 0, Vector2(0.430894, 0.692308), 0.730621, 0.730621, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
|
||||||
point_count = 3
|
point_count = 3
|
||||||
|
|
||||||
[sub_resource type="SphereMesh" id="SphereMesh_jkn5p"]
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_i3qba"]
|
||||||
radius = 0.2
|
no_depth_test = true
|
||||||
height = 0.4
|
shading_mode = 0
|
||||||
|
disable_ambient_light = true
|
||||||
|
disable_fog = true
|
||||||
|
albedo_color = Color(1, 0.596078, 0.0705882, 1)
|
||||||
|
billboard_mode = 1
|
||||||
|
grow_amount = -1.0
|
||||||
|
|
||||||
|
[sub_resource type="PlaneMesh" id="PlaneMesh_rug6u"]
|
||||||
|
material = SubResource("StandardMaterial3D_i3qba")
|
||||||
|
size = Vector2(0.3, 0.3)
|
||||||
|
orientation = 2
|
||||||
|
|
||||||
[node name="TunnelsPlayer" type="TunnelsPlayer"]
|
[node name="TunnelsPlayer" type="TunnelsPlayer"]
|
||||||
camera_rotation_ramp = SubResource("Curve_bxjan")
|
camera_rotation_ramp = SubResource("Curve_bxjan")
|
||||||
|
|
||||||
[node name="Camera3D" type="Camera3D" parent="."]
|
[node name="Camera3D" type="Camera3D" parent="."]
|
||||||
transform = Transform3D(-1, -6.99417e-08, 5.24488e-08, 1.59825e-08, 0.443572, 0.896239, -8.59493e-08, 0.896239, -0.443572, -2.38419e-07, 9.56087, -0.526886)
|
transform = Transform3D(-1, -6.99417e-08, 5.24488e-08, 1.59825e-08, 0.443572, 0.896239, -8.59493e-08, 0.896239, -0.443572, -2.38419e-07, 9.56087, -0.526886)
|
||||||
|
cull_mask = 1048571
|
||||||
fov = 100.0
|
fov = 100.0
|
||||||
|
near = 0.1
|
||||||
|
far = 1000.0
|
||||||
|
|
||||||
[node name="Reticle" type="Node3D" parent="."]
|
[node name="Reticle" type="Node3D" parent="."]
|
||||||
|
|
||||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Reticle"]
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="Reticle"]
|
||||||
mesh = SubResource("SphereMesh_jkn5p")
|
layers = 8
|
||||||
|
gi_mode = 0
|
||||||
|
mesh = SubResource("PlaneMesh_rug6u")
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=9 format=3 uid="uid://dpda341t6ipiv"]
|
[gd_scene load_steps=11 format=3 uid="uid://dpda341t6ipiv"]
|
||||||
|
|
||||||
[sub_resource type="Curve" id="Curve_7rmf4"]
|
[sub_resource type="Curve" id="Curve_7rmf4"]
|
||||||
min_value = 0.2
|
min_value = 0.2
|
||||||
|
@ -6,20 +6,8 @@ max_value = 2.0
|
||||||
_data = [Vector2(0.145299, 0.2), 0.0, 0.482143, 0, 0, Vector2(0.594017, 2), 0.0, 0.0, 0, 0]
|
_data = [Vector2(0.145299, 0.2), 0.0, 0.482143, 0, 0, Vector2(0.594017, 2), 0.0, 0.0, 0, 0]
|
||||||
point_count = 2
|
point_count = 2
|
||||||
|
|
||||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_3g72p"]
|
|
||||||
height = 1.59321
|
|
||||||
|
|
||||||
[sub_resource type="CapsuleMesh" id="CapsuleMesh_rwcvu"]
|
|
||||||
height = 1.605
|
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_scmx3"]
|
|
||||||
albedo_color = Color(0.94902, 0.909804, 0, 1)
|
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_f5yvh"]
|
|
||||||
size = Vector3(0.125, 0.14, 0.94)
|
|
||||||
|
|
||||||
[sub_resource type="MoveStateArgs" id="MoveStateArgs_ibmkn"]
|
[sub_resource type="MoveStateArgs" id="MoveStateArgs_ibmkn"]
|
||||||
argument_property = &"player_character"
|
argument_property = &"g_player_character"
|
||||||
|
|
||||||
[sub_resource type="Action" id="Action_gtisq"]
|
[sub_resource type="Action" id="Action_gtisq"]
|
||||||
effects = {
|
effects = {
|
||||||
|
@ -27,6 +15,15 @@ effects = {
|
||||||
}
|
}
|
||||||
apply_state = SubResource("MoveStateArgs_ibmkn")
|
apply_state = SubResource("MoveStateArgs_ibmkn")
|
||||||
|
|
||||||
|
[sub_resource type="MoveStateArgs" id="MoveStateArgs_vyebd"]
|
||||||
|
argument_property = &"target"
|
||||||
|
|
||||||
|
[sub_resource type="Action" id="Action_cwmvs"]
|
||||||
|
effects = {
|
||||||
|
"is_near_target": true
|
||||||
|
}
|
||||||
|
apply_state = SubResource("MoveStateArgs_vyebd")
|
||||||
|
|
||||||
[sub_resource type="Goal" id="Goal_sqtwb"]
|
[sub_resource type="Goal" id="Goal_sqtwb"]
|
||||||
goal_state = {
|
goal_state = {
|
||||||
"is_near_player": true
|
"is_near_player": true
|
||||||
|
@ -35,36 +32,51 @@ prerequisites = {
|
||||||
"is_near_player": false
|
"is_near_player": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_3g72p"]
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleMesh" id="CapsuleMesh_rwcvu"]
|
||||||
|
radial_segments = 12
|
||||||
|
rings = 1
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_scmx3"]
|
||||||
|
albedo_color = Color(0.94902, 0.909804, 0, 1)
|
||||||
|
|
||||||
|
[sub_resource type="BoxMesh" id="BoxMesh_f5yvh"]
|
||||||
|
size = Vector3(0.125, 0.14, 0.94)
|
||||||
|
|
||||||
[node name="PlayerCharacter" type="CharacterActor"]
|
[node name="PlayerCharacter" type="CharacterActor"]
|
||||||
rotation_speed_curve = SubResource("Curve_7rmf4")
|
rotation_speed_curve = SubResource("Curve_7rmf4")
|
||||||
collision_layer = 7
|
collision_layer = 7
|
||||||
|
|
||||||
|
[node name="Planner" type="Planner" parent="."]
|
||||||
|
actions = [SubResource("Action_gtisq"), SubResource("Action_cwmvs")]
|
||||||
|
goals = [SubResource("Goal_sqtwb")]
|
||||||
|
|
||||||
[node name="Health" type="Health" parent="."]
|
[node name="Health" type="Health" parent="."]
|
||||||
max_health = 5
|
max_health = 5
|
||||||
|
|
||||||
[node name="ProjectilePool" type="ProjectilePool" parent="."]
|
[node name="ProjectilePool" type="ProjectilePool" parent="."]
|
||||||
|
|
||||||
|
[node name="WeaponMuzzle" type="WeaponMuzzle" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.53551, 1.36699, 0.121549)
|
||||||
|
|
||||||
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="."]
|
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="."]
|
||||||
avoidance_enabled = true
|
avoidance_enabled = true
|
||||||
radius = 1.0
|
radius = 1.0
|
||||||
|
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.802835, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
|
||||||
shape = SubResource("CapsuleShape3D_3g72p")
|
shape = SubResource("CapsuleShape3D_3g72p")
|
||||||
|
|
||||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.8121, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.99491, 0)
|
||||||
|
layers = 2
|
||||||
mesh = SubResource("CapsuleMesh_rwcvu")
|
mesh = SubResource("CapsuleMesh_rwcvu")
|
||||||
surface_material_override/0 = SubResource("StandardMaterial3D_scmx3")
|
surface_material_override/0 = SubResource("StandardMaterial3D_scmx3")
|
||||||
|
|
||||||
[node name="MeshInstance3D2" type="MeshInstance3D" parent="."]
|
[node name="MeshInstance3D2" type="MeshInstance3D" parent="MeshInstance3D"]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.509142, 0.986876, 0.380722)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.509142, 0.361236, 0.380722)
|
||||||
|
layers = 2
|
||||||
mesh = SubResource("BoxMesh_f5yvh")
|
mesh = SubResource("BoxMesh_f5yvh")
|
||||||
|
skeleton = NodePath("../..")
|
||||||
surface_material_override/0 = SubResource("StandardMaterial3D_scmx3")
|
surface_material_override/0 = SubResource("StandardMaterial3D_scmx3")
|
||||||
|
|
||||||
[node name="WeaponMuzzle" type="WeaponMuzzle" parent="."]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.53551, 0.931313, 0)
|
|
||||||
|
|
||||||
[node name="Planner" type="Planner" parent="."]
|
|
||||||
actions = [SubResource("Action_gtisq")]
|
|
||||||
goals = [SubResource("Goal_sqtwb")]
|
|
||||||
|
|
|
@ -42,9 +42,19 @@ fire={
|
||||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
tactics_mode={
|
||||||
|
"deadzone": 0.5,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
[layer_names]
|
[layer_names]
|
||||||
|
|
||||||
|
3d_render/layer_1="Default"
|
||||||
|
3d_render/layer_2="Entities"
|
||||||
|
3d_render/layer_3="TacticsModeOnly"
|
||||||
|
3d_render/layer_4="NoShadows"
|
||||||
3d_physics/layer_1="Default"
|
3d_physics/layer_1="Default"
|
||||||
3d_physics/layer_2="Vision"
|
3d_physics/layer_2="Vision"
|
||||||
3d_physics/layer_3="Hitboxes"
|
3d_physics/layer_3="Hitboxes"
|
||||||
|
3d_physics/layer_4="Markers"
|
||||||
|
|
BIN
godot/test_level.exr
Normal file
BIN
godot/test_level.exr
Normal file
Binary file not shown.
26
godot/test_level.exr.import
Normal file
26
godot/test_level.exr.import
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="2d_array_texture"
|
||||||
|
type="CompressedTexture2DArray"
|
||||||
|
uid="uid://batsfdiilq57g"
|
||||||
|
path="res://.godot/imported/test_level.exr-33d7ba8e62b2edfcc804854db2b1cf50.ctexarray"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://test_level.exr"
|
||||||
|
dest_files=["res://.godot/imported/test_level.exr-33d7ba8e62b2edfcc804854db2b1cf50.ctexarray"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=3
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/channel_pack=1
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
slices/horizontal=1
|
||||||
|
slices/vertical=1
|
File diff suppressed because one or more lines are too long
BIN
models/long_light_a.blend
(Stored with Git LFS)
Normal file
BIN
models/long_light_a.blend
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
models/long_light_a.blend1
Normal file
BIN
models/long_light_a.blend1
Normal file
Binary file not shown.
BIN
models/roots_a.blend
(Stored with Git LFS)
Normal file
BIN
models/roots_a.blend
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
models/roots_a.blend1
Normal file
BIN
models/roots_a.blend1
Normal file
Binary file not shown.
BIN
models/tunnel_wall_segment_a.blend
(Stored with Git LFS)
Normal file
BIN
models/tunnel_wall_segment_a.blend
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
models/tunnel_wall_segment_a.blend1
Normal file
BIN
models/tunnel_wall_segment_a.blend1
Normal file
Binary file not shown.
BIN
models/whiteblock-level.blend
(Stored with Git LFS)
Normal file
BIN
models/whiteblock-level.blend
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
models/whiteblock-level.blend1
Normal file
BIN
models/whiteblock-level.blend1
Normal file
Binary file not shown.
|
@ -1,4 +1,5 @@
|
||||||
#include "character_actor.hpp"
|
#include "character_actor.hpp"
|
||||||
|
#include "goal_marker.hpp"
|
||||||
#include "planner.hpp"
|
#include "planner.hpp"
|
||||||
#include "projectile_pool.hpp"
|
#include "projectile_pool.hpp"
|
||||||
#include "state.hpp"
|
#include "state.hpp"
|
||||||
|
@ -15,34 +16,41 @@ namespace godot {
|
||||||
void CharacterActor::_bind_methods() {
|
void CharacterActor::_bind_methods() {
|
||||||
#define CLASSNAME CharacterActor
|
#define CLASSNAME CharacterActor
|
||||||
GDPROPERTY_HINTED(rotation_speed_curve, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Curve");
|
GDPROPERTY_HINTED(rotation_speed_curve, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Curve");
|
||||||
GDFUNCTION_ARGS(set_velocity_target, "value");
|
GDPROPERTY_HINTED(target, Variant::OBJECT, PROPERTY_HINT_NODE_TYPE, "Node");
|
||||||
|
GDPROPERTY(acceleration, Variant::FLOAT);
|
||||||
|
GDPROPERTY(walk_speed, Variant::FLOAT);
|
||||||
|
GDPROPERTY(sprint_speed, Variant::FLOAT);
|
||||||
|
GDPROPERTY(rotation_speed, Variant::FLOAT);
|
||||||
GDFUNCTION(get_is_near_player);
|
GDFUNCTION(get_is_near_player);
|
||||||
GDFUNCTION(get_player_character);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterActor::_enter_tree() { GDGAMEONLY();
|
void CharacterActor::_enter_tree() { GDGAMEONLY();
|
||||||
this->nav_agent = this->get_node<NavigationAgent3D>("NavigationAgent3D");
|
this->nav_agent = this->get_node<NavigationAgent3D>("NavigationAgent3D");
|
||||||
this->nav_agent->connect("velocity_computed", Callable(this, "set_velocity_target"));
|
this->nav_agent->connect("velocity_computed", callable_mp(this, &CharacterActor::set_velocity_target));
|
||||||
this->target_rotation = this->get_global_transform().get_basis().get_quaternion();
|
this->target_rotation = this->get_global_transform().get_basis().get_quaternion();
|
||||||
this->health = this->get_node<Health>("Health");
|
this->health = this->get_node<Health>("Health");
|
||||||
this->primary_weapon_pool = this->get_node<ProjectilePool>("ProjectilePool");
|
this->primary_weapon_pool = this->get_node<ProjectilePool>("ProjectilePool");
|
||||||
this->planner = this->get_node<goap::Planner>("Planner");
|
this->planner = this->get_node<goap::Planner>("Planner");
|
||||||
|
Ref<TunnelsGameMode> game_mode = GameRoot::get_singleton()->get_game_mode();
|
||||||
|
game_mode->register_player_character(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterActor::_process(double delta_time) { GDGAMEONLY();
|
void CharacterActor::_process(double delta_time) { GDGAMEONLY();
|
||||||
this->process_rotation(delta_time);
|
|
||||||
if(!this->mode_manual) {
|
if(!this->mode_manual) {
|
||||||
this->process_behaviour(delta_time);
|
this->process_behaviour(delta_time);
|
||||||
this->process_navigation(delta_time);
|
this->process_navigation(delta_time);
|
||||||
|
if(!this->velocity_target.is_zero_approx())
|
||||||
|
this->aim_direction(this->velocity_target.normalized());
|
||||||
}
|
}
|
||||||
if(this->firing) {
|
if(this->firing) {
|
||||||
this->try_fire_weapon();
|
this->try_fire_weapon();
|
||||||
}
|
}
|
||||||
|
this->process_rotation(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterActor::_physics_process(double delta_time) { GDGAMEONLY();
|
void CharacterActor::_physics_process(double delta_time) { GDGAMEONLY();
|
||||||
// accelerate towards velocity target
|
// accelerate towards velocity target
|
||||||
Vector3 const new_velocity = this->get_velocity().move_toward(this->velocity_target, delta_time * CharacterActor::ACCELERATION);
|
Vector3 const new_velocity = this->get_velocity().move_toward(this->velocity_target, delta_time * CharacterActor::acceleration);
|
||||||
Vector3 const gravity{Vector3{0.f, this->get_velocity().y - 9.8f, 0.f}};
|
Vector3 const gravity{Vector3{0.f, this->get_velocity().y - 9.8f, 0.f}};
|
||||||
// apply either gravity or walking velocity depending on results
|
// apply either gravity or walking velocity depending on results
|
||||||
this->set_velocity(this->is_on_floor() ? new_velocity : this->get_velocity() + gravity);
|
this->set_velocity(this->is_on_floor() ? new_velocity : this->get_velocity() + gravity);
|
||||||
|
@ -51,7 +59,7 @@ void CharacterActor::_physics_process(double delta_time) { GDGAMEONLY();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterActor::move(Vector3 world_vector) {
|
void CharacterActor::move(Vector3 world_vector) {
|
||||||
this->velocity_target = world_vector * CharacterActor::WALK_SPEED;
|
this->velocity_target = world_vector * CharacterActor::walk_speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterActor::aim(Vector3 at) {
|
void CharacterActor::aim(Vector3 at) {
|
||||||
|
@ -82,6 +90,10 @@ void CharacterActor::shoot_at(Vector3 at) {
|
||||||
this->set_firing(true);
|
this->set_firing(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CharacterActor::force_update_action() {
|
||||||
|
this->set_state(this->planner->get_next_state());
|
||||||
|
}
|
||||||
|
|
||||||
void CharacterActor::set_firing(bool firing) {
|
void CharacterActor::set_firing(bool firing) {
|
||||||
this->firing = firing;
|
this->firing = firing;
|
||||||
}
|
}
|
||||||
|
@ -129,12 +141,40 @@ Vector3 CharacterActor::get_velocity_target() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CharacterActor::get_is_near_player() const {
|
bool CharacterActor::get_is_near_player() const {
|
||||||
return this->get_player_character()->get_global_position().distance_to(this->get_global_position()) < 5.f;
|
return Ref<TunnelsGameMode>(GameRoot::get_singleton()->get_game_mode())
|
||||||
|
->get_player_instance()
|
||||||
|
->get_character()
|
||||||
|
->get_global_position().distance_to(this->get_global_position()) < 5.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterActor *CharacterActor::get_player_character() const {
|
bool CharacterActor::get_is_near_target() const {
|
||||||
Ref<TunnelsGameMode> game_mode = GameRoot::get_singleton()->get_game_mode();
|
GoalMarker *target_marker = Object::cast_to<GoalMarker>(this->target);
|
||||||
return game_mode->get_player_instance()->get_character();
|
Node3D *target_node3d = Object::cast_to<Node3D>(this->target);
|
||||||
|
return target_marker
|
||||||
|
? target_marker->is_point_on(this->get_global_position())
|
||||||
|
: (target_node3d && target_node3d->get_global_position().distance_to(this->get_global_position()) < 5.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 CharacterActor::get_move_target() const {
|
||||||
|
GoalMarker *as_marker = Object::cast_to<GoalMarker>(this->current_state.move_to);
|
||||||
|
if(as_marker)
|
||||||
|
return as_marker->nearest_point_on(this->get_global_position());
|
||||||
|
Node3D *as_node3d = Object::cast_to<Node3D>(this->current_state.move_to);
|
||||||
|
if(as_node3d)
|
||||||
|
return as_node3d->get_global_position();
|
||||||
|
return this->get_global_position();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharacterActor::set_target(Node *target) {
|
||||||
|
this->target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node *CharacterActor::get_target() const {
|
||||||
|
return this->target;
|
||||||
|
}
|
||||||
|
|
||||||
|
goap::Planner *CharacterActor::get_planner() const {
|
||||||
|
return this->planner;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterActor::set_state(goap::State state) {
|
void CharacterActor::set_state(goap::State state) {
|
||||||
|
@ -150,7 +190,7 @@ void CharacterActor::set_state(goap::State state) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
case goap::State::STATE_MOVE_TO:
|
case goap::State::STATE_MOVE_TO:
|
||||||
this->move_to(state.move_to->get_global_position());
|
this->move_to(this->get_move_target());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,8 +202,8 @@ void CharacterActor::process_behaviour(double delta_time) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
case goap::State::STATE_MOVE_TO:
|
case goap::State::STATE_MOVE_TO:
|
||||||
if(this->nav_agent->get_target_position().distance_to(this->current_state.move_to->get_global_position()) > 2.f)
|
if(this->nav_agent->get_target_position().distance_to(this->get_move_target()) > 2.f)
|
||||||
this->nav_agent->set_target_position(this->current_state.move_to->get_global_position());
|
this->nav_agent->set_target_position(this->get_move_target());
|
||||||
break;
|
break;
|
||||||
case goap::State::STATE_ACTIVATE:
|
case goap::State::STATE_ACTIVATE:
|
||||||
break;
|
break;
|
||||||
|
@ -179,7 +219,7 @@ void CharacterActor::process_navigation(double delta_time) {
|
||||||
Vector3 const target_position = this->nav_agent->get_next_path_position();
|
Vector3 const target_position = this->nav_agent->get_next_path_position();
|
||||||
Vector3 const direction = (target_position - this->get_global_position()).normalized();
|
Vector3 const direction = (target_position - this->get_global_position()).normalized();
|
||||||
if(this->nav_agent->get_avoidance_enabled())
|
if(this->nav_agent->get_avoidance_enabled())
|
||||||
this->nav_agent->set_velocity(direction * CharacterActor::WALK_SPEED);
|
this->nav_agent->set_velocity(direction * CharacterActor::walk_speed);
|
||||||
else
|
else
|
||||||
this->move(direction);
|
this->move(direction);
|
||||||
}
|
}
|
||||||
|
@ -196,7 +236,7 @@ void CharacterActor::process_rotation(double delta_time) {
|
||||||
// calculate the angle that still needs to be traveled
|
// calculate the angle that still needs to be traveled
|
||||||
float const angle = current_quaternion.angle_to(target_quaternion);
|
float const angle = current_quaternion.angle_to(target_quaternion);
|
||||||
// calculate the angle amount that can be moved this frame
|
// calculate the angle amount that can be moved this frame
|
||||||
float const angle_step{float(this->rotation_speed_curve->sample(angle) * CharacterActor::ROTATION_SPEED * delta_time)};
|
float const angle_step{float(this->rotation_speed_curve->sample(angle) * CharacterActor::rotation_speed * delta_time)};
|
||||||
// update this object's global transform with the new rotation
|
// update this object's global transform with the new rotation
|
||||||
basis.set_quaternion(angle < angle_step ? target_quaternion // to avoid overshooting, check if the max step is smaller than the angle distance
|
basis.set_quaternion(angle < angle_step ? target_quaternion // to avoid overshooting, check if the max step is smaller than the angle distance
|
||||||
: current_quaternion.slerp(target_quaternion, angle_step / angle)); // convert the angle step to a lerp t value between current and target rotations
|
: current_quaternion.slerp(target_quaternion, angle_step / angle)); // convert the angle step to a lerp t value between current and target rotations
|
||||||
|
@ -215,8 +255,35 @@ void CharacterActor::try_fire_weapon() {
|
||||||
node->set_global_transform(this->weapon_muzzle->get_global_transform());
|
node->set_global_transform(this->weapon_muzzle->get_global_transform());
|
||||||
}
|
}
|
||||||
|
|
||||||
float const CharacterActor::ACCELERATION{20.f};
|
void CharacterActor::set_acceleration(float acceleration) {
|
||||||
float const CharacterActor::WALK_SPEED{3.f};
|
this->acceleration = acceleration;
|
||||||
float const CharacterActor::SPRINT_SPEED{5.f};
|
}
|
||||||
float const CharacterActor::ROTATION_SPEED{10.f};
|
|
||||||
|
float CharacterActor::get_acceleration() const {
|
||||||
|
return this->acceleration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharacterActor::set_walk_speed(float walk_speed) {
|
||||||
|
this->walk_speed = walk_speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CharacterActor::get_walk_speed() const {
|
||||||
|
return this->walk_speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharacterActor::set_sprint_speed(float sprint_speed) {
|
||||||
|
this->sprint_speed = sprint_speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CharacterActor::get_sprint_speed() const {
|
||||||
|
return this->sprint_speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharacterActor::set_rotation_speed(float rotation_speed) {
|
||||||
|
this->rotation_speed = rotation_speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CharacterActor::get_rotation_speed() const {
|
||||||
|
return this->rotation_speed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,17 @@ namespace godot {
|
||||||
class NavigationAgent3D;
|
class NavigationAgent3D;
|
||||||
class TunnelsPlayer;
|
class TunnelsPlayer;
|
||||||
class AnimationPlayer;
|
class AnimationPlayer;
|
||||||
|
|
||||||
namespace goap {
|
namespace goap {
|
||||||
class Planner;
|
class Planner;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Team {
|
||||||
|
CHARACTER_TEAM_WORLD = 0u,
|
||||||
|
CHARACTER_TEAM_PLAYER = 1u,
|
||||||
|
CHARACTER_TEAM_ENEMY = 2u,
|
||||||
|
};
|
||||||
|
|
||||||
class CharacterActor : public CharacterBody3D,
|
class CharacterActor : public CharacterBody3D,
|
||||||
public IHealthEntity {
|
public IHealthEntity {
|
||||||
GDCLASS(CharacterActor, CharacterBody3D);
|
GDCLASS(CharacterActor, CharacterBody3D);
|
||||||
|
@ -36,21 +43,42 @@ public:
|
||||||
// fire weapon at a target position
|
// fire weapon at a target position
|
||||||
// calls aim(at) and set_firing(true)
|
// calls aim(at) and set_firing(true)
|
||||||
void shoot_at(Vector3 at);
|
void shoot_at(Vector3 at);
|
||||||
|
// refresh the current action, ending the previous one
|
||||||
|
void force_update_action();
|
||||||
// getter-setters
|
// getter-setters
|
||||||
void set_firing(bool firing);
|
void set_firing(bool firing);
|
||||||
void set_manual_mode(bool value);
|
void set_manual_mode(bool value);
|
||||||
|
|
||||||
void set_rotation_speed_curve(Ref<Curve> curve);
|
void set_rotation_speed_curve(Ref<Curve> curve);
|
||||||
Ref<Curve> get_rotation_speed_curve() const;
|
Ref<Curve> get_rotation_speed_curve() const;
|
||||||
|
|
||||||
virtual Health *get_health() override;
|
virtual Health *get_health() override;
|
||||||
virtual Health const *get_health() const override;
|
virtual Health const *get_health() const override;
|
||||||
|
|
||||||
void set_character_data(Ref<CharacterData> data);
|
void set_character_data(Ref<CharacterData> data);
|
||||||
void set_weapon_muzzle(Node3D *node);
|
void set_weapon_muzzle(Node3D *node);
|
||||||
void set_velocity_target(Vector3 value);
|
void set_velocity_target(Vector3 value);
|
||||||
Vector3 get_velocity_target() const;
|
Vector3 get_velocity_target() const;
|
||||||
|
|
||||||
|
// planner getters
|
||||||
bool get_is_near_player() const;
|
bool get_is_near_player() const;
|
||||||
CharacterActor *get_player_character() const;
|
bool get_is_near_target() const;
|
||||||
|
Vector3 get_move_target() const;
|
||||||
|
|
||||||
|
// getter - setters
|
||||||
|
void set_target(Node *target);
|
||||||
|
Node *get_target() const;
|
||||||
|
goap::Planner *get_planner() const;
|
||||||
void set_state(goap::State state);
|
void set_state(goap::State state);
|
||||||
|
|
||||||
|
void set_acceleration(float acceleration);
|
||||||
|
float get_acceleration() const;
|
||||||
|
void set_walk_speed(float walk_speed);
|
||||||
|
float get_walk_speed() const;
|
||||||
|
void set_sprint_speed(float sprint_speed);
|
||||||
|
float get_sprint_speed() const;
|
||||||
|
void set_rotation_speed(float rotation_speed);
|
||||||
|
float get_rotation_speed() const;
|
||||||
protected:
|
protected:
|
||||||
void process_behaviour(double delta_time);
|
void process_behaviour(double delta_time);
|
||||||
void process_navigation(double delta_time);
|
void process_navigation(double delta_time);
|
||||||
|
@ -85,10 +113,10 @@ private:
|
||||||
Ref<CharacterData> data;
|
Ref<CharacterData> data;
|
||||||
float fire_interval{0.f}; // derived from 1 / the current weapon's rps
|
float fire_interval{0.f}; // derived from 1 / the current weapon's rps
|
||||||
|
|
||||||
static float const ACCELERATION;
|
float acceleration{20.f};
|
||||||
static float const WALK_SPEED;
|
float walk_speed{3.f};
|
||||||
static float const SPRINT_SPEED;
|
float sprint_speed{5.f};
|
||||||
static float const ROTATION_SPEED;
|
float rotation_speed{10.f};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
#include "global_world_state.hpp"
|
#include "global_world_state.hpp"
|
||||||
#include "character_actor.hpp"
|
#include "character_actor.hpp"
|
||||||
#include "utils/game_root.hpp"
|
#include "utils/game_root.hpp"
|
||||||
|
#include "utils/godot_macros.h"
|
||||||
|
#include <godot_cpp/variant/utility_functions.hpp>
|
||||||
|
|
||||||
namespace godot::goap {
|
namespace godot::goap {
|
||||||
void GlobalWorldState::_bind_methods() {
|
void GlobalWorldState::_bind_methods() {
|
||||||
#define CLASSNAME GlobalWorldState
|
#define CLASSNAME GlobalWorldState
|
||||||
|
GDFUNCTION(get_player_character);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GlobalWorldState::has_singleton() {
|
bool GlobalWorldState::has_singleton() {
|
||||||
|
@ -15,26 +18,27 @@ GlobalWorldState *GlobalWorldState::get_singleton() {
|
||||||
return GlobalWorldState::singleton_instance;
|
return GlobalWorldState::singleton_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalWorldState::_enter_tree() {
|
void GlobalWorldState::_enter_tree() { GDGAMEONLY();
|
||||||
if(GlobalWorldState::singleton_instance == nullptr)
|
if(GlobalWorldState::singleton_instance == nullptr) {
|
||||||
GlobalWorldState::singleton_instance = this;
|
GlobalWorldState::singleton_instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalWorldState::_ready() {
|
|
||||||
this->game_mode = GameRoot::get_singleton()->get_game_mode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalWorldState::_exit_tree() {
|
void GlobalWorldState::_exit_tree() { GDGAMEONLY();
|
||||||
if(GlobalWorldState::singleton_instance == this)
|
if(GlobalWorldState::singleton_instance == this)
|
||||||
GlobalWorldState::singleton_instance = nullptr;
|
GlobalWorldState::singleton_instance = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalWorldState::_process(double delta_time) {
|
void GlobalWorldState::_process(double delta_time) { GDGAMEONLY();
|
||||||
global_state_cache.clear(); // invalidate cache
|
global_state_cache.clear(); // invalidate cache
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 GlobalWorldState::get_player_position() {
|
Vector3 GlobalWorldState::get_player_position() const {
|
||||||
return this->game_mode->get_player_instance()->get_character()->get_global_position();
|
return this->get_player_character()->get_global_position();
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterActor *GlobalWorldState::get_player_character() const {
|
||||||
|
return Ref<TunnelsGameMode>(GameRoot::get_singleton()->get_game_mode())->get_player_instance()->get_character();
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant GlobalWorldState::get_world_property(StringName prop_key) {
|
Variant GlobalWorldState::get_world_property(StringName prop_key) {
|
||||||
|
@ -45,15 +49,19 @@ Variant GlobalWorldState::get_world_property(StringName prop_key) {
|
||||||
else if(global_state_cache.has(prop_key))
|
else if(global_state_cache.has(prop_key))
|
||||||
return global_state_cache.get(prop_key);
|
return global_state_cache.get(prop_key);
|
||||||
// fetch by function name
|
// fetch by function name
|
||||||
StringName const fn = "get_" + prop_key.right(prop_key.length() - 2);
|
StringName const fn = "get_" + prop_key.erase(0, 2);
|
||||||
if(this->has_method(fn)) {
|
if(this->has_method(fn)) {
|
||||||
Variant result = this->call(fn);
|
Variant result = this->call(fn);
|
||||||
// cache and return
|
// cache and return
|
||||||
this->global_state_cache.insert(prop_key, result);
|
this->global_state_cache.insert(prop_key, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
} else {
|
||||||
|
#ifdef DEBUG_ENABLED_ENABLED
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GlobalWorldState *GlobalWorldState::singleton_instance{nullptr};
|
GlobalWorldState *GlobalWorldState::singleton_instance{nullptr};
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,16 +14,15 @@ public:
|
||||||
static GlobalWorldState *get_singleton();
|
static GlobalWorldState *get_singleton();
|
||||||
|
|
||||||
virtual void _enter_tree() override;
|
virtual void _enter_tree() override;
|
||||||
virtual void _ready() override;
|
|
||||||
virtual void _exit_tree() override;
|
virtual void _exit_tree() override;
|
||||||
virtual void _process(double delta_time) override;
|
virtual void _process(double delta_time) override;
|
||||||
|
|
||||||
Vector3 get_player_position();
|
Vector3 get_player_position() const;
|
||||||
|
CharacterActor *get_player_character() const;
|
||||||
|
|
||||||
Variant get_world_property(StringName prop_key);
|
Variant get_world_property(StringName prop_key);
|
||||||
private:
|
private:
|
||||||
WorldState global_state_cache{};
|
WorldState global_state_cache{};
|
||||||
Ref<TunnelsGameMode> game_mode{};
|
|
||||||
static GlobalWorldState *singleton_instance;
|
static GlobalWorldState *singleton_instance;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
63
src/goal_marker.cpp
Normal file
63
src/goal_marker.cpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#include "goal_marker.hpp"
|
||||||
|
#include "godot_cpp/core/math.hpp"
|
||||||
|
#include "planner.hpp"
|
||||||
|
#include "utils/godot_macros.h"
|
||||||
|
#include <godot_cpp/classes/global_constants.hpp>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
void GoalMarker::_bind_methods() {
|
||||||
|
#define CLASSNAME GoalMarker
|
||||||
|
GDPROPERTY_HINTED(goal, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Goal");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GoalMarker::is_point_on(Vector3 point) {
|
||||||
|
return point.distance_squared_to(this->get_global_position()) <= .5f * .5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 GoalMarker::nearest_point_on(Vector3 nearest_to) {
|
||||||
|
return this->get_global_position();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GoalMarker::set_goal(Ref<goap::Goal> goal) {
|
||||||
|
this->goal = goal;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<goap::Goal> GoalMarker::get_goal() const {
|
||||||
|
return this->goal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GoalMarker::set_radius(float radius) {
|
||||||
|
this->radius = radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GoalMarker::get_radius() const {
|
||||||
|
return this->radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CLASSNAME
|
||||||
|
|
||||||
|
void LineGoalMarker::_bind_methods() {
|
||||||
|
#define CLASSNAME LineGoalMarker
|
||||||
|
GDPROPERTY(extent, Variant::FLOAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LineGoalMarker::is_point_on(Vector3 point) {
|
||||||
|
return this->nearest_point_on(point).distance_to(point) < this->radius * this->radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 LineGoalMarker::nearest_point_on(Vector3 point) {
|
||||||
|
Basis const &basis{this->get_global_basis()};
|
||||||
|
Vector3 const left_unit{basis.get_column(0).normalized()};
|
||||||
|
Vector3 const right_vec{this->get_global_position() + left_unit * -extent};
|
||||||
|
float const length{extent * 2.f};
|
||||||
|
return right_vec + left_unit * Math::clamp(left_unit.dot(point - right_vec), 0.f, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineGoalMarker::set_extent(float extent) {
|
||||||
|
this->extent = extent;
|
||||||
|
}
|
||||||
|
|
||||||
|
float LineGoalMarker::get_extent() const {
|
||||||
|
return this->extent;
|
||||||
|
}
|
||||||
|
}
|
43
src/goal_marker.hpp
Normal file
43
src/goal_marker.hpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef GOAL_MARKER_HPP
|
||||||
|
#define GOAL_MARKER_HPP
|
||||||
|
|
||||||
|
#include <godot_cpp/classes/area3d.hpp>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
class CharacterActor;
|
||||||
|
namespace goap {
|
||||||
|
class Planner;
|
||||||
|
class Goal;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GoalMarker : public Area3D {
|
||||||
|
GDCLASS(GoalMarker, Area3D);
|
||||||
|
static void _bind_methods();
|
||||||
|
public:
|
||||||
|
virtual bool is_point_on(Vector3 point);
|
||||||
|
virtual Vector3 nearest_point_on(Vector3 near_to);
|
||||||
|
|
||||||
|
void set_goal(Ref<goap::Goal> goal);
|
||||||
|
Ref<goap::Goal> get_goal() const;
|
||||||
|
void set_radius(float radius);
|
||||||
|
float get_radius() const;
|
||||||
|
protected:
|
||||||
|
Ref<goap::Goal> goal{nullptr};
|
||||||
|
float radius{0.2f};
|
||||||
|
};
|
||||||
|
|
||||||
|
class LineGoalMarker : public GoalMarker {
|
||||||
|
GDCLASS(LineGoalMarker, GoalMarker);
|
||||||
|
static void _bind_methods();
|
||||||
|
public:
|
||||||
|
virtual bool is_point_on(Vector3 point) override;
|
||||||
|
virtual Vector3 nearest_point_on(Vector3 near_to) override;
|
||||||
|
|
||||||
|
void set_extent(float left);
|
||||||
|
float get_extent() const;
|
||||||
|
private:
|
||||||
|
float extent{0.5f};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !GOAL_MARKER_HPP
|
|
@ -19,15 +19,14 @@ void PelletProjectile::_physics_process(double delta_time) { GDGAMEONLY();
|
||||||
float const speed = this->data->get_projectile_speed()->sample(distance_traveled / this->data->get_range());
|
float const speed = this->data->get_projectile_speed()->sample(distance_traveled / this->data->get_range());
|
||||||
this->distance_traveled += speed;
|
this->distance_traveled += speed;
|
||||||
Vector3 const next_position{this->get_global_position() + this->get_global_transform().basis.get_column(2) * speed};
|
Vector3 const next_position{this->get_global_position() + this->get_global_transform().basis.get_column(2) * speed};
|
||||||
if(this->check_hit(next_position) || this->distance_traveled > this->data->get_range()) {
|
if(this->check_hit(next_position) || this->distance_traveled > this->data->get_range())
|
||||||
this->return_to_pool();
|
this->return_to_pool();
|
||||||
} else {
|
else
|
||||||
this->set_global_position(next_position);
|
this->set_global_position(next_position);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool PelletProjectile::check_hit(Vector3 next_position) {
|
bool PelletProjectile::check_hit(Vector3 next_position) {
|
||||||
Ref<PhysicsRayQueryParameters3D> query{PhysicsRayQueryParameters3D::create(this->get_global_position(), next_position, 0x4)};
|
Ref<PhysicsRayQueryParameters3D> query{PhysicsRayQueryParameters3D::create(this->get_global_position(), next_position, 0b101)};
|
||||||
Dictionary hit = this->get_world_3d()->get_direct_space_state()->intersect_ray(query);
|
Dictionary hit = this->get_world_3d()->get_direct_space_state()->intersect_ray(query);
|
||||||
if(hit.is_empty())
|
if(hit.is_empty())
|
||||||
return false;
|
return false;
|
||||||
|
@ -36,7 +35,7 @@ bool PelletProjectile::check_hit(Vector3 next_position) {
|
||||||
return false;
|
return false;
|
||||||
IHealthEntity *health_entity = dynamic_cast<IHealthEntity*>(collider);
|
IHealthEntity *health_entity = dynamic_cast<IHealthEntity*>(collider);
|
||||||
if(health_entity == nullptr)
|
if(health_entity == nullptr)
|
||||||
return false;
|
return true;
|
||||||
health_entity->get_health()->damage(this->data->get_damage());
|
health_entity->get_health()->damage(this->data->get_damage());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#ifndef PELLET_PROJECTILE_HPP
|
#ifndef PELLET_PROJECTILE_HPP
|
||||||
#define PELLET_PROJECTILE_HPP
|
#define PELLET_PROJECTILE_HPP
|
||||||
|
|
||||||
#include "weapon_data.hpp"
|
|
||||||
#include "projectile.hpp"
|
#include "projectile.hpp"
|
||||||
#include <godot_cpp/classes/node3d.hpp>
|
#include <godot_cpp/classes/node3d.hpp>
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
#include <godot_cpp/variant/vector3.hpp>
|
#include <godot_cpp/variant/vector3.hpp>
|
||||||
|
|
||||||
namespace godot::goap {
|
namespace godot::goap {
|
||||||
typedef HashMap<PlannerNode, PlannerNode, PlannerNodeHasher> FromMap;
|
typedef HashMap<PlannerNode, PlannerNode, PlannerNodeHasher> NodeNodeMap;
|
||||||
typedef HashMap<PlannerNode, float, PlannerNodeHasher> ScoreMap;
|
typedef HashMap<PlannerNode, float, PlannerNodeHasher> NodeScoreMap;
|
||||||
typedef HashSet<PlannerNode, PlannerNodeHasher> NodeSet;
|
typedef HashSet<PlannerNode, PlannerNodeHasher> NodeSet;
|
||||||
|
|
||||||
void Goal::_bind_methods() {
|
void Goal::_bind_methods() {
|
||||||
|
@ -64,12 +64,12 @@ void Planner::_bind_methods() {
|
||||||
GDPROPERTY_HINTED(goals, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, GDRESOURCETYPE(Goal));
|
GDPROPERTY_HINTED(goals, Variant::ARRAY, PROPERTY_HINT_ARRAY_TYPE, GDRESOURCETYPE(Goal));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Planner::_ready() {
|
void Planner::_enter_tree() {
|
||||||
this->global_world_state = GlobalWorldState::get_singleton();
|
this->global_world_state = GlobalWorldState::get_singleton();
|
||||||
this->actor = Object::cast_to<CharacterActor>(this->get_parent());
|
this->actor = Object::cast_to<CharacterActor>(this->get_parent());
|
||||||
}
|
}
|
||||||
|
|
||||||
static Vector<Ref<Action>> trace_path(FromMap &map, PlannerNode &end) {
|
static Vector<Ref<Action>> trace_path(NodeNodeMap &map, PlannerNode &end) {
|
||||||
Vector<Ref<Action>> edges{};
|
Vector<Ref<Action>> edges{};
|
||||||
PlannerNode node{end};
|
PlannerNode node{end};
|
||||||
while(node.last_edge.is_valid()) {
|
while(node.last_edge.is_valid()) {
|
||||||
|
@ -82,24 +82,29 @@ static Vector<Ref<Action>> trace_path(FromMap &map, PlannerNode &end) {
|
||||||
Vector<Ref<Action>> Planner::make_plan() {
|
Vector<Ref<Action>> Planner::make_plan() {
|
||||||
// clear cache every planning phase
|
// clear cache every planning phase
|
||||||
this->cached_world_state.clear();
|
this->cached_world_state.clear();
|
||||||
|
// select the most desirable goal available
|
||||||
Ref<Goal> goal = this->select_goal();
|
Ref<Goal> goal = this->select_goal();
|
||||||
if(!goal.is_valid())
|
if(!goal.is_valid()) {
|
||||||
return {};
|
this->plan = {};
|
||||||
|
return this->plan;
|
||||||
|
}
|
||||||
// ordered list of all nodes still being considered
|
// ordered list of all nodes still being considered
|
||||||
Vector<PlannerNode> open{PlannerNode::goal_node(goal->goal_state)};
|
Vector<PlannerNode> open{PlannerNode::goal_node(goal->goal_state)};
|
||||||
PlannerNode first = open.get(0);
|
PlannerNode first = open.get(0);
|
||||||
FromMap from{}; // mapping states to the previous in the path
|
NodeNodeMap from{}; // mapping states to the previous in the path
|
||||||
ScoreMap dist_traveled{}; // mapping states to the shortest found distance from start
|
NodeScoreMap dist_traveled{}; // mapping states to the shortest found distance from start
|
||||||
dist_traveled.insert(first, 0);
|
dist_traveled.insert(first, 0);
|
||||||
ScoreMap best_guess{}; // mapping states to the best guess of the distance to the goal
|
NodeScoreMap best_guess{}; // mapping states to the best guess of the distance to the goal
|
||||||
best_guess.insert(first, first.open_requirements.size());
|
best_guess.insert(first, first.open_requirements.size());
|
||||||
PlannerNode current{}; // state we're checking for neighbours or completion
|
PlannerNode current{}; // state we're checking for neighbours or completion
|
||||||
while(!open.is_empty()) {
|
while(!open.is_empty()) {
|
||||||
// current is the top of the ordered list
|
// current is the top of the ordered list
|
||||||
current = open.get(0);
|
current = open.get(0);
|
||||||
// check if we've reached the goal
|
// check if we've reached the goal
|
||||||
if(current.open_requirements.is_empty())
|
if(current.open_requirements.is_empty()) {
|
||||||
return trace_path(from, current);
|
this->plan = trace_path(from, current);
|
||||||
|
return this->plan;
|
||||||
|
}
|
||||||
// current is no longer considered as it cannot be the end
|
// current is no longer considered as it cannot be the end
|
||||||
open.erase(current);
|
open.erase(current);
|
||||||
// find all neighbours of this state
|
// find all neighbours of this state
|
||||||
|
@ -118,19 +123,15 @@ Vector<Ref<Action>> Planner::make_plan() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {};
|
UtilityFunctions::push_warning("Failed to find a plan satisfying goal");
|
||||||
|
this->plan = {};
|
||||||
|
return this->plan;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Goal> Planner::select_goal() {
|
Ref<Goal> Planner::select_goal() {
|
||||||
for(Ref<Goal> const &goal : this->goals) {
|
for(Ref<Goal> const &goal : this->goals) {
|
||||||
bool can_try{true};
|
if(this->can_do(goal))
|
||||||
for(WorldProperty const &prop : goal->prerequisites) {
|
return goal;
|
||||||
if(prop.value != this->get_world_property(prop.key)) {
|
|
||||||
can_try = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(can_try) return goal;
|
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -148,10 +149,16 @@ Variant Planner::get_world_property(StringName prop_key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Planner::can_do(Ref<Action> action) {
|
bool Planner::can_do(Ref<Action> action) {
|
||||||
for(WorldProperty &prop : action->context_prerequisites) {
|
for(WorldProperty &prop : action->context_prerequisites)
|
||||||
if(this->get_world_property(prop.key) != prop.value)
|
if(this->get_world_property(prop.key) != prop.value)
|
||||||
return false;
|
return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Planner::can_do(Ref<Goal> goal) {
|
||||||
|
for(WorldProperty const &prop : goal->prerequisites)
|
||||||
|
if(this->get_world_property(prop.key) != prop.value)
|
||||||
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +203,7 @@ State Planner::get_next_state() {
|
||||||
this->plan = this->make_plan();
|
this->plan = this->make_plan();
|
||||||
if(this->plan.is_empty())
|
if(this->plan.is_empty())
|
||||||
return State::new_invalid();
|
return State::new_invalid();
|
||||||
return this->plan.get(0)->apply_state->construct(this->actor);
|
return this->plan.get(0)->apply_state->construct(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Planner::set_actions(Array value) {
|
void Planner::set_actions(Array value) {
|
||||||
|
@ -234,4 +241,14 @@ Array Planner::get_goals() const {
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Planner::add_goal(Ref<Goal> goal) {
|
||||||
|
bool can_do = this->can_do(goal);
|
||||||
|
this->goals.insert(0, goal);
|
||||||
|
return can_do;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Planner::remove_goal(Ref<Goal> goal) {
|
||||||
|
this->goals.erase(goal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define GOAP_PLANNER_HPP
|
#define GOAP_PLANNER_HPP
|
||||||
|
|
||||||
#include "action.hpp"
|
#include "action.hpp"
|
||||||
|
#include "goal_marker.hpp"
|
||||||
#include "godot_cpp/variant/variant.hpp"
|
#include "godot_cpp/variant/variant.hpp"
|
||||||
#include <godot_cpp/classes/node.hpp>
|
#include <godot_cpp/classes/node.hpp>
|
||||||
#include <godot_cpp/classes/resource.hpp>
|
#include <godot_cpp/classes/resource.hpp>
|
||||||
|
@ -44,7 +45,7 @@ class Planner : public Node {
|
||||||
GDCLASS(Planner, Node);
|
GDCLASS(Planner, Node);
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
public:
|
public:
|
||||||
virtual void _ready() override;
|
virtual void _enter_tree() override;
|
||||||
|
|
||||||
Vector<Ref<Action>> make_plan();
|
Vector<Ref<Action>> make_plan();
|
||||||
Ref<Goal> select_goal();
|
Ref<Goal> select_goal();
|
||||||
|
@ -52,6 +53,7 @@ public:
|
||||||
Variant get_world_property(StringName prop_key);
|
Variant get_world_property(StringName prop_key);
|
||||||
|
|
||||||
bool can_do(Ref<Action> action);
|
bool can_do(Ref<Action> action);
|
||||||
|
bool can_do(Ref<Goal> goal);
|
||||||
Vector<PlannerNode> find_neighbours_of(PlannerNode &node);
|
Vector<PlannerNode> find_neighbours_of(PlannerNode &node);
|
||||||
Vector<Ref<Action>> find_actions_satisfying(WorldState requirements);
|
Vector<Ref<Action>> find_actions_satisfying(WorldState requirements);
|
||||||
|
|
||||||
|
@ -62,6 +64,8 @@ public:
|
||||||
Array get_actions() const;
|
Array get_actions() const;
|
||||||
void set_goals(Array goals);
|
void set_goals(Array goals);
|
||||||
Array get_goals() const;
|
Array get_goals() const;
|
||||||
|
bool add_goal(Ref<Goal> goal);
|
||||||
|
void remove_goal(Ref<Goal> goal);
|
||||||
private:
|
private:
|
||||||
CharacterActor *actor{nullptr}; // the parent actor of this planner
|
CharacterActor *actor{nullptr}; // the parent actor of this planner
|
||||||
WorldState cached_world_state{}; // the cached worldstate, cleared for every make_plan call
|
WorldState cached_world_state{}; // the cached worldstate, cleared for every make_plan call
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "character_data.hpp"
|
#include "character_data.hpp"
|
||||||
#include "enemy.hpp"
|
#include "enemy.hpp"
|
||||||
#include "global_world_state.hpp"
|
#include "global_world_state.hpp"
|
||||||
|
#include "goal_marker.hpp"
|
||||||
#include "health.hpp"
|
#include "health.hpp"
|
||||||
#include "pellet_projectile.hpp"
|
#include "pellet_projectile.hpp"
|
||||||
#include "planner.hpp"
|
#include "planner.hpp"
|
||||||
|
@ -64,6 +65,8 @@ void initialize_gdextension_types(ModuleInitializationLevel p_level)
|
||||||
ClassDB::register_class<goap::ActivateStateArgs>();
|
ClassDB::register_class<goap::ActivateStateArgs>();
|
||||||
ClassDB::register_class<goap::Goal>();
|
ClassDB::register_class<goap::Goal>();
|
||||||
ClassDB::register_class<goap::Planner>();
|
ClassDB::register_class<goap::Planner>();
|
||||||
|
ClassDB::register_class<GoalMarker>();
|
||||||
|
ClassDB::register_class<LineGoalMarker>();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "state.hpp"
|
#include "state.hpp"
|
||||||
#include "character_actor.hpp"
|
#include "character_actor.hpp"
|
||||||
|
#include "planner.hpp"
|
||||||
#include "utils/godot_macros.h"
|
#include "utils/godot_macros.h"
|
||||||
|
|
||||||
namespace godot::goap {
|
namespace godot::goap {
|
||||||
|
@ -51,7 +52,7 @@ void StateArgs::_bind_methods() {
|
||||||
GDPROPERTY(argument_property, Variant::STRING_NAME);
|
GDPROPERTY(argument_property, Variant::STRING_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
State StateArgs::construct(Node *context) const {
|
State StateArgs::construct(Planner *context) const {
|
||||||
return { .type = State::STATE_TYPE_MAX };
|
return { .type = State::STATE_TYPE_MAX };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,21 +61,21 @@ StringName StateArgs::get_argument_property() const { return this->argument_prop
|
||||||
|
|
||||||
void MoveStateArgs::_bind_methods() {}
|
void MoveStateArgs::_bind_methods() {}
|
||||||
|
|
||||||
State MoveStateArgs::construct(Node *context) const {
|
State MoveStateArgs::construct(Planner *context) const {
|
||||||
Node3D *node = Object::cast_to<Node3D>(context->call("get_" + this->argument_property));
|
Node3D *node = Object::cast_to<Node3D>(context->get_world_property(this->argument_property));
|
||||||
return State::new_move_to(node);
|
return State::new_move_to(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimateStateArgs::_bind_methods() {}
|
void AnimateStateArgs::_bind_methods() {}
|
||||||
|
|
||||||
State AnimateStateArgs::construct(Node *context) const {
|
State AnimateStateArgs::construct(Planner *context) const {
|
||||||
return State::new_animate(context->call("get_" + this->argument_property));
|
return State::new_animate(context->get_world_property(this->argument_property));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActivateStateArgs::_bind_methods() {}
|
void ActivateStateArgs::_bind_methods() {}
|
||||||
|
|
||||||
State ActivateStateArgs::construct(Node *context) const {
|
State ActivateStateArgs::construct(Planner *context) const {
|
||||||
Node *node = Object::cast_to<Node>(context->call("get_" + this->argument_property));
|
Node *node = Object::cast_to<Node>(context->get_world_property(this->argument_property));
|
||||||
return State::new_activate(node);
|
return State::new_activate(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
namespace godot { class CharacterActor; }
|
namespace godot { class CharacterActor; }
|
||||||
namespace godot::goap {
|
namespace godot::goap {
|
||||||
|
class Planner;
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
~State();
|
~State();
|
||||||
static State new_move_to(Node3D *location);
|
static State new_move_to(Node3D *location);
|
||||||
|
@ -36,7 +38,7 @@ class StateArgs : public Resource {
|
||||||
GDCLASS(StateArgs, Resource);
|
GDCLASS(StateArgs, Resource);
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
public:
|
public:
|
||||||
virtual State construct(Node *context) const;
|
virtual State construct(Planner *context) const;
|
||||||
void set_argument_property(StringName name);
|
void set_argument_property(StringName name);
|
||||||
StringName get_argument_property() const;
|
StringName get_argument_property() const;
|
||||||
StringName argument_property;
|
StringName argument_property;
|
||||||
|
@ -45,21 +47,21 @@ public:
|
||||||
class MoveStateArgs : public StateArgs {
|
class MoveStateArgs : public StateArgs {
|
||||||
GDCLASS(MoveStateArgs, StateArgs);
|
GDCLASS(MoveStateArgs, StateArgs);
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
virtual State construct(Node *context) const override;
|
virtual State construct(Planner *context) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AnimateStateArgs : public StateArgs {
|
class AnimateStateArgs : public StateArgs {
|
||||||
GDCLASS(AnimateStateArgs, StateArgs);
|
GDCLASS(AnimateStateArgs, StateArgs);
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
public:
|
public:
|
||||||
virtual State construct(Node *context) const override;
|
virtual State construct(Planner *context) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ActivateStateArgs : public StateArgs {
|
class ActivateStateArgs : public StateArgs {
|
||||||
GDCLASS(ActivateStateArgs, StateArgs);
|
GDCLASS(ActivateStateArgs, StateArgs);
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
public:
|
public:
|
||||||
virtual State construct(Node *context) const override;
|
virtual State construct(Planner *context) const override;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
#include "tunnels_game_mode.hpp"
|
#include "tunnels_game_mode.hpp"
|
||||||
#include "godot_cpp/variant/utility_functions.hpp"
|
#include "character_actor.hpp"
|
||||||
#include "utils/game_root.hpp"
|
#include "utils/game_root.hpp"
|
||||||
#include "utils/godot_macros.h"
|
#include "utils/godot_macros.h"
|
||||||
#include <godot_cpp/classes/node.hpp>
|
#include <godot_cpp/classes/node.hpp>
|
||||||
|
#include <godot_cpp/core/object.hpp>
|
||||||
|
#include <godot_cpp/variant/callable.hpp>
|
||||||
|
#include <godot_cpp/variant/callable_method_pointer.hpp>
|
||||||
|
#include <godot_cpp/variant/variant.hpp>
|
||||||
|
|
||||||
namespace godot {
|
namespace godot {
|
||||||
void TunnelsGameMode::_bind_methods() {
|
void TunnelsGameMode::_bind_methods() {
|
||||||
|
@ -21,4 +25,27 @@ void TunnelsGameMode::on_player_spawned(Node *player) {
|
||||||
TunnelsPlayer *TunnelsGameMode::get_player_instance() const {
|
TunnelsPlayer *TunnelsGameMode::get_player_instance() const {
|
||||||
return this->player;
|
return this->player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TunnelsGameMode::register_player_character(CharacterActor *actor) {
|
||||||
|
if(!this->player_characters.has(actor)) {
|
||||||
|
this->player_characters.push_back(actor);
|
||||||
|
actor->connect("tree_exited", callable_mp(this, &TunnelsGameMode::on_character_destroyed).bind(actor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TunnelsGameMode::set_manual_character(CharacterActor *actor) {
|
||||||
|
if(!this->player_characters.has(actor))
|
||||||
|
this->register_player_character(actor);
|
||||||
|
this->manual_character = actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TunnelsGameMode::on_character_destroyed(CharacterActor *actor) {
|
||||||
|
this->player_characters.erase(actor);
|
||||||
|
if(this->manual_character == actor)
|
||||||
|
this->manual_character = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<CharacterActor*> const &TunnelsGameMode::get_player_characters() const {
|
||||||
|
return this->player_characters;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#ifndef TUNNELS_GAME_MODE_HPP
|
#ifndef TUNNELS_GAME_MODE_HPP
|
||||||
#define TUNNELS_GAME_MODE_HPP
|
#define TUNNELS_GAME_MODE_HPP
|
||||||
|
|
||||||
#include "tunnels_game_state.hpp"
|
|
||||||
#include "tunnels_player.hpp"
|
#include "tunnels_player.hpp"
|
||||||
#include "utils/game_mode.hpp"
|
#include "utils/game_mode.hpp"
|
||||||
|
#include <godot_cpp/templates/vector.hpp>
|
||||||
|
|
||||||
namespace godot {
|
namespace godot {
|
||||||
class TunnelsGameMode : public GameMode {
|
class TunnelsGameMode : public GameMode {
|
||||||
|
@ -15,8 +15,14 @@ public:
|
||||||
void on_player_spawned(Node *player);
|
void on_player_spawned(Node *player);
|
||||||
|
|
||||||
TunnelsPlayer *get_player_instance() const;
|
TunnelsPlayer *get_player_instance() const;
|
||||||
|
void register_player_character(CharacterActor *actor);
|
||||||
|
void set_manual_character(CharacterActor *actor);
|
||||||
|
void on_character_destroyed(CharacterActor *actor);
|
||||||
|
Vector<CharacterActor*> const &get_player_characters() const;
|
||||||
private:
|
private:
|
||||||
TunnelsPlayer *player{nullptr};
|
TunnelsPlayer *player{nullptr};
|
||||||
|
CharacterActor *manual_character{nullptr};
|
||||||
|
Vector<CharacterActor*> player_characters{};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,34 +1,42 @@
|
||||||
#include "tunnels_player.hpp"
|
#include "tunnels_player.hpp"
|
||||||
#include "character_data.hpp"
|
|
||||||
#include "godot_cpp/variant/plane.hpp"
|
|
||||||
#include "godot_cpp/variant/projection.hpp"
|
|
||||||
#include "character_actor.hpp"
|
#include "character_actor.hpp"
|
||||||
|
#include "character_data.hpp"
|
||||||
|
#include "goal_marker.hpp"
|
||||||
|
#include "godot_cpp/variant/callable_method_pointer.hpp"
|
||||||
|
#include "godot_cpp/variant/utility_functions.hpp"
|
||||||
|
#include "planner.hpp"
|
||||||
|
#include "tunnels_game_mode.hpp"
|
||||||
#include "tunnels_game_state.hpp"
|
#include "tunnels_game_state.hpp"
|
||||||
#include "utils/game_root.hpp"
|
#include "utils/game_root.hpp"
|
||||||
#include "utils/godot_macros.h"
|
#include "utils/godot_macros.h"
|
||||||
#include "utils/player_input.hpp"
|
#include "utils/player_input.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <godot_cpp/classes/input_event.hpp>
|
#include <godot_cpp/classes/input_event.hpp>
|
||||||
|
#include <godot_cpp/classes/physics_direct_space_state3d.hpp>
|
||||||
|
#include <godot_cpp/classes/physics_ray_query_parameters3d.hpp>
|
||||||
#include <godot_cpp/classes/resource_loader.hpp>
|
#include <godot_cpp/classes/resource_loader.hpp>
|
||||||
#include <godot_cpp/classes/scene_state.hpp>
|
#include <godot_cpp/classes/scene_state.hpp>
|
||||||
#include <godot_cpp/classes/viewport.hpp>
|
#include <godot_cpp/classes/viewport.hpp>
|
||||||
|
#include <godot_cpp/classes/world3d.hpp>
|
||||||
|
#include <godot_cpp/variant/plane.hpp>
|
||||||
|
#include <godot_cpp/variant/projection.hpp>
|
||||||
|
|
||||||
namespace godot {
|
namespace godot {
|
||||||
void TunnelsPlayer::_bind_methods() {
|
void TunnelsPlayer::_bind_methods() {
|
||||||
#define CLASSNAME TunnelsPlayer
|
#define CLASSNAME TunnelsPlayer
|
||||||
GDFUNCTION_ARGS(horizontal_move_input, "event", "value");
|
|
||||||
GDFUNCTION_ARGS(vertical_move_input, "event", "value");
|
|
||||||
GDFUNCTION_ARGS(fire_pressed, "event", "value");
|
|
||||||
GDPROPERTY_HINTED(camera_rotation_ramp, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Curve");
|
GDPROPERTY_HINTED(camera_rotation_ramp, Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Curve");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TunnelsPlayer::_ready() { GDGAMEONLY();
|
void TunnelsPlayer::_enter_tree() { GDGAMEONLY();
|
||||||
this->camera = this->get_viewport()->get_camera_3d();
|
|
||||||
this->initialize_character();
|
this->initialize_character();
|
||||||
this->camera_rotation_ramp->bake();
|
this->camera_rotation_ramp->bake();
|
||||||
this->reticle = this->get_node<Node3D>("Reticle");
|
this->reticle = this->get_node<Node3D>("Reticle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TunnelsPlayer::_ready() {
|
||||||
|
this->camera = this->get_viewport()->get_camera_3d();
|
||||||
|
}
|
||||||
|
|
||||||
void TunnelsPlayer::_exit_tree() { GDGAMEONLY();
|
void TunnelsPlayer::_exit_tree() { GDGAMEONLY();
|
||||||
GameRoot::get_singleton()->remove_player(this->get_player_id());
|
GameRoot::get_singleton()->remove_player(this->get_player_id());
|
||||||
}
|
}
|
||||||
|
@ -52,10 +60,11 @@ void TunnelsPlayer::_process(double delta_time) { GDGAMEONLY();
|
||||||
this->set_global_position(this->character->get_global_position());
|
this->set_global_position(this->character->get_global_position());
|
||||||
break;
|
break;
|
||||||
case State::Tactics:
|
case State::Tactics:
|
||||||
|
// move camera along with the input
|
||||||
|
this->set_global_position(this->get_global_position() + this->get_world_move_input().normalized() *
|
||||||
|
delta_time * TunnelsPlayer::TACTICS_MOVEMENT_SPEED);
|
||||||
break;
|
break;
|
||||||
case State::Overview:
|
case State::Overview:
|
||||||
// move camera along with the input
|
|
||||||
this->set_global_position(this->get_global_position() + this->get_world_move_input().normalized());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,15 +82,16 @@ void TunnelsPlayer::process_camera_rotation(double delta_time) {
|
||||||
Vector3 rotation = this->get_global_rotation();
|
Vector3 rotation = this->get_global_rotation();
|
||||||
// the influence of the mouse's y position on the rotation speed
|
// the influence of the mouse's y position on the rotation speed
|
||||||
float const y_multiplier = std::max(TunnelsPlayer::ROTATION_Y_MIN_INFLUENCE, this->mouse_location.y);
|
float const y_multiplier = std::max(TunnelsPlayer::ROTATION_Y_MIN_INFLUENCE, this->mouse_location.y);
|
||||||
|
float const margin = TunnelsPlayer::ROTATION_MARGIN * (this->state == State::Tactics ? TunnelsPlayer::ROTATION_MARGIN_TACTICS_MUL : 1.f);
|
||||||
// rotate the camera when the mouse is close to the edge of the screen
|
// rotate the camera when the mouse is close to the edge of the screen
|
||||||
if(this->mouse_location.x < TunnelsPlayer::ROTATION_MARGIN) {
|
if(this->mouse_location.x < margin) {
|
||||||
// normalized measurement of how far into the rotation margin the mouse is
|
// normalized measurement of how far into the rotation margin the mouse is
|
||||||
float const normalized{1.f - (this->mouse_location.x / TunnelsPlayer::ROTATION_MARGIN)};
|
float const normalized{1.f - (this->mouse_location.x / margin)};
|
||||||
// rotate based on delta time and use a curve to make the rotation zone feel more natural
|
// rotate based on delta time and use a curve to make the rotation zone feel more natural
|
||||||
rotation.y += delta_time * double(TunnelsPlayer::ROTATION_SPEED * camera_rotation_ramp->sample(normalized) * y_multiplier);
|
rotation.y += delta_time * double(TunnelsPlayer::ROTATION_SPEED * camera_rotation_ramp->sample(normalized) * y_multiplier);
|
||||||
}
|
}
|
||||||
if(this->mouse_location.x > 1.f - TunnelsPlayer::ROTATION_MARGIN) {
|
if(this->mouse_location.x > 1.f - margin) {
|
||||||
float const normalized{((this->mouse_location.x - (1.f - TunnelsPlayer::ROTATION_MARGIN)) / TunnelsPlayer::ROTATION_MARGIN)};
|
float const normalized{((this->mouse_location.x - (1.f - margin)) / margin)};
|
||||||
rotation.y -= delta_time * double(TunnelsPlayer::ROTATION_SPEED * camera_rotation_ramp->sample(normalized) * y_multiplier);
|
rotation.y -= delta_time * double(TunnelsPlayer::ROTATION_SPEED * camera_rotation_ramp->sample(normalized) * y_multiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,15 +105,21 @@ void TunnelsPlayer::process_camera_rotation(double delta_time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TunnelsPlayer::setup_player_input(PlayerInput *input) {
|
void TunnelsPlayer::setup_player_input(PlayerInput *input) {
|
||||||
input->listen_to(PlayerInput::Listener("move_left", "move_right", this, "horizontal_move_input"));
|
input->listen_to(PlayerInput::Listener("move_left", "move_right", callable_mp(this, &TunnelsPlayer::horizontal_move_input)));
|
||||||
input->listen_to(PlayerInput::Listener("move_forward", "move_backward", this, "vertical_move_input"));
|
input->listen_to(PlayerInput::Listener("move_forward", "move_backward", callable_mp(this, &TunnelsPlayer::vertical_move_input)));
|
||||||
input->listen_to(PlayerInput::Listener("fire", this, "fire_pressed"));
|
input->listen_to(PlayerInput::Listener("fire", callable_mp(this, &TunnelsPlayer::fire_pressed)));
|
||||||
|
input->listen_to(PlayerInput::Listener("tactics_mode", callable_mp(this, &TunnelsPlayer::mode_switch_input)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *TunnelsPlayer::to_node() {
|
Node *TunnelsPlayer::to_node() {
|
||||||
return Object::cast_to<Node>(this);
|
return Object::cast_to<Node>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TunnelsPlayer::spawn_at_position(Transform3D const &at) {
|
||||||
|
this->character->set_global_transform(at);
|
||||||
|
this->set_global_basis(at.get_basis());
|
||||||
|
}
|
||||||
|
|
||||||
void TunnelsPlayer::horizontal_move_input(Ref<InputEvent> event, float value) {
|
void TunnelsPlayer::horizontal_move_input(Ref<InputEvent> event, float value) {
|
||||||
this->move_input.x = value;
|
this->move_input.x = value;
|
||||||
}
|
}
|
||||||
|
@ -112,8 +128,70 @@ void TunnelsPlayer::vertical_move_input(Ref<InputEvent> event, float value) {
|
||||||
this->move_input.y = value;
|
this->move_input.y = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TunnelsPlayer::mode_switch_input(Ref<InputEvent> event, float value) {
|
||||||
|
if(value != 0.f)
|
||||||
|
this->state = this->state == State::Tactics ? State::ManualControl : State::Tactics;
|
||||||
|
}
|
||||||
|
|
||||||
void TunnelsPlayer::fire_pressed(Ref<InputEvent> event, float value) {
|
void TunnelsPlayer::fire_pressed(Ref<InputEvent> event, float value) {
|
||||||
|
switch(this->state) {
|
||||||
|
case State::ManualControl:
|
||||||
this->character->set_firing(value != 0);
|
this->character->set_firing(value != 0);
|
||||||
|
break;
|
||||||
|
case State::Tactics:
|
||||||
|
if(value == 1.f)
|
||||||
|
this->try_select_marker();
|
||||||
|
break;
|
||||||
|
case State::Overview:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TunnelsPlayer::try_select_marker() {
|
||||||
|
UtilityFunctions::print("TunnelsPlayer::try_select_marker()");
|
||||||
|
Transform3D const &camera_trans{this->camera->get_global_transform()};
|
||||||
|
// prepare raycast query
|
||||||
|
Ref<PhysicsRayQueryParameters3D> params{PhysicsRayQueryParameters3D::create(camera_trans.origin, camera_trans.origin + this->mouse_world_ray_normal * 1000.f)};
|
||||||
|
params->set_collision_mask(1u << 3u);
|
||||||
|
params->set_collide_with_areas(true);
|
||||||
|
// fetch current physics state and cast ray
|
||||||
|
PhysicsDirectSpaceState3D *state = this->get_world_3d()->get_direct_space_state();
|
||||||
|
Dictionary dict{state->intersect_ray(params)};
|
||||||
|
// fail if nothing was hit
|
||||||
|
if(dict.is_empty())
|
||||||
|
return;
|
||||||
|
// attempt to cast hit node to a marker
|
||||||
|
GoalMarker *marker{Object::cast_to<GoalMarker>(dict["collider"])};
|
||||||
|
// fail if hit object is not a marker
|
||||||
|
if(marker == nullptr)
|
||||||
|
return;
|
||||||
|
UtilityFunctions::print("Hit: ", marker->get_path());
|
||||||
|
CharacterActor *target_character{nullptr};
|
||||||
|
for(CharacterActor *loop_character : Ref<TunnelsGameMode>(GameRoot::get_singleton()->get_game_mode())->get_player_characters()) {
|
||||||
|
if(loop_character != this->character) {
|
||||||
|
target_character = loop_character;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no non-player ally was found
|
||||||
|
if(target_character == nullptr)
|
||||||
|
return;
|
||||||
|
// cache planner component
|
||||||
|
goap::Planner *planner{target_character->get_planner()};
|
||||||
|
// cache previous target in case planning fails
|
||||||
|
Node *previous_target{target_character->get_target()};
|
||||||
|
// attempt to find a plan to marker's goal
|
||||||
|
target_character->set_target(marker);
|
||||||
|
if(planner->can_do(marker->get_goal())) {
|
||||||
|
planner->add_goal(marker->get_goal());
|
||||||
|
planner->make_plan();
|
||||||
|
target_character->force_update_action();
|
||||||
|
UtilityFunctions::print("Made plan for character ", target_character->get_path());
|
||||||
|
} else {
|
||||||
|
// reset character to the state it was in before attempts to change goal
|
||||||
|
UtilityFunctions::push_warning("Failed to make plan for ", marker->get_goal()->get_path());
|
||||||
|
target_character->set_target(previous_target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TunnelsPlayer::initialize_character() {
|
void TunnelsPlayer::initialize_character() {
|
||||||
|
@ -123,6 +201,7 @@ void TunnelsPlayer::initialize_character() {
|
||||||
return;
|
return;
|
||||||
if(player_scene->get_state()->get_node_type(0) != StringName("CharacterActor"))
|
if(player_scene->get_state()->get_node_type(0) != StringName("CharacterActor"))
|
||||||
return;
|
return;
|
||||||
|
UtilityFunctions::print("initialize_character pos: ", this->get_global_position());
|
||||||
// instantiate and store the player character
|
// instantiate and store the player character
|
||||||
this->character = Object::cast_to<CharacterActor>(player_scene->instantiate());
|
this->character = Object::cast_to<CharacterActor>(player_scene->instantiate());
|
||||||
this->get_parent()->add_child(this->character);
|
this->get_parent()->add_child(this->character);
|
||||||
|
@ -132,6 +211,7 @@ void TunnelsPlayer::initialize_character() {
|
||||||
this->character->set_character_data(game_state->get_characters()[0]);
|
this->character->set_character_data(game_state->get_characters()[0]);
|
||||||
// disable navmesh navigation and start using player input
|
// disable navmesh navigation and start using player input
|
||||||
this->character->set_manual_mode(true);
|
this->character->set_manual_mode(true);
|
||||||
|
Ref<TunnelsGameMode>(GameRoot::get_singleton()->get_game_mode())->set_manual_character(this->character);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 TunnelsPlayer::get_world_move_input() const {
|
Vector3 TunnelsPlayer::get_world_move_input() const {
|
||||||
|
@ -175,4 +255,6 @@ CharacterActor *TunnelsPlayer::get_character() const {
|
||||||
float const TunnelsPlayer::ROTATION_SPEED{0.5f};
|
float const TunnelsPlayer::ROTATION_SPEED{0.5f};
|
||||||
float const TunnelsPlayer::ROTATION_Y_MIN_INFLUENCE{7.f};
|
float const TunnelsPlayer::ROTATION_Y_MIN_INFLUENCE{7.f};
|
||||||
float const TunnelsPlayer::ROTATION_MARGIN{0.4f};
|
float const TunnelsPlayer::ROTATION_MARGIN{0.4f};
|
||||||
|
float const TunnelsPlayer::ROTATION_MARGIN_TACTICS_MUL{0.6f};
|
||||||
|
float const TunnelsPlayer::TACTICS_MOVEMENT_SPEED{10.f};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef TUNNELS_PLAYER_HPP
|
#ifndef TUNNELS_PLAYER_HPP
|
||||||
#define TUNNELS_PLAYER_HPP
|
#define TUNNELS_PLAYER_HPP
|
||||||
|
|
||||||
|
#include "godot_cpp/variant/transform3d.hpp"
|
||||||
#include "utils/player.hpp"
|
#include "utils/player.hpp"
|
||||||
#include "utils/player_input.hpp"
|
#include "utils/player_input.hpp"
|
||||||
#include <godot_cpp/classes/camera3d.hpp>
|
#include <godot_cpp/classes/camera3d.hpp>
|
||||||
|
@ -22,6 +23,7 @@ class TunnelsPlayer : public Node3D, public IPlayer {
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
virtual void _enter_tree() override;
|
||||||
virtual void _ready() override;
|
virtual void _ready() override;
|
||||||
virtual void _exit_tree() override;
|
virtual void _exit_tree() override;
|
||||||
virtual void _process(double delta_time) override;
|
virtual void _process(double delta_time) override;
|
||||||
|
@ -31,11 +33,14 @@ public:
|
||||||
|
|
||||||
virtual void setup_player_input(PlayerInput *input) override;
|
virtual void setup_player_input(PlayerInput *input) override;
|
||||||
virtual Node *to_node() override;
|
virtual Node *to_node() override;
|
||||||
|
virtual void spawn_at_position(Transform3D const &at) override;
|
||||||
|
|
||||||
void horizontal_move_input(Ref<InputEvent> event, float value);
|
void horizontal_move_input(Ref<InputEvent> event, float value);
|
||||||
void vertical_move_input(Ref<InputEvent> event, float value);
|
void vertical_move_input(Ref<InputEvent> event, float value);
|
||||||
|
void mode_switch_input(Ref<InputEvent> event, float value);
|
||||||
void fire_pressed(Ref<InputEvent> event, float value);
|
void fire_pressed(Ref<InputEvent> event, float value);
|
||||||
|
|
||||||
|
void try_select_marker();
|
||||||
void initialize_character();
|
void initialize_character();
|
||||||
|
|
||||||
Vector3 get_world_move_input() const;
|
Vector3 get_world_move_input() const;
|
||||||
|
@ -59,7 +64,9 @@ private:
|
||||||
|
|
||||||
static float const ROTATION_SPEED;
|
static float const ROTATION_SPEED;
|
||||||
static float const ROTATION_Y_MIN_INFLUENCE;
|
static float const ROTATION_Y_MIN_INFLUENCE;
|
||||||
|
static float const ROTATION_MARGIN_TACTICS_MUL;
|
||||||
static float const ROTATION_MARGIN;
|
static float const ROTATION_MARGIN;
|
||||||
|
static float const TACTICS_MOVEMENT_SPEED;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit f0bddcf074f040e6a02f70a26c58507580669327
|
Subproject commit d81ad91a885a74338c02edf1d52a2fa5aa8746b6
|
Loading…
Reference in a new issue