diff --git a/.cache/clangd/index/actor_body.cpp.3DBD3D1A5C1819D9.idx b/.cache/clangd/index/actor_body.cpp.3DBD3D1A5C1819D9.idx index 5b29a397..1cce0b75 100644 Binary files a/.cache/clangd/index/actor_body.cpp.3DBD3D1A5C1819D9.idx and b/.cache/clangd/index/actor_body.cpp.3DBD3D1A5C1819D9.idx differ diff --git a/.cache/clangd/index/actor_body.h.E6B5DA66D5128F55.idx b/.cache/clangd/index/actor_body.h.E6B5DA66D5128F55.idx index 9c066aed..18d7cdd0 100644 Binary files a/.cache/clangd/index/actor_body.h.E6B5DA66D5128F55.idx and b/.cache/clangd/index/actor_body.h.E6B5DA66D5128F55.idx differ diff --git a/.cache/clangd/index/actor_state_machine.cpp.AEBD1F5B3A3BA6BD.idx b/.cache/clangd/index/actor_state_machine.cpp.AEBD1F5B3A3BA6BD.idx new file mode 100644 index 00000000..d027e247 Binary files /dev/null and b/.cache/clangd/index/actor_state_machine.cpp.AEBD1F5B3A3BA6BD.idx differ diff --git a/.cache/clangd/index/actor_state_machine.h.8F19423C17C722EB.idx b/.cache/clangd/index/actor_state_machine.h.8F19423C17C722EB.idx new file mode 100644 index 00000000..2e1d9b63 Binary files /dev/null and b/.cache/clangd/index/actor_state_machine.h.8F19423C17C722EB.idx differ diff --git a/.cache/clangd/index/equipment.cpp.0DAD3B9F041D4E8F.idx b/.cache/clangd/index/equipment.cpp.0DAD3B9F041D4E8F.idx index e323f642..40114d0d 100644 Binary files a/.cache/clangd/index/equipment.cpp.0DAD3B9F041D4E8F.idx and b/.cache/clangd/index/equipment.cpp.0DAD3B9F041D4E8F.idx differ diff --git a/.cache/clangd/index/equipment.h.87F30A43991E43C4.idx b/.cache/clangd/index/equipment.h.87F30A43991E43C4.idx index 3202309d..fb8f9b51 100644 Binary files a/.cache/clangd/index/equipment.h.87F30A43991E43C4.idx and b/.cache/clangd/index/equipment.h.87F30A43991E43C4.idx differ diff --git a/justfile b/justfile index ad237ea9..900b47bc 100644 --- a/justfile +++ b/justfile @@ -1,6 +1,6 @@ set export -BUILD_NAME := "change_me" +BUILD_NAME := "tabtargeting" build: cd engine/ && scons target=editor symbols=yes optimization=debug dev_build=yes linker=mold use_llvm=yes compiledb=yes custom_modules="../modules" diff --git a/modules/tabtargeting/actor_body.cpp b/modules/tabtargeting/actor_body.cpp index ef6be220..3cecee43 100644 --- a/modules/tabtargeting/actor_body.cpp +++ b/modules/tabtargeting/actor_body.cpp @@ -1,10 +1,49 @@ #include "actor_body.h" +#include "core/object/class_db.h" #include "core/string/print_string.h" void ActorBody::_bind_methods() { + ClassDB::bind_method(D_METHOD("request_attack"), &self_type::request_attack, "target"); +} + +void ActorBody::_notification(int what) { + if(Engine::get_singleton()->is_editor_hint()) { + return; + } + switch(what) { + case NOTIFICATION_READY: + this->equipment = Object::cast_to(this->get_node(NodePath("Equipment"))); + break; + } +} + +void ActorBody::_attack() { + if(this->attack_requested != nullptr) { + this->attack_requested->receive_damage(this->equipment->apply(this->equipment->get_weapon()->create_base_damage_event(this), this)); + this->attack_requested = nullptr; + } +} + +bool ActorBody::is_attack_requested() { + return this->attack_requested != nullptr; } void ActorBody::receive_damage(DamageEvent event) { - this->health -= event.compound_damage; + this->health -= this->equipment->apply(event, this).compound_damage; print_line(vformat("Damage dealth (%d to %s, %d remaining", event.compound_damage, this->get_name(), this->health)); + if(this->health < 0) { + this->queue_free(); + } +} + +void ActorBody::request_attack(ActorBody *target) { + this->attack_requested = target; +} + +void ActorBody::set_health(int health) { + this->health = health; +} + +int ActorBody::get_health() const { + return this->health; } diff --git a/modules/tabtargeting/actor_body.h b/modules/tabtargeting/actor_body.h index c1fa26ee..df9b1d02 100644 --- a/modules/tabtargeting/actor_body.h +++ b/modules/tabtargeting/actor_body.h @@ -8,13 +8,19 @@ class ActorBody; class ActorBody : public CharacterBody3D { GDCLASS(ActorBody, CharacterBody3D); static void _bind_methods(); + void _notification(int what); public: + void _attack(); + bool is_attack_requested(); void receive_damage(DamageEvent event); - void send_damage(int amount, ActorBody *target); + void request_attack(ActorBody *target); + void set_health(int health); + int get_health() const; private: - int health{10}; + int health{50}; int max_health{10}; + ActorBody *attack_requested{nullptr}; - class Equipment *equipment{nullptr}; + Equipment *equipment{nullptr}; }; #endif // !ACTOR_BODY_H diff --git a/modules/tabtargeting/actor_state_machine.cpp b/modules/tabtargeting/actor_state_machine.cpp new file mode 100644 index 00000000..8309411b --- /dev/null +++ b/modules/tabtargeting/actor_state_machine.cpp @@ -0,0 +1,84 @@ +#include "actor_state_machine.h" +#include "core/config/engine.h" +#include "tabtargeting/actor_body.h" + +void ActorState::_bind_methods() { +} + +void ActorStateMachine::_bind_methods() { +} + +void ActorStateMachine::_notification(int what) { + if(Engine::get_singleton()->is_editor_hint()) { + return; + } + switch(what) { + case NOTIFICATION_READY: + this->ready(); + break; + case NOTIFICATION_PROCESS: + this->process(this->get_process_delta_time()); + break; + default: break; + } +} + +void ActorStateMachine::ready() { + this->set_process(true); + this->body = Object::cast_to(this->get_parent()); + this->add_state(); + this->add_state(); + this->current_state->state_entered(); +} + +void ActorStateMachine::process(double delta) { + this->current_state->process(delta); + this->try_next_state(); +} + +void ActorStateMachine::try_next_state() { + StringName name{this->current_state->next_state()}; + if(name != this->current_state->get_class()) { + this->switch_state(this->states[name]); + } +} + +void ActorStateMachine::switch_state(ActorState *state) { + this->current_state->state_exited(); + this->current_state = state; + this->current_state->state_entered(); +} + +void IdleState::_bind_methods() {} + +void IdleState::state_exited() { + print_line("state idle exited"); + if(this->body->is_attack_requested()) { + this->body->_attack(); + } +} + +StringName IdleState::next_state() const { + if(this->body->is_attack_requested()) { + return ActingState::get_class_static(); + } + return this->get_class_name(); +} + +void ActingState::_bind_methods() {} + +void ActingState::state_entered() { + print_line("state acting exited"); + this->timer = this->seconds_to_wait; +} + +void ActingState::process(double delta) { + this->timer -= delta; +} + +StringName ActingState::next_state() const { + if(this->timer <= 0) { + return IdleState::get_class_static(); + } + return this->get_class(); +} diff --git a/modules/tabtargeting/actor_state_machine.h b/modules/tabtargeting/actor_state_machine.h index 0f71f381..b35a88e5 100644 --- a/modules/tabtargeting/actor_state_machine.h +++ b/modules/tabtargeting/actor_state_machine.h @@ -1,13 +1,70 @@ #ifndef ACTOR_STATE_MACHINE_H #define ACTOR_STATE_MACHINE_H +#include "core/string/string_name.h" #include "scene/main/node.h" +#include "core/object/ref_counted.h" +class ActorBody; + +class ActorState : public RefCounted { + GDCLASS(ActorState, Object); + static void _bind_methods(); +public: + virtual void state_entered() {} + virtual void process(double delta) {} + virtual StringName next_state() const { + return this->get_class(); + } + virtual void state_exited() {} +protected: + ActorBody *body{nullptr}; + friend class ActorStateMachine; +}; class ActorStateMachine : public Node { GDCLASS(ActorStateMachine, Node); static void _bind_methods(); -public: + void _notification(int what); + void ready(); + void process(double delta); + template + void add_state(); + void try_next_state(); + void switch_state(ActorState *state); private: + ActorBody *body{nullptr}; + ActorState *current_state{}; + HashMap states{}; +}; + +template +void ActorStateMachine::add_state() { + ActorState *state{memnew(TState)}; + state->body = this->body; + this->states.insert(TState::get_class_static(), state); + if(current_state == nullptr) { + this->current_state = state; + } +} + +class IdleState : public ActorState { + GDCLASS(IdleState, ActorState); + static void _bind_methods(); +public: + virtual void state_exited() override; + virtual StringName next_state() const override; +}; + +class ActingState : public ActorState { + GDCLASS(ActingState, ActorState); + static void _bind_methods(); +public: + virtual void state_entered() override; + virtual void process(double delta) override; + virtual StringName next_state() const override; +private: + double const seconds_to_wait{1.0}; + double timer{0.0}; }; #endif // !ACTOR_STATE_MACHINE_H diff --git a/modules/tabtargeting/equipment.cpp b/modules/tabtargeting/equipment.cpp index 18a3de83..e480263a 100644 --- a/modules/tabtargeting/equipment.cpp +++ b/modules/tabtargeting/equipment.cpp @@ -8,6 +8,14 @@ DamageEvent::DamageEvent(int damage, ActorBody *source) , source{source} {} +int DamageEvent::get_initial_damage() const { + return this->initial_damage; +} + +ActorBody *DamageEvent::get_source() const { + return this->source; +} + void DamageStats::_bind_methods() { BIND_PROPERTY(Variant::INT, damage_add); BIND_PROPERTY(Variant::INT, damage_mul); @@ -101,7 +109,15 @@ void Equipment::_bind_methods() { } DamageEvent Equipment::apply(DamageEvent event, ActorBody *target) { - + if(this->weapon.is_valid()) + event = this->weapon->apply(event, target); + if(this->helmet.is_valid()) + event = this->helmet->apply(event, target); + if(this->chest.is_valid()) + event = this->chest->apply(event, target); + if(this->legs.is_valid()) + event = this->legs->apply(event, target); + return event; } void Equipment::set_weapon(Ref weapon) { diff --git a/modules/tabtargeting/equipment.h b/modules/tabtargeting/equipment.h index 2b540f8e..d293e847 100644 --- a/modules/tabtargeting/equipment.h +++ b/modules/tabtargeting/equipment.h @@ -2,7 +2,6 @@ #define EQUIPMENT_H #include "core/io/resource.h" -#include "core/templates/vector.h" #include "scene/main/node.h" class ActorBody; diff --git a/modules/tabtargeting/register_types.cpp b/modules/tabtargeting/register_types.cpp index ab2fc76d..b6917bc5 100644 --- a/modules/tabtargeting/register_types.cpp +++ b/modules/tabtargeting/register_types.cpp @@ -1,6 +1,7 @@ #include "register_types.h" #include "core/object/class_db.h" +#include "tabtargeting/actor_state_machine.h" #include "tabtargeting/equipment.h" #include "tabtargeting/actor_body.h" @@ -9,6 +10,10 @@ void initialize_tabtargeting_module(ModuleInitializationLevel p_level) { return; } ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); diff --git a/project/buttons.gd b/project/buttons.gd new file mode 100644 index 00000000..e2c5f68a --- /dev/null +++ b/project/buttons.gd @@ -0,0 +1,7 @@ +extends CanvasLayer + +func _on_button1_down() -> void: + $"../Actor1".request_attack($"../Actor2") + +func _on_button2_down() -> void: + $"../Actor2".request_attack($"../Actor1") diff --git a/project/buttons.gd.uid b/project/buttons.gd.uid new file mode 100644 index 00000000..588817e8 --- /dev/null +++ b/project/buttons.gd.uid @@ -0,0 +1 @@ +uid://cikkvrxl0xfut diff --git a/project/equip_items/chestplate.tres b/project/equip_items/chestplate.tres new file mode 100644 index 00000000..52e2680e --- /dev/null +++ b/project/equip_items/chestplate.tres @@ -0,0 +1,3 @@ +[gd_resource type="Armor" format=3 uid="uid://ciqakeu7a82fw"] + +[resource] diff --git a/project/equip_items/weapon1.tres b/project/equip_items/weapon1.tres new file mode 100644 index 00000000..ffa85f56 --- /dev/null +++ b/project/equip_items/weapon1.tres @@ -0,0 +1,9 @@ +[gd_resource type="Weapon" load_steps=2 format=3 uid="uid://rkcmcib35uj7"] + +[sub_resource type="DamageStats" id="DamageStats_jtxwa"] +damage_add = 1 +damage_mul = 2.0 + +[resource] +stats = SubResource("DamageStats_jtxwa") +base_damage = 5 diff --git a/project/project.godot b/project/project.godot index a51099b5..178c7ba9 100644 --- a/project/project.godot +++ b/project/project.godot @@ -11,5 +11,6 @@ config_version=5 [application] config/name="tabtargeting" -config/features=PackedStringArray("4.4", "Forward Plus") +run/main_scene="uid://b7klcvxm3e578" +config/features=PackedStringArray("4.5", "Forward Plus") config/icon="res://icon.svg" diff --git a/project/scene.tscn b/project/scene.tscn new file mode 100644 index 00000000..acbce9af --- /dev/null +++ b/project/scene.tscn @@ -0,0 +1,95 @@ +[gd_scene load_steps=9 format=3 uid="uid://b7klcvxm3e578"] + +[ext_resource type="Weapon" uid="uid://rkcmcib35uj7" path="res://equip_items/weapon1.tres" id="1_nxogm"] +[ext_resource type="Script" uid="uid://cikkvrxl0xfut" path="res://buttons.gd" id="1_ulcgi"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_nxogm"] +sky_horizon_color = Color(0.662243, 0.671743, 0.686743, 1) +ground_horizon_color = Color(0.662243, 0.671743, 0.686743, 1) + +[sub_resource type="Sky" id="Sky_3253y"] +sky_material = SubResource("ProceduralSkyMaterial_nxogm") + +[sub_resource type="Environment" id="Environment_u3cyc"] +background_mode = 2 +sky = SubResource("Sky_3253y") +tonemap_mode = 2 +glow_enabled = true + +[sub_resource type="SphereShape3D" id="SphereShape3D_drvgu"] + +[sub_resource type="CylinderMesh" id="CylinderMesh_drvgu"] + +[sub_resource type="BoxMesh" id="BoxMesh_nxogm"] +size = Vector3(-5, -5, -5) + +[node name="Scene" type="Node3D"] + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_u3cyc") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(0.433659, -0.651449, 0.622538, 0, 0.690882, 0.722967, -0.901077, -0.313521, 0.299607, 0, 0, 0) +shadow_enabled = true + +[node name="Actor1" type="ActorBody" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1.59845) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Actor1"] +shape = SubResource("SphereShape3D_drvgu") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Actor1"] +mesh = SubResource("CylinderMesh_drvgu") + +[node name="Equipment" type="Equipment" parent="Actor1"] +weapon = ExtResource("1_nxogm") + +[node name="ActorStateMachine" type="ActorStateMachine" parent="Actor1"] + +[node name="Actor2" type="ActorBody" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1.66257) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Actor2"] +shape = SubResource("SphereShape3D_drvgu") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Actor2"] +mesh = SubResource("CylinderMesh_drvgu") + +[node name="Equipment" type="Equipment" parent="Actor2"] +weapon = ExtResource("1_nxogm") + +[node name="ActorStateMachine" type="ActorStateMachine" parent="Actor2"] + +[node name="Camera3D" type="Camera3D" parent="."] +transform = Transform3D(0.251056, -0.361214, 0.898051, 0, 0.927765, 0.373166, -0.967973, -0.0936855, 0.232921, 1.89697, 0.912457, 1.11904) + +[node name="CanvasLayer" type="CanvasLayer" parent="."] +script = ExtResource("1_ulcgi") + +[node name="Button" type="Button" parent="CanvasLayer"] +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_top = -113.0 +offset_right = 327.0 +grow_vertical = 0 +text = "Attack (Actor 1)" + +[node name="Button2" type="Button" parent="CanvasLayer"] +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -327.0 +offset_top = -113.0 +grow_horizontal = 0 +grow_vertical = 0 +text = "Attack (Actor 2)" + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.57152, 0) +mesh = SubResource("BoxMesh_nxogm") + +[connection signal="button_down" from="CanvasLayer/Button" to="CanvasLayer" method="_on_button1_down"] +[connection signal="button_down" from="CanvasLayer/Button2" to="CanvasLayer" method="_on_button2_down"]