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_team.hpp"
16 
17 #include "scripting/lua_common.hpp"
18 #include "team.hpp"
19 #include "resources.hpp" // for gameboard
20 #include "game_board.hpp"
21 
22 #include <string>
23 
24 #include "lua/lua.h"
25 #include "lua/lauxlib.h"
26 
27 /**
28  * Implementation for a lua reference to a team,
29  * used by the wesnoth in-game sides table.
30  *
31  * (The userdata has type team** because lua holds
32  * only a pointer to a team, not a full-size team.
33  * If it were a full object then we would cast to
34  * type team *, since checkudata returns a pointer
35  * to the type corresponding to the sizeof expr
36  * used when we allocated the userdata.)
37  */
38 
39 // Registry key
40 static const char * Team = "side";
41 
42 /**
43  * Gets some data on a side (__index metamethod).
44  * - Arg 1: full userdata containing the team.
45  * - Arg 2: string containing the name of the property.
46  * - Ret 1: something containing the attribute.
47  */
impl_side_get(lua_State * L)48 static int impl_side_get(lua_State *L)
49 {
50 	// Hidden metamethod, so arg1 has to be a pointer to a team.
51 	team &t = luaW_checkteam(L, 1);
52 	char const *m = luaL_checkstring(L, 2);
53 
54 	// Find the corresponding attribute.
55 	return_int_attrib("side", t.side());
56 	return_string_attrib("save_id", t.save_id());
57 	return_int_attrib("gold", t.gold());
58 	return_tstring_attrib("objectives", t.objectives());
59 	return_int_attrib("village_gold", t.village_gold());
60 	return_int_attrib("village_support", t.village_support());
61 	return_int_attrib("num_villages", t.villages().size());
62 	return_int_attrib("recall_cost", t.recall_cost());
63 	return_int_attrib("base_income", t.base_income());
64 	return_int_attrib("total_income", t.total_income());
65 	return_bool_attrib("objectives_changed", t.objectives_changed());
66 	return_bool_attrib("fog", t.uses_fog());
67 	return_bool_attrib("shroud", t.uses_shroud());
68 	return_bool_attrib("hidden", t.hidden());
69 	return_bool_attrib("scroll_to_leader", t.get_scroll_to_leader());
70 	return_string_attrib("flag", t.flag().empty() ? game_config::images::flag : t.flag());
71 	return_string_attrib("flag_icon", t.flag_icon().empty() ? game_config::images::flag_icon : t.flag_icon());
72 	return_tstring_attrib("user_team_name", t.user_team_name());
73 	return_string_attrib("team_name", t.team_name());
74 	return_string_attrib("faction", t.faction());
75 	return_tstring_attrib("faction_name", t.faction_name());
76 	return_string_attrib("color", t.color());
77 	return_cstring_attrib("controller", t.controller().to_string().c_str());
78 	return_bool_attrib("is_local", t.is_local());
79 	return_string_attrib("defeat_condition", t.defeat_condition().to_string());
80 	return_string_attrib("share_vision", t.share_vision().to_string());
81 	return_float_attrib("carryover_bonus", t.carryover_bonus());
82 	return_int_attrib("carryover_percentage", t.carryover_percentage());
83 	return_bool_attrib("carryover_add", t.carryover_add());
84 	return_bool_attrib("lost", t.lost());
85 	return_bool_attrib("persistent", t.persistent());
86 	return_bool_attrib("suppress_end_turn_confirmation", t.no_turn_confirmation());
87 	return_string_attrib("share_vision", t.share_vision().to_string());
88 	return_bool_attrib("share_maps", t.share_maps());
89 	return_bool_attrib("share_view", t.share_view());
90 	return_bool_attrib("chose_random", t.chose_random());
91 	return_tstring_attrib("side_name", t.side_name_tstr());
92 
93 	if (strcmp(m, "recruit") == 0) {
94 		const std::set<std::string>& recruits = t.recruits();
95 		lua_createtable(L, recruits.size(), 0);
96 		int i = 1;
97 		for (const std::string& r : t.recruits()) {
98 			lua_pushstring(L, r.c_str());
99 			lua_rawseti(L, -2, i++);
100 		}
101 		return 1;
102 	}
103 
104 	// These are blocked together because they are all part of the team_data struct.
105 	// Some of these values involve iterating over the units map to calculate them.
106 	auto d = [&](){ return team_data(*resources::gameboard, t); };
107 	return_int_attrib("num_units", d().units);
108 	return_int_attrib("total_upkeep", d().upkeep);
109 	return_int_attrib("expenses", d().expenses);
110 	return_int_attrib("net_income", d().net_income);
111 
112 	return_cfg_attrib("__cfg", t.write(cfg));
113 	if(luaW_getmetafield(L, 1, m)) {
114 		return 1;
115 	}
116 	return 0;
117 }
118 
119 /**
120  * Sets some data on a side (__newindex metamethod).
121  * - Arg 1: full userdata containing the team.
122  * - Arg 2: string containing the name of the property.
123  * - Arg 3: something containing the attribute.
124  */
impl_side_set(lua_State * L)125 static int impl_side_set(lua_State *L)
126 {
127 	// Hidden metamethod, so arg1 has to be a pointer to a team.
128 	team &t = luaW_checkteam(L, 1);
129 	char const *m = luaL_checkstring(L, 2);
130 
131 	// Find the corresponding attribute.
132 	modify_int_attrib("gold", t.set_gold(value));
133 	modify_tstring_attrib("objectives", t.set_objectives(value, true));
134 	//maybe add a setter for save_id too?
135 	modify_int_attrib("village_gold", t.set_village_gold(value));
136 	modify_int_attrib("village_support", t.set_village_support(value));
137 	modify_int_attrib("recall_cost", t.set_recall_cost(value));
138 	modify_int_attrib("base_income", t.set_base_income(value));
139 	modify_bool_attrib("objectives_changed", t.set_objectives_changed(value));
140 	modify_bool_attrib("hidden", t.set_hidden(value));
141 	modify_bool_attrib("scroll_to_leader", t.set_scroll_to_leader(value));
142 	modify_tstring_attrib("user_team_name", t.change_team(t.team_name(), value));
143 	modify_string_attrib("team_name", t.change_team(value, t.user_team_name()));
144 	modify_string_attrib("controller", t.change_controller_by_wml(value));
145 	modify_string_attrib("color", t.set_color(value));
146 	modify_string_attrib("defeat_condition", t.set_defeat_condition_string(value));
147 	modify_int_attrib("carryover_percentage", t.set_carryover_percentage(value));
148 	modify_bool_attrib("carryover_add", t.set_carryover_add(value));
149 	modify_bool_attrib("lost", t.set_lost(value));
150 	modify_bool_attrib("persistent", t.set_persistent(value));
151 	modify_bool_attrib("suppress_end_turn_confirmation", t.set_no_turn_confirmation(value));
152 	modify_bool_attrib("shroud", t.set_shroud(value));
153 	modify_bool_attrib("fog", t.set_fog(value));
154 	modify_string_attrib("flag_icon", t.set_flag_icon(value));
155 	modify_tstring_attrib("side_name", t.set_side_name(value));
156 	modify_string_attrib("share_vision", {
157 		team::SHARE_VISION v;
158 		if(v.parse(value)) {
159 			t.set_share_vision(v);
160 		} else {
161 			return luaL_argerror(L, 3, "Invalid share_vision value (should be 'all', 'none', or 'shroud')");
162 		}
163 	});
164 
165 	if (strcmp(m, "carryover_bonus") == 0) {
166 		t.set_carryover_bonus(luaL_checknumber(L, 3));
167 		return 0;
168 	}
169 
170 	if (strcmp(m, "recruit") == 0) {
171 		t.set_recruits(std::set<std::string>());
172 		if (!lua_istable(L, 3)) return 0;
173 		for (int i = 1;; ++i) {
174 			lua_rawgeti(L, 3, i);
175 			if (lua_isnil(L, -1)) break;
176 			t.add_recruit(lua_tostring(L, -1));
177 			lua_pop(L, 1);
178 		}
179 		return 0;
180 	}
181 
182 	std::string err_msg = "unknown modifiable property of side: ";
183 	err_msg += m;
184 	return luaL_argerror(L, 2, err_msg.c_str());
185 }
186 
impl_side_equal(lua_State * L)187 static int impl_side_equal(lua_State *L)
188 {
189 	// Hidden metamethod, so arg1 has to be a pointer to a team.
190 	team &t1 = luaW_checkteam(L, 1);
191 	if(team* t2 = luaW_toteam(L, 2)) {
192 		lua_pushboolean(L, t1.side() == t2->side());
193 	} else {
194 		lua_pushboolean(L, false);
195 	}
196 	return 1;
197 }
198 
199 namespace lua_team {
200 
register_metatable(lua_State * L)201 	std::string register_metatable(lua_State * L)
202 	{
203 		luaL_newmetatable(L, Team);
204 
205 		static luaL_Reg const callbacks[] {
206 			{ "__index", 	    &impl_side_get},
207 			{ "__newindex",	    &impl_side_set},
208 			{ "__eq",	        &impl_side_equal},
209 			{ nullptr, nullptr }
210 		};
211 		luaL_setfuncs(L, callbacks, 0);
212 
213 		lua_pushstring(L, Team);
214 		lua_setfield(L, -2, "__metatable");
215 		// Side methods
216 		luaW_getglobal(L, "wesnoth", "match_side");
217 		lua_setfield(L, -2, "matches");
218 
219 		return "Adding getside metatable...\n";
220 	}
221 }
222 
luaW_pushteam(lua_State * L,team & tm)223 void luaW_pushteam(lua_State *L, team & tm)
224 {
225 	team** t = static_cast<team**>(lua_newuserdata(L, sizeof(team*)));
226 	*t = &tm;
227 	luaL_setmetatable(L, Team);
228 }
229 
luaW_checkteam(lua_State * L,int idx)230 team& luaW_checkteam(lua_State* L, int idx)
231 {
232 	return **static_cast<team **>(luaL_checkudata(L, idx, Team));
233 }
234 
luaW_toteam(lua_State * L,int idx)235 team* luaW_toteam(lua_State* L, int idx)
236 {
237 	if(void* p = luaL_testudata(L, idx, Team)) {
238 		return *static_cast<team **>(p);
239 	}
240 	return nullptr;
241 }
242