The Battle for Wesnoth  1.17.0-dev
recall.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2021
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/recall.hpp"
21 
22 #include "whiteboard/manager.hpp"
24 #include "whiteboard/utility.hpp"
25 #include "whiteboard/visitor.hpp"
26 
27 #include "actions/create.hpp"
28 #include "display.hpp"
29 #include "fake_unit_manager.hpp"
30 #include "fake_unit_ptr.hpp"
31 #include "game_board.hpp"
32 #include "recall_list_manager.hpp"
33 #include "resources.hpp"
34 #include "replay_helper.hpp"
35 #include "statistics.hpp"
36 #include "synced_context.hpp"
37 #include "team.hpp"
38 #include "units/filter.hpp"
39 #include "units/unit.hpp"
41 
42 namespace wb
43 {
44 
45 std::ostream& operator<<(std::ostream& s, recall_ptr recall)
46 {
47  assert(recall);
48  return recall->print(s);
49 }
50 std::ostream& operator<<(std::ostream& s, recall_const_ptr recall)
51 {
52  assert(recall);
53  return recall->print(s);
54 }
55 
56 std::ostream& recall::print(std::ostream &s) const
57 {
58  s << "Recalling " << fake_unit_->name() << " [" << fake_unit_->id() << "] on hex " << recall_hex_;
59  return s;
60 }
61 
62 recall::recall(std::size_t team_index, bool hidden, const unit& u, const map_location& recall_hex)
63  : action(team_index,hidden)
64  , temp_unit_(u.clone())
65  , recall_hex_(recall_hex)
66  , fake_unit_(u.clone())
67  , original_mp_(0)
68  , original_ap_(0)
70 {
71  this->init();
72 }
73 
74 recall::recall(const config& cfg, bool hidden)
75  : action(cfg,hidden)
76  , temp_unit_()
77  , recall_hex_(cfg.child("recall_hex_")["x"],cfg.child("recall_hex_")["y"], wml_loc())
78  , fake_unit_()
79  , original_mp_(0)
80  , original_ap_(0)
82 {
83  // Construct and validate temp_unit_
84  std::size_t underlying_id = cfg["temp_unit_"];
85  for(const unit_ptr & recall_unit : resources::gameboard->teams().at(team_index()).recall_list())
86  {
87  if(recall_unit->underlying_id()==underlying_id)
88  {
90  break;
91  }
92  }
93  if(!temp_unit_.get()) {
94  throw action::ctor_err("recall: Invalid underlying_id");
95  }
96 
97  fake_unit_.reset(temp_unit_->clone()); //makes copy of temp_unit_
98 
99  this->init();
100 }
101 
103 {
104  fake_unit_->set_location(recall_hex_);
105  fake_unit_->set_movement(0, true);
106  fake_unit_->set_attacks(0);
107  fake_unit_->anim_comp().set_ghosted(false);
109 }
110 
112 {
113 }
114 
116 {
117  v.visit(shared_from_this());
118 }
119 
120 void recall::execute(bool& success, bool& complete)
121 {
122  team & current_team = resources::gameboard->teams().at(team_index());
123 
124  assert(valid());
125  assert(temp_unit_.get());
126  temporary_unit_hider const raii(*fake_unit_);
127  //Give back the spent gold so we don't get "not enough gold" message
128  int cost = current_team.recall_cost();
129  if (temp_unit_->recall_cost() > -1) {
130  cost=temp_unit_->recall_cost();
131  }
132  current_team.get_side_actions()->change_gold_spent_by(-cost);
133  bool const result = synced_context::run_and_throw("recall",
135  true,
136  true,
138 
139  if (!result) {
140  current_team.get_side_actions()->change_gold_spent_by(cost);
141  }
142  success = complete = result;
143 }
144 
146 {
147  assert(valid());
148 
149 
150  DBG_WB << "Inserting future recall " << temp_unit_->name() << " [" << temp_unit_->id()
151  << "] at position " << temp_unit_->get_location() << ".\n";
152 
153  //temporarily remove unit from recall list
154  unit_ptr it = resources::gameboard->teams().at(team_index()).recall_list().extract_if_matches_id(temp_unit_->id(), &original_recall_pos_);
155  assert(it);
156 
157  //Usually (temp_unit_ == it) is true here, but wml might have changed the original unit in which case not doing 'temp_unit_ = it' would result in a gamestate change.
158  temp_unit_ = it;
159  original_mp_ = temp_unit_->movement_left(true);
160  original_ap_ = temp_unit_->attacks_left(true);
161 
162  temp_unit_->set_movement(0, true);
163  temp_unit_->set_attacks(0);
164  temp_unit_->set_location(recall_hex_);
165 
166  //Add cost to money spent on recruits.
167  int cost = resources::gameboard->teams().at(team_index()).recall_cost();
168  if (it->recall_cost() > -1) {
169  cost = it->recall_cost();
170  }
171 
172  // Temporarily insert unit into unit_map
173  //unit map takes ownership of temp_unit
174  unit_map.insert(temp_unit_);
175 
176  resources::gameboard->teams().at(team_index()).get_side_actions()->change_gold_spent_by(cost);
177  // Update gold in top bar
179 }
180 
182 {
183  temp_unit_ = unit_map.extract(recall_hex_);
184  assert(temp_unit_.get());
185 
186  temp_unit_->set_movement(original_mp_, true);
187  temp_unit_->set_attacks(original_ap_);
188 
189  original_mp_ = 0;
190  original_ap_ = 0;
191  //Put unit back into recall list
193 }
194 
196 {
197  if (hex == recall_hex_)
198  {
199  const double x_offset = 0.5;
200  const double y_offset = 0.7;
201  //position 0,0 in the hex is the upper left corner
202  std::stringstream number_text;
203  unit &it = *get_unit();
204  int cost = statistics::un_recall_unit_cost(it);
205  if (cost < 0) {
206  number_text << font::unicode_minus << resources::gameboard->teams().at(team_index()).recall_cost();
207  }
208  else {
209  number_text << font::unicode_minus << cost;
210  }
211  std::size_t font_size = 16;
212  color_t color {255, 0, 0}; //red
214  number_text.str(), font_size, color, x_offset, y_offset);
215  }
216 }
217 
219 {
221 }
222 
224 {
225  //Check that destination hex is still free
226  if(resources::gameboard->units().find(recall_hex_) != resources::gameboard->units().end()) {
227  return LOCATION_OCCUPIED;
228  }
229  //Check that unit to recall is still in side's recall list
230  if( !resources::gameboard->teams()[team_index()].recall_list().find_if_matches_id(temp_unit_->id()) ) {
231  return UNIT_UNAVAILABLE;
232  }
233  //Check that there is still enough gold to recall this unit
234  if(resources::gameboard->teams()[team_index()].recall_cost() > resources::gameboard->teams()[team_index()].gold()) {
235  return NOT_ENOUGH_GOLD;
236  }
237  //Check that there is a leader available to recall this unit
238  bool has_recruiter = any_recruiter(team_index() + 1, get_recall_hex(), [&](unit& leader) {
239  const unit_filter ufilt(vconfig(leader.recall_filter()));
240  return ufilt(*temp_unit_, map_location::null_location());
241  });
242 
243  if(!has_recruiter) {
244  return NO_LEADER;
245  }
246 
247  return OK;
248 }
249 
250 /** @todo Find a better way to serialize unit_ because underlying_id isn't cutting it */
252 {
253  config final_cfg = action::to_config();
254 
255  final_cfg["type"] = "recall";
256  final_cfg["temp_unit_"] = static_cast<int>(temp_unit_->underlying_id());
257 // final_cfg["temp_cost_"] = temp_cost_; //Unnecessary
258 
259  config loc_cfg;
260  loc_cfg["x"]=recall_hex_.wml_x();
261  loc_cfg["y"]=recall_hex_.wml_y();
262  final_cfg.add_child("recall_hex_", std::move(loc_cfg));
263 
264  return final_cfg;
265 }
266 
267 void recall::do_hide() {fake_unit_->set_hidden(true);}
268 void recall::do_show() {fake_unit_->set_hidden(false);}
269 
270 } //end namespace wb
void reset()
Reset the internal unit pointer, and deregister from the manager.
std::shared_ptr< wb::side_actions > get_side_actions() const
get the whiteboard planned actions for this team
Definition: team.hpp:398
Move numbering for the whiteboard.
Definition: display.hpp:855
virtual void do_show()
Definition: recall.cpp:268
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:92
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:85
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:296
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:3026
This class represents a single unit of a specific type.
Definition: unit.hpp:121
fake_unit_ptr fake_unit_
Definition: recall.hpp:89
umap_retval_pair_t insert(unit_ptr p)
Inserts the unit pointed to by p into the map.
Definition: map.cpp:134
static config get_recall(const std::string &unit_id, const map_location &loc, const map_location &from)
virtual void do_hide()
Called by the non-virtual hide() and show(), respectively.
Definition: recall.cpp:267
int original_mp_
Definition: recall.hpp:91
void place_on_fake_unit_manager(fake_unit_manager *d)
Place this on manager&#39;s fake_units_ dequeue.
std::shared_ptr< recall const > recall_const_ptr
Definition: typedefs.hpp:75
virtual void execute(bool &success, bool &complete)
Output parameters: success: Whether or not to continue an execute-all after this execution complete: ...
Definition: recall.cpp:120
virtual void accept(visitor &v)
Definition: recall.cpp:115
bool recall_unit(const std::string &id, team &current_team, const map_location &loc, const map_location &from, map_location::DIRECTION facing, bool show, bool use_undo)
Recalls the unit with the indicated ID for the provided team.
Definition: create.cpp:737
virtual void remove_temp_modifier(unit_map &unit_map)
Removes the result of this action from the specified unit map.
Definition: recall.cpp:181
int wml_x() const
Definition: location.hpp:153
virtual void visit(move_ptr move)=0
virtual void apply_temp_modifier(unit_map &unit_map)
Applies temporarily the result of this action to the specified unit map.
Definition: recall.cpp:145
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
std::size_t team_index() const
Returns the index of the team that owns this action.
Definition: action.hpp:84
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: recall.cpp:195
bool valid()
Returns whether this action is valid or not.
Definition: action.hpp:135
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:72
static std::string at(const std::string &file, int line)
const std::string unicode_minus
Definition: constants.cpp:42
error
Possible errors.
Definition: action.hpp:106
int wml_y() const
Definition: location.hpp:154
const config & recall_filter() const
Gets the filter constraints upon which units this unit may recall, if able.
Definition: unit.hpp:643
int un_recall_unit_cost(const unit &u)
Definition: statistics.cpp:675
game_board * gameboard
Definition: resources.cpp:21
fake_unit_manager * fake_units
Definition: resources.cpp:31
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:1523
map_display and display: classes which take care of displaying the map and game-data on the screen...
map_location recall_hex_
Definition: recall.hpp:88
std::shared_ptr< recall > shared_from_this()
Definition: recall.hpp:76
int recall_cost() const
Definition: team.hpp:205
Encapsulates the map of the game.
Definition: location.hpp:38
Various functions related to the creation of units (recruits, recalls, and placed units)...
virtual error check_validity() const
Check the validity of the action.
Definition: recall.cpp:223
virtual std::ostream & print(std::ostream &s) const
Definition: recall.cpp:56
virtual void redraw()
Redrawing function, called each time the action situation might have changed.
Definition: recall.cpp:218
static map_location::DIRECTION s
map_location const get_recall_hex() const
Definition: recall.hpp:70
static void ignore_error_function(const std::string &message)
A function to be passed to run_in_synced_context to ignore the error.
void init()
Definition: recall.cpp:102
config & add_child(config_key_type key)
Definition: config.cpp:514
bool any_recruiter(int team_num, const map_location &loc, std::function< bool(unit &)> func)
executes func for each unti of side of side_num that can recruit on loc.
Definition: utility.cpp:93
static bool run_and_throw(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
bool hidden() const
Definition: action.hpp:64
#define DBG_WB
Definition: typedefs.hpp:28
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
unit_ptr extract(const map_location &loc)
Extracts a unit from the map.
Definition: map.cpp:267
virtual config to_config() const
Definition: recall.cpp:251
static const map_location & null_location()
Definition: location.hpp:81
Container associating units to locations.
Definition: map.hpp:98
virtual ~recall()
Definition: recall.cpp:111
std::ostream & operator<<(std::ostream &s, action_ptr action)
Definition: action.cpp:34
std::shared_ptr< recall > recall_ptr
Definition: typedefs.hpp:74
visitor is an abstract interface : action.accept(visitor) calls visitor.visit(action) ...
Abstract base class for all the whiteboard planned actions.
Definition: action.hpp:33
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
recall(std::size_t team_index, bool hidden, const unit &unit, const map_location &recall_hex)
Definition: recall.cpp:62
unit_ptr temp_unit_
Definition: recall.hpp:87
int original_ap_
Definition: recall.hpp:92
virtual config to_config() const
Constructs and returns a config object representing this object.
Definition: action.cpp:51
virtual unit_ptr get_unit() const
Definition: recall.hpp:65
int original_recall_pos_
Definition: recall.hpp:93
Definition: display.hpp:49
Abstract base class for all the visitors (cf GoF Visitor Design Pattern) the whiteboard uses...
Definition: visitor.hpp:32