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