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