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/>.
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
;
34 this->FinalRelease(); // may throw, for example ScriptTest/ExecMode
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
)
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
));
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;
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 */
302 SetLastError(ScriptError::StringToError(res
.GetErrorMessage()));
306 /* No error, then clear it. */
307 SetLastError(ScriptError::ERR_NONE
);
309 /* Estimates, update the cost for the estimate and be done */
311 IncreaseDoCommandCosts(res
.GetCost());
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());
333 } else if (_networking
) {
334 /* Suspend the script till the command is really executed. */
335 throw Script_Suspend(-(int)GetDoCommandDelay(), callback
);
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
);
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());