The Battle for Wesnoth  1.19.7+dev
game_state.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2024
3  by Chris Beck <render787@gmail.com>
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 #include "game_state.hpp"
17 
18 #include "actions/undo.hpp"
19 #include "game_board.hpp"
20 #include "game_data.hpp"
21 #include "game_events/manager.hpp"
22 #include "log.hpp"
23 #include "map/map.hpp"
24 #include "pathfind/pathfind.hpp"
25 #include "pathfind/teleport.hpp"
26 #include "play_controller.hpp"
28 #include "random_deterministic.hpp"
29 #include "reports.hpp"
31 #include "synced_context.hpp"
32 #include "teambuilder.hpp"
33 #include "units/unit.hpp"
35 #include "side_controller.hpp"
36 
37 #include <algorithm>
38 #include <set>
39 
40 static lg::log_domain log_engine("engine");
41 #define LOG_NG LOG_STREAM(info, log_engine)
42 #define DBG_NG LOG_STREAM(debug, log_engine)
43 #define ERR_NG LOG_STREAM(err, log_engine)
44 
46  : gamedata_(level)
47  , board_(level)
48  , tod_manager_(level)
49  , pathfind_manager_(new pathfind::manager(level))
50  , reports_(new reports())
51  , lua_kernel_(new game_lua_kernel(*this, pc, *reports_))
52  , ai_manager_()
53  , events_manager_(new game_events::manager())
54  , undo_stack_(new actions::undo_list())
55  , player_number_(level["playing_team"].to_int() + 1)
56  , next_player_number_(level["next_player_number"].to_int(player_number_ + 1))
57  , do_healing_(level["do_healing"].to_bool(false))
58  , victory_when_enemies_defeated_(level["victory_when_enemies_defeated"].to_bool(true))
59  , remove_from_carryover_on_defeat_(level["remove_from_carryover_on_defeat"].to_bool(true))
60  , server_request_number_(level["server_request_number"].to_int())
61 {
62  lua_kernel_->load_core();
63  if(auto endlevel_cfg = level.optional_child("end_level_data")) {
64  end_level_data el_data;
65  el_data.read(*endlevel_cfg);
66  el_data.transient.carryover_report = false;
67  end_level_data_ = el_data;
68  }
69 }
70 
72 
73 static int placing_score(const config& side, const gamemap& map, const map_location& pos)
74 {
75  int positions = 0, liked = 0;
76  const t_translation::ter_list terrain = t_translation::read_list(side["terrain_liked"].str());
77 
78  for(int i = -8; i != 8; ++i) {
79  for(int j = -8; j != +8; ++j) {
80  const map_location pos2 = pos.plus(i, j);
81  if(map.on_board(pos2)) {
82  ++positions;
83  if(std::count(terrain.begin(),terrain.end(),map[pos2])) {
84  ++liked;
85  }
86  }
87  }
88  }
89 
90  return (100*liked)/positions;
91 }
92 
93 struct placing_info {
94 
96  side(0),
97  score(0),
98  pos()
99  {
100  }
101 
102  int side, score;
104 };
105 
106 static bool operator<(const placing_info& a, const placing_info& b) { return a.score > b.score; }
107 
108 
110 {
111  std::vector<placing_info> placings;
112 
113  int num_pos = board_.map().num_valid_starting_positions();
114 
115  int side_num = 1;
116  for(const config &side : level.child_range("side"))
117  {
118  for(int p = 1; p <= num_pos; ++p) {
119  const map_location& pos = board_.map().starting_position(p);
120  int score = placing_score(side, board_.map(), pos);
121  placing_info obj;
122  obj.side = side_num;
123  obj.score = score;
124  obj.pos = pos;
125  placings.push_back(obj);
126  }
127  ++side_num;
128  }
129 
130  std::stable_sort(placings.begin(),placings.end());
131  std::set<int> placed;
132  std::set<map_location> positions_taken;
133 
134  for (std::vector<placing_info>::const_iterator i = placings.begin(); i != placings.end() && static_cast<int>(placed.size()) != side_num - 1; ++i) {
135  if(placed.count(i->side) == 0 && positions_taken.count(i->pos) == 0) {
136  placed.insert(i->side);
137  positions_taken.insert(i->pos);
138  board_.map().set_starting_position(i->side,i->pos);
139  LOG_NG << "placing side " << i->side << " at " << i->pos;
140  }
141  }
142 }
143 
145 {
146  events_manager_->read_scenario(level, *lua_kernel_);
148  if (level["modify_placing"].to_bool()) {
149  LOG_NG << "modifying placing...";
151  }
152 
153  LOG_NG << "initialized time of day regions... " << pc.timer();
154  for (const config &t : level.child_range("time_area")) {
156  }
157 
158  LOG_NG << "initialized teams... " << pc.timer();
159 
160  board_.teams().resize(level.child_count("side"));
161  if (player_number_ != 1 && player_number_ > static_cast<int>(board_.teams().size())) {
162  ERR_NG << "invalid player number " << player_number_ << " #sides=" << board_.teams().size();
163  player_number_ = 1;
164  // in case there are no teams, using player_number_ migh still cause problems later.
165  }
166 
167  std::vector<team_builder> team_builders;
168 
169  // Note this isn't strictly necessary since team_builder declares a move constructor which will
170  // be used if a copy is needed (see the class documentation for why a copy causes crashes), but
171  // it can't hurt to be doubly safe.
172  team_builders.reserve(board_.teams().size());
173 
174  int team_num = 0;
175  for (const config &side : level.child_range("side"))
176  {
177  ++team_num;
178 
179  team_builders.emplace_back(side, board_.get_team(team_num), level, board_, team_num);
180  team_builders.back().build_team_stage_one();
181  }
182 
183  //Initialize the lua kernel before the units are created.
184  lua_kernel_->initialize(level);
185 
186  {
187  //sync traits of start units and the random start time.
189 
191 
192  undo_stack_->read(level.child_or_empty("undo_stack"), player_number_);
193 
194  for(team_builder& tb : team_builders) {
195  tb.build_team_stage_two();
196  }
197  for(team_builder& tb : team_builders) {
198  tb.build_team_stage_three();
199  }
200 
201  for(const team& t : board_.teams()) {
202  // Labels from players in your ignore list default to hidden
203  if(prefs::get().is_ignored(t.current_player())) {
204  std::string label_cat = "side:" + std::to_string(t.side());
205  board_.hidden_label_categories().push_back(label_cat);
206  }
207  }
208  }
209 }
210 
212 {
213  lua_kernel_->set_game_display(gd);
214 }
215 
216 void game_state::write(config& cfg) const
217 {
218  // dont write this before we fired the (pre)start events
219  // This is the case for the 'replay_start' part of the savegame.
221  cfg["playing_team"] = player_number_ - 1;
222  cfg["next_player_number"] = next_player_number_;
223  }
224  cfg["server_request_number"] = server_request_number_;
225  cfg["do_healing"] = do_healing_;
226  cfg["victory_when_enemies_defeated"] = victory_when_enemies_defeated_;
227  cfg["remove_from_carryover_on_defeat"] = remove_from_carryover_on_defeat_;
228  //Call the lua save_game functions
229  lua_kernel_->save_game(cfg);
230 
231  //Write the game events.
232  events_manager_->write_events(cfg);
233 
234  //Write the map, unit_map, and teams info
235  board_.write_config(cfg);
236 
237  //Write the tod manager, and time areas
239 
240  //write out the current state of the map
241  cfg.merge_with(pathfind_manager_->to_config());
242 
243  //Write the game data, including wml vars
245 
246  // Preserve the undo stack so that fog/shroud clearing is kept accurate.
247  undo_stack_->write(cfg.add_child("undo_stack"));
248 
249  if(end_level_data_) {
250  end_level_data_->write(cfg.add_child("end_level_data"));
251  }
252 }
253 
254 namespace {
255  struct castle_cost_calculator : pathfind::cost_calculator
256  {
257  castle_cost_calculator(const gamemap& map, const team & view_team) :
258  map_(map),
259  viewer_(view_team),
260  use_shroud_(view_team.uses_shroud())
261  {}
262 
263  virtual double cost(const map_location& loc, const double) const
264  {
265  if(!map_.is_castle(loc))
266  return 10000;
267 
268  if ( use_shroud_ && viewer_.shrouded(loc) )
269  return 10000;
270 
271  return 1;
272  }
273 
274  private:
275  const gamemap& map_;
276  const team& viewer_;
277  const bool use_shroud_; // Allows faster checks when shroud is disabled.
278  };
279 }//anonymous namespace
280 
281 
282 /**
283  * Checks to see if a leader at @a leader_loc could recruit somewhere.
284  * This takes into account terrain, shroud (for side @a side), and the presence
285  * of visible units.
286  * The behavior for an invalid @a side is subject to change for future needs.
287  */
288 bool game_state::can_recruit_from(const map_location& leader_loc, int side) const
289 {
290  const gamemap& map = board_.map();
291 
292  if(!map.is_keep(leader_loc)) {
293  return false;
294  }
295 
296  try {
297  return pathfind::find_vacant_tile(leader_loc, pathfind::VACANT_CASTLE, nullptr, &board_.get_team(side))
299  } catch(const std::out_of_range&) {
300  // Invalid side specified.
301  // Currently this cannot happen, but it could conceivably be used in
302  // the future to request that shroud and visibility be ignored. Until
303  // that comes to pass, just return.
304  return false;
305  }
306 }
307 
308 bool game_state::can_recruit_from(const unit& leader) const
309 {
310  return can_recruit_from(leader.get_location(), leader.side());
311 }
312 
313 
314 /**
315  * Checks to see if a leader at @a leader_loc could recruit on @a recruit_loc.
316  * This takes into account terrain, shroud (for side @a side), and whether or
317  * not there is already a visible unit at recruit_loc.
318  * The behavior for an invalid @a side is subject to change for future needs.
319  */
320 bool game_state::can_recruit_on(const map_location& leader_loc, const map_location& recruit_loc, int side) const
321 {
322  const gamemap& map = board_.map();
323 
324  if(!map.is_castle(recruit_loc)) {
325  return false;
326  }
327 
328  if(!map.is_keep(leader_loc)) {
329  return false;
330  }
331 
332  try {
333  const team& view_team = board_.get_team(side);
334 
335  if(view_team.shrouded(recruit_loc)) {
336  return false;
337  }
338 
339  if(board_.has_visible_unit(recruit_loc, view_team)) {
340  return false;
341  }
342 
343  castle_cost_calculator calc(map, view_team);
344 
345  // The limit computed in the third argument is more than enough for
346  // any convex castle on the map. Strictly speaking it could be
347  // reduced to sqrt(map.w()**2 + map.h()**2).
349  pathfind::a_star_search(leader_loc, recruit_loc, map.w() + map.h(), calc, map.w(), map.h());
350 
351  return !rt.steps.empty();
352  } catch(const std::out_of_range&) {
353  // Invalid side specified.
354  // Currently this cannot happen, but it could conceivably be used in
355  // the future to request that shroud and visibility be ignored. Until
356  // that comes to pass, just return.
357  return false;
358  }
359 }
360 
361 bool game_state::can_recruit_on(const unit& leader, const map_location& recruit_loc) const
362 {
363  return can_recruit_on(leader.get_location(), recruit_loc, leader.side());
364 }
365 
367 {
368  unit_map::const_iterator leader = board_.units().find(hex);
369  if ( leader != board_.units().end() ) {
370  return leader->can_recruit() && leader->side() == side && can_recruit_from(*leader);
371  } else {
372  // Look for a leader who can recruit on last_hex.
373  for ( leader = board_.units().begin(); leader != board_.units().end(); ++leader) {
374  if ( leader->can_recruit() && leader->side() == side && can_recruit_on(*leader, hex) ) {
375  return true;
376  }
377  }
378  }
379  // No leader found who can recruit at last_hex.
380  return false;
381 }
382 
384 {
385  return this->events_manager_->wml_menu_items();
386 }
387 
389 {
390  return this->events_manager_->wml_menu_items();
391 }
392 
394 {
395  return !gamedata_.next_scenario().empty() && gamedata_.next_scenario() != "null";
396 }
397 
398 namespace
399 {
400  //not really a 'choice' we just need to make sure to inform the server about this.
401 class add_side_wml_choice : public synced_context::server_choice
402 {
403 public:
404  add_side_wml_choice()
405  {
406  }
407 
408  /** We are in a game with no mp server and need to do this choice locally */
409  virtual config local_choice() const
410  {
411  return config{};
412  }
413 
414  /** The request which is sent to the mp server. */
415  virtual config request() const
416  {
417  return config{};
418  }
419 
420  virtual const char* name() const
421  {
422  return "add_side_wml";
423  }
424 
425 private:
426 };
427 } // end anon namespace
428 
429 
431 {
432  cfg["side"] = board_.teams().size() + 1;
433  //if we want to also allow setting the controller we must update the server code.
434  cfg["controller"] = side_controller::none;
435  //TODO: is this it? are there caches which must be cleared?
436  board_.teams().emplace_back();
437  board_.teams().back().build(cfg, board_.map(), cfg["gold"].to_int());
438  config choice = synced_context::ask_server_choice(add_side_wml_choice());
439 }
map_location loc
Definition: move.cpp:172
double t
Definition: astarsearch.cpp:63
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
void merge_with(const config &c)
Merge config 'c' into this config, overwriting this config's values.
Definition: config.cpp:1123
config & add_child(config_key_type key)
Definition: config.cpp:440
bool has_visible_unit(const map_location &loc, const team &team, bool see_all=false) const
Definition: game_board.cpp:199
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:80
virtual const std::vector< std::string > & hidden_label_categories() const override
Definition: game_board.hpp:117
team & get_team(int i)
Definition: game_board.hpp:92
void write_config(config &cfg) const
Definition: game_board.cpp:381
virtual const unit_map & units() const override
Definition: game_board.hpp:107
virtual const gamemap & map() const override
Definition: game_board.hpp:97
@ PRELOAD
the preload [event] is fired next phase: PRESTART (normal game), TURN_STARTING_WAITING (reloaded game...
Definition: game_data.hpp:76
@ INITIAL
creating intitial [unit]s, executing toplevel [lua] etc.
Definition: game_data.hpp:73
const randomness::mt_rng & rng() const
Definition: game_data.hpp:67
void write_snapshot(config &cfg) const
Definition: game_data.cpp:130
const std::string & next_scenario() const
Definition: game_data.hpp:130
void write(config &cfg) const
Definition: game_state.cpp:216
utils::optional< end_level_data > end_level_data_
Definition: game_state.hpp:64
bool remove_from_carryover_on_defeat_
Definition: game_state.hpp:62
void add_side_wml(config cfg)
creates a new side during a game.
Definition: game_state.cpp:430
int player_number_
Definition: game_state.hpp:57
bool has_next_scenario() const
Checks whether this is not the last scenario (usually of a campaign)
Definition: game_state.cpp:393
int next_player_number_
Definition: game_state.hpp:58
int server_request_number_
Definition: game_state.hpp:66
bool can_recruit_from(const map_location &leader_loc, int side) const
Checks to see if a leader at leader_loc could recruit somewhere.
Definition: game_state.cpp:288
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:320
bool side_can_recruit_on(int side, map_location loc) const
Checks if any of the sides leaders can recruit at a location.
Definition: game_state.cpp:366
bool in_phase(game_data::PHASE phase) const
Definition: game_state.hpp:109
void set_game_display(game_display *)
Definition: game_state.cpp:211
std::unique_ptr< game_lua_kernel > lua_kernel_
Definition: game_state.hpp:48
game_state(const config &level, play_controller &)
Definition: game_state.cpp:45
std::unique_ptr< pathfind::manager > pathfind_manager_
Definition: game_state.hpp:46
const std::unique_ptr< actions::undo_list > undo_stack_
undo_stack_ is never nullptr.
Definition: game_state.hpp:56
void init(const config &level, play_controller &)
Definition: game_state.cpp:144
bool do_healing_
True if healing should be done at the beginning of the next side turn.
Definition: game_state.hpp:60
void place_sides_in_preferred_locations(const config &level)
Definition: game_state.cpp:109
game_board board_
Definition: game_state.hpp:44
const std::unique_ptr< game_events::manager > events_manager_
Definition: game_state.hpp:50
game_events::wmi_manager & get_wml_menu_items()
Definition: game_state.cpp:383
bool victory_when_enemies_defeated_
Definition: game_state.hpp:61
tod_manager tod_manager_
Definition: game_state.hpp:45
game_data gamedata_
Definition: game_state.hpp:43
int w() const
Effective map width.
Definition: map.hpp:50
void set_starting_position(int side, const map_location &loc)
Manipulate starting positions of the different sides.
Definition: map.cpp:380
int h() const
Effective map height.
Definition: map.hpp:53
map_location starting_position(int side) const
Definition: map.cpp:324
int num_valid_starting_positions() const
Counts the number of sides that have valid starting positions on this map.
Definition: map.cpp:335
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:385
Encapsulates the map of the game.
Definition: map.hpp:172
bool is_castle(const map_location &loc) const
Definition: map.cpp:70
bool is_keep(const map_location &loc) const
Definition: map.cpp:72
static void progress(loading_stage stage=loading_stage::none)
Report what is being loaded to the loading screen.
const auto & timer() const
static prefs & get()
RAII class to use rng_deterministic in the current scope.
virtual config request() const =0
The request which is sent to the mp server.
virtual const char * name() const =0
virtual config local_choice() const =0
We are in a game with no mp server and need to do this choice locally.
static config ask_server_choice(const server_choice &)
If we are in a mp game, ask the server, otherwise generate the answer ourselves.
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:75
bool shrouded(const map_location &loc) const
Definition: team.cpp:650
config to_config(const std::string &textdomain="") const
Definition: tod_manager.cpp:96
void add_time_area(const gamemap &map, const config &cfg)
Adds a new local time area from config, making it follow its own time-of-day sequence.
void resolve_random(randomness::rng &r)
handles random_start_time, should be called before the game starts.
Definition: tod_manager.cpp:67
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
This class represents a single unit of a specific type.
Definition: unit.hpp:133
std::size_t i
Definition: function.cpp:1029
static bool operator<(const placing_info &a, const placing_info &b)
Definition: game_state.cpp:106
static int placing_score(const config &side, const gamemap &map, const map_location &pos)
Definition: game_state.cpp:73
static lg::log_domain log_engine("engine")
#define ERR_NG
Definition: game_state.cpp:43
#define LOG_NG
Definition: game_state.cpp:41
int side() const
The side this unit belongs to.
Definition: unit.hpp:343
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1404
Standard logging facilities (interface).
Domain specific events.
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator &calc, const std::size_t width, const std::size_t height, const teleport_map *teleports, bool border)
@ VACANT_CASTLE
Definition: pathfind.hpp:39
map_location find_vacant_tile(const map_location &loc, VACANT_TILE_TYPE vacancy, const unit *pass_check, const team *shroud_check, const game_board *board)
Function that will find a location on the board that is as near to loc as possible,...
Definition: pathfind.cpp:54
rng * generator
This generator is automatically synced during synced context.
Definition: random.cpp:60
Unit and team statistics.
std::vector< terrain_code > ter_list
Definition: translation.hpp:77
ter_list read_list(std::string_view str, const ter_layer filler)
Reads a list of terrains from a string, when reading the.
This module contains various pathfinding functions and utilities.
Additional information on the game outcome which can be provided by WML.
void read(const config &cfg)
transient_end_level transient
Encapsulates the map of the game.
Definition: location.hpp:45
static const map_location & null_location()
Definition: location.hpp:102
map_location plus(int x_diff, int y_diff) const
Definition: location.hpp:190
virtual double cost(const map_location &loc, const double so_far) const =0
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:133
std::vector< map_location > steps
Definition: pathfind.hpp:135
map_location pos
Definition: game_state.cpp:103
bool carryover_report
Should a summary of the scenario outcome be displayed?
mock_party p
Various functions that implement the undoing (and redoing) of in-game commands.
#define b