The Battle for Wesnoth  1.13.4+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 - 2016 by David White <[email protected]>
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 "create.hpp"
21 
22 #include "move.hpp"
23 #include "undo.hpp"
24 #include "vision.hpp"
25 
26 #include "config.hpp"
27 #include "config_assign.hpp"
28 #include "filter_context.hpp"
29 #include "game_board.hpp"
30 #include "game_display.hpp"
31 #include "game_events/manager.hpp"
32 #include "game_events/pump.hpp"
33 #include "game_state.hpp"
34 #include "game_preferences.hpp"
35 #include "game_data.hpp"
36 #include "gettext.hpp"
37 #include "log.hpp"
38 #include "map/map.hpp"
39 #include "pathfind/pathfind.hpp"
40 #include "recall_list_manager.hpp"
41 #include "replay.hpp"
42 #include "replay_helper.hpp"
43 #include "resources.hpp"
44 #include "statistics.hpp"
45 #include "synced_checkup.hpp"
46 #include "synced_context.hpp"
47 #include "team.hpp"
48 #include "units/unit.hpp"
49 #include "units/udisplay.hpp"
50 #include "units/filter.hpp"
51 #include "variable.hpp"
52 #include "whiteboard/manager.hpp"
53 
54 #include <boost/scoped_ptr.hpp>
55 
56 static lg::log_domain log_engine("engine");
57 #define DBG_NG LOG_STREAM(debug, log_engine)
58 #define LOG_NG LOG_STREAM(info, log_engine)
59 #define ERR_NG LOG_STREAM(err, log_engine)
60 
61 namespace actions {
62 
63 const std::set<std::string> get_recruits(int side, const map_location &recruit_loc)
64 {
65  const team & current_team = (*resources::teams)[side -1];
66 
67  LOG_NG << "getting recruit list for side " << side << " at location " << recruit_loc << "\n";
68 
69  std::set<std::string> local_result;
70  std::set<std::string> global_result;
72  u_end = resources::units->end();
73 
74  bool leader_in_place = false;
75  bool allow_local = resources::gameboard->map().is_castle(recruit_loc);
76 
77 
78  // Check for a leader at recruit_loc (means we are recruiting from there,
79  // rather than to there).
80  unit_map::const_iterator find_it = resources::units->find(recruit_loc);
81  if ( find_it != u_end ) {
82  if ( find_it->can_recruit() && find_it->side() == side &&
83  resources::gameboard->map().is_keep(recruit_loc) )
84  {
85  // We have been requested to get the recruit list for this
86  // particular leader.
87  leader_in_place = true;
88  local_result.insert(find_it->recruits().begin(),
89  find_it->recruits().end());
90  }
91  else if ( find_it->is_visible_to_team(current_team, resources::gameboard->map(), false) )
92  {
93  // This hex is visibly occupied, so we cannot recruit here.
94  allow_local = false;
95  }
96  }
97 
98  if ( !leader_in_place ) {
99  // Check all leaders for their ability to recruit here.
100  for( ; u != u_end; ++u ) {
101  // Only consider leaders on this side.
102  if ( !(u->can_recruit() && u->side() == side) )
103  continue;
104 
105  // Check if the leader is on a connected keep.
106  if ( allow_local && dynamic_cast<game_state*>(resources::filter_con)->can_recruit_on(*u, recruit_loc) ) {
107  leader_in_place= true;
108  local_result.insert(u->recruits().begin(), u->recruits().end());
109  }
110  else if ( !leader_in_place )
111  global_result.insert(u->recruits().begin(), u->recruits().end());
112  }
113  }
114 
115  // Determine which result set to use.
116  std::set<std::string> & result = leader_in_place ? local_result : global_result;
117 
118  // Add the team-wide recruit list.
119  const std::set<std::string>& recruit_list = current_team.recruits();
120  result.insert(recruit_list.begin(), recruit_list.end());
121 
122  return result;
123 }
124 
125 
126 namespace { // Helpers for get_recalls()
127  /**
128  * Adds to @a result those units that @a leader (assumed a leader) can recall.
129  * If @a already_added is supplied, it contains the underlying IDs of units
130  * that can be skipped (because they are already in @a result), and the
131  * underlying ID of units added to @a result will be added to @a already_added.
132  */
133  void add_leader_filtered_recalls(const unit_const_ptr leader,
134  std::vector< unit_const_ptr > & result,
135  std::set<size_t> * already_added = nullptr)
136  {
137  const team& leader_team = (*resources::teams)[leader->side()-1];
138  const std::string& save_id = leader_team.save_id();
139 
140  const unit_filter ufilt(vconfig(leader->recall_filter()), resources::filter_con);
141 
142  for (const unit_const_ptr & recall_unit_ptr : leader_team.recall_list())
143  {
144  const unit & recall_unit = *recall_unit_ptr;
145  // Do not add a unit twice.
146  size_t underlying_id = recall_unit.underlying_id();
147  if ( !already_added || already_added->count(underlying_id) == 0 )
148  {
149  // Only units that match the leader's recall filter are valid.
150  scoped_recall_unit this_unit("this_unit", save_id, leader_team.recall_list().find_index(recall_unit.id()));
151 
152  if ( ufilt(recall_unit, map_location::null_location()) )
153  {
154  result.push_back(recall_unit_ptr);
155  if ( already_added != nullptr )
156  already_added->insert(underlying_id);
157  }
158  }
159  }
160  }
161 }// anonymous namespace
162 
163 std::vector<unit_const_ptr > get_recalls(int side, const map_location &recall_loc)
164 {
165  LOG_NG << "getting recall list for side " << side << " at location " << recall_loc << "\n";
166 
167  std::vector<unit_const_ptr > result;
168 
169  /*
170  * We have three use cases:
171  * 1. An empty castle tile is highlighted; we return only the units recallable there.
172  * 2. A leader on a keep is highlighted; we return only the units recallable by that leader.
173  * 3. Otherwise, we return all units in the recall list.
174  */
175 
176  bool leader_in_place = false;
177  bool allow_local = resources::gameboard->map().is_castle(recall_loc);
178 
179 
180  // Check for a leader at recall_loc (means we are recalling from there,
181  // rather than to there).
182  const unit_map::const_iterator find_it = resources::units->find(recall_loc);
183  if ( find_it != resources::units->end() ) {
184  if ( find_it->can_recruit() && find_it->side() == side &&
185  resources::gameboard->map().is_keep(recall_loc) )
186  {
187  // We have been requested to get the recalls for this
188  // particular leader.
189  add_leader_filtered_recalls(find_it.get_shared_ptr(), result);
190  return result;
191  }
192  else if ( find_it->is_visible_to_team((*resources::teams)[side-1], resources::gameboard->map(), false) )
193  {
194  // This hex is visibly occupied, so we cannot recall here.
195  allow_local = false;
196  }
197  }
198 
199  if ( allow_local )
200  {
202  u_end = resources::units->end();
203  std::set<size_t> valid_local_recalls;
204 
205  for(; u != u_end; ++u) {
206  //We only consider leaders on our side.
207  if (!(u->can_recruit() && u->side() == side))
208  continue;
209 
210  // Check if the leader is on a connected keep.
211  if ( !dynamic_cast<game_state*>(resources::filter_con)->can_recruit_on(*u, recall_loc) )
212  continue;
213  leader_in_place= true;
214 
215  add_leader_filtered_recalls(u.get_shared_ptr(), result, &valid_local_recalls);
216  }
217  }
218 
219  if ( !leader_in_place )
220  {
221  // Return the full recall list.
222  for (const unit_const_ptr & recall : (*resources::teams)[side-1].recall_list())
223  {
224  result.push_back(recall);
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::teams)[recaller.side()-1];
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 {
280  const unit_map & units = *resources::units;
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::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 don’t 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 ( !util::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 {
410  const unit_map & units = *resources::units;
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::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 ( util::contains((*resources::teams)[side-1].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 don’t 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_of("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  {
539  const unit_map & units = *resources::units;
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::units->find(current_loc);
568  if ( un_it == resources::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::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::teams)[unit_itor->side()-1].is_enemy(new_unit.side()) &&
588  unit_itor->is_visible_to_team((*resources::teams)[new_unit.side()-1], *map, 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, 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_all();
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::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  set_recruit_facing(new_unit_itor, *u, recruit_location, leader_loc);
636 
637  // Do some bookkeeping.
638  recruit_checksums(*u, wml_triggered);
639  resources::whiteboard->on_gamestate_change();
640 
641  resources::game_events->pump().fire("unit placed", current_loc);
642 
643  if ( fire_event ) {
644  const std::string event_name = is_recall ? "prerecall" : "prerecruit";
645  LOG_NG << "firing " << event_name << " event\n";
646  {
647  res.get<0>() |= resources::game_events->pump().fire(event_name, current_loc, recruited_from);
648  }
649  if ( !validate_recruit_iterator(new_unit_itor, current_loc) )
650  return true;
651  new_unit_itor->set_hidden(true);
652  }
653  preferences::encountered_units().insert(new_unit_itor->type_id());
654  (*resources::teams)[u->side()-1].spend_gold(cost);
655 
656  if ( show ) {
657  unit_display::unit_recruited(current_loc, leader_loc);
658  }
659  // Make sure the unit appears (if either !show or the animation is suppressed).
660  new_unit_itor->set_hidden(false);
661  if ( resources::screen != nullptr ) {
662  resources::screen->invalidate(current_loc);
664  }
665 
666  // Village capturing.
667  if ( resources::gameboard->map().is_village(current_loc) ) {
668  res.get<1>() = resources::gameboard->village_owner(current_loc) + 1;
669  res.get<0>() |= actions::get_village(current_loc, new_unit_itor->side(), &res.get<2>());
670  if ( !validate_recruit_iterator(new_unit_itor, current_loc) )
671  return true;
672  }
673 
674  // Fog clearing.
675  actions::shroud_clearer clearer;
676  if ( !wml_triggered ) // To preserve current WML behavior.
677  res.get<0>() |= clearer.clear_unit(current_loc, *new_unit_itor);
678 
679  if ( fire_event ) {
680  const std::string event_name = is_recall ? "recall" : "recruit";
681  LOG_NG << "firing " << event_name << " event\n";
682  {
683  res.get<0>() |= resources::game_events->pump().fire(event_name, current_loc, recruited_from);
684  }
685  }
686 
687  // "sighted" event(s).
688  res.get<0>() |= clearer.fire_events();
689  if ( new_unit_itor.valid() )
690  res.get<0>() |= actions::actor_sighted(*new_unit_itor);
691 
692  return res;
693 }
694 
695 
696 /**
697  * Recruits a unit of the given type for the given side.
698  */
699 void recruit_unit(const unit_type & u_type, int side_num, const map_location & loc,
700  const map_location & from, bool show, bool use_undo)
701 {
702  const unit_ptr new_unit = unit_ptr( new unit(u_type, side_num, true));
703 
704 
705  // Place the recruit.
706  place_recruit_result res = place_recruit(new_unit, loc, from, u_type.cost(), false, show);
707  statistics::recruit_unit(*new_unit);
708 
709  // To speed things a bit, don't bother with the undo stack during
710  // an AI turn. The AI will not undo nor delay shroud updates.
711  // (Undo stack processing is also suppressed when redoing a recruit.)
712  if ( use_undo ) {
713  resources::undo_stack->add_recruit(new_unit, loc, from, res.get<1>(), res.get<2>());
714  // Check for information uncovered or randomness used.
715 
716  if ( res.get<0>() || !synced_context::can_undo()) {
718  }
719  }
720 
721  // Update the screen.
722  if ( resources::screen != nullptr )
724  // Other updates were done by place_recruit().
725 }
726 
727 
728 /**
729  * Recalls the unit with the indicated ID for the provided team.
730  */
731 bool recall_unit(const std::string & id, team & current_team,
732  const map_location & loc, const map_location & from,
733  bool show, bool use_undo)
734 {
735  unit_ptr recall = current_team.recall_list().extract_if_matches_id(id);
736 
737  if ( !recall )
738  return false;
739 
740 
741  // ** IMPORTANT: id might become invalid at this point!
742  // (Use recall.id() instead, if needed.)
743 
744  // Place the recall.
745  // We also check to see if a custom unit level recall has been set if not,
746  // we use the team's recall cost otherwise the unit's.
748  if (recall->recall_cost() < 0) {
749  res = place_recruit(recall, loc, from, current_team.recall_cost(),
750  true, show);
751  }
752  else {
753  res = place_recruit(recall, loc, from, recall->recall_cost(),
754  true, show);
755  }
756  statistics::recall_unit(*recall);
757 
758  // To speed things a bit, don't bother with the undo stack during
759  // an AI turn. The AI will not undo nor delay shroud updates.
760  // (Undo stack processing is also suppressed when redoing a recall.)
761  if ( use_undo ) {
762  resources::undo_stack->add_recall(recall, loc, from, res.get<1>(), res.get<2>());
763  if ( res.get<0>() || !synced_context::can_undo()) {
765  }
766  }
767 
768  // Update the screen.
769  if ( resources::screen != nullptr )
771  // Other updates were done by place_recruit().
772 
773  return true;
774 }
775 
776 
777 }//namespace actions
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:226
const std::string & id() const
Definition: unit.hpp:148
place_recruit_result place_recruit(unit_ptr u, const map_location &recruit_location, const map_location &recruited_from, int cost, bool is_recall, bool show, bool fire_event, bool full_movement, bool wml_triggered)
Definition: create.cpp:610
unit_iterator end()
Definition: map.hpp:311
const map_location & get_location() const
Definition: unit.hpp:286
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:299
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:3536
Definition: unit.hpp:95
size_t underlying_id() const
The unique internal ID of the unit.
Definition: unit.hpp:150
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:331
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:121
No leaders exist.
Definition: create.hpp:41
Various functions implementing vision (through fog of war and shroud).
bool get_village(const map_location &loc, int side, bool *action_timebonus, bool fire_event)
Makes it so the village at the given location is owned by the given side.
Definition: move.cpp:140
const std::set< std::string > & recruits() const
Definition: team.hpp:228
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: util.hpp:489
game_display * screen
Definition: resources.cpp:27
bool actor_sighted(const unit &target, const std::vector< int > *cache)
Fires sighted events for the sides that can see target.
Definition: vision.cpp:623
unit_iterator begin()
Definition: map.hpp:308
Recruitment OK, but not at the specified location.
Definition: create.hpp:45
bool 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:471
#define DBG_NG
Definition: create.cpp:57
Replay control code.
DIRECTION get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:220
int side() const
Definition: unit.hpp:201
void write(config &cfg) const
Definition: unit.cpp:1379
RECRUIT_CHECK
The possible results of finding a location for recruiting (or recalling).
Definition: create.hpp:39
static config unit_type(const unit *u)
Definition: reports.cpp:161
-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:629
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:163
void recruit_unit(const unit &u)
Definition: statistics.cpp:480
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.
const std::vector< std::string > & recruits() const
Definition: unit.hpp:209
const std::string & type_id() const
The id of the type of the unit.
Definition: unit.hpp:142
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:50
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:699
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
int recall_cost() const
Definition: team.hpp:198
GLuint GLuint end
Definition: glew.h:1221
GLuint64EXT * result
Definition: glew.h:10727
std::vector< team > * teams
Definition: resources.cpp:29
pointer get_shared_ptr() const
Definition: map.hpp:180
bool valid() const
Definition: location.hpp:69
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:63
std::pair< unit_iterator, bool > insert(unit_ptr p)
Adds the unit to the map.
Definition: map.cpp:126
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:357
filter_context * filter_con
Definition: resources.cpp:23
int w() const
Effective map width.
Definition: map.hpp:105
game_board * gameboard
Definition: resources.cpp:20
#define LOG_NG
Definition: create.cpp:58
Encapsulates the map of the game.
Definition: map.hpp:37
bool can_recruit() const
Definition: unit.hpp:207
static DIRECTION get_opposite_dir(DIRECTION d)
Definition: location.hpp:163
checkup * checkup_instance
boost::tuple< bool, int, bool > place_recruit_result
Place a unit into the game.
Definition: create.hpp:151
#define ERR_NG
Definition: create.cpp:59
static bool can_undo()
static const map_location & null_location()
Definition: location.hpp:195
No vacant castle tiles around a leader on a keep.
Definition: create.hpp:44
game_events::manager * game_events
Definition: resources.cpp:24
GLuint GLuint GLsizei count
Definition: glew.h:1221
void pump()
Definition: events.cpp:336
const std::string & save_id() const
Definition: team.hpp:236
int cost() const
Definition: types.hpp:134
void show(CVideo &video, const std::string &window_id, const t_string &message, const tpoint &mouse)
Shows a tip.
Definition: tip.cpp:133
Encapsulates the map of the game.
Definition: location.hpp:38
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:200
static void process_error(const std::string &msg)
Definition: replay.cpp:198
Various functions related to the creation of units (recruits, recalls, and placed units)...
GLuint res
Definition: glew.h:9258
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:108
No able leaders are on a keep.
Definition: create.hpp:43
std::string get_checksum(const unit &u)
Gets a checksum for a unit.
Definition: unit.cpp:2511
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:191
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
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
std::set< std::string > & encountered_units()
bool is_castle(const map_location &loc) const
Definition: map.cpp:72
const config & recall_filter() const
Definition: unit.hpp:212
bool fire_event(const tevent event, std::vector< std::pair< twidget *, tevent > > &event_chain, twidget *dispatcher, twidget *widget, F functor)
Helper function for fire_event.
void recall_unit(const unit &u)
Definition: statistics.cpp:487
game_events::t_pump & pump()
Definition: manager.cpp:194
virtual const gamemap & map() const
Definition: game_board.hpp:98
bool fire_events()
Fires the sighted events that were earlier recorded by fog/shroud clearing.
Definition: vision.cpp:547
size_t find_index(const std::string &unit_id) const
Find the index of a unit by its id.
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:703
A variable-expanding proxy for the config class.
Definition: variable.hpp:36
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).
bool recall_unit(const std::string &id, team &current_team, const map_location &loc, const map_location &from, bool show, bool use_undo)
Recalls the unit with the indicated ID for the provided team.
Definition: create.cpp:731
recall_list_manager & recall_list()
Definition: team.hpp:220
Container associating units to locations.
Definition: map.hpp:90
Class to encapsulate fog/shroud clearing and the resultant sighted events.
Definition: vision.hpp:58
boost::shared_ptr< wb::manager > whiteboard
Definition: resources.cpp:36
static lg::log_domain log_engine("engine")
actions::undo_list * undo_stack
Definition: resources.cpp:34
unit_iterator find(size_t id)
Definition: map.cpp:285
bool valid() const
Definition: map.hpp:229
virtual bool local_checkup(const config &expected_data, config &real_data)=0
Compares data to the results calculated during the original game.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
This module contains various pathfinding functions and utilities.
GLsizei const GLcharARB ** string
Definition: glew.h:4503
unit_map * units
Definition: resources.cpp:35
Display units performing various actions: moving, attacking, and dying.
bool is_keep(const map_location &loc) const
Definition: map.cpp:74
No leaders able to recall/recruit the given unit/type.
Definition: create.hpp:42