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