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"
29 * Get the storage associated with the current ScriptInstance.
30 * @return The storage.
32 static ScriptStorage
*GetStorage()
34 return ScriptObject::GetActiveInstance()->GetStorage();
38 /* static */ ScriptInstance
*ScriptObject::ActiveInstance::active
= nullptr;
40 ScriptObject::ActiveInstance::ActiveInstance(ScriptInstance
*instance
) : alc_scope(instance
->engine
)
42 this->last_active
= ScriptObject::ActiveInstance::active
;
43 ScriptObject::ActiveInstance::active
= instance
;
46 ScriptObject::ActiveInstance::~ActiveInstance()
48 ScriptObject::ActiveInstance::active
= this->last_active
;
51 /* static */ ScriptInstance
*ScriptObject::GetActiveInstance()
53 assert(ScriptObject::ActiveInstance::active
!= nullptr);
54 return ScriptObject::ActiveInstance::active
;
58 /* static */ void ScriptObject::SetDoCommandDelay(uint ticks
)
61 GetStorage()->delay
= ticks
;
64 /* static */ uint
ScriptObject::GetDoCommandDelay()
66 return GetStorage()->delay
;
69 /* static */ void ScriptObject::SetDoCommandMode(ScriptModeProc
*proc
, ScriptObject
*instance
)
71 GetStorage()->mode
= proc
;
72 GetStorage()->mode_instance
= instance
;
75 /* static */ ScriptModeProc
*ScriptObject::GetDoCommandMode()
77 return GetStorage()->mode
;
80 /* static */ ScriptObject
*ScriptObject::GetDoCommandModeInstance()
82 return GetStorage()->mode_instance
;
85 /* static */ void ScriptObject::SetDoCommandAsyncMode(ScriptAsyncModeProc
*proc
, ScriptObject
*instance
)
87 GetStorage()->async_mode
= proc
;
88 GetStorage()->async_mode_instance
= instance
;
91 /* static */ ScriptAsyncModeProc
*ScriptObject::GetDoCommandAsyncMode()
93 return GetStorage()->async_mode
;
96 /* static */ ScriptObject
*ScriptObject::GetDoCommandAsyncModeInstance()
98 return GetStorage()->async_mode_instance
;
101 /* static */ void ScriptObject::SetLastCommand(const CommandDataBuffer
&data
, Commands cmd
)
103 ScriptStorage
*s
= GetStorage();
104 Debug(script
, 6, "SetLastCommand company={:02d} cmd={} data={}", s
->root_company
, cmd
, FormatArrayAsHex(data
));
109 /* static */ bool ScriptObject::CheckLastCommand(const CommandDataBuffer
&data
, Commands cmd
)
111 ScriptStorage
*s
= GetStorage();
112 Debug(script
, 6, "CheckLastCommand company={:02d} cmd={} data={}", s
->root_company
, cmd
, FormatArrayAsHex(data
));
113 if (s
->last_cmd
!= cmd
) return false;
114 if (s
->last_data
!= data
) return false;
118 /* static */ void ScriptObject::SetDoCommandCosts(Money value
)
120 GetStorage()->costs
= CommandCost(INVALID_EXPENSES
, value
); // Expense type is never read.
123 /* static */ void ScriptObject::IncreaseDoCommandCosts(Money value
)
125 GetStorage()->costs
.AddCost(value
);
128 /* static */ Money
ScriptObject::GetDoCommandCosts()
130 return GetStorage()->costs
.GetCost();
133 /* static */ void ScriptObject::SetLastError(ScriptErrorType last_error
)
135 GetStorage()->last_error
= last_error
;
138 /* static */ ScriptErrorType
ScriptObject::GetLastError()
140 return GetStorage()->last_error
;
143 /* static */ void ScriptObject::SetLastCost(Money last_cost
)
145 GetStorage()->last_cost
= last_cost
;
148 /* static */ Money
ScriptObject::GetLastCost()
150 return GetStorage()->last_cost
;
153 /* static */ void ScriptObject::SetRoadType(RoadType road_type
)
155 GetStorage()->road_type
= road_type
;
158 /* static */ RoadType
ScriptObject::GetRoadType()
160 return GetStorage()->road_type
;
163 /* static */ void ScriptObject::SetRailType(RailType rail_type
)
165 GetStorage()->rail_type
= rail_type
;
168 /* static */ RailType
ScriptObject::GetRailType()
170 return GetStorage()->rail_type
;
173 /* static */ void ScriptObject::SetLastCommandRes(bool res
)
175 GetStorage()->last_command_res
= res
;
178 /* static */ bool ScriptObject::GetLastCommandRes()
180 return GetStorage()->last_command_res
;
183 /* static */ void ScriptObject::SetLastCommandResData(CommandDataBuffer data
)
185 GetStorage()->last_cmd_ret
= std::move(data
);
188 /* static */ const CommandDataBuffer
&ScriptObject::GetLastCommandResData()
190 return GetStorage()->last_cmd_ret
;
193 /* static */ void ScriptObject::SetAllowDoCommand(bool allow
)
195 GetStorage()->allow_do_command
= allow
;
198 /* static */ bool ScriptObject::GetAllowDoCommand()
200 return GetStorage()->allow_do_command
;
203 /* static */ void ScriptObject::SetCompany(CompanyID company
)
205 if (GetStorage()->root_company
== INVALID_OWNER
) GetStorage()->root_company
= company
;
206 GetStorage()->company
= company
;
208 _current_company
= company
;
211 /* static */ CompanyID
ScriptObject::GetCompany()
213 return GetStorage()->company
;
216 /* static */ CompanyID
ScriptObject::GetRootCompany()
218 return GetStorage()->root_company
;
221 /* static */ bool ScriptObject::CanSuspend()
223 Squirrel
*squirrel
= ScriptObject::GetActiveInstance()->engine
;
224 return GetStorage()->allow_do_command
&& squirrel
->CanSuspend();
227 /* static */ void *&ScriptObject::GetEventPointer()
229 return GetStorage()->event_data
;
232 /* static */ ScriptLogTypes::LogData
&ScriptObject::GetLogData()
234 return GetStorage()->log_data
;
237 /* static */ std::string
ScriptObject::GetString(StringID string
)
239 return ::StrMakeValid(::GetString(string
));
242 /* static */ void ScriptObject::SetCallbackVariable(int index
, int value
)
244 if (static_cast<size_t>(index
) >= GetStorage()->callback_value
.size()) GetStorage()->callback_value
.resize(index
+ 1);
245 GetStorage()->callback_value
[index
] = value
;
248 /* static */ int ScriptObject::GetCallbackVariable(int index
)
250 return GetStorage()->callback_value
[index
];
253 /* static */ CommandCallbackData
*ScriptObject::GetDoCommandCallback()
255 return ScriptObject::GetActiveInstance()->GetDoCommandCallback();
258 std::tuple
<bool, bool, bool, bool> ScriptObject::DoCommandPrep()
260 if (!ScriptObject::CanSuspend()) {
261 throw Script_FatalError("You are not allowed to execute any DoCommand (even indirect) in your constructor, Save(), Load(), and any valuator.");
264 /* Are we only interested in the estimate costs? */
265 bool estimate_only
= GetDoCommandMode() != nullptr && !GetDoCommandMode()();
267 /* Should the command be executed asynchronously? */
268 bool asynchronous
= GetDoCommandAsyncMode() != nullptr && GetDoCommandAsyncMode()();
270 bool networking
= _networking
&& !_generating_world
;
272 if (!ScriptCompanyMode::IsDeity() && !ScriptCompanyMode::IsValid()) {
273 ScriptObject::SetLastError(ScriptError::ERR_PRECONDITION_INVALID_COMPANY
);
274 return { true, estimate_only
, asynchronous
, networking
};
277 return { false, estimate_only
, asynchronous
, networking
};
280 bool ScriptObject::DoCommandProcessResult(const CommandCost
&res
, Script_SuspendCallbackProc
*callback
, bool estimate_only
, bool asynchronous
)
282 /* Set the default callback to return a true/false result of the DoCommand */
283 if (callback
== nullptr) callback
= &ScriptInstance::DoCommandReturn
;
285 /* We failed; set the error and bail out */
287 SetLastError(ScriptError::StringToError(res
.GetErrorMessage()));
291 /* No error, then clear it. */
292 SetLastError(ScriptError::ERR_NONE
);
294 /* Estimates, update the cost for the estimate and be done */
296 IncreaseDoCommandCosts(res
.GetCost());
300 /* Costs of this operation. */
301 SetLastCost(res
.GetCost());
302 SetLastCommandRes(true);
304 if (_generating_world
|| asynchronous
) {
305 IncreaseDoCommandCosts(res
.GetCost());
306 if (!_generating_world
) {
307 /* Charge a nominal fee for asynchronously executed commands */
308 Squirrel
*engine
= ScriptObject::GetActiveInstance()->engine
;
309 Squirrel::DecreaseOps(engine
->GetVM(), 100);
311 if (callback
!= nullptr) {
312 /* Insert return value into to stack and throw a control code that
313 * the return value in the stack should be used. */
314 callback(GetActiveInstance());
318 } else if (_networking
) {
319 /* Suspend the script till the command is really executed. */
320 throw Script_Suspend(-(int)GetDoCommandDelay(), callback
);
322 IncreaseDoCommandCosts(res
.GetCost());
324 /* Suspend the script player for 1+ ticks, so it simulates multiplayer. This
325 * both avoids confusion when a developer launched the script in a
326 * multiplayer game, but also gives time for the GUI and human player
327 * to interact with the game. */
328 throw Script_Suspend(GetDoCommandDelay(), callback
);
335 /* static */ Randomizer
ScriptObject::random_states
[OWNER_END
];
337 Randomizer
&ScriptObject::GetRandomizer(Owner owner
)
339 return ScriptObject::random_states
[owner
];
342 void ScriptObject::InitializeRandomizers()
344 Randomizer random
= _random
;
345 for (Owner owner
= OWNER_BEGIN
; owner
< OWNER_END
; owner
++) {
346 ScriptObject::GetRandomizer(owner
).SetSeed(random
.Next());