The Battle for Wesnoth  1.17.23+dev
lua_unit_attacks.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2023
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) {}
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("attacks_used", attack.attacks_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  */
285 static int impl_unit_attack_set(lua_State *L)
286 {
287  attack_type& attack = luaW_checkweapon(L, 1);
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("attacks_used", attack.set_attacks_used(value));
301  modify_int_attrib("parry", attack.set_parry(value));
302 
303  if(strcmp(m, "specials") == 0) {
304  attack.set_specials(luaW_checkconfig(L, 3));
305  return 0;
306  }
307 
308  std::string err_msg = "unknown modifiable property of attack: ";
309  err_msg += m;
310  return luaL_argerror(L, 2, err_msg.c_str());
311 }
312 
313 static int impl_unit_attack_equal(lua_State* L)
314 {
315  const_attack_ptr ut1 = luaW_toweapon(L, 1);
316  const_attack_ptr ut2 = luaW_toweapon(L, 2);
317  lua_pushboolean(L, ut1 == ut2);
318  return 1;
319 }
320 
321 /**
322  * Turns a lua proxy attack to string. (__tostring metamethod)
323  */
324 static int impl_unit_attack_tostring(lua_State* L)
325 {
327  std::ostringstream str;
328  str << "weapon: <" << atk->id() << '>';
329  lua_push(L, str.str());
330  return 1;
331 }
332 
333 static int impl_unit_attack_match(lua_State* L)
334 {
335  const_attack_ptr atk = luaW_toweapon(L, 1);
336  config cfg = luaW_checkconfig(L, 2);
337  if(!atk) {
338  return luaL_argerror(L, 1, "invalid attack");
339  }
340  lua_pushboolean(L, atk->matches_filter(cfg));
341  return 1;
342 }
343 
344 static int impl_unit_attack_collect(lua_State* L)
345 {
346  attack_ref* atk = static_cast<attack_ref*>(luaL_checkudata(L, 1, uattackKey));
347  atk->~attack_ref();
348  return 0;
349 }
350 
351 int intf_create_attack(lua_State* L)
352 {
353  auto atk = std::make_shared<attack_type>(luaW_checkconfig(L, 1));
354  luaW_pushweapon(L, atk);
355  return 1;
356 }
357 
358 namespace lua_units {
359  std::string register_attacks_metatables(lua_State* L)
360  {
361  std::ostringstream cmd_out;
362 
363  // Create the unit attacks metatable.
364  cmd_out << "Adding unit attacks metatable...\n";
365 
366  luaL_newmetatable(L, uattacksKey);
367  lua_pushcfunction(L, impl_unit_attacks_get);
368  lua_setfield(L, -2, "__index");
369  lua_pushcfunction(L, impl_unit_attacks_set);
370  lua_setfield(L, -2, "__newindex");
371  lua_pushcfunction(L, impl_unit_attacks_len);
372  lua_setfield(L, -2, "__len");
373  lua_pushcfunction(L, impl_unit_attacks_iter);
374  lua_setfield(L, -2, "__ipairs");
375  lua_pushstring(L, uattacksKey);
376  lua_setfield(L, -2, "__metatable");
377 
378  // Create the unit attack metatable
379  luaL_newmetatable(L, uattackKey);
380  lua_pushcfunction(L, impl_unit_attack_get);
381  lua_setfield(L, -2, "__index");
382  lua_pushcfunction(L, impl_unit_attack_set);
383  lua_setfield(L, -2, "__newindex");
384  lua_pushcfunction(L, impl_unit_attack_equal);
385  lua_setfield(L, -2, "__eq");
386  lua_pushcfunction(L, impl_unit_attack_tostring);
387  lua_setfield(L, -2, "__tostring");
388  lua_pushcfunction(L, impl_unit_attack_collect);
389  lua_setfield(L, -2, "__gc");
390  lua_pushstring(L, uattackKey);
391  lua_setfield(L, -2, "__metatable");
392  lua_pushcfunction(L, impl_unit_attack_match);
393  lua_setfield(L, -2, "matches");
394 
395  return cmd_out.str();
396  }
397 }
const config & specials() const
Definition: attack_type.hpp:57
double defense_weight() const
Definition: attack_type.hpp:56
void set_num_attacks(int value)
Definition: attack_type.hpp:67
double attack_weight() const
Definition: attack_type.hpp:55
const std::string & range() const
Definition: attack_type.hpp:47
void set_attacks_used(int value)
int movement_used() const
void set_accuracy(int value)
Definition: attack_type.hpp:64
const std::string & type() const
Definition: attack_type.hpp:45
void set_movement_used(int value)
int parry() const
Definition: attack_type.hpp:52
void set_specials(config value)
Definition: attack_type.hpp:70
void set_defense_weight(double value)
Definition: attack_type.hpp:69
int num_attacks() const
Definition: attack_type.hpp:54
void set_parry(int value)
Definition: attack_type.hpp:65
void set_attack_weight(double value)
Definition: attack_type.hpp:68
void set_damage(int value)
Definition: attack_type.hpp:66
const t_string & name() const
Definition: attack_type.hpp:43
int attacks_used() const
const std::string & id() const
Definition: attack_type.hpp:44
void set_icon(const std::string &value)
Definition: attack_type.hpp:62
config to_config() const
void set_id(const std::string &value)
Definition: attack_type.hpp:60
void set_type(const std::string &value)
Definition: attack_type.hpp:61
int accuracy() const
Definition: attack_type.hpp:51
void set_range(const std::string &value)
Definition: attack_type.hpp:63
const std::string & icon() const
Definition: attack_type.hpp:46
int damage() const
Definition: attack_type.hpp:53
void set_name(const t_string &value)
Definition: attack_type.hpp:59
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
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:46
const_attack_itors attacks() const
Definition: types.cpp:543
This class represents a single unit of a specific type.
Definition: unit.hpp:135
std::size_t i
Definition: function.cpp:968
attack_ptr add_attack(attack_itors::iterator position, Args &&... args)
Adds a new attack to the unit.
Definition: unit.hpp:946
bool remove_attack(attack_ptr atk)
Remove an attack from the unit.
Definition: unit.cpp:2766
attack_itors attacks()
Gets an iterator over this unit's attacks.
Definition: unit.hpp:929
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:920
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:527
#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:373
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