OpenTTD Source 20250522-master-g467f832c2f
ai_info.cpp
Go to the documentation of this file.
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 */
7
10#include "../stdafx.h"
11
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"
18
19#include "../safeguards.h"
20
25static bool CheckAPIVersion(const std::string &api_version)
26{
27 return std::ranges::find(AIInfo::ApiVersions, api_version) != std::end(AIInfo::ApiVersions);
28}
29
30template <> SQInteger PushClassName<AIInfo, ScriptType::AI>(HSQUIRRELVM vm) { sq_pushstring(vm, "AIInfo"); return 1; }
31
32/* static */ void AIInfo::RegisterAPI(Squirrel &engine)
33{
34 /* Create the AIInfo class, and add the RegisterAI function */
35 DefSQClass<AIInfo, ScriptType::AI> SQAIInfo("AIInfo");
36 SQAIInfo.PreRegister(engine);
37 SQAIInfo.AddConstructor<void (AIInfo::*)(), 1>(engine, "x");
38 SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddSetting, "AddSetting");
39 SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddLabels, "AddLabels");
40 SQAIInfo.DefSQConst(engine, ScriptConfigFlags{}.base(), "CONFIG_NONE");
41 SQAIInfo.DefSQConst(engine, ScriptConfigFlags{}.base(), "CONFIG_RANDOM"); // Deprecated, mapped to NONE.
42 SQAIInfo.DefSQConst(engine, ScriptConfigFlags{ScriptConfigFlag::Boolean}.base(), "CONFIG_BOOLEAN");
43 SQAIInfo.DefSQConst(engine, ScriptConfigFlags{ScriptConfigFlag::InGame}.base(), "CONFIG_INGAME");
44 SQAIInfo.DefSQConst(engine, ScriptConfigFlags{ScriptConfigFlag::Developer}.base(), "CONFIG_DEVELOPER");
45
46 /* Pre 1.2 had an AI prefix */
47 SQAIInfo.DefSQConst(engine, ScriptConfigFlags{}.base(), "AICONFIG_NONE");
48 SQAIInfo.DefSQConst(engine, ScriptConfigFlags{}.base(), "AICONFIG_RANDOM"); // Deprecated, mapped to NONE.
49 SQAIInfo.DefSQConst(engine, ScriptConfigFlags{ScriptConfigFlag::Boolean}.base(), "AICONFIG_BOOLEAN");
50 SQAIInfo.DefSQConst(engine, ScriptConfigFlags{ScriptConfigFlag::InGame}.base(), "AICONFIG_INGAME");
51
52 SQAIInfo.PostRegister(engine);
53 engine.AddMethod("RegisterAI", &AIInfo::Constructor, "tx");
54 engine.AddMethod("RegisterDummyAI", &AIInfo::DummyConstructor, "tx");
55}
56
57/* static */ SQInteger AIInfo::Constructor(HSQUIRRELVM vm)
58{
59 /* Get the AIInfo */
60 SQUserPointer instance = nullptr;
61 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");
62 AIInfo *info = (AIInfo *)instance;
63
64 SQInteger res = ScriptInfo::Constructor(vm, *info);
65 if (res != 0) return res;
66
67 if (info->engine->MethodExists(info->SQ_instance, "MinVersionToLoad")) {
68 if (!info->engine->CallIntegerMethod(info->SQ_instance, "MinVersionToLoad", &info->min_loadable_version, MAX_GET_OPS)) return SQ_ERROR;
69 if (info->min_loadable_version < 0) return SQ_ERROR;
70 } else {
71 info->min_loadable_version = info->GetVersion();
72 }
73 /* When there is an UseAsRandomAI function, call it. */
74 if (info->engine->MethodExists(info->SQ_instance, "UseAsRandomAI")) {
75 if (!info->engine->CallBoolMethod(info->SQ_instance, "UseAsRandomAI", &info->use_as_random, MAX_GET_OPS)) return SQ_ERROR;
76 } else {
77 info->use_as_random = true;
78 }
79 /* Try to get the API version the AI is written for. */
80 if (info->engine->MethodExists(info->SQ_instance, "GetAPIVersion")) {
81 if (!info->engine->CallStringMethod(info->SQ_instance, "GetAPIVersion", &info->api_version, MAX_GET_OPS)) return SQ_ERROR;
82 if (!CheckAPIVersion(info->api_version)) {
83 Debug(script, 1, "Loading info.nut from ({}.{}): GetAPIVersion returned invalid version", info->GetName(), info->GetVersion());
84 return SQ_ERROR;
85 }
86 } else {
87 info->api_version = "0.7";
88 }
89
90 /* Remove the link to the real instance, else it might get deleted by RegisterAI() */
91 sq_setinstanceup(vm, 2, nullptr);
92 /* Register the AI to the base system */
93 info->GetScanner()->RegisterScript(info);
94 return 0;
95}
96
97/* static */ SQInteger AIInfo::DummyConstructor(HSQUIRRELVM vm)
98{
99 /* Get the AIInfo */
100 SQUserPointer instance;
101 sq_getinstanceup(vm, 2, &instance, nullptr);
102 AIInfo *info = (AIInfo *)instance;
103 info->api_version = *std::rbegin(AIInfo::ApiVersions);
104
105 SQInteger res = ScriptInfo::Constructor(vm, *info);
106 if (res != 0) return res;
107
108 /* Remove the link to the real instance, else it might get deleted by RegisterAI() */
109 sq_setinstanceup(vm, 2, nullptr);
110 /* Register the AI to the base system */
111 static_cast<AIScannerInfo *>(info->GetScanner())->SetDummyAI(info);
112 return 0;
113}
114
115AIInfo::AIInfo() :
116 min_loadable_version(0),
117 use_as_random(false)
118{
119}
120
121bool AIInfo::CanLoadFromVersion(int version) const
122{
123 if (version == -1) return true;
124 return version >= this->min_loadable_version && version <= this->GetVersion();
125}
126
127
128/* static */ void AILibrary::RegisterAPI(Squirrel &engine)
129{
130 /* Create the AILibrary class, and add the RegisterLibrary function */
131 engine.AddClassBegin("AILibrary");
133 engine.AddMethod("RegisterLibrary", &AILibrary::Constructor, "tx");
134}
135
136/* static */ SQInteger AILibrary::Constructor(HSQUIRRELVM vm)
137{
138 /* Create a new library */
139 AILibrary *library = new AILibrary();
140
141 SQInteger res = ScriptInfo::Constructor(vm, *library);
142 if (res != 0) {
143 delete library;
144 return res;
145 }
146
147 /* Cache the category */
148 if (!library->CheckMethod("GetCategory") || !library->engine->CallStringMethod(library->SQ_instance, "GetCategory", &library->category, MAX_GET_OPS)) {
149 delete library;
150 return SQ_ERROR;
151 }
152
153 /* Register the Library to the base system */
154 library->GetScanner()->RegisterScript(library);
155
156 return 0;
157}
static bool CheckAPIVersion(const std::string &api_version)
Check if the API version provided by the AI is supported.
Definition ai_info.cpp:25
AIInfo keeps track of all information of an AI, like Author, Description, ...
declarations of the class for AI scanner
All static information from an AI like name, version, etc.
Definition ai_info.hpp:16
bool CanLoadFromVersion(int version) const
Check if we can start this AI.
Definition ai_info.cpp:121
bool use_as_random
Should this AI be used when the user wants a "random AI"?
Definition ai_info.hpp:55
int min_loadable_version
The AI can load savegame data if the version is equal or greater than this.
Definition ai_info.hpp:54
std::string api_version
API version used by this AI.
Definition ai_info.hpp:56
static void RegisterAPI(Squirrel &engine)
Register the functions of this class.
Definition ai_info.cpp:32
static SQInteger Constructor(HSQUIRRELVM vm)
Create an AI, using this AIInfo as start-template.
Definition ai_info.cpp:57
static SQInteger DummyConstructor(HSQUIRRELVM vm)
Create a dummy-AI.
Definition ai_info.cpp:97
All static information from an AI library like name, version, etc.
Definition ai_info.hpp:60
std::string category
The category this library is in.
Definition ai_info.hpp:80
static void RegisterAPI(Squirrel &engine)
Register the functions of this class.
Definition ai_info.cpp:128
static SQInteger Constructor(HSQUIRRELVM vm)
Create an AI, using this AIInfo as start-template.
Definition ai_info.cpp:136
constexpr Tstorage base() const noexcept
Retrieve the raw value behind this bit set.
The template to define classes in Squirrel.
void DefSQAdvancedMethod(Squirrel &engine, Func function_proc, std::string_view function_name)
This defines a method inside a class for Squirrel, which has access to the 'engine' (experts only!...
bool CheckMethod(std::string_view name) const
Check if a given method exists.
int version
Version of the script.
const std::string & GetName() const
Get the Name of the script.
class Squirrel * engine
Engine used to register for Squirrel.
static SQInteger Constructor(HSQUIRRELVM vm, ScriptInfo &info)
Process the creation of a FileInfo object.
SQInteger AddSetting(HSQUIRRELVM vm)
Set a setting.
SQInteger AddLabels(HSQUIRRELVM vm)
Add labels for a setting.
int GetVersion() const
Get the version of the script.
HSQOBJECT SQ_instance
The Squirrel instance created for this info.
virtual class ScriptScanner * GetScanner()
Get the scanner which has found this ScriptInfo.
void RegisterScript(class ScriptInfo *info)
Register a ScriptInfo to the scanner.
void AddClassEnd()
Finishes adding a class to the global scope.
Definition squirrel.cpp:315
void AddClassBegin(std::string_view class_name)
Adds a class to the global scope.
Definition squirrel.cpp:291
void AddMethod(std::string_view method_name, SQFUNCTION proc, std::string_view params={}, void *userdata=nullptr, int size=0)
Adds a function to the stack.
Definition squirrel.cpp:256
bool MethodExists(HSQOBJECT instance, std::string_view method_name)
Check if a method exists in an instance.
Definition squirrel.cpp:323
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
@ Boolean
This value is a boolean (either 0 (false) or 1 (true) ).
@ Developer
This setting will only be visible when the Script development tools are active.
@ InGame
This setting can be changed while the Script is running.
static const int MAX_GET_OPS
Number of operations to get the author and similar information.