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