The Battle for Wesnoth  1.15.0-dev
aspect.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2018 by Yurii Chernyi <terraninfo@terraninfo.net>
3  Part of the Battle for Wesnoth Project https://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  */
18 
19 #pragma once
20 
23 #include "ai/lua/lua_object.hpp"
24 #include "ai/lua/core.hpp"
26 
27 #include "log.hpp"
28 
29 #include "utils/functional.hpp"
30 
31 #include <boost/range/adaptor/reversed.hpp>
32 
33 namespace ai {
34 
35 class aspect : public readonly_context_proxy, public events::observer, public component {
36 public:
37  aspect(readonly_context &context, const config &cfg, const std::string &id);
38 
39  virtual ~aspect();
40 
41  void invalidate() const
42  {
43  valid_ = false;
44  valid_variant_ = false;
45  valid_lua_ = false;
46  }
47 
48 
49  virtual const wfl::variant& get_variant() const = 0;
50 
51 
52  virtual std::shared_ptr<wfl::variant> get_variant_ptr() const = 0;
53 
54 
55  virtual void recalculate() const = 0;
56 
57 
58  virtual void on_create();
59 
60 
61  virtual bool redeploy(const config &cfg, const std::string & id);
62 
63 
64  virtual config to_config() const;
65 
66 
67  virtual bool delete_all_facets();
68 
69 
70  void handle_generic_event(const std::string &/*event_name*/)
71  {
72  invalidate();
73  }
74 
75 
76  virtual bool active() const;
77 
78  virtual std::string get_name() const
79  { return name_; }
80 
81  virtual std::string get_id() const
82  { return id_; }
83 
84  virtual std::string get_engine() const
85  { return engine_; }
86 
87  static lg::log_domain& log();
88 
89 protected:
90  std::string time_of_day_;
91  std::string turns_;
92 
93  mutable bool valid_;
94  mutable bool valid_variant_;
95  mutable bool valid_lua_;
96 
102  std::string engine_;
103  std::string name_;
104  std::string id_;
105 
106 };
107 
108 template<typename T>
109 class typesafe_aspect : public aspect {
110 public:
111  typesafe_aspect(readonly_context &context, const config &cfg, const std::string &id)
112  : aspect(context,cfg,id)
113  , value_()
114  , value_variant_()
115  , value_lua_()
116  {
117  }
118 
120  {
121  }
122 
123 
124  virtual const T& get() const
125  {
126  return *get_ptr();
127  }
128 
129 
130  virtual const wfl::variant& get_variant() const
131  {
132  return *get_variant_ptr();
133  }
134 
135  virtual std::shared_ptr<wfl::variant> get_variant_ptr() const
136  {
137  if (!valid_variant_) {
138  if (!valid_) {
139  recalculate();
140  }
141 
142  if (!valid_variant_ && valid_ ) {
143  value_variant_.reset(new wfl::variant(variant_value_translator<T>::value_to_variant(this->get())));
144  valid_variant_ = true;
145  } else if (!valid_variant_ && valid_lua_) {
146  value_ = value_lua_->get();
147  value_variant_.reset(new wfl::variant(variant_value_translator<T>::value_to_variant(this->get())));
148  valid_variant_ = true; // @note: temporary workaround
149  } else {
150  assert(valid_variant_);
151  }
152  }
153  return value_variant_;
154  }
155 
156  virtual void recalculate() const = 0;
157 
158 
159  virtual std::shared_ptr<T> get_ptr() const
160  {
161  if (!valid_) {
162  if (!(valid_variant_ || valid_lua_)) {
163  recalculate();
164  }
165 
166  if (!valid_ ) {
167  if (valid_variant_) {
169  valid_ = true;
170  } else if (valid_lua_){
171  value_ = value_lua_->get();
172  valid_ = true;
173  } else {
174  assert(valid_);
175  }
176  }
177  }
178  return value_;
179  }
180 
181 protected:
182  mutable std::shared_ptr<T> value_;
183  mutable std::shared_ptr<wfl::variant> value_variant_;
184  mutable std::shared_ptr< lua_object<T>> value_lua_;
185 };
186 
187 
189 public:
190  known_aspect(const std::string &name);
191 
192 
193  virtual ~known_aspect();
194 
195 
196  virtual void set(aspect_ptr a) = 0;
197 
198 
199  virtual void add_facet(const config &cfg) = 0;
200 
201 
202  const std::string& get_name() const;
203 
204 protected:
205  const std::string name_;
206 };
207 
208 
209 template<class T>
211 
212 template<typename T>
214 public:
215  typesafe_known_aspect(const std::string &name, typesafe_aspect_ptr<T>& where, aspect_map &aspects)
216  : known_aspect(name), where_(where), aspects_(aspects)
217  {
218  }
219 
220  void set(aspect_ptr a)
221  {
222  typesafe_aspect_ptr<T> c = std::dynamic_pointer_cast<typesafe_aspect<T>>(a);
223  if (c) {
224  assert (c->get_id()== this->get_name());
225  where_ = c;
226  aspects_.emplace(this->get_name(),c);
227  } else {
228  LOG_STREAM(debug, aspect::log()) << "typesafe_known_aspect [" << this->get_name() << "] : while setting aspect, got null. this might be caused by invalid [aspect] WML" << std::endl;
229  }
230  }
231 
232  virtual void add_facet(const config &cfg)
233  {
234  std::shared_ptr< composite_aspect <T>> c = std::dynamic_pointer_cast< composite_aspect<T>>(where_);
235  if (c) {
236  assert (c->get_id()==this->get_name());
237  c->add_facet(-1, cfg);
238  c->invalidate();
239  } else {
240  LOG_STREAM(debug, aspect::log()) << "typesafe_known_aspect [" << this->get_name() << "] : while adding facet to aspect, got null. this might be caused by target [aspect] being not composite" << std::endl;
241  }
242  }
243 
244 protected:
247 };
248 
249 
250 template<typename T>
251 class composite_aspect : public typesafe_aspect<T> {
252 public:
253 
254  composite_aspect(readonly_context &context, const config &cfg, const std::string &id)
255  : typesafe_aspect<T>(context, cfg, id)
256  , facets_()
257  , default_()
258  , parent_id_(id)
259  {
260  for (const config &cfg_element : this->cfg_.child_range("facet")) {
261  add_facet(-1,cfg_element);
262  }
263 
264  config _default = this->cfg_.child("default");
265  if (_default) {
266  _default["id"] = "default_facet";
267  std::vector< aspect_ptr > default_aspects;
268  engine::parse_aspect_from_config(*this,_default,parent_id_,std::back_inserter(default_aspects));
269  if (!default_aspects.empty()) {
270  typesafe_aspect_ptr<T> b = std::dynamic_pointer_cast< typesafe_aspect<T>>(default_aspects.front());
271  if (composite_aspect<T>* c = dynamic_cast<composite_aspect<T>*>(b.get())) {
272  c->parent_id_ = parent_id_;
273  }
274  default_ = b;
275  }
276  }
277 
278  std::function<void(typesafe_aspect_vector<T>&, const config&)> factory_facets =
279  std::bind(&ai::composite_aspect<T>::create_facet,*this,_1,_2);
280 
281  register_facets_property(this->property_handlers(),"facet",facets_,default_, factory_facets);
282 
283  }
284 
285 
287  {
288  std::vector<aspect_ptr> facets_base;
289  engine::parse_aspect_from_config(*this,cfg,parent_id_,std::back_inserter(facets_base));
290  for (aspect_ptr a : facets_base) {
291  typesafe_aspect_ptr<T> b = std::dynamic_pointer_cast< typesafe_aspect<T>> (a);
292  if (composite_aspect<T>* c = dynamic_cast<composite_aspect<T>*>(b.get())) {
293  c->parent_id_ = parent_id_;
294  }
295  facets.push_back(b);
296  }
297  }
298 
299 
300  virtual void recalculate() const
301  {
302  ///@todo 1.9 optimize in case of an aspect which returns variant
303  for(const auto& f : boost::adaptors::reverse(facets_)) {
304  if (f->active()) {
305  this->value_ = f->get_ptr();
306  this->valid_ = true;
307  return;
308  }
309  }
310  if (default_) {
311  this->value_ = default_->get_ptr();
312  this->valid_ = true;
313  }
314  }
315 
316 
317  virtual config to_config() const
318  {
319  config cfg = aspect::to_config();
320  for (const typesafe_aspect_ptr<T> f : facets_) {
321  cfg.add_child("facet",f->to_config());
322  }
323  if (default_) {
324  cfg.add_child("default",default_->to_config());
325  }
326  return cfg;
327  }
328 
329 
331  virtual bool add_facet(int pos, const config &cfg)
332  {
333  if (pos<0) {
334  pos = facets_.size();
335  }
336  std::vector< aspect_ptr > facets;
337  engine::parse_aspect_from_config(*this,cfg,parent_id_,std::back_inserter(facets));
338  int j=0;
339  for (aspect_ptr a : facets) {
340  typesafe_aspect_ptr<T> b = std::dynamic_pointer_cast< typesafe_aspect<T>> (a);
341  if (composite_aspect<T>* c = dynamic_cast<composite_aspect<T>*>(b.get())) {
342  c->parent_id_ = parent_id_;
343  }
344  facets_.insert(facets_.begin()+pos+j,b);
345  j++;
346  }
347  return (j>0);
348  }
349 
350 
351  virtual bool delete_all_facets()
352  {
353  bool b = !facets_.empty();
354  facets_.clear();
355  return b;
356  }
357 
359  {
360  for(aspect_ptr a : facets_) {
361  if(a->active()) {
362  return *a;
363  }
364  }
365  return *default_;
366  }
367 
368 protected:
371  std::string parent_id_;
372 
373 };
374 
375 template<typename T>
376 class standard_aspect : public typesafe_aspect<T> {
377 public:
378  standard_aspect(readonly_context &context, const config &cfg, const std::string &id)
379  : typesafe_aspect<T>(context, cfg, id)
380  {
381  this->name_ = "standard_aspect";
382  this->value_ = std::make_shared<T>(config_value_translator<T>::cfg_to_value(this->cfg_));
383  LOG_STREAM(debug, aspect::log()) << "standard aspect has value: "<< std::endl << config_value_translator<T>::value_to_cfg(this->get()) << std::endl;
384  }
385 
386 
387  void recalculate() const
388  {
389  //nothing to recalculate
390  this->valid_ = true;
391  }
392 
393 
395  {
396  config cfg = aspect::to_config();
398  return cfg;
399  }
400 
401 };
402 
403 class lua_aspect_visitor : public boost::static_visitor<std::string> {
404  static std::string quote_string(const std::string& s);
405 public:
406  std::string operator()(bool b) const {return utils::bool_string(b);}
407  std::string operator()(int i) const {return std::to_string(i);}
408  std::string operator()(unsigned long long i) const {return std::to_string(i);}
409  std::string operator()(double i) const {return std::to_string(i);}
410  std::string operator()(const std::string& s) const {return quote_string(s);}
411  std::string operator()(const t_string& s) const {return quote_string(s.str());}
412  std::string operator()(boost::blank) const {return "nil";}
413 };
414 
415 template<typename T>
416 class lua_aspect : public typesafe_aspect<T>
417 {
418 public:
419  lua_aspect(readonly_context &context, const config &cfg, const std::string &id, std::shared_ptr<lua_ai_context>& l_ctx)
420  : typesafe_aspect<T>(context, cfg, id)
421  , handler_(), code_(), params_(cfg.child_or_empty("args"))
422  {
423  this->name_ = "lua_aspect";
424  if (cfg.has_attribute("code"))
425  {
426  code_ = cfg["code"].str();
427  }
428  else if (cfg.has_attribute("value"))
429  {
430  code_ = "return " + cfg["value"].apply_visitor(lua_aspect_visitor());
431  }
432  else
433  {
434  // error
435  return;
436  }
437  handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(), *l_ctx));
438  }
439 
440  void recalculate() const
441  {
442  this->valid_lua_ = true;
443  this->value_lua_.reset(new lua_object<T>);
444  handler_->handle(params_, true, this->value_lua_);
445  }
446 
448  {
449  config cfg = aspect::to_config();
450  cfg["code"] = code_;
451  if (!params_.empty()) {
452  cfg.add_child("args", params_);
453  }
454  return cfg;
455  }
456 
457 private:
458  std::shared_ptr<lua_ai_action_handler> handler_;
459  std::string code_;
461 };
462 
463 
465  bool is_duplicate(const std::string &name);
466 public:
467  typedef std::shared_ptr< aspect_factory > factory_ptr;
468  typedef std::map<std::string, factory_ptr> factory_map;
469  typedef std::pair<const std::string, factory_ptr> factory_map_pair;
470 
471  static factory_map& get_list() {
472  static factory_map *aspect_factories;
473  if (aspect_factories==nullptr) {
474  aspect_factories = new factory_map;
475  }
476  return *aspect_factories;
477  }
478 
479  virtual aspect_ptr get_new_instance( readonly_context &context, const config &cfg, const std::string &id) = 0;
480 
481  aspect_factory( const std::string &name )
482  {
483  if (is_duplicate(name)) {
484  return;
485  }
486  factory_ptr ptr_to_this(this);
487  get_list().emplace(name,ptr_to_this);
488  }
489 
490  virtual ~aspect_factory() {}
491 };
492 
493 
494 template<class ASPECT>
496 public:
497  register_aspect_factory( const std::string &name )
498  : aspect_factory( name )
499  {
500  }
501 
502  aspect_ptr get_new_instance( readonly_context &context, const config &cfg, const std::string &id)
503  {
504  aspect_ptr a = std::make_shared<ASPECT>(context, cfg, id);
505  a->on_create();
506  return a;
507  }
508 };
509 
511 public:
512  typedef std::shared_ptr< lua_aspect_factory > factory_ptr;
513  typedef std::map<std::string, factory_ptr> factory_map;
514  typedef std::pair<const std::string, factory_ptr> factory_map_pair;
515 
516  static factory_map& get_list() {
517  static factory_map *aspect_factories;
518  if (aspect_factories==nullptr) {
519  aspect_factories = new factory_map;
520  }
521  return *aspect_factories;
522  }
523 
524  virtual aspect_ptr get_new_instance( readonly_context &context, const config &cfg, const std::string &id, std::shared_ptr<lua_ai_context>& l_ctx) = 0;
525 
526  lua_aspect_factory( const std::string &name )
527  {
528  factory_ptr ptr_to_this(this);
529  get_list().emplace(name,ptr_to_this);
530  }
531 
532  virtual ~lua_aspect_factory() {}
533 };
534 
535 template<class ASPECT>
537 public:
538  register_lua_aspect_factory( const std::string &name )
539  : lua_aspect_factory( name )
540  {
541  }
542 
543  aspect_ptr get_new_instance( readonly_context &context, const config &cfg, const std::string &id, std::shared_ptr<lua_ai_context>& l_ctx)
544  {
545  aspect_ptr a = std::make_shared<ASPECT>(context, cfg, id, l_ctx);
546  a->on_create();
547  return a;
548  }
549 };
550 
551 
552 } //end of namespace ai
static void parse_aspect_from_config(readonly_context &context, const config &cfg, const std::string &id, std::back_insert_iterator< std::vector< aspect_ptr >> b)
Definition: engine.cpp:51
virtual void recalculate() const
Definition: aspect.hpp:300
static std::unique_ptr< class sdl_event_handler > handler_
Definition: handler.cpp:54
std::string operator()(bool b) const
Definition: aspect.hpp:406
lua_aspect_factory(const std::string &name)
Definition: aspect.hpp:526
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:423
bool invalidate_on_tod_change_
Definition: aspect.hpp:99
aspect_ptr get_new_instance(readonly_context &context, const config &cfg, const std::string &id)
Definition: aspect.hpp:502
std::string operator()(const std::string &s) const
Definition: aspect.hpp:410
std::string operator()(const t_string &s) const
Definition: aspect.hpp:411
virtual bool add_facet(int pos, const config &cfg)
Definition: aspect.hpp:331
bool valid_variant_
Definition: aspect.hpp:94
virtual ~lua_aspect_factory()
Definition: aspect.hpp:532
std::map< std::string, factory_ptr > factory_map
Definition: aspect.hpp:513
virtual std::shared_ptr< wfl::variant > get_variant_ptr() const
Definition: aspect.hpp:135
std::string turns_
Definition: aspect.hpp:91
static factory_map & get_list()
Definition: aspect.hpp:516
aspect_factory(const std::string &name)
Definition: aspect.hpp:481
bool has_attribute(config_key_type key) const
Definition: config.cpp:217
#define a
property_handler_map & property_handlers()
Definition: component.cpp:132
virtual void recalculate() const =0
virtual ~aspect()
Definition: aspect.cpp:47
typesafe_known_aspect(const std::string &name, typesafe_aspect_ptr< T > &where, aspect_map &aspects)
Definition: aspect.hpp:215
bool invalidate_on_minor_gamestate_change_
Definition: aspect.hpp:101
child_itors child_range(config_key_type key)
Definition: config.cpp:366
void handle_generic_event(const std::string &)
Definition: aspect.hpp:70
virtual bool redeploy(const config &cfg, const std::string &id)
Definition: aspect.cpp:74
register_aspect_factory(const std::string &name)
Definition: aspect.hpp:497
std::string time_of_day_
Definition: aspect.hpp:90
bool valid_lua_
Definition: aspect.hpp:95
virtual void add_facet(const std::string &id, const config &cfg) const override
Definition: contexts.hpp:641
void recalculate() const
Definition: aspect.hpp:440
std::shared_ptr< T > value_
Definition: aspect.hpp:182
#define LOG_STREAM(level, domain)
Definition: log.hpp:189
void invalidate() const
Definition: aspect.hpp:41
lua_aspect(readonly_context &context, const config &cfg, const std::string &id, std::shared_ptr< lua_ai_context > &l_ctx)
Definition: aspect.hpp:419
Lua object(value) wrapper implementation.
config to_config() const
Definition: aspect.hpp:394
virtual bool delete_all_facets()
Definition: aspect.cpp:144
virtual std::shared_ptr< T > get_ptr() const
Definition: aspect.hpp:159
std::shared_ptr< aspect > aspect_ptr
Definition: game_info.hpp:94
virtual std::string get_name() const
Definition: aspect.hpp:78
#define b
virtual void add_facet(const config &cfg)
Definition: aspect.hpp:232
std::shared_ptr< wfl::variant > value_variant_
Definition: aspect.hpp:183
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:58
std::shared_ptr< typesafe_aspect< T > > typesafe_aspect_ptr
Definition: game_info.hpp:57
std::string name_
Definition: aspect.hpp:103
std::string operator()(double i) const
Definition: aspect.hpp:409
const t_string id
const t_string name
virtual config to_config() const
Definition: aspect.hpp:317
static lg::log_domain & log()
Definition: aspect.cpp:65
virtual std::string get_id() const
Definition: aspect.hpp:81
typesafe_aspect_ptr< T > & where_
Definition: aspect.hpp:245
virtual const wfl::variant & get_variant() const =0
aspect_map & aspects_
Definition: aspect.hpp:246
typesafe_aspect(readonly_context &context, const config &cfg, const std::string &id)
Definition: aspect.hpp:111
void recalculate() const
Definition: aspect.hpp:387
const std::string name_
Definition: aspect.hpp:205
virtual std::shared_ptr< wfl::variant > get_variant_ptr() const =0
virtual config to_config() const
Definition: aspect.cpp:120
virtual ~typesafe_aspect()
Definition: aspect.hpp:119
std::string parent_id_
Definition: aspect.hpp:371
std::string id_
Definition: aspect.hpp:104
std::string operator()(int i) const
Definition: aspect.hpp:407
void create_facet(typesafe_aspect_vector< T > &facets, const config &cfg)
Definition: aspect.hpp:286
aspect(readonly_context &context, const config &cfg, const std::string &id)
Definition: aspect.cpp:31
bool invalidate_on_turn_start_
Definition: aspect.hpp:98
aspect & find_active()
Definition: aspect.hpp:358
typesafe_aspect_ptr< T > default_
Definition: aspect.hpp:370
typesafe_aspect_vector< T > facets_
Definition: aspect.hpp:369
static T cfg_to_value(const config &cfg)
std::size_t i
Definition: function.cpp:933
std::shared_ptr< lua_object< T > > value_lua_
Definition: aspect.hpp:184
static map_location::DIRECTION s
virtual bool delete_all_facets()
Definition: aspect.hpp:351
std::string bool_string(const bool value)
Converts a bool value to &#39;true&#39; or &#39;false&#39;.
virtual void on_create()
Definition: aspect.cpp:70
std::shared_ptr< lua_aspect_factory > factory_ptr
Definition: aspect.hpp:512
#define debug(x)
virtual bool active() const
Definition: aspect.cpp:139
config & add_child(config_key_type key)
Definition: config.cpp:479
const config params_
Definition: aspect.hpp:460
bool valid_
Definition: aspect.hpp:93
std::shared_ptr< lua_ai_action_handler > handler_
Definition: aspect.hpp:458
std::pair< const std::string, factory_ptr > factory_map_pair
Definition: aspect.hpp:514
Composite AI component.
std::map< std::string, aspect_ptr > aspect_map
Definition: game_info.hpp:103
#define f
std::shared_ptr< aspect_factory > factory_ptr
Definition: aspect.hpp:467
virtual ~aspect_factory()
Definition: aspect.hpp:490
static void value_to_cfg(const T &value, config &cfg)
std::string operator()(boost::blank) const
Definition: aspect.hpp:412
static factory_map & get_list()
Definition: aspect.hpp:471
Standard logging facilities (interface).
virtual std::string get_engine() const
Definition: aspect.hpp:84
config cfg_
Definition: aspect.hpp:97
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
std::string engine_
Definition: aspect.hpp:102
register_lua_aspect_factory(const std::string &name)
Definition: aspect.hpp:538
static void register_facets_property(property_handler_map &property_handlers, const std::string &property, std::vector< std::shared_ptr< X >> &values, std::shared_ptr< X > &def, std::function< void(std::vector< std::shared_ptr< X >> &, const config &)> construction_factory)
std::pair< const std::string, factory_ptr > factory_map_pair
Definition: aspect.hpp:469
static void reverse(lua_State *L, StkId from, StkId to)
Definition: lapi.cpp:193
config to_config() const
Definition: aspect.hpp:447
std::map< std::string, factory_ptr > factory_map
Definition: aspect.hpp:468
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
mock_char c
const std::string & str() const
Definition: tstring.hpp:186
std::string code_
Definition: aspect.hpp:459
composite_aspect(readonly_context &context, const config &cfg, const std::string &id)
Definition: aspect.hpp:254
bool invalidate_on_gamestate_change_
Definition: aspect.hpp:100
standard_aspect(readonly_context &context, const config &cfg, const std::string &id)
Definition: aspect.hpp:378
std::string operator()(unsigned long long i) const
Definition: aspect.hpp:408
aspect_ptr get_new_instance(readonly_context &context, const config &cfg, const std::string &id, std::shared_ptr< lua_ai_context > &l_ctx)
Definition: aspect.hpp:543
virtual const wfl::variant & get_variant() const
Definition: aspect.hpp:130
std::vector< typesafe_aspect_ptr< T > > typesafe_aspect_vector
Definition: game_info.hpp:60