The Battle for Wesnoth  1.19.7+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_gold", int) {
270  return t.carryover_gold();
271 }
272 
273 SIDE_SETTER("carryover_gold", int) {
274  t.set_carryover_gold(value);
275 }
276 
277 SIDE_GETTER("carryover_add", bool) {
278  return t.carryover_add();
279 }
280 
281 SIDE_SETTER("carryover_add", bool) {
282  t.set_carryover_add(value);
283 }
284 
285 SIDE_GETTER("lost", bool) {
286  return t.lost();
287 }
288 
289 SIDE_SETTER("lost", bool) {
290  t.set_lost(value);
291 }
292 
293 SIDE_GETTER("persistent", bool) {
294  return t.persistent();
295 }
296 
297 SIDE_SETTER("persistent", bool) {
298  t.set_persistent(value);
299 }
300 
301 SIDE_GETTER("suppress_end_turn_confirmation", bool) {
302  return t.no_turn_confirmation();
303 }
304 
305 SIDE_SETTER("suppress_end_turn_confirmation", bool) {
306  t.set_no_turn_confirmation(value);
307 }
308 
309 SIDE_GETTER("share_maps", bool) {
310  return t.share_maps();
311 }
312 
313 SIDE_GETTER("share_view", bool) {
314  return t.share_view();
315 }
316 
317 SIDE_GETTER("chose_random", bool) {
318  return t.chose_random();
319 }
320 
321 SIDE_GETTER("side_name", t_string) {
322  return t.side_name_tstr();
323 }
324 
325 SIDE_SETTER("side_name", t_string) {
326  t.set_side_name(value);
327 }
328 
329 SIDE_GETTER("shroud_data", std::string) {
330  return t.shroud_data();
331 }
332 
333 SIDE_SETTER("shroud_data", std::string) {
334  t.reshroud();
335  t.merge_shroud_map_data(value);
336 }
337 
338 SIDE_GETTER("recruit", std::set<std::string>) {
339  return t.recruits();
340 }
341 
342 SIDE_SETTER("recruit", std::set<std::string>) {
343  t.set_recruits(value);
344 }
345 
346 SIDE_GETTER("variables", lua_index_raw) {
347  (void)t;
348  lua_createtable(L, 1, 0);
349  lua_pushvalue(L, 1);
350  lua_rawseti(L, -2, 1);
351  luaL_setmetatable(L, teamVar);
352  return lua_index_raw(L);
353 }
354 
355 SIDE_GETTER("starting_location", utils::optional<map_location>) {
356  const map_location& starting_pos = resources::gameboard->map().starting_position(t.side());
357  if(!resources::gameboard->map().on_board(starting_pos)) return utils::nullopt;
358  return starting_pos;
359 }
360 
361 SIDE_GETTER("num_units", int) {
363 }
364 
365 SIDE_GETTER("total_upkeep", int) {
367 }
368 
369 SIDE_GETTER("expenses", int) {
371 }
372 
373 SIDE_GETTER("net_income", int) {
375 }
376 
377 SIDE_GETTER("__cfg", config) {
378  config cfg;
379  t.write(cfg);
380  return cfg;
381 }
382 
383 /**
384  * Gets some data on a side (__index metamethod).
385  * - Arg 1: full userdata containing the team.
386  * - Arg 2: string containing the name of the property.
387  * - Ret 1: something containing the attribute.
388  */
389 static int impl_side_get(lua_State *L)
390 {
391  return sideReg.get(L);
392 }
393 
394 /**
395  * Gets a list of data on a side (__dir metamethod).
396  * - Arg 1: full userdata containing the team.
397  * - Ret 1: a list of attributes.
398  */
399 static int impl_side_dir(lua_State *L)
400 {
401  return sideReg.dir(L);
402 }
403 
404 /**
405  * Turns a lua proxy side to string. (__tostring metamethod)
406  */
407 static int impl_side_tostring(lua_State* L)
408 {
409  const team& team = luaW_checkteam(L, 1);
410  std::ostringstream str;
411 
412  str << "side: <" << team.side();
413  if(!team.side_name().empty()) {
414  str << " " << team.side_name();
415  }
416  str << '>';
417 
418  lua_push(L, str.str());
419  return 1;
420 }
421 
422 /**
423  * Sets some data on a side (__newindex metamethod).
424  * - Arg 1: full userdata containing the team.
425  * - Arg 2: string containing the name of the property.
426  * - Arg 3: something containing the attribute.
427  */
428 static int impl_side_set(lua_State *L)
429 {
430  return sideReg.set(L);
431 }
432 
433 static int impl_side_equal(lua_State *L)
434 {
435  // Hidden metamethod, so arg1 has to be a pointer to a team.
436  team &t1 = luaW_checkteam(L, 1);
437  if(team* t2 = luaW_toteam(L, 2)) {
438  lua_pushboolean(L, t1.side() == t2->side());
439  } else {
440  lua_pushboolean(L, false);
441  }
442  return 1;
443 }
444 
445 /**
446  * Gets the variable of a side (__index metamethod).
447  * - Arg 1: table containing the userdata containing the side id.
448  * - Arg 2: string containing the name of the status.
449  * - Ret 1: boolean.
450  */
451 static int impl_side_variables_get(lua_State *L)
452 {
453  if(!lua_istable(L, 1)) {
454  return luaW_type_error(L, 1, "side variables");
455  }
456  lua_rawgeti(L, 1, 1);
457  const team& side = luaW_checkteam(L, -1);
458 
459  char const *m = luaL_checkstring(L, 2);
460  return_cfgref_attrib("__cfg", side.variables());
461 
462  variable_access_const v(m, side.variables());
463  return luaW_pushvariable(L, v) ? 1 : 0;
464 }
465 
466 /**
467  * Sets the variable of a side (__newindex metamethod).
468  * - Arg 1: table containing the userdata containing the side id.
469  * - Arg 2: string containing the name of the status.
470  * - Arg 3: scalar.
471  */
472 static int impl_side_variables_set(lua_State *L)
473 {
474  if(!lua_istable(L, 1)) {
475  return luaW_type_error(L, 1, "side variables");
476  }
477  lua_rawgeti(L, 1, 1);
478  team& side = luaW_checkteam(L, -1);
479 
480  char const *m = luaL_checkstring(L, 2);
481  if(strcmp(m, "__cfg") == 0) {
482  side.variables() = luaW_checkconfig(L, 3);
483  return 0;
484  }
485  config& vars = side.variables();
486  if(lua_isnoneornil(L, 3)) {
487  try {
488  variable_access_throw(m, vars).clear(false);
489  } catch(const invalid_variablename_exception&) {
490  }
491  return 0;
492  }
493  variable_access_create v(m, vars);
494  luaW_checkvariable(L, v, 3);
495  return 0;
496 }
497 
498 namespace lua_team {
499 
500  std::string register_metatable(lua_State * L)
501  {
502  std::ostringstream cmd_out;
503 
504  cmd_out << "Adding getside metatable...\n";
505 
506  luaL_newmetatable(L, Team);
507 
508  static luaL_Reg const callbacks[] {
509  { "__index", &impl_side_get},
510  { "__newindex", &impl_side_set},
511  { "__dir", &impl_side_dir},
512  { "__eq", &impl_side_equal},
513  { "__tostring", &impl_side_tostring},
514  { nullptr, nullptr }
515  };
516  luaL_setfuncs(L, callbacks, 0);
517 
518  lua_pushstring(L, Team);
519  lua_setfield(L, -2, "__metatable");
520 
521  // Create the side variables metatable.
522  cmd_out << "Adding side variables metatable...\n";
523 
524  luaL_newmetatable(L, teamVar);
525  lua_pushcfunction(L, impl_side_variables_get);
526  lua_setfield(L, -2, "__index");
527  lua_pushcfunction(L, impl_side_variables_set);
528  lua_setfield(L, -2, "__newindex");
529  lua_pushstring(L, "side variables");
530  lua_setfield(L, -2, "__metatable");
531 
532  return cmd_out.str();
533  }
534 }
535 
536 void luaW_pushteam(lua_State *L, team & tm)
537 {
538  team** t = static_cast<team**>(lua_newuserdatauv(L, sizeof(team*), 0));
539  *t = &tm;
540  luaL_setmetatable(L, Team);
541 }
542 
543 team& luaW_checkteam(lua_State* L, int idx)
544 {
545  return **static_cast<team **>(luaL_checkudata(L, idx, Team));
546 }
547 
548 team& luaW_checkteam(lua_State* L, int idx, game_board& board)
549 {
550  if(lua_isinteger(L, idx)) {
551  int side = lua_tointeger(L, idx);
552  if(!board.has_team(side)) {
553  std::string error = "side " + std::to_string(side) + " does not exist";
554  luaL_argerror(L, 1, error.c_str());
555  // Unreachable
556  }
557  return board.get_team(side);
558  }
559  return **static_cast<team **>(luaL_checkudata(L, idx, Team));
560 }
561 
562 team* luaW_toteam(lua_State* L, int idx)
563 {
564  if(void* p = luaL_testudata(L, idx, Team)) {
565  return *static_cast<team **>(p);
566  }
567  return nullptr;
568 }
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:389
#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:407
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:543
static int impl_side_dir(lua_State *L)
Gets a list of data on a side (__dir metamethod).
Definition: lua_team.cpp:399
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