The Battle for Wesnoth  1.19.1+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_int_attrib("damage", attack.damage());
261  return_int_attrib("number", attack.num_attacks());
262  return_float_attrib("attack_weight", attack.attack_weight());
263  return_float_attrib("defense_weight", attack.defense_weight());
264  return_int_attrib("accuracy", attack.accuracy());
265  return_int_attrib("movement_used", attack.movement_used());
266  return_int_attrib("attacks_used", attack.attacks_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 {
286  attack_type& attack = luaW_checkweapon(L, 1);
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("attacks_used", attack.set_attacks_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 
312 static int impl_unit_attack_equal(lua_State* L)
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  */
323 static int impl_unit_attack_tostring(lua_State* L)
324 {
326  std::ostringstream str;
327  str << "weapon: <" << atk->id() << '>';
328  lua_push(L, str.str());
329  return 1;
330 }
331 
332 static int impl_unit_attack_match(lua_State* L)
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 
343 static int impl_unit_attack_collect(lua_State* L)
344 {
345  attack_ref* atk = static_cast<attack_ref*>(luaL_checkudata(L, 1, uattackKey));
346  atk->~attack_ref();
347  return 0;
348 }
349 
350 int intf_create_attack(lua_State* L)
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 {
358  std::string register_attacks_metatables(lua_State* L)
359  {
360  std::ostringstream cmd_out;
361 
362  // Create the unit attacks metatable.
363  cmd_out << "Adding unit attacks metatable...\n";
364 
365  luaL_newmetatable(L, uattacksKey);
366  lua_pushcfunction(L, impl_unit_attacks_get);
367  lua_setfield(L, -2, "__index");
368  lua_pushcfunction(L, impl_unit_attacks_set);
369  lua_setfield(L, -2, "__newindex");
370  lua_pushcfunction(L, impl_unit_attacks_len);
371  lua_setfield(L, -2, "__len");
372  lua_pushcfunction(L, impl_unit_attacks_iter);
373  lua_setfield(L, -2, "__ipairs");
374  lua_pushstring(L, uattacksKey);
375  lua_setfield(L, -2, "__metatable");
376 
377  // Create the unit attack metatable
378  luaL_newmetatable(L, uattackKey);
379  lua_pushcfunction(L, impl_unit_attack_get);
380  lua_setfield(L, -2, "__index");
381  lua_pushcfunction(L, impl_unit_attack_set);
382  lua_setfield(L, -2, "__newindex");
383  lua_pushcfunction(L, impl_unit_attack_equal);
384  lua_setfield(L, -2, "__eq");
385  lua_pushcfunction(L, impl_unit_attack_tostring);
386  lua_setfield(L, -2, "__tostring");
387  lua_pushcfunction(L, impl_unit_attack_collect);
388  lua_setfield(L, -2, "__gc");
389  lua_pushstring(L, uattackKey);
390  lua_setfield(L, -2, "__metatable");
391  lua_pushcfunction(L, impl_unit_attack_match);
392  lua_setfield(L, -2, "matches");
393 
394  return cmd_out.str();
395  }
396 }
const config & specials() const
Definition: attack_type.hpp:55
double defense_weight() const
Definition: attack_type.hpp:54
void set_num_attacks(int value)
Definition: attack_type.hpp:65
double attack_weight() const
Definition: attack_type.hpp:53
const std::string & range() const
Definition: attack_type.hpp:45
void set_attacks_used(int value)
int movement_used() const
void set_accuracy(int value)
Definition: attack_type.hpp:62
const std::string & type() const
Definition: attack_type.hpp:43
void set_movement_used(int value)
int parry() const
Definition: attack_type.hpp:50
void set_specials(config value)
Definition: attack_type.hpp:68
void set_defense_weight(double value)
Definition: attack_type.hpp:67
int num_attacks() const
Definition: attack_type.hpp:52
void set_parry(int value)
Definition: attack_type.hpp:63
void set_attack_weight(double value)
Definition: attack_type.hpp:66
void set_damage(int value)
Definition: attack_type.hpp:64
const t_string & name() const
Definition: attack_type.hpp:41
int attacks_used() const
const std::string & id() const
Definition: attack_type.hpp:42
void set_icon(const std::string &value)
Definition: attack_type.hpp:60
config to_config() const
void set_id(const std::string &value)
Definition: attack_type.hpp:58
void set_type(const std::string &value)
Definition: attack_type.hpp:59
int accuracy() const
Definition: attack_type.hpp:49
void set_range(const std::string &value)
Definition: attack_type.hpp:61
const std::string & icon() const
Definition: attack_type.hpp:44
int damage() const
Definition: attack_type.hpp:51
void set_name(const t_string &value)
Definition: attack_type.hpp:57
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
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:968
attack_ptr add_attack(attack_itors::iterator position, Args &&... args)
Adds a new attack to the unit.
Definition: unit.hpp:944
bool remove_attack(attack_ptr atk)
Remove an attack from the unit.
Definition: unit.cpp:2804
attack_itors attacks()
Gets an iterator over this unit's attacks.
Definition: unit.hpp:927
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:914
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:189
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:160
unit * luaW_tounit(lua_State *L, int index, bool only_on_map)
Converts a Lua value to a unit pointer.
Definition: lua_unit.cpp:140
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