The Battle for Wesnoth  1.19.25+dev
lua_unit_type.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2025
3  by Chris Beck <render787@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 
19 #include "deprecation.hpp"
20 #include "game_version.hpp"
21 #include "scripting/lua_common.hpp"
23 #include "scripting/push_check.hpp"
24 #include "units/types.hpp"
25 
26 #include <string>
27 #include <cstring>
28 
29 
30 /**
31  * Implementation for a lua reference to a unit_type.
32  */
33 
34 // Registry key
35 static const char UnitType[] = "unit type";
36 static const char UnitTypeTable[] = "unit types";
37 
38 #define UNIT_TYPE_GETTER(name, type) LATTR_GETTER(name, type, unit_type, ut)
39 #define UNIT_TYPE_VALID(name) LATTR_VALID(name, unit_type, ut)
41 
42 template<> struct lua_object_traits<unit_type> {
43  inline static auto metatable = UnitType;
44  inline static const unit_type& get(lua_State* L, int n) {
45  return luaW_checkunittype(L, n);
46  }
47 };
48 
50  return ut.type_name();
51 }
52 
53 UNIT_TYPE_GETTER("id", std::string) {
54  return ut.id();
55 }
56 
57 UNIT_TYPE_GETTER("alignment", std::string) {
58  return unit_alignments::get_string(ut.alignment());
59 }
60 
61 UNIT_TYPE_GETTER("race", std::string) {
62  return ut.race_id();
63 }
64 
65 UNIT_TYPE_GETTER("image", std::string) {
66  return ut.image();
67 }
68 
69 UNIT_TYPE_GETTER("icon", std::string) {
70  return ut.icon();
71 }
72 
73 UNIT_TYPE_GETTER("profile", std::string) {
74  return ut.big_profile();
75 }
76 
77 UNIT_TYPE_GETTER("small_profile", std::string) {
78  return ut.small_profile();
79 }
80 
81 UNIT_TYPE_GETTER("max_hitpoints", int) {
82  return ut.hitpoints();
83 }
84 
85 UNIT_TYPE_GETTER("max_moves", int) {
86  return ut.movement();
87 }
88 
89 UNIT_TYPE_GETTER("max_experience", int) {
90  return ut.experience_needed();
91 }
92 
93 UNIT_TYPE_GETTER("cost", int) {
94  return ut.cost();
95 }
96 
97 UNIT_TYPE_GETTER("level", int) {
98  return ut.level();
99 }
100 
101 UNIT_TYPE_GETTER("recall_cost", int) {
102  return ut.recall_cost();
103 }
104 
105 UNIT_TYPE_GETTER("advances_to", std::vector<std::string>) {
106  return ut.advances_to();
107 }
108 
109 UNIT_TYPE_GETTER("advances_from", std::vector<std::string>) {
110  return ut.advances_from();
111 }
112 
114  return ut.get_cfg();
115 }
116 
117 using traits_map = std::map<std::string,config>;
119  traits_map traits;
120  for (const config& trait : ut.possible_traits()) {
121  traits.emplace(trait["id"], trait);
122  }
123  return traits;
124 }
125 
126 UNIT_TYPE_GETTER("abilities", std::vector<std::string>) {
127  deprecated_message("unit_type.abilities", DEP_LEVEL::FOR_REMOVAL, version_info("1.21"), "Use unit_type.ability_ids instead.");
128  return ut.get_ability_id_list();
129 }
130 
131 UNIT_TYPE_GETTER("ability_ids", std::vector<std::string>) {
132  return ut.get_ability_id_list();
133 }
135  (void)ut;
137  return lua_index_raw(L);
138 }
139 
140 UNIT_TYPE_VALID("variations") {
141  return ut.variation_id().empty();
142 }
143 
145  // TODO: Should this only exist for base units?
146  *new(L) const unit_type* = &ut;
147  luaL_setmetatable(L, UnitTypeTable);
148  return lua_index_raw(L);
149 }
150 
151 /**
152  * Gets some data on a unit type (__index metamethod).
153  * - Arg 1: table containing an "id" field.
154  * - Arg 2: string containing the name of the property.
155  * - Ret 1: something containing the attribute.
156  */
157 static int impl_unit_type_get(lua_State *L)
158 {
159  return unitTypeReg.get(L);
160 }
161 
162 /**
163  * Gets a list of data on a unit type (__dir metamethod).
164  * - Ret 1: a list of attributes.
165  */
166 static int impl_unit_type_dir(lua_State *L)
167 {
168  return unitTypeReg.dir(L);
169 }
170 
171 static int impl_unit_type_equal(lua_State* L)
172 {
173  const unit_type& ut1 = luaW_checkunittype(L, 1);
174  if(const unit_type* ut2 = luaW_tounittype(L, 2)) {
175  lua_pushboolean(L, &ut1 == ut2);
176  } else {
177  lua_pushboolean(L, false);
178  }
179  return 1;
180 }
181 
182 static int impl_unit_type_list(lua_State* L) {
183  std::vector<std::string> keys;
184  if(const unit_type* base = *static_cast<const unit_type**>(luaL_testudata(L, 1, UnitTypeTable))) {
185  keys = base->variations();
186  if(base->has_gender_variation(unit_race::MALE)) {
187  keys.push_back("male");
188  }
189  if(base->has_gender_variation(unit_race::FEMALE)) {
190  keys.push_back("female");
191  }
192  } else {
193  keys.reserve(unit_types.types().size());
194  for(const auto& p : unit_types.types()) {
195  keys.push_back(p.first);
196  }
197  }
198  lua_push(L, keys);
199  return 1;
200 }
201 
202 static int impl_unit_type_lookup(lua_State* L)
203 {
204  std::string id = luaL_checkstring(L, 2);
205  const unit_type* ut;
206  if(const unit_type* base = *static_cast<const unit_type**>(luaL_testudata(L, 1, UnitTypeTable))) {
207  if(id == "male" || id == "female") {
208  ut = &base->get_gender_unit_type(id);
209  } else {
210  ut = &base->get_variation(id);
211  }
212  } else {
213  ut = unit_types.find(id);
214  }
215  if(ut) {
216  luaW_pushunittype(L, *ut);
217  return 1;
218  }
219  return 0;
220 }
221 
222 static int impl_unit_type_new(lua_State* L)
223 {
224  // This could someday become a hook to construct new unit types on the fly?
225  // For now though, it's just an error
226  lua_pushstring(L, "unit_types table is read-only");
227  return lua_error(L);
228 }
229 
230 static int impl_unit_type_count(lua_State* L)
231 {
232  lua_pushnumber(L, unit_types.types().size());
233  return 1;
234 }
235 
236 static int impl_unit_type_next(lua_State* L)
237 {
238  const unit_type* base = *static_cast<const unit_type**>(luaL_checkudata(L, 1, UnitTypeTable));
239  const auto& unit_map = base ? base->variation_types() : unit_types.types();
240  auto it = unit_map.end();
241  if(lua_isnoneornil(L, 2)) {
242  if(base) {
244  lua_pushstring(L, "male");
246  return 2;
247  } else if(base->has_gender_variation(unit_race::FEMALE)) {
248  lua_pushstring(L, "female");
250  return 2;
251  }
252  }
253  it = unit_map.begin();
254  } else {
255  const std::string id = luaL_checkstring(L, 2);
256  if(base) {
257  if(id == "male" && base->has_gender_variation(unit_race::FEMALE)) {
258  lua_pushstring(L, "female");
260  return 2;
261  } else if(id == "male" || id == "female") {
262  it = unit_map.begin();
263  }
264  }
265  if(it == unit_map.end()) {
266  it = unit_map.find(id);
267  }
268  if(it == unit_map.end()) {
269  return 0;
270  }
271  ++it;
272  }
273  if (it == unit_map.end()) {
274  return 0;
275  }
276  lua_pushlstring(L, it->first.c_str(), it->first.size());
277  luaW_pushunittype(L, it->second);
278  if(!base) {
279  // Make sure the unit is built.
281  }
282  return 2;
283 }
284 
285 static int impl_unit_type_pairs(lua_State* L) {
286  lua_pushcfunction(L, &impl_unit_type_next);
287  lua_pushvalue(L, -2);
288  lua_pushnil(L);
289  return 3;
290 }
291 
292 /**
293  * Turns a lua proxy unit type to string. (__tostring metamethod)
294  */
295 static int impl_unit_type_tostring(lua_State* L)
296 {
297  const unit_type& ut = luaW_checkunittype(L, 1);
298  std::ostringstream str;
299 
300  str << "unit type: <" << ut.id() << '>';
301 
302  lua_push(L, str.str());
303  return 1;
304 }
305 
306 namespace lua_unit_type {
307  std::string register_metatable(lua_State * L)
308  {
309  luaL_newmetatable(L, UnitType);
310 
311  lua_pushcfunction(L, impl_unit_type_get);
312  lua_setfield(L, -2, "__index");
313  lua_pushcfunction(L, impl_unit_type_dir);
314  lua_setfield(L, -2, "__dir");
315  lua_pushcfunction(L, impl_unit_type_tostring);
316  lua_setfield(L, -2, "__tostring");
317  lua_pushcfunction(L, impl_unit_type_equal);
318  lua_setfield(L, -2, "__eq");
319  lua_pushstring(L, UnitType);
320  lua_setfield(L, -2, "__metatable");
321 
322  return "Adding unit type metatable...\n";
323  }
324 
325  std::string register_table(lua_State* L)
326  {
327  lua_getglobal(L, "wesnoth");
328  *new(L) unit_type* = nullptr;
329  luaL_newmetatable(L, UnitTypeTable);
330  lua_pushcfunction(L, impl_unit_type_lookup);
331  lua_setfield(L, -2, "__index");
332  lua_pushcfunction(L, impl_unit_type_list);
333  lua_setfield(L, -2, "__dir");
334  lua_pushcfunction(L, impl_unit_type_new);
335  lua_setfield(L, -2, "__newindex");
336  lua_pushcfunction(L, impl_unit_type_count);
337  lua_setfield(L, -2, "__len");
338  lua_pushcfunction(L, impl_unit_type_pairs);
339  lua_setfield(L, -2, "__pairs");
340  lua_pushstring(L, UnitTypeTable);
341  lua_setfield(L, -2, "__metatable");
342  lua_setmetatable(L, -2);
343  lua_setfield(L, -2, "unit_types");
344  lua_pop(L, 1);
345 
346  return "Adding unit_types table...\n";
347  }
348 }
349 
350 void luaW_pushunittype(lua_State *L, const unit_type& ut)
351 {
352  *static_cast<const unit_type**>(lua_newuserdatauv(L, sizeof(unit_type*), 0)) = &ut;
353  luaL_setmetatable(L, UnitType);
354 }
355 
356 const unit_type* luaW_tounittype(lua_State* L, int idx)
357 {
358  if(void* p = luaL_testudata(L, idx, UnitType)) {
359  return *static_cast<const unit_type**>(p);
360  }
361  return nullptr;
362 }
363 
364 const unit_type& luaW_checkunittype(lua_State* L, int idx)
365 {
366  return **static_cast<const unit_type**>(luaL_checkudata(L, idx, UnitType));
367 }
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:157
Container associating units to locations.
Definition: map.hpp:98
unit_iterator end()
Definition: map.hpp:428
unit_iterator find(std::size_t id)
Definition: map.cpp:302
unit_iterator begin()
Definition: map.hpp:418
@ FEMALE
Definition: race.hpp:28
@ MALE
Definition: race.hpp:28
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1253
void build_unit_type(const unit_type &ut, unit_type::BUILD_STATUS status) const
Makes sure the provided unit_type is built to the specified level.
Definition: types.cpp:1245
const unit_type_map & types() const
Definition: types.hpp:398
A single unit type that the player may recruit.
Definition: types.hpp:43
const unit_type & get_gender_unit_type(const std::string &gender) const
Returns a gendered variant of this unit_type.
Definition: types.cpp:416
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:145
const unit_type & get_variation(const std::string &id) const
Definition: types.cpp:437
const variations_map & variation_types() const
Definition: types.hpp:258
bool has_gender_variation(const unit_race::GENDER gender) const
Definition: types.cpp:681
@ FULL
Definition: types.hpp:77
Represents version numbers.
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
Definition: deprecation.cpp:29
Interfaces for manipulating version numbers of engine, add-ons, etc.
void push_unit_attacks_table(lua_State *L, int idx)
static int impl_unit_type_lookup(lua_State *L)
static int impl_unit_type_tostring(lua_State *L)
Turns a lua proxy unit type to string.
std::map< std::string, config > traits_map
static int impl_unit_type_next(lua_State *L)
#define UNIT_TYPE_VALID(name)
static int impl_unit_type_dir(lua_State *L)
Gets a list of data on a unit type (__dir metamethod).
const unit_type * luaW_tounittype(lua_State *L, int idx)
Test if a stack element is a unit type, and return it if so.
static int impl_unit_type_new(lua_State *L)
static int impl_unit_type_get(lua_State *L)
Gets some data on a unit type (__index metamethod).
static int impl_unit_type_equal(lua_State *L)
const unit_type & luaW_checkunittype(lua_State *L, int idx)
Test if a stack element is a unit type, and return it if so.
static const char UnitType[]
Implementation for a lua reference to a unit_type.
#define UNIT_TYPE_GETTER(name, type)
static int impl_unit_type_count(lua_State *L)
static int impl_unit_type_list(lua_State *L)
void luaW_pushunittype(lua_State *L, const unit_type &ut)
Create a lua object containing a reference to a unittype, and a metatable to access the properties.
static const char UnitTypeTable[]
luaW_Registry unitTypeReg
static int impl_unit_type_pairs(lua_State *L)
This namespace contains bindings for lua to hold a reference to a unit type and access its stats.
std::string register_metatable(lua_State *L)
std::string register_table(lua_State *L)
constexpr auto keys
Definition: ranges.hpp:43
void lua_push(lua_State *L, const T &val)
Definition: push_check.hpp:425
Holds a lookup table for members of one type of object.
int dir(lua_State *L)
Implement __dir metamethod.
int get(lua_State *L)
Implement __index metamethod.
static const unit_type & get(lua_State *L, int n)
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
mock_party p
static map_location::direction n
unit_type_data unit_types
Definition: types.cpp:1494