The Battle for Wesnoth  1.17.4+dev
lua_unit_attacks.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2022
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 
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 
41 void push_unit_attacks_table(lua_State* L, int idx)
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);
48  luaL_setmetatable(L, uattacksKey);
49 }
50 
51 void luaW_pushweapon(lua_State* L, attack_ptr weapon)
52 {
53  if(weapon != nullptr) {
54  new(L) attack_ref(weapon);
55  luaL_setmetatable(L, uattackKey);
56  } else {
57  lua_pushnil(L);
58  }
59 }
60 
61 void luaW_pushweapon(lua_State* L, const_attack_ptr weapon)
62 {
63  if(weapon != nullptr) {
64  new(L) attack_ref(weapon);
65  luaL_setmetatable(L, uattackKey);
66  } else {
67  lua_pushnil(L);
68  }
69 }
70 
71 static attack_ref& luaW_checkweapon_ref(lua_State* L, int idx)
72 {
73  return *static_cast<attack_ref*>(luaL_checkudata(L, idx, uattackKey));
74 }
75 
76 const_attack_ptr luaW_toweapon(lua_State* L, int idx)
77 {
78  if(void* p = luaL_testudata(L, idx, uattackKey)) {
79  return static_cast<attack_ref*>(p)->cattack;
80  }
81  return nullptr;
82 }
83 
84 attack_type& luaW_checkweapon(lua_State* L, int idx)
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  */
127 static int impl_unit_attacks_get(lua_State *L)
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) {
140  const_attack_ptr atk = lua_isnumber(L, 2) ? find_attack(ut, luaL_checkinteger(L, 2) - 1) : find_attack(ut, luaL_checkstring(L, 2));
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 
156 static int impl_unit_attacks_set(lua_State* L)
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  */
207 static int impl_unit_attacks_len(lua_State *L)
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 
222 static int impl_unit_attacks_next(lua_State *L)
223 {
224  lua_len(L, 1);
225  int n = luaL_checkinteger(L, 2) + 1;
226  int max_n = luaL_checkinteger(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 
236 static int impl_unit_attacks_iter(lua_State* L)
237 {
238  lua_pushcfunction(L, impl_unit_attacks_next);
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  */
250 static int impl_unit_attack_get(lua_State *L)
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_float_attrib("attack_weight", attack.attack_weight());
264  return_float_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  */
284 static int impl_unit_attack_set(lua_State *L)
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 
311 static int impl_unit_attack_equal(lua_State* L)
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  */
322 static int impl_unit_attack_tostring(lua_State* L)
323 {
325  std::ostringstream str;
326  str << "weapon: <" << atk->id() << '>';
327  lua_push(L, str.str());
328  return 1;
329 }
330 
331 static int impl_unit_attack_match(lua_State* L)
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 
342 static int impl_unit_attack_collect(lua_State* L)
343 {
344  attack_ref* atk = static_cast<attack_ref*>(luaL_checkudata(L, 1, uattackKey));
345  atk->~attack_ref();
346  return 0;
347 }
348 
349 int intf_create_attack(lua_State* L)
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 {
357  std::string register_attacks_metatables(lua_State* L)
358  {
359  std::ostringstream cmd_out;
360 
361  // Create the unit attacks metatable.
362  cmd_out << "Adding unit attacks metatable...\n";
363 
364  luaL_newmetatable(L, uattacksKey);
365  lua_pushcfunction(L, impl_unit_attacks_get);
366  lua_setfield(L, -2, "__index");
367  lua_pushcfunction(L, impl_unit_attacks_set);
368  lua_setfield(L, -2, "__newindex");
369  lua_pushcfunction(L, impl_unit_attacks_len);
370  lua_setfield(L, -2, "__len");
371  lua_pushcfunction(L, impl_unit_attacks_iter);
372  lua_setfield(L, -2, "__ipairs");
373  lua_pushstring(L, uattacksKey);
374  lua_setfield(L, -2, "__metatable");
375 
376  // Create the unit attack metatable
377  luaL_newmetatable(L, uattackKey);
378  lua_pushcfunction(L, impl_unit_attack_get);
379  lua_setfield(L, -2, "__index");
380  lua_pushcfunction(L, impl_unit_attack_set);
381  lua_setfield(L, -2, "__newindex");
382  lua_pushcfunction(L, impl_unit_attack_equal);
383  lua_setfield(L, -2, "__eq");
384  lua_pushcfunction(L, impl_unit_attack_tostring);
385  lua_setfield(L, -2, "__tostring");
386  lua_pushcfunction(L, impl_unit_attack_collect);
387  lua_setfield(L, -2, "__gc");
388  lua_pushstring(L, uattackKey);
389  lua_setfield(L, -2, "__metatable");
390  lua_pushcfunction(L, impl_unit_attack_match);
391  lua_setfield(L, -2, "matches");
392 
393  return cmd_out.str();
394  }
395 }
static int impl_unit_attack_collect(lua_State *L)
const std::string & id() const
Definition: attack_type.hpp:44
This class represents a single unit of a specific type.
Definition: unit.hpp:120
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:191
static int impl_unit_attacks_get(lua_State *L)
Gets the attacks of a unit or unit type (__index metamethod).
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:162
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
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)
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:360
attack_ptr add_attack(attack_itors::iterator position, Args &&... args)
Adds a new attack to the unit.
Definition: unit.hpp:932
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
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:142
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
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
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
std::size_t i
Definition: function.cpp:967
void set_damage(int value)
Definition: attack_type.hpp:66
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:900
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:915
double defense_weight() const
Definition: attack_type.hpp:56
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:515
void luaW_pushweapon(lua_State *L, attack_ptr weapon)
std::string register_attacks_metatables(lua_State *L)
#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:544
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:60
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:34
static map_location::DIRECTION n
void set_accuracy(int value)
Definition: attack_type.hpp:64
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
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
bool remove_attack(attack_ptr atk)
Remove an attack from the unit.
Definition: unit.cpp:2605
void set_type(const std::string &value)
Definition: attack_type.hpp:61