The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
lua_unit_attacks.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2009 - 2025
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
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
31static const char uattacksKey[] = "unit attacks table";
32static const char uattackKey[] = "unit attack";
33
40
41void 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
51void 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
61void 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
71static attack_ref& luaW_checkweapon_ref(lua_State* L, int idx)
72{
73 return *static_cast<attack_ref*>(luaL_checkudata(L, idx, uattackKey));
74}
75
76const_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
84attack_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
93template<typename T>
94using 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
97template<typename T>
98auto 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
109template<typename T>
110auto 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 */
127static 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
148static attack_itors::iterator get_attack_iter(unit& u, attack_ptr atk)
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
156static 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 {
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 */
207static 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
222static 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
236static 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 */
250static 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_cfg());
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 */
288static 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) {
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
319static int impl_unit_attack_equal(lua_State* L)
320{
323 lua_pushboolean(L, ut1 == ut2);
324 return 1;
325}
326
327/**
328 * Turns a lua proxy attack to string. (__tostring metamethod)
329 */
330static 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
339static int impl_unit_attack_match(lua_State* L)
340{
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
350static 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
357int 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
364namespace 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}
std::string alignment_str() const
Returns alignment specified by alignment() for filtering when exist.
void set_min_range(int value)
const t_string & name() const
double defense_weight() const
void set_num_attacks(int value)
int min_range() const
double attack_weight() const
void set_attacks_used(int value)
int movement_used() const
void set_accuracy(int value)
void set_movement_used(int value)
int parry() const
void set_specials_cfg(const config &value)
config specials_cfg() const
void set_defense_weight(double value)
int num_attacks() const
const std::string & id() const
const std::string & icon() const
void set_parry(int value)
void set_attack_weight(double value)
void set_damage(int value)
const std::string & range() const
int attacks_used() const
void set_icon(const std::string &value)
config to_config() const
const std::string & type() const
void set_id(const std::string &value)
void set_max_range(int value)
void set_type(const std::string &value)
int accuracy() const
int max_range() const
void set_range(const std::string &value)
int damage() const
void set_name(const t_string &value)
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
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:505
This class represents a single unit of a specific type.
Definition unit.hpp:39
const config * cfg
attack_ptr add_attack(attack_itors::iterator position, Args &&... args)
Adds a new attack to the unit.
Definition unit.hpp:865
bool remove_attack(const attack_ptr &atk)
Remove an attack from the unit.
Definition unit.cpp:2808
attack_itors attacks()
Gets an iterator over this unit's attacks.
Definition unit.hpp:848
std::string id
Text to match against addon_info.tags()
Definition manager.cpp:199
config luaW_checkconfig(lua_State *L, int index)
Converts an optional table or vconfig to a config object.
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.
#define return_float_attrib(name, accessor)
#define return_string_attrib(name, accessor)
#define return_cfgref_attrib(name, accessor)
#define return_int_attrib(name, accessor)
#define return_bool_attrib(name, accessor)
#define modify_int_attrib(name, accessor)
#define modify_tstring_attrib(name, accessor)
#define modify_string_attrib(name, accessor)
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
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
attack_type & luaW_checkweapon(lua_State *L, int idx)
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 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)
static attack_ref & luaW_checkweapon_ref(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)
STL namespace.
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)
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
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
#define i