The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
tod_manager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2016 by Eugen Jiresch
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 #include "tod_manager.hpp"
16 
17 #include "display_context.hpp"
18 #include "formula/string_utils.hpp"
19 #include "game_data.hpp"
20 #include "gettext.hpp"
21 #include "log.hpp"
22 #include "map/map.hpp"
23 #include "play_controller.hpp"
24 #include "random_new.hpp"
25 #include "units/unit.hpp"
26 #include "units/abilities.hpp"
27 #include "wml_exception.hpp"
28 #include "resources.hpp"
29 #include "config_assign.hpp"
30 
31 #include <boost/range/adaptors.hpp>
32 #include <boost/range/algorithm/copy.hpp>
33 #include "utils/functional.hpp"
34 
35 static lg::log_domain log_engine("engine");
36 #define LOG_NG LOG_STREAM(info, log_engine)
37 
38 tod_manager::tod_manager(const config& scenario_cfg):
39  currentTime_(0),
40  times_(),
41  areas_(),
42  turn_(scenario_cfg["turn_at"].to_int(1)),
43  num_turns_(scenario_cfg["turns"].to_int(-1)),
44  has_turn_event_fired_(!scenario_cfg["it_is_a_new_turn"].to_bool(true))
45 {
46  // ? : operator doesn't work in this case.
47  if (scenario_cfg["current_time"].to_int(-17403) == -17403)
48  random_tod_ = scenario_cfg["random_start_time"];
49  else
50  random_tod_ = false;
51 
52  time_of_day::parse_times(scenario_cfg,times_);
53  //We need to call parse_times before calculate_current_time because otherwise the first parameter will always be 0.
54  currentTime_ = calculate_current_time(times_.size(), turn_, scenario_cfg["current_time"].to_int(0), true);
55 
56 }
57 
59 {
60  if(this == &manager) {
61  return *this;
62  }
63 
64  currentTime_ = manager.currentTime_;
65  times_ = manager.times_;
66  areas_ = manager.areas_;
67 
68  turn_ = manager.turn_;
69  num_turns_ = manager.num_turns_;
70 
71  return *this;
72 }
73 
74 template <typename T>
75 struct greater
76 {
77  greater(T val) : value (val) {}
78  bool operator () (T v2) const
79  {
80  return value < v2;
81  }
82 
83  T value;
84 };
85 
87 {
88  //process the random_start_time string, which can be boolean yes/no true/false or a
89  //comma-separated string of integers >= 1 referring to the times_ array indices
90  std::vector<int> output;
91  boost::copy( utils::split(random_tod_.str())
92  | boost::adaptors::transformed(boost::bind(lexical_cast_default<int, std::string>, _1 , 0))
93  | boost::adaptors::filtered(greater<int>(0))
94  , std::back_inserter(output) );
95 
96  if(!output.empty())
97  {
98  int chosen = output[r.next_random() % output.size()];
99  currentTime_ = calculate_current_time(times_.size(), turn_, chosen, true);
100  r.next_random();
101  }
102  else if (random_tod_.to_bool(false))
103  {
105  }
106  random_tod_ = false;
107 }
109 {
110  config cfg;
111  cfg["turn_at"] = turn_;
112  cfg["turns"] = num_turns_;
113  cfg["current_time"] = currentTime_;
114  cfg["random_start_time"] = random_tod_;
115  cfg["it_is_a_new_turn"] = !has_turn_event_fired_;
116  std::vector<time_of_day>::const_iterator t;
117  for(t = times_.begin(); t != times_.end(); ++t) {
118  t->write(cfg.add_child("time"));
119  }
120  for(std::vector<area_time_of_day>::const_iterator i = areas_.begin(); i != areas_.end(); ++i) {
121  config& area = cfg.add_child("time_area");
122  // if no ranges, then use hexes to generate ranges
123  if(i->xsrc.empty() && i->ysrc.empty()) {
124  write_location_range(i->hexes, area);
125  } else {
126  area["x"] = i->xsrc;
127  area["y"] = i->ysrc;
128  }
129  for(t = i->times.begin(); t != i->times.end(); ++t) {
130  t->write(area.add_child("time"));
131  }
132  area["current_time"] = i->currentTime;
133  if (!i->id.empty())
134  area["id"] = i->id;
135  }
136  return cfg;
137 }
138 
140 {
142 }
143 
145  assert(index < static_cast<int>(areas_.size()) );
146  return areas_[index].currentTime;
147 }
148 
150 {
151  if ( loc != map_location::null_location() ) {
152  for ( std::vector<area_time_of_day>::const_reverse_iterator
153  i = areas_.rbegin(), i_end = areas_.rend(); i != i_end; ++i )
154  {
155  if (i->hexes.find(loc) != i->hexes.end())
156  return i->currentTime;
157  }
158  }
159 
160  return currentTime_;
161 }
162 
163 const std::vector<time_of_day>& tod_manager::times(const map_location& loc) const
164 {
165  if ( loc != map_location::null_location() ) {
166  for ( std::vector<area_time_of_day>::const_reverse_iterator
167  i = areas_.rbegin(), i_end = areas_.rend(); i != i_end; ++i )
168  {
169  if (i->hexes.find(loc) != i->hexes.end())
170  return i->times;
171  }
172  }
173 
174  return times_;
175 }
176 
177 const time_of_day& tod_manager::get_time_of_day(const map_location& loc, int n_turn) const
178 {
179  if(n_turn == 0)
180  n_turn = turn_;
181 
182  if ( loc != map_location::null_location() )
183  {
184  for ( std::vector<area_time_of_day>::const_reverse_iterator
185  i = areas_.rbegin(), i_end = areas_.rend(); i != i_end; ++i )
186  {
187  if (i->hexes.find(loc) != i->hexes.end())
188  return get_time_of_day_turn(i->times, n_turn, i->currentTime);
189  }
190  }
191 
192  return get_time_of_day_turn(times_, n_turn, currentTime_);
193 }
194 
195 const time_of_day tod_manager::get_illuminated_time_of_day(const unit_map & units, const gamemap & map, const map_location& loc, int for_turn) const
196 {
197  // get ToD ignoring illumination
198  time_of_day tod = get_time_of_day(loc, for_turn);
199 
200  if ( map.on_board_with_border(loc) )
201  {
202  // Now add terrain illumination.
203  const int terrain_light = map.get_terrain_info(loc).light_bonus(tod.lawful_bonus);
204 
205  std::vector<int> mod_list;
206  std::vector<int> max_list;
207  std::vector<int> min_list;
208  int most_add = 0;
209  int most_sub = 0;
210 
211  // Find the "illuminates" effects from units that can affect loc.
212  map_location locs[7];
213  locs[0] = loc;
214  get_adjacent_tiles(loc,locs+1);
215  for ( size_t i = 0; i != 7; ++i ) {
216  const unit_map::const_iterator itor = units.find(locs[i]);
217  if (itor != units.end() &&
218  itor->get_ability_bool("illuminates") &&
219  !itor->incapacitated())
220  {
221  unit_ability_list illum = itor->get_abilities("illuminates");
222  unit_abilities::effect illum_effect(illum, terrain_light, false);
223  const int unit_mod = illum_effect.get_composite_value();
224 
225  // Record this value.
226  mod_list.push_back(unit_mod);
227  max_list.push_back(illum.highest("max_value").first);
228  min_list.push_back(illum.lowest("min_value").first);
229  if ( unit_mod > most_add )
230  most_add = unit_mod;
231  else if ( unit_mod < most_sub )
232  most_sub = unit_mod;
233  }
234  }
235  const bool net_darker = most_add < -most_sub;
236 
237  // Apply each unit's effect, tracking the best result.
238  int best_result = terrain_light;
239  const int base_light = terrain_light + (net_darker ? most_add : most_sub);
240  for ( size_t i = 0; i != mod_list.size(); ++i ) {
241  int result =
242  bounded_add(base_light, mod_list[i], max_list[i], min_list[i]);
243 
244  if ( net_darker && result < best_result )
245  best_result = result;
246  else if ( !net_darker && result > best_result )
247  best_result = result;
248  }
249 
250  // Update the object we will return.
251  tod.bonus_modified = best_result - tod.lawful_bonus;
252  tod.lawful_bonus = best_result;
253  }
254 
255  return tod;
256 }
257 
258 
260 {
261  return !random_start_time.empty()
262  && utils::string_bool(random_start_time, true);
263 }
264 
266 {
267  int bonus = times_[currentTime_].lawful_bonus;
268  times_.clear();
270  currentTime_ = time_cfg["current_time"].to_int(0);
271  if (bonus != times_[currentTime_].lawful_bonus) {
272  has_tod_bonus_changed_ = true;
273  }
274 }
275 
276 void tod_manager::replace_schedule(const std::vector<time_of_day>& schedule)
277 {
278  int bonus = times_[currentTime_].lawful_bonus;
279  times_ = schedule;
280  currentTime_ = 0;
281  if (bonus != times_[currentTime_].lawful_bonus) {
282  has_tod_bonus_changed_ = true;
283  }
284 }
285 
286 void tod_manager::replace_area_locations(int area_index, const std::set<map_location>& locs) {
287  assert(area_index < static_cast<int>(areas_.size()));
288  areas_[area_index].hexes = locs;
289  has_tod_bonus_changed_ = true;
290 }
291 
292 void tod_manager::replace_local_schedule(const std::vector<time_of_day>& schedule, int area_index)
293 {
294  assert(area_index < static_cast<int>(areas_.size()));
295  area_time_of_day& area = areas_[area_index];
296  int bonus = area.times[area.currentTime].lawful_bonus;
297  area.times = schedule;
298  area.currentTime = 0;
299  if (bonus != area.times[area.currentTime].lawful_bonus) {
300  has_tod_bonus_changed_ = true;
301  }
302 }
303 
304 void tod_manager::set_area_id(int area_index, const std::string& id) {
305  assert(area_index < static_cast<int>(areas_.size()));
306  areas_[area_index].id = id;
307 }
308 
309 std::vector<std::string> tod_manager::get_area_ids() const
310 {
311  std::vector<std::string> areas;
312  for (const area_time_of_day& area : areas_) {
313  areas.push_back(area.id);
314  }
315  return areas;
316 }
317 
318 const std::set<map_location>& tod_manager::get_area_by_id(const std::string& id) const
319 {
320  for (const area_time_of_day& area : areas_) {
321  if (area.id == id)
322  return area.hexes;
323  }
324  return areas_[0].hexes;
325 }
326 
327 const std::set<map_location>& tod_manager::get_area_by_index(int index) const
328 {
329  return areas_[index].hexes;
330 }
331 
332 void tod_manager::add_time_area(const gamemap & map, const config& cfg)
333 {
334  areas_.push_back(area_time_of_day());
335  area_time_of_day &area = areas_.back();
336  area.id = cfg["id"].str();
337  area.xsrc = cfg["x"].str();
338  area.ysrc = cfg["y"].str();
339  area.currentTime = cfg["current_time"].to_int(0);
340  std::vector<map_location> const& locs (map.parse_location_range(area.xsrc, area.ysrc, true));
341  area.hexes.insert(locs.begin(), locs.end());
342  time_of_day::parse_times(cfg, area.times);
343  has_tod_bonus_changed_ = true;
344 }
345 
346 void tod_manager::add_time_area(const std::string& id, const std::set<map_location>& locs,
347  const config& time_cfg)
348 {
349  areas_.push_back(area_time_of_day());
350  area_time_of_day& area = areas_.back();
351  area.id = id;
352  area.hexes = locs;
353  area.currentTime = time_cfg["current_time"].to_int(0);
354  time_of_day::parse_times(time_cfg, area.times);
355  has_tod_bonus_changed_ = true;
356 }
357 
359 {
360  if(area_id.empty()) {
361  areas_.clear();
362  } else {
363  // search for all time areas that match the id.
365  while(i != areas_.end()) {
366  if((*i).id == area_id) {
367  i = areas_.erase(i);
368  } else {
369  ++i;
370  }
371  }
372  }
373  has_tod_bonus_changed_ = true;
374 }
375 
376 void tod_manager::remove_time_area(int area_index)
377 {
378  assert(area_index < static_cast<int>(areas_.size()));
379  areas_.erase(areas_.begin() + area_index);
380  has_tod_bonus_changed_ = true;
381 }
382 
383 const time_of_day& tod_manager::get_time_of_day_turn(const std::vector<time_of_day>& times, int nturn, const int current_time) const
384 {
385  const int time = calculate_current_time(times.size(), nturn, current_time);
386  return times[time];
387 }
388 
390 {
391  num_turns_ = std::max<int>(utils::apply_modifier(num_turns_,mod,0),-1);
392 }
394 {
395  num_turns_ = std::max<int>(num, -1);
396 }
397 
399 {
400  if(resources::controller->current_team().is_local()) {
401  //the currently active side informs the mp server about the turn change.
402  //NOTE: The current implementation does not guarnateee that the server gets informed
403  // about those changes in 100% of cases. But that is ok because the information is only
404  // used to display the turn limit in the lobby (as opposed to things that cause OOS).
406  ("change_turns_wml", config_of
407  ("current", turn_)
408  ("max", num_turns_)
409  )
410  );
411  }
412 }
414 {
415  modify_turns(mod);
417 }
419 {
420  set_number_of_turns(num);
422 }
423 
424 void tod_manager::set_turn(const int num, game_data* vars, const bool increase_limit_if_needed)
425 {
426  has_tod_bonus_changed_ = false;
427  const int new_turn = std::max<int>(num, 1);
428  LOG_NG << "changing current turn number from " << turn_ << " to " << new_turn << '\n';
429  // Correct ToD
430  set_new_current_times(new_turn);
431 
432  if(increase_limit_if_needed && (new_turn > num_turns_) && num_turns_ != -1) {
433  set_number_of_turns(new_turn);
434  }
435  turn_ = new_turn;
436  if (vars)
437  vars->get_variable("turn_number") = new_turn;
438 }
439 
440 void tod_manager::set_turn_by_wml(const int num, game_data* vars, const bool increase_limit_if_needed)
441 {
442  set_turn(num, vars, increase_limit_if_needed);
444 }
445 void tod_manager::set_new_current_times(const int new_current_turn_number)
446 {
447  set_current_time(calculate_current_time(times_.size(), new_current_turn_number, currentTime_));
448  for (area_time_of_day& area : areas_) {
450  area.times.size(),
451  new_current_turn_number,
452  area.currentTime),
453  area);
454  }
455 }
456 
458  const int number_of_times,
459  const int for_turn_number,
460  const int current_time,
461  const bool only_to_allowed_range) const
462 {
463  if (number_of_times == 0) return 0;
464  int new_current_time = 0;
465  if(only_to_allowed_range) new_current_time = current_time % number_of_times;
466  else new_current_time = (current_time + for_turn_number - turn_) % number_of_times;
467  while(new_current_time < 0) { new_current_time += number_of_times; }
468  return new_current_time;
469 }
470 
472  if (times_[time].lawful_bonus != times_[currentTime_].lawful_bonus) {
473  has_tod_bonus_changed_ = true;
474  }
475  currentTime_ = time;
476 }
477 
478 void tod_manager::set_current_time(int time, int area_index) {
479  assert(area_index < static_cast<int>(areas_.size()));
480  set_current_time(time, areas_[area_index]);
481 }
482 
483 void tod_manager::set_current_time(int time, const std::string& area_id) {
484  for (area_time_of_day& area : areas_) {
485  if (area.id == area_id)
486  set_current_time(time, area);
487  }
488 }
489 
491  assert(time < static_cast<int>(area.times.size()) );
492  if (area.times[time].lawful_bonus != area.times[area.currentTime].lawful_bonus) {
493  has_tod_bonus_changed_ = true;
494  }
495  area.currentTime = time;
496 }
497 
499 {
500  set_turn(turn_ + 1, vars, false);
501  has_turn_event_fired_ = false;
502  return is_time_left();
503 }
504 
505 
507 {
508  return num_turns_ == -1 || turn_ <= num_turns_;
509 }
play_controller * controller
Definition: resources.cpp:21
bool on_board_with_border(const map_location &loc) const
Definition: map.cpp:472
void set_new_current_times(const int new_current_turn_number)
For a change of the current turn number, sets the current times of the main time and all time areas...
void replace_local_schedule(const std::vector< time_of_day > &schedule, int area_index)
std::string str() const
Definition: config.cpp:353
tod_manager & operator=(const tod_manager &manager)
Definition: tod_manager.cpp:58
virtual void send_to_wesnothd(const config &, const std::string &="unknown") const
unit_iterator end()
Definition: map.hpp:311
bool has_turn_event_fired_
void replace_schedule(const config &time_cfg)
Replace the time of day schedule.
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
bool to_bool(bool def=false) const
Definition: config.cpp:275
void set_turn(const int num, game_data *vars=nullptr, const bool increase_limit_if_needed=true)
Dynamically change the current turn number.
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.hpp:274
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
Definition: tod_manager.hpp:56
int get_current_area_time(int index) const
void set_area_id(int area_index, const std::string &id)
bool has_tod_bonus_changed_
int lawful_bonus
The % bonus lawful units receive.
Definition: time_of_day.hpp:70
GLfloat GLfloat GLfloat v2
Definition: glew.h:1824
GLuint const GLfloat * val
Definition: glew.h:2614
void modify_turns_by_wml(const std::string &mod)
std::set< map_location > hexes
std::pair< int, map_location > highest(const std::string &key, int def=0) const
Definition: abilities.cpp:426
int light_bonus(int base) const
Returns the light (lawful) bonus for this terrain when the time of day gives a base bonus...
Definition: terrain.hpp:55
GLdouble GLdouble t
Definition: glew.h:1366
config::attribute_value & get_variable(const std::string &varname)
throws invalid_variablename_exception if varname is no valid variable name.
Definition: game_data.cpp:70
void modify_turns(const std::string &mod)
config to_config() const
const time_of_day & get_previous_time_of_day() const
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:48
void set_current_time(int time)
bool operator()(T v2) const
Definition: tod_manager.cpp:78
GLuint64EXT * result
Definition: glew.h:10727
void set_number_of_turns_by_wml(int num)
GLuint id
Definition: glew.h:1647
std::vector< time_of_day > times
uint32_t next_random()
Provides the next random draw.
Definition: random_new.cpp:76
config::attribute_value random_tod_
GLsizei const GLfloat * value
Definition: glew.h:1817
void update_server_information() const
void remove_time_area(const std::string &id)
Removes a time area from config, making it follow the scenario's normal time-of-day sequence...
std::vector< time_of_day > times_
Encapsulates the map of the game.
Definition: map.hpp:37
tod_manager(const config &scenario_cfg=config())
Definition: tod_manager.cpp:38
greater(T val)
Definition: tod_manager.cpp:77
GLuint num
Definition: glew.h:2552
config & add_child(const std::string &key)
Definition: config.cpp:743
static const map_location & null_location()
Definition: location.hpp:195
bool is_time_left()
Function to check the end of turns.
void set_turn_by_wml(const int num, game_data *vars=nullptr, const bool increase_limit_if_needed=true)
Dynamically change the current turn number.
Encapsulates the map of the game.
Definition: location.hpp:38
const terrain_type & get_terrain_info(const t_translation::t_terrain &terrain) const
Definition: map.cpp:100
std::vector< map_location > parse_location_range(const std::string &xvals, const std::string &yvals, bool with_border=false) const
Parses ranges of locations into a vector of locations, using this map's dimensions as bounds...
Definition: map.cpp:537
static const char * output
Definition: luac.cpp:31
std::vector< std::string > get_area_ids() const
std::map< std::string, tfilter >::iterator itor
Definition: filter.cpp:199
void write_location_range(const std::set< map_location > &locs, config &cfg)
Write a set of locations into a config using ranges, adding keys x=x1,..,xn and y=y1a-y1b,..,yna-ynb.
Definition: location.cpp:371
void set_number_of_turns(int num)
GLuint index
Definition: glew.h:1782
const time_of_day & get_time_of_day_turn(const std::vector< time_of_day > &times, int nturn, const int current_time) const
Returns time of day object in the turn "nturn".
this class does not give synced random results derived classes might do.
Definition: random_new.hpp:27
const std::set< map_location > & get_area_by_index(int index) const
size_t i
Definition: function.cpp:1057
static void parse_times(const config &cfg, std::vector< time_of_day > &normal_times)
Parse config and add time of day entries into passed vector.
Definition: time_of_day.cpp:68
bool string_bool(const std::string &str, bool def)
Convert no, false, off, 0, 0.0 to false, empty to def, and others to true.
GLdouble GLdouble GLdouble r
Definition: glew.h:1374
#define LOG_NG
Definition: tod_manager.cpp:36
static lg::log_domain log_engine("engine")
static bool is_start_ToD(const std::string &)
const std::set< map_location > & get_area_by_id(const std::string &id) const
const time_of_day get_illuminated_time_of_day(const unit_map &units, const gamemap &map, const map_location &loc, int for_turn=0) const
Returns time of day object for the passed turn at a location.
int bounded_add(int base, int increment, int max_sum, int min_sum=0)
Returns base + increment, but will not increase base above max_sum, nor decrease it below min_sum...
Definition: util.hpp:44
std::pair< int, map_location > lowest(const std::string &key, int def=0) const
Definition: abilities.cpp:457
int calculate_current_time(const int number_of_times, const int for_turn_number, const int current_time, const bool only_to_allowed_range=false) const
Computes for the main time or a time area the index of its times where we're currently at...
int get_current_time(const map_location &loc=map_location::null_location()) const
void replace_area_locations(int index, const std::set< map_location > &locs)
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.
int apply_modifier(const int number, const std::string &amount, const int minimum)
const std::vector< time_of_day > & times(const map_location &loc=map_location::null_location()) const
Standard logging facilities (interface).
Container associating units to locations.
Definition: map.hpp:90
std::vector< area_time_of_day > areas_
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
int get_composite_value() const
Definition: abilities.hpp:50
void resolve_random(random_new::rng &r)
handles random_start_time, should be called before the game starts.
Definition: tod_manager.cpp:86
unit_iterator find(size_t id)
Definition: map.cpp:285
bool next_turn(game_data *vars)
Function to move to the next turn.
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
bool random_start_time()
int bonus_modified
Definition: time_of_day.hpp:71
GLsizei const GLcharARB ** string
Definition: glew.h:4503
unit_map * units
Definition: resources.cpp:35