The Battle for Wesnoth  1.19.15+dev
recruit.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2025
3  by Gabriel Morin <gabrielmorin (at) gmail (dot) com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  */
19 
20 #include "whiteboard/recruit.hpp"
21 
23 #include "whiteboard/utility.hpp"
24 #include "whiteboard/visitor.hpp"
25 
26 #include "fake_unit_ptr.hpp"
27 #include "menu_events.hpp"
28 #include "play_controller.hpp"
29 #include "resources.hpp"
30 #include "units/unit.hpp"
32 #include "units/map.hpp"
33 #include "units/types.hpp"
34 #include "utils/general.hpp"
35 
36 namespace wb
37 {
38 
39 std::ostream& operator<<(std::ostream& s, const recruit_ptr& recruit)
40 {
41  assert(recruit);
42  return recruit->print(s);
43 }
44 std::ostream& operator<<(std::ostream& s, const recruit_const_ptr& recruit)
45 {
46  assert(recruit);
47  return recruit->print(s);
48 }
49 
50 std::ostream& recruit::print(std::ostream &s) const
51 {
52  s << "Recruiting " << unit_name_ << " on hex " << recruit_hex_;
53  return s;
54 }
55 
56 recruit::recruit(std::size_t team_index, bool hidden, const std::string& unit_name, const map_location& recruit_hex):
57  action(team_index,hidden),
58  unit_name_(unit_name),
59  recruit_hex_(recruit_hex),
60  temp_unit_(create_corresponding_unit()), //auto-ptr ownership transfer
61  fake_unit_(temp_unit_->clone()), //temp_unit_ *copied* into new fake unit
62  cost_(0)
63 {
64  this->init();
65 }
66 
67 recruit::recruit(const config& cfg, bool hidden)
68  : action(cfg,hidden)
69  , unit_name_(cfg["unit_name_"])
70  , recruit_hex_(cfg.mandatory_child("recruit_hex_")["x"],cfg.mandatory_child("recruit_hex_")["y"], wml_loc())
71  , temp_unit_()
72  , fake_unit_()
73  , cost_(0)
74 {
75  // Validate unit_name_
77  throw action::ctor_err("recruit: Invalid recruit unit type");
78 
79  // Construct temp_unit_ and fake_unit_
80  temp_unit_ = create_corresponding_unit(); //auto-ptr ownership transfer
81  fake_unit_.reset(temp_unit_->clone()); //temp_unit_ copied into new fake_unit
82 
83  this->init();
84 }
85 
87 {
88  temp_unit_->set_movement(0, true);
89  temp_unit_->set_attacks(0);
90 
91  fake_unit_->set_location(recruit_hex_);
92  fake_unit_->set_movement(0, true);
93  fake_unit_->set_attacks(0);
94  fake_unit_->anim_comp().set_ghosted(false);
96 
97  cost_ = fake_unit_->type().cost();
98 }
99 
101 {
102 }
103 
105 {
106  v.visit(shared_from_this());
107 }
108 
109 void recruit::execute(bool& success, bool& complete)
110 {
111  assert(valid());
112  temporary_unit_hider const raii(*fake_unit_);
113  const std::size_t old_id = fake_unit_->underlying_id();
115  const int side_num = team_index() + 1;
116  //Give back the spent gold so we don't get "not enough gold" message
117  resources::gameboard->teams().at(team_index()).get_side_actions()->change_gold_spent_by(-cost_);
118  bool const result = resources::controller->get_menu_handler().do_recruit(unit_name_, side_num, loc);
119  //If it failed, take back the gold
120  if (!result) {
121  resources::gameboard->teams().at(team_index()).get_side_actions()->change_gold_spent_by(cost_);
122  }
123  else {
124  auto mit = resources::gameboard->units().find(loc);
125  if(mit != resources::gameboard->units().end()) {
126  viewer_actions()->update_recruited_unit(old_id, *mit);
127  }
128 
129  }
130  success = complete = result;
131 }
132 
134 {
135  assert(valid());
136  temp_unit_->set_location(recruit_hex_);
137 
138  DBG_WB << "Inserting future recruit [" << temp_unit_->id()
139  << "] at position " << temp_unit_->get_location() << ".";
140 
141  // Add cost to money spent on recruits.
142  resources::gameboard->teams().at(team_index()).get_side_actions()->change_gold_spent_by(cost_);
143 
144  // Temporarily insert unit into unit_map
145  // unit map takes ownership of temp_unit
146  const std::size_t old_id = temp_unit_->underlying_id();
148 
149  //in the past there was a bug where the map changed the unit ids here (because a unit with that id already existed) which caused crashes later.
150  assert(temp_unit_->underlying_id() == old_id);
151 
152  // Update gold in the top bar
154 }
155 
157 {
158  //Unit map gives back ownership of temp_unit_
160 
161  //remove simulated unit refresh on new turn done by mapbuilder.
162  temp_unit_->set_movement(0, true);
163  temp_unit_->set_attacks(0);
164 
165  assert(temp_unit_.get());
166 }
167 
169 {
170  if (hex == recruit_hex_)
171  {
172  const double x_offset = 0.5;
173  const double y_offset = 0.7;
174  //position 0,0 in the hex is the upper left corner
175  std::stringstream number_text;
176  number_text << font::unicode_minus << cost_;
177  std::size_t font_size = 16;
178  color_t color {255, 0, 0}; //red
180  number_text.str(), font_size, color, x_offset, y_offset);
181  }
182 }
183 
185 {
187 }
188 
189 
191 {
193  assert(type);
194  int side_num = team_index() + 1;
195  //real_unit = false needed to avoid generating random traits and causing OOS
196  bool real_unit = false;
197  unit_ptr result = unit::create(*type, side_num, real_unit);
198  result->set_movement(0, true);
199  result->set_attacks(0);
200  return result; //ownership gets transferred to returned unique_ptr copy
201 }
202 
204 {
205  //Check that destination hex is still free
206  if(resources::gameboard->units().find(recruit_hex_) != resources::gameboard->units().end()) {
207  return LOCATION_OCCUPIED;
208  }
209  //Check that unit to recruit is still in side's recruit list
210  const std::set<std::string>& recruits = resources::gameboard->teams()[team_index()].recruits();
211  if(recruits.find(unit_name_) == recruits.end()) {
212  bool in_extra_recruit = any_recruiter(team_index() + 1, get_recruit_hex(), [&](unit& leader) {
213  return utils::contains(leader.recruits(), unit_name_);
214  });
215  if (!in_extra_recruit) {
216  return UNIT_UNAVAILABLE;
217  }
218  }
219  //Check that there is still enough gold to recruit this unit
220  if(temp_unit_->cost() > resources::gameboard->teams()[team_index()].gold()) {
221  return NOT_ENOUGH_GOLD;
222  }
223  //Check that there is a leader available to recruit this unit
225  return NO_LEADER;
226  }
227 
228  return OK;
229 }
230 
232 {
233  config final_cfg = action::to_config();
234 
235  final_cfg["type"] = "recruit";
236  final_cfg["unit_name_"] = unit_name_;
237 // final_cfg["temp_cost_"] = temp_cost_; //Unnecessary
238 
239  config loc_cfg;
240  loc_cfg["x"]=recruit_hex_.wml_x();
241  loc_cfg["y"]=recruit_hex_.wml_y();
242  final_cfg.add_child("recruit_hex_", std::move(loc_cfg));
243 
244  return final_cfg;
245 }
246 
247 void recruit::do_hide() {fake_unit_->set_hidden(true);}
248 void recruit::do_show() {fake_unit_->set_hidden(false);}
249 
250 }
map_location loc
Definition: move.cpp:172
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
config & add_child(config_key_type key)
Definition: config.cpp:436
void draw_text_in_hex(const map_location &loc, const drawing_layer layer, const std::string &text, std::size_t font_size, color_t color, double x_in_hex=0.5, double y_in_hex=0.5)
Draw text on a hex.
Definition: display.cpp:1348
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:2960
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:307
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:102
bool do_recruit(const std::string &type, int side_num, const map_location &target_hex)
void reset()
Reset the internal unit pointer, and deregister from the manager.
void place_on_fake_unit_manager(fake_unit_manager *d)
Place this on manager's fake_units_ dequeue.
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:80
virtual const unit_map & units() const override
Definition: game_board.hpp:107
events::menu_handler & get_menu_handler()
Container associating units to locations.
Definition: map.hpp:98
unit_ptr extract(const map_location &loc)
Extracts a unit from the map.
Definition: map.cpp:259
unit_iterator find(std::size_t id)
Definition: map.cpp:302
umap_retval_pair_t insert(const unit_ptr &p)
Inserts the unit pointed to by p into the map.
Definition: map.cpp:135
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1224
A single unit type that the player may recruit.
Definition: types.hpp:43
This class represents a single unit of a specific type.
Definition: unit.hpp:132
static unit_ptr create(const config &cfg, bool use_traits=false, const vconfig *vcfg=nullptr)
Initializes a unit from a config.
Definition: unit.hpp:200
Abstract base class for all the whiteboard planned actions.
Definition: action.hpp:34
bool valid()
Returns whether this action is valid or not.
Definition: action.hpp:135
std::size_t team_index() const
Returns the index of the team that owns this action.
Definition: action.hpp:84
virtual config to_config() const
Constructs and returns a config object representing this object.
Definition: action.cpp:51
error
Possible errors.
Definition: action.hpp:107
@ NOT_ENOUGH_GOLD
Definition: action.hpp:118
@ LOCATION_OCCUPIED
Definition: action.hpp:112
@ UNIT_UNAVAILABLE
Definition: action.hpp:117
virtual error check_validity() const
Check the validity of the action.
Definition: recruit.cpp:203
std::string unit_name_
Definition: recruit.hpp:85
virtual void do_hide()
Called by the non-virtual hide() and show(), respectively.
Definition: recruit.cpp:247
fake_unit_ptr fake_unit_
Definition: recruit.hpp:89
std::shared_ptr< recruit > shared_from_this()
Definition: recruit.hpp:81
virtual std::ostream & print(std::ostream &s) const
Definition: recruit.cpp:50
virtual void draw_hex(const map_location &hex)
Gets called by display when drawing a hex, to allow actions to draw to the screen.
Definition: recruit.cpp:168
map_location const get_recruit_hex() const
Definition: recruit.hpp:75
virtual config to_config() const
Constructs and returns a config object representing this object.
Definition: recruit.cpp:231
map_location recruit_hex_
Definition: recruit.hpp:86
unit_ptr temp_unit_
Definition: recruit.hpp:88
virtual void do_show()
Definition: recruit.cpp:248
virtual void execute(bool &success, bool &complete)
Output parameters: success: Whether or not to continue an execute-all after this execution complete: ...
Definition: recruit.cpp:109
recruit(std::size_t team_index, bool hidden, const std::string &unit_name, const map_location &recruit_hex)
Definition: recruit.cpp:56
virtual void apply_temp_modifier(unit_map &unit_map)
Applies temporarily the result of this action to the specified unit map.
Definition: recruit.cpp:133
void init()
Definition: recruit.cpp:86
virtual ~recruit()
Definition: recruit.cpp:100
unit_ptr create_corresponding_unit()
Definition: recruit.cpp:190
virtual void remove_temp_modifier(unit_map &unit_map)
Removes the result of this action from the specified unit map.
Definition: recruit.cpp:156
virtual void accept(visitor &v)
Definition: recruit.cpp:104
virtual void redraw()
Redrawing function, called each time the action situation might have changed.
Definition: recruit.cpp:184
Abstract base class for all the visitors (cf GoF Visitor Design Pattern) the whiteboard uses.
Definition: visitor.hpp:33
virtual void visit(move_ptr move)=0
@ actions_numbering
Move numbering for the whiteboard.
const config * cfg
const std::vector< std::string > & recruits() const
The type IDs of the other units this unit may recruit, if possible.
Definition: unit.hpp:639
const std::string unicode_minus
Definition: constants.cpp:42
game_board * gameboard
Definition: resources.cpp:20
fake_unit_manager * fake_units
Definition: resources.cpp:30
play_controller * controller
Definition: resources.cpp:21
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:87
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:141
Definition: display.hpp:45
std::shared_ptr< recruit > recruit_ptr
Definition: typedefs.hpp:72
unit * find_recruiter(std::size_t team_index, const map_location &hex)
Definition: utility.cpp:70
std::shared_ptr< recruit const > recruit_const_ptr
Definition: typedefs.hpp:73
side_actions_ptr viewer_actions()
Definition: utility.cpp:42
bool any_recruiter(int team_num, const map_location &loc, const std::function< bool(unit &)> &func)
executes func for each unti of side of side_num that can recruit on loc.
Definition: utility.cpp:83
std::ostream & operator<<(std::ostream &s, const action_ptr &action)
Definition: action.cpp:34
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
static config unit_name(const unit *u)
Definition: reports.cpp:163
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:61
Encapsulates the map of the game.
Definition: location.hpp:46
int wml_y() const
Definition: location.hpp:187
int wml_x() const
Definition: location.hpp:186
static map_location::direction s
#define DBG_WB
Definition: typedefs.hpp:28
unit_type_data unit_types
Definition: types.cpp:1463
visitor is an abstract interface : action.accept(visitor) calls visitor.visit(action)