The Battle for Wesnoth  1.19.0-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 
18 #include "scripting/lua_common.hpp"
19 #include "scripting/push_check.hpp"
21 #include "team.hpp"
22 #include "resources.hpp" // for gameboard
23 #include "game_board.hpp"
24 #include "game_display.hpp"
25 #include "map/map.hpp"
26 
27 #include <string>
28 
29 
30 /**
31  * Implementation for a lua reference to a team,
32  * used by the wesnoth in-game sides table.
33  *
34  * (The userdata has type team** because lua holds
35  * only a pointer to a team, not a full-size team.
36  * If it were a full object then we would cast to
37  * type team *, since checkudata returns a pointer
38  * to the type corresponding to the sizeof expr
39  * used when we allocated the userdata.)
40  */
41 
42 // Registry key
43 static const char * Team = "side";
44 static const char teamVar[] = "side variables";
45 
46 /**
47  * Gets some data on a side (__index metamethod).
48  * - Arg 1: full userdata containing the team.
49  * - Arg 2: string containing the name of the property.
50  * - Ret 1: something containing the attribute.
51  */
52 static int impl_side_get(lua_State *L)
53 {
54  // Hidden metamethod, so arg1 has to be a pointer to a team.
55  team &t = luaW_checkteam(L, 1);
56  char const *m = luaL_checkstring(L, 2);
57 
58  // Find the corresponding attribute.
59  return_int_attrib("side", t.side());
60  return_string_attrib("save_id", t.save_id());
61  return_int_attrib("gold", t.gold());
62  return_tstring_attrib("objectives", t.objectives());
63  return_int_attrib("village_gold", t.village_gold());
64  return_int_attrib("village_support", t.village_support());
65  return_int_attrib("num_villages", t.villages().size());
66  return_int_attrib("recall_cost", t.recall_cost());
67  return_int_attrib("base_income", t.base_income());
68  return_int_attrib("total_income", t.total_income());
69  return_bool_attrib("objectives_changed", t.objectives_changed());
70  return_bool_attrib("fog", t.uses_fog());
71  return_bool_attrib("shroud", t.uses_shroud());
72  return_bool_attrib("hidden", t.hidden());
73  return_bool_attrib("scroll_to_leader", t.get_scroll_to_leader());
74  return_string_attrib("flag", t.flag().empty() ? game_config::images::flag : t.flag());
75  return_string_attrib("flag_icon", t.flag_icon().empty() ? game_config::images::flag_icon : t.flag_icon());
76  return_tstring_attrib("user_team_name", t.user_team_name());
77  return_string_attrib("team_name", t.team_name());
78  return_string_attrib("faction", t.faction());
79  return_tstring_attrib("faction_name", t.faction_name());
80  return_string_attrib("color", t.color());
81  return_string_attrib("controller", side_controller::get_string(t.controller()));
82  return_bool_attrib("is_local", t.is_local());
83  return_string_attrib("defeat_condition", defeat_condition::get_string(t.defeat_cond()));
84  return_string_attrib("share_vision", team_shared_vision::get_string(t.share_vision()));
85  return_float_attrib("carryover_bonus", t.carryover_bonus());
86  return_int_attrib("carryover_percentage", t.carryover_percentage());
87  return_bool_attrib("carryover_add", t.carryover_add());
88  return_bool_attrib("lost", t.lost());
89  return_bool_attrib("persistent", t.persistent());
90  return_bool_attrib("suppress_end_turn_confirmation", t.no_turn_confirmation());
91  return_bool_attrib("share_maps", t.share_maps());
92  return_bool_attrib("share_view", t.share_view());
93  return_bool_attrib("chose_random", t.chose_random());
94  return_tstring_attrib("side_name", t.side_name_tstr());
95  return_string_attrib("shroud_data", t.shroud_data());
96 
97  if (strcmp(m, "recruit") == 0) {
98  const std::set<std::string>& recruits = t.recruits();
99  lua_createtable(L, recruits.size(), 0);
100  int i = 1;
101  for (const std::string& r : t.recruits()) {
102  lua_pushstring(L, r.c_str());
103  lua_rawseti(L, -2, i++);
104  }
105  return 1;
106  }
107  if(strcmp(m, "variables") == 0) {
108  lua_createtable(L, 1, 0);
109  lua_pushvalue(L, 1);
110  lua_rawseti(L, -2, 1);
111  luaL_setmetatable(L, teamVar);
112  return 1;
113  }
114  if(strcmp(m, "starting_location") == 0) {
115  const map_location& starting_pos = resources::gameboard->map().starting_position(t.side());
116  if(!resources::gameboard->map().on_board(starting_pos)) return 0;
117 
118  luaW_pushlocation(L, starting_pos);
119  return 1;
120  }
121 
122  // These are blocked together because they are all part of the team_data struct.
123  // Some of these values involve iterating over the units map to calculate them.
124  auto d = [&](){ return team_data(*resources::gameboard, t); };
125  return_int_attrib("num_units", d().units);
126  return_int_attrib("total_upkeep", d().upkeep);
127  return_int_attrib("expenses", d().expenses);
128  return_int_attrib("net_income", d().net_income);
129 
130  return_cfg_attrib("__cfg", t.write(cfg));
131  if(luaW_getglobal(L, "wesnoth", "sides", m)) {
132  return 1;
133  }
134  return 0;
135 }
136 
137 /**
138  * Turns a lua proxy side to string. (__tostring metamethod)
139  */
140 static int impl_side_tostring(lua_State* L)
141 {
142  const team& team = luaW_checkteam(L, 1);
143  std::ostringstream str;
144 
145  str << "side: <" << team.side();
146  if(!team.side_name().empty()) {
147  str << " " << team.side_name();
148  }
149  str << '>';
150 
151  lua_push(L, str.str());
152  return 1;
153 }
154 
155 /**
156  * Sets some data on a side (__newindex metamethod).
157  * - Arg 1: full userdata containing the team.
158  * - Arg 2: string containing the name of the property.
159  * - Arg 3: something containing the attribute.
160  */
161 static int impl_side_set(lua_State *L)
162 {
163  // Hidden metamethod, so arg1 has to be a pointer to a team.
164  team &t = luaW_checkteam(L, 1);
165  char const *m = luaL_checkstring(L, 2);
166 
167  const auto& reinit_flag_for_team = [&L] (const team& t) -> void {
168  auto* disp = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).get_display();
169  if(disp) {
170  disp->reinit_flags_for_team(t);
171  }
172  };
173  // Find the corresponding attribute.
174  modify_int_attrib("gold", t.set_gold(value));
175  modify_tstring_attrib("objectives", t.set_objectives(value, true));
176  //maybe add a setter for save_id too?
177  modify_int_attrib("village_gold", t.set_village_gold(value));
178  modify_int_attrib("village_support", t.set_village_support(value));
179  modify_int_attrib("recall_cost", t.set_recall_cost(value));
180  modify_int_attrib("base_income", t.set_base_income(value));
181  modify_bool_attrib("objectives_changed", t.set_objectives_changed(value));
182  modify_bool_attrib("hidden", t.set_hidden(value));
183  modify_bool_attrib("scroll_to_leader", t.set_scroll_to_leader(value));
184  modify_string_attrib("flag", {
185  t.set_flag(value);
186  reinit_flag_for_team(t);
187  });
188  modify_string_attrib("flag_icon", t.set_flag_icon(value));
189  modify_tstring_attrib("user_team_name", t.change_team(t.team_name(), value));
190  modify_string_attrib("team_name", t.change_team(value, t.user_team_name()));
191  modify_string_attrib("controller", t.change_controller_by_wml(value));
192  modify_string_attrib("color", {
193  t.set_color(value);
194  reinit_flag_for_team(t);
195  });
196  modify_string_attrib("defeat_condition", t.set_defeat_condition_string(value));
197  modify_int_attrib("carryover_percentage", t.set_carryover_percentage(value));
198  modify_bool_attrib("carryover_add", t.set_carryover_add(value));
199  modify_bool_attrib("lost", t.set_lost(value));
200  modify_bool_attrib("persistent", t.set_persistent(value));
201  modify_bool_attrib("suppress_end_turn_confirmation", t.set_no_turn_confirmation(value));
202  modify_bool_attrib("shroud", t.set_shroud(value));
203  modify_bool_attrib("fog", t.set_fog(value));
204  modify_string_attrib("flag_icon", t.set_flag_icon(value));
205  modify_tstring_attrib("side_name", t.set_side_name(value));
206  modify_string_attrib("shroud_data", t.reshroud(); t.merge_shroud_map_data(value));
207  modify_string_attrib("share_vision", {
208  auto v = team_shared_vision::get_enum(value);
209  if(v) {
210  t.set_share_vision(*v);
211  } else {
212  return luaL_argerror(L, 3, "Invalid share_vision value (should be 'all', 'none', or 'shroud')");
213  }
214  });
215 
216  if (strcmp(m, "carryover_bonus") == 0) {
217  t.set_carryover_bonus(luaL_checknumber(L, 3));
218  return 0;
219  }
220 
221  if (strcmp(m, "recruit") == 0) {
222  t.set_recruits(std::set<std::string>());
223  if (!lua_istable(L, 3)) return 0;
224  for (int i = 1;; ++i) {
225  lua_rawgeti(L, 3, i);
226  if (lua_isnil(L, -1)) break;
227  t.add_recruit(lua_tostring(L, -1));
228  lua_pop(L, 1);
229  }
230  return 0;
231  }
232 
233  std::string err_msg = "unknown modifiable property of side: ";
234  err_msg += m;
235  return luaL_argerror(L, 2, err_msg.c_str());
236 }
237 
238 static int impl_side_equal(lua_State *L)
239 {
240  // Hidden metamethod, so arg1 has to be a pointer to a team.
241  team &t1 = luaW_checkteam(L, 1);
242  if(team* t2 = luaW_toteam(L, 2)) {
243  lua_pushboolean(L, t1.side() == t2->side());
244  } else {
245  lua_pushboolean(L, false);
246  }
247  return 1;
248 }
249 
250 /**
251  * Gets the variable of a side (__index metamethod).
252  * - Arg 1: table containing the userdata containing the side id.
253  * - Arg 2: string containing the name of the status.
254  * - Ret 1: boolean.
255  */
256 static int impl_side_variables_get(lua_State *L)
257 {
258  if(!lua_istable(L, 1)) {
259  return luaW_type_error(L, 1, "side variables");
260  }
261  lua_rawgeti(L, 1, 1);
262  const team& side = luaW_checkteam(L, -1);
263 
264  char const *m = luaL_checkstring(L, 2);
265  return_cfgref_attrib("__cfg", side.variables());
266 
267  variable_access_const v(m, side.variables());
268  return luaW_pushvariable(L, v) ? 1 : 0;
269 }
270 
271 /**
272  * Sets the variable of a side (__newindex metamethod).
273  * - Arg 1: table containing the userdata containing the side id.
274  * - Arg 2: string containing the name of the status.
275  * - Arg 3: scalar.
276  */
277 static int impl_side_variables_set(lua_State *L)
278 {
279  if(!lua_istable(L, 1)) {
280  return luaW_type_error(L, 1, "side variables");
281  }
282  lua_rawgeti(L, 1, 1);
283  team& side = luaW_checkteam(L, -1);
284 
285  char const *m = luaL_checkstring(L, 2);
286  if(strcmp(m, "__cfg") == 0) {
287  side.variables() = luaW_checkconfig(L, 3);
288  return 0;
289  }
290  config& vars = side.variables();
291  if(lua_isnoneornil(L, 3)) {
292  try {
293  variable_access_throw(m, vars).clear(false);
294  } catch(const invalid_variablename_exception&) {
295  }
296  return 0;
297  }
298  variable_access_create v(m, vars);
299  luaW_checkvariable(L, v, 3);
300  return 0;
301 }
302 
303 namespace lua_team {
304 
305  std::string register_metatable(lua_State * L)
306  {
307  std::ostringstream cmd_out;
308 
309  cmd_out << "Adding getside metatable...\n";
310 
311  luaL_newmetatable(L, Team);
312 
313  static luaL_Reg const callbacks[] {
314  { "__index", &impl_side_get},
315  { "__newindex", &impl_side_set},
316  { "__eq", &impl_side_equal},
317  { "__tostring", &impl_side_tostring},
318  { nullptr, nullptr }
319  };
320  luaL_setfuncs(L, callbacks, 0);
321 
322  lua_pushstring(L, Team);
323  lua_setfield(L, -2, "__metatable");
324 
325  // Create the side variables metatable.
326  cmd_out << "Adding side variables metatable...\n";
327 
328  luaL_newmetatable(L, teamVar);
329  lua_pushcfunction(L, impl_side_variables_get);
330  lua_setfield(L, -2, "__index");
331  lua_pushcfunction(L, impl_side_variables_set);
332  lua_setfield(L, -2, "__newindex");
333  lua_pushstring(L, "side variables");
334  lua_setfield(L, -2, "__metatable");
335 
336  return cmd_out.str();
337  }
338 }
339 
340 void luaW_pushteam(lua_State *L, team & tm)
341 {
342  team** t = static_cast<team**>(lua_newuserdatauv(L, sizeof(team*), 0));
343  *t = &tm;
344  luaL_setmetatable(L, Team);
345 }
346 
347 team& luaW_checkteam(lua_State* L, int idx)
348 {
349  return **static_cast<team **>(luaL_checkudata(L, idx, Team));
350 }
351 
352 team& luaW_checkteam(lua_State* L, int idx, game_board& board)
353 {
354  if(lua_isinteger(L, idx)) {
355  int side = lua_tointeger(L, idx);
356  if(!board.has_team(side)) {
357  std::string error = "side " + std::to_string(side) + " does not exist";
358  luaL_argerror(L, 1, error.c_str());
359  // Unreachable
360  }
361  return board.get_team(side);
362  }
363  return **static_cast<team **>(luaL_checkudata(L, idx, Team));
364 }
365 
366 team* luaW_toteam(lua_State* L, int idx)
367 {
368  if(void* p = luaL_testudata(L, idx, Team)) {
369  return *static_cast<team **>(p);
370  }
371  return nullptr;
372 }
double t
Definition: astarsearch.cpp:63
virtual const gamemap & map() const override
Definition: game_board.hpp:96
map_location starting_position(int side) const
Definition: map.cpp:323
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:293
int side() const
Definition: team.hpp:174
std::size_t i
Definition: function.cpp:968
void luaW_pushlocation(lua_State *L, const map_location &ml)
Converts a map location object to a Lua table pushed at the top of the stack.
Definition: lua_common.cpp:730
bool luaW_getglobal(lua_State *L, const std::vector< std::string > &path)
Pushes the value found by following the variadic names (char *), if the value is not nil.
Definition: lua_common.cpp:969
#define return_float_attrib(name, accessor)
Definition: lua_common.hpp:277
#define return_string_attrib(name, accessor)
Definition: lua_common.hpp:256
#define return_int_attrib(name, accessor)
Definition: lua_common.hpp:267
#define return_bool_attrib(name, accessor)
Definition: lua_common.hpp:287
#define return_tstring_attrib(name, accessor)
Definition: lua_common.hpp:236
#define return_cfg_attrib(name, accessor)
Definition: lua_common.hpp:297
static int impl_side_get(lua_State *L)
Gets some data on a side (__index metamethod).
Definition: lua_team.cpp:52
static const char teamVar[]
Definition: lua_team.cpp:44
static const char * Team
Implementation for a lua reference to a team, used by the wesnoth in-game sides table.
Definition: lua_team.cpp:43
static int impl_side_tostring(lua_State *L)
Turns a lua proxy side to string.
Definition: lua_team.cpp:140
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:347
std::string flag_icon
bool shroud()
Definition: game.cpp:532
game_board * gameboard
Definition: resources.cpp:20
Encapsulates the map of the game.
Definition: location.hpp:38
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
#define d