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