The Battle for Wesnoth  1.19.7+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 #include <utility>
30 
31 static const char uattacksKey[] = "unit attacks table";
32 static const char uattackKey[] = "unit attack";
33 
34 struct attack_ref {
37  attack_ref(const attack_ptr& atk) : attack(atk), cattack(atk) {}
38  attack_ref(const_attack_ptr atk) : cattack(std::move(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, const 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 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_string_attrib("alignment", attack.alignment_str());
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("attacks_used", attack.attacks_used());
269  return_int_attrib("parry", attack.parry());
270  return_int_attrib("max_range", attack.max_range());
271  return_int_attrib("min_range", attack.min_range());
272  return_cfgref_attrib("specials", attack.specials());
273  return_cfgref_attrib("__cfg", attack.to_config());
274  if(luaW_getmetafield(L, 1, m)) {
275  return 1;
276  }
277  std::string err_msg = "unknown property of attack: ";
278  err_msg += m;
279  return luaL_argerror(L, 2, err_msg.c_str());
280 }
281 
282 /**
283  * Gets a property of a units attack (__index metamethod).
284  * - Arg 1: table containing the userdata containing the unit id. and a string identyfying the attack.
285  * - Arg 2: string
286  * - Ret 1:
287  */
288 static int impl_unit_attack_set(lua_State *L)
289 {
290  attack_type& attack = luaW_checkweapon(L, 1);
291  char const *m = luaL_checkstring(L, 2);
292  modify_tstring_attrib("description", attack.set_name(value));
293  modify_string_attrib("name", attack.set_id(value));
294  modify_string_attrib("type", attack.set_type(value));
295  modify_string_attrib("icon", attack.set_icon(value));
296  modify_string_attrib("range", attack.set_range(value));
297  modify_string_attrib("alignment", attack.set_range(value));
298  modify_int_attrib("damage", attack.set_damage(value));
299  modify_int_attrib("number", attack.set_num_attacks(value));
300  modify_int_attrib("attack_weight", attack.set_attack_weight(value));
301  modify_int_attrib("defense_weight", attack.set_defense_weight(value));
302  modify_int_attrib("accuracy", attack.set_accuracy(value));
303  modify_int_attrib("movement_used", attack.set_movement_used(value));
304  modify_int_attrib("attacks_used", attack.set_attacks_used(value));
305  modify_int_attrib("parry", attack.set_parry(value));
306  modify_int_attrib("max_range", attack.set_max_range(value));
307  modify_int_attrib("min_range", attack.set_min_range(value));
308 
309  if(strcmp(m, "specials") == 0) {
310  attack.set_specials(luaW_checkconfig(L, 3));
311  return 0;
312  }
313 
314  std::string err_msg = "unknown modifiable property of attack: ";
315  err_msg += m;
316  return luaL_argerror(L, 2, err_msg.c_str());
317 }
318 
319 static int impl_unit_attack_equal(lua_State* L)
320 {
321  const_attack_ptr ut1 = luaW_toweapon(L, 1);
322  const_attack_ptr ut2 = luaW_toweapon(L, 2);
323  lua_pushboolean(L, ut1 == ut2);
324  return 1;
325 }
326 
327 /**
328  * Turns a lua proxy attack to string. (__tostring metamethod)
329  */
330 static int impl_unit_attack_tostring(lua_State* L)
331 {
333  std::ostringstream str;
334  str << "weapon: <" << atk->id() << '>';
335  lua_push(L, str.str());
336  return 1;
337 }
338 
339 static int impl_unit_attack_match(lua_State* L)
340 {
341  const_attack_ptr atk = luaW_toweapon(L, 1);
342  config cfg = luaW_checkconfig(L, 2);
343  if(!atk) {
344  return luaL_argerror(L, 1, "invalid attack");
345  }
346  lua_pushboolean(L, atk->matches_filter(cfg));
347  return 1;
348 }
349 
350 static int impl_unit_attack_collect(lua_State* L)
351 {
352  attack_ref* atk = static_cast<attack_ref*>(luaL_checkudata(L, 1, uattackKey));
353  atk->~attack_ref();
354  return 0;
355 }
356 
357 int intf_create_attack(lua_State* L)
358 {
359  auto atk = std::make_shared<attack_type>(luaW_checkconfig(L, 1));
360  luaW_pushweapon(L, atk);
361  return 1;
362 }
363 
364 namespace lua_units {
365  std::string register_attacks_metatables(lua_State* L)
366  {
367  std::ostringstream cmd_out;
368 
369  // Create the unit attacks metatable.
370  cmd_out << "Adding unit attacks metatable...\n";
371 
372  luaL_newmetatable(L, uattacksKey);
373  lua_pushcfunction(L, impl_unit_attacks_get);
374  lua_setfield(L, -2, "__index");
375  lua_pushcfunction(L, impl_unit_attacks_set);
376  lua_setfield(L, -2, "__newindex");
377  lua_pushcfunction(L, impl_unit_attacks_len);
378  lua_setfield(L, -2, "__len");
379  lua_pushcfunction(L, impl_unit_attacks_iter);
380  lua_setfield(L, -2, "__ipairs");
381  lua_pushstring(L, uattacksKey);
382  lua_setfield(L, -2, "__metatable");
383 
384  // Create the unit attack metatable
385  luaL_newmetatable(L, uattackKey);
386  lua_pushcfunction(L, impl_unit_attack_get);
387  lua_setfield(L, -2, "__index");
388  lua_pushcfunction(L, impl_unit_attack_set);
389  lua_setfield(L, -2, "__newindex");
390  lua_pushcfunction(L, impl_unit_attack_equal);
391  lua_setfield(L, -2, "__eq");
392  lua_pushcfunction(L, impl_unit_attack_tostring);
393  lua_setfield(L, -2, "__tostring");
394  lua_pushcfunction(L, impl_unit_attack_collect);
395  lua_setfield(L, -2, "__gc");
396  lua_pushstring(L, uattackKey);
397  lua_setfield(L, -2, "__metatable");
398  lua_pushcfunction(L, impl_unit_attack_match);
399  lua_setfield(L, -2, "matches");
400 
401  return cmd_out.str();
402  }
403 }
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.hpp:95
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:545
This class represents a single unit of a specific type.
Definition: unit.hpp:133
std::size_t i
Definition: function.cpp:1029
attack_ptr add_attack(attack_itors::iterator position, Args &&... args)
Adds a new attack to the unit.
Definition: unit.hpp:950
bool remove_attack(const attack_ptr &atk)
Remove an attack from the unit.
Definition: unit.cpp:2748
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)
void luaW_pushweapon(lua_State *L, const attack_ptr &weapon)
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)
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
const_attack_ptr cattack
attack_ref(const_attack_ptr atk)
attack_ptr attack
attack_ref(const attack_ptr &atk)
mock_party p
static map_location::direction n