The Battle for Wesnoth  1.17.0-dev
create.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2021
3  by David White <dave@whitevine.net>
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  * Recruiting, recalling.
19  */
20 
21 #include "actions/create.hpp"
22 
23 #include "actions/move.hpp"
24 #include "actions/undo.hpp"
25 #include "actions/vision.hpp"
26 
27 #include "config.hpp"
28 #include "display.hpp"
29 #include "filter_context.hpp"
30 #include "game_events/pump.hpp"
31 #include "game_state.hpp"
32 #include "preferences/game.hpp"
33 #include "game_data.hpp"
34 #include "gettext.hpp"
35 #include "log.hpp"
36 #include "map/map.hpp"
37 #include "pathfind/pathfind.hpp"
38 #include "recall_list_manager.hpp"
39 #include "replay.hpp"
40 #include "replay_helper.hpp"
41 #include "resources.hpp"
42 #include "statistics.hpp"
43 #include "synced_checkup.hpp"
44 #include "synced_context.hpp"
45 #include "team.hpp"
46 #include "units/unit.hpp"
47 #include "units/udisplay.hpp"
48 #include "units/filter.hpp"
49 #include "units/types.hpp"
50 #include "utils/general.hpp"
51 #include "variable.hpp"
52 #include "whiteboard/manager.hpp"
53 
54 static lg::log_domain log_engine("engine");
55 #define DBG_NG LOG_STREAM(debug, log_engine)
56 #define LOG_NG LOG_STREAM(info, log_engine)
57 #define ERR_NG LOG_STREAM(err, log_engine)
58 
59 namespace actions {
60 
61 const std::set<std::string> get_recruits(int side, const map_location &recruit_loc)
62 {
63  const team & current_team = resources::gameboard->get_team(side);
64 
65  LOG_NG << "getting recruit list for side " << side << " at location " << recruit_loc << "\n";
66 
67  std::set<std::string> local_result;
68  std::set<std::string> global_result;
70  u_end = resources::gameboard->units().end();
71 
72  bool leader_in_place = false;
73  bool allow_local = resources::gameboard->map().is_castle(recruit_loc);
74 
75 
76  // Check for a leader at recruit_loc (means we are recruiting from there,
77  // rather than to there).
78  unit_map::const_iterator find_it = resources::gameboard->units().find(recruit_loc);
79  if ( find_it != u_end ) {
80  if ( find_it->can_recruit() && find_it->side() == side &&
81  resources::gameboard->map().is_keep(recruit_loc) )
82  {
83  // We have been requested to get the recruit list for this
84  // particular leader.
85  leader_in_place = true;
86  local_result.insert(find_it->recruits().begin(),
87  find_it->recruits().end());
88  }
89  else if ( find_it->is_visible_to_team(current_team, false) )
90  {
91  // This hex is visibly occupied, so we cannot recruit here.
92  allow_local = false;
93  }
94  }
95 
96  if ( !leader_in_place ) {
97  // Check all leaders for their ability to recruit here.
98  for( ; u != u_end; ++u ) {
99  // Only consider leaders on this side.
100  if ( !(u->can_recruit() && u->side() == side) )
101  continue;
102 
103  // Check if the leader is on a connected keep.
104  if (allow_local && dynamic_cast<game_state&>(*resources::filter_con).can_recruit_on(*u, recruit_loc)) {
105  leader_in_place= true;
106  local_result.insert(u->recruits().begin(), u->recruits().end());
107  }
108  else if ( !leader_in_place )
109  global_result.insert(u->recruits().begin(), u->recruits().end());
110  }
111  }
112 
113  // Determine which result set to use.
114  std::set<std::string> & result = leader_in_place ? local_result : global_result;
115 
116  // Add the team-wide recruit list.
117  const std::set<std::string>& recruit_list = current_team.recruits();
118  result.insert(recruit_list.begin(), recruit_list.end());
119 
120  return result;
121 }
122 
123 
124 namespace { // Helpers for get_recalls()
125  /**
126  * Adds to @a result those units that @a leader (assumed a leader) can recall.
127  * If @a already_added is supplied, it contains the underlying IDs of units
128  * that can be skipped (because they are already in @a result), and the
129  * underlying ID of units added to @a result will be added to @a already_added.
130  */
131  void add_leader_filtered_recalls(const unit_const_ptr leader,
132  std::vector< unit_const_ptr > & result,
133  std::set<std::size_t> * already_added = nullptr)
134  {
135  const team& leader_team = resources::gameboard->get_team(leader->side());
136  const std::string& save_id = leader_team.save_id_or_number();
137 
138  const unit_filter ufilt(vconfig(leader->recall_filter()));
139 
140  for (const unit_const_ptr recall_unit_ptr : leader_team.recall_list())
141  {
142  const unit & recall_unit = *recall_unit_ptr;
143  // Do not add a unit twice.
144  std::size_t underlying_id = recall_unit.underlying_id();
145  if ( !already_added || already_added->count(underlying_id) == 0 )
146  {
147  // Only units that match the leader's recall filter are valid.
148  scoped_recall_unit this_unit("this_unit", save_id, leader_team.recall_list().find_index(recall_unit.id()));
149 
150  if ( ufilt(recall_unit, map_location::null_location()) )
151  {
152  result.push_back(recall_unit_ptr);
153  if ( already_added != nullptr )
154  already_added->insert(underlying_id);
155  }
156  }
157  }
158  }
159 }// anonymous namespace
160 
161 std::vector<unit_const_ptr > get_recalls(int side, const map_location &recall_loc)
162 {
163  LOG_NG << "getting recall list for side " << side << " at location " << recall_loc << "\n";
164 
165  std::vector<unit_const_ptr > result;
166 
167  /*
168  * We have three use cases:
169  * 1. An empty castle tile is highlighted; we return only the units recallable there.
170  * 2. A leader on a keep is highlighted; we return only the units recallable by that leader.
171  * 3. Otherwise, we return all units in the recall list that can be recalled by any leader on the map.
172  */
173 
174  bool leader_in_place = false;
175  bool allow_local = resources::gameboard->map().is_castle(recall_loc);
176 
177 
178  // Check for a leader at recall_loc (means we are recalling from there,
179  // rather than to there).
180  const unit_map::const_iterator find_it = resources::gameboard->units().find(recall_loc);
181  if ( find_it != resources::gameboard->units().end() ) {
182  if ( find_it->can_recruit() && find_it->side() == side &&
183  resources::gameboard->map().is_keep(recall_loc) )
184  {
185  // We have been requested to get the recalls for this
186  // particular leader.
187  add_leader_filtered_recalls(find_it.get_shared_ptr(), result);
188  return result;
189  }
190  else if ( find_it->is_visible_to_team(resources::gameboard->get_team(side), false) )
191  {
192  // This hex is visibly occupied, so we cannot recall here.
193  allow_local = false;
194  }
195  }
196 
197  if ( allow_local )
198  {
200  u_end = resources::gameboard->units().end();
201  std::set<std::size_t> valid_local_recalls;
202 
203  for(; u != u_end; ++u) {
204  //We only consider leaders on our side.
205  if (!(u->can_recruit() && u->side() == side))
206  continue;
207 
208  // Check if the leader is on a connected keep.
209  if (!dynamic_cast<game_state&>(*resources::filter_con).can_recruit_on(*u, recall_loc))
210  continue;
211  leader_in_place= true;
212 
213  add_leader_filtered_recalls(u.get_shared_ptr(), result, &valid_local_recalls);
214  }
215  }
216 
217  if ( !leader_in_place )
218  {
219  std::set<std::size_t> valid_local_recalls;
220 
221  for(auto u = resources::gameboard->units().begin(); u != resources::gameboard->units().end(); ++u) {
222  //We only consider leaders on our side.
223  if(!u->can_recruit() || u->side() != side) {
224  continue;
225  }
226 
227  add_leader_filtered_recalls(u.get_shared_ptr(), result, &valid_local_recalls);
228  }
229  }
230 
231  return result;
232 }
233 
234 namespace { // Helpers for check_recall_location()
235  /**
236  * Checks if @a recaller can recall @a recall_unit at @a preferred.
237  * If recalling can occur but not at the preferred location, then a
238  * permissible location is stored in @a alternative.
239  * @returns the reason why recalling is not allowed (or RECRUIT_OK).
240  */
241  RECRUIT_CHECK check_unit_recall_location(
242  const unit & recaller, const unit & recall_unit,
243  const map_location & preferred, map_location & alternative)
244  {
245  // Make sure the unit can actually recall.
246  if ( !recaller.can_recruit() )
247  return RECRUIT_NO_LEADER;
248 
249  // Make sure the recalling unit can recall this specific unit.
250  team& recall_team = (*resources::gameboard).get_team(recaller.side());
251  scoped_recall_unit this_unit("this_unit", recall_team.save_id_or_number(),
252  recall_team.recall_list().find_index(recall_unit.id()));
253 
254  const unit_filter ufilt(vconfig(recaller.recall_filter()));
255  if ( !ufilt(recall_unit, map_location::null_location()) )
256  return RECRUIT_NO_ABLE_LEADER;
257 
258  // Make sure the unit is on a keep.
259  if ( !resources::gameboard->map().is_keep(recaller.get_location()) )
260  return RECRUIT_NO_KEEP_LEADER;
261 
262  // Make sure there is a permissible location to which to recruit.
263  map_location permissible = pathfind::find_vacant_castle(recaller);
264  if ( !permissible.valid() )
265  return RECRUIT_NO_VACANCY;
266 
267  // See if the preferred location cannot be used.
268  if (!dynamic_cast<game_state&>(*resources::filter_con).can_recruit_on(recaller, preferred)) {
269  alternative = permissible;
271  }
272 
273  // All tests passed.
274  return RECRUIT_OK;
275  }
276 }//anonymous namespace
277 
278 /** Checks if there is a location on which to recall @a unit_recall. */
279 RECRUIT_CHECK check_recall_location(const int side, map_location& recall_location,
280  map_location& recall_from,
281  const unit &unit_recall)
282 {
283  const unit_map & units = resources::gameboard->units();
284  const unit_map::const_iterator u_end = units.end();
285 
286  map_location check_location = recall_location;
287  map_location alternative; // Set by check_unit_recall_location().
288 
289  // If the specified location is occupied, proceed as if no location was specified.
290  if ( resources::gameboard->units().count(recall_location) != 0 )
291  check_location = map_location::null_location();
292 
293  // If the check location is not valid, we will never get an "OK" result.
294  RECRUIT_CHECK const goal_result = check_location.valid() ? RECRUIT_OK :
296  RECRUIT_CHECK best_result = RECRUIT_NO_LEADER;
297 
298  // Test the specified recaller (if there is one).
299  unit_map::const_iterator u = units.find(recall_from);
300  if ( u != u_end && u->side() == side ) {
301  best_result =
302  check_unit_recall_location(*u, unit_recall, check_location, alternative);
303  }
304 
305  // Loop through all units on the specified side.
306  for ( u = units.begin(); best_result < goal_result && u != u_end; ++u ) {
307  if ( u->side() != side )
308  continue;
309 
310  // Check this unit's viability as a recaller.
311  RECRUIT_CHECK current_result =
312  check_unit_recall_location(*u, unit_recall, check_location, alternative);
313 
314  // If this is not an improvement, proceed to the next unit.
315  if ( current_result <= best_result )
316  continue;
317  best_result = current_result;
318 
319  // If we have a viable recaller, record its location.
320  if ( current_result >= RECRUIT_ALTERNATE_LOCATION )
321  recall_from = u->get_location();
322  }
323 
324  if ( best_result == RECRUIT_ALTERNATE_LOCATION )
325  // Report the alternate location to the caller.
326  recall_location = alternative;
327 
328  return best_result;
329 }
330 
331 std::string find_recall_location(const int side, map_location& recall_location, map_location& recall_from, const unit &unit_recall)
332 {
333  LOG_NG << "finding recall location for side " << side << " and unit " << unit_recall.id() << "\n";
334 
335  // This function basically translates check_recall_location() to a
336  // human-readable string.
337  switch ( check_recall_location(side, recall_location, recall_from, unit_recall) )
338  {
339  case RECRUIT_NO_LEADER:
340  LOG_NG << "No leaders on side " << side << " when recalling " << unit_recall.id() << ".\n";
341  return _("You do not have a leader to recall with.");
342 
344  LOG_NG << "No leader is able to recall " << unit_recall.id() << " on side " << side << ".\n";
345  return _("None of your leaders are able to recall that unit.");
346 
348  LOG_NG << "No leader able to recall " << unit_recall.id() << " is on a keep.\n";
349  return _("You must have a leader on a keep who is able to recall that unit.");
350 
351  case RECRUIT_NO_VACANCY:
352  LOG_NG << "No vacant castle tiles around a keep are available for recalling " << unit_recall.id() << "; requested location is " << recall_location << ".\n";
353  return _("There are no vacant castle tiles in which to recall the unit.");
354 
356  case RECRUIT_OK:
357  return std::string();
358  }
359 
360  // We should never get down to here. But just in case someone decides to
361  // mess with the enum without updating this function:
362  ERR_NG << "Unrecognized enum in find_recall_location()" << std::endl;
363  return _("An unrecognized error has occurred.");
364 }
365 
366 namespace { // Helpers for check_recruit_location()
367  /**
368  * Checks if @a recruiter can recruit at @a preferred.
369  * If @a unit_type is not empty, it must be in the unit-specific recruit list.
370  * If recruitment can occur but not at the preferred location, then a
371  * permissible location is stored in @a alternative.
372  * @returns the reason why recruitment is not allowed (or RECRUIT_OK).
373  */
374  RECRUIT_CHECK check_unit_recruit_location(
375  const unit & recruiter, const std::string & unit_type,
376  const map_location & preferred, map_location & alternative)
377  {
378  // Make sure the unit can actually recruit.
379  if ( !recruiter.can_recruit() )
380  return RECRUIT_NO_LEADER;
381 
382  if ( !unit_type.empty() ) {
383  // Make sure the specified type is in the unit's recruit list.
384  if ( !utils::contains(recruiter.recruits(), unit_type) )
385  return RECRUIT_NO_ABLE_LEADER;
386  }
387 
388  // Make sure the unit is on a keep.
389  if ( !resources::gameboard->map().is_keep(recruiter.get_location()) )
390  return RECRUIT_NO_KEEP_LEADER;
391 
392  // Make sure there is a permissible location to which to recruit.
393  map_location permissible = pathfind::find_vacant_castle(recruiter);
394  if ( !permissible.valid() )
395  return RECRUIT_NO_VACANCY;
396 
397  // See if the preferred location cannot be used.
398  if (!dynamic_cast<game_state&>(*resources::filter_con).can_recruit_on(recruiter, preferred)) {
399  alternative = permissible;
401  }
402 
403  // All tests passed.
404  return RECRUIT_OK;
405  }
406 }//anonymous namespace
407 
408 /** Checks if there is a location on which to place a recruited unit. */
409 RECRUIT_CHECK check_recruit_location(const int side, map_location &recruit_location,
410  map_location& recruited_from,
411  const std::string& unit_type)
412 {
413  const unit_map & units = resources::gameboard->units();
414  const unit_map::const_iterator u_end = units.end();
415 
416  map_location check_location = recruit_location;
417  std::string check_type = unit_type;
418  map_location alternative; // Set by check_unit_recruit_location().
419 
420  // If the specified location is occupied, proceed as if no location was specified.
421  if ( resources::gameboard->units().count(recruit_location) != 0 )
422  check_location = map_location::null_location();
423 
424  // If the specified unit type is in the team's recruit list, there is no
425  // need to check each leader's list.
426  if ( utils::contains(resources::gameboard->get_team(side).recruits(), unit_type) )
427  check_type.clear();
428 
429  // If the check location is not valid, we will never get an "OK" result.
430  RECRUIT_CHECK const goal_result = check_location.valid() ? RECRUIT_OK :
432  RECRUIT_CHECK best_result = RECRUIT_NO_LEADER;
433 
434  // Test the specified recruiter (if there is one).
435  unit_map::const_iterator u = units.find(recruited_from);
436  if ( u != u_end && u->side() == side ) {
437  best_result =
438  check_unit_recruit_location(*u, check_type, check_location, alternative);
439  }
440 
441  // Loop through all units on the specified side.
442  for ( u = units.begin(); best_result < goal_result && u != u_end; ++u ) {
443  if ( u->side() != side )
444  continue;
445 
446  // Check this unit's viability as a recruiter.
447  RECRUIT_CHECK current_result =
448  check_unit_recruit_location(*u, check_type, check_location, alternative);
449 
450  // If this is not an improvement, proceed to the next unit.
451  if ( current_result <= best_result )
452  continue;
453  best_result = current_result;
454 
455  // If we have a viable recruiter, record its location.
456  if ( current_result >= RECRUIT_ALTERNATE_LOCATION )
457  recruited_from = u->get_location();
458  }
459 
460  if ( best_result == RECRUIT_ALTERNATE_LOCATION )
461  // Report the alternate location to the caller.
462  recruit_location = alternative;
463 
464  return best_result;
465 }
466 
467 std::string find_recruit_location(const int side, map_location& recruit_location, map_location& recruited_from, const std::string& unit_type)
468 {
469  LOG_NG << "finding recruit location for side " << side << "\n";
470 
471  // This function basically translates check_recruit_location() to a
472  // human-readable string.
473  switch ( check_recruit_location(side, recruit_location, recruited_from, unit_type) )
474  {
475  case RECRUIT_NO_LEADER:
476  LOG_NG << "No leaders on side " << side << " when recruiting '" << unit_type << "'.\n";
477  return _("You do not have a leader to recruit with.");
478 
480  LOG_NG << "No leader is able to recruit '" << unit_type << "' on side " << side << ".\n";
481  return _("None of your leaders are able to recruit this unit.");
482 
484  LOG_NG << "No leader able to recruit '" << unit_type << "' is on a keep.\n";
485  return _("You must have a leader on a keep who is able to recruit the unit.");
486 
487  case RECRUIT_NO_VACANCY:
488  LOG_NG << "No vacant castle tiles around a keep are available for recruiting '" << unit_type << "'; requested location is " << recruit_location << ".\n";
489  return _("There are no vacant castle tiles in which to recruit the unit.");
490 
492  case RECRUIT_OK:
493  return std::string();
494  }
495 
496  // We should never get down to here. But just in case someone decides to
497  // mess with the enum without updating this function:
498  ERR_NG << "Unrecognized enum in find_recruit_location()" << std::endl;
499  return _("An unrecognized error has occurred.");
500 }
501 
502 
503 namespace { // Helpers for place_recruit()
504  /**
505  * Performs a checksum check on a newly recruited/recalled unit.
506  */
507  void recruit_checksums(const unit &new_unit, bool wml_triggered)
508  {
509  if(wml_triggered)
510  {
511  return;
512  }
513  const std::string checksum = get_checksum(new_unit);
514  config original_checksum_config;
515 
516  bool checksum_equals = checkup_instance->local_checkup(config {"checksum", checksum},original_checksum_config);
517  if(!checksum_equals)
518  {
519  const std::string old_checksum = original_checksum_config["checksum"];
520  std::stringstream error_msg;
521  error_msg << "SYNC: In recruit " << new_unit.type_id() <<
522  ": has checksum " << checksum <<
523  " while datasource has checksum " << old_checksum << "\n";
524  if(old_checksum.empty())
525  {
526  error_msg << "Original result is \n" << original_checksum_config << "\n";
527  }
528  config cfg_unit1;
529  new_unit.write(cfg_unit1);
530  DBG_NG << cfg_unit1;
531  replay::process_error(error_msg.str());
532  }
533  }
534 
535  /**
536  * Locates a leader on side @a side who can recruit at @a recruit_location.
537  * A leader at @a recruited_from is chosen in preference to others.
538  */
539  const map_location & find_recruit_leader(int side,
540  const map_location &recruit_location, const map_location &recruited_from)
541  {
542  const unit_map & units = resources::gameboard->units();
543 
544  // See if the preferred location is an option.
545  unit_map::const_iterator leader = units.find(recruited_from);
546  if (leader != units.end() && leader->can_recruit() &&
547  leader->side() == side && dynamic_cast<game_state&>(*resources::filter_con).can_recruit_on(*leader, recruit_location))
548  return leader->get_location();
549 
550  // Check all units.
551  for (leader = units.begin(); leader != units.end(); ++leader)
552  if (leader->can_recruit() && leader->side() == side &&
553  dynamic_cast<game_state&>(*resources::filter_con).can_recruit_on(*leader, recruit_location))
554  return leader->get_location();
555 
556  // No usable leader found.
558  }
559 
560  /**
561  * Tries to make @a un_it valid, and updates @a current_loc.
562  * Used by place_recruit() after WML might have changed something.
563  * @returns true if the iterator was made valid.
564  */
565  bool validate_recruit_iterator(unit_map::iterator & un_it,
566  map_location & current_loc)
567  {
568  if ( !un_it.valid() ) {
569  // Maybe WML provided a replacement?
570  un_it = resources::gameboard->units().find(current_loc);
571  if ( un_it == resources::gameboard->units().end() )
572  // The unit is gone.
573  return false;
574  }
575  current_loc = un_it->get_location();
576  return true;
577  }
578 
579  void set_recruit_facing(unit_map::iterator &new_unit_itor, const unit &new_unit,
580  const map_location &recruit_loc, const map_location &leader_loc)
581  {
582  // Find closest enemy and turn towards it (level 2s count more than level 1s, etc.)
583  const gamemap *map = & resources::gameboard->map();
584  const unit_map & units = resources::gameboard->units();
585  unit_map::const_iterator unit_itor;
586  map_location min_loc;
587  int min_dist = INT_MAX;
588 
589  for ( unit_itor = units.begin(); unit_itor != units.end(); ++unit_itor ) {
590  if (resources::gameboard->get_team(unit_itor->side()).is_enemy(new_unit.side()) &&
591  unit_itor->is_visible_to_team(resources::gameboard->get_team(new_unit.side()), false)) {
592  int dist = distance_between(unit_itor->get_location(),recruit_loc) - unit_itor->level();
593  if (dist < min_dist) {
594  min_dist = dist;
595  min_loc = unit_itor->get_location();
596  }
597  }
598  }
599  if (min_dist < INT_MAX) {
600  // Face towards closest enemy
601  new_unit_itor->set_facing(recruit_loc.get_relative_dir(min_loc));
602  } else if (leader_loc != map_location::null_location()) {
603  // Face away from leader
604  new_unit_itor->set_facing(map_location::get_opposite_dir(recruit_loc.get_relative_dir(leader_loc)));
605  } else {
606  // Face towards center of map
607  const map_location center(map->w()/2, map->h()/2);
608  new_unit_itor->set_facing(recruit_loc.get_relative_dir(center));
609  }
610  }
611 }// anonymous namespace
612 //Used by recalls and recruits
613 place_recruit_result place_recruit(unit_ptr u, const map_location &recruit_location, const map_location& recruited_from,
614  int cost, bool is_recall, map_location::DIRECTION facing, bool show, bool fire_event, bool full_movement,
615  bool wml_triggered)
616 {
617  place_recruit_result res(false, 0, false);
618  LOG_NG << "placing new unit on location " << recruit_location << "\n";
619  if (full_movement) {
620  u->set_movement(u->total_movement(), true);
621  } else {
622  u->set_movement(0, true);
623  u->set_attacks(0);
624  }
625  if(!is_recall) {
626  u->heal_fully();
627  }
628  u->set_hidden(true);
629 
630  // Get the leader location before adding the unit to the board.
631  const map_location leader_loc = !show ? map_location::null_location() :
632  find_recruit_leader(u->side(), recruit_location, recruited_from);
633  u->set_location(recruit_location);
634 
635  // Add the unit to the board.
636  auto [new_unit_itor, success] = resources::gameboard->units().insert(u);
637  assert(success);
638 
639  map_location current_loc = recruit_location;
640 
641  if (facing == map_location::NDIRECTIONS) {
642  set_recruit_facing(new_unit_itor, *u, recruit_location, leader_loc);
643  } else {
644  new_unit_itor->set_facing(facing);
645  }
646 
647  // Do some bookkeeping.
648  team& current_team = resources::gameboard->get_team(u->side());
649  recruit_checksums(*u, wml_triggered);
650  resources::whiteboard->on_gamestate_change();
651 
652  resources::game_events->pump().fire("unit_placed", current_loc);
653  if(!new_unit_itor.valid()) {
654  return place_recruit_result { true, 0, false };
655  }
656 
657  if ( fire_event ) {
658  const std::string event_name = is_recall ? "prerecall" : "prerecruit";
659  LOG_NG << "firing " << event_name << " event\n";
660  {
661  std::get<0>(res) |= std::get<0>(resources::game_events->pump().fire(event_name, current_loc, recruited_from));
662  }
663  if ( !validate_recruit_iterator(new_unit_itor, current_loc) )
664  return std::tuple(true, 0, false);
665  new_unit_itor->set_hidden(true);
666  }
667  preferences::encountered_units().insert(new_unit_itor->type_id());
668  current_team.spend_gold(cost);
669 
670  if ( show ) {
671  unit_display::unit_recruited(current_loc, leader_loc);
672  }
673  // Make sure the unit appears (if either !show or the animation is suppressed).
674  new_unit_itor->set_hidden(false);
675  if (display::get_singleton() != nullptr ) {
676  display::get_singleton()->invalidate(current_loc);
678  }
679 
680  // Village capturing.
681  if ( resources::gameboard->map().is_village(current_loc) ) {
682  std::get<1>(res) = resources::gameboard->village_owner(current_loc);
683  std::get<0>(res) |= std::get<0>(actions::get_village(current_loc, new_unit_itor->side(), &std::get<2>(res)));
684  if ( !validate_recruit_iterator(new_unit_itor, current_loc) )
685  return std::tuple(true, 0, false);
686  }
687 
688  // Fog clearing.
689  actions::shroud_clearer clearer;
690  if ( !wml_triggered && current_team.auto_shroud_updates() ) // To preserve current WML behavior.
691  std::get<0>(res) |= clearer.clear_unit(current_loc, *new_unit_itor);
692 
693  if ( fire_event ) {
694  const std::string event_name = is_recall ? "recall" : "recruit";
695  LOG_NG << "firing " << event_name << " event\n";
696  {
697  std::get<0>(res) |= std::get<0>(resources::game_events->pump().fire(event_name, current_loc, recruited_from));
698  }
699  }
700 
701  // "sighted" event(s).
702  std::get<0>(res) |= std::get<0>(clearer.fire_events());
703  if ( new_unit_itor.valid() )
704  std::get<0>(res) |= std::get<0>(actions::actor_sighted(*new_unit_itor));
705 
706  return res;
707 }
708 
709 void recruit_unit(const unit_type & u_type, int side_num, const map_location & loc,
710  const map_location & from, bool show, bool use_undo)
711 {
712  const unit_ptr new_unit = unit::create(u_type, side_num, true);
713 
714 
715  // Place the recruit.
716  place_recruit_result res = place_recruit(new_unit, loc, from, u_type.cost(), false, map_location::NDIRECTIONS, show);
717  statistics::recruit_unit(*new_unit);
718 
719  // To speed things a bit, don't bother with the undo stack during
720  // an AI turn. The AI will not undo nor delay shroud updates.
721  // (Undo stack processing is also suppressed when redoing a recruit.)
722  if ( use_undo ) {
723  resources::undo_stack->add_recruit(new_unit, loc, from, std::get<1>(res), std::get<2>(res));
724  // Check for information uncovered or randomness used.
725 
726  if ( std::get<0>(res) || !synced_context::can_undo()) {
728  }
729  }
730 
731  // Update the screen.
732  if (display::get_singleton() != nullptr )
734  // Other updates were done by place_recruit().
735 }
736 
737 bool recall_unit(const std::string & id, team & current_team,
738  const map_location & loc, const map_location & from,
739  map_location::DIRECTION facing, bool show, bool use_undo)
740 {
741  unit_ptr recall = current_team.recall_list().extract_if_matches_id(id);
742 
743  if ( !recall )
744  return false;
745 
746 
747  // ** IMPORTANT: id might become invalid at this point!
748  // (Use recall.id() instead, if needed.)
749 
750  // Place the recall.
751  // We also check to see if a custom unit level recall has been set if not,
752  // we use the team's recall cost otherwise the unit's.
754  if (recall->recall_cost() < 0) {
755  res = place_recruit(recall, loc, from, current_team.recall_cost(),
756  true, facing, show);
757  }
758  else {
759  res = place_recruit(recall, loc, from, recall->recall_cost(),
760  true, facing, show);
761  }
762  statistics::recall_unit(*recall);
763 
764  // To speed things a bit, don't bother with the undo stack during
765  // an AI turn. The AI will not undo nor delay shroud updates.
766  // (Undo stack processing is also suppressed when redoing a recall.)
767  if ( use_undo ) {
768  resources::undo_stack->add_recall(recall, loc, from, std::get<1>(res), std::get<2>(res));
769  if ( std::get<0>(res) || !synced_context::can_undo()) {
771  }
772  }
773 
774  // Update the screen.
775  if (display::get_singleton() != nullptr )
777  // Other updates were done by place_recruit().
778 
779  return true;
780 }
781 
782 
783 }//namespace actions
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:221
bool is_keep(const map_location &loc) const
Definition: map.cpp:72
unit_iterator end()
Definition: map.hpp:429
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:92
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:296
virtual const unit_map & units() const override
Definition: game_board.hpp:112
DIRECTION get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:227
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
const std::string & type_id() const
The id of this unit&#39;s type.
Definition: unit.cpp:1801
bool is_castle(const map_location &loc) const
Definition: map.cpp:70
map_location find_vacant_castle(const unit &leader)
Wrapper for find_vacant_tile() when looking for a vacant castle tile near a leader.
Definition: pathfind.cpp:118
No leaders exist.
Definition: create.hpp:39
Various functions implementing vision (through fog of war and shroud).
umap_retval_pair_t insert(unit_ptr p)
Inserts the unit pointed to by p into the map.
Definition: map.cpp:134
bool can_recruit_on(const map_location &leader_loc, const map_location &recruit_loc, int side) const
Checks to see if a leader at leader_loc could recruit on recruit_loc.
Definition: game_state.cpp:352
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 const gamemap & map() const override
Definition: game_board.hpp:102
unit_iterator begin()
Definition: map.hpp:419
std::tuple< bool, int, bool > place_recruit_result
Definition: create.hpp:143
Recruitment OK, but not at the specified location.
Definition: create.hpp:43
#define DBG_NG
Definition: create.cpp:55
Replay control code.
std::string save_id_or_number() const
Definition: team.hpp:244
RECRUIT_CHECK
The possible results of finding a location for recruiting (or recalling).
Definition: create.hpp:37
static config unit_type(const unit *u)
Definition: reports.cpp:188
static std::string _(const char *str)
Definition: gettext.hpp:93
Definitions for the interface to Wesnoth Markup Language (WML).
void redraw_minimap()
Schedule the minimap to be redrawn.
Definition: display.hpp:625
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
std::vector< unit_const_ptr > get_recalls(int side, const map_location &recall_loc)
Gets the recallable units for a side, restricted by that side&#39;s leaders&#39; personal abilities to recall...
Definition: create.cpp:161
void recruit_unit(const unit &u)
Definition: statistics.cpp:647
static unit_ptr create(const config &cfg, bool use_traits=false, const vconfig *vcfg=nullptr)
Initializes a unit from a config.
Definition: unit.hpp:190
A single unit type that the player may recruit.
Definition: types.hpp:45
void write(config &cfg, bool write_all=true) const
Serializes the current unit metadata values.
Definition: unit.cpp:1416
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:76
bool clear_unit(const map_location &view_loc, team &view_team, std::size_t viewer_id, int sight_range, bool slowed, const movetype::terrain_costs &costs, const map_location &real_loc, const std::set< map_location > *known_units=nullptr, std::size_t *enemy_count=nullptr, std::size_t *friend_count=nullptr, move_unit_spectator *spectator=nullptr, bool instant=true)
Clears shroud (and fog) around the provided location for view_team based on sight_range, costs, and slowed.
Definition: vision.cpp:332
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:72
team & get_team(int i)
Definition: game_board.hpp:97
game_events::pump_result_t fire_events()
Fires the sighted events that were earlier recorded by fog/shroud clearing.
Definition: vision.cpp:553
const std::string & id() const
Gets this unit&#39;s id.
Definition: unit.hpp:371
int cost() const
Definition: types.hpp:175
int w() const
Effective map width.
Definition: map.hpp:50
const std::set< std::string > get_recruits(int side, const map_location &recruit_loc)
Gets the recruitable units from a side&#39;s leaders&#39; personal recruit lists who can recruit on or from a...
Definition: create.cpp:61
filter_context * filter_con
Definition: resources.cpp:24
const config & recall_filter() const
Gets the filter constraints upon which units this unit may recall, if able.
Definition: unit.hpp:643
bool valid() const
Definition: location.hpp:89
game_board * gameboard
Definition: resources.cpp:21
#define LOG_NG
Definition: create.cpp:56
Encapsulates the map of the game.
Definition: map.hpp:171
checkup * checkup_instance
#define ERR_NG
Definition: create.cpp:57
static bool can_undo()
map_display and display: classes which take care of displaying the map and game-data on the screen...
void spend_gold(const int amount)
Definition: team.hpp:220
No vacant castle tiles around a leader on a keep.
Definition: create.hpp:42
game_events::manager * game_events
Definition: resources.cpp:25
void pump()
Definition: events.cpp:473
int recall_cost() const
Definition: team.hpp:205
Encapsulates the map of the game.
Definition: location.hpp:38
bool auto_shroud_updates() const
Definition: team.hpp:351
Various functions related to moving units.
void add_recruit(const unit_const_ptr u, const map_location &loc, const map_location &from, int orig_village_owner, bool time_bonus)
Adds a recruit to the undo stack.
Definition: undo.cpp:195
unit_iterator find(std::size_t id)
Definition: map.cpp:310
std::shared_ptr< wb::manager > whiteboard
Definition: resources.cpp:34
static void process_error(const std::string &msg)
Definition: replay.cpp:196
Various functions related to the creation of units (recruits, recalls, and placed units)...
pointer get_shared_ptr() const
This is exactly the same as operator-> but it&#39;s slightly more readable, and can replace &*iter syntax...
Definition: map.hpp:218
bool fire_event(const ui_event event, std::vector< std::pair< widget *, ui_event >> &event_chain, widget *dispatcher, widget *w, F &&... params)
Helper function for fire_event.
RECRUIT_CHECK check_recall_location(const int side, map_location &recall_location, map_location &recall_from, const unit &unit_recall)
Checks if there is a location on which to recall unit_recall.
Definition: create.cpp:279
No able leaders are on a keep.
Definition: create.hpp:41
std::string get_checksum(const unit &u)
Gets a checksum for a unit.
Definition: unit.cpp:2683
void add_recall(const unit_const_ptr u, const map_location &loc, const map_location &from, int orig_village_owner, bool time_bonus)
Adds a recall to the undo stack.
Definition: undo.cpp:186
bool can_recruit() const
Whether this unit can recruit other units - ie, are they a leader unit.
Definition: unit.hpp:603
Define the game&#39;s event mechanism.
std::string find_recall_location(const int side, map_location &recall_location, map_location &recall_from, const unit &unit_recall)
Finds a location on which to recall unit_recall.
Definition: create.cpp:331
RECRUIT_CHECK check_recruit_location(const int side, map_location &recruit_location, map_location &recruited_from, const std::string &unit_type)
Checks if there is a location on which to place a recruited unit.
Definition: create.cpp:409
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:40
std::set< std::string > & encountered_units()
Definition: game.cpp:930
unit_ptr extract_if_matches_id(const std::string &unit_id, int *pos=nullptr)
Find a unit by id, and extract from this object if found.
void recall_unit(const unit &u)
Definition: statistics.cpp:654
pump_result_t fire(const std::string &event, const entity_location &loc1=entity_location::null_entity, const entity_location &loc2=entity_location::null_entity, const config &data=config())
Function to fire an event.
Definition: pump.cpp:481
const std::vector< std::string > & recruits() const
The type IDs of the other units this unit may recruit, if possible.
Definition: unit.hpp:615
static DIRECTION get_opposite_dir(DIRECTION d)
Definition: location.hpp:55
void recruit_unit(const unit_type &u_type, int side_num, const map_location &loc, const map_location &from, bool show, bool use_undo)
Recruits a unit of the given type for the given side.
Definition: create.cpp:709
std::size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
Definition: location.cpp:546
game_events::pump_result_t get_village(const map_location &loc, int side, bool *action_timebonus, bool fire_event)
Makes it so the village at the given location is owned by the given side.
Definition: move.cpp:140
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1346
void unit_recruited(const map_location &loc, const map_location &leader_loc)
Definition: udisplay.cpp:801
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
Various functions that implement the undoing (and redoing) of in-game commands.
std::string find_recruit_location(const int side, map_location &recruit_location, map_location &recruited_from, const std::string &unit_type)
Finds a location on which to place a unit.
Definition: create.cpp:467
Standard logging facilities (interface).
recall_list_manager & recall_list()
Definition: team.hpp:227
static const map_location & null_location()
Definition: location.hpp:81
Container associating units to locations.
Definition: map.hpp:98
Class to encapsulate fog/shroud clearing and the resultant sighted events.
Definition: vision.hpp:72
static lg::log_domain log_engine("engine")
int side() const
The side this unit belongs to.
Definition: unit.hpp:334
actions::undo_list * undo_stack
Definition: resources.cpp:33
game_events::wml_event_pump & pump()
Definition: manager.cpp:230
virtual bool local_checkup(const config &expected_data, config &real_data)=0
Compares data to the results calculated during the original game.
place_recruit_result place_recruit(unit_ptr u, const map_location &recruit_location, const map_location &recruited_from, int cost, bool is_recall, map_location::DIRECTION facing, bool show, bool fire_event, bool full_movement, bool wml_triggered)
Place a unit into the game.
Definition: create.cpp:613
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
bool valid() const
Definition: map.hpp:274
int h() const
Effective map height.
Definition: map.hpp:53
This module contains various pathfinding functions and utilities.
std::size_t underlying_id() const
This unit&#39;s unique internal ID.
Definition: unit.hpp:383
Display units performing various actions: moving, attacking, and dying.
std::size_t find_index(const std::string &unit_id) const
Find the index of a unit by its id.
No leaders able to recall/recruit the given unit/type.
Definition: create.hpp:40
const std::set< std::string > & recruits() const
Definition: team.hpp:235
game_events::pump_result_t actor_sighted(const unit &target, const std::vector< int > *cache)
Fires sighted events for the sides that can see target.
Definition: vision.cpp:629
void show(const std::string &window_id, const t_string &message, const point &mouse, const SDL_Rect &source_rect)
Shows a tip.
Definition: tooltip.cpp:140