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