The Battle for Wesnoth  1.19.5+dev
lua_unit_attacks.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2024
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 
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) {}
38 };
39 
40 void push_unit_attacks_table(lua_State* L, int idx)
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);
47  luaL_setmetatable(L, uattacksKey);
48 }
49 
50 void luaW_pushweapon(lua_State* L, attack_ptr weapon)
51 {
52  if(weapon != nullptr) {
53  new(L) attack_ref(weapon);
54  luaL_setmetatable(L, uattackKey);
55  } else {
56  lua_pushnil(L);
57  }
58 }
59 
60 void luaW_pushweapon(lua_State* L, const_attack_ptr weapon)
61 {
62  if(weapon != nullptr) {
63  new(L) attack_ref(weapon);
64  luaL_setmetatable(L, uattackKey);
65  } else {
66  lua_pushnil(L);
67  }
68 }
69 
70 static attack_ref& luaW_checkweapon_ref(lua_State* L, int idx)
71 {
72  return *static_cast<attack_ref*>(luaL_checkudata(L, idx, uattackKey));
73 }
74 
75 const_attack_ptr luaW_toweapon(lua_State* L, int idx)
76 {
77  if(void* p = luaL_testudata(L, idx, uattackKey)) {
78  return static_cast<attack_ref*>(p)->cattack;
79  }
80  return nullptr;
81 }
82 
83 attack_type& luaW_checkweapon(lua_State* L, int idx)
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  */
126 static int impl_unit_attacks_get(lua_State *L)
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) {
139  const_attack_ptr atk = lua_isnumber(L, 2) ? find_attack(ut, luaL_checkinteger(L, 2) - 1) : find_attack(ut, luaL_checkstring(L, 2));
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 
155 static int impl_unit_attacks_set(lua_State* L)
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  */
206 static int impl_unit_attacks_len(lua_State *L)
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 
221 static int impl_unit_attacks_next(lua_State *L)
222 {
223  lua_len(L, 1);
224  int n = luaL_checkinteger(L, 2) + 1;
225  int max_n = luaL_checkinteger(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 
235 static int impl_unit_attacks_iter(lua_State* L)
236 {
237  lua_pushcfunction(L, impl_unit_attacks_next);
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  */
249 static int impl_unit_attack_get(lua_State *L)
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_string_attrib("alignment", attack.alignment_str());
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("attacks_used", attack.attacks_used());
268  return_int_attrib("parry", attack.parry());
269  return_int_attrib("max_range", attack.max_range());
270  return_int_attrib("min_range", attack.min_range());
271  return_cfgref_attrib("specials", attack.specials());
272  return_cfgref_attrib("__cfg", attack.to_config());
273  if(luaW_getmetafield(L, 1, m)) {
274  return 1;
275  }
276  std::string err_msg = "unknown property of attack: ";
277  err_msg += m;
278  return luaL_argerror(L, 2, err_msg.c_str());
279 }
280 
281 /**
282  * Gets a property of a units attack (__index metamethod).
283  * - Arg 1: table containing the userdata containing the unit id. and a string identyfying the attack.
284  * - Arg 2: string
285  * - Ret 1:
286  */
287 static int impl_unit_attack_set(lua_State *L)
288 {
289  attack_type& attack = luaW_checkweapon(L, 1);
290  char const *m = luaL_checkstring(L, 2);
291  modify_tstring_attrib("description", attack.set_name(value));
292  modify_string_attrib("name", attack.set_id(value));
293  modify_string_attrib("type", attack.set_type(value));
294  modify_string_attrib("icon", attack.set_icon(value));
295  modify_string_attrib("range", attack.set_range(value));
296  modify_string_attrib("alignment", attack.set_range(value));
297  modify_int_attrib("damage", attack.set_damage(value));
298  modify_int_attrib("number", attack.set_num_attacks(value));
299  modify_int_attrib("attack_weight", attack.set_attack_weight(value));
300  modify_int_attrib("defense_weight", attack.set_defense_weight(value));
301  modify_int_attrib("accuracy", attack.set_accuracy(value));
302  modify_int_attrib("movement_used", attack.set_movement_used(value));
303  modify_int_attrib("attacks_used", attack.set_attacks_used(value));
304  modify_int_attrib("parry", attack.set_parry(value));
305  modify_int_attrib("max_range", attack.set_max_range(value));
306  modify_int_attrib("min_range", attack.set_min_range(value));
307 
308  if(strcmp(m, "specials") == 0) {
309  attack.set_specials(luaW_checkconfig(L, 3));
310  return 0;
311  }
312 
313  std::string err_msg = "unknown modifiable property of attack: ";
314  err_msg += m;
315  return luaL_argerror(L, 2, err_msg.c_str());
316 }
317 
318 static int impl_unit_attack_equal(lua_State* L)
319 {
320  const_attack_ptr ut1 = luaW_toweapon(L, 1);
321  const_attack_ptr ut2 = luaW_toweapon(L, 2);
322  lua_pushboolean(L, ut1 == ut2);
323  return 1;
324 }
325 
326 /**
327  * Turns a lua proxy attack to string. (__tostring metamethod)
328  */
329 static int impl_unit_attack_tostring(lua_State* L)
330 {
332  std::ostringstream str;
333  str << "weapon: <" << atk->id() << '>';
334  lua_push(L, str.str());
335  return 1;
336 }
337 
338 static int impl_unit_attack_match(lua_State* L)
339 {
340  const_attack_ptr atk = luaW_toweapon(L, 1);
341  config cfg = luaW_checkconfig(L, 2);
342  if(!atk) {
343  return luaL_argerror(L, 1, "invalid attack");
344  }
345  lua_pushboolean(L, atk->matches_filter(cfg));
346  return 1;
347 }
348 
349 static int impl_unit_attack_collect(lua_State* L)
350 {
351  attack_ref* atk = static_cast<attack_ref*>(luaL_checkudata(L, 1, uattackKey));
352  atk->~attack_ref();
353  return 0;
354 }
355 
356 int intf_create_attack(lua_State* L)
357 {
358  auto atk = std::make_shared<attack_type>(luaW_checkconfig(L, 1));
359  luaW_pushweapon(L, atk);
360  return 1;
361 }
362 
363 namespace lua_units {
364  std::string register_attacks_metatables(lua_State* L)
365  {
366  std::ostringstream cmd_out;
367 
368  // Create the unit attacks metatable.
369  cmd_out << "Adding unit attacks metatable...\n";
370 
371  luaL_newmetatable(L, uattacksKey);
372  lua_pushcfunction(L, impl_unit_attacks_get);
373  lua_setfield(L, -2, "__index");
374  lua_pushcfunction(L, impl_unit_attacks_set);
375  lua_setfield(L, -2, "__newindex");
376  lua_pushcfunction(L, impl_unit_attacks_len);
377  lua_setfield(L, -2, "__len");
378  lua_pushcfunction(L, impl_unit_attacks_iter);
379  lua_setfield(L, -2, "__ipairs");
380  lua_pushstring(L, uattacksKey);
381  lua_setfield(L, -2, "__metatable");
382 
383  // Create the unit attack metatable
384  luaL_newmetatable(L, uattackKey);
385  lua_pushcfunction(L, impl_unit_attack_get);
386  lua_setfield(L, -2, "__index");
387  lua_pushcfunction(L, impl_unit_attack_set);
388  lua_setfield(L, -2, "__newindex");
389  lua_pushcfunction(L, impl_unit_attack_equal);
390  lua_setfield(L, -2, "__eq");
391  lua_pushcfunction(L, impl_unit_attack_tostring);
392  lua_setfield(L, -2, "__tostring");
393  lua_pushcfunction(L, impl_unit_attack_collect);
394  lua_setfield(L, -2, "__gc");
395  lua_pushstring(L, uattackKey);
396  lua_setfield(L, -2, "__metatable");
397  lua_pushcfunction(L, impl_unit_attack_match);
398  lua_setfield(L, -2, "matches");
399 
400  return cmd_out.str();
401  }
402 }
const config & specials() const
Definition: attack_type.hpp:56
std::string alignment_str() const
Returns alignment specified by alignment() for filtering when exist.
Definition: attack_type.cpp:85
void set_min_range(int value)
Definition: attack_type.hpp:63
double defense_weight() const
Definition: attack_type.hpp:55
void set_num_attacks(int value)
Definition: attack_type.hpp:69
int min_range() const
Definition: attack_type.hpp:47
double attack_weight() const
Definition: attack_type.hpp:54
const std::string & range() const
Definition: attack_type.hpp:46
void set_attacks_used(int value)
int movement_used() const
void set_accuracy(int value)
Definition: attack_type.hpp:66
const std::string & type() const
Definition: attack_type.hpp:44
void set_movement_used(int value)
int parry() const
Definition: attack_type.hpp:51
void set_specials(config value)
Definition: attack_type.hpp:72
void set_defense_weight(double value)
Definition: attack_type.hpp:71
int num_attacks() const
Definition: attack_type.hpp:53
void set_parry(int value)
Definition: attack_type.hpp:67
void set_attack_weight(double value)
Definition: attack_type.hpp:70
void set_damage(int value)
Definition: attack_type.hpp:68
const t_string & name() const
Definition: attack_type.hpp:42
int attacks_used() const
const std::string & id() const
Definition: attack_type.hpp:43
void set_icon(const std::string &value)
Definition: attack_type.hpp:61
config to_config() const
void set_id(const std::string &value)
Definition: attack_type.hpp:59
void set_max_range(int value)
Definition: attack_type.hpp:64
void set_type(const std::string &value)
Definition: attack_type.hpp:60
int accuracy() const
Definition: attack_type.hpp:50
int max_range() const
Definition: attack_type.hpp:48
void set_range(const std::string &value)
Definition: attack_type.hpp:62
const std::string & icon() const
Definition: attack_type.hpp:45
int damage() const
Definition: attack_type.hpp:52
void set_name(const t_string &value)
Definition: attack_type.hpp:58
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
Storage for a unit, either owned by the Lua code (ptr != 0), a local variable unit (c_ptr !...
Definition: lua_unit.hpp:81
A single unit type that the player may recruit.
Definition: types.hpp:43
const_attack_itors attacks() const
Definition: types.cpp:543
This class represents a single unit of a specific type.
Definition: unit.hpp:133
std::size_t i
Definition: function.cpp:1023
attack_ptr add_attack(attack_itors::iterator position, Args &&... args)
Adds a new attack to the unit.
Definition: unit.hpp:950
bool remove_attack(attack_ptr atk)
Remove an attack from the unit.
Definition: unit.cpp:2747
attack_itors attacks()
Gets an iterator over this unit's attacks.
Definition: unit.hpp:933
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:927
int luaW_type_error(lua_State *L, int narg, const char *tname)
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:524
#define return_float_attrib(name, accessor)
Definition: lua_common.hpp:277
#define return_string_attrib(name, accessor)
Definition: lua_common.hpp:256
#define return_cfgref_attrib(name, accessor)
Definition: lua_common.hpp:309
#define return_int_attrib(name, accessor)
Definition: lua_common.hpp:267
#define return_bool_attrib(name, accessor)
Definition: lua_common.hpp:287
#define modify_int_attrib(name, accessor)
Definition: lua_common.hpp:358
#define modify_tstring_attrib(name, accessor)
Definition: lua_common.hpp:336
#define modify_string_attrib(name, accessor)
Definition: lua_common.hpp:347
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
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
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
int intf_create_attack(lua_State *L)
static int impl_unit_attacks_iter(lua_State *L)
static int impl_unit_attacks_len(lua_State *L)
Counts the attacks of a unit (__len metamethod).
static const char uattackKey[]
static attack_ref & luaW_checkweapon_ref(lua_State *L, int idx)
static const char uattacksKey[]
static int impl_unit_attack_match(lua_State *L)
static int impl_unit_attack_collect(lua_State *L)
const_attack_ptr luaW_toweapon(lua_State *L, int idx)
auto find_attack(T *u, const std::string &id) -> attack_ptr_in< T >
static int impl_unit_attacks_get(lua_State *L)
Gets the attacks of a unit or unit type (__index metamethod).
static int impl_unit_attack_get(lua_State *L)
Gets a property of a units attack (__index metamethod).
static int impl_unit_attack_set(lua_State *L)
Gets a property of a units attack (__index metamethod).
static attack_itors::iterator get_attack_iter(unit &u, attack_ptr atk)
static int impl_unit_attack_equal(lua_State *L)
static int impl_unit_attack_tostring(lua_State *L)
Turns a lua proxy attack to string.
static int impl_unit_attacks_next(lua_State *L)
static int impl_unit_attacks_set(lua_State *L)
void push_unit_attacks_table(lua_State *L, int idx)
void luaW_pushweapon(lua_State *L, attack_ptr weapon)
attack_type & luaW_checkweapon(lua_State *L, int idx)
std::shared_ptr< utils::const_clone_t< attack_type, std::remove_pointer_t< T > >> attack_ptr_in
const unit_type * luaW_tounittype(lua_State *L, int idx)
Test if a stack element is a unit type, and return it if so.
std::string register_attacks_metatables(lua_State *L)
static std::string at(const std::string &file, int line)
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:34
std::shared_ptr< attack_type > attack_ptr
Definition: ptr.hpp:33
void lua_push(lua_State *L, const T &val)
Definition: push_check.hpp:425
attack_ref(attack_ptr atk)
const_attack_ptr cattack
attack_ref(const_attack_ptr atk)
attack_ptr attack
mock_party p
static map_location::direction n