Invalidate the right goal list when changing goals
[openttd/fttd.git] / src / ai / ai_core.cpp
blob241c0bf2517223cb3b0662ea94238f70c9af910c
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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 */
10 /** @file ai_core.cpp Implementation of AI. */
12 #include "../stdafx.h"
13 #include "../core/backup_type.hpp"
14 #include "../core/bitmath_func.hpp"
15 #include "../company_base.h"
16 #include "../company_func.h"
17 #include "../network/network.h"
18 #include "../window_func.h"
19 #include "ai_scanner.hpp"
20 #include "ai_instance.hpp"
21 #include "ai_config.hpp"
22 #include "ai_info.hpp"
23 #include "ai.hpp"
25 /* static */ uint AI::frame_counter = 0;
26 /* static */ AIScannerInfo *AI::scanner_info = NULL;
27 /* static */ AIScannerLibrary *AI::scanner_library = NULL;
29 /* static */ bool AI::CanStartNew()
31 /* Only allow new AIs on the server and only when that is allowed in multiplayer */
32 return !_networking || (_network_server && _settings_game.ai.ai_in_multiplayer);
35 /* static */ void AI::StartNew(CompanyID company, bool rerandomise_ai)
37 assert(Company::IsValidID(company));
39 /* Clients shouldn't start AIs */
40 if (_networking && !_network_server) return;
42 AIConfig *config = AIConfig::GetConfig(company, AIConfig::SSS_FORCE_GAME);
43 AIInfo *info = config->GetInfo();
44 if (info == NULL || (rerandomise_ai && config->IsRandom())) {
45 info = AI::scanner_info->SelectRandomAI();
46 assert(info != NULL);
47 /* Load default data and store the name in the settings */
48 config->Change(info->GetName(), -1, false, true);
50 config->AnchorUnchangeableSettings();
52 Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
53 Company *c = Company::Get(company);
55 c->ai_info = info;
56 assert(c->ai_instance == NULL);
57 c->ai_instance = new AIInstance();
58 c->ai_instance->Initialize(info);
60 cur_company.Restore();
62 InvalidateWindowData(WC_AI_DEBUG, 0, -1);
63 return;
66 /* static */ void AI::GameLoop()
68 /* If we are in networking, only servers run this function, and that only if it is allowed */
69 if (_networking && (!_network_server || !_settings_game.ai.ai_in_multiplayer)) return;
71 /* The speed with which AIs go, is limited by the 'competitor_speed' */
72 AI::frame_counter++;
73 assert(_settings_game.difficulty.competitor_speed <= 4);
74 if ((AI::frame_counter & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return;
76 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
77 const Company *c;
78 FOR_ALL_COMPANIES(c) {
79 if (c->is_ai) {
80 cur_company.Change(c->index);
81 c->ai_instance->GameLoop();
84 cur_company.Restore();
86 /* Occasionally collect garbage; every 255 ticks do one company.
87 * Effectively collecting garbage once every two months per AI. */
88 if ((AI::frame_counter & 255) == 0) {
89 CompanyID cid = (CompanyID)GB(AI::frame_counter, 8, 4);
90 if (Company::IsValidAiID(cid)) Company::Get(cid)->ai_instance->CollectGarbage();
94 /* static */ uint AI::GetTick()
96 return AI::frame_counter;
99 /* static */ void AI::Stop(CompanyID company)
101 if (_networking && !_network_server) return;
103 Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
104 Company *c = Company::Get(company);
106 delete c->ai_instance;
107 c->ai_instance = NULL;
108 c->ai_info = NULL;
110 cur_company.Restore();
112 InvalidateWindowData(WC_AI_DEBUG, 0, -1);
113 DeleteWindowById(WC_AI_SETTINGS, company);
116 /* static */ void AI::Pause(CompanyID company)
118 /* The reason why dedicated servers are forbidden to execute this
119 * command is not because it is unsafe, but because there is no way
120 * for the server owner to unpause the script again. */
121 if (_network_dedicated) return;
123 Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
124 Company::Get(company)->ai_instance->Pause();
126 cur_company.Restore();
129 /* static */ void AI::Unpause(CompanyID company)
131 Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
132 Company::Get(company)->ai_instance->Unpause();
134 cur_company.Restore();
137 /* static */ bool AI::IsPaused(CompanyID company)
139 Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
140 bool paused = Company::Get(company)->ai_instance->IsPaused();
142 cur_company.Restore();
144 return paused;
147 /* static */ void AI::KillAll()
149 /* It might happen there are no companies .. than we have nothing to loop */
150 if (Company::GetPoolSize() == 0) return;
152 const Company *c;
153 FOR_ALL_COMPANIES(c) {
154 if (c->is_ai) AI::Stop(c->index);
158 /* static */ void AI::Initialize()
160 if (AI::scanner_info != NULL) AI::Uninitialize(true);
162 AI::frame_counter = 0;
163 if (AI::scanner_info == NULL) {
164 TarScanner::DoScan(TarScanner::AI);
165 AI::scanner_info = new AIScannerInfo();
166 AI::scanner_info->Initialize();
167 AI::scanner_library = new AIScannerLibrary();
168 AI::scanner_library->Initialize();
172 /* static */ void AI::Uninitialize(bool keepConfig)
174 AI::KillAll();
176 if (keepConfig) {
177 /* Run a rescan, which indexes all AIInfos again, and check if we can
178 * still load all the AIS, while keeping the configs in place */
179 Rescan();
180 } else {
181 delete AI::scanner_info;
182 delete AI::scanner_library;
183 AI::scanner_info = NULL;
184 AI::scanner_library = NULL;
186 for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
187 if (_settings_game.ai_config[c] != NULL) {
188 delete _settings_game.ai_config[c];
189 _settings_game.ai_config[c] = NULL;
191 if (_settings_newgame.ai_config[c] != NULL) {
192 delete _settings_newgame.ai_config[c];
193 _settings_newgame.ai_config[c] = NULL;
199 /* static */ void AI::ResetConfig()
201 /* Check for both newgame as current game if we can reload the AIInfo inside
202 * the AIConfig. If not, remove the AI from the list (which will assign
203 * a random new AI on reload). */
204 for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
205 if (_settings_game.ai_config[c] != NULL && _settings_game.ai_config[c]->HasScript()) {
206 if (!_settings_game.ai_config[c]->ResetInfo(true)) {
207 DEBUG(script, 0, "After a reload, the AI by the name '%s' was no longer found, and removed from the list.", _settings_game.ai_config[c]->GetName());
208 _settings_game.ai_config[c]->Change(NULL);
209 if (Company::IsValidAiID(c)) {
210 /* The code belonging to an already running AI was deleted. We can only do
211 * one thing here to keep everything sane and that is kill the AI. After
212 * killing the offending AI we start a random other one in it's place, just
213 * like what would happen if the AI was missing during loading. */
214 AI::Stop(c);
215 AI::StartNew(c, false);
217 } else if (Company::IsValidAiID(c)) {
218 /* Update the reference in the Company struct. */
219 Company::Get(c)->ai_info = _settings_game.ai_config[c]->GetInfo();
222 if (_settings_newgame.ai_config[c] != NULL && _settings_newgame.ai_config[c]->HasScript()) {
223 if (!_settings_newgame.ai_config[c]->ResetInfo(false)) {
224 DEBUG(script, 0, "After a reload, the AI by the name '%s' was no longer found, and removed from the list.", _settings_newgame.ai_config[c]->GetName());
225 _settings_newgame.ai_config[c]->Change(NULL);
231 /* static */ void AI::NewEvent(CompanyID company, ScriptEvent *event)
233 /* AddRef() and Release() need to be called at least once, so do it here */
234 event->AddRef();
236 /* Clients should ignore events */
237 if (_networking && !_network_server) {
238 event->Release();
239 return;
242 /* Only AIs can have an event-queue */
243 if (!Company::IsValidAiID(company)) {
244 event->Release();
245 return;
248 /* Queue the event */
249 Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
250 Company::Get(_current_company)->ai_instance->InsertEvent(event);
251 cur_company.Restore();
253 event->Release();
256 /* static */ void AI::BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company)
258 /* AddRef() and Release() need to be called at least once, so do it here */
259 event->AddRef();
261 /* Clients should ignore events */
262 if (_networking && !_network_server) {
263 event->Release();
264 return;
267 /* Try to send the event to all AIs */
268 for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
269 if (c != skip_company) AI::NewEvent(c, event);
272 event->Release();
275 /* static */ void AI::Save(SaveDumper *dumper, CompanyID company)
277 if (!_networking || _network_server) {
278 Company *c = Company::GetIfValid(company);
279 assert(c != NULL && c->ai_instance != NULL);
281 Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
282 c->ai_instance->Save(dumper);
283 cur_company.Restore();
284 } else {
285 AIInstance::SaveEmpty(dumper);
289 /* static */ void AI::Load(LoadBuffer *reader, CompanyID company, int version)
291 if (!_networking || _network_server) {
292 Company *c = Company::GetIfValid(company);
293 assert(c != NULL && c->ai_instance != NULL);
295 Backup<CompanyByte> cur_company(_current_company, company, FILE_LINE);
296 c->ai_instance->Load(reader, version);
297 cur_company.Restore();
298 } else {
299 /* Read, but ignore, the load data */
300 AIInstance::LoadEmpty(reader);
304 /* static */ int AI::GetStartNextTime()
306 /* Find the first company which doesn't exist yet */
307 for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
308 if (!Company::IsValidID(c)) return AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->GetSetting("start_date");
311 /* Currently no AI can be started, check again in a year. */
312 return DAYS_IN_YEAR;
315 /* static */ char *AI::GetConsoleList(char *p, const char *last, bool newest_only)
317 return AI::scanner_info->GetConsoleList(p, last, newest_only);
320 /* static */ char *AI::GetConsoleLibraryList(char *p, const char *last)
322 return AI::scanner_library->GetConsoleList(p, last, true);
325 /* static */ const ScriptInfoList *AI::GetInfoList()
327 return AI::scanner_info->GetInfoList();
330 /* static */ const ScriptInfoList *AI::GetUniqueInfoList()
332 return AI::scanner_info->GetUniqueInfoList();
335 /* static */ AIInfo *AI::FindInfo(const char *name, int version, bool force_exact_match)
337 return AI::scanner_info->FindInfo(name, version, force_exact_match);
340 /* static */ AILibrary *AI::FindLibrary(const char *library, int version)
342 return AI::scanner_library->FindLibrary(library, version);
345 /* static */ void AI::Rescan()
347 TarScanner::DoScan(TarScanner::AI);
349 AI::scanner_info->RescanDir();
350 AI::scanner_library->RescanDir();
351 ResetConfig();
353 InvalidateWindowData(WC_AI_LIST, 0, 1);
354 SetWindowClassesDirty(WC_AI_DEBUG);
355 InvalidateWindowClassesData(WC_AI_SETTINGS);
358 #if defined(ENABLE_NETWORK)
361 * Check whether we have an AI (library) with the exact characteristics as ci.
362 * @param ci the characteristics to search on (shortname and md5sum)
363 * @param md5sum whether to check the MD5 checksum
364 * @return true iff we have an AI (library) matching.
366 /* static */ bool AI::HasAI(const ContentInfo *ci, bool md5sum)
368 return AI::scanner_info->HasScript(ci, md5sum);
371 /* static */ bool AI::HasAILibrary(const ContentInfo *ci, bool md5sum)
373 return AI::scanner_library->HasScript(ci, md5sum);
376 #endif /* defined(ENABLE_NETWORK) */
378 /* static */ AIScannerInfo *AI::GetScannerInfo()
380 return AI::scanner_info;
383 /* static */ AIScannerLibrary *AI::GetScannerLibrary()
385 return AI::scanner_library;