diff --git a/godot/GameObjects/enemy_unit.tscn b/godot/GameObjects/enemy_unit.tscn index 4523747..81ac260 100644 --- a/godot/GameObjects/enemy_unit.tscn +++ b/godot/GameObjects/enemy_unit.tscn @@ -32,7 +32,7 @@ available_goals_inspector = [ExtResource("1_jwvis"), ExtResource("1_b1qo1")] unique_name_in_owner = true [node name="Planner" type="Planner" parent="."] -actions_inspector = [3, 2, 4, 5, 6] +actions_inspector = [3, 2, 4, 5, 1] unique_name_in_owner = true [node name="EntityHealth" type="EntityHealth" parent="."] diff --git a/godot/GameObjects/player_unit.tscn b/godot/GameObjects/player_unit.tscn index 2b4fb02..c807cac 100644 --- a/godot/GameObjects/player_unit.tscn +++ b/godot/GameObjects/player_unit.tscn @@ -27,7 +27,7 @@ collision_mask = 0 unique_name_in_owner = true [node name="Planner" type="Planner" parent="."] -actions_inspector = [0, 1, 2] +actions_inspector = [0, 1, 2, 3] unique_name_in_owner = true [node name="EntityHealth" type="EntityHealth" parent="."] diff --git a/src/character_world_state.cpp b/src/character_world_state.cpp index b97296a..d5ae7b0 100644 --- a/src/character_world_state.cpp +++ b/src/character_world_state.cpp @@ -1,4 +1,5 @@ #include "character_world_state.hpp" +#include "unit.hpp" #include "utils/godot_macros.hpp" void CharacterWorldState::_bind_methods() { @@ -10,7 +11,11 @@ void CharacterWorldState::_enter_tree() { this->inventory = this->get_node("%Inventory"); } -bool CharacterWorldState::get_is_weapon_ranged() const { - return this->inventory->get_weapon()->has_capability(ItemCapability::WeaponRanged); +gd::String CharacterWorldState::get_weapon_animation() const { + return this->inventory->get_weapon()->get_animation(); } +bool CharacterWorldState::get_is_in_range() const { + float const distance{this->parent_unit->get_global_position().distance_to(this->target_node->get_global_position())}; + return distance <= inventory->get_stats().weapon_range; +} diff --git a/src/character_world_state.hpp b/src/character_world_state.hpp index bd35b81..c8f8164 100644 --- a/src/character_world_state.hpp +++ b/src/character_world_state.hpp @@ -9,7 +9,8 @@ class CharacterWorldState : public UnitWorldState { static void _bind_methods(); public: virtual void _enter_tree() override; - virtual bool get_is_weapon_ranged() const override; + virtual gd::String get_weapon_animation() const override; + virtual bool get_is_in_range() const override; private: Inventory *inventory{nullptr}; }; diff --git a/src/inventory.cpp b/src/inventory.cpp index 06e5ba1..c915199 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -17,7 +17,12 @@ void Inventory::_ready() { } Stats Inventory::get_stats() const { - return this->weapon->get_stats() & this->armour->get_stats() & this->helmet->get_stats() & this->utility->get_stats(); + Stats stats{}; + if(this->weapon != nullptr) this->weapon->apply_stats(stats); + if(this->utility != nullptr) this->utility->apply_stats(stats); + if(this->armour != nullptr) this->armour->apply_stats(stats); + if(this->helmet != nullptr) this->helmet->apply_stats(stats); + return stats; } void Inventory::add_item(const Item *item, unsigned num) { diff --git a/src/inventory.hpp b/src/inventory.hpp index f8ccd64..d867d56 100644 --- a/src/inventory.hpp +++ b/src/inventory.hpp @@ -36,9 +36,9 @@ public: gd::Dictionary get_inventory_inspector() const; private: Item const *weapon{nullptr}; + Item const *utility{nullptr}; Item const *armour{nullptr}; Item const *helmet{nullptr}; - Item const *utility{nullptr}; gd::HashMap inventory{}; Unit *parent_unit{nullptr}; diff --git a/src/item.cpp b/src/item.cpp index 112ce97..975348c 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -22,14 +22,17 @@ void Item::use_on(Unit *used_by, gd::Object *used_on) const { gd::UtilityFunctions::push_warning(used_by->get_path(), " tried to use default Item::use_on implementation on object of type ", used_on->get_class()); } -Stats Item::get_stats() const { - return {}; +void Item::apply_stats(Stats &stats) const { } bool Item::has_capability(ItemCapability const &capability) const { return this->capabilities.has(capability); } +gd::StringName const &Item::get_animation() const { + return this->animation; +} + ItemID Item::get_id() const { return this->id; } diff --git a/src/item.hpp b/src/item.hpp index 752cad6..e998ad5 100644 --- a/src/item.hpp +++ b/src/item.hpp @@ -48,18 +48,21 @@ public: public: virtual bool can_use_on(Unit *used_by, gd::Object *used_on) const; bool try_use_on(Unit *used_by, gd::Object *used_on) const; - virtual Stats get_stats() const; + virtual void apply_stats(Stats &stats) const; bool has_capability(ItemCapability const &capability) const; + gd::StringName const &get_animation() const; ItemID get_id() const; protected: virtual void use_on(Unit *used_by, gd::Object *used_on) const; protected: + gd::StringName animation{"RESET"}; gd::HashSet capabilities{}; private: ItemID id{-1}; }; #define ITEM_CLASS(Class_, DisplayName_, Description_)\ +friend class ItemDB;\ public: \ _FORCE_INLINE_ static godot::StringName get_static_class() { return #Class_; }\ _FORCE_INLINE_ virtual godot::StringName get_class() const override { return #Class_; }\ diff --git a/src/register_types.cpp b/src/register_types.cpp index 5e87596..9f20d2b 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -39,10 +39,9 @@ void initialize_gdextension_types(gd::ModuleInitializationLevel p_level) // always register actions before classes, // so that ActionDB::get_enum_hint is complete before _bind_methods. goap::ActionDB::register_action(); - goap::ActionDB::register_action(); + goap::ActionDB::register_action(); goap::ActionDB::register_action(); - goap::ActionDB::register_action(); - goap::ActionDB::register_action(); + goap::ActionDB::register_action(); goap::ActionDB::register_action(); goap::ActionDB::register_action(); diff --git a/src/rts_actions.cpp b/src/rts_actions.cpp index ac057d6..8bf316e 100644 --- a/src/rts_actions.cpp +++ b/src/rts_actions.cpp @@ -26,16 +26,16 @@ goap::State *MoveToTarget::get_apply_state(goap::ActorWorldState *context) const } } -FireAtTarget::FireAtTarget() +UseWeapon::UseWeapon() : Action() { this->required.insert("can_see_target", true); - this->required.insert("is_weapon_ranged", true); + this->required.insert("is_in_range", true); this->effects.insert("is_target_dead", true); } -goap::State *FireAtTarget::get_apply_state(goap::ActorWorldState *) const { +goap::State *UseWeapon::get_apply_state(goap::ActorWorldState *context) const { Animate *state{this->create_state()}; - state->animation = "fire_weapon"; + state->animation = context->get_world_property("weapon_animation"); return state; } @@ -52,33 +52,20 @@ goap::State *FindTarget::get_apply_state(goap::ActorWorldState *context) const { return state; } -GetInMeleeRange::GetInMeleeRange() +GetInRange::GetInRange() : Action() { this->require_state_complete = false; this->required.insert("can_see_target", true); - this->effects.insert("is_in_melee_range", true); + this->effects.insert("is_in_range", true); } -goap::State *GetInMeleeRange::get_apply_state(goap::ActorWorldState *context) const { +goap::State *GetInRange::get_apply_state(goap::ActorWorldState *context) const { gd::Node3D *target{gd::Object::cast_to(context->get_world_property("target_node"))}; MoveTo *state{this->create_state()}; state->target_node = target; return state; } -MeleeAttack::MeleeAttack() -: Action() { - this->required.insert("can_see_target", true); - this->required.insert("is_in_melee_range", true); - this->effects.insert("is_target_dead", true); -} - -goap::State *MeleeAttack::get_apply_state(goap::ActorWorldState *) const { - Animate *state{this->create_state()}; - state->animation = "melee_attack"; - return state; -} - TankSelfHeal::TankSelfHeal() : Action() { this->required.insert("can_see_target", false); diff --git a/src/rts_actions.hpp b/src/rts_actions.hpp index e29a219..8c9af95 100644 --- a/src/rts_actions.hpp +++ b/src/rts_actions.hpp @@ -11,11 +11,11 @@ public: virtual goap::State *get_apply_state(goap::ActorWorldState *context) const override; }; -class FireAtTarget : public goap::Action { - GOAP_ACTION(FireAtTarget); +class UseWeapon : public goap::Action { + GOAP_ACTION(UseWeapon); public: - FireAtTarget(); - virtual goap::State *get_apply_state(goap::ActorWorldState *) const override; + UseWeapon(); + virtual goap::State *get_apply_state(goap::ActorWorldState *context) const override; }; class FindTarget : public goap::Action { @@ -25,20 +25,13 @@ public: virtual goap::State *get_apply_state(goap::ActorWorldState *context) const override; }; -class GetInMeleeRange : public goap::Action { - GOAP_ACTION(GetInMeleeRange); +class GetInRange : public goap::Action { + GOAP_ACTION(GetInRange); public: - GetInMeleeRange(); + GetInRange(); virtual goap::State *get_apply_state(goap::ActorWorldState *context) const override; }; -class MeleeAttack : public goap::Action { - GOAP_ACTION(MeleeAttack); -public: - MeleeAttack(); - virtual goap::State *get_apply_state(goap::ActorWorldState *) const override; -}; - class TankSelfHeal : public goap::Action { GOAP_ACTION(TankSelfHeal); public: diff --git a/src/rts_items.cpp b/src/rts_items.cpp index 6d6f00c..c69bc7f 100644 --- a/src/rts_items.cpp +++ b/src/rts_items.cpp @@ -19,6 +19,7 @@ void Medkit::use_on(Unit *used_by, gd::Object *used_on) const { Handgun::Handgun() : Item() { + this->animation = "fire_weapon"; this->capabilities.insert(ItemCapability::WeaponEquip); this->capabilities.insert(ItemCapability::WeaponRanged); } @@ -28,6 +29,10 @@ bool Handgun::can_use_on(Unit *used_by, gd::Object *used_on) const { && used_by->get_world_state()->get_can_see_target(); } +void Handgun::apply_stats(Stats &stats) const { + stats.weapon_range = 10.f; +} + void Handgun::use_on(Unit* used_by, gd::Object* used_on) const { Unit *target{gd::Object::cast_to(used_on)}; used_by->aim_at(target); diff --git a/src/rts_items.hpp b/src/rts_items.hpp index e8dc2df..d06150c 100644 --- a/src/rts_items.hpp +++ b/src/rts_items.hpp @@ -6,8 +6,9 @@ class Medkit : public Item { ITEM_CLASS(Medkit, "Medkit", "Standard emergency home medical kit. Use to manage wounds and stabilize a person."); -public: +private: Medkit(); +public: virtual bool can_use_on(Unit *used_by, gd::Object *used_on) const override; protected: virtual void use_on(Unit *used_by, gd::Object *used_on) const override; @@ -16,9 +17,11 @@ protected: class Handgun : public Item { ITEM_CLASS(Handgun, "9mm handgun.", "A standard issue police firearm."); -public: +private: Handgun(); +public: virtual bool can_use_on(Unit *used_by, gd::Object *used_on) const override; + virtual void apply_stats(Stats &stats) const override; protected: virtual void use_on(Unit *used_by, gd::Object *used_on) const override; }; @@ -26,8 +29,9 @@ protected: class Lasercutter : public Item { ITEM_CLASS(Lasercutter, "Lasercutter", "A laser cutter, use to clear metal obstacles."); -public: +private: Lasercutter(); +public: // virtual bool can_use_on(Unit *used_by, gd::Object *object) const override; protected: // virtual void use_on(Unit *used_by, gd::Object *object) const override; @@ -36,8 +40,9 @@ protected: class Welder : public Item { ITEM_CLASS(Welder, "Welder", "A welder with tanks intended to be carried on a person's back. Use to repair broken metal parts."); -public: +private: Welder(); +public: // virtual bool can_use_on(Unit *used_by, gd::Object *object) const override; protected: // virtual void use_on(Unit *used_by, gd::Object *object) const override; diff --git a/src/stats.cpp b/src/stats.cpp index 76f1156..a23bb4e 100644 --- a/src/stats.cpp +++ b/src/stats.cpp @@ -2,14 +2,3 @@ #include namespace gd = godot; - -Stats operator &(Stats const &lhs, Stats const &rhs) { - Stats rval{}; - rval.damage_absorb = gd::Math::min(lhs.damage_absorb + rhs.damage_absorb, 1.f); - rval.hazmat_level = gd::Math::max(lhs.hazmat_level, rhs.hazmat_level); - return rval; -} - -Stats &operator <<(Stats &lhs, Stats const &rhs) { - return (lhs = lhs & rhs); -} diff --git a/src/stats.hpp b/src/stats.hpp index 89697cf..a8c5b53 100644 --- a/src/stats.hpp +++ b/src/stats.hpp @@ -7,11 +7,10 @@ namespace gd = godot; struct Stats { - float damage_absorb{0.f}; - int hazmat_level{0}; + float weapon_range{1.f}; + + float armour_damage_absorb{0.f}; + int armour_hazmat_level{0}; }; -Stats operator &(Stats const &lhs, Stats const &rhs); -Stats &operator <<(Stats &lhs, Stats const &rhs); - #endif // !STATS_HPP diff --git a/src/unit.cpp b/src/unit.cpp index 8301e0e..00af6f6 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -76,7 +76,7 @@ void Unit::begin_goal(gd::Ref goal) { void Unit::use_weapon() { Unit *target_unit{gd::Object::cast_to(this->world_state->get_target_node())}; - if(target_unit != nullptr && this->world_state->get_can_see_target() && this->world_state->get_is_in_melee_range()) + if(target_unit != nullptr && this->world_state->get_can_see_target() && this->world_state->get_is_in_range()) target_unit->get_entity_health()->damaged_by(1, this); } diff --git a/src/unit.hpp b/src/unit.hpp index 6f46824..fee712a 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -32,7 +32,6 @@ public: void begin_marker_temporary(GoalMarker *marker); void begin_goal(gd::Ref goal); void set_target_goal(gd::Node3D *target, gd::Ref goal); - virtual void use_weapon(); void aim_at(gd::Node3D *node); void on_unconscious(Unit *damage_source); diff --git a/src/unit_world_state.cpp b/src/unit_world_state.cpp index 40635b6..8de6b32 100644 --- a/src/unit_world_state.cpp +++ b/src/unit_world_state.cpp @@ -12,14 +12,14 @@ void UnitWorldState::_bind_methods() { #define CLASSNAME UnitWorldState GDSIGNAL("attention_changed"); - GDFUNCTION(get_is_weapon_ranged); + GDFUNCTION(get_weapon_animation); GDFUNCTION(get_can_see_target); GDFUNCTION(get_is_target_dead); GDFUNCTION(get_is_at_target); GDFUNCTION(get_has_target); GDFUNCTION(get_target_node); GDFUNCTION(get_is_target_enemy); - GDFUNCTION(get_is_in_melee_range); + GDFUNCTION(get_is_in_range); GDFUNCTION(get_is_health_safe); GDFUNCTION(get_parent_global_position); GDFUNCTION(get_target_global_position); @@ -42,8 +42,8 @@ gd::Variant UnitWorldState::get_world_property(gd::String property) { return ActorWorldState::get_world_property(property); } -bool UnitWorldState::get_is_weapon_ranged() const { - return false; +gd::String UnitWorldState::get_weapon_animation() const { + return "melee_attack"; } bool UnitWorldState::get_can_see_target() { @@ -105,10 +105,9 @@ bool UnitWorldState::get_is_unit_enemy(Unit *unit) const { && unit->get_team() != this->parent_unit->get_team(); } -bool UnitWorldState::get_is_in_melee_range() const { +bool UnitWorldState::get_is_in_range() const { return this->target_node != nullptr - && this->target_node->get_global_position() - .distance_squared_to(this->parent_unit->get_global_position()) <= 2.f * 2.f; + && this->target_node->get_global_position().distance_squared_to(this->parent_unit->get_global_position()) <= 2.f * 2.f; } bool UnitWorldState::get_is_health_safe() const { diff --git a/src/unit_world_state.hpp b/src/unit_world_state.hpp index 87f924a..6a49872 100644 --- a/src/unit_world_state.hpp +++ b/src/unit_world_state.hpp @@ -23,7 +23,7 @@ public: virtual void _enter_tree() override; virtual gd::Variant get_world_property(gd::String property) override; - virtual bool get_is_weapon_ranged() const; + virtual gd::String get_weapon_animation() const; bool get_can_see_target(); bool get_can_see_node(gd::Node3D *node) const; bool get_is_at_target() const; @@ -32,7 +32,7 @@ public: bool get_is_target_unit() const; bool get_is_target_enemy() const; bool get_is_unit_enemy(Unit *unit) const; - bool get_is_in_melee_range() const; + virtual bool get_is_in_range() const; bool get_is_health_safe() const; gd::Vector3 get_parent_global_position() const; gd::Vector3 get_target_global_position() const;