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