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