The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
component.cpp
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 * Composite AI component
18 * @file
19 */
20
23#include "config.hpp"
24#include "log.hpp"
25
26
27#include <boost/regex.hpp>
28
29namespace ai {
30
31static lg::log_domain log_ai_component("ai/component");
32#define DBG_AI_COMPONENT LOG_STREAM(debug, log_ai_component)
33#define LOG_AI_COMPONENT LOG_STREAM(info, log_ai_component)
34#define ERR_AI_COMPONENT LOG_STREAM(err, log_ai_component)
35
36/*
37[modify_ai]
38 path = "stage[fallback]
39 action = "change"
40 [stage]...[/stage]
41[/modify_ai]
42
43[modify_ai]
44 component = "aspect[avoid].facet[zzz]"
45 action = "change"
46 [facet]...[/facet]
47[/modify_ai]
48
49[modify_ai]
50 path = "aspect[aggression].facet[zzzz]
51 action = "delete"
52[/modify_ai]
53
54[modify_ai]
55 component = "aspect[aggression].facet"
56 action = "add"
57 [facet]...[/facet]
58[/modify_ai]
59*/
60
62{
63 auto i = property_handlers_.find(child.property);
64 if(i != property_handlers_.end()) {
65 return i->second->handle_get(child);
66 }
67 return nullptr;
68}
69
70bool component::add_child(const path_element &child, const config &cfg)
71{
72 auto i = property_handlers_.find(child.property);
73 if(i != property_handlers_.end()) {
74 return i->second->handle_add(child,cfg);
75 }
76 return false;
77}
78
80{
81 auto i = property_handlers_.find(child.property);
82 if(i != property_handlers_.end()) {
83 return i->second->handle_change(child,cfg);
84 }
85 return false;
86}
87
89{
90 auto i = property_handlers_.find(child.property);
91 if(i != property_handlers_.end()) {
92 return i->second->handle_delete(child);
93 }
94 return false;
95}
96
97std::vector<component*> component::get_children(const std::string &type) const
98{
99 auto i = property_handlers_.find(type);
100 if(i != property_handlers_.end()) {
101 return i->second->handle_get_children();
102 }
103
104 return std::vector<component*>();
105}
106
107std::vector<std::string> component::get_children_types() const
108{
109 std::vector<std::string> types;
110 for(const auto& [type, handler] : property_handlers_) {
111 types.push_back(type);
112 }
113 return types;
114}
115
120
121static component *find_component(component *root, const std::string &path, path_element &tail)
122{
123 if (root==nullptr) {
124 return nullptr;
125 }
126
127 //match path elements in [modify_ai] tag
128 boost::regex re(R"(([^\.^\[]+)(\[(\d*)\]|\[([^\]]+)\]|()))");
129 const int sub_matches[] {1,3,4};
130 boost::sregex_token_iterator i(path.begin(), path.end(), re, sub_matches);
131 boost::sregex_token_iterator j;
132
133 component *c = root;
134
135 std::vector< path_element > elements;
136 while(i != j)
137 {
138 path_element pe;
139 pe.property = *i++;
140 std::string position = *i++;
141 pe.id = *i++;
142 if (position.empty()) {
143 pe.position = -2;
144 } else {
145 try {
146 pe.position = std::stoi(position);
147 } catch (const std::invalid_argument&) {
148 pe.position = -2;
149 }
150 }
151 //DBG_AI_COMPONENT << "adding path element: "<< pe;
152 elements.push_back(pe);
153 }
154 if (elements.size()<1) {
155 return nullptr;
156 }
157
158 std::vector< path_element >::iterator k_max = elements.end()-1;
159 for (std::vector< path_element >::iterator k = elements.begin(); k!=k_max; ++k) {
160 //not last
161 c = c->get_child(*k);
162 if (c==nullptr) {
163 return nullptr;
164 }
165 }
166
167 tail = *k_max;
168 return c;
169
170}
171
172bool component_manager::add_component(component *root, const std::string &path, const config &cfg)
173{
174 path_element tail;
175 component *c = find_component(root,path,tail);
176 if (c==nullptr) {
177 return false;
178 }
179 auto ch = cfg.optional_child(tail.property);
180 if (!ch) {
181 return false;
182 }
183 return c->add_child(tail, *ch);
184
185}
186
187bool component_manager::change_component(component *root, const std::string &path, const config &cfg)
188{
189 path_element tail;
190 component *c = find_component(root,path,tail);
191 if (c==nullptr) {
192 return false;
193 }
194 auto ch = cfg.optional_child(tail.property);
195 if (!ch) {
196 return false;
197 }
198 return c->change_child(tail, *ch);
199}
200
201bool component_manager::delete_component(component *root, const std::string &path)
202{
203 path_element tail;
204 component *c = find_component(root,path,tail);
205 if (c==nullptr) {
206 return false;
207 }
208 return c->delete_child(tail);
209}
210
211static void print_component(component *root, const std::string &type, std::stringstream &s, int offset)
212{
213 std::stringstream offset_ss;
214 for (int i=0;i<offset;++i) {
215 offset_ss<<" ";
216 }
217 const std::string &offset_str = offset_ss.str();
218
219 const std::vector<std::string> &t_list = root->get_children_types();
220
221 s << offset_str << type<<"["<<root->get_id() <<"] "<<root->get_engine()<<" "<<root->get_name()<< std::endl;
222
223 for (std::string t : t_list) {
224 std::vector<component*> c_list = root->get_children(t);
225 for (component *c : c_list) {
226 print_component(c,t,s,offset+1);
227 }
228 }
229}
230
231std::string component_manager::print_component_tree(component *root, const std::string &path)
232{
233 path_element tail;
234 component *c;
235 if (!path.empty()) {
236 c = find_component(root,path,tail);
237 if (c==nullptr) {
238 ERR_AI_COMPONENT << "unable to find component";
239 return "";
240 }
241 } else {
242 c = root;
243 }
244 std::stringstream s;
245 print_component(c, "", s, 0);
246 return s.str();
247}
248
250{
251 if(!path.empty()) {
252 path_element tail;
253 return find_component(root, path, tail);
254 }
255 return nullptr;
256}
257
258} //end of namespace ai
259
260std::ostream &operator<<(std::ostream &o, const ai::path_element &e)
261{
262 o << "property["<<e.property<<"] id["<<e.id <<"] position["<<e.position<<"]"<<std::endl;
263 return o;
264}
double t
static bool delete_component(component *root, const std::string &path)
static component * get_component(component *root, const std::string &path)
static bool change_component(component *root, const std::string &path, const config &cfg)
static bool add_component(component *root, const std::string &path, const config &cfg)
static std::string print_component_tree(component *root, const std::string &path)
virtual component * get_child(const path_element &child) const
Definition component.cpp:61
virtual bool delete_child(const path_element &child)
Definition component.cpp:88
virtual std::string get_engine() const =0
virtual bool change_child(const path_element &child, const config &cfg)
Definition component.cpp:79
virtual std::string get_name() const =0
virtual bool add_child(const path_element &child, const config &cfg)
Definition component.cpp:70
virtual std::vector< std::string > get_children_types() const
virtual std::string get_id() const =0
property_handler_map & property_handlers()
virtual std::vector< component * > get_children(const std::string &type) const
Definition component.cpp:97
property_handler_map property_handlers_
Definition component.hpp:71
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition config.cpp:380
std::ostream & operator<<(std::ostream &o, const ai::path_element &e)
#define ERR_AI_COMPONENT
Definition component.cpp:34
A component of the AI framework.
Definitions for the interface to Wesnoth Markup Language (WML).
const config * cfg
Standard logging facilities (interface).
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition actions.cpp:59
static void print_component(component *root, const std::string &type, std::stringstream &s, int offset)
static component * find_component(component *root, const std::string &path, path_element &tail)
static lg::log_domain log_ai_component("ai/component")
std::map< std::string, property_handler_ptr > property_handler_map
Definition component.hpp:48
Composite AI component.
std::string property
Definition component.hpp:41
std::string id
Definition component.hpp:42
static map_location::direction s
#define i
#define e
#define c