feat: implemented item and inventory outline
This commit is contained in:
		
							parent
							
								
									f771b383f3
								
							
						
					
					
						commit
						a024aab203
					
				| 
						 | 
					@ -28,7 +28,7 @@ collision_layer = 6
 | 
				
			||||||
collision_mask = 0
 | 
					collision_mask = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[node name="ActorWorldState" type="EnemyWorldState" parent="."]
 | 
					[node name="ActorWorldState" type="EnemyWorldState" parent="."]
 | 
				
			||||||
editor_available_goals = [ExtResource("1_jwvis"), ExtResource("1_b1qo1")]
 | 
					available_goals_inspector = [ExtResource("1_jwvis"), ExtResource("1_b1qo1")]
 | 
				
			||||||
unique_name_in_owner = true
 | 
					unique_name_in_owner = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[node name="Planner" type="Planner" parent="."]
 | 
					[node name="Planner" type="Planner" parent="."]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +33,10 @@ unique_name_in_owner = true
 | 
				
			||||||
[node name="EntityHealth" type="EntityHealth" parent="."]
 | 
					[node name="EntityHealth" type="EntityHealth" parent="."]
 | 
				
			||||||
unique_name_in_owner = true
 | 
					unique_name_in_owner = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[node name="Inventory" type="Inventory" parent="."]
 | 
				
			||||||
 | 
					weapon = 2
 | 
				
			||||||
 | 
					unique_name_in_owner = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="."]
 | 
					[node name="NavigationAgent3D" type="NavigationAgent3D" parent="."]
 | 
				
			||||||
unique_name_in_owner = true
 | 
					unique_name_in_owner = true
 | 
				
			||||||
