The Battle for Wesnoth  1.19.5+dev
lua_team.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2024
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_support(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(lua_State* L, const team& t) {
164  auto* disp = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).get_display();
165  if(disp) {
166  disp->reinit_flags_for_team(t);
167  }
168 }
169 
170 SIDE_GETTER("color", std::string) {
171  return t.color();
172 }
173 
174 SIDE_SETTER("color", std::string) {
175  t.set_color(value);
177 }
178 
179 SIDE_GETTER("flag", std::string) {
180  return t.flag().empty() ? game_config::images::flag : t.flag();
181 }
182 
183 SIDE_SETTER("flag", std::string) {
184  t.set_flag(value);
186 }
187 
188 SIDE_GETTER("flag_icon", std::string) {
189  return t.flag_icon().empty() ? game_config::images::flag_icon : t.flag_icon();
190 }
191 
192 SIDE_SETTER("flag_icon", std::string) {
193  t.set_flag_icon(value);
194 }
195 
196 SIDE_GETTER("user_team_name", t_string) {
197  return t.user_team_name();
198 }
199 
200 SIDE_SETTER("user_team_name", t_string) {
201  t.change_team(t.team_name(), value);
202 }
203 
204 SIDE_GETTER("team_name", std::string) {
205  return t.team_name();
206 }
207 
208 SIDE_SETTER("team_name", std::string) {
209  t.change_team(value, t.user_team_name());
210 }
211 
212 SIDE_GETTER("faction", std::string) {
213  return t.faction();
214 }
215 
216 SIDE_GETTER("faction_name", t_string) {
217  return t.faction_name();
218 }
219 
220 SIDE_GETTER("controller", std::string) {
221  return side_controller::get_string(t.controller());
222 }
223 
224 SIDE_SETTER("controller", std::string) {
225  t.change_controller_by_wml(value);
226 }
227 
228 SIDE_GETTER("is_local", bool) {
229  return t.is_local();
230 }
231 
232 SIDE_GETTER("defeat_condition", std::string) {
233  return defeat_condition::get_string(t.defeat_cond());
234 }
235 
236 SIDE_SETTER("defeat_condition", std::string) {
237  t.set_defeat_condition_string(value);
238 }
239 
240 SIDE_GETTER("share_vision", std::string) {
241  return team_shared_vision::get_string(t.share_vision());
242 }
243 
244 SIDE_SETTER("share_vision", std::string) {
245  auto v = team_shared_vision::get_enum(value);
246  if(v) {
247  t.set_share_vision(*v);
248  } else {
249  throw luaL_argerror(L, 3, "Invalid share_vision value (should be 'all', 'none', or 'shroud')");
250  }
251 }
252 
253 SIDE_GETTER("carryover_bonus", double) {
254  return t.carryover_bonus();
255 }
256 
257 SIDE_SETTER("carryover_bonus", double) {
258  t.set_carryover_bonus(value);
259 }
260 
261 SIDE_GETTER("carryover_percentage", int) {
262  return t.carryover_percentage();
263 }
264 
265 SIDE_SETTER("carryover_percentage", int) {
266  t.set_carryover_percentage(value);
267 }
268 
269 SIDE_GETTER("carryover_add", bool) {
270  return t.carryover_add();
271 }
272 
273 SIDE_SETTER("carryover_add", bool) {
274  t.set_carryover_add(value);
275 }
276 
277 SIDE_GETTER("lost", bool) {
278  return t.lost();
279 }
280 
281 SIDE_SETTER("lost", bool) {
282  t.set_lost(value);
283 }
284 
285 SIDE_GETTER("persistent", bool) {
286  return t.persistent();
287 }
288 
289 SIDE_SETTER("persistent", bool) {
290  t.set_persistent(value);
291 }
292 
293 SIDE_GETTER("suppress_end_turn_confirmation", bool) {
294  return t.no_turn_confirmation();
295 }
296 
297 SIDE_SETTER("suppress_end_turn_confirmation", bool) {
298  t.set_no_turn_confirmation(value);
299 }
300 
301 SIDE_GETTER("share_maps", bool) {
302  return t.share_maps();
303 }
304 
305 SIDE_GETTER("share_view", bool) {
306  return t.share_view();
307 }
308 
309 SIDE_GETTER("chose_random", bool) {
310  return t.chose_random();
311 }
312 
313 SIDE_GETTER("side_name", t_string) {
314  return t.side_name_tstr();
315 }
316 
317 SIDE_SETTER("side_name", t_string) {
318  t.set_side_name(value);
319 }
320 
321 SIDE_GETTER("shroud_data", std::string) {
322  return t.shroud_data();
323 }
324 
325 SIDE_SETTER("shroud_data", std::string) {
326  t.reshroud();
327  t.merge_shroud_map_data(value);
328 }
329 
330 SIDE_GETTER("recruit", std::set<std::string>) {
331  return t.recruits();
332 }
333 
334 SIDE_SETTER("recruit", std::set<std::string>) {
335  t.set_recruits(value);
336 }
337 
338 SIDE_GETTER("variables", lua_index_raw) {
339  (void)t;
340  lua_createtable(L, 1, 0);
341  lua_pushvalue(L, 1);
342  lua_rawseti(L, -2, 1);
343  luaL_setmetatable(L, teamVar);
344  return lua_index_raw(L);
345 }
346 
347 SIDE_GETTER("starting_location", utils::optional<map_location>) {
348  const map_location& starting_pos = resources::gameboard->map().starting_position(t.side());
349  if(!resources::gameboard->map().on_board(starting_pos)) return utils::nullopt;
350  return starting_pos;
351 }
352 
353 SIDE_GETTER("num_units", int) {
355 }
356 
357 SIDE_GETTER("total_upkeep", int) {
359 }
360 
361 SIDE_GETTER("expenses", int) {
363 }
364 
365 SIDE_GETTER("net_income", int) {
367 }
368 
369 SIDE_GETTER("__cfg", config) {
370  config cfg;
371  t.write(cfg);
372  return cfg;
373 }
374 
375 /**
376  * Gets some data on a side (__index metamethod).
377  * - Arg 1: full userdata containing the team.
378  * - Arg 2: string containing the name of the property.
379  * - Ret 1: something containing the attribute.
380  */
381 static int impl_side_get(lua_State *L)
382 {
383  return sideReg.get(L);
384 }
385 
386 /**
387  * Gets a list of data on a side (__dir metamethod).
388  * - Arg 1: full userdata containing the team.
389  * - Ret 1: a list of attributes.
390  */
391 static int impl_side_dir(lua_State *L)
392 {
393  return sideReg.dir(L);
394 }
395 
396 /**
397  * Turns a lua proxy side to string. (__tostring metamethod)
398  */
399 static int impl_side_tostring(lua_State* L)
400 {
401  const team& team = luaW_checkteam(L, 1);
402  std::ostringstream str;
403 
404  str << "side: <" << team.side();
405  if(!team.side_name().empty()) {
406  str << " " << team.side_name();
407  }
408  str << '>';
409 
410  lua_push(L, str.str());
411  return 1;
412 }
413 
414 /**
415  * Sets some data on a side (__newindex metamethod).
416  * - Arg 1: full userdata containing the team.
417  * - Arg 2: string containing the name of the property.
418  * - Arg 3: something containing the attribute.
419  */
420 static int impl_side_set(lua_State *L)
421 {
422  return sideReg.set(L);
423 }
424 
425 static int impl_side_equal(lua_State *L)
426 {
427  // Hidden metamethod, so arg1 has to be a pointer to a team.
428  team &t1 = luaW_checkteam(L, 1);
429  if(team* t2 = luaW_toteam(L, 2)) {
430  lua_pushboolean(L, t1.side() == t2->side());
431  } else {
432  lua_pushboolean(L, false);
433  }
434  return 1;
435 }
436 
437 /**
438  * Gets the variable of a side (__index metamethod).
439  * - Arg 1: table containing the userdata containing the side id.
440  * - Arg 2: string containing the name of the status.
441  * - Ret 1: boolean.
442  */
443 static int impl_side_variables_get(lua_State *L)
444 {
445  if(!lua_istable(L, 1)) {
446  return luaW_type_error(L, 1, "side variables");
447  }
448  lua_rawgeti(L, 1, 1);
449  const team& side = luaW_checkteam(L, -1);
450 
451  char const *m = luaL_checkstring(L, 2);
452  return_cfgref_attrib("__cfg", side.variables());
453 
454  variable_access_const v(m, side.variables());
455  return luaW_pushvariable(L, v) ? 1 : 0;
456 }
457 
458 /**
459  * Sets the variable of a side (__newindex metamethod).
460  * - Arg 1: table containing the userdata containing the side id.
461  * - Arg 2: string containing the name of the status.
462  * - Arg 3: scalar.
463  */
464 static int impl_side_variables_set(lua_State *L)
465 {
466  if(!lua_istable(L, 1)) {
467  return luaW_type_error(L, 1, "side variables");
468  }
469  lua_rawgeti(L, 1, 1);
470  team& side = luaW_checkteam(L, -1);
471 
472  char const *m = luaL_checkstring(L, 2);
473  if(strcmp(m, "__cfg") == 0) {
474  side.variables() = luaW_checkconfig(L, 3);
475  return 0;
476  }
477  config& vars = side.variables();
478  if(lua_isnoneornil(L, 3)) {
479  try {
480  variable_access_throw(m, vars).clear(false);
481  } catch(const invalid_variablename_exception&) {
482  }
483  return 0;
484  }
485  variable_access_create v(m, vars);
486  luaW_checkvariable(L, v, 3);
487  return 0;
488 }
489 
490 namespace lua_team {
491 
492  std::string register_metatable(lua_State * L)
493  {
494  std::ostringstream cmd_out;
495 
496  cmd_out << "Adding getside metatable...\n";
497 
498  luaL_newmetatable(L, Team);
499 
500  static luaL_Reg const callbacks[] {
501  { "__index", &impl_side_get},
502  { "__newindex", &impl_side_set},
503  { "__dir", &impl_side_dir},
504  { "__eq", &impl_side_equal},
505  { "__tostring", &impl_side_tostring},
506  { nullptr, nullptr }
507  };
508  luaL_setfuncs(L, callbacks, 0);
509 
510  lua_pushstring(L, Team);
511  lua_setfield(L, -2, "__metatable");
512 
513  // Create the side variables metatable.
514  cmd_out << "Adding side variables metatable...\n";
515 
516  luaL_newmetatable(L, teamVar);
517  lua_pushcfunction(L, impl_side_variables_get);
518  lua_setfield(L, -2, "__index");
519  lua_pushcfunction(L, impl_side_variables_set);
520  lua_setfield(L, -2, "__newindex");
521  lua_pushstring(L, "side variables");
522  lua_setfield(L, -2, "__metatable");
523 
524  return cmd_out.str();
525  }
526 }
527 
528 void luaW_pushteam(lua_State *L, team & tm)
529 {
530  team** t = static_cast<team**>(lua_newuserdatauv(L, sizeof(team*), 0));
531  *t = &tm;
532  luaL_setmetatable(L, Team);
533 }
534 
535 team& luaW_checkteam(lua_State* L, int idx)
536 {
537  return **static_cast<team **>(luaL_checkudata(L, idx, Team));
538 }
539 
540 team& luaW_checkteam(lua_State* L, int idx, game_board& board)
541 {
542  if(lua_isinteger(L, idx)) {
543  int side = lua_tointeger(L, idx);
544  if(!board.has_team(side)) {
545  std::string error = "side " + std::to_string(side) + " does not exist";
546  luaL_argerror(L, 1, error.c_str());
547  // Unreachable
548  }
549  return board.get_team(side);
550  }
551  return **static_cast<team **>(luaL_checkudata(L, idx, Team));
552 }
553 
554 team* luaW_toteam(lua_State* L, int idx)
555 {
556  if(void* p = luaL_testudata(L, idx, Team)) {
557  return *static_cast<team **>(p);
558  }
559  return nullptr;
560 }
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:172
virtual const gamemap & map() const override
Definition: game_board.hpp:97
map_location starting_position(int side) const
Definition: map.cpp:324
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:75
const std::string & side_name() const
Definition: team.hpp:293
int side() const
Definition: team.hpp:175
static int impl_side_get(lua_State *L)
Gets some data on a side (__index metamethod).
Definition: lua_team.cpp:381
#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 void reinit_flag_for_team(lua_State *L, const team &t)
Definition: lua_team.cpp:163
static int impl_side_tostring(lua_State *L)
Turns a lua proxy side to string.
Definition: lua_team.cpp:399
luaW_Registry sideReg
Definition: lua_team.cpp:49
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:535
static int impl_side_dir(lua_State *L)
Gets a list of data on a side (__dir metamethod).
Definition: lua_team.cpp:391
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:45
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