The Battle for Wesnoth  1.17.0-dev
lua_unit_attacks.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2021
3  by Guillaume Melquiond <guillaume.melquiond@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 
17 
18 #include "scripting/lua_common.hpp"
19 #include "scripting/lua_unit.hpp"
21 #include "scripting/push_check.hpp"
22 #include "units/unit.hpp"
23 #include "units/types.hpp"
24 #include "units/attack_type.hpp"
25 #include "utils/const_clone.hpp"
26 
27 #include "lua/lauxlib.h"
28 #include "lua/lua.h" // for lua_State, lua_settop, etc
29 
30 #include <type_traits>
31 
32 static const char uattacksKey[] = "unit attacks table";
33 static const char uattackKey[] = "unit attack";
34 
35 struct attack_ref {
38  attack_ref(attack_ptr atk) : attack(atk), cattack(atk) {}
39  attack_ref(const_attack_ptr atk) : cattack(atk) {}
40 };
41 
43 {
44  idx = lua_absindex(L, idx);
45  lua_createtable(L, 1, 0);
46  lua_pushvalue(L, idx);
47  // hack: store the unit_type at 0 because we want positive indices to refer to the attacks.
48  lua_rawseti(L, -2, 0);
50 }
51 
53 {
54  if(weapon != nullptr) {
55  new(L) attack_ref(weapon);
57  } else {
58  lua_pushnil(L);
59  }
60 }
61 
63 {
64  if(weapon != nullptr) {
65  new(L) attack_ref(weapon);
67  } else {
68  lua_pushnil(L);
69  }
70 }
71 
73 {
74  return *static_cast<attack_ref*>(luaL_checkudata(L, idx, uattackKey));
75 }
76 
78 {
79  if(void* p = luaL_testudata(L, idx, uattackKey)) {
80  return static_cast<attack_ref*>(p)->cattack;
81  }
82  return nullptr;
83 }
84 
86 {
87  attack_ref& atk = luaW_checkweapon_ref(L, idx);
88  if(!atk.attack) {
89  luaL_argerror(L, idx, "attack is read-only");
90  }
91  return *atk.attack;
92 }
93 
94 template<typename T>
95 using attack_ptr_in = std::shared_ptr<utils::const_clone_t<attack_type, std::remove_pointer_t<T>>>;
96 
97 // Note that these two templates are designed on the assumption that T is either unit or unit_type
98 template<typename T>
99 auto find_attack(T* u, const std::string& id) -> attack_ptr_in<T>
100 {
101  auto attacks = u->attacks();
102  for(auto at = attacks.begin(); at != attacks.end(); ++at) {
103  if(at->id() == id) {
104  return *at.base();
105  }
106  }
107  return nullptr;
108 }
109 
110 template<typename T>
111 auto find_attack(T* u, std::size_t i) -> attack_ptr_in<T>
112 {
113  auto attacks = u->attacks();
114  if(i < static_cast<std::size_t>(attacks.size())) {
115  auto iter = attacks.begin();
116  iter += i;
117  return *iter.base();
118  }
119  return nullptr;
120 }
121 
122 /**
123  * Gets the attacks of a unit or unit type (__index metamethod).
124  * - Arg 1: table containing the userdata containing the unit or unit type.
125  * - Arg 2: index (int) or id (string) identifying a particular attack.
126  * - Ret 1: the unit's attacks.
127  */
129 {
130  if(!lua_istable(L, 1)) {
131  return luaW_type_error(L, 1, "unit attacks");
132  }
133  lua_rawgeti(L, 1, 0);
134  lua_unit* lu = luaW_tounit_ref(L, -1);
135  const unit_type* ut = luaW_tounittype(L, -1);
136  if(lu && lu->get()) {
137  unit* u = lu->get();
138  attack_ptr atk = lua_isnumber(L, 2) ? find_attack(u, luaL_checkinteger(L, 2) - 1) : find_attack(u, luaL_checkstring(L, 2));
139  luaW_pushweapon(L, atk);
140  } else if(ut) {
142  luaW_pushweapon(L, atk);
143  } else {
144  return luaL_argerror(L, 1, "unit not found");
145  }
146  return 1;
147 }
148 
150 {
151  // This is slightly inefficient since it walks the attack list a second time...
152  return std::find_if(u.attacks().begin(), u.attacks().end(), [&atk](const attack_type& atk2) {
153  return &atk2 == atk.get();
154  });
155 }
156 
158 {
159  if(!lua_istable(L, 1)) {
160  return luaW_type_error(L, 1, "unit attacks");
161  }
162  lua_rawgeti(L, 1, 0);
163  const unit_type* ut = luaW_tounittype(L, -1);
164  if(ut) {
165  return luaL_argerror(L, 1, "unit type attack table is immutable");
166  }
167 
168  unit& u = luaW_checkunit(L, -1);
169  attack_ptr atk = lua_isnumber(L, 2) ? find_attack(&u, luaL_checkinteger(L, 2) - 1) : find_attack(&u, luaL_checkstring(L, 2));
170  if(lua_isnumber(L, 2) && lua_tonumber(L, 2) - 1 > u.attacks().size()) {
171  return luaL_argerror(L, 2, "attack can only be added at the end of the list");
172  }
173 
174  if(lua_isnil(L, 3)) {
175  // Delete the attack
176  u.remove_attack(atk);
177  return 0;
178  }
179 
180  auto iter = get_attack_iter(u, atk), end = u.attacks().end();
181  if(const_attack_ptr atk2 = luaW_toweapon(L, 3)) {
182  if(iter == end) {
183  atk = u.add_attack(end, *atk2);
184  } else {
185  iter.base()->reset(new attack_type(*atk2));
186  atk = *iter.base();
187  }
188  } else {
189  config cfg = luaW_checkconfig(L, 3);
190  if(iter == end) {
191  atk = u.add_attack(end, cfg);
192  } else {
193  iter.base()->reset(new attack_type(cfg));
194  atk = *iter.base();
195  }
196  }
197  if(!lua_isnumber(L, 2)) {
198  atk->set_id(lua_tostring(L, 2));
199  }
200  return 0;
201 }
202 
203 /**
204  * Counts the attacks of a unit (__len metamethod).
205  * - Arg 1: table containing the userdata containing the unit id.
206  * - Ret 1: size of unit attacks vector.
207  */
209 {
210  if(!lua_istable(L, 1)) {
211  return luaW_type_error(L, 1, "unit attacks");
212  }
213  lua_rawgeti(L, 1, 0);
214  const unit* u = luaW_tounit(L, -1);
215  const unit_type* ut = luaW_tounittype(L, -1);
216  if(!u && !ut) {
217  return luaL_argerror(L, 1, "unknown unit");
218  }
219  lua_pushinteger(L, (u ? u->attacks() : ut->attacks()).size());
220  return 1;
221 }
222 
224 {
225  lua_len(L, 1);
226  int n = luaL_checkinteger(L, 2) + 1;
227  int max_n = luaL_checkinteger(L, -1);
228  if(n > max_n) {
229  return 0;
230  }
231  lua_pushnumber(L, n);
232  lua_pushvalue(L, -1);
233  lua_gettable(L, 1);
234  return 2;
235 }
236 
238 {
240  lua_pushvalue(L, 1);
241  lua_pushnumber(L, 0);
242  return 3;
243 }
244 
245 /**
246  * Gets a property of a units attack (__index metamethod).
247  * - Arg 1: table containing the userdata containing the unit id. and a string identifying the attack.
248  * - Arg 2: string
249  * - Ret 1:
250  */
252 {
253  attack_ref& atk_ref = luaW_checkweapon_ref(L, 1);
254  const attack_type& attack = *atk_ref.cattack;
255  char const *m = luaL_checkstring(L, 2);
256  return_bool_attrib("read_only", atk_ref.attack == nullptr);
257  return_string_attrib("description", attack.name());
258  return_string_attrib("name", attack.id());
259  return_string_attrib("type", attack.type());
260  return_string_attrib("icon", attack.icon());
261  return_string_attrib("range", attack.range());
262  return_int_attrib("damage", attack.damage());
263  return_int_attrib("number", attack.num_attacks());
264  return_float_attrib("attack_weight", attack.attack_weight());
265  return_float_attrib("defense_weight", attack.defense_weight());
266  return_int_attrib("accuracy", attack.accuracy());
267  return_int_attrib("movement_used", attack.movement_used());
268  return_int_attrib("parry", attack.parry());
269  return_cfgref_attrib("specials", attack.specials());
270  return_cfgref_attrib("__cfg", attack.to_config());
271  if(luaW_getmetafield(L, 1, m)) {
272  return 1;
273  }
274  std::string err_msg = "unknown property of attack: ";
275  err_msg += m;
276  return luaL_argerror(L, 2, err_msg.c_str());
277 }
278 
279 /**
280  * Gets a property of a units attack (__index metamethod).
281  * - Arg 1: table containing the userdata containing the unit id. and a string identyfying the attack.
282  * - Arg 2: string
283  * - Ret 1:
284  */
286 {
288  char const *m = luaL_checkstring(L, 2);
289  modify_tstring_attrib("description", attack.set_name(value));
290  modify_string_attrib("name", attack.set_id(value));
291  modify_string_attrib("type", attack.set_type(value));
292  modify_string_attrib("icon", attack.set_icon(value));
293  modify_string_attrib("range", attack.set_range(value));
294  modify_int_attrib("damage", attack.set_damage(value));
295  modify_int_attrib("number", attack.set_num_attacks(value));
296  modify_int_attrib("attack_weight", attack.set_attack_weight(value));
297  modify_int_attrib("defense_weight", attack.set_defense_weight(value));
298  modify_int_attrib("accuracy", attack.set_accuracy(value));
299  modify_int_attrib("movement_used", attack.set_movement_used(value));
300  modify_int_attrib("parry", attack.set_parry(value));
301 
302  if(strcmp(m, "specials") == 0) {
303  attack.set_specials(luaW_checkconfig(L, 3));
304  return 0;
305  }
306 
307  std::string err_msg = "unknown modifiable property of attack: ";
308  err_msg += m;
309  return luaL_argerror(L, 2, err_msg.c_str());
310 }
311 
313 {
314  const_attack_ptr ut1 = luaW_toweapon(L, 1);
315  const_attack_ptr ut2 = luaW_toweapon(L, 2);
316  lua_pushboolean(L, ut1 == ut2);
317  return 1;
318 }
319 
320 /**
321  * Turns a lua proxy attack to string. (__tostring metamethod)
322  */
324 {
326  std::ostringstream str;
327  str << "weapon: <" << atk->id() << '>';
328  lua_push(L, str.str());
329  return 1;
330 }
331 
333 {
334  const_attack_ptr atk = luaW_toweapon(L, 1);
335  config cfg = luaW_checkconfig(L, 2);
336  if(!atk) {
337  return luaL_argerror(L, 1, "invalid attack");
338  }
339  lua_pushboolean(L, atk->matches_filter(cfg));
340  return 1;
341 }
342 
344 {
345  attack_ref* atk = static_cast<attack_ref*>(luaL_checkudata(L, 1, uattackKey));
346  atk->~attack_ref();
347  return 0;
348 }
349 
351 {
352  auto atk = std::make_shared<attack_type>(luaW_checkconfig(L, 1));
353  luaW_pushweapon(L, atk);
354  return 1;
355 }
356 
357 namespace lua_units {
359  {
360  std::ostringstream cmd_out;
361 
362  // Create the unit attacks metatable.
363  cmd_out << "Adding unit attacks metatable...\n";
364 
367  lua_setfield(L, -2, "__index");
369  lua_setfield(L, -2, "__newindex");
371  lua_setfield(L, -2, "__len");
373  lua_setfield(L, -2, "__ipairs");
375  lua_setfield(L, -2, "__metatable");
376 
377  // Create the unit attack metatable
380  lua_setfield(L, -2, "__index");
382  lua_setfield(L, -2, "__newindex");
384  lua_setfield(L, -2, "__eq");
386  lua_setfield(L, -2, "__tostring");
388  lua_setfield(L, -2, "__gc");
390  lua_setfield(L, -2, "__metatable");
392  lua_setfield(L, -2, "matches");
393 
394  return cmd_out.str();
395  }
396 }
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:345
LUA_API void lua_createtable(lua_State *L, int narray, int nrec)
Definition: lapi.cpp:728
#define lua_pushcfunction(L, f)
Definition: lua.h:370
const std::string & id() const
Definition: attack_type.hpp:44
This class represents a single unit of a specific type.
Definition: unit.hpp:121
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:581
LUA_API int lua_rawgeti(lua_State *L, int idx, lua_Integer n)
Definition: lapi.cpp:710
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:889
LUA_API int lua_gettable(lua_State *L, int idx)
Definition: lapi.cpp:640
void set_num_attacks(int value)
Definition: attack_type.hpp:67
#define return_string_attrib(name, accessor)
Definition: lua_common.hpp:256
int parry() const
Definition: attack_type.hpp:52
#define lua_tonumber(L, i)
Definition: lua.h:361
const std::string & type() const
Definition: attack_type.hpp:45
static int impl_unit_attack_match(lua_State *L)
int intf_create_attack(lua_State *L)
LUALIB_API void luaL_setmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:324
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:385
LUA_API int lua_absindex(lua_State *L, int idx)
Definition: lapi.cpp:161
attack_ptr add_attack(attack_itors::iterator position, Args &&... args)
Adds a new attack to the unit.
Definition: unit.hpp:933
int num_attacks() const
Definition: attack_type.hpp:54
A single unit type that the player may recruit.
Definition: types.hpp:45
static int impl_unit_attack_equal(lua_State *L)
#define return_cfgref_attrib(name, accessor)
Definition: lua_common.hpp:309
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:59
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:336
int movement_used() const
LUALIB_API int luaL_argerror(lua_State *L, int arg, const char *extramsg)
Definition: lauxlib.cpp:175
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
#define modify_string_attrib(name, accessor)
Definition: lua_common.hpp:347
#define return_int_attrib(name, accessor)
Definition: lua_common.hpp:267
attack_type & luaW_checkweapon(lua_State *L, int idx)
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:43
const std::string & range() const
Definition: attack_type.hpp:47
static int impl_unit_attacks_set(lua_State *L)
const std::string & icon() const
Definition: attack_type.hpp:46
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:330
LUA_API void lua_pushnil(lua_State *L)
Definition: lapi.cpp:473
LUA_API void lua_pushnumber(lua_State *L, lua_Number n)
Definition: lapi.cpp:481
LUA_API void lua_len(lua_State *L, int idx)
Definition: lapi.cpp:1267
void set_defense_weight(double value)
Definition: attack_type.hpp:69
void set_specials(config value)
Definition: attack_type.hpp:70
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:80
LUALIB_API int luaL_newmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:311
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
#define lua_isnil(L, n)
Definition: lua.h:375
std::size_t i
Definition: function.cpp:967
void set_damage(int value)
Definition: attack_type.hpp:66
LUALIB_API lua_Integer luaL_checkinteger(lua_State *L, int arg)
Definition: lauxlib.cpp:442
int damage() const
Definition: attack_type.hpp:53
std::shared_ptr< utils::const_clone_t< attack_type, std::remove_pointer_t< T > >> attack_ptr_in
int accuracy() const
Definition: attack_type.hpp:51
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:901
attack_ref(attack_ptr atk)
mock_party p
const config & specials() const
Definition: attack_type.hpp:57
double attack_weight() const
Definition: attack_type.hpp:55
attack_itors attacks()
Gets an iterator over this unit&#39;s attacks.
Definition: unit.hpp:916
#define lua_tostring(L, i)
Definition: lua.h:386
double defense_weight() const
Definition: attack_type.hpp:56
LUA_API void lua_pushvalue(lua_State *L, int idx)
Definition: lapi.cpp:246
LUA_API int lua_isnumber(lua_State *L, int idx)
Definition: lapi.cpp:285
std::shared_ptr< attack_type > attack_ptr
Definition: ptr.hpp:33
void set_range(const std::string &value)
Definition: attack_type.hpp:63
void set_parry(int value)
Definition: attack_type.hpp:65
#define return_bool_attrib(name, accessor)
Definition: lua_common.hpp:287
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:516
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:373
#define return_float_attrib(name, accessor)
Definition: lua_common.hpp:277
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:62
static const char uattackKey[]
void set_id(const std::string &value)
Definition: attack_type.hpp:60
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:546
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:61
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:34
static map_location::DIRECTION n
LUA_API void lua_pushinteger(lua_State *L, lua_Integer n)
Definition: lapi.cpp:489
void set_accuracy(int value)
Definition: attack_type.hpp:64
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
LUA_API const char * lua_pushstring(lua_State *L, const char *s)
Definition: lapi.cpp:514
LUA_API void lua_setfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:837
void set_attack_weight(double value)
Definition: attack_type.hpp:68
const_attack_ptr luaW_toweapon(lua_State *L, int idx)
#define modify_int_attrib(name, accessor)
Definition: lua_common.hpp:358
#define luaL_checkstring(L, n)
Definition: lauxlib.h:138
bool remove_attack(attack_ptr atk)
Remove an attack from the unit.
Definition: unit.cpp:2602
void set_type(const std::string &value)
Definition: attack_type.hpp:61