Update: Translations from eints
[openttd-github.git] / src / script / api / script_object.cpp
blobcc94b18ff047a54c9a5c0fd05fb757709b107e86
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 */
8 /** @file script_object.cpp Implementation of ScriptObject. */
10 #include "../../stdafx.h"
11 #include "../../script/squirrel.hpp"
12 #include "../../company_func.h"
13 #include "../../company_base.h"
14 #include "../../network/network.h"
15 #include "../../genworld.h"
16 #include "../../string_func.h"
17 #include "../../strings_func.h"
18 #include "../../command_func.h"
20 #include "../script_storage.hpp"
21 #include "../script_instance.hpp"
22 #include "../script_fatalerror.hpp"
23 #include "script_error.hpp"
24 #include "../../debug.h"
26 #include "../../safeguards.h"
28 void SimpleCountedObject::Release()
30 int32_t res = --this->ref_count;
31 assert(res >= 0);
32 if (res == 0) {
33 try {
34 this->FinalRelease(); // may throw, for example ScriptTest/ExecMode
35 } catch (...) {
36 delete this;
37 throw;
39 delete this;
43 /**
44 * Get the storage associated with the current ScriptInstance.
45 * @return The storage.
47 static ScriptStorage *GetStorage()
49 return ScriptObject::GetActiveInstance()->GetStorage();
53 /* static */ ScriptInstance *ScriptObject::ActiveInstance::active = nullptr;
55 ScriptObject::ActiveInstance::ActiveInstance(ScriptInstance *instance) : alc_scope(instance->engine)
57 this->last_active = ScriptObject::ActiveInstance::active;
58 ScriptObject::ActiveInstance::active = instance;
61 ScriptObject::ActiveInstance::~ActiveInstance()
63 ScriptObject::ActiveInstance::active = this->last_active;
66 /* static */ ScriptInstance *ScriptObject::GetActiveInstance()
68 assert(ScriptObject::ActiveInstance::active != nullptr);
69 return ScriptObject::ActiveInstance::active;
73 /* static */ void ScriptObject::SetDoCommandDelay(uint ticks)
75 assert(ticks > 0);
76 GetStorage()->delay = ticks;
79 /* static */ uint ScriptObject::GetDoCommandDelay()
81 return GetStorage()->delay;
84 /* static */ void ScriptObject::SetDoCommandMode(ScriptModeProc *proc, ScriptObject *instance)
86 GetStorage()->mode = proc;
87 GetStorage()->mode_instance = instance;
90 /* static */ ScriptModeProc *ScriptObject::GetDoCommandMode()
92 return GetStorage()->mode;
95 /* static */ ScriptObject *ScriptObject::GetDoCommandModeInstance()
97 return GetStorage()->mode_instance;
100 /* static */ void ScriptObject::SetDoCommandAsyncMode(ScriptAsyncModeProc *proc, ScriptObject *instance)
102 GetStorage()->async_mode = proc;
103 GetStorage()->async_mode_instance = instance;
106 /* static */ ScriptAsyncModeProc *ScriptObject::GetDoCommandAsyncMode()
108 return GetStorage()->async_mode;
111 /* static */ ScriptObject *ScriptObject::GetDoCommandAsyncModeInstance()
113 return GetStorage()->async_mode_instance;
116 /* static */ void ScriptObject::SetLastCommand(const CommandDataBuffer &data, Commands cmd)
118 ScriptStorage *s = GetStorage();
119 Debug(script, 6, "SetLastCommand company={:02d} cmd={} data={}", s->root_company, cmd, FormatArrayAsHex(data));
120 s->last_data = data;
121 s->last_cmd = cmd;
124 /* static */ bool ScriptObject::CheckLastCommand(const CommandDataBuffer &data, Commands cmd)
126 ScriptStorage *s = GetStorage();
127 Debug(script, 6, "CheckLastCommand company={:02d} cmd={} data={}", s->root_company, cmd, FormatArrayAsHex(data));
128 if (s->last_cmd != cmd) return false;
129 if (s->last_data != data) return false;
130 return true;
133 /* static */ void ScriptObject::SetDoCommandCosts(Money value)
135 GetStorage()->costs = CommandCost(INVALID_EXPENSES, value); // Expense type is never read.
138 /* static */ void ScriptObject::IncreaseDoCommandCosts(Money value)
140 GetStorage()->costs.AddCost(value);
143 /* static */ Money ScriptObject::GetDoCommandCosts()
145 return GetStorage()->costs.GetCost();
148 /* static */ void ScriptObject::SetLastError(ScriptErrorType last_error)
150 GetStorage()->last_error = last_error;
153 /* static */ ScriptErrorType ScriptObject::GetLastError()
155 return GetStorage()->last_error;
158 /* static */ void ScriptObject::SetLastCost(Money last_cost)
160 GetStorage()->last_cost = last_cost;
163 /* static */ Money ScriptObject::GetLastCost()
165 return GetStorage()->last_cost;
168 /* static */ void ScriptObject::SetRoadType(RoadType road_type)
170 GetStorage()->road_type = road_type;
173 /* static */ RoadType ScriptObject::GetRoadType()
175 return GetStorage()->road_type;
178 /* static */ void ScriptObject::SetRailType(RailType rail_type)
180 GetStorage()->rail_type = rail_type;
183 /* static */ RailType ScriptObject::GetRailType()
185 return GetStorage()->rail_type;
188 /* static */ void ScriptObject::SetLastCommandRes(bool res)
190 GetStorage()->last_command_res = res;
193 /* static */ bool ScriptObject::GetLastCommandRes()
195 return GetStorage()->last_command_res;
198 /* static */ void ScriptObject::SetLastCommandResData(CommandDataBuffer data)
200 GetStorage()->last_cmd_ret = std::move(data);
203 /* static */ const CommandDataBuffer &ScriptObject::GetLastCommandResData()
205 return GetStorage()->last_cmd_ret;
208 /* static */ void ScriptObject::SetAllowDoCommand(bool allow)
210 GetStorage()->allow_do_command = allow;
213 /* static */ bool ScriptObject::GetAllowDoCommand()
215 return GetStorage()->allow_do_command;
218 /* static */ void ScriptObject::SetCompany(CompanyID company)
220 if (GetStorage()->root_company == INVALID_OWNER) GetStorage()->root_company = company;
221 GetStorage()->company = company;
223 _current_company = company;
226 /* static */ CompanyID ScriptObject::GetCompany()
228 return GetStorage()->company;
231 /* static */ CompanyID ScriptObject::GetRootCompany()
233 return GetStorage()->root_company;
236 /* static */ bool ScriptObject::CanSuspend()
238 Squirrel *squirrel = ScriptObject::GetActiveInstance()->engine;
239 return GetStorage()->allow_do_command && squirrel->CanSuspend();
242 /* static */ void *&ScriptObject::GetEventPointer()
244 return GetStorage()->event_data;
247 /* static */ ScriptLogTypes::LogData &ScriptObject::GetLogData()
249 return GetStorage()->log_data;
252 /* static */ std::string ScriptObject::GetString(StringID string)
254 return ::StrMakeValid(::GetString(string));
257 /* static */ void ScriptObject::SetCallbackVariable(int index, int value)
259 if (static_cast<size_t>(index) >= GetStorage()->callback_value.size()) GetStorage()->callback_value.resize(index + 1);
260 GetStorage()->callback_value[index] = value;
263 /* static */ int ScriptObject::GetCallbackVariable(int index)
265 return GetStorage()->callback_value[index];
268 /* static */ CommandCallbackData *ScriptObject::GetDoCommandCallback()
270 return ScriptObject::GetActiveInstance()->GetDoCommandCallback();
273 std::tuple<bool, bool, bool, bool> ScriptObject::DoCommandPrep()
275 if (!ScriptObject::CanSuspend()) {
276 throw Script_FatalError("You are not allowed to execute any DoCommand (even indirect) in your constructor, Save(), Load(), and any valuator.");
279 /* Are we only interested in the estimate costs? */
280 bool estimate_only = GetDoCommandMode() != nullptr && !GetDoCommandMode()();
282 /* Should the command be executed asynchronously? */
283 bool asynchronous = GetDoCommandAsyncMode() != nullptr && GetDoCommandAsyncMode()();
285 bool networking = _networking && !_generating_world;
287 if (!ScriptCompanyMode::IsDeity() && !ScriptCompanyMode::IsValid()) {
288 ScriptObject::SetLastError(ScriptError::ERR_PRECONDITION_INVALID_COMPANY);
289 return { true, estimate_only, asynchronous, networking };
292 return { false, estimate_only, asynchronous, networking };
295 bool ScriptObject::DoCommandProcessResult(const CommandCost &res, Script_SuspendCallbackProc *callback, bool estimate_only, bool asynchronous)
297 /* Set the default callback to return a true/false result of the DoCommand */
298 if (callback == nullptr) callback = &ScriptInstance::DoCommandReturn;
300 /* We failed; set the error and bail out */
301 if (res.Failed()) {
302 SetLastError(ScriptError::StringToError(res.GetErrorMessage()));
303 return false;
306 /* No error, then clear it. */
307 SetLastError(ScriptError::ERR_NONE);
309 /* Estimates, update the cost for the estimate and be done */
310 if (estimate_only) {
311 IncreaseDoCommandCosts(res.GetCost());
312 return true;
315 /* Costs of this operation. */
316 SetLastCost(res.GetCost());
317 SetLastCommandRes(true);
319 if (_generating_world || asynchronous) {
320 IncreaseDoCommandCosts(res.GetCost());
321 if (!_generating_world) {
322 /* Charge a nominal fee for asynchronously executed commands */
323 Squirrel *engine = ScriptObject::GetActiveInstance()->engine;
324 Squirrel::DecreaseOps(engine->GetVM(), 100);
326 if (callback != nullptr) {
327 /* Insert return value into to stack and throw a control code that
328 * the return value in the stack should be used. */
329 callback(GetActiveInstance());
330 throw SQInteger(1);
332 return true;
333 } else if (_networking) {
334 /* Suspend the script till the command is really executed. */
335 throw Script_Suspend(-(int)GetDoCommandDelay(), callback);
336 } else {
337 IncreaseDoCommandCosts(res.GetCost());
339 /* Suspend the script player for 1+ ticks, so it simulates multiplayer. This
340 * both avoids confusion when a developer launched the script in a
341 * multiplayer game, but also gives time for the GUI and human player
342 * to interact with the game. */
343 throw Script_Suspend(GetDoCommandDelay(), callback);
346 NOT_REACHED();
350 /* static */ Randomizer ScriptObject::random_states[OWNER_END];
352 Randomizer &ScriptObject::GetRandomizer(Owner owner)
354 return ScriptObject::random_states[owner];
357 void ScriptObject::InitializeRandomizers()
359 Randomizer random = _random;
360 for (Owner owner = OWNER_BEGIN; owner < OWNER_END; owner++) {
361 ScriptObject::GetRandomizer(owner).SetSeed(random.Next());