1 /*
2 Copyright (C) 2014 - 2018 by Chris Beck <render787@gmail.com>
3 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY.
11
12 See the COPYING file for more details.
13 */
14
15 #include "scripting/lua_unit_type.hpp"
16
17 #include "scripting/lua_common.hpp"
18 #include "scripting/lua_unit_attacks.hpp"
19 #include "scripting/push_check.hpp"
20 #include "units/types.hpp"
21
22 #include <string>
23 #include <cstring>
24
25 #include "lua/lua.h"
26 #include "lua/lauxlib.h"
27
28 /**
29 * Implementation for a lua reference to a unit_type.
30 */
31
32 // Registry key
33 static const char UnitType[] = "unit type";
34 static const char UnitTypeTable[] = "unit types";
35
36 /**
37 * Gets some data on a unit type (__index metamethod).
38 * - Arg 1: table containing an "id" field.
39 * - Arg 2: string containing the name of the property.
40 * - Ret 1: something containing the attribute.
41 */
impl_unit_type_get(lua_State * L)42 static int impl_unit_type_get(lua_State *L)
43 {
44 const unit_type& ut = luaW_checkunittype(L, 1);
45 char const *m = luaL_checkstring(L, 2);
46
47 // Find the corresponding attribute.
48 return_tstring_attrib("name", ut.type_name());
49 return_string_attrib("id", ut.id());
50 return_string_attrib("alignment", ut.alignment().to_string());
51 return_string_attrib("race", ut.race_id());
52 return_string_attrib("image", ut.image());
53 return_string_attrib("icon", ut.icon());
54 return_string_attrib("profile", ut.big_profile());
55 return_string_attrib("small_profile", ut.small_profile());
56 return_int_attrib("max_hitpoints", ut.hitpoints());
57 return_int_attrib("max_moves", ut.movement());
58 return_int_attrib("max_experience", ut.experience_needed());
59 return_int_attrib("cost", ut.cost());
60 return_int_attrib("level", ut.level());
61 return_int_attrib("recall_cost", ut.recall_cost());
62 return_vector_string_attrib("advances_to", ut.advances_to());
63 return_vector_string_attrib("advances_from", ut.advances_from());
64 return_cfgref_attrib("__cfg", ut.get_cfg());
65 if (strcmp(m, "traits") == 0) {
66 lua_newtable(L);
67 for (const config& trait : ut.possible_traits()) {
68 const std::string& id = trait["id"];
69 lua_pushlstring(L, id.c_str(), id.length());
70 luaW_pushconfig(L, trait);
71 lua_rawset(L, -3);
72 }
73 return 1;
74 }
75 if (strcmp(m, "abilities") == 0) {
76 lua_push(L, ut.get_ability_list());
77 return 1;
78 }
79 if (strcmp(m, "attacks") == 0) {
80 push_unit_attacks_table(L, 1);
81 return 1;
82 }
83 // TODO: Should this only exist for base units?
84 if(strcmp(m, "variations") == 0) {
85 *new(L) const unit_type* = &ut;
86 luaL_setmetatable(L, UnitTypeTable);
87 return 1;
88 }
89 return 0;
90 }
91
impl_unit_type_equal(lua_State * L)92 static int impl_unit_type_equal(lua_State* L)
93 {
94 const unit_type& ut1 = luaW_checkunittype(L, 1);
95 if(const unit_type* ut2 = luaW_tounittype(L, 2)) {
96 lua_pushboolean(L, &ut1 == ut2);
97 } else {
98 lua_pushboolean(L, false);
99 }
100 return 1;
101 }
102
impl_unit_type_lookup(lua_State * L)103 static int impl_unit_type_lookup(lua_State* L)
104 {
105 std::string id = luaL_checkstring(L, 2);
106 const unit_type* ut;
107 if(const unit_type* base = *static_cast<const unit_type**>(luaL_testudata(L, 1, UnitTypeTable))) {
108 if(id == "male" || id == "female") {
109 ut = &base->get_gender_unit_type(id);
110 } else {
111 ut = &base->get_variation(id);
112 }
113 } else {
114 ut = unit_types.find(id);
115 }
116 if(ut) {
117 luaW_pushunittype(L, *ut);
118 return 1;
119 }
120 return 0;
121 }
122
impl_unit_type_new(lua_State * L)123 static int impl_unit_type_new(lua_State* L)
124 {
125 // This could someday become a hook to construct new unit types on the fly?
126 // For now though, it's just an error
127 lua_pushstring(L, "unit_types table is read-only");
128 return lua_error(L);
129 }
130
impl_unit_type_count(lua_State * L)131 static int impl_unit_type_count(lua_State* L)
132 {
133 lua_pushnumber(L, unit_types.types().size());
134 return 1;
135 }
136
impl_unit_type_next(lua_State * L)137 static int impl_unit_type_next(lua_State* L)
138 {
139 const unit_type* base = *static_cast<const unit_type**>(luaL_checkudata(L, 1, UnitTypeTable));
140 const auto& unit_map = base ? base->variation_types() : unit_types.types();
141 auto it = unit_map.end();
142 if(lua_isnoneornil(L, 2)) {
143 if(base) {
144 if(base->has_gender_variation(unit_race::MALE)) {
145 lua_pushstring(L, "male");
146 luaW_pushunittype(L, base->get_gender_unit_type(unit_race::MALE));
147 return 2;
148 } else if(base->has_gender_variation(unit_race::FEMALE)) {
149 lua_pushstring(L, "female");
150 luaW_pushunittype(L, base->get_gender_unit_type(unit_race::FEMALE));
151 return 2;
152 }
153 }
154 it = unit_map.begin();
155 } else {
156 const std::string id = luaL_checkstring(L, 2);
157 if(base) {
158 if(id == "male" && base->has_gender_variation(unit_race::FEMALE)) {
159 lua_pushstring(L, "female");
160 luaW_pushunittype(L, base->get_gender_unit_type(unit_race::FEMALE));
161 return 2;
162 } else if(id == "male" || id == "female") {
163 it = unit_map.begin();
164 }
165 }
166 if(it == unit_map.end()) {
167 it = unit_map.find(id);
168 }
169 if(it == unit_map.end()) {
170 return 0;
171 }
172 ++it;
173 }
174 if (it == unit_map.end()) {
175 return 0;
176 }
177 lua_pushlstring(L, it->first.c_str(), it->first.size());
178 luaW_pushunittype(L, it->second);
179 return 2;
180 }
181
impl_unit_type_pairs(lua_State * L)182 static int impl_unit_type_pairs(lua_State* L) {
183 lua_pushcfunction(L, &impl_unit_type_next);
184 lua_pushvalue(L, -2);
185 lua_pushnil(L);
186 return 3;
187 }
188
189 namespace lua_unit_type {
register_metatable(lua_State * L)190 std::string register_metatable(lua_State * L)
191 {
192 luaL_newmetatable(L, UnitType);
193
194 lua_pushcfunction(L, impl_unit_type_get);
195 lua_setfield(L, -2, "__index");
196 lua_pushcfunction(L, impl_unit_type_equal);
197 lua_setfield(L, -2, "__eq");
198 lua_pushstring(L, UnitType);
199 lua_setfield(L, -2, "__metatable");
200
201 return "Adding unit type metatable...\n";
202 }
203
register_table(lua_State * L)204 std::string register_table(lua_State* L)
205 {
206 lua_getglobal(L, "wesnoth");
207 *new(L) unit_type* = nullptr;
208 luaL_newmetatable(L, UnitTypeTable);
209 lua_pushcfunction(L, impl_unit_type_lookup);
210 lua_setfield(L, -2, "__index");
211 lua_pushcfunction(L, impl_unit_type_new);
212 lua_setfield(L, -2, "__newindex");
213 lua_pushcfunction(L, impl_unit_type_count);
214 lua_setfield(L, -2, "__len");
215 lua_pushcfunction(L, impl_unit_type_pairs);
216 lua_setfield(L, -2, "__pairs");
217 lua_pushstring(L, UnitTypeTable);
218 lua_setfield(L, -2, "__metatable");
219 lua_setmetatable(L, -2);
220 lua_setfield(L, -2, "unit_types");
221 lua_pop(L, 1);
222
223 return "Adding unit_types table...\n";
224 }
225 }
226
luaW_pushunittype(lua_State * L,const unit_type & ut)227 void luaW_pushunittype(lua_State *L, const unit_type& ut)
228 {
229 *static_cast<const unit_type**>(lua_newuserdata(L, sizeof(unit_type*))) = &ut;
230 luaL_setmetatable(L, UnitType);
231 }
232
luaW_tounittype(lua_State * L,int idx)233 const unit_type* luaW_tounittype(lua_State* L, int idx)
234 {
235 if(void* p = luaL_testudata(L, idx, UnitType)) {
236 return *static_cast<const unit_type**>(p);
237 }
238 return nullptr;
239 }
240
luaW_checkunittype(lua_State * L,int idx)241 const unit_type& luaW_checkunittype(lua_State* L, int idx)
242 {
243 return **static_cast<const unit_type**>(luaL_checkudata(L, idx, UnitType));
244 }
245