The Battle for Wesnoth  1.17.0-dev
lua_team.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2018 by Chris Beck <render787@gmail.com>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "scripting/lua_team.hpp"
16 
17 #include "scripting/lua_common.hpp"
18 #include "scripting/push_check.hpp"
20 #include "team.hpp"
21 #include "resources.hpp" // for gameboard
22 #include "game_board.hpp"
23 #include "game_display.hpp"
24 #include "map/map.hpp"
25 
26 #include <string>
27 
28 #include "lua/lua.h"
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());
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_cstring_attrib("controller", t.controller().to_string().c_str());
83  return_bool_attrib("is_local", t.is_local());
84  return_string_attrib("defeat_condition", t.defeat_condition().to_string());
85  return_string_attrib("share_vision", t.share_vision().to_string());
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_string_attrib("share_vision", t.share_vision().to_string());
93  return_bool_attrib("share_maps", t.share_maps());
94  return_bool_attrib("share_view", t.share_view());
95  return_bool_attrib("chose_random", t.chose_random());
96  return_tstring_attrib("side_name", t.side_name_tstr());
97  return_string_attrib("shroud_data", t.shroud_data());
98 
99  if (strcmp(m, "recruit") == 0) {
100  const std::set<std::string>& recruits = t.recruits();
101  lua_createtable(L, recruits.size(), 0);
102  int i = 1;
103  for (const std::string& r : t.recruits()) {
104  lua_pushstring(L, r.c_str());
105  lua_rawseti(L, -2, i++);
106  }
107  return 1;
108  }
109  if(strcmp(m, "variables") == 0) {
110  lua_createtable(L, 1, 0);
111  lua_pushvalue(L, 1);
112  lua_rawseti(L, -2, 1);
114  return 1;
115  }
116  if(strcmp(m, "starting_location") == 0) {
117  const map_location& starting_pos = resources::gameboard->map().starting_position(t.side());
118  if(!resources::gameboard->map().on_board(starting_pos)) return 0;
119 
120  luaW_pushlocation(L, starting_pos);
121  return 1;
122  }
123 
124  // These are blocked together because they are all part of the team_data struct.
125  // Some of these values involve iterating over the units map to calculate them.
126  auto d = [&](){ return team_data(*resources::gameboard, t); };
127  return_int_attrib("num_units", d().units);
128  return_int_attrib("total_upkeep", d().upkeep);
129  return_int_attrib("expenses", d().expenses);
130  return_int_attrib("net_income", d().net_income);
131 
132  return_cfg_attrib("__cfg", t.write(cfg));
133  if(luaW_getglobal(L, "wesnoth", "sides", m)) {
134  return 1;
135  }
136  return 0;
137 }
138 
139 /**
140  * Turns a lua proxy side to string. (__tostring metamethod)
141  */
143 {
144  const team& team = luaW_checkteam(L, 1);
145  std::ostringstream str;
146 
147  str << "side: <" << team.side();
148  if(!team.side_name().empty()) {
149  str << " " << team.side_name();
150  }
151  str << '>';
152 
153  lua_push(L, str.str());
154  return 1;
155 }
156 
157 /**
158  * Sets some data on a side (__newindex metamethod).
159  * - Arg 1: full userdata containing the team.
160  * - Arg 2: string containing the name of the property.
161  * - Arg 3: something containing the attribute.
162  */
163 static int impl_side_set(lua_State *L)
164 {
165  // Hidden metamethod, so arg1 has to be a pointer to a team.
166  team &t = luaW_checkteam(L, 1);
167  char const *m = luaL_checkstring(L, 2);
168 
169  const auto& reinit_flag_for_team = [&L] (const team& t) -> void {
170  auto* disp = lua_kernel_base::get_lua_kernel<game_lua_kernel>(L).get_display();
171  if(disp) {
172  disp->reinit_flags_for_team(t);
173  }
174  };
175  // Find the corresponding attribute.
176  modify_int_attrib("gold", t.set_gold(value));
177  modify_tstring_attrib("objectives", t.set_objectives(value, true));
178  //maybe add a setter for save_id too?
179  modify_int_attrib("village_gold", t.set_village_gold(value));
180  modify_int_attrib("village_support", t.set_village_support(value));
181  modify_int_attrib("recall_cost", t.set_recall_cost(value));
182  modify_int_attrib("base_income", t.set_base_income(value));
183  modify_bool_attrib("objectives_changed", t.set_objectives_changed(value));
184  modify_bool_attrib("hidden", t.set_hidden(value));
185  modify_bool_attrib("scroll_to_leader", t.set_scroll_to_leader(value));
186  modify_string_attrib("flag", {
187  t.set_flag(value);
188  reinit_flag_for_team(t);
189  });
190  modify_string_attrib("flag_icon", t.set_flag_icon(value));
191  modify_tstring_attrib("user_team_name", t.change_team(t.team_name(), value));
192  modify_string_attrib("team_name", t.change_team(value, t.user_team_name()));
193  modify_string_attrib("controller", t.change_controller_by_wml(value));
194  modify_string_attrib("color", {
195  t.set_color(value);
196  reinit_flag_for_team(t);
197  });
198  modify_string_attrib("defeat_condition", t.set_defeat_condition_string(value));
199  modify_int_attrib("carryover_percentage", t.set_carryover_percentage(value));
200  modify_bool_attrib("carryover_add", t.set_carryover_add(value));
201  modify_bool_attrib("lost", t.set_lost(value));
202  modify_bool_attrib("persistent", t.set_persistent(value));
203  modify_bool_attrib("suppress_end_turn_confirmation", t.set_no_turn_confirmation(value));
204  modify_bool_attrib("shroud", t.set_shroud(value));
205  modify_bool_attrib("fog", t.set_fog(value));
206  modify_string_attrib("flag_icon", t.set_flag_icon(value));
207  modify_tstring_attrib("side_name", t.set_side_name(value));
208  modify_string_attrib("shroud_data", t.reshroud(); t.merge_shroud_map_data(value));
209  modify_string_attrib("share_vision", {
210  team::SHARE_VISION v;
211  if(v.parse(value)) {
212  t.set_share_vision(v);
213  } else {
214  return luaL_argerror(L, 3, "Invalid share_vision value (should be 'all', 'none', or 'shroud')");
215  }
216  });
217 
218  if (strcmp(m, "carryover_bonus") == 0) {
219  t.set_carryover_bonus(luaL_checknumber(L, 3));
220  return 0;
221  }
222 
223  if (strcmp(m, "recruit") == 0) {
224  t.set_recruits(std::set<std::string>());
225  if (!lua_istable(L, 3)) return 0;
226  for (int i = 1;; ++i) {
227  lua_rawgeti(L, 3, i);
228  if (lua_isnil(L, -1)) break;
229  t.add_recruit(lua_tostring(L, -1));
230  lua_pop(L, 1);
231  }
232  return 0;
233  }
234 
235  std::string err_msg = "unknown modifiable property of side: ";
236  err_msg += m;
237  return luaL_argerror(L, 2, err_msg.c_str());
238 }
239 
240 static int impl_side_equal(lua_State *L)
241 {
242  // Hidden metamethod, so arg1 has to be a pointer to a team.
243  team &t1 = luaW_checkteam(L, 1);
244  if(team* t2 = luaW_toteam(L, 2)) {
245  lua_pushboolean(L, t1.side() == t2->side());
246  } else {
247  lua_pushboolean(L, false);
248  }
249  return 1;
250 }
251 
252 /**
253  * Gets the variable of a side (__index metamethod).
254  * - Arg 1: table containing the userdata containing the side id.
255  * - Arg 2: string containing the name of the status.
256  * - Ret 1: boolean.
257  */
258 static int impl_side_variables_get(lua_State *L)
259 {
260  if(!lua_istable(L, 1)) {
261  return luaW_type_error(L, 1, "side variables");
262  }
263  lua_rawgeti(L, 1, 1);
264  const team& side = luaW_checkteam(L, -1);
265 
266  char const *m = luaL_checkstring(L, 2);
267  return_cfgref_attrib("__cfg", side.variables());
268 
269  variable_access_const v(m, side.variables());
270  return luaW_pushvariable(L, v) ? 1 : 0;
271 }
272 
273 /**
274  * Sets the variable of a side (__newindex metamethod).
275  * - Arg 1: table containing the userdata containing the side id.
276  * - Arg 2: string containing the name of the status.
277  * - Arg 3: scalar.
278  */
279 static int impl_side_variables_set(lua_State *L)
280 {
281  if(!lua_istable(L, 1)) {
282  return luaW_type_error(L, 1, "side variables");
283  }
284  lua_rawgeti(L, 1, 1);
285  team& side = luaW_checkteam(L, -1);
286 
287  char const *m = luaL_checkstring(L, 2);
288  if(strcmp(m, "__cfg") == 0) {
289  side.variables() = luaW_checkconfig(L, 3);
290  return 0;
291  }
292  config& vars = side.variables();
293  if(lua_isnoneornil(L, 3)) {
294  try {
295  variable_access_throw(m, vars).clear(false);
296  } catch(const invalid_variablename_exception&) {
297  }
298  return 0;
299  }
300  variable_access_create v(m, vars);
301  luaW_checkvariable(L, v, 3);
302  return 0;
303 }
304 
305 namespace lua_team {
306 
307  std::string register_metatable(lua_State * L)
308  {
309  std::ostringstream cmd_out;
310 
311  cmd_out << "Adding getside metatable...\n";
312 
313  luaL_newmetatable(L, Team);
314 
315  static luaL_Reg const callbacks[] {
316  { "__index", &impl_side_get},
317  { "__newindex", &impl_side_set},
318  { "__eq", &impl_side_equal},
319  { "__tostring", &impl_side_tostring},
320  { nullptr, nullptr }
321  };
322  luaL_setfuncs(L, callbacks, 0);
323 
324  lua_pushstring(L, Team);
325  lua_setfield(L, -2, "__metatable");
326 
327  // Create the side variables metatable.
328  cmd_out << "Adding side variables metatable...\n";
329 
330  luaL_newmetatable(L, teamVar);
331  lua_pushcfunction(L, impl_side_variables_get);
332  lua_setfield(L, -2, "__index");
333  lua_pushcfunction(L, impl_side_variables_set);
334  lua_setfield(L, -2, "__newindex");
335  lua_pushstring(L, "side variables");
336  lua_setfield(L, -2, "__metatable");
337 
338  return cmd_out.str();
339  }
340 }
341 
342 void luaW_pushteam(lua_State *L, team & tm)
343 {
344  team** t = static_cast<team**>(lua_newuserdatauv(L, sizeof(team*), 0));
345  *t = &tm;
346  luaL_setmetatable(L, Team);
347 }
348 
349 team& luaW_checkteam(lua_State* L, int idx)
350 {
351  return **static_cast<team **>(luaL_checkudata(L, idx, Team));
352 }
353 
354 team& luaW_checkteam(lua_State* L, int idx, game_board& board)
355 {
356  if(lua_isinteger(L, idx)) {
357  int side = lua_tointeger(L, idx);
358  if(!board.has_team(side)) {
359  std::string error = "side " + std::to_string(side) + " does not exist";
360  luaL_argerror(L, 1, error.c_str());
361  // Unreachable
362  }
363  return board.get_team(side);
364  }
365  return **static_cast<team **>(luaL_checkudata(L, idx, Team));
366 }
367 
368 team* luaW_toteam(lua_State* L, int idx)
369 {
370  if(void* p = luaL_testudata(L, idx, Team)) {
371  return *static_cast<team **>(p);
372  }
373  return nullptr;
374 }
bool no_turn_confirmation() const
Definition: team.hpp:377
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:235
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:349
bool persistent() const
Definition: team.hpp:361
map_location starting_position(int side) const
Definition: map.cpp:323
const std::string & side_name() const
Definition: team.hpp:319
t_string side_name_tstr() const
Definition: team.hpp:320
const std::string & flag() const
Definition: team.hpp:312
bool carryover_add() const
Definition: team.hpp:369
bool share_view() const
Definition: team.hpp:402
LUA_API void lua_rawseti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:889
bool objectives_changed() const
Definition: team.hpp:252
virtual const gamemap & map() const override
Definition: game_board.hpp:101
#define return_string_attrib(name, accessor)
Definition: lua_common.hpp:255
const std::string & save_id() const
Definition: team.hpp:242
#define d
LUALIB_API void luaL_setmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:324
double carryover_bonus() const
Definition: team.hpp:371
int village_support() const
Definition: team.hpp:210
const std::string & faction() const
Definition: team.hpp:322
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
std::string flag_icon
bool uses_fog() const
Definition: team.hpp:330
int gold() const
Definition: team.hpp:200
std::string shroud_data() const
Definition: team.hpp:339
int village_gold() const
Definition: team.hpp:203
void write(config &cfg) const
Definition: team.cpp:404
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:71
SHARE_VISION share_vision() const
Definition: team.hpp:403
#define return_int_attrib(name, accessor)
Definition: lua_common.hpp:266
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:949
bool uses_shroud() const
Definition: team.hpp:329
int carryover_percentage() const
Definition: team.hpp:367
game_board * gameboard
Definition: resources.cpp:20
const t_string & faction_name() const
Definition: team.hpp:323
static const char teamVar[]
Definition: lua_team.cpp:45
bool chose_random() const
Definition: team.hpp:422
bool hidden() const
Definition: team.hpp:359
int recall_cost() const
Definition: team.hpp:204
Encapsulates the map of the game.
Definition: location.hpp:37
#define return_cfg_attrib(name, accessor)
Definition: lua_common.hpp:296
bool shroud()
Definition: game.cpp:548
std::size_t i
Definition: function.cpp:940
const std::string & flag_icon() const
Definition: team.hpp:313
const t_string & objectives() const
Definition: team.hpp:251
DEFEAT_CONDITION defeat_condition() const
Definition: team.hpp:354
LUA_API void lua_pushvalue(lua_State *L, int idx)
Definition: lapi.cpp:246
bool is_local() const
Definition: team.hpp:272
static int impl_side_tostring(lua_State *L)
Turns a lua proxy side to string.
Definition: lua_team.cpp:142
bool get_scroll_to_leader() const
Definition: team.hpp:231
#define return_bool_attrib(name, accessor)
Definition: lua_common.hpp:286
const std::string & color() const
Definition: team.hpp:267
bool share_maps() const
Definition: team.hpp:401
double t
Definition: astarsearch.cpp:64
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:711
#define return_float_attrib(name, accessor)
Definition: lua_common.hpp:276
static int impl_side_get(lua_State *L)
Gets some data on a side (__index metamethod).
Definition: lua_team.cpp:53
int base_income() const
Definition: team.hpp:202
int side() const
Definition: team.hpp:199
int total_income() const
Definition: team.hpp:207
const std::set< map_location > & villages() const
Definition: team.hpp:195
LUA_API const char * lua_pushstring(lua_State *L, const char *s)
Definition: lapi.cpp:514
bool lost() const
Definition: team.hpp:364
const std::string & team_name() const
Definition: team.hpp:308
const t_string & user_team_name() const
Definition: team.hpp:309
const std::set< std::string > & recruits() const
Definition: team.hpp:234
#define return_cstring_attrib(name, accessor)
Definition: lua_common.hpp:245
#define luaL_checkstring(L, n)
Definition: lauxlib.h:138
CONTROLLER controller() const
Definition: team.hpp:266