diff --git a/libopenage/gamestate/resource.cpp b/libopenage/gamestate/resource.cpp index aead52d926..c0c7d5b9e7 100644 --- a/libopenage/gamestate/resource.cpp +++ b/libopenage/gamestate/resource.cpp @@ -62,7 +62,7 @@ bool ResourceBundle::has(const ResourceBundle& amount) const { } bool ResourceBundle::deduct(const ResourceBundle& amount) { - if (*this >= amount) { + if (this->has(amount)) { *this -= amount; return true; } @@ -70,3 +70,22 @@ bool ResourceBundle::deduct(const ResourceBundle& amount) { } } // openage + +namespace std { + +string to_string(const openage::game_resource &res) { + switch (res) { + case openage::game_resource::wood: + return "wood"; + case openage::game_resource::food: + return "food"; + case openage::game_resource::gold: + return "gold"; + case openage::game_resource::stone: + return "stone"; + default: + return "unknown"; + } +} + +} // namespace std diff --git a/libopenage/gamestate/resource.h b/libopenage/gamestate/resource.h index b3f78ebbfa..699239d815 100644 --- a/libopenage/gamestate/resource.h +++ b/libopenage/gamestate/resource.h @@ -41,6 +41,10 @@ class ResourceBundle { bool has(const ResourceBundle& amount) const; + /** + * If amount can't be deducted return false, else deduct the given amount + * and return true. + */ bool deduct(const ResourceBundle& amount); double& operator[] (const game_resource res) { return value[(int) res]; } @@ -49,7 +53,7 @@ class ResourceBundle { // Getters double get(const game_resource res) const { return value[(int) res]; } - double get(int index) const { return value[index]; } + double get(const int index) const { return value[index]; } private: double value[(int) game_resource::RESOURCE_TYPE_COUNT]; @@ -59,6 +63,8 @@ class ResourceBundle { namespace std { +std::string to_string(const openage::game_resource &res); + /** * hasher for game resource */ diff --git a/libopenage/unit/CMakeLists.txt b/libopenage/unit/CMakeLists.txt index 97734018ed..fc901e8c87 100644 --- a/libopenage/unit/CMakeLists.txt +++ b/libopenage/unit/CMakeLists.txt @@ -1,6 +1,8 @@ add_sources(libopenage ability.cpp action.cpp + attribute.cpp + attributes.cpp command.cpp producer.cpp selection.cpp diff --git a/libopenage/unit/ability.cpp b/libopenage/unit/ability.cpp index 44baf51bca..c449a531aa 100644 --- a/libopenage/unit/ability.cpp +++ b/libopenage/unit/ability.cpp @@ -12,17 +12,17 @@ namespace openage { bool UnitAbility::has_hitpoints(Unit &target) { - return target.has_attribute(attr_type::hitpoints) && - target.get_attribute().current > 0; + return target.has_attribute(attr_type::damaged) && + target.get_attribute().hp > 0; } bool UnitAbility::is_damaged(Unit &target) { - return target.has_attribute(attr_type::hitpoints) && - target.get_attribute().current < target.get_attribute().max; + return target.has_attribute(attr_type::damaged) && target.has_attribute(attr_type::hitpoints) && + target.get_attribute().hp < target.get_attribute().hp; } bool UnitAbility::has_resource(Unit &target) { - return target.has_attribute(attr_type::resource) && + return target.has_attribute(attr_type::resource) && !target.has_attribute(attr_type::worker) && target.get_attribute().amount > 0; } @@ -224,7 +224,7 @@ bool GatherAbility::can_invoke(Unit &to_modify, const Command &cmd) { Unit &target = *cmd.unit(); return &to_modify != &target && to_modify.location && - to_modify.has_attribute(attr_type::gatherer) && + to_modify.has_attribute(attr_type::worker) && has_resource(target); } return false; diff --git a/libopenage/unit/action.cpp b/libopenage/unit/action.cpp index 8cced43fa3..b372b4075a 100644 --- a/libopenage/unit/action.cpp +++ b/libopenage/unit/action.cpp @@ -98,20 +98,20 @@ void UnitAction::face_towards(const coord::phys3 pos) { // TODO remove (keep for testing) void UnitAction::damage_object(Unit &target, unsigned dmg) { - if (target.has_attribute(attr_type::hitpoints)) { - auto &hp = target.get_attribute(); - if (hp.current > dmg) { - hp.current -= dmg; + if (target.has_attribute(attr_type::damaged)) { + auto &dm = target.get_attribute(); + if (dm.hp > dmg) { + dm.hp -= dmg; } else { - hp.current = 0; + dm.hp = 0; } } } void UnitAction::damage_object(Unit &target) { - if (target.has_attribute(attr_type::hitpoints)) { - auto &hp = target.get_attribute(); + if (target.has_attribute(attr_type::damaged)) { + auto &dm = target.get_attribute(); if (target.has_attribute(attr_type::armor) && this->entity->has_attribute(attr_type::attack)) { auto &armor = target.get_attribute().armor; @@ -131,11 +131,11 @@ void UnitAction::damage_object(Unit &target) { actual_damage = 1; } - if (hp.current > actual_damage) { - hp.current -= actual_damage; + if (dm.hp > actual_damage) { + dm.hp -= actual_damage; } else { - hp.current = 0; + dm.hp = 0; } } else { @@ -229,8 +229,9 @@ void TargetAction::on_completion() { new_target = find_near(*this->entity->location, [this](const TerrainObject &obj) { return obj.unit.unit_type->id() == this->target_type_id && + !obj.unit.has_attribute(attr_type::worker) && obj.unit.has_attribute(attr_type::resource) && - obj.unit.get_attribute().amount > 0.0f; + obj.unit.get_attribute().amount > 0.0; }); } @@ -317,9 +318,9 @@ DeadAction::DeadAction(Unit *e, std::function on_complete) } void DeadAction::update(unsigned int time) { - if (this->entity->has_attribute(attr_type::hitpoints)) { - auto &h_attr = this->entity->get_attribute(); - h_attr.current = 0; + if (this->entity->has_attribute(attr_type::damaged)) { + auto &dm = this->entity->get_attribute(); + dm.hp = 0; } // inc frame but do not pass the end frame @@ -338,8 +339,8 @@ void DeadAction::on_completion() { bool DeadAction::completed() const { - // check resource, trees/huntables with resource are not removed - if (this->entity->has_attribute(attr_type::resource)) { + // check resource, trees/huntables with resource are not removed but not workers + if (this->entity->has_attribute(attr_type::resource) && !this->entity->has_attribute(attr_type::worker)) { auto &res_attr = this->entity->get_attribute(); return res_attr.amount <= 0; // cannot complete when resource remains } @@ -396,7 +397,8 @@ void IdleAction::update(unsigned int time) { if (this->entity->location && this->entity->has_attribute(attr_type::owner) && this->entity->has_attribute(attr_type::attack) && - this->entity->get_attribute().stance != attack_stance::do_nothing) { + this->entity->has_attribute(attr_type::formation) && + this->entity->get_attribute().stance != attack_stance::do_nothing) { // restart search from new tile when moved auto terrain = this->entity->location->get_terrain(); @@ -425,9 +427,9 @@ void IdleAction::update(unsigned int time) { // unit carrying ressources take the carrying sprite when idle // we're not updating frames because the carying sprite is walking - if (entity->has_attribute(attr_type::gatherer)) { - auto gatherer_attrib = entity->get_attribute(); - if (gatherer_attrib.amount > 0) { + if (entity->has_attribute(attr_type::worker)) { + auto &worker_resource = this->entity->get_attribute(); + if (worker_resource.amount > 0) { this->graphic = graphic_type::carrying; } else { this->graphic = graphic_type::standing; @@ -443,13 +445,13 @@ void IdleAction::update(unsigned int time) { void IdleAction::on_completion() {} bool IdleAction::completed() const { - if (this->entity->has_attribute(attr_type::hitpoints)) { - auto &hp = this->entity->get_attribute(); - return hp.current == 0; + if (this->entity->has_attribute(attr_type::damaged)) { + auto &dm = this->entity->get_attribute(); + return dm.hp == 0; } else if (this->entity->has_attribute(attr_type::resource)) { auto &res_attr = this->entity->get_attribute(); - return res_attr.amount <= 0.0f; + return res_attr.amount <= 0.0; } return false; } @@ -478,9 +480,9 @@ MoveAction::MoveAction(Unit *e, UnitReference tar, coord::phys_t within_range) void MoveAction::initialise() { // switch workers to the carrying graphic - if (this->entity->has_attribute(attr_type::gatherer)) { - auto &gather_attr = this->entity->get_attribute(); - if (gather_attr.amount > 0) { + if (this->entity->has_attribute(attr_type::worker)) { + auto &worker_resource = this->entity->get_attribute(); + if (worker_resource.amount > 0) { this->graphic = graphic_type::carrying; } } @@ -753,12 +755,8 @@ BuildAction::BuildAction(Unit *e, UnitReference foundation) build_rate{.0001f} { // update the units type - auto &gatherer_attr = this->entity->get_attribute(); - auto building_class = gamedata::unit_classes::BUILDING; - if (gatherer_attr.graphics.count(building_class) > 0) { - auto *new_type = gatherer_attr.graphics.at(building_class); - auto &pl_attr = this->entity->get_attribute(); - new_type->initialise(this->entity, pl_attr.player); + if (this->entity->has_attribute(attr_type::multitype)) { + this->entity->get_attribute().switchType(gamedata::unit_classes::BUILDING, this->entity); } } @@ -810,7 +808,8 @@ void BuildAction::on_completion() { } this->entity->log(MSG(dbg) << "Done building, searching for new building"); auto valid = [this](const TerrainObject &obj) { - if (!obj.unit.has_attribute(attr_type::building) || + if (!this->entity->get_attribute().player.owns(obj.unit) || + !obj.unit.has_attribute(attr_type::building) || obj.unit.get_attribute().completed >= 1.0f) { return false; } @@ -828,25 +827,6 @@ void BuildAction::on_completion() { } } -const graphic_set &BuildAction::current_graphics() const { - if (this->entity->has_attribute(attr_type::gatherer)) { - - // the gatherer attributes attached to the unit - // are used to modify the graphic - auto &gatherer_attr = this->entity->get_attribute(); - - if (this->get_target().is_valid() && - this->get_target().get()->has_attribute(attr_type::building)) { - - // set builder graphics if available - if (gatherer_attr.graphics.count(gamedata::unit_classes::BUILDING) > 0) { - return gatherer_attr.graphics[gamedata::unit_classes::BUILDING]->graphics; - } - } - } - return this->entity->unit_type->graphics; -} - RepairAction::RepairAction(Unit *e, UnitReference tar) : TargetAction{e, graphic_type::work, tar}, @@ -873,16 +853,17 @@ RepairAction::RepairAction(Unit *e, UnitReference tar) //this->cost += get target cost; this->cost[game_resource::wood] = 100; // temp - this->cost *= 0.5 / hp.max; + this->cost *= 0.5 / hp.hp; } } void RepairAction::update_in_range(unsigned int time, Unit *target_unit) { auto &hp = target_unit->get_attribute(); + auto &dm = target_unit->get_attribute(); auto &owner = this->entity->get_attribute(); - if (hp.current >= hp.max) { + if (dm.hp >= hp.hp) { // repaired by something else this->complete = true; } @@ -890,9 +871,9 @@ void RepairAction::update_in_range(unsigned int time, Unit *target_unit) { this->time_left -= time; if (this->time_left <= 0) { - hp.current += 1; + dm.hp += 1; - if (hp.current >= hp.max) { + if (dm.hp >= hp.hp) { this->complete = true; } } @@ -921,22 +902,20 @@ GatherAction::GatherAction(Unit *e, UnitReference tar) Unit *target = this->target.get(); this->resource_class = target->unit_type->unit_class; - auto &gatherer_attr = this->entity->get_attribute(); // handle unit type changes based on resource class - if (gatherer_attr.graphics.count(this->resource_class) > 0) { - auto *new_type = gatherer_attr.graphics.at(this->resource_class); - auto &pl_attr = this->entity->get_attribute(); - new_type->initialise(this->entity, pl_attr.player); + if (this->entity->has_attribute(attr_type::multitype)) { + this->entity->get_attribute().switchType(this->resource_class, this->entity); } // set the type of gatherer + auto &worker_resource = this->entity->get_attribute(); if (target->has_attribute(attr_type::resource)) { auto &resource_attr = target->get_attribute(); - if (gatherer_attr.current_type != resource_attr.resource_type) { - this->entity->get_attribute().amount = 0; + if (worker_resource.resource_type != resource_attr.resource_type) { + worker_resource.amount = 0; } - gatherer_attr.current_type = resource_attr.resource_type; + worker_resource.resource_type = resource_attr.resource_type; } else { throw std::invalid_argument("Unit reference has no resource attribute"); } @@ -945,7 +924,8 @@ GatherAction::GatherAction(Unit *e, UnitReference tar) GatherAction::~GatherAction() {} void GatherAction::update_in_range(unsigned int time, Unit *targeted_resource) { - auto &gatherer_attr = this->entity->get_attribute(); + auto &worker = this->entity->get_attribute(); + auto &worker_resource = this->entity->get_attribute(); if (this->target_resource) { // the targets attributes @@ -956,13 +936,13 @@ void GatherAction::update_in_range(unsigned int time, Unit *targeted_resource) { // attack objects which have hitpoints (trees, hunt, sheep) if (this->entity->has_attribute(attr_type::owner) && - targeted_resource->has_attribute(attr_type::hitpoints)) { - auto &pl_attr = this->entity->get_attribute(); - auto &hp_attr = targeted_resource->get_attribute(); + targeted_resource->has_attribute(attr_type::damaged)) { + auto &pl = this->entity->get_attribute(); + auto &dm = targeted_resource->get_attribute(); // only attack if hitpoints remain - if (hp_attr.current > 0) { - Command cmd(pl_attr.player, targeted_resource); + if (dm.hp > 0) { + Command cmd(pl.player, targeted_resource); cmd.set_ability(ability_type::attack); cmd.add_flag(command_flag::attack_res); this->entity->queue_cmd(cmd); @@ -971,21 +951,21 @@ void GatherAction::update_in_range(unsigned int time, Unit *targeted_resource) { } // need to return to dropsite - if (gatherer_attr.amount > gatherer_attr.capacity) { + if (worker_resource.amount > worker.capacity) { // move to dropsite location this->target_resource = false; - this->set_target(this->nearest_dropsite(gatherer_attr.current_type)); + this->set_target(this->nearest_dropsite(worker_resource.resource_type)); } else { auto &resource_attr = targeted_resource->get_attribute(); - if (resource_attr.amount <= 0.0f) { + if (resource_attr.amount <= 0.0) { // when the resource runs out - if (gatherer_attr.amount > 0.0f) { + if (worker_resource.amount > 0.0) { this->target_resource = false; - this->set_target(this->nearest_dropsite(gatherer_attr.current_type)); + this->set_target(this->nearest_dropsite(worker_resource.resource_type)); } else { this->complete = true; @@ -994,8 +974,9 @@ void GatherAction::update_in_range(unsigned int time, Unit *targeted_resource) { else { // transfer using gather rate - gatherer_attr.amount += gatherer_attr.gather_rate * time; - resource_attr.amount -= gatherer_attr.gather_rate * time; + double amount = worker.gather_rate[worker_resource.resource_type] * time; + worker_resource.amount += amount; + resource_attr.amount -= amount; } } } @@ -1003,13 +984,13 @@ void GatherAction::update_in_range(unsigned int time, Unit *targeted_resource) { // dropsite has been reached // add value to player stockpile - Player &player = this->entity->get_attribute().player; - player.receive(gatherer_attr.current_type, gatherer_attr.amount); - gatherer_attr.amount = 0.0f; + auto &player = this->entity->get_attribute().player; + player.receive(worker_resource.resource_type, worker_resource.amount); + worker_resource.amount = 0.0; // make sure the resource stil exists if (this->target.is_valid() && - this->target.get()->get_attribute().amount > 0.0f) { + this->target.get()->get_attribute().amount > 0.0) { // return to resouce collection this->target_resource = true; @@ -1052,22 +1033,19 @@ UnitReference GatherAction::nearest_dropsite(game_resource res_type) { } } -const graphic_set &GatherAction::current_graphics() const { - return this->entity->unit_type->graphics; -} - AttackAction::AttackAction(Unit *e, UnitReference tar) : TargetAction{e, graphic_type::attack, tar, get_attack_range(e)}, strike_percent{0.0f}, rate_of_fire{0.002f} { - // switch graphic type for villagers not collecting resources - if (this->entity->has_attribute(attr_type::gatherer) && - !tar.get()->has_attribute(attr_type::resource)) { - auto &att_attr = this->entity->get_attribute(); - auto &pl_attr = this->entity->get_attribute(); - att_attr.attack_type->initialise(this->entity, pl_attr.player); + // check if attacking a non resource unit + if (this->entity->has_attribute(attr_type::worker) && + (!tar.get()->has_attribute(attr_type::resource) || tar.get()->has_attribute(attr_type::worker))) { + // switch to default villager graphics + if (this->entity->has_attribute(attr_type::multitype)) { + this->entity->get_attribute().switchType(gamedata::unit_classes::CIVILIAN, this->entity); + } } } @@ -1087,8 +1065,8 @@ void AttackAction::update_in_range(unsigned int time, Unit *target_ptr) { } bool AttackAction::completed_in_range(Unit *target_ptr) const { - auto &h_attr = target_ptr->get_attribute(); - return h_attr.current < 1; // is unit still alive? + auto &dm = target_ptr->get_attribute(); + return dm.hp < 1; // is unit still alive? } void AttackAction::attack(Unit &target) { @@ -1111,7 +1089,7 @@ void AttackAction::fire_projectile(const Attribute &att, cons current_pos.up = att.init_height; // create using the producer - auto player = this->entity->get_attribute().player; + auto &player = this->entity->get_attribute().player; auto projectile_ref = container->new_unit(*att.ptype, player, current_pos); // send towards target using a projectile ability (creates projectile motion action) @@ -1153,8 +1131,9 @@ void HealAction::update_in_range(unsigned int time, Unit *target_ptr) { } bool HealAction::completed_in_range(Unit *target_ptr) const { - auto &h_attr = target_ptr->get_attribute(); - return h_attr.current >= h_attr.max; // is unit at full hitpoints? + auto &hp = target_ptr->get_attribute(); + auto &dm = target_ptr->get_attribute(); + return dm.hp >= hp.hp; // is unit at full hitpoints? } void HealAction::heal(Unit &target) { @@ -1162,13 +1141,14 @@ void HealAction::heal(Unit &target) { // TODO move to seperate function heal_object (like damage_object)? // heal object - if (target.has_attribute(attr_type::hitpoints)) { + if (target.has_attribute(attr_type::hitpoints) && target.has_attribute(attr_type::damaged)) { auto &hp = target.get_attribute(); - if ((hp.current + heal.life) < hp.max) { - hp.current += heal.life; + auto &dm = target.get_attribute(); + if ((dm.hp + heal.life) < hp.hp) { + dm.hp += heal.life; } else { - hp.current = hp.max; + dm.hp = hp.hp; } } diff --git a/libopenage/unit/action.h b/libopenage/unit/action.h index 3debea77ac..4ca9b60377 100644 --- a/libopenage/unit/action.h +++ b/libopenage/unit/action.h @@ -1,4 +1,4 @@ -// Copyright 2014-2016 the openage authors. See copying.md for legal info. +// Copyright 2014-2017 the openage authors. See copying.md for legal info. #pragma once @@ -397,7 +397,6 @@ class BuildAction: public TargetAction { bool completed_in_range(Unit *) const override { return this->complete >= 1.0f; } void on_completion() override; std::string name() const override { return "build"; } - const graphic_set ¤t_graphics() const override; private: float complete, build_rate; @@ -441,7 +440,6 @@ class GatherAction: public TargetAction { void update_in_range(unsigned int time, Unit *target_unit) override; bool completed_in_range(Unit *) const override { return this->complete; } std::string name() const override { return "gather"; } - const graphic_set ¤t_graphics() const override; private: bool complete, target_resource; diff --git a/libopenage/unit/attribute.cpp b/libopenage/unit/attribute.cpp new file mode 100644 index 0000000000..6826bded35 --- /dev/null +++ b/libopenage/unit/attribute.cpp @@ -0,0 +1,21 @@ +// Copyright 2016-2017 the openage authors. See copying.md for legal info. + +#include "attribute.h" +#include "unit.h" +#include "unit_type.h" + +namespace openage { + +bool Attribute::accepting_resource(game_resource res) const { + return std::find(resource_types.begin(), resource_types.end(), res) != resource_types.end(); +} + +void Attribute::switchType(const gamedata::unit_classes cls, Unit *unit) const { + auto search = this->types.find(cls); + if (search != this->types.end()) { + auto &player = unit->get_attribute(); + search->second->reinitialise(unit, player.player); + } +} + +} // namespace openage diff --git a/libopenage/unit/attribute.h b/libopenage/unit/attribute.h index 97cfa42048..9a81aaac42 100644 --- a/libopenage/unit/attribute.h +++ b/libopenage/unit/attribute.h @@ -1,4 +1,4 @@ -// Copyright 2014-2016 the openage authors. See copying.md for legal info. +// Copyright 2014-2017 the openage authors. See copying.md for legal info. #pragma once @@ -31,7 +31,7 @@ template<> struct hash { namespace openage { /** - * types of action graphics + * Types of action graphics */ enum class graphic_type { construct, @@ -49,18 +49,20 @@ enum class graphic_type { class UnitTexture; /** - * collection of graphics attached to each unit + * Collection of graphics attached to each unit. */ using graphic_set = std::map>; /** - * list of attribute types + * List of unit's attribute types. */ enum class attr_type { owner, + damaged, hitpoints, armor, attack, + formation, heal, speed, direction, @@ -68,10 +70,15 @@ enum class attr_type { building, dropsite, resource, - gatherer, + worker, + multitype, garrison }; +/** + * List of unit's attack stance. + * Can be used for buildings also. + */ enum class attack_stance { aggresive, devensive, @@ -79,13 +86,25 @@ enum class attack_stance { do_nothing }; +/** + * List of unit's formation. + * Effect applys on a group of units. + */ +enum class attack_formation { + line, + staggered, + box, + flank +}; + + /** * this type gets specialized for each attribute */ template class Attribute; /** - * wraps a templated attribute + * Wraps a templated attribute */ class AttributeContainer { public: @@ -107,14 +126,11 @@ class AttributeContainer { virtual bool shared() const = 0; /** - * produces an copy of the attribute for non-shared attributes - * shared attributes will return themselves + * Produces an copy of the attribute. */ virtual std::shared_ptr copy() const = 0; }; -using attr_map_t = std::map>; - /** * An unordered_map with a int key used as a type id * and a unsigned int value used as the amount @@ -122,11 +138,40 @@ using attr_map_t = std::map>; using typeamount_map = std::unordered_map; /** - * return attribute from a container + * Wraps a templated shared attribute + * + * Shared attributes are common across all units of + * one type + */ +class SharedAttributeContainer: public AttributeContainer { +public: + + SharedAttributeContainer(attr_type t) + : + AttributeContainer{t} {} + + bool shared() const override { + return true; + } +}; + +/** + * Wraps a templated unshared attribute + * + * Shared attributes are copied for each unit of + * one type */ -template Attribute get_attr(attr_map_t &map) { - return *reinterpret_cast *>(map[T]); -} +class UnsharedAttributeContainer: public AttributeContainer { +public: + + UnsharedAttributeContainer(attr_type t) + : + AttributeContainer{t} {} + + bool shared() const override { + return false; + } +}; // ----------------------------- // attribute definitions go here @@ -134,17 +179,13 @@ template Attribute get_attr(attr_map_t &map) { class Player; -template<> class Attribute: public AttributeContainer { +template<> class Attribute: public SharedAttributeContainer { public: Attribute(Player &p) : - AttributeContainer{attr_type::owner}, + SharedAttributeContainer{attr_type::owner}, player(p) {} - bool shared() const override { - return false; - } - std::shared_ptr copy() const override { return std::make_shared>(*this); } @@ -152,38 +193,56 @@ template<> class Attribute: public AttributeContainer { Player &player; }; -template<> class Attribute: public AttributeContainer { +/** + * The max hitpoints and health bar information. + * TODO change bar information stucture + */ +template<> class Attribute: public SharedAttributeContainer { public: Attribute(unsigned int i) : - AttributeContainer{attr_type::hitpoints}, - current{i}, - max{i} {} - - bool shared() const override { - return false; - } + SharedAttributeContainer{attr_type::hitpoints}, + hp{i} {} std::shared_ptr copy() const override { return std::make_shared>(*this); } - unsigned int current; - unsigned int max; + /** + * The max hitpoints + */ + unsigned int hp; float hp_bar_height; }; -template<> class Attribute: public AttributeContainer { +/** + * The current hitpoints. + * TODO add last damage taken timestamp + */ +template<> class Attribute: public UnsharedAttributeContainer { public: - Attribute(typeamount_map a) + Attribute(unsigned int i) : - AttributeContainer{attr_type::armor}, - armor{a} {} + UnsharedAttributeContainer{attr_type::damaged}, + hp{i} {} - bool shared() const override { - return true; + std::shared_ptr copy() const override { + return std::make_shared>(*this); } + /** + * The current hitpoint + */ + unsigned int hp; +}; + +template<> class Attribute: public SharedAttributeContainer { +public: + Attribute(typeamount_map a) + : + SharedAttributeContainer{attr_type::armor}, + armor{a} {} + std::shared_ptr copy() const override { return std::make_shared>(*this); } @@ -191,100 +250,127 @@ template<> class Attribute: public AttributeContainer { typeamount_map armor; }; -template<> class Attribute: public AttributeContainer { +/** + * TODO implement min range + * TODO can a unit have multiple attacks such as villagers hunting map target classes onto attacks + */ +template<> class Attribute: public SharedAttributeContainer { public: // TODO remove (keep for testing) // 4 = gamedata::hit_class::UNITS_MELEE (not exported at the moment) - Attribute(UnitType *type, coord::phys_t r, coord::phys_t h, unsigned int d, UnitType *reset_type) + Attribute(UnitType *type, coord::phys_t r, coord::phys_t h, unsigned int d) : - Attribute{type, r, h, {{4, d}}, reset_type} {} + Attribute{type, r, h, {{4, d}}} {} - Attribute(UnitType *type, coord::phys_t r, coord::phys_t h, typeamount_map d, UnitType *reset_type) + Attribute(UnitType *type, coord::phys_t r, coord::phys_t h, typeamount_map d) : - AttributeContainer{attr_type::attack}, + SharedAttributeContainer{attr_type::attack}, ptype{type}, range{r}, init_height{h}, - damage{d}, - stance{attack_stance::do_nothing}, - attack_type{reset_type} {} - - bool shared() const override { - return false; - } + damage{d} {} std::shared_ptr copy() const override { return std::make_shared>(*this); } - // TODO: can a unit have multiple attacks such as villagers hunting - // map target classes onto attacks + /** + * The projectile's unit type + */ + UnitType *ptype; - UnitType *ptype; // projectile type + /** + * The max range of the attack + */ coord::phys_t range; + + /** + * The height from which the projectile starts + */ coord::phys_t init_height; + typeamount_map damage; - attack_stance stance; +}; - // TODO move elsewhere in order to become shared attribute - // used to change graphics back to normal for villagers - UnitType *attack_type; +/** + * The attack stance and formation + * TODO store patrol and follow command information + */ +template<> class Attribute: public UnsharedAttributeContainer { +public: + + Attribute() + : + Attribute{attack_stance::do_nothing} {} + + Attribute(attack_stance stance) + : + UnsharedAttributeContainer{attr_type::formation}, + stance{stance}, + formation{attack_formation::line} {} + + std::shared_ptr copy() const override { + return std::make_shared>(*this); + } + + attack_stance stance; + attack_formation formation; }; -template<> class Attribute: public AttributeContainer { +/** + * Healing capabilities. + */ +template<> class Attribute: public SharedAttributeContainer { public: - Attribute(coord::phys_t r, coord::phys_t h, unsigned int l, float ra) + Attribute(coord::phys_t r, unsigned int l, float ra) : - AttributeContainer{attr_type::heal}, + SharedAttributeContainer{attr_type::heal}, range{r}, - init_height{h}, life{l}, rate{ra} {} - bool shared() const override { - return true; - } - std::shared_ptr copy() const override { return std::make_shared>(*this); } + /** + * The max range of the healing. + */ coord::phys_t range; - coord::phys_t init_height; // TODO remove? + + /** + * Life healed in each cycle + */ unsigned int life; + + /** + * The rate of each heal cycle + */ float rate; }; - -template<> class Attribute: public AttributeContainer { +template<> class Attribute: public SharedAttributeContainer { public: Attribute(coord::phys_t sp) : - AttributeContainer{attr_type::speed}, + SharedAttributeContainer{attr_type::speed}, unit_speed{sp} {} - bool shared() const override { - return true; - } - std::shared_ptr copy() const override { return std::make_shared>(*this); } - coord::phys_t unit_speed; // possibly use a pointer to account for tech upgrades + // TODO rename to default or normal + coord::phys_t unit_speed; }; -template<> class Attribute: public AttributeContainer { +template<> class Attribute: public UnsharedAttributeContainer { public: Attribute(coord::phys3_delta dir) : - AttributeContainer{attr_type::direction}, + UnsharedAttributeContainer{attr_type::direction}, unit_dir(dir) {} - bool shared() const override { - return false; - } - std::shared_ptr copy() const override { return std::make_shared>(*this); } @@ -292,18 +378,14 @@ template<> class Attribute: public AttributeContainer { coord::phys3_delta unit_dir; }; -template<> class Attribute: public AttributeContainer { +template<> class Attribute: public UnsharedAttributeContainer { public: Attribute(float arc) : - AttributeContainer{attr_type::projectile}, + UnsharedAttributeContainer{attr_type::projectile}, projectile_arc{arc}, launched{false} {} - bool shared() const override { - return false; - } - std::shared_ptr copy() const override { return std::make_shared>(*this); } @@ -313,133 +395,149 @@ template<> class Attribute: public AttributeContainer { bool launched; }; -template<> class Attribute: public AttributeContainer { +/** + * TODO revisit after unit training is improved + */ +template<> class Attribute: public UnsharedAttributeContainer { public: Attribute() : - AttributeContainer{attr_type::building}, + UnsharedAttributeContainer{attr_type::building}, completed{.0f}, - is_dropsite{true}, foundation_terrain{0} {} - bool shared() const override { - return false; - } - std::shared_ptr copy() const override { return std::make_shared>(*this); } float completed; - bool is_dropsite; int foundation_terrain; // set the TerrainObject to this state // once building has been completed object_state completion_state; - // TODO: use unit class, fish and forage have different dropsites - game_resource resource_type; - // TODO: list allowed trainable producers UnitType *pp; + + /** + * The go to point after a unit is created. + */ coord::phys3 gather_point; }; -template<> class Attribute: public AttributeContainer { +/** + * The resources that are accepted to be dropped. + */ +template<> class Attribute: public SharedAttributeContainer { public: - Attribute(std::vector types) : - AttributeContainer{attr_type::dropsite}, + SharedAttributeContainer{attr_type::dropsite}, resource_types{types} {} - bool shared() const override { - return true; - } - std::shared_ptr copy() const override { return std::make_shared>(*this); } - bool accepting_resource(game_resource res) { - if (std::find(resource_types.begin(), resource_types.end(), res) != resource_types.end()) { - return true; - } else { - return false; - } - } + bool accepting_resource(game_resource res) const; -private: std::vector resource_types; }; /** - * resource capacity of an object, trees, mines, villagers etc. + * Resource capacity of a trees, mines, animal, worker etc. + * TODO add a way to define slower and faster resource gathering time needed */ -template<> class Attribute: public AttributeContainer { +template<> class Attribute: public UnsharedAttributeContainer { public: - Attribute(game_resource type, float init_amount) + Attribute() : - AttributeContainer{attr_type::resource}, + Attribute{game_resource::food, 0} {} + + Attribute(game_resource type, double init_amount) + : + UnsharedAttributeContainer{attr_type::resource}, resource_type{type}, amount{init_amount} {} - bool shared() const override { - return false; - } - std::shared_ptr copy() const override { return std::make_shared>(*this); } game_resource resource_type; - float amount; + double amount; }; -class UnitTexture; - /** - * TODO: rename to worker + * The worker's capacity and gather rates. */ -template<> class Attribute: public AttributeContainer { +template<> class Attribute: public SharedAttributeContainer { public: Attribute() : - AttributeContainer{attr_type::gatherer}, - amount{.0f} {} - - bool shared() const override { - return false; - } + SharedAttributeContainer{attr_type::worker} {} std::shared_ptr copy() const override { - return std::make_shared>(*this); + return std::make_shared>(*this); } - game_resource current_type; - float amount; - float capacity; - float gather_rate; + /** + * The max number of resources that can be carried. + */ + double capacity; - // texture sets available for each resource - std::unordered_map graphics; + /** + * The gather rate for each resource. + * The ResourceBundle class is used but instead of amounts it stores gather rates. + */ + ResourceBundle gather_rate; }; -template<> class Attribute: public AttributeContainer { +class Unit; + +/** + * Stores the collection of unit types based on a unit class. + * It is used mostly for units with multiple graphics (villagers, trebuchets). + */ +template<> class Attribute: public SharedAttributeContainer { public: Attribute() : - AttributeContainer{attr_type::garrison} {} + SharedAttributeContainer{attr_type::multitype} {} - bool shared() const override { - return false; + std::shared_ptr copy() const override { + return std::make_shared>(*this); } + /** + * Switch the type of a unit based on a given unit class + */ + void switchType(const gamedata::unit_classes cls, Unit *unit) const; + + /** + * The collection of unit class to unit type pairs + */ + std::unordered_map types; +}; + +/** + * Units put inside a building. + * TODO add capacity per type of unit + */ +template<> class Attribute: public UnsharedAttributeContainer { +public: + Attribute() + : + UnsharedAttributeContainer{attr_type::garrison} {} + std::shared_ptr copy() const override { return std::make_shared>(*this); } + /** + * The units that are garrisoned. + */ std::vector content; }; diff --git a/libopenage/unit/attributes.cpp b/libopenage/unit/attributes.cpp new file mode 100644 index 0000000000..d42069189e --- /dev/null +++ b/libopenage/unit/attributes.cpp @@ -0,0 +1,49 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "attributes.h" + +namespace openage { + +void Attributes::add(const std::shared_ptr attr) { + this->attrs[attr->type] = attr; +} + +void Attributes::add_copies(const Attributes &other) { + this->add_copies(other, true, true); +} + +void Attributes::add_copies(const Attributes &other, bool shared, bool unshared) { + for (auto &i : other.attrs) { + auto &attr = *i.second.get(); + + if (attr.shared()) { + if (shared) { + // pass self + this->add(i.second); + } + } + else if (unshared) { + // create copy + this->add(attr.copy()); + } + } +} + +bool Attributes::remove(const attr_type type) { + return this->attrs.erase(type) > 0; +} + +bool Attributes::has(const attr_type type) const { + return this->attrs.find(type) != this->attrs.end(); +} + +std::shared_ptr Attributes::get(const attr_type type) const { + return this->attrs.at(type); +} + +template +Attribute &Attributes::get() const { + return *reinterpret_cast *>(this->attrs.at(T).get()); +} + +} // namespace openage diff --git a/libopenage/unit/attributes.h b/libopenage/unit/attributes.h new file mode 100644 index 0000000000..03f40d3227 --- /dev/null +++ b/libopenage/unit/attributes.h @@ -0,0 +1,62 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "attribute.h" + +namespace openage { + +/** + * Contains a group of attributes. + * Can contain only one attribute of each type. + */ +class Attributes { +public: + Attributes() {} + + /** + * Add an attribute or replace any attribute of the same type. + */ + void add(const std::shared_ptr attr); + + /** + * Add copies of all the attributes from the given Attributes. + */ + void add_copies(const Attributes &attrs); + + /** + * Add copies of all the attributes from the given Attributes. + * If shared is false, shared attributes are ignored. + * If unshared is false, unshared attributes are ignored. + */ + void add_copies(const Attributes &attrs, bool shared, bool unshared); + + /** + * Remove an attribute based on the type. + */ + bool remove(const attr_type type); + + /** + * Check if the attribute of the given type exists. + */ + bool has(const attr_type type) const; + + /** + * Get the attribute based on the type. + */ + std::shared_ptr get(const attr_type type) const; + + /** + * Get the attribute + */ + template + Attribute &get() const; + +private: + + std::map> attrs; +}; + +} // namespace openage diff --git a/libopenage/unit/producer.cpp b/libopenage/unit/producer.cpp index eda671e7fa..bcc8ba56f9 100644 --- a/libopenage/unit/producer.cpp +++ b/libopenage/unit/producer.cpp @@ -191,6 +191,7 @@ void ObjectProducer::initialise(Unit *unit, Player &player) { // hitpoints if available if (this->unit_data.hit_points > 0) { unit->add_attribute(std::make_shared>(this->unit_data.hit_points)); + unit->add_attribute(std::make_shared>(this->unit_data.hit_points)); } // collectable resources @@ -379,16 +380,16 @@ void MovableProducer::initialise(Unit *unit, Player &player) { // projectile of melee attacks UnitType *proj_type = this->owner.get_type(this->projectile); - UnitType *reset_type = this->parent_type(); if (this->unit_data.projectile_unit_id > 0 && proj_type) { // calculate requirements for ranged attacks coord::phys_t range_phys = coord::settings::phys_per_tile * this->unit_data.max_range; - unit->add_attribute(std::make_shared>(proj_type, range_phys, 48000, 1, reset_type)); + unit->add_attribute(std::make_shared>(proj_type, range_phys, 48000, 1)); } else { - unit->add_attribute(std::make_shared>(nullptr, 0, 0, 1, reset_type)); + unit->add_attribute(std::make_shared>(nullptr, 0, 0, 1)); } + unit->add_attribute(std::make_shared>()); } TerrainObject *MovableProducer::place(Unit *unit, std::shared_ptr terrain, coord::phys3 init_pos) const { @@ -415,50 +416,56 @@ void LivingProducer::initialise(Unit *unit, Player &player) { // add worker attributes if (this->unit_data.unit_class == gamedata::unit_classes::CIVILIAN) { - unit->add_attribute(std::make_shared>()); + unit->add_attribute(std::make_shared>()); + unit->add_attribute(std::make_shared>()); + unit->add_attribute(std::make_shared>()); // add graphic ids for resource actions - auto &gather_attr = unit->get_attribute(); - gather_attr.current_type = game_resource::wood; - gather_attr.capacity = 10.0f; - gather_attr.gather_rate = 0.002f; - + auto &worker_attr = unit->get_attribute(); + worker_attr.capacity = 10.0; + worker_attr.gather_rate[game_resource::wood] = 0.002; + worker_attr.gather_rate[game_resource::food] = 0.002; + worker_attr.gather_rate[game_resource::gold] = 0.002; + worker_attr.gather_rate[game_resource::stone] = 0.002; + + auto &multitype_attr = unit->get_attribute(); // currently not sure where the game data keeps these values // todo PREY_ANIMAL SEA_FISH if (this->parent_id() == 83) { // male graphics - gather_attr.graphics[gamedata::unit_classes::BUILDING] = this->owner.get_type(156); // builder 118 - gather_attr.graphics[gamedata::unit_classes::BERRY_BUSH] = this->owner.get_type(120); // forager - gather_attr.graphics[gamedata::unit_classes::SHEEP] = this->owner.get_type(592); // sheperd - gather_attr.graphics[gamedata::unit_classes::TREES] = this->owner.get_type(123); // woodcutter - gather_attr.graphics[gamedata::unit_classes::GOLD_MINE] = this->owner.get_type(579); // gold miner - gather_attr.graphics[gamedata::unit_classes::STONE_MINE] = this->owner.get_type(124); // stone miner + multitype_attr.types[gamedata::unit_classes::CIVILIAN] = this->parent_type(); // get default villager + multitype_attr.types[gamedata::unit_classes::BUILDING] = this->owner.get_type(156); // builder 118 + multitype_attr.types[gamedata::unit_classes::BERRY_BUSH] = this->owner.get_type(120); // forager + multitype_attr.types[gamedata::unit_classes::SHEEP] = this->owner.get_type(592); // sheperd + multitype_attr.types[gamedata::unit_classes::TREES] = this->owner.get_type(123); // woodcutter + multitype_attr.types[gamedata::unit_classes::GOLD_MINE] = this->owner.get_type(579); // gold miner + multitype_attr.types[gamedata::unit_classes::STONE_MINE] = this->owner.get_type(124); // stone miner } else { // female graphics - gather_attr.graphics[gamedata::unit_classes::BUILDING] = this->owner.get_type(222); // builder 212 - gather_attr.graphics[gamedata::unit_classes::BERRY_BUSH] = this->owner.get_type(354); // forager - gather_attr.graphics[gamedata::unit_classes::SHEEP] = this->owner.get_type(590); // sheperd - gather_attr.graphics[gamedata::unit_classes::TREES] = this->owner.get_type(218); // woodcutter - gather_attr.graphics[gamedata::unit_classes::GOLD_MINE] = this->owner.get_type(581); // gold miner - gather_attr.graphics[gamedata::unit_classes::STONE_MINE] = this->owner.get_type(220); // stone miner + multitype_attr.types[gamedata::unit_classes::CIVILIAN] = this->parent_type(); // get default villager + multitype_attr.types[gamedata::unit_classes::BUILDING] = this->owner.get_type(222); // builder 212 + multitype_attr.types[gamedata::unit_classes::BERRY_BUSH] = this->owner.get_type(354); // forager + multitype_attr.types[gamedata::unit_classes::SHEEP] = this->owner.get_type(590); // sheperd + multitype_attr.types[gamedata::unit_classes::TREES] = this->owner.get_type(218); // woodcutter + multitype_attr.types[gamedata::unit_classes::GOLD_MINE] = this->owner.get_type(581); // gold miner + multitype_attr.types[gamedata::unit_classes::STONE_MINE] = this->owner.get_type(220); // stone miner } unit->give_ability(std::make_shared(this->on_attack)); unit->give_ability(std::make_shared(this->on_attack)); unit->give_ability(std::make_shared(this->on_attack)); } else if (this->unit_data.unit_class == gamedata::unit_classes::FISHING_BOAT) { - unit->add_attribute(std::make_shared>()); + unit->add_attribute(std::make_shared>()); + unit->add_attribute(std::make_shared>()); // add fishing abilites - auto &gather_attr = unit->get_attribute(); - gather_attr.current_type = game_resource::food; - gather_attr.capacity = 15.0f; - gather_attr.gather_rate = 0.002f; - gather_attr.graphics[gamedata::unit_classes::SEA_FISH] = this; + auto &worker_attr = unit->get_attribute(); + worker_attr.capacity = 15.0; + worker_attr.gather_rate[game_resource::food] = 0.002; unit->give_ability(std::make_shared(this->on_attack)); } @@ -555,6 +562,7 @@ void BuildingProducer::initialise(Unit *unit, Player &player) { // garrison and hp for all buildings unit->add_attribute(std::make_shared>()); unit->add_attribute(std::make_shared>(this->unit_data.hit_points)); + unit->add_attribute(std::make_shared>(this->unit_data.hit_points)); bool has_destruct_graphic = this->destroyed != nullptr; unit->push_action(std::make_unique(unit, has_destruct_graphic), true); @@ -562,7 +570,9 @@ void BuildingProducer::initialise(Unit *unit, Player &player) { UnitType *proj_type = this->owner.get_type(this->projectile); if (this->unit_data.projectile_unit_id > 0 && proj_type) { coord::phys_t range_phys = coord::settings::phys_per_tile * this->unit_data.max_range; - unit->add_attribute(std::make_shared>(proj_type, range_phys, 350000, 1, this)); + unit->add_attribute(std::make_shared>(proj_type, range_phys, 350000, 1)); + // formation is used only for the attack_stance + unit->add_attribute(std::make_shared>(attack_stance::aggresive)); unit->give_ability(std::make_shared()); } diff --git a/libopenage/unit/selection.cpp b/libopenage/unit/selection.cpp index bfb7c8d0be..b6fabb9f97 100644 --- a/libopenage/unit/selection.cpp +++ b/libopenage/unit/selection.cpp @@ -47,9 +47,10 @@ bool UnitSelection::on_drawhud() { for (auto u : this->units) { if (u.second.is_valid()) { Unit *unit_ptr = u.second.get(); - if (unit_ptr->location && unit_ptr->has_attribute(attr_type::hitpoints)) { + if (unit_ptr->location && unit_ptr->has_attribute(attr_type::hitpoints) && unit_ptr->has_attribute(attr_type::damaged)) { auto &hp = unit_ptr->get_attribute(); - float percent = static_cast(hp.current) / static_cast(hp.max); + auto &dm = unit_ptr->get_attribute(); + float percent = static_cast(dm.hp) / static_cast(hp.hp); int mid = percent * 28.0f - 14.0f; coord::phys3 &pos_phys3 = unit_ptr->location->pos.draw; @@ -127,7 +128,7 @@ void UnitSelection::toggle_unit(const Player &player, Unit *u, bool append) { void UnitSelection::add_unit(const Player &player, Unit *u, bool append) { // Only select resources and units with hitpoints > 0 if (u->has_attribute(attr_type::resource) || - (u->has_attribute(attr_type::hitpoints) && u->get_attribute().current > 0)) { + (u->has_attribute(attr_type::damaged) && u->get_attribute().hp > 0)) { selection_type_t unit_type = get_unit_selection_type(player, u); int unit_type_i = static_cast(unit_type); @@ -281,13 +282,14 @@ void UnitSelection::show_attributes(Unit *u) { auto &own_attr = u->get_attribute(); lines.push_back(own_attr.player.name); } - if (u->has_attribute(attr_type::hitpoints)) { - auto &hp_attr = u->get_attribute(); - lines.push_back("hitpoints: "+std::to_string(hp_attr.current)+"/"+std::to_string(hp_attr.max)); + if (u->has_attribute(attr_type::hitpoints) && u->has_attribute(attr_type::damaged)) { + auto &hp = u->get_attribute(); + auto &dm = u->get_attribute(); + lines.push_back("hitpoints: "+std::to_string(dm.hp)+"/"+std::to_string(hp.hp)); } if (u->has_attribute(attr_type::resource)) { auto &res_attr = u->get_attribute(); - lines.push_back("resource: "+std::to_string(res_attr.amount)); + lines.push_back("resource: "+std::to_string(res_attr.amount)+" "+std::to_string(res_attr.resource_type)); } if (u->has_attribute(attr_type::building)) { auto &build_attr = u->get_attribute(); @@ -310,7 +312,8 @@ void UnitSelection::show_attributes(Unit *u) { selection_type_t UnitSelection::get_unit_selection_type(const Player &player, Unit *u) { bool is_building = u->has_attribute(attr_type::building); - // Check color + // Check owner + // TODO implement allied units if (u->is_own_unit(player)) { return is_building ? selection_type_t::own_buildings : selection_type_t::own_units; } else { diff --git a/libopenage/unit/unit.cpp b/libopenage/unit/unit.cpp index a57e5ece9b..c0d1bac18e 100644 --- a/libopenage/unit/unit.cpp +++ b/libopenage/unit/unit.cpp @@ -1,4 +1,4 @@ -// Copyright 2014-2016 the openage authors. See copying.md for legal info. +// Copyright 2014-2017 the openage authors. See copying.md for legal info. #include #include @@ -243,11 +243,19 @@ void Unit::secondary_action(std::unique_ptr action) { } void Unit::add_attribute(std::shared_ptr attr) { - this->attribute_map.emplace(attr_map_t::value_type(attr->type, attr)); + this->attributes.add(attr); +} + +void Unit::add_attributes(const Attributes &attr) { + this->attributes.add_copies(attr); +} + +void Unit::add_attributes(const Attributes &attr, bool shared, bool unshared) { + this->attributes.add_copies(attr, shared, unshared); } bool Unit::has_attribute(attr_type type) const { - return (this->attribute_map.count(type) > 0); + return this->attributes.has(type); } std::shared_ptr Unit::queue_cmd(const Command &cmd) { @@ -279,8 +287,8 @@ void Unit::stop_gather() { void Unit::stop_actions() { - // work around for gatherers continuing to work after retasking - if (this->has_attribute(attr_type::gatherer)) { + // work around for workers continuing to work after retasking + if (this->has_attribute(attr_type::worker)) { this->stop_gather(); } diff --git a/libopenage/unit/unit.h b/libopenage/unit/unit.h index 2a4bcc20d6..81c658632c 100644 --- a/libopenage/unit/unit.h +++ b/libopenage/unit/unit.h @@ -15,6 +15,7 @@ #include "../util/timing.h" #include "ability.h" #include "attribute.h" +#include "attributes.h" #include "command.h" #include "unit_container.h" @@ -173,6 +174,19 @@ class Unit : public log::LogSource { */ void add_attribute(std::shared_ptr attr); + /** + * Give new attributes to this unit. + * This is used to add the default attributes + */ + void add_attributes(const Attributes &attr); + + /** + * Give new attributes to this unit. + * If shared is false, shared attributes are ignored. + * If unshared is false, unshared attributes are ignored. + */ + void add_attributes(const Attributes &attr, bool shared, bool unshared); + /** * returns whether attribute is available */ @@ -183,7 +197,9 @@ class Unit : public log::LogSource { */ template Attribute &get_attribute() { - return *reinterpret_cast *>(attribute_map[T].get()); + return *reinterpret_cast *>(attributes.get(T).get()); + // TODO change to (templates errors) + //return attributes.get(); } /** @@ -231,6 +247,12 @@ class Unit : public log::LogSource { */ std::string logsource_name() override; + /** + * Unit attributes include color, hitpoints, speed, objects garrisoned etc + * contains 0 or 1 values for each type + */ + Attributes attributes; + private: /** * ability available -- actions that this entity @@ -262,13 +284,6 @@ class Unit : public log::LogSource { std::mutex command_queue_lock; - /** - * Unit attributes include color, hitpoints, speed, objects garrisoned etc - * contains 0 or 1 values for each type - */ - attr_map_t attribute_map; - - /** * pop any destructable actions on the next update cycle * and prevent additional actions being added diff --git a/libopenage/unit/unit_type.cpp b/libopenage/unit/unit_type.cpp index f4e415fd33..2fe7828aae 100644 --- a/libopenage/unit/unit_type.cpp +++ b/libopenage/unit/unit_type.cpp @@ -29,6 +29,18 @@ UnitType::UnitType(const Player &owner) owner{owner} { } +void UnitType::reinitialise(Unit *unit, Player &player) { + // In case reinitialise is not implemented separately + + Attributes tmp; + // copy only unshared + tmp.add_copies(unit->attributes, false, true); + // initialise the new unit + this->initialise(unit, player); + // replace new unshared attributes with the old + unit->attributes.add_copies(tmp); +} + bool UnitType::operator==(const UnitType &other) const { return this->type_abilities == other.type_abilities; } @@ -69,13 +81,11 @@ TerrainObject *UnitType::place_beside(Unit *u, TerrainObject const *other) const } void UnitType::copy_attributes(Unit *unit) const { - for (auto &attr : this->default_attributes) { - unit->add_attribute(attr.second->copy()); - } + unit->add_attributes(this->default_attributes); } -void UnitType::upgrade(const AttributeContainer &attr) { - *this->default_attributes[attr.type] = attr; +void UnitType::upgrade(const std::shared_ptr &attr) { + this->default_attributes.add(attr); } UnitType *UnitType::parent_type() const { @@ -103,7 +113,7 @@ std::string NyanType::name() const { } void NyanType::initialise(Unit *unit, Player &) { - // reset any existing attributes and type + // removes all actions and abilities unit->reset(); // initialise unit diff --git a/libopenage/unit/unit_type.h b/libopenage/unit/unit_type.h index f15a886484..acb7e0017a 100644 --- a/libopenage/unit/unit_type.h +++ b/libopenage/unit/unit_type.h @@ -1,4 +1,4 @@ -// Copyright 2015-2016 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 the openage authors. See copying.md for legal info. #pragma once @@ -8,7 +8,7 @@ #include #include "../coord/phys3.h" -#include "attribute.h" +#include "attributes.h" namespace openage { @@ -86,6 +86,16 @@ class UnitType { */ virtual void initialise(Unit *, Player &) = 0; + /** + * Initialize units shared attributes only to this type spec + * + * This can be called using existing units to modify type if the type + * Ensure that the unit has been placed before seting the units type + * + * TODO define if pure vitrual or not / should be in nyan? + */ + virtual void reinitialise(Unit *, Player &); + /** * set unit in place -- return if placement was successful * @@ -119,7 +129,7 @@ class UnitType { /** * upgrades one attribute of this unit type */ - void upgrade(const AttributeContainer &attr); + void upgrade(const std::shared_ptr &attr); /** * returns type matching parent_id() @@ -145,7 +155,7 @@ class UnitType { /** * default attributes which get copied to new units */ - attr_map_t default_attributes; + Attributes default_attributes; /** * The set of graphics used for this type