The Battle for Wesnoth  1.15.2+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 "scripting/push_check.hpp"
21 #include "units/unit.hpp"
22 #include "units/attack_type.hpp"
23 #include "utils/const_clone.hpp"
24 
25 #include "lua/lauxlib.h"
26 #include "lua/lua.h" // for lua_State, lua_settop, etc
27 
28 #include <type_traits>
29 
30 static const char uattacksKey[] = "unit attacks table";
31 static const char uattackKey[] = "unit attack";
32 
33 struct attack_ref {
36  attack_ref(attack_ptr atk) : attack(atk), cattack(atk) {}
37  attack_ref(const_attack_ptr atk) : cattack(atk) {}
38 };
39 
41 {
42  idx = lua_absindex(L, idx);
43  lua_createtable(L, 1, 0);
44  lua_pushvalue(L, idx);
45  // hack: store the unit_type at 0 because we want positive indices to refer to the attacks.
46  lua_rawseti(L, -2, 0);
48 }
49 
51 {
52  if(weapon != nullptr) {
53  new(L) attack_ref(weapon);
55  } else {
56  lua_pushnil(L);
57  }
58 }
59 
61 {
62  if(weapon != nullptr) {
63  new(L) attack_ref(weapon);
65  } else {
66  lua_pushnil(L);
67  }
68 }
69 
71 {
72  return *static_cast<attack_ref*>(luaL_checkudata(L, idx, uattackKey));
73 }
74 
76 {
77  if(void* p = luaL_testudata(L, idx, uattackKey)) {
78  return static_cast<attack_ref*>(p)->cattack;
79  }
80  return nullptr;
81 }
82 
84 {
85  attack_ref& atk = luaW_checkweapon_ref(L, idx);
86  if(!atk.attack) {
87  luaL_argerror(L, idx, "attack is read-only");
88  }
89  return *atk.attack;
90 }
91 
92 template<typename T>
93 using attack_ptr_in = std::shared_ptr<utils::const_clone_t<attack_type, std::remove_pointer_t<T>>>;
94 
95 // Note that these two templates are designed on the assumption that T is either unit or unit_type
96 template<typename T>
97 auto find_attack(T* u, const std::string& id) -> attack_ptr_in<T>
98 {
99  auto attacks = u->attacks();
100  for(auto at = attacks.begin(); at != attacks.end(); ++at) {
101  if(at->id() == id) {
102  return *at.base();
103  }
104  }
105  return nullptr;
106 }
107 
108 template<typename T>
109 auto find_attack(T* u, std::size_t i) -> attack_ptr_in<T>
110 {
111  auto attacks = u->attacks();
112  if(i < static_cast<std::size_t>(attacks.size())) {
113  auto iter = attacks.begin();
114  iter += i;
115  return *iter.base();
116  }
117  return nullptr;
118 }
119 
120 /**
121  * Gets the attacks of a unit or unit type (__index metamethod).
122  * - Arg 1: table containing the userdata containing the unit or unit type.
123  * - Arg 2: index (int) or id (string) identifying a particular attack.
124  * - Ret 1: the unit's attacks.
125  */
127 {
128  if(!lua_istable(L, 1)) {
129  return luaW_type_error(L, 1, "unit attacks");
130  }
131  lua_rawgeti(L, 1, 0);
132  lua_unit* lu = luaW_tounit_ref(L, -1);
133  const unit_type* ut = luaW_tounittype(L, -1);
134  if(lu && lu->get()) {
135  unit* u = lu->get();
136  attack_ptr atk = lua_isnumber(L, 2) ? find_attack(u, luaL_checkinteger(L, 2) - 1) : find_attack(u, luaL_checkstring(L, 2));
137  luaW_pushweapon(L, atk);
138  } else if(ut) {
140  luaW_pushweapon(L, atk);
141  } else {
142  return luaL_argerror(L, 1, "unit not found");
143  }
144  return 1;
145 }
146 
148 {
149  // This is slightly inefficient since it walks the attack list a second time...
150  return std::find_if(u.attacks().begin(), u.attacks().end(), [&atk](const attack_type& atk2) {
151  return &atk2 == atk.get();
152  });
153 }
154 
156 {
157  if(!lua_istable(L, 1)) {
158  return luaW_type_error(L, 1, "unit attacks");
159  }
160  lua_rawgeti(L, 1, 0);
161  const unit_type* ut = luaW_tounittype(L, -1);
162  if(ut) {
163  return luaL_argerror(L, 1, "unit type attack table is immutable");
164  }
165 
166  unit& u = luaW_checkunit(L, -1);
167  attack_ptr atk = lua_isnumber(L, 2) ? find_attack(&u, luaL_checkinteger(L, 2) - 1) : find_attack(&u, luaL_checkstring(L, 2));
168  if(lua_isnumber(L, 2) && lua_tonumber(L, 2) - 1 > u.attacks().size()) {
169  return luaL_argerror(L, 2, "attack can only be added at the end of the list");
170  }
171 
172  if(lua_isnil(L, 3)) {
173  // Delete the attack
174  u.remove_attack(atk);
175  return 0;
176  }
177 
178  auto iter = get_attack_iter(u, atk), end = u.attacks().end();
179  if(const_attack_ptr atk2 = luaW_toweapon(L, 3)) {
180  if(iter == end) {
181  atk = u.add_attack(end, *atk2);
182  } else {
183  iter.base()->reset(new attack_type(*atk2));
184  atk = *iter.base();
185  }
186  } else {
187  config cfg = luaW_checkconfig(L, 3);
188  if(iter == end) {
189  atk = u.add_attack(end, cfg);
190  } else {
191  iter.base()->reset(new attack_type(cfg));
192  atk = *iter.base();
193  }
194  }
195  if(!lua_isnumber(L, 2)) {
196  atk->set_id(lua_tostring(L, 2));
197  }
198  return 0;
199 }
200 
201 /**
202  * Counts the attacks of a unit (__len metamethod).
203  * - Arg 1: table containing the userdata containing the unit id.
204  * - Ret 1: size of unit attacks vector.
205  */
207 {
208  if(!lua_istable(L, 1)) {
209  return luaW_type_error(L, 1, "unit attacks");
210  }
211  lua_rawgeti(L, 1, 0);
212  const unit* u = luaW_tounit(L, -1);
213  const unit_type* ut = luaW_tounittype(L, -1);
214  if(!u && !ut) {
215  return luaL_argerror(L, 1, "unknown unit");
216  }
217  lua_pushinteger(L, (u ? u->attacks() : ut->attacks()).size());
218  return 1;
219 }
220 
222 {
223  lua_len(L, 1);
224  int n = luaL_checknumber(L, 2) + 1;
225  int max_n = luaL_checknumber(L, -1);
226  if(n > max_n) {
227  return 0;
228  }
229  lua_pushnumber(L, n);
230  lua_pushvalue(L, -1);
231  lua_gettable(L, 1);
232  return 2;
233 }
234 
236 {
238  lua_pushvalue(L, 1);
239  lua_pushnumber(L, 0);
240  return 3;
241 }
242 
243 /**
244  * Gets a property of a units attack (__index metamethod).
245  * - Arg 1: table containing the userdata containing the unit id. and a string identifying the attack.
246  * - Arg 2: string
247  * - Ret 1:
248  */
250 {
251  attack_ref& atk_ref = luaW_checkweapon_ref(L, 1);
252  const attack_type& attack = *atk_ref.cattack;
253  char const *m = luaL_checkstring(L, 2);
254  return_bool_attrib("read_only", atk_ref.attack == nullptr);
255  return_string_attrib("description", attack.name());
256  return_string_attrib("name", attack.id());
257  return_string_attrib("type", attack.type());
258  return_string_attrib("icon", attack.icon());
259  return_string_attrib("range", attack.range());
260  return_int_attrib("damage", attack.damage());
261  return_int_attrib("number", attack.num_attacks());
262  return_int_attrib("attack_weight", attack.attack_weight());
263  return_int_attrib("defense_weight", attack.defense_weight());
264  return_int_attrib("accuracy", attack.accuracy());
265  return_int_attrib("movement_used", attack.movement_used());
266  return_int_attrib("parry", attack.parry());
267  return_cfgref_attrib("specials", attack.specials());
268  return_cfgref_attrib("__cfg", attack.to_config());
269  if(luaW_getmetafield(L, 1, m)) {
270  return 1;
271  }
272  std::string err_msg = "unknown property of attack: ";
273  err_msg += m;
274  return luaL_argerror(L, 2, err_msg.c_str());
275 }
276 
277 /**
278  * Gets a property of a units attack (__index metamethod).
279  * - Arg 1: table containing the userdata containing the unit id. and a string identyfying the attack.
280  * - Arg 2: string
281  * - Ret 1:
282  */
284 {
286  char const *m = luaL_checkstring(L, 2);
287  modify_tstring_attrib("description", attack.set_name(value));
288  modify_string_attrib("name", attack.set_id(value));
289  modify_string_attrib("type", attack.set_type(value));
290  modify_string_attrib("icon", attack.set_icon(value));
291  modify_string_attrib("range", attack.set_range(value));
292  modify_int_attrib("damage", attack.set_damage(value));
293  modify_int_attrib("number", attack.set_num_attacks(value));
294  modify_int_attrib("attack_weight", attack.set_attack_weight(value));
295  modify_int_attrib("defense_weight", attack.set_defense_weight(value));
296  modify_int_attrib("accuracy", attack.set_accuracy(value));
297  modify_int_attrib("movement_used", attack.set_movement_used(value));
298  modify_int_attrib("parry", attack.set_parry(value));
299 
300  if(strcmp(m, "specials") == 0) {
301  attack.set_specials(luaW_checkconfig(L, 3));
302  return 0;
303  }
304 
305  std::string err_msg = "unknown modifiable property of attack: ";
306  err_msg += m;
307  return luaL_argerror(L, 2, err_msg.c_str());
308 }
309 
311 {
312  const_attack_ptr ut1 = luaW_toweapon(L, 1);
313  const_attack_ptr ut2 = luaW_toweapon(L, 2);
314  lua_pushboolean(L, ut1 == ut2);
315  return 1;
316 }
317 
318 /**
319  * Turns a lua proxy attack to string. (__tostring metamethod)
320  */
322 {
324  std::ostringstream str;
325  str << "weapon: <" << atk->id() << '>';
326  lua_push(L, str.str());
327  return 1;
328 }
329 
331 {
332  const_attack_ptr atk = luaW_toweapon(L, 1);
333  config cfg = luaW_checkconfig(L, 2);
334  if(!atk) {
335  return luaL_argerror(L, 1, "invalid attack");
336  }
337  lua_pushboolean(L, atk->matches_filter(cfg));
338  return 1;
339 }
340 
342 {
343  attack_ref* atk = static_cast<attack_ref*>(luaL_checkudata(L, 1, uattackKey));
344  atk->~attack_ref();
345  return 0;
346 }
347 
349 {
350  auto atk = std::make_shared<attack_type>(luaW_checkconfig(L, 1));
351  luaW_pushweapon(L, atk);
352  return 1;
353 }
354 
355 namespace lua_units {
357  {
358  std::ostringstream cmd_out;
359 
360  // Create the unit attacks metatable.
361  cmd_out << "Adding unit attacks metatable...\n";
362 
365  lua_setfield(L, -2, "__index");
367  lua_setfield(L, -2, "__newindex");
369  lua_setfield(L, -2, "__len");
371  lua_setfield(L, -2, "__ipairs");
373  lua_setfield(L, -2, "__metatable");
374 
375  // Create the unit attack metatable
378  lua_setfield(L, -2, "__index");
380  lua_setfield(L, -2, "__newindex");
382  lua_setfield(L, -2, "__eq");
384  lua_setfield(L, -2, "__tostring");
386  lua_setfield(L, -2, "__gc");
388  lua_setfield(L, -2, "__metatable");
390  lua_setfield(L, -2, "matches");
391 
392  // Add create_attack
393  luaW_getglobal(L, "wesnoth");
395  lua_setfield(L, -2, "create_weapon");
396  lua_pop(L, 1);
397 
398  return cmd_out.str();
399  }
400 }
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:106
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:194
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:165
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).
void lua_push(lua_State *L, const T &val)
Definition: push_check.hpp:371
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:920
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
static int impl_unit_attack_tostring(lua_State *L)
Turns a lua proxy attack to string.
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:99
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:871
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:145
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:819
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:903
#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:512
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
static attack_itors::iterator get_attack_iter(unit &u, attack_ptr atk)
const_attack_ptr cattack
const_attack_itors attacks() const
Definition: types.cpp:509
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)
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
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:2606
void set_type(const std::string &value)
Definition: attack_type.hpp:58