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_controller.cpp Implementation of ScriptControler. */
10 #include "../../stdafx.h"
11 #include "../../string_func.h"
12 #include "../../script/squirrel.hpp"
13 #include "../../rev.h"
15 #include "script_controller.hpp"
16 #include "script_error.hpp"
17 #include "../script_fatalerror.hpp"
18 #include "../script_info.hpp"
19 #include "../script_instance.hpp"
20 #include "script_log.hpp"
21 #include "../script_gui.h"
22 #include "../../settings_type.h"
23 #include "../../network/network.h"
24 #include "../../misc_cmd.h"
26 #include "../../safeguards.h"
28 /* static */ void ScriptController::SetCommandDelay(int ticks
)
30 if (ticks
<= 0) return;
31 ScriptObject::SetDoCommandDelay(ticks
);
34 /* static */ void ScriptController::Sleep(int ticks
)
36 if (!ScriptObject::CanSuspend()) {
37 throw Script_FatalError("You are not allowed to call Sleep in your constructor, Save(), Load(), and any valuator.");
41 ScriptLog::Warning("Sleep() value should be > 0. Assuming value 1.");
45 throw Script_Suspend(ticks
, nullptr);
48 /* static */ void ScriptController::Break(const std::string
&message
)
50 if (_network_dedicated
|| !_settings_client
.gui
.ai_developer_tools
) return;
52 ScriptObject::GetActiveInstance()->Pause();
54 ScriptLog::Log(ScriptLogTypes::LOG_SQ_ERROR
, fmt::format("Break: {}", message
));
56 /* Inform script developer that their script has been paused and
57 * needs manual action to continue. */
58 ShowScriptDebugWindow(ScriptObject::GetRootCompany());
60 if ((_pause_mode
& PM_PAUSED_NORMAL
) == PM_UNPAUSED
) {
61 ScriptObject::Command
<CMD_PAUSE
>::Do(PM_PAUSED_NORMAL
, true);
65 /* static */ void ScriptController::Print(bool error_msg
, const std::string
&message
)
67 ScriptLog::Log(error_msg
? ScriptLogTypes::LOG_SQ_ERROR
: ScriptLogTypes::LOG_SQ_INFO
, message
);
70 ScriptController::ScriptController(CompanyID company
) :
72 loaded_library_count(0)
74 ScriptObject::SetCompany(company
);
77 /* static */ uint
ScriptController::GetTick()
79 return ScriptObject::GetActiveInstance()->GetController()->ticks
;
82 /* static */ int ScriptController::GetOpsTillSuspend()
84 return ScriptObject::GetActiveInstance()->GetOpsTillSuspend();
87 /* static */ int ScriptController::GetSetting(const std::string
&name
)
89 return ScriptObject::GetActiveInstance()->GetSetting(name
);
92 /* static */ uint
ScriptController::GetVersion()
94 return _openttd_newgrf_version
;
97 /* static */ HSQOBJECT
ScriptController::Import(const std::string
&library
, const std::string
&class_name
, int version
)
99 ScriptController
*controller
= ScriptObject::GetActiveInstance()->GetController();
100 Squirrel
*engine
= ScriptObject::GetActiveInstance()->engine
;
101 HSQUIRRELVM vm
= engine
->GetVM();
103 ScriptInfo
*lib
= ScriptObject::GetActiveInstance()->FindLibrary(library
, version
);
104 if (lib
== nullptr) {
105 throw sq_throwerror(vm
, fmt::format("couldn't find library '{}' with version {}", library
, version
));
108 /* Internally we store libraries as 'library.version' */
109 std::string library_name
= fmt::format("{}.{}", library
, version
);
111 /* Get the current table/class we belong to */
113 sq_getstackobj(vm
, 1, &parent
);
115 std::string fake_class
;
117 LoadedLibraryList::iterator it
= controller
->loaded_library
.find(library_name
);
118 if (it
!= controller
->loaded_library
.end()) {
119 fake_class
= (*it
).second
;
121 int next_number
= ++controller
->loaded_library_count
;
123 /* Create a new fake internal name */
124 fake_class
= fmt::format("_internalNA{}", next_number
);
126 /* Load the library in a 'fake' namespace, so we can link it to the name the user requested */
127 sq_pushroottable(vm
);
128 sq_pushstring(vm
, fake_class
, -1);
129 sq_newclass(vm
, SQFalse
);
130 /* Load the library */
131 if (!engine
->LoadScript(vm
, lib
->GetMainScript(), false)) {
132 throw sq_throwerror(vm
, fmt::format("there was a compile error when importing '{}' version {}", library
, version
));
134 /* Create the fake class */
135 sq_newslot(vm
, -3, SQFalse
);
138 controller
->loaded_library
[library_name
] = fake_class
;
141 /* Find the real class inside the fake class (like 'sets.Vector') */
142 sq_pushroottable(vm
);
143 sq_pushstring(vm
, fake_class
, -1);
144 if (SQ_FAILED(sq_get(vm
, -2))) {
145 throw sq_throwerror(vm
, "internal error assigning library class");
147 sq_pushstring(vm
, lib
->GetInstanceName(), -1);
148 if (SQ_FAILED(sq_get(vm
, -2))) {
149 throw sq_throwerror(vm
, fmt::format("unable to find class '{}' in the library '{}' version {}", lib
->GetInstanceName(), library
, version
));
152 sq_getstackobj(vm
, -1, &obj
);
155 if (class_name
.empty()) return obj
;
157 /* Now link the name the user wanted to our 'fake' class */
158 sq_pushobject(vm
, parent
);
159 sq_pushstring(vm
, class_name
, -1);
160 sq_pushobject(vm
, obj
);
161 sq_newclass(vm
, SQTrue
);
162 sq_newslot(vm
, -3, SQFalse
);