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
|
||||
|
||||
[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
|
||||
|
||||
[node name="Planner" type="Planner" parent="."]
|
||||
|
|
|
@ -33,6 +33,10 @@ unique_name_in_owner = true
|
|||
[node name="EntityHealth" type="EntityHealth" parent="."]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="Inventory" type="Inventory" parent="."]
|
||||
weapon = 2
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
path_desired_distance = 0.2
|
||||
|
|
|
@ -8,10 +8,7 @@
|
|||
|
||||
void EnemyWorldState::_bind_methods() {
|
||||
#define CLASSNAME EnemyWorldState
|
||||
GDPROPERTY_HINTED(editor_available_goals,
|
||||
gd::Variant::ARRAY,
|
||||
gd::PROPERTY_HINT_ARRAY_TYPE,
|
||||
GDRESOURCETYPE("Goal"));
|
||||
GDPROPERTY_HINTED(available_goals_inspector, gd::Variant::ARRAY, gd::PROPERTY_HINT_ARRAY_TYPE, GDRESOURCETYPE("Goal"));
|
||||
}
|
||||
|
||||
void EnemyWorldState::_ready() { GDGAMEONLY();
|
||||
|
@ -144,13 +141,13 @@ void EnemyWorldState::try_set_target(Unit *unit) {
|
|||
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();
|
||||
for(int i{0}; i < array.size(); ++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{};
|
||||
for(gd::Ref<goap::Goal> const &goal : this->available_goals)
|
||||
a.push_back(goal);
|
||||
|
|
|
@ -36,8 +36,8 @@ private:
|
|||
float calculate_priority(Unit *target);
|
||||
gd::Ref<goap::Goal> get_goal_for_target(Unit *unit);
|
||||
void try_set_target(Unit *target);
|
||||
void set_editor_available_goals(gd::Array array);
|
||||
gd::Array get_editor_available_goals() const;
|
||||
void set_available_goals_inspector(gd::Array array);
|
||||
gd::Array get_available_goals_inspector() const;
|
||||
private:
|
||||
gd::Callable aware_unit_death{callable_mp(this, &EnemyWorldState::on_aware_unit_death)};
|
||||
gd::Vector<gd::Ref<goap::Goal>> available_goals{};
|
||||
|
|
|
@ -73,21 +73,33 @@ float EntityHealth::get_wounds_damage_factor() 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 {
|
||||
return this->wounds_current <= 0 && this->injury_current > 0;
|
||||
return this->wounds_current <= 0
|
||||
&& this->injury_current > 0;
|
||||
}
|
||||
|
||||
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 {
|
||||
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) {
|
||||
this->injury_max = max_health;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef RTS_ENTITY_HEALTH_HPP
|
||||
#define RTS_ENTITY_HEALTH_HPP
|
||||
|
||||
#include "stats.hpp"
|
||||
#include <godot_cpp/classes/node.hpp>
|
||||
|
||||
namespace gd = godot;
|
||||
|
@ -25,6 +26,8 @@ public:
|
|||
bool can_be_healed() const;
|
||||
bool can_be_damaged() const;
|
||||
bool is_dead() const;
|
||||
void set_stats(Stats const *stats);
|
||||
Stats const *get_stats() const;
|
||||
void set_injury_max(int max_health);
|
||||
int get_injury_max() const;
|
||||
int get_injury_current() const;
|
||||
|
@ -32,6 +35,7 @@ public:
|
|||
int get_wounds_max() const;
|
||||
int get_wounds_current() const;
|
||||
private:
|
||||
Stats const *stats;
|
||||
// long term health
|
||||
int injury_max{10};
|
||||
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() {
|
||||
#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) {
|
||||
|
@ -44,14 +44,14 @@ gd::Vector<NavMarker*> const &NavRoom::get_markers() const {
|
|||
return this->markers;
|
||||
}
|
||||
|
||||
gd::Array NavRoom::get_editor_neighbours() const {
|
||||
gd::Array NavRoom::get_neighbours_inspector() const {
|
||||
gd::Array a{};
|
||||
for(NavRoom *room : this->neighbours)
|
||||
a.push_back(room);
|
||||
return a;
|
||||
}
|
||||
|
||||
void NavRoom::set_editor_neighbours(gd::Array array) {
|
||||
void NavRoom::set_neighbours_inspector(gd::Array array) {
|
||||
this->neighbours.clear();
|
||||
while(!array.is_empty()) if(NavRoom *room{gd::Object::cast_to<NavRoom>(array.pop_front())})
|
||||
this->neighbours.push_back(room);
|
||||
|
|
|
@ -19,8 +19,8 @@ public:
|
|||
gd::Vector<NavRoom*> const &get_neighbours() const;
|
||||
gd::Vector<NavMarker*> const &get_markers() const;
|
||||
private:
|
||||
void set_editor_neighbours(gd::Array array);
|
||||
gd::Array get_editor_neighbours() const;
|
||||
void set_neighbours_inspector(gd::Array array);
|
||||
gd::Array get_neighbours_inspector() const;
|
||||
private:
|
||||
float radius{1.f};
|
||||
gd::Vector3 centre{0.f, 0.f, 0.f};
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#include "register_types.h"
|
||||
#include "enemy_world_state.hpp"
|
||||
#include "entity_health.hpp"
|
||||
#include "goap/state.hpp"
|
||||
#include "inventory.hpp"
|
||||
#include "item_db.hpp"
|
||||
#include "nav_marker.hpp"
|
||||
#include "nav_room.hpp"
|
||||
#include "rts_actions.hpp"
|
||||
#include "rts_game_mode.hpp"
|
||||
#include "rts_items.hpp"
|
||||
#include "rts_player.hpp"
|
||||
#include "rts_states.hpp"
|
||||
#include "unit.hpp"
|
||||
|
@ -15,6 +17,7 @@
|
|||
#include "goap/actor_world_state.hpp"
|
||||
#include "goap/goal.hpp"
|
||||
#include "goap/planner.hpp"
|
||||
#include "goap/state.hpp"
|
||||
#include "utils/register_types.hpp"
|
||||
#include <gdextension_interface.h>
|
||||
#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();
|
||||
|
||||
// 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<FireAtTarget>();
|
||||
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<TakeCover>();
|
||||
|
||||
gd::ClassDB::register_class<goap::ActorWorldState>();
|
||||
gd::ClassDB::register_class<goap::Goal>();
|
||||
gd::ClassDB::register_class<goap::Planner>();
|
||||
gd::ClassDB::register_class<goap::State>();
|
||||
// same for items,
|
||||
// make sure ItemDB::get_enum_hint is fully populated out before _bind_methods.
|
||||
ItemDB::register_item<Medkit>();
|
||||
ItemDB::register_item<Handgun>();
|
||||
ItemDB::register_item<Lasercutter>();
|
||||
ItemDB::register_item<Welder>();
|
||||
|
||||
gd::ClassDB::register_class<MoveTo>();
|
||||
gd::ClassDB::register_class<Animate>();
|
||||
gd::ClassDB::register_class<Activate>();
|
||||
GDREGISTER_CLASS(goap::ActorWorldState);
|
||||
GDREGISTER_CLASS(goap::Goal);
|
||||
GDREGISTER_CLASS(goap::Planner);
|
||||
GDREGISTER_CLASS(goap::State);
|
||||
|
||||
gd::ClassDB::register_class<UnitWorldState>();
|
||||
gd::ClassDB::register_class<EnemyWorldState>();
|
||||
gd::ClassDB::register_class<GoalMarker>();
|
||||
gd::ClassDB::register_class<Unit>();
|
||||
gd::ClassDB::register_class<RTSGameMode>();
|
||||
gd::ClassDB::register_class<RTSPlayer>();
|
||||
gd::ClassDB::register_class<EntityHealth>();
|
||||
gd::ClassDB::register_class<NavMarker>();
|
||||
gd::ClassDB::register_class<NavRoom>();
|
||||
GDREGISTER_CLASS(MoveTo);
|
||||
GDREGISTER_CLASS(Animate);
|
||||
GDREGISTER_CLASS(Activate);
|
||||
GDREGISTER_CLASS(UnitWorldState);
|
||||
GDREGISTER_CLASS(EnemyWorldState);
|
||||
GDREGISTER_CLASS(GoalMarker);
|
||||
GDREGISTER_CLASS(Unit);
|
||||
GDREGISTER_CLASS(RTSGameMode);
|
||||
GDREGISTER_CLASS(RTSPlayer);
|
||||
GDREGISTER_CLASS(EntityHealth);
|
||||
GDREGISTER_CLASS(NavMarker);
|
||||
GDREGISTER_CLASS(NavRoom);
|
||||
GDREGISTER_CLASS(Inventory);
|
||||
}
|
||||
|
||||
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->health = this->get_node<EntityHealth>("%EntityHealth");
|
||||
this->health->connect("death", callable_mp(this, &Unit::on_death));
|
||||
this->inventory = this->get_node<Inventory>("%Inventory");
|
||||
}
|
||||
|
||||
void Unit::_physics_process(double) { GDGAMEONLY();
|
||||
|
@ -78,15 +79,15 @@ void Unit::begin_goal(gd::Ref<goap::Goal> goal) {
|
|||
|
||||
void Unit::use_weapon() {
|
||||
gd::Node3D *target{this->world_state->get_target_node()};
|
||||
if(target == nullptr)
|
||||
return;
|
||||
if(!this->world_state->get_can_see_target())
|
||||
return;
|
||||
this->aim_at(target);
|
||||
EntityHealth *health{target->get_node<EntityHealth>("EntityHealth")};
|
||||
if(health == nullptr)
|
||||
return;
|
||||
health->damaged_by(1, this);
|
||||
Item const *weapon{this->inventory->get_weapon()};
|
||||
if(weapon == nullptr) {
|
||||
// try and do a melee attack instead when no weapon is equipped
|
||||
Unit *target_unit{gd::Object::cast_to<Unit>(target)};
|
||||
if(target_unit != nullptr && this->world_state->get_can_see_target() && this->world_state->get_is_in_melee_range())
|
||||
target_unit->get_entity_health()->damaged_by(1, this);
|
||||
} else {
|
||||
this->inventory->get_weapon()->try_use_on(this, target);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::aim_at(gd::Node3D *target) {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define RTS_UNIT_HPP
|
||||
|
||||
#include "goal_marker.hpp"
|
||||
#include "inventory.hpp"
|
||||
#include "unit_world_state.hpp"
|
||||
#include "goap/goal.hpp"
|
||||
#include "goap/planner.hpp"
|
||||
|
@ -69,6 +70,7 @@ protected:
|
|||
goap::Planner *planner{nullptr};
|
||||
EntityHealth *health{nullptr};
|
||||
UnitWorldState *world_state{nullptr};
|
||||
Inventory *inventory{nullptr};
|
||||
#ifdef DEBUG_ENABLED
|
||||
public:
|
||||
gd::String DEBUG_print_debug_info();
|
||||
|
|
Loading…
Reference in a new issue