path_desired_distance = 0.2
 | 
					path_desired_distance = 0.2
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,10 +8,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EnemyWorldState::_bind_methods() {
 | 
					void EnemyWorldState::_bind_methods() {
 | 
				
			||||||
#define CLASSNAME EnemyWorldState
 | 
					#define CLASSNAME EnemyWorldState
 | 
				
			||||||
    GDPROPERTY_HINTED(editor_available_goals,
 | 
					    GDPROPERTY_HINTED(available_goals_inspector, gd::Variant::ARRAY, gd::PROPERTY_HINT_ARRAY_TYPE, GDRESOURCETYPE("Goal"));
 | 
				
			||||||
                      gd::Variant::ARRAY,
 | 
					 | 
				
			||||||
                      gd::PROPERTY_HINT_ARRAY_TYPE,
 | 
					 | 
				
			||||||
                      GDRESOURCETYPE("Goal"));
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EnemyWorldState::_ready() { GDGAMEONLY();
 | 
					void EnemyWorldState::_ready() { GDGAMEONLY();
 | 
				
			||||||
| 
						 | 
					@ -144,13 +141,13 @@ void EnemyWorldState::try_set_target(Unit *unit) {
 | 
				
			||||||
        this->parent_unit->set_target_goal(unit, goal);
 | 
					        this->parent_unit->set_target_goal(unit, goal);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EnemyWorldState::set_editor_available_goals(gd::Array array) {
 | 
					void EnemyWorldState::set_available_goals_inspector(gd::Array array) {
 | 
				
			||||||
    this->available_goals.clear();
 | 
					    this->available_goals.clear();
 | 
				
			||||||
    for(int i{0}; i < array.size(); ++i)
 | 
					    for(int i{0}; i < array.size(); ++i)
 | 
				
			||||||
        this->available_goals.push_back(array[i]);
 | 
					        this->available_goals.push_back(array[i]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
gd::Array EnemyWorldState::get_editor_available_goals() const {
 | 
					gd::Array EnemyWorldState::get_available_goals_inspector() const {
 | 
				
			||||||
    gd::Array a{};
 | 
					    gd::Array a{};
 | 
				
			||||||
    for(gd::Ref<goap::Goal> const &goal : this->available_goals)
 | 
					    for(gd::Ref<goap::Goal> const &goal : this->available_goals)
 | 
				
			||||||
        a.push_back(goal);
 | 
					        a.push_back(goal);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,8 +36,8 @@ private:
 | 
				
			||||||
    float calculate_priority(Unit *target);
 | 
					    float calculate_priority(Unit *target);
 | 
				
			||||||
    gd::Ref<goap::Goal> get_goal_for_target(Unit *unit);
 | 
					    gd::Ref<goap::Goal> get_goal_for_target(Unit *unit);
 | 
				
			||||||
    void try_set_target(Unit *target);
 | 
					    void try_set_target(Unit *target);
 | 
				
			||||||
    void set_editor_available_goals(gd::Array array);
 | 
					    void set_available_goals_inspector(gd::Array array);
 | 
				
			||||||
    gd::Array get_editor_available_goals() const;
 | 
					    gd::Array get_available_goals_inspector() const;
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    gd::Callable aware_unit_death{callable_mp(this, &EnemyWorldState::on_aware_unit_death)};
 | 
					    gd::Callable aware_unit_death{callable_mp(this, &EnemyWorldState::on_aware_unit_death)};
 | 
				
			||||||
    gd::Vector<gd::Ref<goap::Goal>> available_goals{};
 | 
					    gd::Vector<gd::Ref<goap::Goal>> available_goals{};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,21 +73,33 @@ float EntityHealth::get_wounds_damage_factor() const {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool EntityHealth::is_conscious() const {
 | 
					bool EntityHealth::is_conscious() const {
 | 
				
			||||||
    return !this->is_dead() && this->wounds_current > 0;
 | 
					    return !this->is_dead()
 | 
				
			||||||
 | 
					        && this->wounds_current > 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool EntityHealth::can_be_revived() const {
 | 
					bool EntityHealth::can_be_revived() const {
 | 
				
			||||||
    return this->wounds_current <= 0 && this->injury_current > 0;
 | 
					    return this->wounds_current <= 0
 | 
				
			||||||
 | 
					        && this->injury_current > 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool EntityHealth::can_be_healed() const {
 | 
					bool EntityHealth::can_be_healed() const {
 | 
				
			||||||
    return this->injury_current > 0 && this->wounds_current > 0 && this->wounds_current < this->wounds_max;
 | 
					    return this->injury_current > 0
 | 
				
			||||||
 | 
					        && this->wounds_current > 0
 | 
				
			||||||
 | 
					        && this->wounds_current < this->wounds_max;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool EntityHealth::is_dead() const {
 | 
					bool EntityHealth::is_dead() const {
 | 
				
			||||||
    return this->injury_current <= 0;
 | 
					    return this->injury_current <= 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void EntityHealth::set_stats(Stats const *stats) {
 | 
				
			||||||
 | 
					    this->stats = stats;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Stats const *EntityHealth::get_stats() const {
 | 
				
			||||||
 | 
					    return this->stats;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void EntityHealth::set_injury_max(int max_health) {
 | 
					void EntityHealth::set_injury_max(int max_health) {
 | 
				
			||||||
    this->injury_max = max_health;
 | 
					    this->injury_max = max_health;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
#ifndef RTS_ENTITY_HEALTH_HPP
 | 
					#ifndef RTS_ENTITY_HEALTH_HPP
 | 
				
			||||||
#define RTS_ENTITY_HEALTH_HPP
 | 
					#define RTS_ENTITY_HEALTH_HPP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "stats.hpp"
 | 
				
			||||||
#include <godot_cpp/classes/node.hpp>
 | 
					#include <godot_cpp/classes/node.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace gd = godot;
 | 
					namespace gd = godot;
 | 
				
			||||||
| 
						 | 
					@ -25,6 +26,8 @@ public:
 | 
				
			||||||
    bool can_be_healed() const;
 | 
					    bool can_be_healed() const;
 | 
				
			||||||
    bool can_be_damaged() const;
 | 
					    bool can_be_damaged() const;
 | 
				
			||||||
    bool is_dead() const;
 | 
					    bool is_dead() const;
 | 
				
			||||||
 | 
					    void set_stats(Stats const *stats);
 | 
				
			||||||
 | 
					    Stats const *get_stats() const;
 | 
				
			||||||
    void set_injury_max(int max_health);
 | 
					    void set_injury_max(int max_health);
 | 
				
			||||||
    int get_injury_max() const;
 | 
					    int get_injury_max() const;
 | 
				
			||||||
    int get_injury_current() const;
 | 
					    int get_injury_current() const;
 | 
				
			||||||
| 
						 | 
					@ -32,6 +35,7 @@ public:
 | 
				
			||||||
    int get_wounds_max() const;
 | 
					    int get_wounds_max() const;
 | 
				
			||||||
    int get_wounds_current() const;
 | 
					    int get_wounds_current() const;
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					    Stats const *stats;
 | 
				
			||||||
    // long term health
 | 
					    // long term health
 | 
				
			||||||
    int injury_max{10};
 | 
					    int injury_max{10};
 | 
				
			||||||
    int injury_current{0};
 | 
					    int injury_current{0};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										85
									
								
								src/inventory.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/inventory.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,85 @@
 | 
				
			||||||
 | 
					#include "inventory.hpp"
 | 
				
			||||||
 | 
					#include "item_db.hpp"
 | 
				
			||||||
 | 
					#include "unit.hpp"
 | 
				
			||||||
 | 
					#include "utils/godot_macros.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Inventory::_bind_methods() {
 | 
				
			||||||
 | 
					#define CLASSNAME Inventory
 | 
				
			||||||
 | 
					    GDPROPERTY_HINTED(weapon_id, gd::Variant::INT, gd::PROPERTY_HINT_ENUM, ItemDB::get_enum_hint());
 | 
				
			||||||
 | 
					    GDPROPERTY_HINTED(armour_id, gd::Variant::INT, gd::PROPERTY_HINT_ENUM, ItemDB::get_enum_hint());
 | 
				
			||||||
 | 
					    GDPROPERTY(inventory_inspector, gd::Variant::DICTIONARY);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Inventory::_ready() {
 | 
				
			||||||
 | 
					    this->parent_unit = gd::Object::cast_to<Unit>(this->get_parent());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Stats Inventory::get_stats() const {
 | 
				
			||||||
 | 
					    return this->weapon->get_stats() & this->armour->get_stats() & this->helmet->get_stats() & this->utility->get_stats();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Inventory::add_item(const Item *item, unsigned num) {
 | 
				
			||||||
 | 
					    if(!this->inventory.has(item))
 | 
				
			||||||
 | 
					        this->inventory.insert(item, num);
 | 
				
			||||||
 | 
					    else if(num > 0)
 | 
				
			||||||
 | 
					        this->inventory[item] += num;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned Inventory::get_item_amount(Item const *item) {
 | 
				
			||||||
 | 
					    return this->inventory.has(item) ? this->inventory[item] : 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Inventory::has_item(Item const *item) {
 | 
				
			||||||
 | 
					    return this->inventory.has(item);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Inventory::set_weapon(Item const *item) {
 | 
				
			||||||
 | 
					    this->weapon = item != nullptr && item->has_capability(ItemCapability::WeaponEquip) ? item : nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Item const *Inventory::get_weapon() const {
 | 
				
			||||||
 | 
					    return this->weapon;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Inventory::set_armour(Item const *item) {
 | 
				
			||||||
 | 
					    this->armour = item != nullptr && item->has_capability(ItemCapability::ArmourEquip) ? item : nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Item const *Inventory::get_armour() const {
 | 
				
			||||||
 | 
					    return this->armour;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Inventory::set_weapon_id(int item_id) {
 | 
				
			||||||
 | 
					    this->set_weapon(ItemDB::get_item(item_id));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int Inventory::get_weapon_id() const {
 | 
				
			||||||
 | 
					    return this->weapon ? this->weapon->get_id() : 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Inventory::set_armour_id(int item_id) {
 | 
				
			||||||
 | 
					    this->set_armour(ItemDB::get_item(item_id));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int Inventory::get_armour_id() const {
 | 
				
			||||||
 | 
					    return this->armour ? this->armour->get_id() : 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Inventory::set_inventory_inspector(gd::Dictionary dict) {
 | 
				
			||||||
 | 
					    this->inventory.clear();
 | 
				
			||||||
 | 
					    gd::Array a{dict.keys()};
 | 
				
			||||||
 | 
					    for(int i{0}; i < a.size(); ++i) {
 | 
				
			||||||
 | 
					        gd::StringName const item_name{a[i]};
 | 
				
			||||||
 | 
					        unsigned const amount{dict[item_name]};
 | 
				
			||||||
 | 
					        Item const *item{ItemDB::get_item_by_name(item_name)};
 | 
				
			||||||
 | 
					        if(item != nullptr && amount > 0)
 | 
				
			||||||
 | 
					            this->inventory[item] = amount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gd::Dictionary Inventory::get_inventory_inspector() const {
 | 
				
			||||||
 | 
					    gd::Dictionary dict{};
 | 
				
			||||||
 | 
					    for(gd::KeyValue<Item const *, unsigned> const &item : this->inventory)
 | 
				
			||||||
 | 
					        dict[item.key->get_class()] = item.value;
 | 
				
			||||||
 | 
					    return dict;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										47
									
								
								src/inventory.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/inventory.hpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,47 @@
 | 
				
			||||||
 | 
					#ifndef INVENTORY_HPP
 | 
				
			||||||
 | 
					#define INVENTORY_HPP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "item.hpp"
 | 
				
			||||||
 | 
					#include "stats.hpp"
 | 
				
			||||||
 | 
					#include "utils/godot_macros.hpp"
 | 
				
			||||||
 | 
					#include <godot_cpp/classes/node.hpp>
 | 
				
			||||||
 | 
					#include <godot_cpp/classes/ref_counted.hpp>
 | 
				
			||||||
 | 
					#include <godot_cpp/templates/pair.hpp>
 | 
				
			||||||
 | 
					#include <godot_cpp/templates/hash_map.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace gd = godot;
 | 
				
			||||||
 | 
					class Unit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Inventory : public gd::Node {
 | 
				
			||||||
 | 
					    GDCLASS(Inventory, gd::Node);
 | 
				
			||||||
 | 
					    static void _bind_methods();
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    virtual void _ready() override;
 | 
				
			||||||
 | 
					    Stats get_stats() const;
 | 
				
			||||||
 | 
					    Item const *get_capability_for(ItemCapability const &capability, gd::Object *target) const;
 | 
				
			||||||
 | 
					    void add_item(Item const *item, unsigned num);
 | 
				
			||||||
 | 
					    unsigned get_item_amount(Item const *item);
 | 
				
			||||||
 | 
					    bool has_item(Item const *item);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void set_weapon(Item const *weapon);
 | 
				
			||||||
 | 
					    Item const *get_weapon() const;
 | 
				
			||||||
 | 
					    void set_armour(Item const *weapon);
 | 
				
			||||||
 | 
					    Item const *get_armour() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void set_weapon_id(int item_id);
 | 
				
			||||||
 | 
					    int get_weapon_id() const;
 | 
				
			||||||
 | 
					    void set_armour_id(int item_id);
 | 
				
			||||||
 | 
					    int get_armour_id() const;
 | 
				
			||||||
 | 
					    void set_inventory_inspector(gd::Dictionary dict);
 | 
				
			||||||
 | 
					    gd::Dictionary get_inventory_inspector() const;
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    Item const *weapon{nullptr};
 | 
				
			||||||
 | 
					    Item const *armour{nullptr};
 | 
				
			||||||
 | 
					    Item const *helmet{nullptr};
 | 
				
			||||||
 | 
					    Item const *utility{nullptr};
 | 
				
			||||||
 | 
					    gd::HashMap<Item const *, unsigned> inventory{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Unit *parent_unit{nullptr};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // !INVENTORY_HPP
 | 
				
			||||||
							
								
								
									
										35
									
								
								src/item.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/item.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					#include "item.hpp"
 | 
				
			||||||
 | 
					#include "unit.hpp"
 | 
				
			||||||
 | 
					#include "utils/godot_macros.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Item::~Item() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Item::can_use_on(Unit *used_by, gd::Object *used_on) const {
 | 
				
			||||||
 | 
					    return used_on != nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Item::try_use_on(Unit *used_by, gd::Object *used_on) const {
 | 
				
			||||||
 | 
					    if(this->can_use_on(used_by, used_on)) {
 | 
				
			||||||
 | 
					        this->use_on(used_by, used_on);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Item::use_on(Unit *used_by, gd::Object *used_on) const {
 | 
				
			||||||
 | 
					    if(used_on != nullptr)
 | 
				
			||||||
 | 
					        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 {};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Item::has_capability(ItemCapability const &capability) const {
 | 
				
			||||||
 | 
					    return this->capabilities.has(capability);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ItemID Item::get_id() const {
 | 
				
			||||||
 | 
					    return this->id;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										70
									
								
								src/item.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/item.hpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,70 @@
 | 
				
			||||||
 | 
					#ifndef ITEM_HPP
 | 
				
			||||||
 | 
					#define ITEM_HPP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "stats.hpp"
 | 
				
			||||||
 | 
					#include "goap/action.hpp"
 | 
				
			||||||
 | 
					#include "utils/godot_macros.hpp"
 | 
				
			||||||
 | 
					#include <godot_cpp/templates/hash_set.hpp>
 | 
				
			||||||
 | 
					#include <godot_cpp/variant/utility_functions.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace gd = godot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GDENUM(ItemCapability,
 | 
				
			||||||
 | 
					    Heal,
 | 
				
			||||||
 | 
					    Revive,
 | 
				
			||||||
 | 
					    WeaponEquip,
 | 
				
			||||||
 | 
					    WeaponRanged,
 | 
				
			||||||
 | 
					    WeaponMelee,
 | 
				
			||||||
 | 
					    ArmourEquip,
 | 
				
			||||||
 | 
					    HeadEquip,
 | 
				
			||||||
 | 
					    UtilityEquip,
 | 
				
			||||||
 | 
					    Consume
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef int ItemID;
 | 
				
			||||||
 | 
					class Unit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Item {
 | 
				
			||||||
 | 
					friend class ItemDB;
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    static gd::StringName get_static_class() {
 | 
				
			||||||
 | 
					        gd::UtilityFunctions::push_error("Calling Item::get_static_class() on an Item class that does not implement it. Use ITEM_CLASS(*) macro.");
 | 
				
			||||||
 | 
					        return "Item";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    virtual gd::StringName get_class() const {
 | 
				
			||||||
 | 
					        gd::UtilityFunctions::push_error("Calling Item::get_class() on an Item class that does not implement it. Use ITEM_CLASS(*) macro.");
 | 
				
			||||||
 | 
					        return "Item";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    virtual gd::String get_display_name() const {
 | 
				
			||||||
 | 
					        gd::UtilityFunctions::push_error("Calling Item::get_display_name() on an Item class that does not implement it. Use ITEM_CLASS(*) macro.");
 | 
				
			||||||
 | 
					        return "<UNNAMED ITEM>";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    virtual gd::String get_description() const {
 | 
				
			||||||
 | 
					        gd::UtilityFunctions::push_error("Calling Item::get_description() on an Item class that does not implement it. Use ITEM_CLASS(*) macro.");
 | 
				
			||||||
 | 
					        return "<UNDESCRIBED>";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    Item() = default;
 | 
				
			||||||
 | 
					    virtual ~Item();
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
					    bool has_capability(ItemCapability const &capability) const;
 | 
				
			||||||
 | 
					    ItemID get_id() const;
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					    virtual void use_on(Unit *used_by, gd::Object *used_on) const;
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					    gd::HashSet<uint32_t> capabilities{};
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    ItemID id{-1};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ITEM_CLASS(Class_, DisplayName_, Description_)\
 | 
				
			||||||
 | 
					public: \
 | 
				
			||||||
 | 
					    _FORCE_INLINE_ static godot::StringName get_static_class() { return #Class_; }\
 | 
				
			||||||
 | 
					    _FORCE_INLINE_ virtual godot::StringName get_class() const override { return #Class_; }\
 | 
				
			||||||
 | 
					    _FORCE_INLINE_ virtual godot::String get_display_name() const override { return DisplayName_; }\
 | 
				
			||||||
 | 
					    _FORCE_INLINE_ virtual godot::String get_description() const override { return Description_; }\
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // !ITEM_HPP
 | 
				
			||||||
							
								
								
									
										58
									
								
								src/item_db.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/item_db.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,58 @@
 | 
				
			||||||
 | 
					#include "item_db.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ItemDB::StaticData::~StaticData() {
 | 
				
			||||||
 | 
					    if(this->hint != nullptr)
 | 
				
			||||||
 | 
					        delete this->hint;
 | 
				
			||||||
 | 
					    if(this->array_hint != nullptr)
 | 
				
			||||||
 | 
					        delete this->array_hint;
 | 
				
			||||||
 | 
					    for(Item *action : this->items)
 | 
				
			||||||
 | 
					        delete action;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gd::String &ItemDB::StaticData::get_hint() {
 | 
				
			||||||
 | 
					    if(this->hint == nullptr)
 | 
				
			||||||
 | 
					        this->hint = new gd::String("None");
 | 
				
			||||||
 | 
					    return *this->hint;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gd::String &ItemDB::StaticData::get_array_hint() {
 | 
				
			||||||
 | 
					    if(this->array_hint == nullptr)
 | 
				
			||||||
 | 
					        this->array_hint = new gd::String("None");
 | 
				
			||||||
 | 
					    return *this->array_hint;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ItemID ItemDB::register_item(Item *item, gd::String item_name) {
 | 
				
			||||||
 | 
					    item->id = ItemDB::data.items.size()+1;
 | 
				
			||||||
 | 
					    ItemDB::data.get_hint() += ",";
 | 
				
			||||||
 | 
					    ItemDB::data.get_hint() += item_name;
 | 
				
			||||||
 | 
					    ItemDB::data.get_array_hint() = gd::vformat("%s/%s:%s", gd::Variant::INT, gd::PROPERTY_HINT_ENUM, ItemDB::data.get_hint());
 | 
				
			||||||
 | 
					    ItemDB::data.items.push_back(item);
 | 
				
			||||||
 | 
					    gd::UtilityFunctions::print("registered item type ", item_name);
 | 
				
			||||||
 | 
					    return item->get_id();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Item const *ItemDB::get_item(ItemID index) {
 | 
				
			||||||
 | 
					    index -= 1;
 | 
				
			||||||
 | 
					    if(ItemDB::data.items.size() <= index || index < 0) {
 | 
				
			||||||
 | 
					        gd::UtilityFunctions::push_warning("Attempted to get pointer to non-existent item type by ID ", index);
 | 
				
			||||||
 | 
					        return nullptr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return ItemDB::data.items[index];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gd::String const &ItemDB::get_enum_hint() {
 | 
				
			||||||
 | 
					    return ItemDB::data.get_hint();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gd::String const &ItemDB::get_array_hint() {
 | 
				
			||||||
 | 
					    return ItemDB::data.get_array_hint();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Item const *ItemDB::get_item_by_name(gd::StringName item_name) {
 | 
				
			||||||
 | 
					    for(Item const *item : ItemDB::data.items)
 | 
				
			||||||
 | 
					        if(item->get_class() == item_name)
 | 
				
			||||||
 | 
					            return item;
 | 
				
			||||||
 | 
					    return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ItemDB::StaticData ItemDB::data{};
 | 
				
			||||||
							
								
								
									
										34
									
								
								src/item_db.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/item_db.hpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					#ifndef ITEM_DB_HPP
 | 
				
			||||||
 | 
					#define ITEM_DB_HPP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "item.hpp"
 | 
				
			||||||
 | 
					#include <godot_cpp/templates/vector.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace gd = godot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ItemDB {
 | 
				
			||||||
 | 
					    struct StaticData {
 | 
				
			||||||
 | 
					        StaticData() = default;
 | 
				
			||||||
 | 
					        ~StaticData();
 | 
				
			||||||
 | 
					        gd::String &get_hint();
 | 
				
			||||||
 | 
					        gd::String &get_array_hint();
 | 
				
			||||||
 | 
					        gd::String *array_hint{};
 | 
				
			||||||
 | 
					        gd::String *hint{};
 | 
				
			||||||
 | 
					        gd::Vector<Item*> items{};
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    static ItemID register_item(Item *item, gd::String item_name);
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    static Item const *get_item(ItemID id);
 | 
				
			||||||
 | 
					    static gd::String const &get_enum_hint();
 | 
				
			||||||
 | 
					    static gd::String const &get_array_hint();
 | 
				
			||||||
 | 
					    static Item const *get_item_by_name(gd::StringName item);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    template <class TItem>
 | 
				
			||||||
 | 
					    _FORCE_INLINE_ static ItemID register_item() {
 | 
				
			||||||
 | 
					        return ItemDB::register_item(new TItem(), TItem::get_static_class());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    static StaticData data;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // !ITEM_DB_HPP
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void NavRoom::_bind_methods() {
 | 
					void NavRoom::_bind_methods() {
 | 
				
			||||||
#define CLASSNAME NavRoom
 | 
					#define CLASSNAME NavRoom
 | 
				
			||||||
    GDPROPERTY_HINTED(editor_neighbours, gd::Variant::ARRAY, gd::PROPERTY_HINT_ARRAY_TYPE, GDNODETYPE("NavRoom"));
 | 
					    GDPROPERTY_HINTED(neighbours_inspector, gd::Variant::ARRAY, gd::PROPERTY_HINT_ARRAY_TYPE, GDNODETYPE("NavRoom"));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
NavRoom *NavRoom::get_closest_room(gd::Vector3 const &closest_to) {
 | 
					NavRoom *NavRoom::get_closest_room(gd::Vector3 const &closest_to) {
 | 
				
			||||||
| 
						 | 
					@ -44,14 +44,14 @@ gd::Vector<NavMarker*> const &NavRoom::get_markers() const {
 | 
				
			||||||
    return this->markers;
 | 
					    return this->markers;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
gd::Array NavRoom::get_editor_neighbours() const {
 | 
					gd::Array NavRoom::get_neighbours_inspector() const {
 | 
				
			||||||
    gd::Array a{};
 | 
					    gd::Array a{};
 | 
				
			||||||
    for(NavRoom *room : this->neighbours)
 | 
					    for(NavRoom *room : this->neighbours)
 | 
				
			||||||
        a.push_back(room);
 | 
					        a.push_back(room);
 | 
				
			||||||
    return a;
 | 
					    return a;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void NavRoom::set_editor_neighbours(gd::Array array) {
 | 
					void NavRoom::set_neighbours_inspector(gd::Array array) {
 | 
				
			||||||
    this->neighbours.clear();
 | 
					    this->neighbours.clear();
 | 
				
			||||||
    while(!array.is_empty()) if(NavRoom *room{gd::Object::cast_to<NavRoom>(array.pop_front())})
 | 
					    while(!array.is_empty()) if(NavRoom *room{gd::Object::cast_to<NavRoom>(array.pop_front())})
 | 
				
			||||||
        this->neighbours.push_back(room);
 | 
					        this->neighbours.push_back(room);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,8 +19,8 @@ public:
 | 
				
			||||||
    gd::Vector<NavRoom*> const &get_neighbours() const;
 | 
					    gd::Vector<NavRoom*> const &get_neighbours() const;
 | 
				
			||||||
    gd::Vector<NavMarker*> const &get_markers() const;
 | 
					    gd::Vector<NavMarker*> const &get_markers() const;
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    void set_editor_neighbours(gd::Array array);
 | 
					    void set_neighbours_inspector(gd::Array array);
 | 
				
			||||||
    gd::Array get_editor_neighbours() const;
 | 
					    gd::Array get_neighbours_inspector() const;
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    float radius{1.f};
 | 
					    float radius{1.f};
 | 
				
			||||||
    gd::Vector3 centre{0.f, 0.f, 0.f};
 | 
					    gd::Vector3 centre{0.f, 0.f, 0.f};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,13 @@
 | 
				
			||||||
#include "register_types.h"
 | 
					#include "register_types.h"
 | 
				
			||||||
#include "enemy_world_state.hpp"
 | 
					#include "enemy_world_state.hpp"
 | 
				
			||||||
#include "entity_health.hpp"
 | 
					#include "entity_health.hpp"
 | 
				
			||||||
#include "goap/state.hpp"
 | 
					#include "inventory.hpp"
 | 
				
			||||||
 | 
					#include "item_db.hpp"
 | 
				
			||||||
#include "nav_marker.hpp"
 | 
					#include "nav_marker.hpp"
 | 
				
			||||||
#include "nav_room.hpp"
 | 
					#include "nav_room.hpp"
 | 
				
			||||||
#include "rts_actions.hpp"
 | 
					#include "rts_actions.hpp"
 | 
				
			||||||
#include "rts_game_mode.hpp"
 | 
					#include "rts_game_mode.hpp"
 | 
				
			||||||
 | 
					#include "rts_items.hpp"
 | 
				
			||||||
#include "rts_player.hpp"
 | 
					#include "rts_player.hpp"
 | 
				
			||||||
#include "rts_states.hpp"
 | 
					#include "rts_states.hpp"
 | 
				
			||||||
#include "unit.hpp"
 | 
					#include "unit.hpp"
 | 
				
			||||||
| 
						 | 
					@ -15,6 +17,7 @@
 | 
				
			||||||
#include "goap/actor_world_state.hpp"
 | 
					#include "goap/actor_world_state.hpp"
 | 
				
			||||||
#include "goap/goal.hpp"
 | 
					#include "goap/goal.hpp"
 | 
				
			||||||
#include "goap/planner.hpp"
 | 
					#include "goap/planner.hpp"
 | 
				
			||||||
 | 
					#include "goap/state.hpp"
 | 
				
			||||||
#include "utils/register_types.hpp"
 | 
					#include "utils/register_types.hpp"
 | 
				
			||||||
#include <gdextension_interface.h>
 | 
					#include <gdextension_interface.h>
 | 
				
			||||||
#include <godot_cpp/core/class_db.hpp>
 | 
					#include <godot_cpp/core/class_db.hpp>
 | 
				
			||||||
| 
						 | 
					@ -31,7 +34,7 @@ void initialize_gdextension_types(gd::ModuleInitializationLevel p_level)
 | 
				
			||||||
    utils::godot_cpp_utils_register_types();
 | 
					    utils::godot_cpp_utils_register_types();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // always register actions before classes,
 | 
					    // always register actions before classes,
 | 
				
			||||||
    // so that ActionDB::get_enum_hint is complete before _bind_methods
 | 
					    // so that ActionDB::get_enum_hint is complete before _bind_methods.
 | 
				
			||||||
    goap::ActionDB::register_action<MoveToTarget>();
 | 
					    goap::ActionDB::register_action<MoveToTarget>();
 | 
				
			||||||
    goap::ActionDB::register_action<FireAtTarget>();
 | 
					    goap::ActionDB::register_action<FireAtTarget>();
 | 
				
			||||||
    goap::ActionDB::register_action<FindTarget>();
 | 
					    goap::ActionDB::register_action<FindTarget>();
 | 
				
			||||||
| 
						 | 
					@ -40,24 +43,31 @@ void initialize_gdextension_types(gd::ModuleInitializationLevel p_level)
 | 
				
			||||||
    goap::ActionDB::register_action<TankSelfHeal>();
 | 
					    goap::ActionDB::register_action<TankSelfHeal>();
 | 
				
			||||||
    goap::ActionDB::register_action<TakeCover>();
 | 
					    goap::ActionDB::register_action<TakeCover>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    gd::ClassDB::register_class<goap::ActorWorldState>();
 | 
					    // same for items,
 | 
				
			||||||
    gd::ClassDB::register_class<goap::Goal>();
 | 
					    // make sure ItemDB::get_enum_hint is fully populated out before _bind_methods.
 | 
				
			||||||
    gd::ClassDB::register_class<goap::Planner>();
 | 
					    ItemDB::register_item<Medkit>();
 | 
				
			||||||
    gd::ClassDB::register_class<goap::State>();
 | 
					    ItemDB::register_item<Handgun>();
 | 
				
			||||||
 | 
					    ItemDB::register_item<Lasercutter>();
 | 
				
			||||||
 | 
					    ItemDB::register_item<Welder>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    gd::ClassDB::register_class<MoveTo>();
 | 
					    GDREGISTER_CLASS(goap::ActorWorldState);
 | 
				
			||||||
    gd::ClassDB::register_class<Animate>();
 | 
					    GDREGISTER_CLASS(goap::Goal);
 | 
				
			||||||
    gd::ClassDB::register_class<Activate>();
 | 
					    GDREGISTER_CLASS(goap::Planner);
 | 
				
			||||||
 | 
					    GDREGISTER_CLASS(goap::State);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    gd::ClassDB::register_class<UnitWorldState>();
 | 
					    GDREGISTER_CLASS(MoveTo);
 | 
				
			||||||
    gd::ClassDB::register_class<EnemyWorldState>();
 | 
					    GDREGISTER_CLASS(Animate);
 | 
				
			||||||
    gd::ClassDB::register_class<GoalMarker>();
 | 
					    GDREGISTER_CLASS(Activate);
 | 
				
			||||||
    gd::ClassDB::register_class<Unit>();
 | 
					    GDREGISTER_CLASS(UnitWorldState);
 | 
				
			||||||
    gd::ClassDB::register_class<RTSGameMode>();
 | 
					    GDREGISTER_CLASS(EnemyWorldState);
 | 
				
			||||||
    gd::ClassDB::register_class<RTSPlayer>();
 | 
					    GDREGISTER_CLASS(GoalMarker);
 | 
				
			||||||
    gd::ClassDB::register_class<EntityHealth>();
 | 
					    GDREGISTER_CLASS(Unit);
 | 
				
			||||||
    gd::ClassDB::register_class<NavMarker>();
 | 
					    GDREGISTER_CLASS(RTSGameMode);
 | 
				
			||||||
    gd::ClassDB::register_class<NavRoom>();
 | 
					    GDREGISTER_CLASS(RTSPlayer);
 | 
				
			||||||
 | 
					    GDREGISTER_CLASS(EntityHealth);
 | 
				
			||||||
 | 
					    GDREGISTER_CLASS(NavMarker);
 | 
				
			||||||
 | 
					    GDREGISTER_CLASS(NavRoom);
 | 
				
			||||||
 | 
					    GDREGISTER_CLASS(Inventory);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern "C"
 | 
					extern "C"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/rts_items.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/rts_items.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,46 @@
 | 
				
			||||||
 | 
					#include "rts_items.hpp"
 | 
				
			||||||
 | 
					#include "unit.hpp"
 | 
				
			||||||
 | 
					#include "entity_health.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Medkit::Medkit()
 | 
				
			||||||
 | 
					: Item() {
 | 
				
			||||||
 | 
					    this->capabilities.insert(ItemCapability::Consume);
 | 
				
			||||||
 | 
					    this->capabilities.insert(ItemCapability::Heal);
 | 
				
			||||||
 | 
					    this->capabilities.insert(ItemCapability::Revive);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Medkit::can_use_on(Unit *used_by, gd::Object *used_on) const {
 | 
				
			||||||
 | 
					    return gd::Object::cast_to<Unit>(used_on) != nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Medkit::use_on(Unit *used_by, gd::Object *used_on) const {
 | 
				
			||||||
 | 
					    gd::Object::cast_to<Unit>(used_on)->get_entity_health()->healed_by(3, used_by);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Handgun::Handgun()
 | 
				
			||||||
 | 
					: Item() {
 | 
				
			||||||
 | 
					    this->capabilities.insert(ItemCapability::WeaponEquip);
 | 
				
			||||||
 | 
					    this->capabilities.insert(ItemCapability::WeaponRanged);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Handgun::can_use_on(Unit *used_by, gd::Object *used_on) const {
 | 
				
			||||||
 | 
					    return gd::Object::cast_to<Unit>(used_on) != nullptr
 | 
				
			||||||
 | 
					        && used_by->get_world_state()->get_can_see_target();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Handgun::use_on(Unit* used_by, gd::Object* used_on) const {
 | 
				
			||||||
 | 
					    Unit *target{gd::Object::cast_to<Unit>(used_on)};
 | 
				
			||||||
 | 
					    used_by->aim_at(target);
 | 
				
			||||||
 | 
					    EntityHealth *health{target->get_entity_health()};
 | 
				
			||||||
 | 
					    health->damaged_by(4, used_by);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Lasercutter::Lasercutter()
 | 
				
			||||||
 | 
					: Item() {
 | 
				
			||||||
 | 
					    this->capabilities.insert(ItemCapability::UtilityEquip);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Welder::Welder()
 | 
				
			||||||
 | 
					: Item() {
 | 
				
			||||||
 | 
					    this->capabilities.insert(ItemCapability::UtilityEquip);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/rts_items.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/rts_items.hpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,46 @@
 | 
				
			||||||
 | 
					#ifndef RTS_ITEMS_HPP
 | 
				
			||||||
 | 
					#define RTS_ITEMS_HPP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "item.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Medkit : public Item {
 | 
				
			||||||
 | 
					    ITEM_CLASS(Medkit, "Medkit",
 | 
				
			||||||
 | 
					              "Standard emergency home medical kit. Use to manage wounds and stabilize a person.");
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    Medkit();
 | 
				
			||||||
 | 
					    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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Handgun : public Item {
 | 
				
			||||||
 | 
					    ITEM_CLASS(Handgun, "9mm handgun.",
 | 
				
			||||||
 | 
					              "A standard issue police firearm.");
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    Handgun();
 | 
				
			||||||
 | 
					    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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Lasercutter : public Item {
 | 
				
			||||||
 | 
					    ITEM_CLASS(Lasercutter, "Lasercutter",
 | 
				
			||||||
 | 
					              "A laser cutter, use to clear metal obstacles.");
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    Lasercutter();
 | 
				
			||||||
 | 
					    // 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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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:
 | 
				
			||||||
 | 
					    Welder();
 | 
				
			||||||
 | 
					    // 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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // !RTS_ITEMS_HPP
 | 
				
			||||||
							
								
								
									
										15
									
								
								src/stats.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/stats.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					#include "stats.hpp"
 | 
				
			||||||
 | 
					#include <godot_cpp/core/math.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								src/stats.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/stats.hpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					#ifndef STATS_HPP
 | 
				
			||||||
 | 
					#define STATS_HPP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "utils/godot_macros.hpp"
 | 
				
			||||||
 | 
					#include <godot_cpp/templates/hash_set.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace gd = godot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Stats {
 | 
				
			||||||
 | 
					    float damage_absorb{0.f};
 | 
				
			||||||
 | 
					    int hazmat_level{0};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Stats operator &(Stats const &lhs, Stats const &rhs);
 | 
				
			||||||
 | 
					Stats &operator <<(Stats &lhs, Stats const &rhs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // !STATS_HPP
 | 
				
			||||||
							
								
								
									
										19
									
								
								src/unit.cpp
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								src/unit.cpp
									
									
									
									
									
								
							| 
						 | 
					@ -29,6 +29,7 @@ void Unit::_enter_tree() { GDGAMEONLY();
 | 
				
			||||||
    this->anim_player = this->get_node<gd::AnimationPlayer>("%AnimationPlayer");
 | 
					    this->anim_player = this->get_node<gd::AnimationPlayer>("%AnimationPlayer");
 | 
				
			||||||
    this->health = this->get_node<EntityHealth>("%EntityHealth");
 | 
					    this->health = this->get_node<EntityHealth>("%EntityHealth");
 | 
				
			||||||
    this->health->connect("death", callable_mp(this, &Unit::on_death));
 | 
					    this->health->connect("death", callable_mp(this, &Unit::on_death));
 | 
				
			||||||
 | 
					    this->inventory = this->get_node<Inventory>("%Inventory");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Unit::_physics_process(double) { GDGAMEONLY();
 | 
					void Unit::_physics_process(double) { GDGAMEONLY();
 | 
				
			||||||
| 
						 | 
					@ -78,15 +79,15 @@ void Unit::begin_goal(gd::Ref<goap::Goal> goal) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Unit::use_weapon() {
 | 
					void Unit::use_weapon() {
 | 
				
			||||||
    gd::Node3D *target{this->world_state->get_target_node()};
 | 
					    gd::Node3D *target{this->world_state->get_target_node()};
 | 
				
			||||||
    if(target == nullptr)
 | 
					    Item const *weapon{this->inventory->get_weapon()};
 | 
				
			||||||
        return;
 | 
					    if(weapon == nullptr) {
 | 
				
			||||||
    if(!this->world_state->get_can_see_target())
 | 
					        // try and do a melee attack instead when no weapon is equipped
 | 
				
			||||||
        return;
 | 
					        Unit *target_unit{gd::Object::cast_to<Unit>(target)};
 | 
				
			||||||
    this->aim_at(target);
 | 
					        if(target_unit != nullptr && this->world_state->get_can_see_target() && this->world_state->get_is_in_melee_range())
 | 
				
			||||||
    EntityHealth *health{target->get_node<EntityHealth>("EntityHealth")};
 | 
					            target_unit->get_entity_health()->damaged_by(1, this);
 | 
				
			||||||
    if(health == nullptr)
 | 
					    } else {
 | 
				
			||||||
        return;
 | 
					        this->inventory->get_weapon()->try_use_on(this, target);
 | 
				
			||||||
    health->damaged_by(1, this);
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Unit::aim_at(gd::Node3D *target) {
 | 
					void Unit::aim_at(gd::Node3D *target) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@
 | 
				
			||||||
#define RTS_UNIT_HPP
 | 
					#define RTS_UNIT_HPP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "goal_marker.hpp"
 | 
					#include "goal_marker.hpp"
 | 
				
			||||||
 | 
					#include "inventory.hpp"
 | 
				
			||||||
#include "unit_world_state.hpp"
 | 
					#include "unit_world_state.hpp"
 | 
				
			||||||
#include "goap/goal.hpp"
 | 
					#include "goap/goal.hpp"
 | 
				
			||||||
#include "goap/planner.hpp"
 | 
					#include "goap/planner.hpp"
 | 
				
			||||||
| 
						 | 
					@ -69,6 +70,7 @@ protected:
 | 
				
			||||||
    goap::Planner *planner{nullptr};
 | 
					    goap::Planner *planner{nullptr};
 | 
				
			||||||
    EntityHealth *health{nullptr};
 | 
					    EntityHealth *health{nullptr};
 | 
				
			||||||
    UnitWorldState *world_state{nullptr};
 | 
					    UnitWorldState *world_state{nullptr};
 | 
				
			||||||
 | 
					    Inventory *inventory{nullptr};
 | 
				
			||||||
#ifdef DEBUG_ENABLED
 | 
					#ifdef DEBUG_ENABLED
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    gd::String DEBUG_print_debug_info();
 | 
					    gd::String DEBUG_print_debug_info();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue