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