The Battle for Wesnoth  1.15.1+dev
lua_unit_attacks.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2018 by Guillaume Melquiond <guillaume.melquiond@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 
16 
17 #include "scripting/lua_common.hpp"
18 #include "scripting/lua_unit.hpp"
20 #include "units/unit.hpp"
21 #include "units/attack_type.hpp"
22 #include "utils/const_clone.hpp"
23 
24 #include "lua/lauxlib.h"
25 #include "lua/lua.h" // for lua_State, lua_settop, etc
26 
27 #include <type_traits>
28 
29 static const char uattacksKey[] = "unit attacks table";
30 static const char uattackKey[] = "unit attack";
31 
32 struct attack_ref {
35  attack_ref(attack_ptr atk) : attack(atk), cattack(atk) {}
36  attack_ref(const_attack_ptr atk) : cattack(atk) {}
37 };
38 
40 {
41  idx = lua_absindex(L, idx);
42  lua_createtable(L, 1, 0);
43  lua_pushvalue(L, idx);
44  // hack: store the unit_type at 0 because we want positive indices to refer to the attacks.
45  lua_rawseti(L, -2, 0);
47 }
48 
50 {
51  if(weapon != nullptr) {
52  new(L) attack_ref(weapon);
54  } else {
55  lua_pushnil(L);
56  }
57 }
58 
60 {
61  if(weapon != nullptr) {
62  new(L) attack_ref(weapon);
64  } else {
65  lua_pushnil(L);
66  }
67 }
68 
70 {
71  return *static_cast<attack_ref*>(luaL_checkudata(L, idx, uattackKey));
72 }
73 
75 {
76  if(void* p = luaL_testudata(L, idx, uattackKey)) {
77  return static_cast<attack_ref*>(p)->cattack;
78  }
79  return nullptr;
80 }
81 
83 {
84  attack_ref& atk = luaW_checkweapon_ref(L, idx);
85  if(!atk.attack) {
86  luaL_argerror(L, idx, "attack is read-only");
87  }
88  return *atk.attack;
89 }
90 
91 template<typename T>
92 using attack_ptr_in = std::shared_ptr<utils::const_clone_t<attack_type, std::remove_pointer_t<T>>>;
93 
94 // Note that these two templates are designed on the assumption that T is either unit or unit_type
95 template<typename T>
96 auto find_attack(T* u, const std::string& id) -> attack_ptr_in<T>
97 {
98  auto attacks = u->attacks();
99  for(auto at = attacks.begin(); at != attacks.end(); ++at) {
100  if(at->id() == id) {
101  return *at.base();
102  }
103  }
104  return nullptr;
105 }
106 
107 template<typename T>
108 auto find_attack(T* u, std::size_t i) -> attack_ptr_in<T>
109 {
110  auto attacks = u->attacks();
111  if(i < static_cast<std::size_t>(attacks.size())) {
112  auto iter = attacks.begin();
113  iter += i;
114  return *iter.base();
115  }
116  return nullptr;
117 }
118 
119 /**
120  * Gets the attacks of a unit or unit type (__index metamethod).
121  * - Arg 1: table containing the userdata containing the unit or unit type.
122  * - Arg 2: index (int) or id (string) identifying a particular attack.
123  * - Ret 1: the unit's attacks.
124  */
126 {
127  if(!lua_istable(L, 1)) {
128  return luaW_type_error(L, 1, "unit attacks");
129  }
130  lua_rawgeti(L, 1, 0);
131  lua_unit* lu = luaW_tounit_ref(L, -1);
132  const unit_type* ut = luaW_tounittype(L, -1);
133  if(lu && lu->get()) {
134  unit* u = lu->get();
135  attack_ptr atk = lua_isnumber(L, 2) ? find_attack(u, luaL_checkinteger(L, 2) - 1) : find_attack(u, luaL_checkstring(L, 2));
136  luaW_pushweapon(L, atk);
137  } else if(ut) {
139  luaW_pushweapon(L, atk);
140  } else {
141  return luaL_argerror(L, 1, "unit not found");
142  }
143  return 1;
144 }
145 
147 {
148  // This is slightly inefficient since it walks the attack list a second time...
149  return std::find_if(u.attacks().begin(), u.attacks().end(), [&atk](const attack_type& atk2) {
150  return &atk2 == atk.get();
151  });
152 }
153 
155 {
156  if(!lua_istable(L, 1)) {
157  return luaW_type_error(L, 1, "unit attacks");
158  }
159  lua_rawgeti(L, 1, 0);
160  const unit_type* ut = luaW_tounittype(L, -1);
161  if(ut) {
162  return luaL_argerror(L, 1, "unit type attack table is immutable");
163  }
164 
165  unit& u = luaW_checkunit(L, -1);
166  attack_ptr atk = lua_isnumber(L, 2) ? find_attack(&u, luaL_checkinteger(L, 2) - 1) : find_attack(&u, luaL_checkstring(L, 2));
167  if(lua_isnumber(L, 2) && lua_tonumber(L, 2) - 1 > u.attacks().size()) {
168  return luaL_argerror(L, 2, "attack can only be added at the end of the list");
169  }
170 
171  if(lua_isnil(L, 3)) {
172  // Delete the attack
173  u.remove_attack(atk);
174  return 0;
175  }
176 
177  auto iter = get_attack_iter(u, atk), end = u.attacks().end();
178  if(const_attack_ptr atk2 = luaW_toweapon(L, 3)) {
179  if(iter == end) {
180  atk = u.add_attack(end, *atk2);
181  } else {
182  iter.base()->reset(new attack_type(*atk2));
183  atk = *iter.base();
184  }
185  } else {
186  config cfg = luaW_checkconfig(L, 3);
187  if(iter == end) {
188  atk = u.add_attack(end, cfg);
189  } else {
190  iter.base()->reset(new attack_type(cfg));
191  atk = *iter.base();
192  }
193  }
194  if(!lua_isnumber(L, 2)) {
195  atk->set_id(lua_tostring(L, 2));
196  }
197  return 0;
198 }
199 
200 /**
201  * Counts the attacks of a unit (__len metamethod).
202  * - Arg 1: table containing the userdata containing the unit id.
203  * - Ret 1: size of unit attacks vector.
204  */
206 {
207  if(!lua_istable(L, 1)) {
208  return luaW_type_error(L, 1, "unit attacks");
209  }
210  lua_rawgeti(L, 1, 0);
211  const unit* u = luaW_tounit(L, -1);
212  const unit_type* ut = luaW_tounittype(L, -1);
213  if(!u && !ut) {
214  return luaL_argerror(L, 1, "unknown unit");
215  }
216  lua_pushinteger(L, (u ? u->attacks() : ut->attacks()).size());
217  return 1;
218 }
219 
221 {
222  lua_len(L, 1);
223  int n = luaL_checknumber(L, 2) + 1;
224  int max_n = luaL_checknumber(L, -1);
225  if(n > max_n) {
226  return 0;
227  }
228  lua_pushnumber(L, n);
229  lua_pushvalue(L, -1);
230  lua_gettable(L, 1);
231  return 2;
232 }
233 
235 {
237  lua_pushvalue(L, 1);
238  lua_pushnumber(L, 0);
239  return 3;
240 }
241 
242 /**
243  * Gets a property of a units attack (__index metamethod).
244  * - Arg 1: table containing the userdata containing the unit id. and a string identifying the attack.
245  * - Arg 2: string
246  * - Ret 1:
247  */
249 {
250  attack_ref& atk_ref = luaW_checkweapon_ref(L, 1);
251  const attack_type& attack = *atk_ref.cattack;
252  char const *m = luaL_checkstring(L, 2);
253  return_bool_attrib("read_only", atk_ref.attack == nullptr);
254  return_string_attrib("description", attack.name());
255  return_string_attrib("name", attack.id());
256  return_string_attrib("type", attack.type());
257  return_string_attrib("icon", attack.icon());
258  return_string_attrib("range", attack.range());
259  return_int_attrib("damage", attack.damage());
260  return_int_attrib("number", attack.num_attacks());
261  return_int_attrib("attack_weight", attack.attack_weight());
262  return_int_attrib("defense_weight", attack.defense_weight());
263  return_int_attrib("accuracy", attack.accuracy());
264  return_int_attrib("movement_used", attack.movement_used());
265  return_int_attrib("parry", attack.parry());
266  return_cfgref_attrib("specials", attack.specials());
267  return_cfgref_attrib("__cfg", attack.to_config());
268  if(luaW_getmetafield(L, 1, m)) {
269  return 1;
270  }
271  std::string err_msg = "unknown property of attack: ";
272  err_msg += m;
273  return luaL_argerror(L, 2, err_msg.c_str());
274 }
275 
276 /**
277  * Gets a property of a units attack (__index metamethod).
278  * - Arg 1: table containing the userdata containing the unit id. and a string identyfying the attack.
279  * - Arg 2: string
280  * - Ret 1:
281  */
283 {
285  char const *m = luaL_checkstring(L, 2);
286  modify_tstring_attrib("description", attack.set_name(value));
287  modify_string_attrib("name", attack.set_id(value));
288  modify_string_attrib("type", attack.set_type(value));
289  modify_string_attrib("icon", attack.set_icon(value));
290  modify_string_attrib("range", attack.set_range(value));
291  modify_int_attrib("damage", attack.set_damage(value));
292  modify_int_attrib("number", attack.set_num_attacks(value));
293  modify_int_attrib("attack_weight", attack.set_attack_weight(value));
294  modify_int_attrib("defense_weight", attack.set_defense_weight(value));
295  modify_int_attrib("accuracy", attack.set_accuracy(value));
296  modify_int_attrib("movement_used", attack.set_movement_used(value));
297  modify_int_attrib("parry", attack.set_parry(value));
298 
299  if(strcmp(m, "specials") == 0) {
300  attack.set_specials(luaW_checkconfig(L, 3));
301  return 0;
302  }
303 
304  std::string err_msg = "unknown modifiable property of attack: ";
305  err_msg += m;
306  return luaL_argerror(L, 2, err_msg.c_str());
307 }
308 
310 {
311  const_attack_ptr ut1 = luaW_toweapon(L, 1);
312  const_attack_ptr ut2 = luaW_toweapon(L, 2);
313  lua_pushboolean(L, ut1 == ut2);
314  return 1;
315 }
316 
318 {
319  const_attack_ptr atk = luaW_toweapon(L, 1);
320  config cfg = luaW_checkconfig(L, 2);
321  if(!atk) {
322  return luaL_argerror(L, 1, "invalid attack");
323  }
324  lua_pushboolean(L, atk->matches_filter(cfg));
325  return 1;
326 }
327 
329 {
330  attack_ref* atk = static_cast<attack_ref*>(luaL_checkudata(L, 1, uattackKey));
331  atk->~attack_ref();
332  return 0;
333 }
334 
336 {
337  auto atk = std::make_shared<attack_type>(luaW_checkconfig(L, 1));
338  luaW_pushweapon(L, atk);
339  return 1;
340 }
341 
342 namespace lua_units {
344  {
345  std::ostringstream cmd_out;
346 
347  // Create the unit attacks metatable.
348  cmd_out << "Adding unit attacks metatable...\n";
349 
352  lua_setfield(L, -2, "__index");
354  lua_setfield(L, -2, "__newindex");
356  lua_setfield(L, -2, "__len");
358  lua_setfield(L, -2, "__ipairs");
360  lua_setfield(L, -2, "__metatable");
361 
362  // Create the unit attack metatable
365  lua_setfield(L, -2, "__index");
367  lua_setfield(L, -2, "__newindex");
369  lua_setfield(L, -2, "__eq");
371  lua_setfield(L, -2, "__gc");
373  lua_setfield(L, -2, "__metatable");
375  lua_setfield(L, -2, "matches");
376 
377  // Add create_attack
378  luaW_getglobal(L, "wesnoth");
380  lua_setfield(L, -2, "create_weapon");
381  lua_pop(L, 1);
382 
383  return cmd_out.str();
384  }
385 }
static int impl_unit_attack_collect(lua_State *L)
LUALIB_API void * luaL_checkudata(lua_State *L, int ud, const char *tname)
Definition: lauxlib.cpp:333
LUA_API void lua_createtable(lua_State *L, int narray, int nrec)
Definition: lapi.cpp:684
#define lua_pushcfunction(L, f)
Definition: lua.h:350
const std::string & id() const
Definition: attack_type.hpp:41
LUALIB_API lua_Number luaL_checknumber(lua_State *L, int arg)
Definition: lauxlib.cpp:408
This class represents a single unit of a specific type.
Definition: unit.hpp:99
int luaW_type_error(lua_State *L, int narg, const char *tname)
unit & luaW_checkunit(lua_State *L, int index, bool only_on_map)
Converts a Lua value to a unit pointer.
Definition: lua_unit.cpp:192
static int impl_unit_attacks_get(lua_State *L)
Gets the attacks of a unit or unit type (__index metamethod).
LUA_API void lua_pushboolean(lua_State *L, int b)
Definition: lapi.cpp:557
LUA_API int lua_rawgeti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:658
static int impl_unit_attacks_next(lua_State *L)
lua_unit * luaW_tounit_ref(lua_State *L, int index)
Similar to luaW_tounit but returns a lua_unit; use this if you need to handle map and recall units di...
Definition: lua_unit.cpp:163
LUA_API void lua_rawseti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:817
LUA_API int lua_gettable(lua_State *L, int idx)
Definition: lapi.cpp:612
void set_num_attacks(int value)
Definition: attack_type.hpp:64
#define return_string_attrib(name, accessor)
Definition: lua_common.hpp:217
int parry() const
Definition: attack_type.hpp:49
#define lua_tonumber(L, i)
Definition: lua.h:341
const std::string & type() const
Definition: attack_type.hpp:42
static int impl_unit_attack_match(lua_State *L)
LUALIB_API void luaL_setmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:312
static int impl_unit_attack_set(lua_State *L)
Gets a property of a units attack (__index metamethod).
LUA_API int lua_absindex(lua_State *L, int idx)
Definition: lapi.cpp:160
attack_ptr add_attack(attack_itors::iterator position, Args &&... args)
Adds a new attack to the unit.
Definition: unit.hpp:893
int num_attacks() const
Definition: attack_type.hpp:51
A single unit type that the player may recruit.
Definition: types.hpp:42
#define lua_pop(L, n)
Definition: lua.h:344
static int impl_unit_attack_equal(lua_State *L)
#define return_cfgref_attrib(name, accessor)
Definition: lua_common.hpp:260
void set_name(const t_string &value)
Definition: attack_type.hpp:56
static const char uattacksKey[]
static int impl_unit_attack_get(lua_State *L)
Gets a property of a units attack (__index metamethod).
static std::string at(const std::string &file, int line)
#define modify_tstring_attrib(name, accessor)
Definition: lua_common.hpp:283
int movement_used() const
Definition: attack_type.hpp:93
LUALIB_API int luaL_argerror(lua_State *L, int arg, const char *extramsg)
Definition: lauxlib.cpp:164
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
#define modify_string_attrib(name, accessor)
Definition: lua_common.hpp:292
#define return_int_attrib(name, accessor)
Definition: lua_common.hpp:226
attack_type & luaW_checkweapon(lua_State *L, int idx)
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:851
auto find_attack(T *u, const std::string &id) -> attack_ptr_in< T >
unit * luaW_tounit(lua_State *L, int index, bool only_on_map)
Converts a Lua value to a unit pointer.
Definition: lua_unit.cpp:143
const t_string & name() const
Definition: attack_type.hpp:40
const std::string & range() const
Definition: attack_type.hpp:44
static int impl_unit_attacks_set(lua_State *L)
const std::string & icon() const
Definition: attack_type.hpp:43
attack_ref(const_attack_ptr atk)
attack_ptr attack
LUALIB_API void * luaL_testudata(lua_State *L, int ud, const char *tname)
Definition: lauxlib.cpp:318
LUA_API void lua_pushnil(lua_State *L)
Definition: lapi.cpp:450
LUA_API void lua_pushnumber(lua_State *L, lua_Number n)
Definition: lapi.cpp:458
LUA_API void lua_len(lua_State *L, int idx)
Definition: lapi.cpp:1156
void set_defense_weight(double value)
Definition: attack_type.hpp:66
void set_specials(config value)
Definition: attack_type.hpp:67
const unit_type * luaW_tounittype(lua_State *L, int idx)
Test if a stack element is a unit type, and return it if so.
static attack_ref & luaW_checkweapon_ref(lua_State *L, int idx)
Storage for a unit, either owned by the Lua code (ptr != 0), a local variable unit (c_ptr != 0)...
Definition: lua_unit.hpp:79
LUALIB_API int luaL_newmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:299
#define lua_isnil(L, n)
Definition: lua.h:355
static int intf_create_attack(lua_State *L)
std::size_t i
Definition: function.cpp:933
void set_damage(int value)
Definition: attack_type.hpp:63
LUALIB_API lua_Integer luaL_checkinteger(lua_State *L, int arg)
Definition: lauxlib.cpp:430
int damage() const
Definition: attack_type.hpp:50
std::shared_ptr< utils::const_clone_t< attack_type, std::remove_pointer_t< T > >> attack_ptr_in
int accuracy() const
Definition: attack_type.hpp:48
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:799
attack_ref(attack_ptr atk)
mock_party p
const config & specials() const
Definition: attack_type.hpp:54
double attack_weight() const
Definition: attack_type.hpp:52
attack_itors attacks()
Gets an iterator over this unit&#39;s attacks.
Definition: unit.hpp:876
#define lua_tostring(L, i)
Definition: lua.h:366
double defense_weight() const
Definition: attack_type.hpp:53
LUA_API void lua_pushvalue(lua_State *L, int idx)
Definition: lapi.cpp:237
LUA_API int lua_isnumber(lua_State *L, int idx)
Definition: lapi.cpp:276
std::shared_ptr< attack_type > attack_ptr
Definition: ptr.hpp:36
void set_range(const std::string &value)
Definition: attack_type.hpp:60
void set_parry(int value)
Definition: attack_type.hpp:62
#define return_bool_attrib(name, accessor)
Definition: lua_common.hpp:242
bool luaW_getmetafield(lua_State *L, int idx, const char *key)
Like luaL_getmetafield, but returns false if key is an empty string or begins with two underscores...
Definition: lua_common.cpp:494
void luaW_pushweapon(lua_State *L, attack_ptr weapon)
std::string register_attacks_metatables(lua_State *L)
#define lua_istable(L, n)
Definition: lua.h:353
static int impl_unit_attacks_len(lua_State *L)
Counts the attacks of a unit (__len metamethod).
void set_icon(const std::string &value)
Definition: attack_type.hpp:59
static const char uattackKey[]
void set_id(const std::string &value)
Definition: attack_type.hpp:57
config to_config() const
Definition: attack_type.hpp:97
static attack_itors::iterator get_attack_iter(unit &u, attack_ptr atk)
const_attack_ptr cattack
const_attack_itors attacks() const
Definition: types.cpp:494
static int impl_unit_attacks_iter(lua_State *L)
void push_unit_attacks_table(lua_State *L, int idx)
void set_movement_used(int value)
Definition: attack_type.hpp:94
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:37
static map_location::DIRECTION n
LUA_API void lua_pushinteger(lua_State *L, lua_Integer n)
Definition: lapi.cpp:466
void set_accuracy(int value)
Definition: attack_type.hpp:61
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
LUA_API const char * lua_pushstring(lua_State *L, const char *s)
Definition: lapi.cpp:491
LUA_API void lua_setfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:777
void set_attack_weight(double value)
Definition: attack_type.hpp:65
const_attack_ptr luaW_toweapon(lua_State *L, int idx)
#define modify_int_attrib(name, accessor)
Definition: lua_common.hpp:301
#define luaL_checkstring(L, n)
Definition: lauxlib.h:124
bool remove_attack(attack_ptr atk)
Remove an attack from the unit.
Definition: unit.cpp:2552
void set_type(const std::string &value)
Definition: attack_type.hpp:58