1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
8 /** @file script_event_types.cpp Implementation of all EventTypes. */
9 
10 #include "../../stdafx.h"
11 #include "script_event_types.hpp"
12 #include "script_vehicle.hpp"
13 #include "script_log.hpp"
14 #include "../../strings_func.h"
15 #include "../../settings_type.h"
16 #include "../../engine_base.h"
17 #include "../../articulated_vehicles.h"
18 #include "../../string_func.h"
19 #include "table/strings.h"
20 
21 #include "../../safeguards.h"
22 
IsEngineValid() const23 bool ScriptEventEnginePreview::IsEngineValid() const
24 {
25 	const Engine *e = ::Engine::GetIfValid(this->engine);
26 	return e != nullptr && e->IsEnabled();
27 }
28 
GetName()29 char *ScriptEventEnginePreview::GetName()
30 {
31 	if (!this->IsEngineValid()) return nullptr;
32 
33 	::SetDParam(0, this->engine);
34 	return GetString(STR_ENGINE_NAME);
35 }
36 
GetCargoType()37 CargoID ScriptEventEnginePreview::GetCargoType()
38 {
39 	if (!this->IsEngineValid()) return CT_INVALID;
40 	CargoArray cap = ::GetCapacityOfArticulatedParts(this->engine);
41 
42 	CargoID most_cargo = CT_INVALID;
43 	uint amount = 0;
44 	for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
45 		if (cap[cid] > amount) {
46 			amount = cap[cid];
47 			most_cargo = cid;
48 		}
49 	}
50 
51 	return most_cargo;
52 }
53 
GetCapacity()54 int32 ScriptEventEnginePreview::GetCapacity()
55 {
56 	if (!this->IsEngineValid()) return -1;
57 	const Engine *e = ::Engine::Get(this->engine);
58 	switch (e->type) {
59 		case VEH_ROAD:
60 		case VEH_TRAIN: {
61 			CargoArray capacities = GetCapacityOfArticulatedParts(this->engine);
62 			for (CargoID c = 0; c < NUM_CARGO; c++) {
63 				if (capacities[c] == 0) continue;
64 				return capacities[c];
65 			}
66 			return -1;
67 		}
68 
69 		case VEH_SHIP:
70 		case VEH_AIRCRAFT:
71 			return e->GetDisplayDefaultCapacity();
72 
73 		default: NOT_REACHED();
74 	}
75 }
76 
GetMaxSpeed()77 int32 ScriptEventEnginePreview::GetMaxSpeed()
78 {
79 	if (!this->IsEngineValid()) return -1;
80 	const Engine *e = ::Engine::Get(this->engine);
81 	int32 max_speed = e->GetDisplayMaxSpeed(); // km-ish/h
82 	if (e->type == VEH_AIRCRAFT) max_speed /= _settings_game.vehicle.plane_speed;
83 	return max_speed;
84 }
85 
GetPrice()86 Money ScriptEventEnginePreview::GetPrice()
87 {
88 	if (!this->IsEngineValid()) return -1;
89 	return ::Engine::Get(this->engine)->GetCost();
90 }
91 
GetRunningCost()92 Money ScriptEventEnginePreview::GetRunningCost()
93 {
94 	if (!this->IsEngineValid()) return -1;
95 	return ::Engine::Get(this->engine)->GetRunningCost();
96 }
97 
GetVehicleType()98 int32 ScriptEventEnginePreview::GetVehicleType()
99 {
100 	if (!this->IsEngineValid()) return ScriptVehicle::VT_INVALID;
101 	switch (::Engine::Get(this->engine)->type) {
102 		case VEH_ROAD:     return ScriptVehicle::VT_ROAD;
103 		case VEH_TRAIN:    return ScriptVehicle::VT_RAIL;
104 		case VEH_SHIP:     return ScriptVehicle::VT_WATER;
105 		case VEH_AIRCRAFT: return ScriptVehicle::VT_AIR;
106 		default: NOT_REACHED();
107 	}
108 }
109 
AcceptPreview()110 bool ScriptEventEnginePreview::AcceptPreview()
111 {
112 	if (!this->IsEngineValid()) return false;
113 	return ScriptObject::DoCommand(0, this->engine, 0, CMD_WANT_ENGINE_PREVIEW);
114 }
115 
AcceptMerger()116 bool ScriptEventCompanyAskMerger::AcceptMerger()
117 {
118 	return ScriptObject::DoCommand(0, this->owner, 0, CMD_BUY_COMPANY);
119 }
120 
ScriptEventAdminPort(const std::string & json)121 ScriptEventAdminPort::ScriptEventAdminPort(const std::string &json) :
122 		ScriptEvent(ET_ADMIN_PORT),
123 		json(json)
124 {
125 }
126 
127 #define SKIP_EMPTY(p) while (*(p) == ' ' || *(p) == '\n' || *(p) == '\r') (p)++;
128 #define RETURN_ERROR(stack) { ScriptLog::Error("Received invalid JSON data from AdminPort."); if (stack != 0) sq_pop(vm, stack); return nullptr; }
129 
GetObject(HSQUIRRELVM vm)130 SQInteger ScriptEventAdminPort::GetObject(HSQUIRRELVM vm)
131 {
132 	const char *p = this->json.c_str();
133 
134 	if (this->ReadTable(vm, p) == nullptr) {
135 		sq_pushnull(vm);
136 		return 1;
137 	}
138 
139 	return 1;
140 }
141 
ReadString(HSQUIRRELVM vm,const char * p)142 const char *ScriptEventAdminPort::ReadString(HSQUIRRELVM vm, const char *p)
143 {
144 	const char *value = p;
145 
146 	bool escape = false;
147 	for (;;) {
148 		if (*p == '\\') {
149 			escape = true;
150 			p++;
151 			continue;
152 		}
153 		if (*p == '"' && escape) {
154 			escape = false;
155 			p++;
156 			continue;
157 		}
158 		escape = false;
159 
160 		if (*p == '"') break;
161 		if (*p == '\0') RETURN_ERROR(0);
162 
163 		p++;
164 	}
165 
166 	size_t len = p - value;
167 	sq_pushstring(vm, value, len);
168 	p++; // Step past the end-of-string marker (")
169 
170 	return p;
171 }
172 
ReadTable(HSQUIRRELVM vm,const char * p)173 const char *ScriptEventAdminPort::ReadTable(HSQUIRRELVM vm, const char *p)
174 {
175 	sq_newtable(vm);
176 
177 	SKIP_EMPTY(p);
178 	if (*p++ != '{') RETURN_ERROR(1);
179 
180 	for (;;) {
181 		SKIP_EMPTY(p);
182 		if (*p++ != '"') RETURN_ERROR(1);
183 
184 		p = ReadString(vm, p);
185 		if (p == nullptr) {
186 			sq_pop(vm, 1);
187 			return nullptr;
188 		}
189 
190 		SKIP_EMPTY(p);
191 		if (*p++ != ':') RETURN_ERROR(2);
192 
193 		p = this->ReadValue(vm, p);
194 		if (p == nullptr) {
195 			sq_pop(vm, 2);
196 			return nullptr;
197 		}
198 
199 		sq_rawset(vm, -3);
200 		/* The key (-2) and value (-1) are popped from the stack by squirrel. */
201 
202 		SKIP_EMPTY(p);
203 		if (*p == ',') {
204 			p++;
205 			continue;
206 		}
207 		break;
208 	}
209 
210 	SKIP_EMPTY(p);
211 	if (*p++ != '}') RETURN_ERROR(1);
212 
213 	return p;
214 }
215 
ReadValue(HSQUIRRELVM vm,const char * p)216 const char *ScriptEventAdminPort::ReadValue(HSQUIRRELVM vm, const char *p)
217 {
218 	SKIP_EMPTY(p);
219 
220 	if (strncmp(p, "false", 5) == 0) {
221 		sq_pushinteger(vm, 0);
222 		return p + 5;
223 	}
224 	if (strncmp(p, "true", 4) == 0) {
225 		sq_pushinteger(vm, 1);
226 		return p + 4;
227 	}
228 	if (strncmp(p, "null", 4) == 0) {
229 		sq_pushnull(vm);
230 		return p + 4;
231 	}
232 
233 	switch (*p) {
234 		case '"': {
235 			/* String */
236 			p = ReadString(vm, ++p);
237 			if (p == nullptr) return nullptr;
238 
239 			break;
240 		}
241 
242 		case '{': {
243 			/* Table */
244 			p = this->ReadTable(vm, p);
245 			if (p == nullptr) return nullptr;
246 
247 			break;
248 		}
249 
250 		case '[': {
251 			/* Array */
252 			sq_newarray(vm, 0);
253 
254 			/* Empty array? */
255 			const char *p2 = p + 1;
256 			SKIP_EMPTY(p2);
257 			if (*p2 == ']') {
258 				p = p2 + 1;
259 				break;
260 			}
261 
262 			while (*p++ != ']') {
263 				p = this->ReadValue(vm, p);
264 				if (p == nullptr) {
265 					sq_pop(vm, 1);
266 					return nullptr;
267 				}
268 				sq_arrayappend(vm, -2);
269 
270 				SKIP_EMPTY(p);
271 				if (*p == ',') continue;
272 				if (*p == ']') break;
273 				RETURN_ERROR(1);
274 			}
275 
276 			p++;
277 
278 			break;
279 		}
280 
281 		case '1': case '2': case '3': case '4': case '5':
282 		case '6': case '7': case '8': case '9': case '0':
283 		case '-': {
284 			/* Integer */
285 
286 			const char *value = p++;
287 			for (;;) {
288 				switch (*p++) {
289 					case '1': case '2': case '3': case '4': case '5':
290 					case '6': case '7': case '8': case '9': case '0':
291 						continue;
292 
293 					default:
294 						break;
295 				}
296 
297 				p--;
298 				break;
299 			}
300 
301 			int res = atoi(value);
302 			sq_pushinteger(vm, (SQInteger)res);
303 
304 			break;
305 		}
306 
307 		default:
308 			RETURN_ERROR(0);
309 	}
310 
311 	return p;
312 }
313 
314 #undef SKIP_EMPTY
315 #undef RETURN_ERROR
316