diff --git a/libopenage/gamestate/player.cpp b/libopenage/gamestate/player.cpp index cb13d3ac7b..baefa32020 100644 --- a/libopenage/gamestate/player.cpp +++ b/libopenage/gamestate/player.cpp @@ -18,7 +18,8 @@ Player::Player(Civilisation *civ, unsigned int number, std::string name) name{name}, team{nullptr}, population{0, 200}, // TODO change, get population cap max from game options - score{this} { + score{this}, + age{1} { // TODO change, get starting age from game options // starting resources // TODO change, get starting resources from game options this->resources[game_resource::food] = 1000; @@ -85,6 +86,10 @@ bool Player::deduct(const game_resource resource, double amount) { return false; } +bool Player::can_deduct(const ResourceBundle& amount) { + return this->resources >= amount; +} + double Player::amount(const game_resource resource) const { return this->resources.get(resource); } @@ -192,6 +197,10 @@ void Player::killed_unit(const Unit & unit) { this->score.add_score(score_category::military, unit.unit_type->cost.sum() * 0.2); } +void Player::advance_age() { + this->age += 1; +} + void Player::on_resources_change() { // score this->score.update_resources(this->resources); diff --git a/libopenage/gamestate/player.h b/libopenage/gamestate/player.h index 42c90433b4..91e54ba025 100644 --- a/libopenage/gamestate/player.h +++ b/libopenage/gamestate/player.h @@ -79,6 +79,11 @@ class Player { bool deduct(const ResourceBundle& amount); bool deduct(const game_resource resource, double amount); + /** + * Check if the player has enough resources to deduct the given amount. + */ + bool can_deduct(const ResourceBundle& amount); + /** * current stockpile amount */ @@ -131,6 +136,11 @@ class Player { */ void killed_unit(const Unit & unit); + /** + * Advance to next age; + */ + void advance_age(); + // Getters /** @@ -143,6 +153,12 @@ class Player { */ int get_units_had(int type_id) const; + /** + * Get the current age. + * The first age has the value 1. + */ + int get_age() const { return age; } + private: /** @@ -180,6 +196,11 @@ class Player { */ std::unordered_map units_had; + /** + * The current age. + */ + int age; + }; } // openage diff --git a/libopenage/unit/CMakeLists.txt b/libopenage/unit/CMakeLists.txt index fc901e8c87..3c7a6e27cc 100644 --- a/libopenage/unit/CMakeLists.txt +++ b/libopenage/unit/CMakeLists.txt @@ -5,6 +5,7 @@ add_sources(libopenage attributes.cpp command.cpp producer.cpp + research.cpp selection.cpp unit.cpp unit_container.cpp diff --git a/libopenage/unit/ability.cpp b/libopenage/unit/ability.cpp index c449a531aa..ea31b3ba1e 100644 --- a/libopenage/unit/ability.cpp +++ b/libopenage/unit/ability.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 @@ -7,6 +7,7 @@ #include "ability.h" #include "action.h" #include "command.h" +#include "research.h" #include "unit.h" namespace openage { @@ -93,7 +94,7 @@ void MoveAbility::invoke(Unit &to_modify, const Command &cmd, bool play_sound) { // add the range of the unit if cmd indicator is set if (cmd.has_flag(command_flag::use_range) && to_modify.has_attribute(attr_type::attack)) { auto &att = to_modify.get_attribute(); - radius += att.range; + radius += att.max_range; } to_modify.push_action(std::make_unique(&to_modify, target->get_ref(), radius)); } @@ -187,6 +188,28 @@ void TrainAbility::invoke(Unit &to_modify, const Command &cmd, bool play_sound) to_modify.push_action(std::make_unique(&to_modify, cmd.type())); } +ResearchAbility::ResearchAbility(const Sound *s) + : + sound{s} { +} + +bool ResearchAbility::can_invoke(Unit &to_modify, const Command &cmd) { + if (to_modify.has_attribute(attr_type::owner) && cmd.has_research()) { + auto &player = to_modify.get_attribute().player; + auto research = cmd.research(); + return research->can_start() && + player.can_deduct(research->type->get_research_cost()); + } + return false; +} + +void ResearchAbility::invoke(Unit &to_modify, const Command &cmd, bool play_sound) { + if (play_sound && this->sound) { + this->sound->play(); + } + to_modify.push_action(std::make_unique(&to_modify, cmd.research())); +} + BuildAbility::BuildAbility(const Sound *s) : sound{s} { @@ -331,24 +354,6 @@ void HealAbility::invoke(Unit &to_modify, const Command &cmd, bool play_sound) { to_modify.push_action(std::make_unique(&to_modify, target->get_ref())); } -ResearchAbility::ResearchAbility(const Sound *s) - : - sound{s} { -} - -bool ResearchAbility::can_invoke(Unit &/*to_modify*/, const Command &/*cmd*/) { - // TODO implement - return false; -} - -void ResearchAbility::invoke(Unit &to_modify, const Command &/*cmd*/, bool play_sound) { - to_modify.log(MSG(dbg) << "not implemented"); - if (play_sound && this->sound) { - this->sound->play(); - } - // TODO implement -} - PatrolAbility::PatrolAbility(const Sound *s) : sound{s} { @@ -402,7 +407,7 @@ ability_set UnitAbility::set_from_list(const std::vector &items) { return result; } -} /* namespace openage */ +} // namespace openage namespace std { diff --git a/libopenage/unit/ability.h b/libopenage/unit/ability.h index 41641de938..001a0701eb 100644 --- a/libopenage/unit/ability.h +++ b/libopenage/unit/ability.h @@ -41,7 +41,7 @@ enum class ability_type { }; /** - * a containter where each ability uses 1 bit + * a container where each ability uses 1 bit */ constexpr int ability_type_size = static_cast(ability_type::MAX); using ability_set = std::bitset; @@ -208,6 +208,25 @@ class TrainAbility: public UnitAbility { const Sound *sound; }; +/** + * initiates a research + */ +class ResearchAbility: public UnitAbility { +public: + ResearchAbility(const Sound *s=nullptr); + + ability_type type() override { + return ability_type::research; + } + + bool can_invoke(Unit &to_modify, const Command &cmd) override; + + void invoke(Unit &to_modify, const Command &cmd, bool play_sound=false) override; + +private: + const Sound *sound; +}; + /** * villagers build new buildings */ @@ -303,26 +322,6 @@ class HealAbility: public UnitAbility { const Sound *sound; }; -/** - * initiates a research - * TODO implement - */ -class ResearchAbility: public UnitAbility { -public: - ResearchAbility(const Sound *s=nullptr); - - ability_type type() override { - return ability_type::research; - } - - bool can_invoke(Unit &to_modify, const Command &cmd) override; - - void invoke(Unit &to_modify, const Command &cmd, bool play_sound=false) override; - -private: - const Sound *sound; -}; - /** * initiates a patrol action when given a valid target * TODO implement diff --git a/libopenage/unit/action.cpp b/libopenage/unit/action.cpp index 3d582996de..426a5948ab 100644 --- a/libopenage/unit/action.cpp +++ b/libopenage/unit/action.cpp @@ -10,10 +10,57 @@ #include "action.h" #include "command.h" #include "producer.h" +#include "research.h" #include "unit_texture.h" namespace openage { +IntervalTimer::IntervalTimer(unsigned int interval) + : + IntervalTimer{interval, -1} { +} + +IntervalTimer::IntervalTimer(unsigned int interval, int max_triggers) + : + interval{interval}, + max_triggers{max_triggers}, + time_left{interval}, + triggers{0} { +} + +void IntervalTimer::skip_to_trigger() { + this->time_left = 0; +} + +bool IntervalTimer::update(unsigned int time) { + if (this->triggers == this->max_triggers) { + return false; + } else if (this->time_left > time) { + this->time_left -= time; + return false; + } else { + this->time_left += this->interval - time; + this->triggers += 1; + return true; + } +} + +unsigned int IntervalTimer::get_time_left() const { + return this->time_left; +} + +float IntervalTimer::get_progress() const { + return 1.0f - (this->time_left * 1.0f / this->interval); +} + +bool IntervalTimer::has_triggers() const { + return this->triggers > 0; +} + +bool IntervalTimer::finished() const { + return this->triggers == this->max_triggers; +} + bool UnitAction::show_debug = false; coord::phys_t UnitAction::adjacent_range(Unit *u) { @@ -24,7 +71,7 @@ coord::phys_t UnitAction::get_attack_range(Unit *u) { coord::phys_t range = adjacent_range(u); if (u->has_attribute(attr_type::attack)) { auto &attack = u->get_attribute(); - range += attack.range; + range += attack.max_range; } return range; } @@ -727,9 +774,10 @@ TrainAction::TrainAction(Unit *e, UnitType *pp) : UnitAction{e, graphic_type::standing}, trained{pp}, + timer{10000, 1}, // TODO get the training time from unit type started{false}, - complete{false}, - train_percent{.0f} { + complete{false} { + // TODO deduct resources } void TrainAction::update(unsigned int time) { @@ -750,7 +798,7 @@ void TrainAction::update(unsigned int time) { if (this->started) { // place unit when ready - if (this->train_percent >= 1.0f) { + if (this->timer.finished() || this->timer.update(time)) { // create using the producer UnitContainer *container = this->entity->get_container(); @@ -771,13 +819,37 @@ void TrainAction::update(unsigned int time) { this->complete = true; } } - else { - this->train_percent += 0.001 * time; - } } } -void TrainAction::on_completion() {} +void TrainAction::on_completion() { + if (!this->complete) { + // TODO give back the resources + } +} + +ResearchAction::ResearchAction(Unit *e, Research *research) + : + UnitAction{e, graphic_type::standing}, + research{research}, + timer{research->type->get_research_time(), 1}, + complete{false} { + this->research->started(); +} + +void ResearchAction::update(unsigned int time) { + if (timer.update(time)) { + this->complete = true; + this->research->apply(); + this->research->completed(); + } +} + +void ResearchAction::on_completion() { + if (!this->complete) { + this->research->stopped(); + } +} BuildAction::BuildAction(Unit *e, UnitReference foundation) : @@ -861,9 +933,8 @@ void BuildAction::on_completion() { RepairAction::RepairAction(Unit *e, UnitReference tar) : TargetAction{e, graphic_type::work, tar}, - complete{false}, - time{80}, - time_left{0} { + timer{80}, + complete{false} { if (!tar.is_valid()) { // the target no longer exists @@ -874,7 +945,7 @@ RepairAction::RepairAction(Unit *e, UnitReference tar) Unit *target = tar.get(); if (!target->has_attribute(attr_type::building)) { - this->time *= 4; + this->timer.set_interval(this->timer.get_interval() * 4); } // cost formula: 0.5 * (target cost) / (target max hp) @@ -883,6 +954,12 @@ RepairAction::RepairAction(Unit *e, UnitReference tar) // get the target unit's cost this->cost += target->unit_type->cost; this->cost *= 0.5 / hp.hp; + + auto &owner = this->entity->get_attribute(); + if (!owner.player.deduct(this->cost)) { + // no resources to start + this->complete = true; + } } } @@ -890,34 +967,27 @@ 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 (dm.hp >= hp.hp) { // repaired by something else this->complete = true; } - else if (this->time_left > 0) { - this->time_left -= time; + else if (this->timer.update(time)) { + dm.hp += 1; - if (this->time_left <= 0) { - dm.hp += 1; + if (dm.hp >= hp.hp) { + this->complete = true; + } - if (dm.hp >= hp.hp) { + if (!this->complete) { + auto &owner = this->entity->get_attribute(); + if (!owner.player.deduct(this->cost)) { + // no resources to continue this->complete = true; } } } - if (this->time_left <= 0 && !this->complete) { - if (owner.player.deduct(this->cost)) { - this->time_left += this->time; - } - else { - // no resources to continue - this->complete = true; - } - } - // inc frame this->frame += time * this->frame_rate / 2.5f; } @@ -1085,8 +1155,7 @@ UnitReference GatherAction::nearest_dropsite(game_resource res_type) { 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} { + timer{500} { // TODO get fire rate from unit type // check if attacking a non resource unit if (this->entity->has_attribute(attr_type::worker) && @@ -1096,21 +1165,20 @@ AttackAction::AttackAction(Unit *e, UnitReference tar) this->entity->get_attribute().switchType(gamedata::unit_classes::CIVILIAN, this->entity); } } + + // TODO rivit logic, a start inside the animation should be provided + this->timer.skip_to_trigger(); } AttackAction::~AttackAction() {} void AttackAction::update_in_range(unsigned int time, Unit *target_ptr) { - if (this->strike_percent > 0.0) { - this->strike_percent -= this->rate_of_fire * time; - } - else { - this->strike_percent += 1.0f; + if (this->timer.update(time)) { this->attack(*target_ptr); } // inc frame - this->frame += time * this->current_graphics().at(graphic)->frame_count * this->rate_of_fire; + this->frame += time * this->current_graphics().at(graphic)->frame_count * 1.0f / this->timer.get_interval(); } bool AttackAction::completed_in_range(Unit *target_ptr) const { @@ -1158,25 +1226,19 @@ void AttackAction::fire_projectile(const Attribute &att, cons HealAction::HealAction(Unit *e, UnitReference tar) : TargetAction{e, graphic_type::heal, tar, get_attack_range(e)}, - heal_percent{0.0f} { + timer{this->entity->get_attribute().rate} { } HealAction::~HealAction() {} void HealAction::update_in_range(unsigned int time, Unit *target_ptr) { - auto &heal = this->entity->get_attribute(); - - if (this->heal_percent > 0.0) { - this->heal_percent -= heal.rate * time; - } - else { - this->heal_percent += 1.0f; + if (this->timer.update(time)) { this->heal(*target_ptr); } // inc frame - this->frame += time * this->current_graphics().at(graphic)->frame_count * heal.rate; + this->frame += time * this->current_graphics().at(graphic)->frame_count * 1.0f / this->timer.get_interval(); } bool HealAction::completed_in_range(Unit *target_ptr) const { @@ -1261,6 +1323,8 @@ void ProjectileAction::update(unsigned int time) { coord::phys3 new_position = this->entity->location->pos.draw + d_attr.unit_dir * time; if (!this->entity->location->move(new_position)) { + // TODO implement friendly_fire (now friendly_fire is always on), attack_attribute.friendly_fire + // find object which was hit auto terrain = this->entity->location->get_terrain(); TileContent *tc = terrain->get_data(new_position.to_tile3().to_tile()); @@ -1274,6 +1338,8 @@ void ProjectileAction::update(unsigned int time) { } } + // TODO implement area of effect, attack_attribute.area_of_effect + has_hit = true; } @@ -1287,4 +1353,4 @@ bool ProjectileAction::completed() const { return (has_hit || this->entity->location->pos.draw.up <= 0); } -} /* namespace openage */ +} // namespace openage diff --git a/libopenage/unit/action.h b/libopenage/unit/action.h index 5fca291575..c462912f4e 100644 --- a/libopenage/unit/action.h +++ b/libopenage/unit/action.h @@ -8,6 +8,7 @@ #include "../pathfinding/path.h" #include "../gamestate/resource.h" #include "attribute.h" +#include "research.h" #include "unit.h" #include "unit_container.h" @@ -15,6 +16,69 @@ namespace openage { class TerrainSearch; +// TODO use a type instead of unsigned int for time + +/** + * A interval triggering timer used in actions. + * TODO find a better name for triggers + */ +class IntervalTimer { +public: + + /** + * Constructs a timer with a given interval + */ + IntervalTimer(unsigned int interval); + + /** + * Constructs a timer with a given interval which will + * stop after a given number of triggers. + */ + IntervalTimer(unsigned int interval, int max_triggers); + + void skip_to_trigger(); + + bool update(unsigned int time); + + /** + * Returns the time until the next trigger + */ + unsigned int get_time_left() const; + + float get_progress() const; + + /** + * Returns true if at least one interval has passed. + */ + bool has_triggers() const; + + /** + * Returns true if the interval passed have reached the max. + */ + bool finished() const; + + /** + * Returns the number of intervals passed. + */ + int get_triggers() const { return this->triggers; } + + unsigned int get_interval() const { return this->interval; } + + void set_interval(unsigned int interval) { this->interval = interval; } + +private: + + unsigned int interval; + + int max_triggers; + + unsigned int time_left; + + int triggers; + +}; + + /** * Actions can be pushed onto any units action stack * @@ -45,7 +109,7 @@ class UnitAction { * each action has its own update functionality which gets called when this * is the active action */ - virtual void update(unsigned int) = 0; + virtual void update(unsigned int time) = 0; /** * action to perform when popped from a units action stack @@ -82,7 +146,7 @@ class UnitAction { /** * determines which graphic should be used for drawing this unit - * finds the defualt graphic using the units type, used by most actions + * finds the default graphic using the units type, used by most actions * * this virtual function is overriden for special cases such as * villager task graphics @@ -148,6 +212,7 @@ class UnitAction { /** * Base class for actions which target another unit such as * gather, attack, heal and convert + * TODO implement min range */ class TargetAction: public UnitAction { public: @@ -166,7 +231,7 @@ class TargetAction: public UnitAction { TargetAction(Unit *e, graphic_type gt, UnitReference r); virtual ~TargetAction() {} - void update(unsigned int) override; + void update(unsigned int time) override; void on_completion() override; bool completed() const override; bool allow_interupt() const override { return true; } @@ -212,7 +277,7 @@ class DecayAction: public UnitAction { DecayAction(Unit *e); virtual ~DecayAction() {} - void update(unsigned int) override; + void update(unsigned int time) override; void on_completion() override; bool completed() const override; bool allow_interupt() const override { return false; } @@ -232,7 +297,7 @@ class DeadAction: public UnitAction { DeadAction(Unit *e, std::function on_complete=[]() {}); virtual ~DeadAction() {} - void update(unsigned int) override; + void update(unsigned int time) override; void on_completion() override; bool completed() const override; bool allow_interupt() const override { return false; } @@ -253,7 +318,7 @@ class FoundationAction: public UnitAction { FoundationAction(Unit *e, bool add_destuction=false); virtual ~FoundationAction() {} - void update(unsigned int) override; + void update(unsigned int time) override; void on_completion() override; bool completed() const override; bool allow_interupt() const override { return true; } @@ -273,7 +338,7 @@ class IdleAction: public UnitAction { IdleAction(Unit *e); virtual ~IdleAction() {} - void update(unsigned int) override; + void update(unsigned int time) override; void on_completion() override; bool completed() const override; bool allow_interupt() const override { return false; } @@ -303,7 +368,7 @@ class MoveAction: public UnitAction { MoveAction(Unit *e, UnitReference tar, coord::phys_t within_range); virtual ~MoveAction(); - void update(unsigned int) override; + void update(unsigned int time) override; void on_completion() override; bool completed() const override; bool allow_interupt() const override { return true; } @@ -362,7 +427,7 @@ class UngarrisonAction: public UnitAction { UngarrisonAction(Unit *e, const coord::phys3 &pos); virtual ~UngarrisonAction() {} - void update(unsigned int) override; + void update(unsigned int time) override; void on_completion() override; bool completed() const override { return this->complete; } bool allow_interupt() const override { return true; } @@ -382,18 +447,47 @@ class TrainAction: public UnitAction { TrainAction(Unit *e, UnitType *pp); virtual ~TrainAction() {} - void update(unsigned int) override; + void update(unsigned int time) override; void on_completion() override; bool completed() const override { return this->complete; } bool allow_interupt() const override { return false; } bool allow_control() const override { return true; } std::string name() const override { return "train"; } + float get_progress() const { return this->timer.get_progress(); } + private: UnitType *trained; + + IntervalTimer timer; bool started; bool complete; - float train_percent; +}; + +/** + * trains a new unit + */ +class ResearchAction: public UnitAction { +public: + ResearchAction(Unit *e, Research *research); + virtual ~ResearchAction() {} + + void update(unsigned int time) override; + void on_completion() override; + bool completed() const override { return this->complete; } + bool allow_interupt() const override { return false; } + bool allow_control() const override { return true; } + std::string name() const override { return "train"; } + + float get_progress() const { return this->timer.get_progress(); } + const ResearchType* get_research_type() const { return this->research->type; } + +private: + + Research *research; + + IntervalTimer timer; + bool complete; }; /** @@ -410,6 +504,8 @@ class BuildAction: public TargetAction { void on_completion() override; std::string name() const override { return "build"; } + float get_progress() const { return this->complete; } + private: float complete, build_rate; static constexpr float search_tile_distance = 9.0f; @@ -430,16 +526,14 @@ class RepairAction: public TargetAction { std::string name() const override { return "repair"; } private: - bool complete; - - float time; - float time_left; /** * stores the cost of the repair for 1hp */ ResourceBundle cost; + IntervalTimer timer; + bool complete; }; /** @@ -476,7 +570,8 @@ class AttackAction: public TargetAction { std::string name() const override { return "attack"; } private: - float strike_percent, rate_of_fire; + + IntervalTimer timer; /** * use attack action @@ -503,7 +598,8 @@ class HealAction: public TargetAction { std::string name() const override { return "heal"; } private: - float heal_percent; + + IntervalTimer timer; /** * use heal action @@ -537,7 +633,7 @@ class ProjectileAction: public UnitAction { ProjectileAction(Unit *e, coord::phys3 target); virtual ~ProjectileAction(); - void update(unsigned int) override; + void update(unsigned int time) override; void on_completion() override; bool completed() const override; bool allow_interupt() const override { return false; } diff --git a/libopenage/unit/attribute.h b/libopenage/unit/attribute.h index d30df1ca60..f10573c5cb 100644 --- a/libopenage/unit/attribute.h +++ b/libopenage/unit/attribute.h @@ -83,8 +83,8 @@ enum class attr_type { * Can be used for buildings also. */ enum class attack_stance { - aggresive, - devensive, + aggressive, + defensive, stand_ground, do_nothing }; @@ -273,8 +273,8 @@ template<> class Attribute: public SharedAttributeContainer { }; /** - * TODO implement min range * TODO can a unit have multiple attacks such as villagers hunting map target classes onto attacks + * TODO remove the first constructor and the default values after (keep for now for compatibility) */ template<> class Attribute: public SharedAttributeContainer { public: @@ -284,13 +284,18 @@ template<> class Attribute: public SharedAttributeContainer { : Attribute{type, r, h, {{4, d}}} {} - Attribute(UnitType *type, coord::phys_t r, coord::phys_t h, typeamount_map d) + Attribute(UnitType *type, coord::phys_t r, coord::phys_t h, typeamount_map d, + coord::phys_t min_range=0, bool friendly_fire=false, + coord::phys_t area_of_effect=0) : SharedAttributeContainer{attr_type::attack}, ptype{type}, - range{r}, + min_range{min_range}, + max_range{r}, init_height{h}, - damage{d} {} + damage{d}, + friendly_fire{friendly_fire}, + area_of_effect{area_of_effect} {} std::shared_ptr copy() const override { return std::make_shared>(*this); @@ -301,10 +306,16 @@ template<> class Attribute: public SharedAttributeContainer { */ UnitType *ptype; + /** + * The min range of the attack + * TODO not used + */ + coord::phys_t min_range; + /** * The max range of the attack */ - coord::phys_t range; + coord::phys_t max_range; /** * The height from which the projectile starts @@ -312,6 +323,18 @@ template<> class Attribute: public SharedAttributeContainer { coord::phys_t init_height; typeamount_map damage; + + /** + * If the attack can damage allied (friendly) units. + * TODO not used + */ + bool friendly_fire; + + /** + * The radius of the area of effect of the attack or 0 if there is no area_of_effect. + * TODO not used + */ + coord::phys_t area_of_effect; }; /** @@ -344,7 +367,7 @@ template<> class Attribute: public UnsharedAttributeContai */ template<> class Attribute: public SharedAttributeContainer { public: - Attribute(coord::phys_t r, unsigned int l, float ra) + Attribute(coord::phys_t r, unsigned int l, unsigned int ra) : SharedAttributeContainer{attr_type::heal}, range{r}, @@ -366,9 +389,9 @@ template<> class Attribute: public SharedAttributeContainer { unsigned int life; /** - * The rate of each heal cycle + * The period of each heal cycle */ - float rate; + unsigned int rate; }; template<> class Attribute: public SharedAttributeContainer { diff --git a/libopenage/unit/command.cpp b/libopenage/unit/command.cpp index 5fcd798644..ec83664394 100644 --- a/libopenage/unit/command.cpp +++ b/libopenage/unit/command.cpp @@ -1,43 +1,49 @@ -// Copyright 2014-2015 the openage authors. See copying.md for legal info. +// Copyright 2014-2017 the openage authors. See copying.md for legal info. #include "command.h" namespace openage { -Command::Command(const Player &p, Unit *unit, bool haspos, UnitType *t) +Command::Command(const Player &p, Unit *unit, bool haspos, UnitType *t, Research *res) : player(p), has_pos{haspos}, u{unit}, - unit_type{t} { + unit_type{t}, + res{res} { this->modifiers.set(); } Command::Command(const Player &p, Unit *unit) : - Command{p, unit, false, nullptr} { + Command{p, unit, false, nullptr, nullptr} { } Command::Command(const Player &p, coord::phys3 position) : - Command{p, nullptr, true, nullptr} { + Command{p, nullptr, true, nullptr, nullptr} { this->pos = position; } Command::Command(const Player &p, Unit *unit, coord::phys3 position) : - Command{p, unit, true, nullptr} { + Command{p, unit, true, nullptr, nullptr} { this->pos = position; } Command::Command(const Player &p, UnitType *t) : - Command{p, nullptr, false, t} { + Command{p, nullptr, false, t, nullptr} { +} + +Command::Command(const Player &p, Research *res) + : + Command{p, nullptr, false, nullptr, res} { } Command::Command(const Player &p, UnitType *t, coord::phys3 position) : - Command{p, nullptr, true, t} { + Command{p, nullptr, true, t, nullptr} { this->pos = position; } @@ -53,6 +59,10 @@ bool Command::has_type() const { return this->unit_type; } +bool Command::has_research() const { + return this->res; +} + Unit *Command::unit() const { return this->u; } @@ -65,6 +75,10 @@ UnitType *Command::type() const { return this->unit_type; } +Research *Command::research() const { + return this->res; +} + void Command::set_ability(ability_type t) { this->modifiers = 0; this->modifiers[static_cast(t)] = true; diff --git a/libopenage/unit/command.h b/libopenage/unit/command.h index 6057930de8..da95271a08 100644 --- a/libopenage/unit/command.h +++ b/libopenage/unit/command.h @@ -40,11 +40,13 @@ struct hash { namespace openage { class Player; +class Research; class Unit; class UnitType; /* * Game command from the ui + * TODO reorganize the names of the optional variables and their getters */ class Command { public: @@ -69,6 +71,11 @@ class Command { */ Command(const Player &, UnitType *t); + /** + * select a research + */ + Command(const Player &, Research *res); + /** * place building foundation */ @@ -76,11 +83,13 @@ class Command { bool has_unit() const; bool has_position() const; - bool has_type()const; + bool has_type() const; + bool has_research() const; Unit *unit() const; coord::phys3 position() const; UnitType *type() const; + Research *research() const; /** * sets invoked ability type, no other type may be used if @@ -121,12 +130,13 @@ class Command { /** * basic constructor, which shouldnt be used directly */ - Command(const Player &, Unit *unit, bool haspos, UnitType *t); + Command(const Player &, Unit *unit, bool haspos, UnitType *t, Research *res); bool has_pos; Unit *u; coord::phys3 pos; UnitType *unit_type; + Research *res; /** * additional options diff --git a/libopenage/unit/producer.cpp b/libopenage/unit/producer.cpp index 405e37e842..746f388b21 100644 --- a/libopenage/unit/producer.cpp +++ b/libopenage/unit/producer.cpp @@ -588,7 +588,7 @@ void BuildingProducer::initialise(Unit *unit, Player &player) { coord::phys_t range_phys = coord::settings::phys_per_tile * this->unit_data.weapon_range_max; 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->add_attribute(std::make_shared>(attack_stance::aggressive)); unit->give_ability(std::make_shared()); } @@ -854,4 +854,4 @@ TerrainObject *ProjectileProducer::place(Unit *u, std::shared_ptr terra return nullptr; } -} /* namespace openage */ +} // namespace openage diff --git a/libopenage/unit/research.cpp b/libopenage/unit/research.cpp new file mode 100644 index 0000000000..eb74b138cc --- /dev/null +++ b/libopenage/unit/research.cpp @@ -0,0 +1,63 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "../gamestate/player.h" +#include "research.h" + + +namespace openage { + +ResearchType::ResearchType(Player &owner) + : + owner{owner} { +} + +std::shared_ptr ResearchType::initialise() const { + return std::make_shared(this); +} + +Research::Research(const ResearchType *type) + : + type{type}, + active_count{0}, + completed_count{0} { +} + +void Research::started() { + this->active_count += 1; +} + +void Research::stopped() { + this->active_count -= 1; +} + +void Research::completed() { + this->active_count -= 1; + this->completed_count += 1; +} + +bool Research::can_start() const { + return this->active_count + this->completed_count < this->type->get_max_repeats(); +} + +bool Research::is_active() const { + return this->active_count > 0; +} + +bool Research::is_researched() const { + return this->completed_count == this->type->get_max_repeats(); +} + +void Research::apply() { + // apply the patch + this->type->apply(); + + // perform category based actions + if (type->category() == research_category::age_advance) { + type->owner.advance_age(); + + } else if (type->category() == research_category::generic) { + // TODO implement a way to handle this category + } +} + +} // namespace openage diff --git a/libopenage/unit/research.h b/libopenage/unit/research.h new file mode 100644 index 0000000000..c7afec150b --- /dev/null +++ b/libopenage/unit/research.h @@ -0,0 +1,162 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + + +namespace openage { + +class Player; +class Research; +class ResourceBundle; + +enum class research_category : int { + /** + * Research which modify unit type data. + */ + unit_upgrade, + /** + * Research which modify unit type data and also progresses the next age. + * Separate category for simplification. + */ + age_advance, + /** + * Research which modify unit type data and something else. + * (eg. see enemy line of sight) + */ + generic, + RESEARCH_CATEGORY_COUNT +}; + +/** + * Describes a research for a single player + * + * The get_max_repeats (with a default value of 1) can allow for a research to be + * performed multiple types + */ +class ResearchType { +public: + + ResearchType(Player &owner); + + /** + * Gets the unique id of this research type. + */ + virtual int id() const = 0; + + /** + * Gets the name of the research. + */ + virtual std::string name() const = 0; + + /** + * Gets the research category of the research. + */ + virtual research_category category() const = 0; + + /** + * Creates a single Research object. + * Must be called only once. + */ + std::shared_ptr initialise() const; + + /** + * The player who owns this research type + */ + Player &owner; + + /** + * How many times it can be researched. + * All classic researches have a value of 1. + */ + int get_max_repeats() const { return 1; } + + virtual unsigned int get_research_time() const = 0; + + virtual ResourceBundle get_research_cost() const = 0; + + /** + * Performs the modifications (eg. apply patch to the unit types) + */ + virtual void apply() const = 0; + +protected: + +private: + +}; + +class NyanResearchType : public ResearchType { + // TODO POST-NYAN Implement + + NyanResearchType(Player &owner); + +}; + +/** + * At most one Research must exist for each ResearchType. + * + * A research represents how many times the research type has been completed (completed count) + * and also how many unit are researching it now (active count). + */ +class Research { +public: + + Research(const ResearchType *type); + + const ResearchType *type; + + /** + * Called when a unit started researching this research. + */ + void started(); + + /** + * Called when a unit stopped researching this research before completing it. + */ + void stopped(); + + /** + * Called when a unit completed researching this research. + */ + void completed(); + + /** + * Returns true if a unit can start researching this research. + */ + bool can_start() const; + + /** + * Returns true if any unit is researching this research. + */ + bool is_active() const; + + /** + * Returns true if it has nothing more to offer (reached max repeats). + */ + bool is_researched() const; + + /** + * Apply the modifications to the owner player. + */ + void apply(); + +protected: + + /** + * The number of units that are researching this research + */ + int active_count; + + /** + * The number of times this research has been completed + */ + int completed_count; + +private: + +}; + +} // namespace openage diff --git a/libopenage/unit/selection.cpp b/libopenage/unit/selection.cpp index b6fabb9f97..61939c2b27 100644 --- a/libopenage/unit/selection.cpp +++ b/libopenage/unit/selection.cpp @@ -353,4 +353,4 @@ std::vector tiles_in_range(coord::camgame p1, coord::camgame p2) { return tiles; } -} /* namespace openage */ +} // namespace openage diff --git a/libopenage/unit/selection.h b/libopenage/unit/selection.h index 4de2a7ccb1..c0101bb7d6 100644 --- a/libopenage/unit/selection.h +++ b/libopenage/unit/selection.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 @@ -73,7 +73,7 @@ class UnitSelection: public HudHandler { void select_point(const Player &player, Terrain *terrain, coord::camgame p, bool append=false); /** - * boxed unit selcetion + * boxed unit selection */ void select_space(const Player &player, Terrain *terrain, coord::camgame p1, coord::camgame p2, bool append=false); diff --git a/libopenage/unit/unit.h b/libopenage/unit/unit.h index 81c658632c..4fc6d17c51 100644 --- a/libopenage/unit/unit.h +++ b/libopenage/unit/unit.h @@ -164,7 +164,7 @@ class Unit : public log::LogSource { void push_action(std::unique_ptr action, bool force=false); /** - * adds a seconadry action which is always updated + * adds a secondary action which is always updated */ void secondary_action(std::unique_ptr action); @@ -268,7 +268,7 @@ class Unit : public log::LogSource { /** - * seconadry actions are always updated + * secondary actions are always updated */ std::vector> action_secondary; diff --git a/libopenage/unit/unit_texture.cpp b/libopenage/unit/unit_texture.cpp index 08d42a03df..3651187511 100644 --- a/libopenage/unit/unit_texture.cpp +++ b/libopenage/unit/unit_texture.cpp @@ -176,4 +176,4 @@ unsigned int dir_group(coord::phys3_delta dir, unsigned int angles) { } -} /* namespace openage */ +} // namespace openage diff --git a/libopenage/unit/unit_texture.h b/libopenage/unit/unit_texture.h index 0d19fb647e..747c7e445e 100644 --- a/libopenage/unit/unit_texture.h +++ b/libopenage/unit/unit_texture.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 @@ -45,7 +45,7 @@ class UnitTexture { const float frame_rate; /** - * draw object with vertial orientation (arrows) + * draw object with vertical orientation (arrows) * adding an addtion degree of orientation */ const bool use_up_angles; diff --git a/libopenage/unit/unit_type.cpp b/libopenage/unit/unit_type.cpp index 2fe7828aae..04b79384d5 100644 --- a/libopenage/unit/unit_type.cpp +++ b/libopenage/unit/unit_type.cpp @@ -153,4 +153,4 @@ TerrainObject *NyanType::place(Unit *unit, std::shared_ptr terrain, coo return nullptr; } -} /* namespace openage */ +} // namespace openage diff --git a/libopenage/unit/unit_type.h b/libopenage/unit/unit_type.h index 82818bc282..e49e15d21f 100644 --- a/libopenage/unit/unit_type.h +++ b/libopenage/unit/unit_type.h @@ -117,7 +117,7 @@ class UnitType { UnitTexture *default_texture(); /** - * similiar to place but places adjacent to an existing object + * similar to place but places adjacent to an existing object */ TerrainObject *place_beside(Unit *, TerrainObject const *) const;