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