The Battle for Wesnoth  1.19.18+dev
lua_team.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2025
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 "scripting/lua_team.hpp"
17 
19 #include "scripting/lua_common.hpp"
20 #include "scripting/push_check.hpp"
22 #include "team.hpp"
23 #include "resources.hpp" // for gameboard
24 #include "game_board.hpp"
25 #include "game_display.hpp"
26 #include "map/map.hpp"
27 
28 #include <string>
29 
30 
31 /**
32  * Implementation for a lua reference to a team,
33  * used by the wesnoth in-game sides table.
34  *
35  * (The userdata has type team** because lua holds
36  * only a pointer to a team, not a full-size team.
37  * If it were a full object then we would cast to
38  * type team *, since checkudata returns a pointer
39  * to the type corresponding to the sizeof expr
40  * used when we allocated the userdata.)
41  */
42 
43 // Registry key
44 static const char * Team = "side";
45 static const char teamVar[] = "side variables";
46 
47 #define SIDE_GETTER(name, type) LATTR_GETTER(name, type, team, t)
48 #define SIDE_SETTER(name, type) LATTR_SETTER(name, type, team, t)
49 luaW_Registry sideReg{"wesnoth", "sides", Team};
50 
51 template<> struct lua_object_traits<team> {
52  inline static auto metatable = Team;
53  inline static team& get(lua_State* L, int n) {
54  return luaW_checkteam(L, n);
55  }
56 };
57 
58 SIDE_GETTER("side", int) {
59  return t.side();
60 }
61 
62 SIDE_GETTER("save_id", std::string) {
63  return t.save_id();
64 }
65 //maybe add a setter for save_id too?
66 
67 SIDE_GETTER("gold", int) {
68  return t.gold();
69 }
70 
71 SIDE_SETTER("gold", int) {
72  t.set_gold(value);
73 }
74 
75 SIDE_GETTER("objectives", t_string) {
76  return t.objectives();
77 }
78 
79 SIDE_SETTER("objectives", t_string) {
80  t.set_objectives(value, true);
81 }
82 
83 SIDE_GETTER("village_gold", int) {
84  return t.village_gold();
85 }
86 
87 SIDE_SETTER("village_gold", int) {
88  t.set_village_gold(value);
89 }
90 
91 SIDE_GETTER("village_support", int) {
92  return t.village_support();
93 }
94 
95 SIDE_SETTER("village_support", int) {
96  t.set_village_support(value);
97 }
98 
99 SIDE_GETTER("num_villages", int) {
100  return t.villages().size();
101 }
102 
103 SIDE_GETTER("recall_cost", int) {
104  return t.recall_cost();
105 }
106 
107 SIDE_SETTER("recall_cost", int) {
108  t.set_recall_cost(value);
109 }
110 
111 SIDE_GETTER("base_income", int) {
112  return t.base_income();
113 }
114 
115 SIDE_SETTER("base_income", int) {
116  t.set_base_income(value);
117 }
118 
119 SIDE_GETTER("total_income", int) {
120  return t.total_income();
121 }
122 
123 SIDE_GETTER("objectives_changed", bool) {
124  return t.objectives_changed();
125 }
126 
127 SIDE_SETTER("objectives_changed", bool) {
128  t.set_objectives_changed(value);
129 }
130 
131 SIDE_GETTER("fog", bool) {
132  return t.uses_fog();
133 }
134 
135 SIDE_SETTER("fog", bool) {
136  t.set_fog(value);
137 }
138 
139 SIDE_GETTER("shroud", bool) {
140  return t.uses_shroud();
141 }
142 
143 SIDE_SETTER("shroud", bool) {
144  t.set_shroud(value);
145 }
146 
147 SIDE_GETTER("hidden", bool) {
148  return t.hidden();
149 }
150 
151 SIDE_SETTER("hidden", bool) {
152  t.set_hidden(value);
153 }
154 
155 SIDE_GETTER("scroll_to_leader", bool) {
156  return t.get_scroll_to_leader();
157 }
158 
159 SIDE_SETTER("scroll_to_leader", bool) {
160  t.set_scroll_to_leader(value);
161 }
162 
163 static void reinit_flag_for_team(const team& t) {
164  if(auto disp = game_display::get_singleton()) {
165  disp->reinit_flags_for_team(t);
166  }
167 }
168 
169 SIDE_GETTER("color", std::string) {
170  return t.color();
171 }
172 
173 SIDE_SETTER("color", std::string) {
174  t.set_color(value);
176 }
177 
178 SIDE_GETTER("flag", std::string) {
179  return t.flag().empty() ? game_config::images::flag : t.flag();
180 }
181 
182 SIDE_SETTER("flag", std::string) {
183  t.set_flag(value);
185 }
186 
187 SIDE_GETTER("flag_icon", std::string) {
188  return t.flag_icon().empty() ? game_config::images::flag_icon : t.flag_icon();
189 }
190 
191 SIDE_SETTER("flag_icon", std::string) {
192  t.set_flag_icon(value);
193 }
194 
195 SIDE_GETTER("user_team_name", t_string) {
196  return t.user_team_name();
197 }
198 
199 SIDE_SETTER("user_team_name", t_string) {
200  t.change_team(t.team_name(), value);
201 }
202 
203 SIDE_GETTER("team_name", std::string) {
204  return t.team_name();
205 }
206 
207 SIDE_SETTER("team_name", std::string) {
208  t.change_team(value, t.user_team_name());
209 }
210 
211 SIDE_GETTER("faction", std::string) {
212  return t.faction();
213 }
214 
215 SIDE_GETTER("faction_name", t_string) {
216  return t.faction_name();
217 }
218 
219 SIDE_GETTER("controller", std::string) {
220  return side_controller::get_string(t.controller());
221 }
222 
223 SIDE_SETTER("controller", std::string) {
224  t.change_controller_by_wml(value);
225 }
226 
227 SIDE_GETTER("is_local", bool) {
228  return t.is_local();
229 }
230 
231 SIDE_GETTER("defeat_condition", std::string) {
232  return defeat_condition::get_string(t.defeat_cond());
233 }
234 
235 SIDE_SETTER("defeat_condition", std::string) {
236  t.set_defeat_condition_string(value);
237 }
238 
239 SIDE_GETTER("share_vision", std::string) {
240  return team_shared_vision::get_string(t.share_vision());
241 }
242 
243 SIDE_SETTER("share_vision", std::string) {
244  auto v = team_shared_vision::get_enum(value);
245  if(v) {
246  t.set_share_vision(*v);
247  } else {
248  throw luaL_argerror(L, 3, "Invalid share_vision value (should be 'all', 'none', or 'shroud')");
249  }
250 }
251 
252 SIDE_GETTER("carryover_bonus", double) {
253  return t.carryover_bonus();
254 }
255 
256 SIDE_SETTER("carryover_bonus", double) {
257  t.set_carryover_bonus(value);
258 }
259 
260 SIDE_GETTER("carryover_percentage", int) {
261  return t.carryover_percentage();
262 }
263 
264 SIDE_SETTER("carryover_percentage", int) {
265  t.set_carryover_percentage(value);
266 }
267 
268 SIDE_GETTER("carryover_gold", int) {
269  return t.carryover_gold();
270 }
271 
272 SIDE_SETTER("carryover_gold", int) {
273  t.set_carryover_gold(value);
274 }
275 
276 SIDE_GETTER("carryover_add", bool) {
277  return t.carryover_add();
278 }
279 
280 SIDE_SETTER("carryover_add", bool) {
281  t.set_carryover_add(value);
282 }
283 
284 SIDE_GETTER("lost", bool) {
285  return t.lost();
286 }
287 
288 SIDE_SETTER("lost", bool) {
289  t.set_lost(value);
290 }
291 
292 SIDE_GETTER("persistent", bool) {
293  return t.persistent();
294 }
295 
296 SIDE_SETTER("persistent", bool) {
297  t.set_persistent(value);
298 }
299 
300 SIDE_GETTER("suppress_end_turn_confirmation", bool) {
301  return t.no_turn_confirmation();
302 }
303 
304 SIDE_SETTER("suppress_end_turn_confirmation", bool) {
305  t.set_no_turn_confirmation(value);
306 }
307 
308 SIDE_GETTER("share_maps", bool) {
309  return t.share_maps();
310 }
311 
312 SIDE_GETTER("share_view", bool) {
313  return t.share_view();
314 }
315 
316 SIDE_GETTER("chose_random", bool) {
317  return t.chose_random();
318 }
319 
320 SIDE_GETTER("side_name", t_string) {
321  return t.side_name_tstr();
322 }
323 
324 SIDE_SETTER("side_name", t_string) {
325  t.set_side_name(value);
326 }
327 
328 SIDE_GETTER("shroud_data", std::string) {
329  return t.shroud_data();
330 }
331 
332 SIDE_SETTER("shroud_data", std::string) {
333  t.reshroud();
334  t.merge_shroud_map_data(value);
335 }
336 
337 SIDE_GETTER("recruit", std::set<std::string>) {
338  return t.recruits();
339 }
340 
341 SIDE_SETTER("recruit", std::set<std::string>) {
342  t.set_recruits(value);
343 }
344 
345 SIDE_GETTER("variables", lua_index_raw) {
346  (void)t;
347  lua_createtable(L, 1, 0);
348  lua_pushvalue(L, 1);
349  lua_rawseti(L, -2, 1);
350  luaL_setmetatable(L, teamVar);
351  return lua_index_raw(L);
352 }
353 
354 SIDE_GETTER("starting_location", utils::optional<map_location>) {
355  const map_location& starting_pos = resources::gameboard->map().starting_position(t.side());
356  if(!resources::gameboard->map().on_board(starting_pos)) return utils::nullopt;
357  return starting_pos;
358 }
359 
360 SIDE_GETTER("num_units", int) {
362 }
363 
364 SIDE_GETTER("total_upkeep", int) {
366 }
367 
368 SIDE_GETTER("expenses", int) {
370 }
371 
372 SIDE_GETTER("net_income", int) {
374 }
375 
376 SIDE_GETTER("__cfg", config) {
377  config cfg;
378  t.write(cfg);
379  return cfg;
380 }
381 
382 /**
383  * Gets some data on a side (__index metamethod).
384  * - Arg 1: full userdata containing the team.
385  * - Arg 2: string containing the name of the property.
386  * - Ret 1: something containing the attribute.
387  */
388 static int impl_side_get(lua_State *L)
389 {
390  return sideReg.get(L);
391 }
392 
393 /**
394  * Gets a list of data on a side (__dir metamethod).
395  * - Arg 1: full userdata containing the team.
396  * - Ret 1: a list of attributes.
397  */
398 static int impl_side_dir(lua_State *L)
399 {
400  return sideReg.dir(L);
401 }
402 
403 /**
404  * Turns a lua proxy side to string. (__tostring metamethod)
405  */
406 static int impl_side_tostring(lua_State* L)
407 {
408  const team& team = luaW_checkteam(L, 1);
409  std::ostringstream str;
410 
411  str << "side: <" << team.side();
412  if(!team.side_name().empty()) {
413  str << " " << team.side_name();
414  }
415  str << '>';
416 
417  lua_push(L, str.str());
418  return 1;
419 }
420 
421 /**
422  * Sets some data on a side (__newindex metamethod).
423  * - Arg 1: full userdata containing the team.
424  * - Arg 2: string containing the name of the property.
425  * - Arg 3: something containing the attribute.
426  */
427 static int impl_side_set(lua_State *L)
428 {
429  return sideReg.set(L);
430 }
431 
432 static int impl_side_equal(lua_State *L)
433 {
434  // Hidden metamethod, so arg1 has to be a pointer to a team.
435  team &t1 = luaW_checkteam(L, 1);
436  if(team* t2 = luaW_toteam(L, 2)) {
437  lua_pushboolean(L, t1.side() == t2->side());
438  } else {
439  lua_pushboolean(L, false);
440  }
441  return 1;
442 }
443 
444 /**
445  * Gets the variable of a side (__index metamethod).
446  * - Arg 1: table containing the userdata containing the side id.
447  * - Arg 2: string containing the name of the status.
448  * - Ret 1: boolean.
449  */
450 static int impl_side_variables_get(lua_State *L)
451 {
452  if(!lua_istable(L, 1)) {
453  return luaW_type_error(L, 1, "side variables");
454  }
455  lua_rawgeti(L, 1, 1);
456  const team& side = luaW_checkteam(L, -1);
457 
458  char const *m = luaL_checkstring(L, 2);
459  return_cfgref_attrib("__cfg", side.variables());
460 
461  variable_access_const v(m, side.variables());
462  return luaW_pushvariable(L, v) ? 1 : 0;
463 }
464 
465 /**
466  * Sets the variable of a side (__newindex metamethod).
467  * - Arg 1: table containing the userdata containing the side id.
468  * - Arg 2: string containing the name of the status.
469  * - Arg 3: scalar.
470  */
471 static int impl_side_variables_set(lua_State *L)
472 {
473  if(!lua_istable(L, 1)) {
474  return luaW_type_error(L, 1, "side variables");
475  }
476  lua_rawgeti(L, 1, 1);
477  team& side = luaW_checkteam(L, -1);
478 
479  char const *m = luaL_checkstring(L, 2);
480  if(strcmp(m, "__cfg") == 0) {
481  side.variables() = luaW_checkconfig(L, 3);
482  return 0;
483  }
484  config& vars = side.variables();
485  if(lua_isnoneornil(L, 3)) {
486  try {
487  variable_access_throw(m, vars).clear(false);
488  } catch(const invalid_variablename_exception&) {
489  }
490  return 0;
491  }
492  variable_access_create v(m, vars);
493  luaW_checkvariable(L, v, 3);
494  return 0;
495 }
496 
497 namespace lua_team {
498 
499  std::string register_metatable(lua_State * L)
500  {
501  std::ostringstream cmd_out;
502 
503  cmd_out << "Adding getside metatable...\n";
504 
505  luaL_newmetatable(L, Team);
506 
507  static luaL_Reg const callbacks[] {
508  { "__index", &impl_side_get},
509  { "__newindex", &impl_side_set},
510  { "__dir", &impl_side_dir},
511  { "__eq", &impl_side_equal},
512  { "__tostring", &impl_side_tostring},
513  { nullptr, nullptr }
514  };
515  luaL_setfuncs(L, callbacks, 0);
516 
517  lua_pushstring(L, Team);
518  lua_setfield(L, -2, "__metatable");
519 
520  // Create the side variables metatable.
521  cmd_out << "Adding side variables metatable...\n";
522 
523  luaL_newmetatable(L, teamVar);
524  lua_pushcfunction(L, impl_side_variables_get);
525  lua_setfield(L, -2, "__index");
526  lua_pushcfunction(L, impl_side_variables_set);
527  lua_setfield(L, -2, "__newindex");
528  lua_pushstring(L, "side variables");
529  lua_setfield(L, -2, "__metatable");
530 
531  return cmd_out.str();
532  }
533 }
534 
535 void luaW_pushteam(lua_State *L, team & tm)
536 {
537  team** t = static_cast<team**>(lua_newuserdatauv(L, sizeof(team*), 0));
538  *t = &tm;
539  luaL_setmetatable(L, Team);
540 }
541 
542 team& luaW_checkteam(lua_State* L, int idx)
543 {
544  return **static_cast<team **>(luaL_checkudata(L, idx, Team));
545 }
546 
547 team& luaW_checkteam(lua_State* L, int idx, game_board& board)
548 {
549  if(lua_isinteger(L, idx)) {
550  int side = lua_tointeger(L, idx);
551  if(!board.has_team(side)) {
552  std::string error = "side " + std::to_string(side) + " does not exist";
553  luaL_argerror(L, 1, error.c_str());
554  // Unreachable
555  }
556  return board.get_team(side);
557  }
558  return **static_cast<team **>(luaL_checkudata(L, idx, Team));
559 }
560 
561 team* luaW_toteam(lua_State* L, int idx)
562 {
563  if(void* p = luaL_testudata(L, idx, Team)) {
564  return *static_cast<team **>(p);
565  }
566  return nullptr;
567 }
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:157
virtual const gamemap & map() const override
Definition: game_board.hpp:97
static game_display * get_singleton()
map_location starting_position(int side) const
Definition: map.cpp:292
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:74
const std::string & side_name() const
Definition: team.hpp:331
int side() const
Definition: team.hpp:179
const config * cfg
static int impl_side_get(lua_State *L)
Gets some data on a side (__index metamethod).
Definition: lua_team.cpp:388
#define SIDE_SETTER(name, type)
Definition: lua_team.cpp:48
static const char teamVar[]
Definition: lua_team.cpp:45
static const char * Team
Implementation for a lua reference to a team, used by the wesnoth in-game sides table.
Definition: lua_team.cpp:44
#define SIDE_GETTER(name, type)
Definition: lua_team.cpp:47
static int impl_side_tostring(lua_State *L)
Turns a lua proxy side to string.
Definition: lua_team.cpp:406
luaW_Registry sideReg
Definition: lua_team.cpp:49
static void reinit_flag_for_team(const team &t)
Definition: lua_team.cpp:163
team & luaW_checkteam(lua_State *L, int idx)
Test if the top stack element is a team, and if not, error.
Definition: lua_team.cpp:542
static int impl_side_dir(lua_State *L)
Gets a list of data on a side (__dir metamethod).
Definition: lua_team.cpp:398
std::string flag_icon
game_board * gameboard
Definition: resources.cpp:20
Holds a lookup table for members of one type of object.
int dir(lua_State *L)
Implement __dir metamethod.
int get(lua_State *L)
Implement __index metamethod.
static team & get(lua_State *L, int n)
Definition: lua_team.cpp:53
Encapsulates the map of the game.
Definition: location.hpp:46
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
static constexpr utils::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Definition: enum_base.hpp:57
static map_location::direction n