diff --git a/godot/GameObjects/enemy_unit.tscn b/godot/GameObjects/enemy_unit.tscn index b1f90b1..e5095f6 100644 --- a/godot/GameObjects/enemy_unit.tscn +++ b/godot/GameObjects/enemy_unit.tscn @@ -20,7 +20,7 @@ rings = 3 [sub_resource type="BoxMesh" id="BoxMesh_p8wvo"] size = Vector3(0.2, 0.2, 0.5) -[node name="Tank" type="TankUnit"] +[node name="Tank" type="TankUnit" groups=["enemy_unit"]] configure_team = 3 movement_speed = 1.5 collision_layer = 6 @@ -35,7 +35,6 @@ actions_inspector = [3, 2, 4, 1] unique_name_in_owner = true [node name="EntityHealth" type="EntityHealth" parent="."] -injury_max = 100 unique_name_in_owner = true [node name="NavigationAgent3D" type="NavigationAgent3D" parent="."] diff --git a/godot/GameObjects/player_unit.tscn b/godot/GameObjects/player_unit.tscn index f19f9de..dab92c6 100644 --- a/godot/GameObjects/player_unit.tscn +++ b/godot/GameObjects/player_unit.tscn @@ -17,7 +17,7 @@ rings = 3 [sub_resource type="BoxMesh" id="BoxMesh_p8wvo"] size = Vector3(0.2, 0.2, 0.5) -[node name="Player" type="CharacterUnit"] +[node name="Player" type="CharacterUnit" groups=["player_unit"]] configure_team = 1 collision_layer = 6 collision_mask = 0 diff --git a/godot/Levels/level_complete.tscn b/godot/Levels/level_complete.tscn new file mode 100644 index 0000000..dc2f734 --- /dev/null +++ b/godot/Levels/level_complete.tscn @@ -0,0 +1,22 @@ +[gd_scene load_steps=2 format=3 uid="uid://wlejteovfj8v"] + +[ext_resource type="PackedScene" uid="uid://cljn3tou8pxjt" path="res://menu_game_mode.tscn" id="1_ce5im"] + +[node name="Level3D" type="Level3D"] +game_mode_prototype = ExtResource("1_ce5im") + +[node name="LevelComplete" type="Control" parent="."] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Label" type="Label" parent="LevelComplete"] +layout_mode = 0 +offset_left = 23.0 +offset_top = 27.0 +offset_right = 447.0 +offset_bottom = 163.0 +text = "Test Level Complete" diff --git a/godot/Levels/test_level.tscn b/godot/Levels/test_level.tscn index 07cf75f..ffd3b6d 100644 --- a/godot/Levels/test_level.tscn +++ b/godot/Levels/test_level.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=16 format=3 uid="uid://c62s1jmtgajjk"] +[gd_scene load_steps=19 format=3 uid="uid://c62s1jmtgajjk"] [ext_resource type="PackedScene" uid="uid://dsalxxq3xs842" path="res://rts_game_mode.tscn" id="1_4nchg"] [ext_resource type="Environment" uid="uid://cnfk8yrvklysq" path="res://Environments/default_environment.tres" id="2_jq6bw"] @@ -7,6 +7,7 @@ [ext_resource type="PackedScene" uid="uid://c82s8vpaethtv" path="res://GameObjects/nav_marker.tscn" id="5_ta2oq"] [ext_resource type="PackedScene" uid="uid://duuvete30t8sv" path="res://GameObjects/LevelProps/modern_train.tscn" id="6_favl6"] [ext_resource type="PackedScene" uid="uid://ulvv4o73s48a" path="res://Environments/Models/KenneyTrains/track.glb" id="7_8fuqb"] +[ext_resource type="Material" uid="uid://dluo26ixa4p8o" path="res://Assets/Models/movement_marker.tres" id="8_wyjwf"] [sub_resource type="NavigationMesh" id="NavigationMesh_8a2j6"] vertices = PackedVector3Array(-49.9439, 6.15874, -50.0795, -47.9439, 6.15874, -50.0795, -47.9439, 6.15874, -66.0795, -71.4439, 6.15874, -48.0795, -49.9439, 6.15874, -48.0795, -71.4439, 6.15874, -66.0795, 50.0561, 6.15874, -50.0795, 50.0561, 6.15874, -48.0795, 52.0561, 6.15874, -48.0795, 52.0561, 6.15874, -66.0795, -71.4439, 6.15874, 47.9205, -49.9439, 6.15874, 47.9205, 50.0561, 6.15874, 47.9205, 52.0561, 6.15874, 47.9205, -5.44388, 0.408735, -1.57945, -5.19388, 0.408735, -2.82945, -6.94388, 0.408735, -3.07945, -8.94388, 0.408735, -1.57945, -6.69388, 0.408735, -6.57945, -8.94388, 0.408735, -9.32945, 7.55612, 0.408735, -6.57945, 7.80612, 0.408735, -4.57945, 9.30612, 0.408735, -4.57945, 9.30612, 0.408735, -9.32945, -5.69388, 0.408735, -5.32945, -5.69388, 0.408735, -4.07945, -0.443878, 0.408735, -4.07945, -0.443878, 0.408735, -5.32945, 1.30612, 0.408735, -5.32945, 1.30612, 0.408735, -4.07945, 6.55612, 0.408735, -4.07945, 6.55612, 0.408735, -5.32945, 7.55612, 0.408735, -2.82945, 2.55612, 0.408735, -2.82945, 2.55612, 0.408735, -1.57945, 4.80612, 0.408735, -1.32945, 4.80612, 0.408735, 0.920547, 7.05612, 0.408735, 5.17055, 9.30612, 0.408735, 8.17055, 7.30612, 0.408735, 8.17055, 2.55612, 0.408735, 1.17055, 2.55612, 0.408735, 5.17055, -3.94388, 0.408735, -1.32945, 0.306122, 0.408735, -1.32945, -3.94388, 0.408735, 0.920547, 0.306122, 0.408735, 0.920547, -5.69388, 0.408735, 1.17055, -5.69388, 0.408735, 5.17055, -29.1939, 0.408735, 1.17055, -29.1939, 0.408735, 2.92055, -27.1939, 0.408735, 2.92055, -26.9439, 0.408735, 1.42055, -12.6939, 0.408735, 1.42055, -12.4439, 0.408735, 4.92055, -12.9439, 0.408735, 5.17055, -12.9439, 0.408735, 8.17055, -7.44388, 0.408735, 8.17055, -7.44388, 0.408735, 5.42055, -26.9439, 0.408735, 5.17055, -29.1939, 0.408735, 8.17055, -25.9439, 0.408735, 2.67055, -25.9439, 0.408735, 3.92055, -20.6939, 0.408735, 3.92055, -20.6939, 0.408735, 2.67055, -18.9439, 0.408735, 2.67055, -18.9439, 0.408735, 3.92055, -13.6939, 0.408735, 3.92055, -13.6939, 0.408735, 2.67055, -6.19388, 0.408735, 6.42055, -6.19388, 0.408735, 7.92055, -0.943878, 0.408735, 7.92055, -0.943878, 0.408735, 6.42055, 0.806122, 0.408735, 6.42055, 0.806122, 0.408735, 7.92055, 6.05612, 0.408735, 7.92055, 6.05612, 0.408735, 6.42055, -47.9439, 6.15874, 67.9205, -47.9439, 6.15874, 49.9205, -49.9439, 6.15874, 49.9205, -71.4439, 6.15874, 67.9205, 50.0561, 6.15874, 49.9205, 52.0561, 6.15874, 67.9205) @@ -47,9 +48,19 @@ albedo_color = Color(0, 1, 0.823529, 0.760784) [sub_resource type="BoxMesh" id="BoxMesh_bl5l6"] size = Vector3(7, 1, 1) +[sub_resource type="BoxShape3D" id="BoxShape3D_c8xhj"] +size = Vector3(2.58594, 1, 8.92188) + +[sub_resource type="BoxMesh" id="BoxMesh_mq8af"] +material = ExtResource("8_wyjwf") +size = Vector3(2.035, 1, 8.225) + [node name="TestLevel" type="Level3D"] game_mode_prototype = ExtResource("1_4nchg") +[node name="SpawnPoint3D" type="SpawnPoint3D" parent="."] +transform = Transform3D(0, 0, -1, 0, 1, 0, 1, 0, 0, -7.12546, 0, -8.38951) + [node name="NavRoom" type="NavRoom" parent="."] [node name="CoverMarker" parent="NavRoom" instance=ExtResource("5_ta2oq")] @@ -233,10 +244,10 @@ use_collision = true size = Vector3(5.654, 10.5501, 2.79883) [node name="CSGBox3D3" type="CSGBox3D" parent="WorldEnvironment/NavigationRegion3D/brushwork"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -9.87437, 5.23133, 4.5963) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -9.95075, 5.23133, 4.5963) operation = 2 use_collision = true -size = Vector3(39.7404, 10.5501, 8.32035) +size = Vector3(39.8932, 10.5501, 8.32035) [node name="NavigationObstacle3D" type="NavigationObstacle3D" parent="WorldEnvironment/NavigationRegion3D"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3.008, 0) @@ -430,4 +441,18 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -11.0413, -7.63685e-07, 2.867 [node name="Tank4" parent="." instance=ExtResource("4_0o33v")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -14.4329, -7.63685e-07, 6.82909) +[node name="AreaTransfer" type="AreaTransfer" parent="."] +next_scene = "res://Levels/level_complete.tscn" +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -28.8374, -0.138102, 4.6354) +collision_layer = 4 +collision_mask = 6 +monitorable = false + +[node name="CollisionShape3D" type="CollisionShape3D" parent="AreaTransfer"] +shape = SubResource("BoxShape3D_c8xhj") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="AreaTransfer"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.635551, 0) +mesh = SubResource("BoxMesh_mq8af") + [connection signal="finish_activate" from="WorldEnvironment/NavigationRegion3D/UtilityLock" to="WorldEnvironment/NavigationRegion3D/UtilityLock" method="_on_finish_activate"] diff --git a/godot/menu_game_mode.tscn b/godot/menu_game_mode.tscn new file mode 100644 index 0000000..cb0ad91 --- /dev/null +++ b/godot/menu_game_mode.tscn @@ -0,0 +1,3 @@ +[gd_scene format=3 uid="uid://cljn3tou8pxjt"] + +[node name="GameMode" type="GameMode"] diff --git a/godot/project.godot b/godot/project.godot index 41b9b71..208e80d 100644 --- a/godot/project.godot +++ b/godot/project.godot @@ -35,6 +35,11 @@ folder_colors={ "res://bin/": "gray" } +[global_group] + +player_unit="" +enemy_unit="" + [input] lclick={ diff --git a/src/area_transfer.cpp b/src/area_transfer.cpp new file mode 100644 index 0000000..6280686 --- /dev/null +++ b/src/area_transfer.cpp @@ -0,0 +1,51 @@ +#include "area_transfer.hpp" +#include "godot_cpp/classes/resource_loader.hpp" +#include "godot_cpp/classes/scene_tree.hpp" +#include "godot_cpp/variant/string.hpp" +#include "utils/game_root.hpp" +#include "utils/godot_macros.hpp" + +void AreaTransfer::_bind_methods() { +#define CLASSNAME AreaTransfer + GDPROPERTY_HINTED(next_scene, gd::Variant::STRING, gd::PROPERTY_HINT_FILE, "*.tscn,*.scn"); +} + +void AreaTransfer::_ready() { + this->connect("body_entered", callable_mp(this, &self_type::_on_body_entered)); + this->connect("body_exited", callable_mp(this, &self_type::_on_body_exited)); +} + +void AreaTransfer::_on_body_entered(gd::Node3D *body) { + if(CharacterUnit *character{gd::Object::cast_to(body)}) { + this->ready_units.insert(character); + } + this->check_all_ready(); +} + +void AreaTransfer::_on_body_exited(gd::Node3D *body) { + CharacterUnit *unit{gd::Object::cast_to(body)}; + if(unit && this->ready_units.has(unit)) { + this->ready_units.erase(unit); + } +} + +void AreaTransfer::check_all_ready() { + gd::TypedArray units = this->get_tree()->get_nodes_in_group("player_unit"); + for(int i{0}; i < units.size(); ++i) { + CharacterUnit *unit{gd::Object::cast_to(units[i])}; + if(unit && unit->get_entity_health()->is_conscious() && !this->ready_units.has(unit)) { + return; // fail + } + } + // no missing units found, switch scenes + gd::Ref level{gd::ResourceLoader::get_singleton()->load(this->next_scene)}; + utils::GameRoot3D::get_singleton()->replace_levels(level); +} + +void AreaTransfer::set_next_scene(gd::String path) { + this->next_scene = path; +} + +gd::String AreaTransfer::get_next_scene() const { + return this->next_scene; +} diff --git a/src/area_transfer.hpp b/src/area_transfer.hpp new file mode 100644 index 0000000..3b29171 --- /dev/null +++ b/src/area_transfer.hpp @@ -0,0 +1,25 @@ +#ifndef AREA_TRANSFER_HPP +#define AREA_TRANSFER_HPP + +#include "character_unit.hpp" +#include +#include + +namespace gd = godot; + +class AreaTransfer : public gd::Area3D { + GDCLASS(AreaTransfer, gd::Area3D); + static void _bind_methods(); +public: + virtual void _ready() override; + void _on_body_entered(gd::Node3D *body); + void _on_body_exited(gd::Node3D *body); + void check_all_ready(); + void set_next_scene(gd::String path); + gd::String get_next_scene() const; +private: + gd::String next_scene{}; + gd::HashSet ready_units{}; +}; + +#endif // !AREA_TRANSFER_HPP diff --git a/src/register_types.cpp b/src/register_types.cpp index 9221d74..fdde0ce 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -1,4 +1,5 @@ #include "register_types.h" +#include "area_transfer.hpp" #include "character_unit.hpp" #include "character_world_state.hpp" #include "enemy_world_state.hpp" @@ -75,6 +76,7 @@ void initialize_gdextension_types(gd::ModuleInitializationLevel p_level) { GDREGISTER_CLASS(Inventory); GDREGISTER_RUNTIME_CLASS(RTSGameMode); GDREGISTER_RUNTIME_CLASS(RTSPlayer); + GDREGISTER_RUNTIME_CLASS(AreaTransfer); } extern "C" { diff --git a/src/unit.cpp b/src/unit.cpp index 283946d..efc7b2a 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -113,12 +113,14 @@ void Unit::on_death(Unit *damage_source) { void Unit::on_velocity_computed(gd::Vector3 vel) { gd::Vector3 const pos{this->get_global_position()}; + gd::Vector3 const flattened{vel.x, 0.f, vel.z}; 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}); + if(!flattened.is_zero_approx()) + this->look_at(pos - flattened); } void Unit::destroy_state() {