feat: replaced separate melee and ranged actions with use weapon and get in range
This commit is contained in:
parent
c9c41ac2d7
commit
2a0c9a623e
|
@ -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="."]
|
||||
|
|
|
@ -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="."]
|
||||
|
|
|
@ -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>("%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;
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<Item const *, unsigned> inventory{};
|
||||
|
||||
Unit *parent_unit{nullptr};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<uint32_t> 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_; }\
|
||||
|
|
|
@ -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<MoveToTarget>();
|
||||
goap::ActionDB::register_action<FireAtTarget>();
|
||||
goap::ActionDB::register_action<UseWeapon>();
|
||||
goap::ActionDB::register_action<FindTarget>();
|
||||
goap::ActionDB::register_action<GetInMeleeRange>();
|
||||
goap::ActionDB::register_action<MeleeAttack>();
|
||||
goap::ActionDB::register_action<GetInRange>();
|
||||
goap::ActionDB::register_action<TankSelfHeal>();
|
||||
goap::ActionDB::register_action<TakeCover>();
|
||||
|
||||
|
|
|
@ -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<Animate>()};
|
||||
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<gd::Node3D>(context->get_world_property("target_node"))};
|
||||
MoveTo *state{this->create_state<MoveTo>()};
|
||||
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<Animate>()};
|
||||
state->animation = "melee_attack";
|
||||
return state;
|
||||
}
|
||||
|
||||
TankSelfHeal::TankSelfHeal()
|
||||
: Action() {
|
||||
this->required.insert("can_see_target", false);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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<Unit>(used_on)};
|
||||
used_by->aim_at(target);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -2,14 +2,3 @@
|
|||
#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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -76,7 +76,7 @@ void Unit::begin_goal(gd::Ref<goap::Goal> goal) {
|
|||
|
||||
void Unit::use_weapon() {
|
||||
Unit *target_unit{gd::Object::cast_to<Unit>(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);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ public:
|
|||
void begin_marker_temporary(GoalMarker *marker);
|
||||
void begin_goal(gd::Ref<goap::Goal> goal);
|
||||
void set_target_goal(gd::Node3D *target, gd::Ref<goap::Goal> goal);
|
||||
|
||||
virtual void use_weapon();
|
||||
void aim_at(gd::Node3D *node);
|
||||
void on_unconscious(Unit *damage_source);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue