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