Codefix: [NewGRF] Don't read an extended byte into uint8_t. (#13302)
[openttd-github.git] / src / ai / ai_info.cpp
blobfccfd94871341f2edcf63fd11eeff2643302de3c
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 ai_info.cpp Implementation of AIInfo and AILibrary */
10 #include "../stdafx.h"
12 #include "../script/squirrel_class.hpp"
13 #include "ai_info.hpp"
14 #include "ai_scanner.hpp"
15 #include "../debug.h"
16 #include "../string_func.h"
17 #include "../rev.h"
19 #include "../safeguards.h"
21 /**
22 * Check if the API version provided by the AI is supported.
23 * @param api_version The API version as provided by the AI.
25 static bool CheckAPIVersion(const std::string &api_version)
27 static const std::set<std::string> versions = { "0.7", "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "12", "13", "14", "15" };
28 return versions.find(api_version) != versions.end();
31 #if defined(_WIN32)
32 #undef GetClassName
33 #endif /* _WIN32 */
34 template <> const char *GetClassName<AIInfo, ScriptType::AI>() { return "AIInfo"; }
36 /* static */ void AIInfo::RegisterAPI(Squirrel *engine)
38 /* Create the AIInfo class, and add the RegisterAI function */
39 DefSQClass<AIInfo, ScriptType::AI> SQAIInfo("AIInfo");
40 SQAIInfo.PreRegister(engine);
41 SQAIInfo.AddConstructor<void (AIInfo::*)(), 1>(engine, "x");
42 SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddSetting, "AddSetting");
43 SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddLabels, "AddLabels");
44 SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "CONFIG_NONE");
45 SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "CONFIG_RANDOM"); // Deprecated, mapped to NONE.
46 SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_BOOLEAN, "CONFIG_BOOLEAN");
47 SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_INGAME, "CONFIG_INGAME");
48 SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_DEVELOPER, "CONFIG_DEVELOPER");
50 /* Pre 1.2 had an AI prefix */
51 SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "AICONFIG_NONE");
52 SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "AICONFIG_RANDOM"); // Deprecated, mapped to NONE.
53 SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_BOOLEAN, "AICONFIG_BOOLEAN");
54 SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_INGAME, "AICONFIG_INGAME");
56 SQAIInfo.PostRegister(engine);
57 engine->AddMethod("RegisterAI", &AIInfo::Constructor, 2, "tx");
58 engine->AddMethod("RegisterDummyAI", &AIInfo::DummyConstructor, 2, "tx");
61 /* static */ SQInteger AIInfo::Constructor(HSQUIRRELVM vm)
63 /* Get the AIInfo */
64 SQUserPointer instance = nullptr;
65 if (SQ_FAILED(sq_getinstanceup(vm, 2, &instance, nullptr)) || instance == nullptr) return sq_throwerror(vm, "Pass an instance of a child class of AIInfo to RegisterAI");
66 AIInfo *info = (AIInfo *)instance;
68 SQInteger res = ScriptInfo::Constructor(vm, info);
69 if (res != 0) return res;
71 if (info->engine->MethodExists(info->SQ_instance, "MinVersionToLoad")) {
72 if (!info->engine->CallIntegerMethod(info->SQ_instance, "MinVersionToLoad", &info->min_loadable_version, MAX_GET_OPS)) return SQ_ERROR;
73 } else {
74 info->min_loadable_version = info->GetVersion();
76 /* When there is an UseAsRandomAI function, call it. */
77 if (info->engine->MethodExists(info->SQ_instance, "UseAsRandomAI")) {
78 if (!info->engine->CallBoolMethod(info->SQ_instance, "UseAsRandomAI", &info->use_as_random, MAX_GET_OPS)) return SQ_ERROR;
79 } else {
80 info->use_as_random = true;
82 /* Try to get the API version the AI is written for. */
83 if (info->engine->MethodExists(info->SQ_instance, "GetAPIVersion")) {
84 if (!info->engine->CallStringMethod(info->SQ_instance, "GetAPIVersion", &info->api_version, MAX_GET_OPS)) return SQ_ERROR;
85 if (!CheckAPIVersion(info->api_version)) {
86 Debug(script, 1, "Loading info.nut from ({}.{}): GetAPIVersion returned invalid version", info->GetName(), info->GetVersion());
87 return SQ_ERROR;
89 } else {
90 info->api_version = "0.7";
93 /* Remove the link to the real instance, else it might get deleted by RegisterAI() */
94 sq_setinstanceup(vm, 2, nullptr);
95 /* Register the AI to the base system */
96 info->GetScanner()->RegisterScript(info);
97 return 0;
100 /* static */ SQInteger AIInfo::DummyConstructor(HSQUIRRELVM vm)
102 /* Get the AIInfo */
103 SQUserPointer instance;
104 sq_getinstanceup(vm, 2, &instance, nullptr);
105 AIInfo *info = (AIInfo *)instance;
106 info->api_version = fmt::format("{}.{}", GB(_openttd_newgrf_version, 28, 4), GB(_openttd_newgrf_version, 24, 4));
108 SQInteger res = ScriptInfo::Constructor(vm, info);
109 if (res != 0) return res;
111 /* Remove the link to the real instance, else it might get deleted by RegisterAI() */
112 sq_setinstanceup(vm, 2, nullptr);
113 /* Register the AI to the base system */
114 static_cast<AIScannerInfo *>(info->GetScanner())->SetDummyAI(info);
115 return 0;
118 AIInfo::AIInfo() :
119 min_loadable_version(0),
120 use_as_random(false)
124 bool AIInfo::CanLoadFromVersion(int version) const
126 if (version == -1) return true;
127 return version >= this->min_loadable_version && version <= this->GetVersion();
131 /* static */ void AILibrary::RegisterAPI(Squirrel *engine)
133 /* Create the AILibrary class, and add the RegisterLibrary function */
134 engine->AddClassBegin("AILibrary");
135 engine->AddClassEnd();
136 engine->AddMethod("RegisterLibrary", &AILibrary::Constructor, 2, "tx");
139 /* static */ SQInteger AILibrary::Constructor(HSQUIRRELVM vm)
141 /* Create a new library */
142 AILibrary *library = new AILibrary();
144 SQInteger res = ScriptInfo::Constructor(vm, library);
145 if (res != 0) {
146 delete library;
147 return res;
150 /* Cache the category */
151 if (!library->CheckMethod("GetCategory") || !library->engine->CallStringMethod(library->SQ_instance, "GetCategory", &library->category, MAX_GET_OPS)) {
152 delete library;
153 return SQ_ERROR;
156 /* Register the Library to the base system */
157 library->GetScanner()->RegisterScript(library);
159 return 0;