diff --git a/godot/Levels/test_level.tscn b/godot/Levels/test_level.tscn index b1687ae..70ee47b 100644 --- a/godot/Levels/test_level.tscn +++ b/godot/Levels/test_level.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=17 format=3 uid="uid://c62s1jmtgajjk"] +[gd_scene load_steps=24 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"] @@ -38,6 +38,39 @@ size = Vector3(0.445312, 3, 40) [sub_resource type="BoxMesh" id="BoxMesh_4sjak"] size = Vector3(0.445, 5, 40) +[sub_resource type="Goal" id="Goal_yju55"] +desired_state_dict = { +"is_target_activated": true +} + +[sub_resource type="GDScript" id="GDScript_2bv87"] +script/source = "extends UtilityLock + +func _on_finish_activate(_item: int, _unit: Unit): + self.queue_free() + self.get_parent().remove_child(self) +" + +[sub_resource type="SphereShape3D" id="SphereShape3D_pptgx"] +radius = 1.22941 + +[sub_resource type="BoxShape3D" id="BoxShape3D_3h2p0"] +size = Vector3(6.10718, 2.48376, 1.63379) + +[sub_resource type="CylinderMesh" id="CylinderMesh_r0j0v"] +top_radius = 1.0 +bottom_radius = 1.0 +height = 1.0 +cap_bottom = false + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_emsq8"] +transparency = 1 +shading_mode = 0 +albedo_color = Color(0, 1, 0.823529, 0.760784) + +[sub_resource type="BoxMesh" id="BoxMesh_bl5l6"] +size = Vector3(7, 1, 1) + [node name="TestLevel" type="Level3D"] game_mode_prototype = ExtResource("1_4nchg") @@ -219,6 +252,38 @@ transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -23.14 [node name="ModernTrain6" parent="WorldEnvironment/NavigationRegion3D" instance=ExtResource("6_favl6")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -16.443, 0.268182, 3.2886) +[node name="UtilityLock" type="UtilityLock" parent="WorldEnvironment"] +allowed_items = [3] +goal = SubResource("Goal_yju55") +transform = Transform3D(0.0142588, 0, 0.999898, 0, 1, 0, -0.999898, 0, 0.0142588, -4.68514, 1.8999e-07, 2.96901) +collision_layer = 4 +collision_mask = 0 +script = SubResource("GDScript_2bv87") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="WorldEnvironment/UtilityLock"] +shape = SubResource("SphereShape3D_pptgx") + +[node name="StaticBody3D" type="StaticBody3D" parent="WorldEnvironment/UtilityLock"] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="WorldEnvironment/UtilityLock/StaticBody3D"] +transform = Transform3D(1, 0, 3.1664e-08, 0, 1, 0, -3.1664e-08, 0, 1, 0.0699434, 0.984067, -2.11005) +shape = SubResource("BoxShape3D_3h2p0") + +[node name="NavigationObstacle3D" type="NavigationObstacle3D" parent="WorldEnvironment/UtilityLock/StaticBody3D"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0515832, 0.0826392, -2.12334) +height = 2.0 +vertices = PackedVector3Array(-1, 0, 3, -1, 0, -3, 1, 0, -3, 1, 0, 3) +carve_navigation_mesh = true + +[node name="MeshInstance3D" type="MeshInstance3D" parent="WorldEnvironment/UtilityLock"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.136569, 0) +mesh = SubResource("CylinderMesh_r0j0v") +surface_material_override/0 = SubResource("StandardMaterial3D_emsq8") + +[node name="MeshInstance3D2" type="MeshInstance3D" parent="WorldEnvironment/UtilityLock"] +transform = Transform3D(0.984294, -0.176535, 0, 0.176535, 0.984294, 0, 0, 0, 1, 0, 0.857907, -1.99146) +mesh = SubResource("BoxMesh_bl5l6") + [node name="Tracks" type="Node3D" parent="WorldEnvironment"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 7.25984) diff --git a/src/register_types.cpp b/src/register_types.cpp index 9f20d2b..c1c18fc 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -15,6 +15,7 @@ #include "tank_unit.hpp" #include "unit.hpp" #include "unit_world_state.hpp" +#include "utility_lock.hpp" #include "goap/action.hpp" #include "goap/action_db.hpp" #include "goap/actor_world_state.hpp" @@ -64,6 +65,7 @@ void initialize_gdextension_types(gd::ModuleInitializationLevel p_level) GDREGISTER_RUNTIME_CLASS(EnemyWorldState); GDREGISTER_RUNTIME_CLASS(CharacterWorldState); GDREGISTER_RUNTIME_CLASS(GoalMarker); + GDREGISTER_CLASS(UtilityLock); GDREGISTER_RUNTIME_CLASS(Unit); GDREGISTER_RUNTIME_CLASS(TankUnit) GDREGISTER_RUNTIME_CLASS(CharacterUnit); diff --git a/src/utility_lock.cpp b/src/utility_lock.cpp new file mode 100644 index 0000000..55022b4 --- /dev/null +++ b/src/utility_lock.cpp @@ -0,0 +1,48 @@ +#include "utility_lock.hpp" +#include "item_db.hpp" +#include "utils/godot_macros.hpp" + +void UtilityLock::_bind_methods() { +#define CLASSNAME UtilityLock + GDPROPERTY_HINTED(allowed_items, gd::Variant::ARRAY, gd::PROPERTY_HINT_ARRAY_TYPE, ItemDB::get_array_hint()); + GDSIGNAL("begin_activate", gd::PropertyInfo(gd::Variant::INT, "with_item"), gd::PropertyInfo(gd::Variant::OBJECT, "by_unit", gd::PROPERTY_HINT_NODE_TYPE, "Unit")); + GDSIGNAL("finish_activate", gd::PropertyInfo(gd::Variant::INT, "with_item"), gd::PropertyInfo(gd::Variant::OBJECT, "by_unit", gd::PROPERTY_HINT_NODE_TYPE, "Unit")); +} + +bool UtilityLock::can_use(Item const *with, Unit *by) const { + return this->allowed_items.has(with); +} + +bool UtilityLock::begin_activate(Item const *with, Unit *by) { + if(this->can_use(with, by)) { + this->user = by; + this->emit_signal("begin_activate", with->get_id(), by); + this->_begin_activate(with, by); + return true; + } else return false; +} + +bool UtilityLock::finish_activate(Item const *with, Unit *by) { + if(this->user != nullptr && this->user == by) { + this->user = nullptr; + this->emit_signal("finish_activate", with->get_id(), by); + this->_finish_activate(with, by); + return true; + } else return false; +} + +void UtilityLock::set_allowed_items(gd::Array array) { + this->allowed_items.clear(); + for(int i{0}; i < array.size(); ++i) { + Item const *item{ItemDB::get_item(array[i])}; + if(item == nullptr || item->has_capability(ItemCapability::UtilityEquip)) + this->allowed_items.insert(item); + } +} + +gd::Array UtilityLock::get_allowed_items() const { + gd::Array array{}; + for(Item const *item : this->allowed_items) + array.push_back(item == nullptr ? 0 : item->get_id()); + return array; +} diff --git a/src/utility_lock.hpp b/src/utility_lock.hpp new file mode 100644 index 0000000..f8ea7ea --- /dev/null +++ b/src/utility_lock.hpp @@ -0,0 +1,28 @@ +#ifndef UTILITY_LOCK_HPP +#define UTILITY_LOCK_HPP + +#include "goal_marker.hpp" +#include "item.hpp" +#include +#include + +namespace gd = godot; + +class UtilityLock : public GoalMarker { + GDCLASS(UtilityLock, GoalMarker) + static void _bind_methods(); +public: + bool can_use(Item const *with, Unit *by) const; + bool begin_activate(Item const *with, Unit *by); + bool finish_activate(Item const *with, Unit *by); + virtual void _begin_activate(Item const *with, Unit *by) {} + virtual void _finish_activate(Item const *with, Unit *by) {} + + void set_allowed_items(gd::Array array); + gd::Array get_allowed_items() const; +private: + gd::HashSet allowed_items{}; + Unit *user{nullptr}; +}; + +#endif // !UTILITY_LOCK_HPP