diff --git a/src/ai/ai_core.cpp b/src/ai/ai_core.cpp new file mode 100644 --- /dev/null +++ b/src/ai/ai_core.cpp @@ -0,0 +1,254 @@ +/* $Id$ */ + +/** @file ai_core.cpp Implementation of AI. */ + +#include "../stdafx.h" +#include "../openttd.h" +#include "../company_type.h" +#include "../company_base.h" +#include "../company_func.h" +#include "../debug.h" +#include "../network/network.h" +#include "../settings_type.h" +#include "../window_type.h" +#include "../window_func.h" +#include "../command_func.h" +#include "ai.hpp" +#include "ai_info.hpp" +#include "ai_scanner.hpp" +#include "ai_instance.hpp" +#include "ai_config.hpp" + +/* static */ uint AI::frame_counter = 0; +/* static */ AIScanner *AI::ai_scanner = NULL; + +/* static */ bool AI::CanStartNew() +{ + /* Only allow new AIs on the server and only when that is allowed in multiplayer */ + return !_networking || (_network_server && _settings_game.ai.ai_in_multiplayer); +} + +/* static */ void AI::StartNew(CompanyID company) +{ + assert(IsValidCompanyID(company)); + + /* Clients shouldn't start AIs */ + if (_networking && !_network_server) return; + + AIInfo *info = AIConfig::GetConfig(company)->GetInfo(); + if (info == NULL) { + info = AI::ai_scanner->SelectRandomAI(); + assert(info != NULL); + /* Load default data and store the name in the settings */ + AIConfig::GetConfig(company)->ChangeAI(info->GetDirName()); + } + + _current_company = company; + Company *c = GetCompany(company); + + c->ai_info = info; + c->ai_instance = new AIInstance(info); + + InvalidateWindowData(WC_AI_DEBUG, 0, -1); + return; +} + +/* static */ void AI::GameLoop() +{ + /* If we are in networking, only servers run this function, and that only if it is allowed */ + if (_networking && (!_network_server || !_settings_game.ai.ai_in_multiplayer)) return; + + /* The speed with which AIs go, is limited by the 'competitor_speed' */ + AI::frame_counter++; + assert(_settings_game.difficulty.competitor_speed <= 4); + if ((AI::frame_counter & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return; + + const Company *c; + FOR_ALL_COMPANIES(c) { + if (!IsHumanCompany(c->index)) { + _current_company = c->index; + c->ai_instance->GameLoop(); + } + } + + _current_company = OWNER_NONE; +} + +/* static */ uint AI::GetTick() +{ + return AI::frame_counter; +} + +/* static */ void AI::Stop(CompanyID company) +{ + if (_networking && !_network_server) return; + + _current_company = company; + Company *c = GetCompany(company); + + delete c->ai_instance; + c->ai_instance = NULL; + + InvalidateWindowData(WC_AI_DEBUG, 0, -1); +} + +/* static */ void AI::KillAll() +{ + /* It might happen there are no companies .. than we have nothing to loop */ + if (GetCompanyPoolSize() == 0) return; + + const Company *c; + FOR_ALL_COMPANIES(c) { + if (!IsHumanCompany(c->index)) AI::Stop(c->index); + } +} + +/* static */ void AI::Initialize() +{ + if (AI::ai_scanner != NULL) AI::Uninitialize(true); + + AI::frame_counter = 0; + if (AI::ai_scanner == NULL) AI::ai_scanner = new AIScanner(); +} + +/* static */ void AI::Uninitialize(bool keepConfig) +{ + AI::KillAll(); + + if (keepConfig) { + /* Run a rescan, which indexes all AIInfos again, and check if we can + * still load all the AIS, while keeping the configs in place */ + Rescan(); + } else { + delete AI::ai_scanner; + AI::ai_scanner = NULL; + + for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { + if (_settings_game.ai_config[c] != NULL) { + delete _settings_game.ai_config[c]; + _settings_game.ai_config[c] = NULL; + } + if (_settings_newgame.ai_config[c] != NULL) { + delete _settings_newgame.ai_config[c]; + _settings_newgame.ai_config[c] = NULL; + } + } + } +} + +/* static */ void AI::ResetConfig() +{ + /* Check for both newgame as current game if we can reload the AIInfo insde + * the AIConfig. If not, remove the AI from the list (which will assign + * a random new AI on reload). */ + for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { + if (_settings_game.ai_config[c] != NULL && _settings_game.ai_config[c]->HasAI()) { + if (!_settings_game.ai_config[c]->ResetInfo()) { + DEBUG(ai, 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()); + _settings_game.ai_config[c]->ChangeAI(NULL); + } + } + if (_settings_newgame.ai_config[c] != NULL && _settings_newgame.ai_config[c]->HasAI()) { + if (!_settings_newgame.ai_config[c]->ResetInfo()) { + DEBUG(ai, 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()); + _settings_newgame.ai_config[c]->ChangeAI(NULL); + } + } + } +} + +/* static */ void AI::NewEvent(CompanyID company, AIEvent *event) +{ + /* Clients should ignore events */ + if (_networking && !_network_server) return; + + /* Only AIs can have an event-queue */ + if (!IsValidCompanyID(company) || IsHumanCompany(company)) return; + + /* Queue the event */ + CompanyID old_company = _current_company; + _current_company = company; + AIEventController::InsertEvent(event); + _current_company = old_company; +} + +/* static */ void AI::BroadcastNewEvent(AIEvent *event, CompanyID skip_company) +{ + /* Clients should ignore events */ + if (_networking && !_network_server) return; + + /* Try to send the event to all AIs */ + for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { + if (c != skip_company) AI::NewEvent(c, event); + } +} + +void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2) +{ + AIObject::SetLastCommandRes(success); + + if (!success) { + AIObject::SetLastError(AIError::StringToError(_error_message)); + } else { + AIObject::IncreaseDoCommandCosts(AIObject::GetLastCost()); + } + + GetCompany(_current_company)->ai_instance->Continue(); +} + +/* static */ void AI::Save(CompanyID company) +{ + if (!_networking || _network_server) { + assert(IsValidCompanyID(company)); + assert(GetCompany(company)->ai_instance != NULL); + + CompanyID old_company = _current_company; + _current_company = company; + GetCompany(company)->ai_instance->Save(); + _current_company = old_company; + } else { + AIInstance::SaveEmpty(); + } +} + +/* static */ void AI::Load(CompanyID company) +{ + if (!_networking || _network_server) { + assert(IsValidCompanyID(company)); + assert(GetCompany(company)->ai_instance != NULL); + + CompanyID old_company = _current_company; + _current_company = company; + GetCompany(company)->ai_instance->Load(); + _current_company = old_company; + } else { + /* Read, but ignore, the load data */ + AIInstance::LoadEmpty(); + } +} + +/* static */ char *AI::GetConsoleList(char *p, const char *last) +{ + return AI::ai_scanner->GetAIConsoleList(p, last); +} + +/* static */ const AIInfoList *AI::GetInfoList() +{ + return AI::ai_scanner->GetAIInfoList(); +} + +/* static */ AIInfo *AI::GetCompanyInfo(const char *name) +{ + return AI::ai_scanner->FindAI(name); +} + +/* static */ bool AI::ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm) +{ + return AI::ai_scanner->ImportLibrary(library, class_name, version, vm, GetCompany(_current_company)->ai_instance->GetController()); +} + +/* static */ void AI::Rescan() +{ + AI::ai_scanner->RescanAIDir(); + ResetConfig(); +}