diff options
author | truebrain <truebrain@openttd.org> | 2009-01-12 17:11:45 +0000 |
---|---|---|
committer | truebrain <truebrain@openttd.org> | 2009-01-12 17:11:45 +0000 |
commit | a3dd7506d377b1434f913bd65c019eed52b64b6e (patch) | |
tree | ced1a262eb143ad6e64ec02f4a4c89835c0c32fd /src/ai | |
parent | 9294f9616866b9778c22076c19b5a32b4f85f788 (diff) | |
download | openttd-a3dd7506d377b1434f913bd65c019eed52b64b6e.tar.xz |
(svn r15027) -Merge: tomatos and bananas left to be, here is NoAI for all to see.
NoAI is an API (a framework) to build your own AIs in. See:
http://wiki.openttd.org/wiki/index.php/AI:Main_Page
With many thanks to:
- glx and Rubidium for their syncing, feedback and hard work
- Yexo for his feedback, patches, and AIs which tested the system very deep
- Morloth for his feedback and patches
- TJIP for hosting a challenge which kept NoAI on track
- All AI authors for testing our AI API, and all other people who helped in one way or another
-Remove: all old AIs and their cheats/hacks
Diffstat (limited to 'src/ai')
167 files changed, 19124 insertions, 7079 deletions
diff --git a/src/ai/ai.cpp b/src/ai/ai.cpp deleted file mode 100644 index 01c8dcd68..000000000 --- a/src/ai/ai.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/* $Id$ */ - -/** @file ai.cpp Base for all AIs. */ - -#include "../stdafx.h" -#include "../openttd.h" -#include "../variables.h" -#include "../command_func.h" -#include "../network/network.h" -#include "../core/alloc_func.hpp" -#include "../company_func.h" -#include "../company_base.h" -#include "ai.h" -#include "default/default.h" -#include "trolly/trolly.h" -#include "../signal_func.h" - -AIStruct _ai; -AICompany _ai_company[MAX_COMPANIES]; - -/** - * Dequeues commands put in the queue via AI_PutCommandInQueue. - */ -static void AI_DequeueCommands(CompanyID company) -{ - AICommand *com, *entry_com; - - entry_com = _ai_company[company].queue; - - /* It happens that DoCommandP issues a new DoCommandAI which adds a new command - * to this very same queue (don't argue about this, if it currently doesn't - * happen I can tell you it will happen with AIScript -- TrueLight). If we - * do not make the queue NULL, that commands will be dequeued immediatly. - * Therefor we safe the entry-point to entry_com, and make the queue NULL, so - * the new queue can be safely built up. */ - _ai_company[company].queue = NULL; - _ai_company[company].queue_tail = NULL; - - /* Dequeue all commands */ - while ((com = entry_com) != NULL) { - _current_company = company; - - DoCommandP(com->tile, com->p1, com->p2, com->cmd, com->callback, com->text); - - /* Free item */ - entry_com = com->next; - free(com->text); - free(com); - } -} - -/** - * Needed for SP; we need to delay DoCommand with 1 tick, because else events - * will make infinite loops (AIScript). - */ -static void AI_PutCommandInQueue(CompanyID company, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text = NULL) -{ - AICommand *com; - - if (_ai_company[company].queue_tail == NULL) { - /* There is no item in the queue yet, create the queue */ - _ai_company[company].queue = MallocT<AICommand>(1); - _ai_company[company].queue_tail = _ai_company[company].queue; - } else { - /* Add an item at the end */ - _ai_company[company].queue_tail->next = MallocT<AICommand>(1); - _ai_company[company].queue_tail = _ai_company[company].queue_tail->next; - } - - /* This is our new item */ - com = _ai_company[company].queue_tail; - - /* Assign the info */ - com->tile = tile; - com->p1 = p1; - com->p2 = p2; - com->cmd = cmd; - com->callback = callback; - com->next = NULL; - com->text = text == NULL ? NULL : strdup(text); -} - -/** - * Executes a raw DoCommand for the AI. - */ -CommandCost AI_DoCommandCc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint32 cmd, CommandCallback *callback, const char *text) -{ - CompanyID old_local_company; - CommandCost res; - - /* If you enable DC_EXEC with DC_QUERY_COST you are a really strange - * person.. should we check for those funny jokes? - */ - - /* First, do a test-run to see if we can do this */ - res = DoCommand(tile, p1, p2, flags & ~DC_EXEC, cmd, text); - /* The command failed, or you didn't want to execute, or you are quering, return */ - if (CmdFailed(res) || !(flags & DC_EXEC) || (flags & DC_QUERY_COST)) { - return res; - } - - /* NetworkSend_Command needs _local_company to be set correctly, so - * adjust it, and put it back right after the function */ - old_local_company = _local_company; - _local_company = _current_company; - -#ifdef ENABLE_NETWORK - /* Send the command */ - if (_networking) { - /* Network is easy, send it to his handler */ - NetworkSend_Command(tile, p1, p2, cmd, callback, text); - } else { -#else - { -#endif - /* If we execute BuildCommands directly in SP, we have a big problem with events - * so we need to delay is for 1 tick */ - AI_PutCommandInQueue(_current_company, tile, p1, p2, cmd, callback, text); - } - - /* Set _local_company back */ - _local_company = old_local_company; - - return res; -} - - -CommandCost AI_DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint32 cmd, const char *text) -{ - return AI_DoCommandCc(tile, p1, p2, flags, cmd, NULL, text); -} - - -/** - * Run 1 tick of the AI. Don't overdo it, keep it realistic. - */ -static void AI_RunTick(CompanyID company) -{ - extern void AiNewDoGameLoop(Company *c); - - Company *c = GetCompany(company); - _current_company = company; - - if (_settings_game.ai.ainew_active) { - AiNewDoGameLoop(c); - } else { - /* Enable all kind of cheats the old AI needs in order to operate correctly... */ - _is_old_ai_company = true; - AiDoGameLoop(c); - _is_old_ai_company = false; - } - - /* AI could change some track, so update signals */ - UpdateSignalsInBuffer(); -} - - -/** - * The gameloop for AIs. - * Handles one tick for all the AIs. - */ -void AI_RunGameLoop() -{ - /* Don't do anything if ai is disabled */ - if (!_ai.enabled) return; - - /* Don't do anything if we are a network-client, or the AI has been disabled */ - if (_networking && (!_network_server || !_settings_game.ai.ai_in_multiplayer)) return; - - /* New tick */ - _ai.tick++; - - /* Make sure the AI follows the difficulty rule.. */ - assert(_settings_game.difficulty.competitor_speed <= 4); - if ((_ai.tick & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return; - - /* Check for AI-client (so joining a network with an AI) */ - if (!_networking || _network_server) { - /* Check if we want to run AIs (server or SP only) */ - const Company *c; - - FOR_ALL_COMPANIES(c) { - if (c->is_ai) { - /* This should always be true, else something went wrong... */ - assert(_ai_company[c->index].active); - - /* Run the script */ - AI_DequeueCommands(c->index); - AI_RunTick(c->index); - } - } - } - - _current_company = OWNER_NONE; -} - -/** - * A new AI sees the day of light. You can do here what ever you think is needed. - */ -void AI_StartNewAI(CompanyID company) -{ - assert(IsValidCompanyID(company)); - - /* Called if a new AI is booted */ - _ai_company[company].active = true; -} - -/** - * This AI company died. Give it some chance to make a final puf. - */ -void AI_CompanyDied(CompanyID company) -{ - /* Called if this AI died */ - _ai_company[company].active = false; - - if (_companies_ainew[company].pathfinder == NULL) return; - - AyStarMain_Free(_companies_ainew[company].pathfinder); - delete _companies_ainew[company].pathfinder; - _companies_ainew[company].pathfinder = NULL; - -} - -/** - * Initialize some AI-related stuff. - */ -void AI_Initialize() -{ - /* First, make sure all AIs are DEAD! */ - AI_Uninitialize(); - - memset(&_ai, 0, sizeof(_ai)); - memset(&_ai_company, 0, sizeof(_ai_company)); - - _ai.enabled = true; -} - -/** - * Deinitializer for AI-related stuff. - */ -void AI_Uninitialize() -{ - for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) AI_CompanyDied(c); -} diff --git a/src/ai/ai.h b/src/ai/ai.h deleted file mode 100644 index 0f18490d5..000000000 --- a/src/ai/ai.h +++ /dev/null @@ -1,115 +0,0 @@ -/* $Id$ */ - -/** @file ai.h Base functions for all AIs. */ - -#ifndef AI_H -#define AI_H - -#include "../network/network.h" -#include "../command_type.h" -#include "../core/random_func.hpp" -#include "../settings_type.h" - -/* How DoCommands look like for an AI */ -struct AICommand { - uint32 tile; - uint32 p1; - uint32 p2; - uint32 cmd; - CommandCallback *callback; - - char *text; - uint uid; - - AICommand *next; -}; - -/* The struct for an AIScript Company */ -struct AICompany { - bool active; ///< Is this AI active? - AICommand *queue; ///< The commands that he has in his queue - AICommand *queue_tail; ///< The tail of this queue -}; - -/* The struct to keep some data about the AI in general */ -struct AIStruct { - /* General */ - bool enabled; ///< Is AI enabled? - uint tick; ///< The current tick (something like _frame_counter, only for AIs) -}; - -extern AIStruct _ai; -extern AICompany _ai_company[MAX_COMPANIES]; - -// ai.c -void AI_StartNewAI(CompanyID company); -void AI_CompanyDied(CompanyID company); -void AI_RunGameLoop(); -void AI_Initialize(); -void AI_Uninitialize(); -CommandCost AI_DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc, const char *text = NULL); -CommandCost AI_DoCommandCc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc, CommandCallback *callback, const char *text = NULL); - -/** Is it allowed to start a new AI. - * This function checks some boundries to see if we should launch a new AI. - * @return True if we can start a new AI. - */ -static inline bool AI_AllowNewAI() -{ - /* If disabled, no AI */ - if (!_ai.enabled) - return false; - - /* If in network, but no server, no AI */ - if (_networking && !_network_server) - return false; - - /* If in network, and server, possible AI */ - if (_networking && _network_server) { - /* Do we want AIs in multiplayer? */ - if (!_settings_game.ai.ai_in_multiplayer) - return false; - - /* Only the NewAI is allowed... sadly enough the old AI just doesn't support this - * system, because all commands are delayed by at least 1 tick, which causes - * a big problem, because it uses variables that are only set AFTER the command - * is really executed... */ - if (!_settings_game.ai.ainew_active) - return false; - } - - return true; -} - -#define AI_CHANCE16(a, b) ((uint16) AI_Random() <= (uint16)((65536 * a) / b)) -#define AI_CHANCE16R(a, b, r) ((uint16)(r = AI_Random()) <= (uint16)((65536 * a) / b)) - -/** - * The random-function that should be used by ALL AIs. - */ -static inline uint AI_RandomRange(uint max) -{ - /* We pick RandomRange if we are in SP (so when saved, we do the same over and over) - * but we pick InteractiveRandomRange if we are a network_server or network-client. - */ - if (_networking) - return InteractiveRandomRange(max); - else - return RandomRange(max); -} - -/** - * The random-function that should be used by ALL AIs. - */ -static inline uint32 AI_Random() -{ -/* We pick RandomRange if we are in SP (so when saved, we do the same over and over) - * but we pick InteractiveRandomRange if we are a network_server or network-client. - */ - if (_networking) - return InteractiveRandom(); - else - return Random(); -} - -#endif /* AI_H */ diff --git a/src/ai/ai.hpp b/src/ai/ai.hpp new file mode 100644 index 000000000..bd1c2a907 --- /dev/null +++ b/src/ai/ai.hpp @@ -0,0 +1,102 @@ +/* $Id$ */ + +/** @file ai.hpp Base functions for all AIs. */ + +#ifndef AI_HPP +#define AI_HPP + +#include "api/ai_event_types.hpp" + +#ifndef AI_CONFIG_HPP +struct ltstr { bool operator()(const char *s1, const char *s2) const { return strcmp(s1, s2) < 0; } }; +#endif /* AI_CONFIG_HPP */ +typedef std::map<const char *, class AIInfo *, ltstr> AIInfoList; + + +void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2); + +class AI { +public: + /** + * Is it possible to start a new AI company? + * @return True if a new AI company can be started. + */ + static bool CanStartNew(); + + /** + * Start a new AI company. + * @param company At which slot the AI company should start. + */ + static void StartNew(CompanyID company); + + /** + * Called every game-tick to let AIs do something. + */ + static void GameLoop(); + + /** + * Get the current AI tick. + */ + static uint GetTick(); + + /** + * Stop a company to be controlled by an AI. + * @param company The company from which the AI needs to detach. + * @pre !IsHumanCompany(company). + */ + static void Stop(CompanyID company); + + /** + * Kill any and all AIs we manage. + */ + static void KillAll(); + + /** + * Initialize the AI system. + */ + static void Initialize(); + + /** + * Uninitialize the AI system + * @param keepConfig Should we keep AIConfigs, or can we free that memory? + */ + static void Uninitialize(bool keepConfig); + + /** + * Reset all AIConfigs, and make them reload their AIInfo. + * If the AIInfo could no longer be found, an error is reported to the user. + */ + static void ResetConfig(); + + /** + * Queue a new event for an AI. + */ + static void NewEvent(CompanyID company, AIEvent *event); + + /** + * Broadcast a new event to all active AIs. + */ + static void BroadcastNewEvent(AIEvent *event, CompanyID skip_company = MAX_COMPANIES); + + /** + * Save data from an AI to a savegame. + */ + static void Save(CompanyID company); + + /** + * Load data for an AI from a savegame. + */ + static void Load(CompanyID company); + + static char *GetConsoleList(char *p, const char *last); + static const AIInfoList *GetInfoList(); + static AIInfo *GetCompanyInfo(const char *name); + static bool ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm); + static void Rescan(); + +private: + static uint frame_counter; + static class AIScanner *ai_scanner; +}; + +#endif /* AI_HPP */ diff --git a/src/ai/ai_config.cpp b/src/ai/ai_config.cpp new file mode 100644 index 000000000..f5d22bdaa --- /dev/null +++ b/src/ai/ai_config.cpp @@ -0,0 +1,143 @@ +/* $Id$ */ + +/** @file ai_config.cpp Implementation of AIConfig. */ + +#include "../stdafx.h" +#include "../openttd.h" +#include "../settings_type.h" +#include "ai.hpp" +#include "ai_config.hpp" +#include "ai_info.hpp" + +void AIConfig::ChangeAI(const char *name) +{ + free((void *)this->name); + this->name = (name == NULL) ? NULL : strdup(name); + this->info = (name == NULL) ? NULL : AI::GetCompanyInfo(this->name); + + for (SettingValueList::iterator it = this->settings.begin(); it != this->settings.end(); it++) { + free((void*)(*it).first); + } + this->settings.clear(); +} + +AIConfig::AIConfig(const AIConfig *config) : + name(NULL), + info(NULL) +{ + this->name = (config->name == NULL) ? NULL : strdup(config->name); + this->info = config->info; + + for (SettingValueList::const_iterator it = config->settings.begin(); it != config->settings.end(); it++) { + this->settings[strdup((*it).first)] = (*it).second; + } +} + +AIConfig::~AIConfig() +{ + this->ChangeAI(NULL); +} + +AIInfo *AIConfig::GetInfo() +{ + return this->info; +} + +bool AIConfig::ResetInfo() +{ + this->info = AI::GetCompanyInfo(this->name); + return this->info != NULL; +} + +AIConfig *AIConfig::GetConfig(CompanyID company, bool forceNewgameSetting) +{ + AIConfig **config; + if (!forceNewgameSetting) { + config = (_game_mode == GM_MENU) ? &_settings_newgame.ai_config[company] : &_settings_game.ai_config[company]; + } else { + config = &_settings_newgame.ai_config[company]; + } + if (*config == NULL) *config = new AIConfig(); + return *config; +} + +int AIConfig::GetSetting(const char *name) +{ + assert(this->info != NULL); + + SettingValueList::iterator it = this->settings.find(name); + /* Return the default value if the setting is not set, or if we are in a not-custom difficult level */ + if (it == this->settings.end() || ((_game_mode == GM_MENU) ? _settings_newgame.difficulty.diff_level : _settings_game.difficulty.diff_level) != 3) { + return this->info->GetSettingDefaultValue(name); + } + return (*it).second; +} + +void AIConfig::SetSetting(const char *name, int value) +{ + /* You can only set ai specific settings if an AI is selected. */ + assert(strcmp(name, "start_date") == 0 || this->info != NULL); + + SettingValueList::iterator it = this->settings.find(name); + if (it != this->settings.end()) { + (*it).second = value; + } else { + this->settings[strdup(name)] = value; + } +} + +bool AIConfig::HasAI() +{ + return this->info != NULL; +} + +const char *AIConfig::GetName() +{ + return this->name; +} + +void AIConfig::StringToSettings(const char *value) +{ + char *value_copy = strdup(value); + char *s = value_copy; + + while (s != NULL) { + /* Analyze the string ('name=value,name=value\0') */ + char *item_name = s; + s = strchr(s, '='); + if (s == NULL) break; + if (*s == '\0') break; + *s = '\0'; + s++; + + char *item_value = s; + s = strchr(s, ','); + if (s != NULL) { + *s = '\0'; + s++; + } + + this->SetSetting(item_name, atoi(item_value)); + } + free(value_copy); +} + +void AIConfig::SettingsToString(char *string, int size) +{ + string[0] = '\0'; + for (SettingValueList::iterator it = this->settings.begin(); it != this->settings.end(); it++) { + char no[10]; + snprintf(no, sizeof(no), "%d", (*it).second); + + /* Check if the string would fit in the destination */ + size -= strlen((*it).first) - 1 - strlen(no) - 1; + /* If it doesn't fit, skip the next settings */ + if (size <= 0) return; + + strcat(string, (*it).first); + strcat(string, "="); + strcat(string, no); + strcat(string, ","); + } + string[strlen(string) - 1] = '\0'; +} diff --git a/src/ai/ai_config.hpp b/src/ai/ai_config.hpp new file mode 100644 index 000000000..c628a855f --- /dev/null +++ b/src/ai/ai_config.hpp @@ -0,0 +1,91 @@ +/* $Id$ */ + +/** @file ai_config.hpp AIConfig stores the configuration settings of every AI. */ + +#ifndef AI_CONFIG_HPP +#define AI_CONFIG_HPP + +#include <map> + +#ifndef AI_HPP +struct ltstr { bool operator()(const char *s1, const char *s2) const { return strcmp(s1, s2) < 0; } }; +#endif /* AI_HPP */ + +class AIConfig { +private: + typedef std::map<const char *, int, ltstr> SettingValueList; + +public: + AIConfig() : + name(NULL), + info(NULL) + {} + AIConfig(const AIConfig *config); + ~AIConfig(); + + /** + * Set another AI to be loaded in this slot. + */ + void ChangeAI(const char *name); + + /** + * When ever the AI Scanner is reloaded, all infos become invalid. This + * function tells AIConfig about this. + * @return True if the reset was successfull, false if the AI was no longer + * found. + */ + bool ResetInfo(); + + /** + * Get the AIInfo linked to this AIConfig. + */ + class AIInfo *GetInfo(); + + /** + * Get the config of a company. + */ + static AIConfig *GetConfig(CompanyID company, bool forceNewgameSetting = false); + + /** + * Get the value of a setting for this config. It might fallback to his + * 'info' to find the default value (if not set or if not-custom difficulty + * level). + * @return The (default) value of the setting, or -1 if the setting was not + * found. + */ + int GetSetting(const char *name); + + /** + * Set the value of a setting for this config. + */ + void SetSetting(const char *name, int value); + + /** + * Is this config attached to an AI? + */ + bool HasAI(); + + /** + * Get the name of the AI. + */ + const char *GetName(); + + /** + * Convert a string which is stored in the config file or savegames to + * custom settings of this AI. + */ + void StringToSettings(const char *value); + + /** + * Convert the custom settings to a string that can be stored in the config + * file or savegames. + */ + void SettingsToString(char *string, int size); + +private: + const char *name; + class AIInfo *info; + SettingValueList settings; +}; + +#endif /* AI_CONFIG_HPP */ diff --git a/src/ai/ai_core.cpp b/src/ai/ai_core.cpp new file mode 100644 index 000000000..f0a82f771 --- /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(); +} diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp new file mode 100644 index 000000000..8272cc966 --- /dev/null +++ b/src/ai/ai_gui.cpp @@ -0,0 +1,257 @@ +/* $Id$ */ + +/** @file ai_gui.cpp Window for configuring the AIs */ + +#include "../stdafx.h" +#include "../openttd.h" +#include "../gui.h" +#include "../window_gui.h" +#include "../company_func.h" +#include "../company_base.h" +#include "../company_gui.h" +#include "../economy_func.h" +#include "../variables.h" +#include "../cargotype.h" +#include "../strings_func.h" +#include "../core/alloc_func.hpp" +#include "../window_func.h" +#include "../date_func.h" +#include "../gfx_func.h" +#include "../debug.h" +#include "../command_func.h" +#include "../network/network.h" + +#include "ai.hpp" +#include "api/ai_types.hpp" +#include "api/ai_controller.hpp" +#include "api/ai_object.hpp" +#include "api/ai_log.hpp" +#include "ai_info.hpp" + +#include "table/strings.h" +#include "../table/sprites.h" + +struct AIDebugWindow : public Window { + enum AIDebugWindowWidgets { + AID_WIDGET_CLOSEBOX = 0, + AID_WIDGET_CAPTION, + AID_WIDGET_VIEW, + AID_WIDGET_NAME_TEXT, + AID_WIDGET_RELOAD_TOGGLE, + AID_WIDGET_LOG_PANEL, + AID_WIDGET_SCROLLBAR, + AID_WIDGET_UNUSED_1, + AID_WIDGET_UNUSED_2, + AID_WIDGET_UNUSED_3, + AID_WIDGET_UNUSED_4, + AID_WIDGET_UNUSED_5, + AID_WIDGET_UNUSED_6, + AID_WIDGET_UNUSED_7, + + AID_WIDGET_COMPANY_BUTTON_START, + AID_WIDGET_COMPANY_BUTTON_END = AID_WIDGET_COMPANY_BUTTON_START + 14, + }; + + static CompanyID ai_debug_company; + int redraw_timer; + + AIDebugWindow(const WindowDesc *desc, WindowNumber number) : Window(desc, number) + { + /* Disable the companies who are not active or not an AI */ + for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) { + this->SetWidgetDisabledState(i + AID_WIDGET_COMPANY_BUTTON_START, !IsValidCompanyID(i) || !GetCompany(i)->is_ai); + } + this->DisableWidget(AID_WIDGET_RELOAD_TOGGLE); + + this->vscroll.cap = 14; + this->vscroll.pos = 0; + this->resize.step_height = 12; + + if (ai_debug_company != INVALID_COMPANY) this->LowerWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START); + + this->FindWindowPlacementAndResize(desc); + } + + virtual void OnPaint() + { + /* Check if the currently selected company is still active. */ + if (ai_debug_company == INVALID_COMPANY || !IsValidCompanyID(ai_debug_company)) { + if (ai_debug_company != INVALID_COMPANY) { + /* Raise and disable the widget for the previous selection. */ + this->RaiseWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START); + this->DisableWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START); + + ai_debug_company = INVALID_COMPANY; + } + + for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) { + if (IsValidCompanyID(i) && GetCompany(i)->is_ai) { + /* Lower the widget corresponding to this company. */ + this->LowerWidget(i + AID_WIDGET_COMPANY_BUTTON_START); + + ai_debug_company = i; + break; + } + } + } + + /* Update "Reload AI" button */ + this->SetWidgetDisabledState(AID_WIDGET_RELOAD_TOGGLE, ai_debug_company == INVALID_COMPANY); + + /* Draw standard stuff */ + this->DrawWidgets(); + + /* If there are no active companies, don't display anything else. */ + if (ai_debug_company == INVALID_COMPANY) return; + + /* Paint the company icons */ + for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) { + if (!IsValidCompanyID(i) || !GetCompany(i)->is_ai) { + /* Check if we have the company as an active company */ + if (!this->IsWidgetDisabled(i + AID_WIDGET_COMPANY_BUTTON_START)) { + /* Bah, company gone :( */ + this->DisableWidget(i + AID_WIDGET_COMPANY_BUTTON_START); + + /* We need a repaint */ + this->SetDirty(); + } + continue; + } + + /* Check if we have the company marked as inactive */ + if (this->IsWidgetDisabled(i + AID_WIDGET_COMPANY_BUTTON_START)) { + /* New AI! Yippie :p */ + this->EnableWidget(i + AID_WIDGET_COMPANY_BUTTON_START); + + /* We need a repaint */ + this->SetDirty(); + } + + byte x = (i == ai_debug_company) ? 1 : 0; + DrawCompanyIcon(i, (i % 8) * 37 + 13 + x, (i < 8 ? 0 : 13) + 16 + x); + } + + /* Draw the AI name */ + AIInfo *info = GetCompany(ai_debug_company)->ai_info; + assert(info != NULL); + DoDrawString(info->GetName(), 7, 47, TC_BLACK); + + CompanyID old_company = _current_company; + _current_company = ai_debug_company; + AILog::LogData *log = (AILog::LogData *)AIObject::GetLogPointer(); + _current_company = old_company; + + SetVScrollCount(this, (log == NULL) ? 0 : log->used); + this->InvalidateWidget(AID_WIDGET_SCROLLBAR); + if (log == NULL) return; + + int y = 6; + for (int i = this->vscroll.pos; i < (this->vscroll.cap + this->vscroll.pos); i++) { + uint pos = (log->count + log->pos - i) % log->count; + if (log->lines[pos] == NULL) break; + + uint colour; + switch (log->type[pos]) { + case AILog::LOG_SQ_INFO: colour = TC_BLACK; break; + case AILog::LOG_SQ_ERROR: colour = TC_RED; break; + case AILog::LOG_INFO: colour = TC_BLACK; break; + case AILog::LOG_WARNING: colour = TC_YELLOW; break; + case AILog::LOG_ERROR: colour = TC_RED; break; + default: colour = TC_BLACK; break; + } + + DoDrawStringTruncated(log->lines[pos], 7, this->widget[AID_WIDGET_LOG_PANEL].top + y, colour, this->widget[AID_WIDGET_LOG_PANEL].right - this->widget[AID_WIDGET_LOG_PANEL].left - 14); + y += 12; + } + } + + virtual void OnClick(Point pt, int widget) + { + /* Check which button is clicked */ + if (IsInsideMM(widget, AID_WIDGET_COMPANY_BUTTON_START, AID_WIDGET_COMPANY_BUTTON_END + 1)) { + /* Is it no on disable? */ + if (!this->IsWidgetDisabled(widget)) { + this->RaiseWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START); + ai_debug_company = (CompanyID)(widget - AID_WIDGET_COMPANY_BUTTON_START); + this->LowerWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START); + this->SetDirty(); + } + } + if (widget == AID_WIDGET_RELOAD_TOGGLE && !this->IsWidgetDisabled(widget)) { + /* First kill the company of the AI, then start a new one. This should start the current AI again */ + DoCommandP(0, 2, ai_debug_company, CMD_COMPANY_CTRL); + DoCommandP(0, 1, 0, CMD_COMPANY_CTRL); + } + } + + virtual void OnTimeout() + { + this->RaiseWidget(AID_WIDGET_RELOAD_TOGGLE); + this->SetDirty(); + } + + virtual void OnInvalidateData(int data = 0) + { + if (data == -1 || ai_debug_company == data) this->SetDirty(); + } + + virtual void OnResize(Point new_size, Point delta) + { + this->vscroll.cap += delta.y / (int)this->resize.step_height; + } +}; + +CompanyID AIDebugWindow::ai_debug_company = INVALID_COMPANY; + +static const Widget _ai_debug_widgets[] = { +{ WWT_CLOSEBOX, RESIZE_NONE, COLOUR_GREY, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // AID_WIDGET_CLOSEBOX +{ WWT_CAPTION, RESIZE_RIGHT, COLOUR_GREY, 11, 298, 0, 13, STR_AI_DEBUG, STR_018C_WINDOW_TITLE_DRAG_THIS}, // AID_WIDGET_CAPTION +{ WWT_PANEL, RESIZE_RIGHT, COLOUR_GREY, 0, 298, 14, 40, 0x0, STR_NULL}, // AID_WIDGET_VIEW + +{ WWT_PANEL, RESIZE_RIGHT, COLOUR_GREY, 0, 149, 41, 60, 0x0, STR_AI_DEBUG_NAME_TIP}, // AID_WIDGET_NAME_TEXT +{ WWT_PUSHTXTBTN, RESIZE_LR, COLOUR_GREY, 150, 298, 41, 60, STR_AI_DEBUG_RELOAD, STR_AI_DEBUG_RELOAD_TIP}, // AID_WIDGET_RELOAD_TOGGLE +{ WWT_PANEL, RESIZE_RB, COLOUR_GREY, 0, 286, 61, 240, 0x0, STR_NULL}, // AID_WIDGET_LOG_PANEL +{ WWT_SCROLLBAR, RESIZE_LRB, COLOUR_GREY, 287, 298, 61, 228, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST}, // AID_WIDGET_SCROLLBAR +/* As this is WIP, leave the next few so we can work a bit with the GUI */ +{ WWT_EMPTY, RESIZE_NONE, COLOUR_GREY, 0, 298, 101, 120, 0x0, STR_NULL}, // AID_WIDGET_UNUSED_1 +{ WWT_EMPTY, RESIZE_NONE, COLOUR_GREY, 0, 298, 121, 140, 0x0, STR_NULL}, // AID_WIDGET_UNUSED_2 +{ WWT_EMPTY, RESIZE_NONE, COLOUR_GREY, 0, 298, 141, 160, 0x0, STR_NULL}, // AID_WIDGET_UNUSED_3 +{ WWT_EMPTY, RESIZE_NONE, COLOUR_GREY, 0, 298, 161, 180, 0x0, STR_NULL}, // AID_WIDGET_UNUSED_4 +{ WWT_EMPTY, RESIZE_NONE, COLOUR_GREY, 0, 298, 181, 200, 0x0, STR_NULL}, // AID_WIDGET_UNUSED_5 +{ WWT_EMPTY, RESIZE_NONE, COLOUR_GREY, 0, 298, 201, 220, 0x0, STR_NULL}, // AID_WIDGET_UNUSED_6 +{ WWT_EMPTY, RESIZE_NONE, COLOUR_GREY, 0, 298, 221, 240, 0x0, STR_NULL}, // AID_WIDGET_UNUSED_7 + +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 2, 38, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, // AID_WIDGET_COMPANY_BUTTON_START +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 39, 75, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 76, 112, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 113, 149, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 150, 186, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 187, 223, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 224, 260, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 261, 297, 14, 26, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 2, 38, 27, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 39, 75, 27, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 76, 112, 27, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 113, 149, 27, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 150, 186, 27, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 187, 223, 27, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, +{ WWT_PANEL, RESIZE_NONE, COLOUR_GREY, 224, 260, 27, 39, 0x0, STR_704F_CLICK_HERE_TO_TOGGLE_COMPANY}, // AID_WIDGET_COMPANY_BUTTON_END +{ WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_GREY, 287, 298, 229, 240, STR_NULL, STR_RESIZE_BUTTON}, +{ WIDGETS_END}, +}; + +static const WindowDesc _ai_debug_desc = { + WDP_AUTO, WDP_AUTO, 299, 241, 299, 241, + WC_AI_DEBUG, WC_NONE, + WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE, + _ai_debug_widgets +}; + +void ShowAIDebugWindow() +{ + if (!_networking || _network_server) { + AllocateWindowDescFront<AIDebugWindow>(&_ai_debug_desc, 0); + } else { + ShowErrorMessage(INVALID_STRING_ID, STR_AI_DEBUG_SERVER_ONLY, 0, 0); + } +} diff --git a/src/ai/ai_gui.hpp b/src/ai/ai_gui.hpp new file mode 100644 index 000000000..c5c7ad2ee --- /dev/null +++ b/src/ai/ai_gui.hpp @@ -0,0 +1,10 @@ +/* $Id$ */ + +/** @file ai_gui.hpp Window for configuring the AIs */ + +#ifndef AI_GUI_HPP +#define AI_GUI_HPP + +void ShowAIDebugWindow(); + +#endif /* AI_GUI_HPP */ diff --git a/src/ai/ai_info.cpp b/src/ai/ai_info.cpp new file mode 100644 index 000000000..0eead0302 --- /dev/null +++ b/src/ai/ai_info.cpp @@ -0,0 +1,319 @@ +/* $Id$ */ + +/** @file ai_info.cpp Implementation of AIFileInfo */ + +#include "../stdafx.h" +#include "../core/alloc_func.hpp" + +#include <squirrel.h> +#include "../script/squirrel.hpp" +#include "../script/squirrel_helper.hpp" +#include "../script/squirrel_class.hpp" +#include "../script/squirrel_std.hpp" +#include "ai.hpp" +#include "ai_info.hpp" +#include "ai_scanner.hpp" +#include "api/ai_controller.hpp" +#include "../settings_type.h" +#include "../openttd.h" + +AIFileInfo::~AIFileInfo() +{ + this->engine->ReleaseObject(this->SQ_instance); + free((void *)this->author); + free((void *)this->name); + free((void *)this->description); + free((void *)this->date); + free((void *)this->instance_name); + free(this->script_name); + free(this->dir_name); + free(this->SQ_instance); +} + +const char *AIFileInfo::GetAuthor() +{ + if (this->author == NULL) this->author = this->engine->CallStringMethodStrdup(*this->SQ_instance, "GetAuthor"); + return this->author; +} + +const char *AIFileInfo::GetName() +{ + if (this->name == NULL) this->name = this->engine->CallStringMethodStrdup(*this->SQ_instance, "GetName"); + return this->name; +} + +const char *AIFileInfo::GetDescription() +{ + if (this->description == NULL) this->description = this->engine->CallStringMethodStrdup(*this->SQ_instance, "GetDescription"); + return this->description; +} + +int AIFileInfo::GetVersion() +{ + return this->engine->CallIntegerMethod(*this->SQ_instance, "GetVersion"); +} + +void AIFileInfo::GetSettings() +{ + this->engine->CallMethod(*this->SQ_instance, "GetSettings", NULL, -1); +} + +const char *AIFileInfo::GetDate() +{ + if (this->date == NULL) this->date = this->engine->CallStringMethodStrdup(*this->SQ_instance, "GetDate"); + return this->date; +} + +const char *AIFileInfo::GetInstanceName() +{ + if (this->instance_name == NULL) this->instance_name = this->engine->CallStringMethodStrdup(*this->SQ_instance, "CreateInstance"); + return this->instance_name; +} + +bool AIFileInfo::AllowStartup() +{ + return true; +} + +const char *AIFileInfo::GetDirName() +{ + return this->dir_name; +} + +const char *AIFileInfo::GetScriptName() +{ + return this->script_name; +} + +void AIFileInfo::CheckMethods(SQInteger *res, const char *name) +{ + if (!this->engine->MethodExists(*this->SQ_instance, name)) { + char error[1024]; + snprintf(error, sizeof(error), "your AIFileInfo doesn't have the method '%s'", name); + this->engine->ThrowError(error); + *res = SQ_ERROR; + } +} + +/* static */ SQInteger AIFileInfo::Constructor(HSQUIRRELVM vm, AIFileInfo *info) +{ + SQInteger res = 0; + + /* Set some basic info from the parent */ + info->SQ_instance = MallocT<SQObject>(1); + Squirrel::GetInstance(vm, info->SQ_instance, 2); + /* Make sure the instance stays alive over time */ + sq_addref(vm, info->SQ_instance); + info->base = ((AIScanner *)Squirrel::GetGlobalPointer(vm)); + info->engine = info->base->GetEngine(); + + /* Check if all needed fields are there */ + info->CheckMethods(&res, "GetAuthor"); + info->CheckMethods(&res, "GetName"); + info->CheckMethods(&res, "GetDescription"); + info->CheckMethods(&res, "GetVersion"); + info->CheckMethods(&res, "GetDate"); + info->CheckMethods(&res, "CreateInstance"); + + /* Abort if one method was missing */ + if (res != 0) return res; + + info->script_name = strdup(info->base->GetCurrentScript()); + info->dir_name = strdup(info->base->GetCurrentDirName()); + + return 0; +} + +/* static */ SQInteger AIInfo::Constructor(HSQUIRRELVM vm) +{ + /* Get the AIInfo */ + SQUserPointer instance; + sq_getinstanceup(vm, 2, &instance, 0); + AIInfo *info = (AIInfo *)instance; + + SQInteger res = AIFileInfo::Constructor(vm, info); + if (res != 0) return res; + + AIConfigItem config; + config.name = strdup("start_date"); + config.description = strdup("The amount of months after the start of the last AI, this AI will start (give or take)."); + config.min_value = 0; + config.max_value = 120; + config.easy_value = 48; + config.medium_value = 24; + config.hard_value = 12; + config.custom_value = 24; + config.flags = AICONFIG_NONE; + info->config_list.push_back(config); + + /* Check if we have settings */ + if (info->engine->MethodExists(*info->SQ_instance, "GetSettings")) { + info->GetSettings(); + } + + /* Remove the link to the real instance, else it might get deleted by RegisterAI() */ + sq_setinstanceup(vm, 2, NULL); + /* Register the AI to the base system */ + info->base->RegisterAI(info); + return 0; +} + +/* static */ SQInteger AIInfo::DummyConstructor(HSQUIRRELVM vm) +{ + /* Get the AIInfo */ + SQUserPointer instance; + sq_getinstanceup(vm, 2, &instance, 0); + AIInfo *info = (AIInfo *)instance; + + SQInteger res = AIFileInfo::Constructor(vm, info); + if (res != 0) return res; + + /* Remove the link to the real instance, else it might get deleted by RegisterAI() */ + sq_setinstanceup(vm, 2, NULL); + /* Register the AI to the base system */ + info->base->SetDummyAI(info); + return 0; +} + +AIInfo::~AIInfo() +{ + /* Free all allocated strings */ + for (AIConfigItemList::iterator it = this->config_list.begin(); it != this->config_list.end(); it++) { + free((char *)(*it).name); + } + this->config_list.clear(); +} + +SQInteger AIInfo::AddSetting(HSQUIRRELVM vm) +{ + AIConfigItem config; + memset(&config, 0, sizeof(config)); + int items = 0; + + /* Read the table, and find all properties we care about */ + sq_pushnull(vm); + while (SQ_SUCCEEDED(sq_next(vm, -2))) { + const SQChar *sqkey; + sq_getstring(vm, -2, &sqkey); + const char *key = FS2OTTD(sqkey); + + if (strcmp(key, "name") == 0) { + const SQChar *sqvalue; + sq_getstring(vm, -1, &sqvalue); + config.name = strdup(FS2OTTD(sqvalue)); + char *s; + /* Don't allow '=' and ',' in configure setting names, as we need those + * 2 chars to nicely store the settings as a string. */ + while ((s = (char *)strchr(config.name, '=')) != NULL) *s = '_'; + while ((s = (char *)strchr(config.name, ',')) != NULL) *s = '_'; + items |= 0x001; + } else if (strcmp(key, "description") == 0) { + const SQChar *sqdescription; + sq_getstring(vm, -1, &sqdescription); + config.description = strdup(FS2OTTD(sqdescription)); + items |= 0x002; + } else if (strcmp(key, "min_value") == 0) { + SQInteger res; + sq_getinteger(vm, -1, &res); + config.min_value = res; + items |= 0x004; + } else if (strcmp(key, "max_value") == 0) { + SQInteger res; + sq_getinteger(vm, -1, &res); + config.max_value = res; + items |= 0x008; + } else if (strcmp(key, "easy_value") == 0) { + SQInteger res; + sq_getinteger(vm, -1, &res); + config.easy_value = res; + items |= 0x010; + } else if (strcmp(key, "medium_value") == 0) { + SQInteger res; + sq_getinteger(vm, -1, &res); + config.medium_value = res; + items |= 0x020; + } else if (strcmp(key, "hard_value") == 0) { + SQInteger res; + sq_getinteger(vm, -1, &res); + config.hard_value = res; + items |= 0x040; + } else if (strcmp(key, "custom_value") == 0) { + SQInteger res; + sq_getinteger(vm, -1, &res); + config.custom_value = res; + items |= 0x080; + } else if (strcmp(key, "flags") == 0) { + SQInteger res; + sq_getinteger(vm, -1, &res); + config.flags = (AIConfigFlags)res; + items |= 0x100; + } else { + char error[1024]; + snprintf(error, sizeof(error), "unknown setting property '%s'", key); + this->engine->ThrowError(error); + return SQ_ERROR; + } + + sq_pop(vm, 2); + } + sq_pop(vm, 1); + + /* Make sure all properties are defined */ + if (items != 0x1FF) { + char error[1024]; + snprintf(error, sizeof(error), "please define all properties of a setting"); + this->engine->ThrowError(error); + return SQ_ERROR; + } + + this->config_list.push_back(config); + return 0; +} + +const AIConfigItemList *AIInfo::GetConfigList() +{ + return &this->config_list; +} + +int AIInfo::GetSettingDefaultValue(const char *name) +{ + for (AIConfigItemList::iterator it = this->config_list.begin(); it != this->config_list.end(); it++) { + if (strcmp((*it).name, name) != 0) continue; + /* The default value depends on the difficulty level */ + switch ((_game_mode == GM_MENU) ? _settings_newgame.difficulty.diff_level : _settings_game.difficulty.diff_level) { + case 0: return (*it).easy_value; + case 1: return (*it).medium_value; + case 2: return (*it).hard_value; + case 3: return (*it).custom_value; + default: NOT_REACHED(); + } + } + + /* There is no such setting */ + return -1; +} + +/* static */ SQInteger AILibrary::Constructor(HSQUIRRELVM vm) +{ + /* Create a new AIFileInfo */ + AILibrary *library = new AILibrary(); + + SQInteger res = AIFileInfo::Constructor(vm, library); + if (res != 0) return res; + + /* Register the Library to the base system */ + library->base->RegisterLibrary(library); + + return 0; +} + +/* static */ SQInteger AILibrary::Import(HSQUIRRELVM vm) +{ + SQConvert::SQAutoFreePointers ptr; + const char *library = GetParam(SQConvert::ForceType<const char *>(), vm, 2, &ptr); + const char *class_name = GetParam(SQConvert::ForceType<const char *>(), vm, 3, &ptr); + int version = GetParam(SQConvert::ForceType<int>(), vm, 4, &ptr); + + if (!AI::ImportLibrary(library, class_name, version, vm)) return -1; + return 1; +} diff --git a/src/ai/ai_info.hpp b/src/ai/ai_info.hpp new file mode 100644 index 000000000..d50b89584 --- /dev/null +++ b/src/ai/ai_info.hpp @@ -0,0 +1,153 @@ +/* $Id$ */ + +/** @file ai_info.hpp AIInfo keeps track of all information of an AI, like Author, Description, ... */ + +#ifndef AI_INFO +#define AI_INFO + +#include <list> +#include "api/ai_object.hpp" + +enum AIConfigFlags { + AICONFIG_NONE = 0x0, + AICONFIG_RANDOM = 0x1, //!< When randomizing the AI, pick any value between min_value and max_value when on custom difficulty setting. + AICONFIG_BOOLEAN = 0x2, //!< This value is a boolean (either 0 (false) or 1 (true) ). +}; + +struct AIConfigItem { + const char *name; //!< The name of the configuration setting. + const char *description; //!< The description of the configuration setting. + int min_value; //!< The minimal value this configuration setting can have. + int max_value; //!< The maximal value this configuration setting can have. + int custom_value; //!< The default value on custom difficulty setting. + int easy_value; //!< The default value on easy difficulty setting. + int medium_value; //!< The default value on medium difficulty setting. + int hard_value; //!< The default value on hard difficulty setting. + AIConfigFlags flags; //!< Flags for the configuration setting. +}; + +typedef std::list<AIConfigItem> AIConfigItemList; + +class AIFileInfo : public AIObject { +public: + friend class AIInfo; + friend class AILibrary; + + AIFileInfo() : author(NULL), name(NULL), description(NULL), date(NULL), instance_name(NULL) {}; + ~AIFileInfo(); + + /** + * Get the Author of the AI. + */ + const char *GetAuthor(); + + /** + * Get the Name of the AI. + */ + const char *GetName(); + + /** + * Get the description of the AI. + */ + const char *GetDescription(); + + /** + * Get the version of the AI. + */ + int GetVersion(); + + /** + * Get the settings of the AI. + */ + void GetSettings(); + + /** + * Get the date of the AI. + */ + const char *GetDate(); + + /** + * Get the name of the instance of the AI to create. + */ + const char *GetInstanceName(); + + /** + * Check if we can start this AI. + */ + bool AllowStartup(); + + /** + * Get the name of the dir this AI is in. + */ + const char *GetDirName(); + + /** + * Get the complete script name of this AI. + */ + const char *GetScriptName(); + + /** + * Check if a given method exists. + */ + void CheckMethods(SQInteger *res, const char *name); + + /** + * Process the creation of a FileInfo object. + */ + static SQInteger Constructor(HSQUIRRELVM vm, AIFileInfo *info); + +private: + class Squirrel *engine; + HSQOBJECT *SQ_instance; + char *script_name; + char *dir_name; + class AIScanner *base; + const char *author; + const char *name; + const char *description; + const char *date; + const char *instance_name; +}; + +class AIInfo : public AIFileInfo { +public: + static const char *GetClassName() { return "AIInfo"; } + + ~AIInfo(); + + /** + * Create an AI, using this AIInfo as start-template. + */ + static SQInteger Constructor(HSQUIRRELVM vm); + static SQInteger DummyConstructor(HSQUIRRELVM vm); + + /** + * Get the config list for this AI. + */ + const AIConfigItemList *GetConfigList(); + + /** + * Set a setting. + */ + SQInteger AddSetting(HSQUIRRELVM vm); + + /** + * Get the default value for a setting. + */ + int GetSettingDefaultValue(const char *name); + +private: + AIConfigItemList config_list; +}; + +class AILibrary : public AIFileInfo { +public: + /** + * Create an AI, using this AIInfo as start-template. + */ + static SQInteger Constructor(HSQUIRRELVM vm); + + static SQInteger Import(HSQUIRRELVM vm); +}; + +#endif /* AI_INFO */ diff --git a/src/ai/ai_info_dummy.cpp b/src/ai/ai_info_dummy.cpp new file mode 100644 index 000000000..a060a0a65 --- /dev/null +++ b/src/ai/ai_info_dummy.cpp @@ -0,0 +1,66 @@ +/* $Id$ */ + +#include <squirrel.h> +#include "../stdafx.h" + +/* The reason this exists in C++, is that a user can trash his ai/ dir, + * leaving no AIs available. The complexity to solve this is insane, and + * therefor the alternative is used, and make sure there is always an AI + * available, no matter what the situation is. By defining it in C++, there + * is simply now way a user can delete it, and therefor safe to use. It has + * to be noted that this AI is complete invisible for the user, and impossible + * to select manual. It is a fail-over in case no AIs are available. + */ + +const SQChar dummy_script_info[] = _SC(" \n\ +class DummyAI extends AIInfo { \n\ + function GetAuthor() { return \"OpenTTD NoAI Developers Team\"; } \n\ + function GetName() { return \"DummyAI\"; } \n\ + function GetDescription() { return \"A Dummy AI that is loaded when your ai/ dir is empty\"; }\n\ + function GetVersion() { return 1; } \n\ + function GetDate() { return \"2008-07-26\"; } \n\ + function CreateInstance() { return \"DummyAI\"; } \n\ +} \n\ + \n\ +RegisterDummyAI(DummyAI()); \n\ +"); + +const SQChar dummy_script[] = _SC(" \n\ +class DummyAI extends AIController { \n\ + function Start() { \n\ + AILog.Error(\"No suitable AI found to load.\"); \n\ + AILog.Error(\"This AI is a dummy AI and won't do anything.\"); \n\ + AILog.Error(\"Please add one or several AIs in your ai/ directory.\"); \n\ + } \n\ +} \n\ +"); + +void AI_CreateAIInfoDummy(HSQUIRRELVM vm) +{ + sq_pushroottable(vm); + + /* Load and run the script */ + if (SQ_SUCCEEDED(sq_compilebuffer(vm, dummy_script_info, scstrlen(dummy_script_info), _SC("dummy"), SQTrue))) { + sq_push(vm, -2); + if (SQ_SUCCEEDED(sq_call(vm, 1, SQFalse, SQTrue))) { + sq_pop(vm, 1); + return; + } + } + NOT_REACHED(); +} + +void AI_CreateAIDummy(HSQUIRRELVM vm) +{ + sq_pushroottable(vm); + + /* Load and run the script */ + if (SQ_SUCCEEDED(sq_compilebuffer(vm, dummy_script, scstrlen(dummy_script), _SC("dummy"), SQTrue))) { + sq_push(vm, -2); + if (SQ_SUCCEEDED(sq_call(vm, 1, SQFalse, SQTrue))) { + sq_pop(vm, 1); + return; + } + } + NOT_REACHED(); +} diff --git a/src/ai/ai_instance.cpp b/src/ai/ai_instance.cpp new file mode 100644 index 000000000..a0612498d --- /dev/null +++ b/src/ai/ai_instance.cpp @@ -0,0 +1,628 @@ +/* $Id$ */ + +/** @file ai_instance.cpp Implementation of AIInstance. */ + +#include "../stdafx.h" +#include "../openttd.h" +#include "../debug.h" +#include "../company_func.h" +#include "../core/alloc_func.hpp" +#include "../string_func.h" +#include "../settings_type.h" +#include "../company_base.h" +#include "../saveload/saveload.h" +#include "table/strings.h" + +#include <squirrel.h> +#include "../script/squirrel.hpp" +#include "../script/squirrel_helper.hpp" +#include "../script/squirrel_class.hpp" +#include "../script/squirrel_std.hpp" +#include "ai.hpp" +#include "api/ai_controller.hpp" +#include "ai_info.hpp" +#include "ai_storage.hpp" +#include "ai_instance.hpp" + +/* Convert all AI related classes to Squirrel data. + * Note: this line a marker in squirrel_export.sh. Do not change! */ +#include "api/ai_abstractlist.hpp.sq" +#include "api/ai_accounting.hpp.sq" +#include "api/ai_airport.hpp.sq" +#include "api/ai_base.hpp.sq" +#include "api/ai_bridge.hpp.sq" +#include "api/ai_bridgelist.hpp.sq" +#include "api/ai_cargo.hpp.sq" +#include "api/ai_cargolist.hpp.sq" +#include "api/ai_company.hpp.sq" +#include "api/ai_controller.hpp.sq" +#include "api/ai_date.hpp.sq" +#include "api/ai_depotlist.hpp.sq" +#include "api/ai_engine.hpp.sq" +#include "api/ai_enginelist.hpp.sq" +#include "api/ai_error.hpp.sq" +#include "api/ai_event.hpp.sq" +#include "api/ai_event_types.hpp.sq" +#include "api/ai_execmode.hpp.sq" +#include "api/ai_gamesettings.hpp.sq" +#include "api/ai_group.hpp.sq" +#include "api/ai_grouplist.hpp.sq" +#include "api/ai_industry.hpp.sq" +#include "api/ai_industrylist.hpp.sq" +#include "api/ai_industrytype.hpp.sq" +#include "api/ai_industrytypelist.hpp.sq" +#include "api/ai_list.hpp.sq" +#include "api/ai_log.hpp.sq" +#include "api/ai_map.hpp.sq" +#include "api/ai_marine.hpp.sq" +#include "api/ai_order.hpp.sq" +#include "api/ai_rail.hpp.sq" +#include "api/ai_railtypelist.hpp.sq" +#include "api/ai_road.hpp.sq" +#include "api/ai_sign.hpp.sq" +#include "api/ai_station.hpp.sq" +#include "api/ai_stationlist.hpp.sq" +#include "api/ai_subsidy.hpp.sq" +#include "api/ai_subsidylist.hpp.sq" +#include "api/ai_testmode.hpp.sq" +#include "api/ai_tile.hpp.sq" +#include "api/ai_tilelist.hpp.sq" +#include "api/ai_town.hpp.sq" +#include "api/ai_townlist.hpp.sq" +#include "api/ai_tunnel.hpp.sq" +#include "api/ai_vehicle.hpp.sq" +#include "api/ai_vehiclelist.hpp.sq" + +/* static */ AIInstance *AIInstance::current_instance = NULL; + +AIStorage::~AIStorage() +{ + /* Free our pointers */ + if (event_data != NULL) AIEventController::FreeEventPointer(); + if (log_data != NULL) AILog::FreeLogPointer(); +} + +static void PrintFunc(bool error_msg, const SQChar *message) +{ + /* Convert to OpenTTD internal capable string */ + AIController::Print(error_msg, FS2OTTD(message)); +} + +AIInstance::AIInstance(AIInfo *info) : + controller(NULL), + storage(NULL), + engine(NULL), + instance(NULL), + is_started(false), + is_dead(false), + suspend(0), + callback(NULL) +{ + /* Set the instance already, so we can use AIObject::Set commands */ + GetCompany(_current_company)->ai_instance = this; + AIInstance::current_instance = this; + + this->controller = new AIController(); + this->storage = new AIStorage(); + this->engine = new Squirrel(); + this->engine->SetPrintFunction(&PrintFunc); + + /* The import method is available at a very early stage */ + this->engine->AddMethod("import", &AILibrary::Import, 4, "?ssi"); + + /* Register the AIController */ + SQAIController_Register(this->engine); + + /* Load and execute the script for this AI */ + const char *script_name = info->GetScriptName(); + if (strcmp(script_name, "%_dummy") == 0) { + extern void AI_CreateAIDummy(HSQUIRRELVM vm); + AI_CreateAIDummy(this->engine->GetVM()); + } else if (!this->engine->LoadScript(script_name)) { + this->Died(); + return; + } + + /* Create the main-class */ + this->instance = MallocT<SQObject>(1); + if (!this->engine->CreateClassInstance(info->GetInstanceName(), this->controller, this->instance)) { + this->Died(); + return; + } + + /* Register the API functions and classes */ + this->RegisterAPI(); + + /* Run the constructor if it exists. Don't allow any DoCommands in it. */ + if (this->engine->MethodExists(*this->instance, "constructor")) { + AIObject::SetAllowDoCommand(false); + this->engine->CallMethod(*this->instance, "constructor"); + AIObject::SetAllowDoCommand(true); + } +} + +AIInstance::~AIInstance() +{ + if (engine != NULL) delete this->engine; + delete this->storage; + delete this->controller; + free(this->instance); +} + +void AIInstance::RegisterAPI() +{ +/* Register all classes */ + squirrel_register_std(this->engine); + SQAIAbstractList_Register(this->engine); + SQAIAccounting_Register(this->engine); + SQAIAirport_Register(this->engine); + SQAIBase_Register(this->engine); + SQAIBridge_Register(this->engine); + SQAIBridgeList_Register(this->engine); + SQAIBridgeList_Length_Register(this->engine); + SQAICargo_Register(this->engine); + SQAICargoList_Register(this->engine); + SQAICargoList_IndustryAccepting_Register(this->engine); + SQAICargoList_IndustryProducing_Register(this->engine); + SQAICompany_Register(this->engine); + SQAIDate_Register(this->engine); + SQAIDepotList_Register(this->engine); + SQAIEngine_Register(this->engine); + SQAIEngineList_Register(this->engine); + SQAIError_Register(this->engine); + SQAIEvent_Register(this->engine); + SQAIEventCompanyBankrupt_Register(this->engine); + SQAIEventCompanyInTrouble_Register(this->engine); + SQAIEventCompanyMerger_Register(this->engine); + SQAIEventCompanyNew_Register(this->engine); + SQAIEventController_Register(this->engine); + SQAIEventEngineAvailable_Register(this->engine); + SQAIEventEnginePreview_Register(this->engine); + SQAIEventIndustryClose_Register(this->engine); + SQAIEventIndustryOpen_Register(this->engine); + SQAIEventStationFirstVehicle_Register(this->engine); + SQAIEventSubsidyAwarded_Register(this->engine); + SQAIEventSubsidyExpired_Register(this->engine); + SQAIEventSubsidyOffer_Register(this->engine); + SQAIEventSubsidyOfferExpired_Register(this->engine); + SQAIEventTest_Register(this->engine); + SQAIEventVehicleCrashed_Register(this->engine); + SQAIEventVehicleLost_Register(this->engine); + SQAIEventVehicleUnprofitable_Register(this->engine); + SQAIEventVehicleWaitingInDepot_Register(this->engine); + SQAIExecMode_Register(this->engine); + SQAIGameSettings_Register(this->engine); + SQAIGroup_Register(this->engine); + SQAIGroupList_Register(this->engine); + SQAIIndustry_Register(this->engine); + SQAIIndustryList_Register(this->engine); + SQAIIndustryList_CargoAccepting_Register(this->engine); + SQAIIndustryList_CargoProducing_Register(this->engine); + SQAIIndustryType_Register(this->engine); + SQAIIndustryTypeList_Register(this->engine); + SQAIList_Register(this->engine); + SQAILog_Register(this->engine); + SQAIMap_Register(this->engine); + SQAIMarine_Register(this->engine); + SQAIOrder_Register(this->engine); + SQAIRail_Register(this->engine); + SQAIRailTypeList_Register(this->engine); + SQAIRoad_Register(this->engine); + SQAISign_Register(this->engine); + SQAIStation_Register(this->engine); + SQAIStationList_Register(this->engine); + SQAIStationList_Vehicle_Register(this->engine); + SQAISubsidy_Register(this->engine); + SQAISubsidyList_Register(this->engine); + SQAITestMode_Register(this->engine); + SQAITile_Register(this->engine); + SQAITileList_Register(this->engine); + SQAITileList_IndustryAccepting_Register(this->engine); + SQAITileList_IndustryProducing_Register(this->engine); + SQAITileList_StationType_Register(this->engine); + SQAITown_Register(this->engine); + SQAITownList_Register(this->engine); + SQAITunnel_Register(this->engine); + SQAIVehicle_Register(this->engine); + SQAIVehicleList_Register(this->engine); + SQAIVehicleList_Station_Register(this->engine); + + this->engine->SetGlobalPointer(this->engine); +} + +void AIInstance::Continue() +{ + assert(this->suspend < 0); + this->suspend = -this->suspend - 1; +} + +void AIInstance::Died() +{ + DEBUG(ai, 0, "The AI died unexpectedly."); + this->is_dead = true; + + delete this->engine; + this->engine = NULL; +} + +void AIInstance::GameLoop() +{ + if (this->is_dead) return; + this->controller->ticks++; + + if (this->suspend < -1) this->suspend++; // Multiplayer suspend, increase up to -1. + if (this->suspend < 0) return; // Multiplayer suspend, wait for Continue(). + if (--this->suspend > 0) return; // Singleplayer suspend, decrease to 0. + + /* If there is a callback to call, call that first */ + if (this->callback != NULL) { + try { + this->callback(this); + } catch (AI_VMSuspend e) { + this->suspend = e.GetSuspendTime(); + this->callback = e.GetSuspendCallback(); + + return; + } + } + + this->suspend = 0; + this->callback = NULL; + + if (!this->is_started) { + /* Start the AI by calling Start() */ + try { + if (!this->engine->CallMethod(*this->instance, "Start", _settings_game.ai.ai_max_opcode_till_suspend)) this->Died(); + } catch (AI_VMSuspend e) { + this->suspend = e.GetSuspendTime(); + this->callback = e.GetSuspendCallback(); + } + + this->is_started = true; + return; + } + + /* Continue the VM */ + try { + if (!this->engine->Resume(_settings_game.ai.ai_max_opcode_till_suspend)) this->Died(); + } catch (AI_VMSuspend e) { + this->suspend = e.GetSuspendTime(); + this->callback = e.GetSuspendCallback(); + } +} + +/* static */ void AIInstance::DoCommandReturn(AIInstance *instance) +{ + instance->engine->InsertResult(AIObject::GetLastCommandRes()); +} + +/* static */ void AIInstance::DoCommandReturnVehicleID(AIInstance *instance) +{ + instance->engine->InsertResult(AIObject::GetNewVehicleID()); +} + +/* static */ void AIInstance::DoCommandReturnSignID(AIInstance *instance) +{ + instance->engine->InsertResult(AIObject::GetNewSignID()); +} + +/* static */ void AIInstance::DoCommandReturnGroupID(AIInstance *instance) +{ + instance->engine->InsertResult(AIObject::GetNewGroupID()); +} + +/* static */ AIStorage *AIInstance::GetStorage() +{ + assert(IsValidCompanyID(_current_company) && !IsHumanCompany(_current_company)); + return GetCompany(_current_company)->ai_instance->storage; +} + +/* + * All data is stored in the following format: + * First 1 byte indicating if there is a data blob at all. + * 1 byte indicating the type of data. + * The data itself, this differs per type: + * - integer: a binary representation of the integer (int32). + * - string: First one byte with the string length, then a 0-terminated char + * array. The string can't be longer then 255 bytes (including + * terminating '\0'). + * - array: All data-elements of the array are saved recursive in this + * format, and ended with an element of the type + * SQSL_ARRAY_TABLE_END. + * - table: All key/value pairs are saved in this format (first key 1, then + * value 1, then key 2, etc.). All keys and values can have an + * arbitrary type (as long as it is supported by the save function + * of course). The table is ended with an element of the type + * SQSL_ARRAY_TABLE_END. + * - bool: A single byte with value 1 representing true and 0 false. + * - null: No data. + */ + +/** The type of the data that follows in the savegame. */ +enum SQSaveLoadType { + SQSL_INT = 0x00, ///< The following data is an integer. + SQSL_STRING = 0x01, ///< The following data is an string. + SQSL_ARRAY = 0x02, ///< The following data is an array. + SQSL_TABLE = 0x03, ///< The following data is an table. + SQSL_BOOL = 0x04, ///< The following data is a boolean. + SQSL_NULL = 0x05, ///< A null variable. + SQSL_ARRAY_TABLE_END = 0xFF, ///< Marks the end of an array or table, no data follows. +}; + +static byte _ai_sl_byte; + +static const SaveLoad _ai_byte[] = { + SLEG_VAR(_ai_sl_byte, SLE_UINT8), + SLE_END() +}; + +enum { + AISAVE_MAX_DEPTH = 25, ///< The maximum recursive depth for items stored in the savegame. +}; + +/* static */ bool AIInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test) +{ + if (max_depth == 0) { + AILog::Error("Savedata can only be nested to 5 deep. No data saved."); + return false; + } + + switch (sq_gettype(vm, index)) { + case OT_INTEGER: { + if (!test) { + _ai_sl_byte = SQSL_INT; + SlObject(NULL, _ai_byte); + } + SQInteger res; + sq_getinteger(vm, index, &res); + if (!test) { + int value = (int)res; + SlArray(&value, 1, SLE_INT32); + } + return true; + } + + case OT_STRING: { + if (!test) { + _ai_sl_byte = SQSL_STRING; + SlObject(NULL, _ai_byte); + } + const SQChar *res; + sq_getstring(vm, index, &res); + /* @bug if a string longer than 512 characters is given to FS2OTTD, the + * internal buffer overflows. */ + const char *buf = FS2OTTD(res); + size_t len = strlen(buf) + 1; + if (len >= 255) { + AILog::Error("Maximum string length is 254 chars. No data saved."); + return false; + } + if (!test) { + _ai_sl_byte = (byte)len; + SlObject(NULL, _ai_byte); + SlArray((void*)buf, len, SLE_CHAR); + } + return true; + } + + case OT_ARRAY: { + if (!test) { + _ai_sl_byte = SQSL_ARRAY; + SlObject(NULL, _ai_byte); + } + sq_pushnull(vm); + while (SQ_SUCCEEDED(sq_next(vm, index - 1))) { + /* Store the value */ + bool res = SaveObject(vm, -1, max_depth - 1, test); + sq_pop(vm, 2); + if (!res) { + sq_pop(vm, 1); + return false; + } + } + sq_pop(vm, 1); + if (!test) { + _ai_sl_byte = SQSL_ARRAY_TABLE_END; + SlObject(NULL, _ai_byte); + } + return true; + } + + case OT_TABLE: { + if (!test) { + _ai_sl_byte = SQSL_TABLE; + SlObject(NULL, _ai_byte); + } + sq_pushnull(vm); + while (SQ_SUCCEEDED(sq_next(vm, index - 1))) { + /* Store the key + value */ + bool res = SaveObject(vm, -2, max_depth - 1, test) && SaveObject(vm, -1, max_depth - 1, test); + sq_pop(vm, 2); + if (!res) { + sq_pop(vm, 1); + return false; + } + } + sq_pop(vm, 1); + if (!test) { + _ai_sl_byte = SQSL_ARRAY_TABLE_END; + SlObject(NULL, _ai_byte); + } + return true; + } + + case OT_BOOL: { + if (!test) { + _ai_sl_byte = SQSL_BOOL; + SlObject(NULL, _ai_byte); + } + SQBool res; + sq_getbool(vm, index, &res); + if (!test) { + _ai_sl_byte = res ? 1 : 0; + SlObject(NULL, _ai_byte); + } + return true; + } + + case OT_NULL: { + if (!test) { + _ai_sl_byte = SQSL_NULL; + SlObject(NULL, _ai_byte); + } + return true; + } + + default: + AILog::Error("You tried to save unsupported type. No data saved."); + return false; + } +} + +/* static */ void AIInstance::SaveEmpty() +{ + _ai_sl_byte = 0; + SlObject(NULL, _ai_byte); +} + +void AIInstance::Save() +{ + /* Don't save data if the AI didn't start yet. */ + if (this->engine == NULL) { + SaveEmpty(); + return; + } + + /* We don't want to be interrupted during the save function. */ + AIObject::SetAllowDoCommand(false); + + HSQOBJECT savedata; + if (this->engine->MethodExists(*this->instance, "Save")) { + this->engine->CallMethod(*this->instance, "Save", &savedata); + if (!sq_istable(savedata)) { + AILog::Error("Save function should return a table."); + _ai_sl_byte = 0; + SlObject(NULL, _ai_byte); + AIObject::SetAllowDoCommand(true); + return; + } + HSQUIRRELVM vm = this->engine->GetVM(); + sq_pushobject(vm, savedata); + if (SaveObject(vm, -1, AISAVE_MAX_DEPTH, true)) { + _ai_sl_byte = 1; + SlObject(NULL, _ai_byte); + SaveObject(vm, -1, AISAVE_MAX_DEPTH, false); + } else { + _ai_sl_byte = 0; + SlObject(NULL, _ai_byte); + } + sq_pop(vm, 1); + } else { + AILog::Warning("Save function is not implemented"); + _ai_sl_byte = 0; + SlObject(NULL, _ai_byte); + } + + AIObject::SetAllowDoCommand(true); +} + +/* static */ bool AIInstance::LoadObjects(HSQUIRRELVM vm) +{ + SlObject(NULL, _ai_byte); + switch (_ai_sl_byte) { + case SQSL_INT: { + int value; + SlArray(&value, 1, SLE_INT32); + if (vm != NULL) sq_pushinteger(vm, (SQInteger)value); + return true; + } + + case SQSL_STRING: { + SlObject(NULL, _ai_byte); + static char buf[256]; + SlArray(buf, _ai_sl_byte, SLE_CHAR); + if (vm != NULL) sq_pushstring(vm, OTTD2FS(buf), -1); + return true; + } + + case SQSL_ARRAY: { + if (vm != NULL) sq_newarray(vm, 0); + while (LoadObjects(vm)) { + if (vm != NULL) sq_arrayappend(vm, -2); + /* The value is popped from the stack by squirrel. */ + } + return true; + } + + case SQSL_TABLE: { + if (vm != NULL) sq_newtable(vm); + while (LoadObjects(vm)) { + LoadObjects(vm); + if (vm != NULL) sq_rawset(vm, -3); + /* The key (-2) and value (-1) are popped from the stack by squirrel. */ + } + return true; + } + + case SQSL_BOOL: { + SlObject(NULL, _ai_byte); + if (vm != NULL) sq_pushinteger(vm, (SQBool)(_ai_sl_byte != 0)); + return true; + } + + case SQSL_NULL: { + if (vm != NULL) sq_pushnull(vm); + return true; + } + + case SQSL_ARRAY_TABLE_END: { + return false; + } + + default: NOT_REACHED(); + } +} + +/* static */ void AIInstance::LoadEmpty() +{ + SlObject(NULL, _ai_byte); + /* Check if there was anything saved at all. */ + if (_ai_sl_byte == 0) return; + + LoadObjects(NULL); +} + +bool AIInstance::Load() +{ + HSQUIRRELVM vm = (this->engine == NULL) ? NULL : this->engine->GetVM(); + + SlObject(NULL, _ai_byte); + /* Check if there was anything saved at all. */ + if (_ai_sl_byte == 0) return true; + AIObject::SetAllowDoCommand(false); + + if (vm != NULL) { + /* Go to the instance-root */ + sq_pushobject(vm, *this->instance); + /* Find the function-name inside the script */ + sq_pushstring(vm, OTTD2FS("Load"), -1); + if (SQ_FAILED(sq_get(vm, -2))) sq_pushnull(vm); + sq_pushobject(vm, *this->instance); + } + + LoadObjects(vm); + + if (this->engine != NULL) { + if (this->engine->MethodExists(*this->instance, "Load")) { + sq_call(vm, 2, SQFalse, SQFalse); + } else { + AILog::Warning("Loading failed: there was data for the AI to load, but the AI does not have a Load() function."); + } + } + + /* Pop 1) the object instance, 2) the function name, 3) the instance again, 4) the table. */ + if (vm != NULL) sq_pop(vm, 4); + + AIObject::SetAllowDoCommand(true); + return true; +} diff --git a/src/ai/ai_instance.hpp b/src/ai/ai_instance.hpp new file mode 100644 index 000000000..d953c3915 --- /dev/null +++ b/src/ai/ai_instance.hpp @@ -0,0 +1,140 @@ +/* $Id$ */ + +/** @file ai_instance.hpp The AIInstance tracks an AI. */ + +#ifndef AI_INSTANCE_HPP +#define AI_INSTANCE_HPP + +/** + * The callback function when an AI suspends. + */ +typedef void (AISuspendCallbackProc)(class AIInstance *instance); + +/** + * A throw-class that is given when the VM wants to suspend. + */ +class AI_VMSuspend { +public: + AI_VMSuspend(int time, AISuspendCallbackProc *callback) : + time(time), + callback(callback) + {} + + int GetSuspendTime() { return time; } + AISuspendCallbackProc *GetSuspendCallback() { return callback; } + +private: + int time; + AISuspendCallbackProc *callback; +}; + +class AIInstance { +public: + AIInstance(class AIInfo *info); + ~AIInstance(); + + /** + * An AI in multiplayer waits for the server to handle his DoCommand. + * It keeps waiting for this until this function is called. + */ + void Continue(); + + /** + * Run the GameLoop of an AI. + */ + void GameLoop(); + + /** + * Get the storage of this AI. + */ + static class AIStorage *GetStorage(); + + /** + * Return a true/false reply for a DoCommand. + */ + static void DoCommandReturn(AIInstance *instance); + + /** + * Return a VehicleID reply for a DoCommand. + */ + static void DoCommandReturnVehicleID(AIInstance *instance); + + /** + * Return a SignID reply for a DoCommand. + */ + static void DoCommandReturnSignID(AIInstance *instance); + + /** + * Return a GroupID reply for a DoCommand. + */ + static void DoCommandReturnGroupID(AIInstance *instance); + + /** + * Get the controller attached to the instance. + */ + class AIController *GetController() { return controller; } + + /** + * Call the AI Save function and save all data in the savegame. + */ + void Save(); + + /** + * Don't save any data in the savegame. + */ + static void SaveEmpty(); + + /** + * Load data from a savegame and call the AI Load function if it + * exists. + * @return True if the loading was successfull. + */ + bool Load(); + + /** + * Load and discard data from a savegame. + */ + static void LoadEmpty(); + +private: + static class AIInstance *current_instance; //!< Static current AIInstance, so we can register AIs. + + class AIController *controller; + class AIStorage *storage; + class Squirrel *engine; + SQObject *instance; + + bool is_started; + bool is_dead; + int suspend; + AISuspendCallbackProc *callback; + + /** + * Register all API functions to the VM. + */ + void RegisterAPI(); + + /** + * Tell the AI it died. + */ + void Died(); + + /** + * Save one object (int / string / arrray / table) to the savegame. + * @param index The index on the squirrel stack of the element to save. + * @param max_depth The maximum depth recursive arrays / tables will be stored + * with before an error is returned. + * @param test If true, don't really store the data but only check if it is + * valid. + * @return True if the saving was successfull. + */ + static bool SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test); + + /** + * Load all objects from a savegame. + * @return True if the loading was successfull. + */ + static bool LoadObjects(HSQUIRRELVM vm); +}; + +#endif /* AI_INSTANCE_HPP */ diff --git a/src/ai/ai_scanner.cpp b/src/ai/ai_scanner.cpp new file mode 100644 index 000000000..68d53660d --- /dev/null +++ b/src/ai/ai_scanner.cpp @@ -0,0 +1,395 @@ +/* $Id$ */ + +/** @file ai_scanner.cpp allows scanning AI scripts */ + +#include "../stdafx.h" +#include "../debug.h" +#include "../openttd.h" +#include "../string_func.h" +#include "../fileio_func.h" +#include "../fios.h" +#include "../network/network.h" +#include "../core/random_func.hpp" +#include <sys/types.h> +#include <sys/stat.h> + +#include <squirrel.h> +#include "../script/squirrel.hpp" +#include "../script/squirrel_helper.hpp" +#include "../script/squirrel_class.hpp" +#include "ai.hpp" +#include "ai_info.hpp" +#include "ai_scanner.hpp" +#include "api/ai_controller.hpp" + +void AIScanner::ScanDir(const char *dirname, bool library_dir, char *library_depth) +{ + extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb); + extern bool FiosIsHiddenFile(const struct dirent *ent); + + struct stat sb; + struct dirent *dirent; + DIR *dir; + char d_name[MAX_PATH]; + char script_name[MAX_PATH]; + dir = ttd_opendir(dirname); + /* Dir not found, so do nothing */ + if (dir == NULL) return; + + /* Walk all dirs trying to find a dir in which 'main.nut' exists */ + while ((dirent = readdir(dir)) != NULL) { + ttd_strlcpy(d_name, FS2OTTD(dirent->d_name), sizeof(d_name)); + + /* Valid file, not '.' or '..', not hidden */ + if (!FiosIsValidFile(dirname, dirent, &sb)) continue; + if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue; + if (FiosIsHiddenFile(dirent) && strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) != 0) continue; + + if (S_ISDIR(sb.st_mode)) { + /* Create the full-length script-name */ + ttd_strlcpy(script_name, dirname, sizeof(script_name)); + ttd_strlcat(script_name, d_name, sizeof(script_name)); + ttd_strlcat(script_name, PATHSEP, sizeof(script_name)); + + if (library_dir && library_depth == NULL) { + ScanDir(script_name, library_dir, d_name); + continue; + } + } + if (S_ISREG(sb.st_mode)) { + if (library_dir) continue; + + char *ext = strrchr(d_name, '.'); + if (ext == NULL || strcasecmp(ext, ".tar") != 0) continue; + + /* Create the full path to the tarfile */ + char tarname[MAX_PATH]; + ttd_strlcpy(tarname, dirname, sizeof(tarname)); + ttd_strlcat(tarname, d_name, sizeof(tarname)); + + /* Now the script-name starts with the first dir in the tar */ + if (FioTarFirstDir(tarname) == NULL) continue; + ttd_strlcpy(script_name, "%aitar%", sizeof(tarname)); + ttd_strlcat(script_name, FioTarFirstDir(tarname), sizeof(script_name)); + FioTarAddLink(script_name, FioTarFirstDir(tarname)); + + /* The name of the AI is the name of the tar minus the .tar */ + *ext = '\0'; + } + + if (!library_dir) { + /* We look for the file 'info.nut' inside the AI dir.. if it doesn't exists, it isn't an AI */ + ttd_strlcat(script_name, "info.nut", sizeof(script_name)); + if (FioCheckFileExists(script_name, AI_DIR)) { + char load_script[MAX_PATH]; + ttd_strlcpy(load_script, script_name, sizeof(load_script)); + + /* Remove the 'info.nut' part and replace it with 'main.nut' */ + script_name[strlen(script_name) - 8] = '\0'; + ttd_strlcat(script_name, "main.nut", sizeof(script_name)); + + DEBUG(ai, 6, "[script] Loading script '%s' for AI handling", load_script); + this->current_script = script_name; + this->current_dir = d_name; + this->engine->LoadScript(load_script); + } + } else { + /* We look for the file 'library.nut' inside the library dir.. */ + ttd_strlcat(script_name, "library.nut", sizeof(script_name)); + if (FioCheckFileExists(script_name, AI_DIR)) { + char load_script[MAX_PATH]; + char dir_name[MAX_PATH]; + char d_name_2[MAX_PATH]; + /* In case the directory has a dot in it, ignore it, as that is the + * indicator for multiple versions of the same library */ + ttd_strlcpy(d_name_2, d_name, sizeof(d_name_2)); + char *e = strrchr(d_name_2, '.'); + if (e != NULL) *e = '\0'; + + ttd_strlcpy(load_script, script_name, sizeof(load_script)); + ttd_strlcpy(dir_name, library_depth, sizeof(dir_name)); + ttd_strlcat(dir_name, ".", sizeof(dir_name)); + ttd_strlcat(dir_name, d_name_2, sizeof(dir_name)); + + /* Remove the 'library.nut' part and replace it with 'main.nut' */ + script_name[strlen(script_name) - 11] = '\0'; + ttd_strlcat(script_name, "main.nut", sizeof(script_name)); + + DEBUG(ai, 6, "[script] Loading script '%s' for Squirrel library", load_script); + this->current_script = script_name; + this->current_dir = dir_name; + this->engine->LoadScript(load_script); + } + } + } + closedir(dir); +} + +void AIScanner::ScanAIDir() +{ + char buf[MAX_PATH]; + Searchpath sp; + + FOR_ALL_SEARCHPATHS(sp) { + FioAppendDirectory(buf, MAX_PATH, sp, AI_DIR); + if (FileExists(buf)) this->ScanDir(buf, false); + ttd_strlcat(buf, "library" PATHSEP, MAX_PATH); + if (FileExists(buf)) this->ScanDir(buf, true); + } +} + +void AIScanner::RescanAIDir() +{ + extern void ScanForTarFiles(); + ScanForTarFiles(); + this->ScanAIDir(); +} + +AIScanner::AIScanner() +{ + this->engine = new Squirrel(); + + /* Create the AIInfo class, and add the RegisterAI function */ + DefSQClass <AIInfo> SQAIInfo("AIInfo"); + SQAIInfo.PreRegister(engine); + SQAIInfo.AddConstructor<void (AIInfo::*)(), 1>(engine, "x"); + SQAIInfo.DefSQAdvancedMethod(this->engine, &AIInfo::AddSetting, "AddSetting"); + SQAIInfo.PostRegister(engine); + this->engine->AddMethod("RegisterAI", &AIInfo::Constructor, 2, "tx"); + /* Create the AILibrary class, and add the RegisterLibrary function */ + this->engine->AddClassBegin("AILibrary"); + this->engine->AddClassEnd(); + this->engine->AddMethod("RegisterLibrary", &AILibrary::Constructor, 2, "tx"); + + /* Mark this class as global pointer */ + this->engine->SetGlobalPointer(this); + + /* Scan the AI dir for scripts */ + this->ScanAIDir(); + + /* Create the dummy AI */ + this->engine->AddMethod("RegisterDummyAI", &AIInfo::DummyConstructor, 2, "tx"); + this->current_script = (char *)"%_dummy"; + this->current_dir = (char *)"%_dummy"; + extern void AI_CreateAIInfoDummy(HSQUIRRELVM vm); + AI_CreateAIInfoDummy(this->engine->GetVM()); +} + +AIScanner::~AIScanner() +{ + AIInfoList::iterator it = this->info_list.begin(); + for (; it != this->info_list.end(); it++) { + AIInfo *i = (*it).second; + delete i; + } + + delete this->engine; +} + +bool AIScanner::ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm, AIController *controller) +{ + /* Internally we store libraries as 'library.version' */ + char library_name[1024]; + snprintf(library_name, sizeof(library_name), "%s.%d", library, version); + + /* Check if the library + version exists */ + AILibraryList::iterator iter = this->library_list.find(library_name); + if (iter == this->library_list.end()) { + char error[1024]; + + /* Now see if the version doesn't exist, or the library */ + iter = this->library_list.find(library); + if (iter == this->library_list.end()) { + snprintf(error, sizeof(error), "couldn't find library '%s'", library); + } else { + snprintf(error, sizeof(error), "this AI is expecting library '%s' to be version %d, but the latest available is version %d", library, version, (*iter).second->GetVersion()); + } + sq_throwerror(vm, OTTD2FS(error)); + return false; + } + + /* Get the current table/class we belong to */ + HSQOBJECT parent; + sq_getstackobj(vm, 1, &parent); + + char fake_class[1024]; + int next_number; + /* Check if we already have this library loaded.. if so, fill fake_class + * with the class-name it is nested in */ + if (!controller->LoadedLibrary(library_name, &next_number, &fake_class[0], sizeof(fake_class))) { + /* Create a new fake internal name */ + snprintf(fake_class, sizeof(fake_class), "_internalNA%d", next_number); + + /* Load the library in a 'fake' namespace, so we can link it to the name the user requested */ + sq_pushroottable(vm); + sq_pushstring(vm, OTTD2FS(fake_class), -1); + sq_newclass(vm, SQFalse); + /* Load the library */ + if (!Squirrel::LoadScript(vm, (*iter).second->GetScriptName(), false)) { + char error[1024]; + snprintf(error, sizeof(error), "there was a compile error when importing '%s' version %d", library, version); + sq_throwerror(vm, OTTD2FS(error)); + return false; + } + /* Create the fake class */ + sq_newslot(vm, -3, SQFalse); + sq_pop(vm, 1); + + controller->AddLoadedLibrary(library_name, fake_class); + } + + /* Find the real class inside the fake class (like 'sets.Vector') */ + sq_pushroottable(vm); + sq_pushstring(vm, OTTD2FS(fake_class), -1); + if (SQ_FAILED(sq_get(vm, -2))) { + sq_throwerror(vm, _SC("internal error assigning library class")); + return false; + } + sq_pushstring(vm, OTTD2FS((*iter).second->GetInstanceName()), -1); + if (SQ_FAILED(sq_get(vm, -2))) { + char error[1024]; + snprintf(error, sizeof(error), "unable to find class '%s' in the library '%s' version %d", (*iter).second->GetInstanceName(), library, version); + sq_throwerror(vm, OTTD2FS(error)); + return false; + } + HSQOBJECT obj; + sq_getstackobj(vm, -1, &obj); + sq_pop(vm, 3); + + if (StrEmpty(class_name)) { + sq_pushobject(vm, obj); + return true; + } + + /* Now link the name the user wanted to our 'fake' class */ + sq_pushobject(vm, parent); + sq_pushstring(vm, OTTD2FS(class_name), -1); + sq_pushobject(vm, obj); + sq_newclass(vm, SQTrue); + sq_newslot(vm, -3, SQFalse); + sq_pop(vm, 1); + + sq_pushobject(vm, obj); + return true; +} + +void AIScanner::RegisterLibrary(AILibrary *library) +{ + const char *ai_name_without_version = library->GetDirName(); + char ai_name[1024]; + snprintf(ai_name, sizeof(ai_name), "%s.%d", library->GetDirName(), library->GetVersion()); + + /* Check if we register twice; than the first always wins */ + if (this->library_list.find(ai_name) != this->library_list.end()) { + /* In case they are not the same dir, give a warning */ + if (strcasecmp(library->GetScriptName(), this->library_list[ai_name]->GetScriptName()) != 0) { + DEBUG(ai, 0, "Registering two libraries with the same name"); + DEBUG(ai, 0, " 1: %s", this->library_list[ai_name]->GetScriptName()); + DEBUG(ai, 0, " 2: %s", library->GetScriptName()); + DEBUG(ai, 0, "The first is taking precedence"); + } + /* Delete the new AILibrary, as we will be using the old one */ + delete library; + return; + } + + this->library_list[strdup(ai_name)] = library; + /* Insert the global name too, so we if the library is known at all */ + if (this->library_list.find(ai_name_without_version) == this->library_list.end()) { + this->library_list[strdup(ai_name_without_version)] = library; + } else if (this->library_list[ai_name_without_version]->GetVersion() < library->GetVersion()) { + this->library_list[ai_name_without_version] = library; + } +} + +void AIScanner::RegisterAI(AIInfo *info) +{ + const char *ai_name = info->GetDirName(); + + /* Check if we register twice; than the first always wins */ + if (this->info_list.find(ai_name) != this->info_list.end()) { + /* In case they are not the same dir, give a warning */ + if (strcasecmp(info->GetScriptName(), this->info_list[ai_name]->GetScriptName()) != 0) { + DEBUG(ai, 0, "Registering two AIs with the same name"); + DEBUG(ai, 0, " 1: %s", this->info_list[ai_name]->GetScriptName()); + DEBUG(ai, 0, " 2: %s", info->GetScriptName()); + DEBUG(ai, 0, "The first is taking precedence"); + } + /* Delete the new AIInfo, as we will be using the old one */ + delete info; + return; + } + + this->info_list[strdup(ai_name)] = info; +} + +void AIScanner::UnregisterAI(AIInfo *info) +{ + this->info_list.erase(info->GetDirName()); +} + +AIInfo *AIScanner::SelectRandomAI() +{ + if (this->info_list.size() == 0) { + DEBUG(ai, 0, "No suitable AI found, loading 'dummy' AI."); + return this->info_dummy; + } + + /* Find a random AI */ + uint pos; + if (_networking) pos = InteractiveRandomRange((uint16)this->info_list.size()); + else pos = RandomRange((uint16)this->info_list.size()); + + /* Find the Nth item from the array */ + AIInfoList::iterator it = this->info_list.begin(); + for (; pos > 0; pos--) it++; + AIInfoList::iterator first_it = it; + AIInfo *i = (*it).second; + + if (!i->AllowStartup()) { + /* We can't start this AI, try to find the next best */ + do { + it++; + if (it == this->info_list.end()) it = this->info_list.begin(); + /* Back at the beginning? We can't start an AI. */ + if (first_it == it) { + DEBUG(ai, 0, "No suitable AI found, loading 'dummy' AI."); + return this->info_dummy; + } + + i = (*it).second; + } while (!i->AllowStartup()); + } + return i; +} + +AIInfo *AIScanner::FindAI(const char *name) +{ + if (this->info_list.size() == 0) return NULL; + if (name == NULL) return NULL; + + AIInfoList::iterator it = this->info_list.begin(); + for (; it != this->info_list.end(); it++) { + AIInfo *i = (*it).second; + + if (strcasecmp(name, (*it).first) == 0 && i->AllowStartup()) { + return i; + } + } + + return NULL; +} + +char *AIScanner::GetAIConsoleList(char *p, const char *last) +{ + p += seprintf(p, last, "List of AIs:\n"); + AIInfoList::iterator it = this->info_list.begin(); + for (; it != this->info_list.end(); it++) { + AIInfo *i = (*it).second; + if (!i->AllowStartup()) continue; + p += seprintf(p, last, "%10s: %s\n", (*it).first, i->GetDescription()); + } + p += seprintf(p, last, "\n"); + + return p; +} diff --git a/src/ai/ai_scanner.hpp b/src/ai/ai_scanner.hpp new file mode 100644 index 000000000..2801f556a --- /dev/null +++ b/src/ai/ai_scanner.hpp @@ -0,0 +1,102 @@ +/* $Id$ */ + +/** @file ai_scanner.hpp declarations of the class for AI scanner */ + +#ifndef AI_SCANNER_HPP +#define AI_SCANNER_HPP + +#include <map> + +class AIScanner { +public: + AIScanner(); + ~AIScanner(); + + /** + * Import a library inside the Squirrel VM. + */ + bool ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm, class AIController *controller); + + /** + * Register a library to be put in the available list. + */ + void RegisterLibrary(class AILibrary *library); + + /** + * Register an AI to be put in the available list. + */ + void RegisterAI(class AIInfo *info); + + void SetDummyAI(class AIInfo *info) { this->info_dummy = info; } + + /** + * Remove an AI from the available list. + */ + void UnregisterAI(class AIInfo *info); + + /** + * Select a Random AI. + */ + class AIInfo *SelectRandomAI(); + + /** + * Find an AI by name. + */ + class AIInfo *FindAI(const char *name); + + /** + * Get the list of available AIs for the console. + */ + char *GetAIConsoleList(char *p, const char *last); + + /** + * Get the list of all registered AIs. + */ + const AIInfoList *GetAIInfoList() { return &this->info_list; } + + /** + * Get the engine of the main squirrel handler (it indexes all avialable squirrels). + */ + class Squirrel *GetEngine() { return this->engine; } + + /** + * Get the current script the ScanDir is looking at. + */ + const char *GetCurrentScript() { return this->current_script; } + + /** + * Get the directory of the current script the ScanDir is looking at. + */ + const char *GetCurrentDirName() { return this->current_dir; } + + /** + * Rescan the AI dir for scripts. + */ + void RescanAIDir(); + +private: + typedef std::map<const char *, class AILibrary *, ltstr> AILibraryList; + + /** + * Scan the AI dir for scripts. + */ + void ScanAIDir(); + + /** + * Scan a dir for AIs. + * For non-library-scan, if an AI is found, AIInfo is created, and the AI + * is registered to the central system. + * For library-scan, if a library is found, AILibrary is created, and the + * library is registered to the central system. + */ + void ScanDir(const char *dirname, bool library_dir, char *library_depth = NULL); + + AIInfoList info_list; + AIInfo *info_dummy; + AILibraryList library_list; + class Squirrel *engine; + char *current_script; + char *current_dir; +}; + +#endif /* AI_SCANNER_HPP */ diff --git a/src/ai/ai_storage.hpp b/src/ai/ai_storage.hpp new file mode 100644 index 000000000..2e85046b4 --- /dev/null +++ b/src/ai/ai_storage.hpp @@ -0,0 +1,78 @@ +/* $Id$ */ + +/** @file ai_storage.hpp Defines AIStorage and includes all files required for it. */ + +#ifndef AI_STORAGE_HPP +#define AI_STORAGE_HPP + +#include "../command_func.h" +#include "../map_func.h" +#include "../network/network.h" +#include "../company_func.h" +#include "../signs_func.h" +#include "../tunnelbridge.h" +#include "../vehicle_func.h" +#include "../group.h" + +#include <vector> + +/** + * The callback function for Mode-classes. + */ +typedef bool (AIModeProc)(TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCost costs); + +/** + * The storage for each AI. It keeps track of important information. + */ +class AIStorage { +friend class AIObject; +private: + AIModeProc *mode; //!< The current build mode we are int. + class AIObject *mode_instance; //!< The instance belonging to the current build mode. + + uint delay; //!< The ticks of delay each DoCommand has. + bool allow_do_command; //!< Is the usage of DoCommands restricted? + + CommandCost costs; //!< The costs the AI is tracking. + Money last_cost; //!< The last cost of the command. + uint last_error; //!< The last error of the command. + bool last_command_res; //!< The last result of the command. + + VehicleID new_vehicle_id; //!< The ID of the new Vehicle. + SignID new_sign_id; //!< The ID of the new Sign. + TileIndex new_tunnel_endtile; //!< The TileIndex of the new Tunnel. + GroupID new_group_id; //!< The ID of the new Group. + + std::vector<int> callback_value; //!< The values which need to survive a callback. + + RoadType road_type; //!< The current roadtype we build. + RailType rail_type; //!< The current railtype we build. + + void *event_data; //!< Pointer to the event data storage. + void *log_data; //!< Pointer to the log data storage. + +public: + AIStorage() : + mode (NULL), + mode_instance (NULL), + delay (1), + allow_do_command (true), + /* costs (can't be set) */ + last_cost (0), + last_error (STR_NULL), + last_command_res (true), + new_vehicle_id (0), + new_sign_id (0), + new_tunnel_endtile(INVALID_TILE), + new_group_id (0), + /* calback_value (can't be set) */ + road_type (INVALID_ROADTYPE), + rail_type (INVALID_RAILTYPE), + event_data (NULL), + log_data (NULL) + { } + + ~AIStorage(); +}; + +#endif /* AI_STORAGE_HPP */ diff --git a/src/ai/api/Doxyfile b/src/ai/api/Doxyfile new file mode 100644 index 000000000..ff5d7a511 --- /dev/null +++ b/src/ai/api/Doxyfile @@ -0,0 +1,244 @@ +# Doxyfile 1.5.4 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "OpenTTD NoAI API " +PROJECT_NUMBER = +OUTPUT_DIRECTORY = ../../../docs/aidocs/ +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class " \ + "The $name widget " \ + "The $name file " \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = ./ +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 2 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +TYPEDEF_HIDES_STRUCT = NO +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = YES +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = YES +INTERNAL_DOCS = YES +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = NO +GENERATE_TESTLIST = NO +GENERATE_BUGLIST = NO +GENERATE_DEPRECATEDLIST= NO +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = NO +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_FORMAT = "$file:$line: $text " +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = . +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.h \ + *.hpp +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = GetClassName DECLARE_ENUM_AS_BIT_SET DECLARE_POSTFIX_INCREMENT +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = NO +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +HTML_DYNAMIC_SECTIONS = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 1 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = YES +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = DOXYGEN_SKIP +EXPAND_AS_DEFINED = DEF_COMMAND +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = openttd.tag +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = NO +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/src/ai/api/ai_abstractlist.cpp b/src/ai/api/ai_abstractlist.cpp new file mode 100644 index 000000000..d447311ac --- /dev/null +++ b/src/ai/api/ai_abstractlist.cpp @@ -0,0 +1,818 @@ +/* $Id$ */ + +/** @file ai_abstractlist.cpp Implementation of AIAbstractList. */ + +#include <squirrel.h> +#include "ai_abstractlist.hpp" +#include "../../debug.h" +#include "../../core/alloc_func.hpp" + +/** + * Base class for any AIAbstractList sorter. + */ +class AIAbstractListSorter { +protected: + AIAbstractList *list; + +public: + /** + * Virtual dtor, needed to mute warnings. + */ + virtual ~AIAbstractListSorter() { } + + /** + * Get the first item of the sorter. + */ + virtual int32 Begin() = 0; + + /** + * Stop iterating a sorter. + */ + virtual void End() = 0; + + /** + * Get the next item of the sorter. + */ + virtual int32 Next() = 0; + + /** + * See if there is a next item of the sorter. + */ + virtual bool HasNext() = 0; + + /** + * Callback from the list if an item gets removed. + */ + virtual void Remove(int item) = 0; +}; + +/** + * Sort by value, ascending. + */ +class AIAbstractListSorterValueAscending : public AIAbstractListSorter { +private: + AIAbstractList::AIAbstractListBucket::iterator bucket_iter; + AIAbstractList::AIItemList *bucket_list; + AIAbstractList::AIItemList::iterator bucket_list_iter; + bool has_no_more_items; + int32 item_next; + +public: + AIAbstractListSorterValueAscending(AIAbstractList *list) + { + this->list = list; + this->End(); + } + + int32 Begin() + { + if (this->list->buckets.empty()) return 0; + this->has_no_more_items = false; + + this->bucket_iter = this->list->buckets.begin(); + this->bucket_list = &(*this->bucket_iter).second; + this->bucket_list_iter = this->bucket_list->begin(); + this->item_next = *this->bucket_list_iter; + + int32 item_current = this->item_next; + FindNext(); + return item_current; + } + + void End() + { + this->bucket_list = NULL; + this->has_no_more_items = true; + this->item_next = 0; + } + + void FindNext() + { + if (this->bucket_list == NULL) { + this->has_no_more_items = true; + return; + } + + this->bucket_list_iter++; + if (this->bucket_list_iter == this->bucket_list->end()) { + this->bucket_iter++; + if (this->bucket_iter == this->list->buckets.end()) { + this->bucket_list = NULL; + return; + } + this->bucket_list = &(*this->bucket_iter).second; + this->bucket_list_iter = this->bucket_list->begin(); + } + this->item_next = *this->bucket_list_iter; + } + + int32 Next() + { + if (!this->HasNext()) return 0; + + int32 item_current = this->item_next; + FindNext(); + return item_current; + } + + void Remove(int item) + { + if (!this->HasNext()) return; + + /* If we remove the 'next' item, skip to the next */ + if (item == this->item_next) { + FindNext(); + return; + } + } + + bool HasNext() + { + return !(this->list->buckets.empty() || this->has_no_more_items); + } +}; + +/** + * Sort by value, descending. + */ +class AIAbstractListSorterValueDescending : public AIAbstractListSorter { +private: + AIAbstractList::AIAbstractListBucket::iterator bucket_iter; + AIAbstractList::AIItemList *bucket_list; + AIAbstractList::AIItemList::iterator bucket_list_iter; + bool has_no_more_items; + int32 item_next; + +public: + AIAbstractListSorterValueDescending(AIAbstractList *list) + { + this->list = list; + this->End(); + } + + int32 Begin() + { + if (this->list->buckets.empty()) return 0; + this->has_no_more_items = false; + + /* Go to the end of the bucket-list */ + this->bucket_iter = this->list->buckets.begin(); + for (size_t i = this->list->buckets.size(); i > 1; i--) this->bucket_iter++; + this->bucket_list = &(*this->bucket_iter).second; + + /* Go to the end of the items in the bucket */ + this->bucket_list_iter = this->bucket_list->begin(); + for (size_t i = this->bucket_list->size(); i > 1; i--) this->bucket_list_iter++; + this->item_next = *this->bucket_list_iter; + + int32 item_current = this->item_next; + FindNext(); + return item_current; + } + + void End() { + this->bucket_list = NULL; + this->has_no_more_items = true; + this->item_next = 0; + } + + void FindNext() + { + if (this->bucket_list == NULL) { + this->has_no_more_items = true; + return; + } + + if (this->bucket_list_iter == this->bucket_list->begin()) { + if (this->bucket_iter == this->list->buckets.begin()) { + this->bucket_list = NULL; + return; + } + this->bucket_iter--; + this->bucket_list = &(*this->bucket_iter).second; + /* Go to the end of the items in the bucket */ + this->bucket_list_iter = this->bucket_list->begin(); + for (size_t i = this->bucket_list->size(); i > 1; i--) this->bucket_list_iter++; + } else { + this->bucket_list_iter--; + } + this->item_next = *this->bucket_list_iter; + } + + int32 Next() + { + if (!this->HasNext()) return 0; + + int32 item_current = this->item_next; + FindNext(); + return item_current; + } + + void Remove(int item) + { + if (!this->HasNext()) return; + + /* If we remove the 'next' item, skip to the next */ + if (item == this->item_next) { + FindNext(); + return; + } + } + + bool HasNext() + { + return !(this->list->buckets.empty() || this->has_no_more_items); + } +}; + +/** + * Sort by item, ascending. + */ +class AIAbstractListSorterItemAscending : public AIAbstractListSorter { +private: + AIAbstractList::AIAbstractListMap::iterator item_iter; + bool has_no_more_items; + int32 item_next; + +public: + AIAbstractListSorterItemAscending(AIAbstractList *list) + { + this->list = list; + this->End(); + } + + int32 Begin() + { + if (this->list->items.empty()) return 0; + this->has_no_more_items = false; + + this->item_iter = this->list->items.begin(); + this->item_next = (*this->item_iter).first; + + int32 item_current = this->item_next; + FindNext(); + return item_current; + } + + void End() + { + this->has_no_more_items = true; + } + + void FindNext() + { + if (this->item_iter == this->list->items.end()) { + this->has_no_more_items = true; + return; + } + this->item_iter++; + if (this->item_iter != this->list->items.end()) item_next = (*this->item_iter).first; + } + + int32 Next() + { + if (!this->HasNext()) return 0; + + int32 item_current = this->item_next; + FindNext(); + return item_current; + } + + void Remove(int item) { + if (!this->HasNext()) return; + + /* If we remove the 'next' item, skip to the next */ + if (item == this->item_next) { + FindNext(); + return; + } + } + + bool HasNext() + { + return !(this->list->items.empty() || this->has_no_more_items); + } +}; + +/** + * Sort by item, descending. + */ +class AIAbstractListSorterItemDescending : public AIAbstractListSorter { +private: + AIAbstractList::AIAbstractListMap::iterator item_iter; + bool has_no_more_items; + int32 item_next; + +public: + AIAbstractListSorterItemDescending(AIAbstractList *list) + { + this->list = list; + this->End(); + } + + int32 Begin() + { + if (this->list->items.empty()) return 0; + this->has_no_more_items = false; + + this->item_iter = this->list->items.begin(); + for (size_t i = this->list->items.size(); i > 1; i--) this->item_iter++; + this->item_next = (*this->item_iter).first; + + int32 item_current = this->item_next; + FindNext(); + return item_current; + } + + void End() + { + this->has_no_more_items = true; + } + + void FindNext() + { + if (this->item_iter == this->list->items.end()) { + this->has_no_more_items = true; + return; + } + this->item_iter--; + if (this->item_iter != this->list->items.end()) item_next = (*this->item_iter).first; + } + + int32 Next() + { + if (!this->HasNext()) return 0; + + int32 item_current = this->item_next; + FindNext(); + return item_current; + } + + void Remove(int item) + { + if (!this->HasNext()) return; + + /* If we remove the 'next' item, skip to the next */ + if (item == this->item_next) { + FindNext(); + return; + } + } + + bool HasNext() + { + return !(this->list->items.empty() || this->has_no_more_items); + } +}; + + + +AIAbstractList::AIAbstractList() +{ + /* Default sorter */ + this->sorter = new AIAbstractListSorterValueDescending(this); + this->sorter_type = SORT_BY_VALUE; + this->sort_ascending = false; + this->initialized = false; +} + +AIAbstractList::~AIAbstractList() +{ + delete this->sorter; +} + +bool AIAbstractList::HasItem(int32 item) +{ + return this->items.count(item) == 1; +} + +void AIAbstractList::Clear() +{ + this->items.clear(); + this->buckets.clear(); + this->sorter->End(); +} + +void AIAbstractList::AddItem(int32 item) +{ + if (this->HasItem(item)) return; + + this->items[item] = 0; + this->buckets[0].insert(item); +} + +void AIAbstractList::RemoveItem(int32 item) +{ + if (!this->HasItem(item)) return; + + int32 value = this->GetValue(item); + + this->sorter->Remove(item); + this->buckets[value].erase(item); + if (this->buckets[value].empty()) this->buckets.erase(value); + this->items.erase(item); +} + +int32 AIAbstractList::Begin() +{ + this->initialized = true; + return this->sorter->Begin(); +} + +int32 AIAbstractList::Next() +{ + if (this->initialized == false) { + DEBUG(ai, 0, "ERROR: Next() is invalid as Begin() is never called"); + return false; + } + return this->sorter->Next(); +} + +bool AIAbstractList::IsEmpty() +{ + return this->items.empty(); +} + +bool AIAbstractList::HasNext() +{ + if (this->initialized == false) { + DEBUG(ai, 0, "ERROR: HasNext() is invalid as Begin() is never called"); + return false; + } + return this->sorter->HasNext(); +} + +int32 AIAbstractList::Count() +{ + return (int32)this->items.size(); +} + +int32 AIAbstractList::GetValue(int32 item) +{ + if (!this->HasItem(item)) return 0; + + return this->items[item]; +} + +bool AIAbstractList::SetValue(int32 item, int32 value) +{ + if (!this->HasItem(item)) return false; + + int32 value_old = this->GetValue(item); + + this->sorter->Remove(item); + this->buckets[value_old].erase(item); + if (this->buckets[value_old].empty()) this->buckets.erase(value_old); + this->items[item] = value; + this->buckets[value].insert(item); + + return true; +} + +void AIAbstractList::Sort(SorterType sorter, bool ascending) +{ + if (sorter != SORT_BY_VALUE && sorter != SORT_BY_ITEM) return; + if (sorter == this->sorter_type && ascending == this->sort_ascending) return; + + delete this->sorter; + switch (sorter) { + case SORT_BY_ITEM: + if (ascending) this->sorter = new AIAbstractListSorterItemAscending(this); + else this->sorter = new AIAbstractListSorterItemDescending(this); + break; + + case SORT_BY_VALUE: + if (ascending) this->sorter = new AIAbstractListSorterValueAscending(this); + else this->sorter = new AIAbstractListSorterValueDescending(this); + break; + + default: + this->Sort(SORT_BY_ITEM, false); + return; + } + this->sorter_type = sorter; + this->sort_ascending = ascending; +} + +void AIAbstractList::AddList(AIAbstractList *list) +{ + AIAbstractListMap *list_items = &list->items; + for (AIAbstractListMap::iterator iter = list_items->begin(); iter != list_items->end(); iter++) { + this->AddItem((*iter).first); + this->SetValue((*iter).first, (*iter).second); + } +} + +void AIAbstractList::RemoveAboveValue(int32 value) +{ + for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).second > value) this->items.erase(iter); + } + + for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).first > value) this->buckets.erase(iter); + } +} + +void AIAbstractList::RemoveBelowValue(int32 value) +{ + for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).second < value) this->items.erase(iter); + } + + for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).first < value) this->buckets.erase(iter); + } +} + +void AIAbstractList::RemoveBetweenValue(int32 start, int32 end) +{ + for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).second > start && (*iter).second < end) this->items.erase(iter); + } + + for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).first > start && (*iter).first < end) this->buckets.erase(iter); + } +} + +void AIAbstractList::RemoveValue(int32 value) +{ + for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).second == value) this->items.erase(iter); + } + + for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).first == value) this->buckets.erase(iter); + } +} + +void AIAbstractList::RemoveTop(int32 count) +{ + if (!this->sort_ascending) { + this->Sort(this->sorter_type, !this->sort_ascending); + this->RemoveBottom(count); + this->Sort(this->sorter_type, !this->sort_ascending); + return; + } + + switch (this->sorter_type) { + default: NOT_REACHED(); + case SORT_BY_VALUE: + for (AIAbstractListBucket::iterator iter = this->buckets.begin(); iter != this->buckets.end(); iter = this->buckets.begin()) { + AIItemList *items = &(*iter).second; + size_t size = items->size(); + for (AIItemList::iterator iter = items->begin(); iter != items->end(); iter = items->begin()) { + if (--count < 0) return; + this->RemoveItem(*iter); + /* When the last item is removed from the bucket, the bucket itself is removed. + * This means that the iterators can be invalid after a call to RemoveItem. + */ + if (--size == 0) break; + } + } + break; + + case SORT_BY_ITEM: + for (AIAbstractListMap::iterator iter = this->items.begin(); iter != this->items.end(); iter = this->items.begin()) { + if (--count < 0) return; + this->RemoveItem((*iter).first); + } + break; + } +} + +void AIAbstractList::RemoveBottom(int32 count) +{ + if (!this->sort_ascending) { + this->Sort(this->sorter_type, !this->sort_ascending); + this->RemoveTop(count); + this->Sort(this->sorter_type, !this->sort_ascending); + return; + } + + switch (this->sorter_type) { + default: NOT_REACHED(); + case SORT_BY_VALUE: + for (AIAbstractListBucket::reverse_iterator iter = this->buckets.rbegin(); iter != this->buckets.rend(); iter = this->buckets.rbegin()) { + AIItemList *items = &(*iter).second; + size_t size = items->size(); + for (AIItemList::reverse_iterator iter = items->rbegin(); iter != items->rend(); iter = items->rbegin()) { + if (--count < 0) return; + this->RemoveItem(*iter); + /* When the last item is removed from the bucket, the bucket itself is removed. + * This means that the iterators can be invalid after a call to RemoveItem. + */ + if (--size == 0) break; + } + } + + case SORT_BY_ITEM: + for (AIAbstractListMap::reverse_iterator iter = this->items.rbegin(); iter != this->items.rend(); iter = this->items.rbegin()) { + if (--count < 0) return; + this->RemoveItem((*iter).first); + } + break; + } +} + +void AIAbstractList::RemoveList(AIAbstractList *list) +{ + AIAbstractListMap *list_items = &list->items; + for (AIAbstractListMap::iterator iter = list_items->begin(); iter != list_items->end(); iter++) { + this->RemoveItem((*iter).first); + } +} + +void AIAbstractList::KeepAboveValue(int32 value) +{ + for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).second <= value) this->items.erase(iter); + } + + for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).first <= value) this->buckets.erase(iter); + } +} + +void AIAbstractList::KeepBelowValue(int32 value) +{ + for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).second >= value) this->items.erase(iter); + } + + for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).first >= value) this->buckets.erase(iter); + } +} + +void AIAbstractList::KeepBetweenValue(int32 start, int32 end) +{ + for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).second <= start || (*iter).second >= end) this->items.erase(iter); + } + + for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).first <= start || (*iter).first >= end) this->buckets.erase(iter); + } +} + +void AIAbstractList::KeepValue(int32 value) +{ + for (AIAbstractListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).second != value) this->items.erase(iter); + } + + for (AIAbstractListBucket::iterator next_iter, iter = this->buckets.begin(); iter != this->buckets.end(); iter = next_iter) { + next_iter = iter; next_iter++; + if ((*iter).first != value) this->buckets.erase(iter); + } +} + +void AIAbstractList::KeepTop(int32 count) +{ + this->RemoveBottom(this->Count() - count); +} + +void AIAbstractList::KeepBottom(int32 count) +{ + this->RemoveTop(this->Count() - count); +} + +void AIAbstractList::KeepList(AIAbstractList *list) +{ + AIAbstractList tmp; + for (AIAbstractListMap::iterator iter = this->items.begin(); iter != this->items.end(); iter++) { + tmp.AddItem((*iter).first); + tmp.SetValue((*iter).first, (*iter).second); + } + + tmp.RemoveList(list); + this->RemoveList(&tmp); +} + +SQInteger AIAbstractList::_get(HSQUIRRELVM vm) { + if (sq_gettype(vm, 2) != OT_INTEGER) return SQ_ERROR; + + SQInteger idx; + sq_getinteger(vm, 2, &idx); + + if (!this->HasItem(idx)) return SQ_ERROR; + + sq_pushinteger(vm, this->GetValue(idx)); + return 1; +} + +SQInteger AIAbstractList::_nexti(HSQUIRRELVM vm) { + if (sq_gettype(vm, 2) == OT_NULL) { + if (this->IsEmpty()) { + sq_pushnull(vm); + return 1; + } + sq_pushinteger(vm, this->Begin()); + return 1; + } + + SQInteger idx; + sq_getinteger(vm, 2, &idx); + + int val = this->Next(); + if (!this->HasNext()) { + sq_pushnull(vm); + return 1; + } + + sq_pushinteger(vm, val); + return 1; +} + +SQInteger AIAbstractList::Valuate(HSQUIRRELVM vm) { + int nparam = sq_gettop(vm) - 2; + + /* Get the list instance and the function to call */ + HSQOBJECT obj_list, obj_func; + sq_getstackobj(vm, 1, &obj_list); + sq_getstackobj(vm, 2, &obj_func); + + if (sq_isclass(obj_list)) { + return sq_throwerror(vm, _SC("parameter 1 has an invalid type (expected instance)")); + } + if (sq_isfunction(obj_func)) { + return sq_throwerror(vm, _SC("parameter 2 has an invalid type (expected function)")); + } + + sq_addref(vm, &obj_func); + + /* Read the params */ + HSQOBJECT *obj_params = AllocaM(HSQOBJECT, nparam); + for (int i = 0; i < nparam; i++) { + sq_getstackobj(vm, i + 3, &obj_params[i]); + sq_addref(vm, &obj_params[i]); + } + /* Remove all unneeded stuff */ + sq_pop(vm, nparam + 1); + + /* Walk all items, and query the result */ + this->buckets.clear(); + for (AIAbstractListMap::iterator iter = this->items.begin(); iter != this->items.end(); iter++) { + /* The function to call */ + sq_pushobject(vm, obj_func); + /* The 'list' instance; this is most likely wrong, but we need to send something ;) */ + sq_pushobject(vm, obj_list); + + /* Now send the params */ + sq_pushinteger(vm, (*iter).first); + for (int i = 0; i < nparam; i++) { + sq_pushobject(vm, obj_params[i]); + } + + /* Call the function */ + if (SQ_FAILED(sq_call(vm, nparam + 2, SQTrue, SQTrue))) return SQ_ERROR; + + /* Retreive the return value */ + SQInteger value; + switch (sq_gettype(vm, -1)) { + case OT_INTEGER: { + sq_getinteger(vm, -1, &value); + } break; + + case OT_BOOL: { + SQBool v; + sq_getbool(vm, -1, &v); + value = v ? 1 : 0; + } break; + + default: { + sq_pop(vm, 3); + sq_release(vm, &obj_func); + for (int i = 0; i < nparam; i++) sq_release(vm, &obj_params[i]); + + return sq_throwerror(vm, _SC("return value of valuator is not valid (not integer/bool)")); + } + } + /* Remove junk */ + sq_pop(vm, 2); + + (*iter).second = (int32)value; + this->buckets[(int32)value].insert((*iter).first); + } + + sq_release(vm, &obj_func); + for (int i = 0; i < nparam; i++) sq_release(vm, &obj_params[i]); + return 0; +} diff --git a/src/ai/api/ai_abstractlist.hpp b/src/ai/api/ai_abstractlist.hpp new file mode 100644 index 000000000..55a4559f8 --- /dev/null +++ b/src/ai/api/ai_abstractlist.hpp @@ -0,0 +1,263 @@ +/* $Id$ */ + +/** @file ai_abstractlist.hpp A list which can keep item/value pairs, which you can walk. */ +/** @defgroup AIList Classes that create a list of items. */ + +#ifndef AI_ABSTRACTLIST_HPP +#define AI_ABSTRACTLIST_HPP + +#include "ai_object.hpp" +#include <map> +#include <set> + +class AIAbstractListSorter; + +/** + * Class that creates a list which can keep item/value pairs, which you can walk. + */ +class AIAbstractList : public AIObject { +public: + static const char *GetClassName() { return "AIAbstractList"; } + + /** Type of sorter */ + enum SorterType { + SORT_BY_VALUE, ///< Sort the list based on the value of the item. + SORT_BY_ITEM, ///< Sort the list based on the item itself. + }; + +private: + AIAbstractListSorter *sorter; + SorterType sorter_type; + bool sort_ascending; + bool initialized; + +public: + typedef std::set<int32> AIItemList; //!< The list of items inside the bucket + typedef std::map<int32, AIItemList> AIAbstractListBucket; //!< The bucket list per value + typedef std::map<int32, int32> AIAbstractListMap; //!< List per item + + AIAbstractListMap items; //!< The items in the list + AIAbstractListBucket buckets; //!< The items in the list, sorted by value + +protected: + /** + * Add a single item to the list. + * @param item the item to add. Should be unique, otherwise it is ignored. + * @note the value is set to 0 by default. + */ + void AddItem(int32 item); + + /** + * Remove a single item from the list. + * @param item the item to remove. If not existing, it is ignored. + */ + void RemoveItem(int32 item); + +public: + AIAbstractList(); + ~AIAbstractList(); + + /** + * Clear the list, making Count() returning 0 and IsEmpty() returning true. + */ + void Clear(); + + /** + * Check if an item is in the list. + * @param item the item to check for. + * @return true if the item is in the list. + */ + bool HasItem(int32 item); + + /** + * Go to the beginning of the list. + * @return the item value of the first item. + */ + int32 Begin(); + + /** + * Go to the next item in the list. + * @return the item value of the next item. + * @note returns 0 if beyond end-of-list. Use HasNext() to check for end-of-list. + */ + int32 Next(); + + /** + * Check if a list is empty. + * @return true if the list is empty. + */ + bool IsEmpty(); + + /** + * Check if there is a next element. In other words, if this is true, + * Next() will return a valid item. + * @return true if there is a next item. + */ + bool HasNext(); + + /** + * Returns the amount of items in the list. + * @return amount of items in the list. + */ + int32 Count(); + + /** + * Get the value that belongs to this item. + * @param item the item to get the value from + * @return the value that belongs to this item. + */ + int32 GetValue(int32 item); + + /** + * Set a value of an item directly. + * @param item the item to set the value for. + * @param value the value to give to the item + * @return true if we could set the item to value, false otherwise. + * @note Changing values of items while looping through a list might cause + * entries to be skipped. Be very careful with such operations. + */ + bool SetValue(int32 item, int32 value); + + /** + * Sort this list by the given sorter and direction. + * @param sorter the type of sorter to use + * @param ascending if true, lowest value is on top, else at bottom. + * @note the current item stays at the same place. + */ + void Sort(SorterType sorter, bool ascending); + + /** + * Add one list to an other one. + * @param list The list that will be added to the caller. + * @post The list to be added ('list') stays unmodified. + * @note All added items keep their value as it was in 'list'. + * @note If the item already exists inside the caller, the value of the + * list that is added is set on the item. + */ + void AddList(AIAbstractList *list); + + /** + * Removes all items with a higher value than 'value'. + * @param value the value above which all items are removed. + */ + void RemoveAboveValue(int32 value); + + /** + * Removes all items with a lower value than 'value'. + * @param value the value below which all items are removed. + */ + void RemoveBelowValue(int32 value); + + /** + * Removes all items with a value above start and below end. + * @param start the lower bound of the to be removed values (exclusive). + * @param end the upper bound of the to be removed valuens (exclusive). + */ + void RemoveBetweenValue(int32 start, int32 end); + + /** + * Remove all items with this value. + * @param value the value to remove. + */ + void RemoveValue(int32 value); + + /** + * Remove the first count items. + * @param count the amount of items to remove. + */ + void RemoveTop(int32 count); + + /** + * Remove the last count items. + * @param count the amount of items to remove. + */ + void RemoveBottom(int32 count); + + /** + * Remove everything that is in the given list from this list (same item index that is). + * @param list the list of items to remove. + * @pre list != NULL + */ + void RemoveList(AIAbstractList *list); + + /** + * Keep all items with a higher value than 'value'. + * @param value the value above which all items are kept. + */ + void KeepAboveValue(int32 value); + + /** + * Keep all items with a lower value than 'value'. + * @param value the value below which all items are kept. + */ + void KeepBelowValue(int32 value); + + /** + * Keep all items with a value above start and below end. + * @param start the lower bound of the to be kept values (exclusive). + * @param end the upper bound of the to be kept values (exclusive). + */ + void KeepBetweenValue(int32 start, int32 end); + + /** + * Keep all items with this value. + * @param value the value to keep. + **/ + void KeepValue(int32 value); + + /** + * Keep the first count items, i.e. remove everything except the first count items. + * @param count the amount of items to keep. + */ + void KeepTop(int32 count); + + /** + * Keep the last count items, i.e. remove everything except the last count items. + * @param count the amount of items to keep. + */ + void KeepBottom(int32 count); + + /** + * Keeps everything that is in the given list from this list (same item index that is). + * @param list the list of items to keep. + * @pre list != NULL + */ + void KeepList(AIAbstractList *list); + +#ifndef DOXYGEN_SKIP + /** + * Used for 'foreach()' and [] get from Squirrel. + */ + SQInteger _get(HSQUIRRELVM vm); + + /** + * Used for 'foreach()' from Squirrel. + */ + SQInteger _nexti(HSQUIRRELVM vm); + + /** + * The Valuate() wrapper from Squirrel. + */ + SQInteger Valuate(HSQUIRRELVM vm); +#else + /** + * Give all items a value defined by the valuator you give. + * @param valuator_function The function which will be doing the valuation. + * @param params The params to give to the valuators (minus the first param, + * which is always the index-value we are valuating). + * @note You can write your own valuators and use them. Just remember that + * the first parameter should be the index-value, and it should return + * an integer. + * @note Example: + * list.Valuate(AIBridge.GetPrice, 5); + * list.Valuate(AIBridge.GetMaxLength); + * function MyVal(bridge_id, myparam) { + * return myparam * bridge_id; // This is silly + * } + * list.Valuate(MyVal, 12); + */ + void Valuate(void *valuator_function, int params, ...); +#endif /* DOXYGEN_SKIP */ +}; + +#endif /* AI_LIST_HPP */ diff --git a/src/ai/api/ai_abstractlist.hpp.sq b/src/ai/api/ai_abstractlist.hpp.sq new file mode 100644 index 000000000..25d9f3904 --- /dev/null +++ b/src/ai/api/ai_abstractlist.hpp.sq @@ -0,0 +1,59 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_abstractlist.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AIAbstractList::SorterType GetParam(ForceType<AIAbstractList::SorterType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIAbstractList::SorterType)tmp; } + template <> int Return<AIAbstractList::SorterType>(HSQUIRRELVM vm, AIAbstractList::SorterType res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AIAbstractList to be used as Squirrel parameter */ + template <> AIAbstractList *GetParam(ForceType<AIAbstractList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIAbstractList *)instance; } + template <> AIAbstractList &GetParam(ForceType<AIAbstractList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIAbstractList *)instance; } + template <> const AIAbstractList *GetParam(ForceType<const AIAbstractList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIAbstractList *)instance; } + template <> const AIAbstractList &GetParam(ForceType<const AIAbstractList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIAbstractList *)instance; } + template <> int Return<AIAbstractList *>(HSQUIRRELVM vm, AIAbstractList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIAbstractList", res, NULL, DefSQDestructorCallback<AIAbstractList>); return 1; } +}; // namespace SQConvert + +void SQAIAbstractList_Register(Squirrel *engine) { + DefSQClass <AIAbstractList> SQAIAbstractList("AIAbstractList"); + SQAIAbstractList.PreRegister(engine); + SQAIAbstractList.AddConstructor<void (AIAbstractList::*)(), 1>(engine, "x"); + + SQAIAbstractList.DefSQConst(engine, AIAbstractList::SORT_BY_VALUE, "SORT_BY_VALUE"); + SQAIAbstractList.DefSQConst(engine, AIAbstractList::SORT_BY_ITEM, "SORT_BY_ITEM"); + + SQAIAbstractList.DefSQStaticMethod(engine, &AIAbstractList::GetClassName, "GetClassName", 1, "x"); + + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::Clear, "Clear", 1, "x"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::HasItem, "HasItem", 2, "xi"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::Begin, "Begin", 1, "x"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::Next, "Next", 1, "x"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::IsEmpty, "IsEmpty", 1, "x"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::HasNext, "HasNext", 1, "x"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::Count, "Count", 1, "x"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::GetValue, "GetValue", 2, "xi"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::SetValue, "SetValue", 3, "xii"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::Sort, "Sort", 3, "xib"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::AddList, "AddList", 2, "xx"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::RemoveAboveValue, "RemoveAboveValue", 2, "xi"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::RemoveBelowValue, "RemoveBelowValue", 2, "xi"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::RemoveBetweenValue, "RemoveBetweenValue", 3, "xii"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::RemoveValue, "RemoveValue", 2, "xi"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::RemoveTop, "RemoveTop", 2, "xi"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::RemoveBottom, "RemoveBottom", 2, "xi"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::RemoveList, "RemoveList", 2, "xx"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepAboveValue, "KeepAboveValue", 2, "xi"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepBelowValue, "KeepBelowValue", 2, "xi"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepBetweenValue, "KeepBetweenValue", 3, "xii"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepValue, "KeepValue", 2, "xi"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepTop, "KeepTop", 2, "xi"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepBottom, "KeepBottom", 2, "xi"); + SQAIAbstractList.DefSQMethod(engine, &AIAbstractList::KeepList, "KeepList", 2, "xx"); + SQAIAbstractList.DefSQAdvancedMethod(engine, &AIAbstractList::_get, "_get"); + SQAIAbstractList.DefSQAdvancedMethod(engine, &AIAbstractList::_nexti, "_nexti"); + SQAIAbstractList.DefSQAdvancedMethod(engine, &AIAbstractList::Valuate, "Valuate"); + + SQAIAbstractList.PostRegister(engine); +} diff --git a/src/ai/api/ai_accounting.cpp b/src/ai/api/ai_accounting.cpp new file mode 100644 index 000000000..2134d39b0 --- /dev/null +++ b/src/ai/api/ai_accounting.cpp @@ -0,0 +1,26 @@ +/* $Id$ */ + +/** @file ai_accounting.cpp Implementation of AIAccounting. */ + +#include "ai_accounting.hpp" + +Money AIAccounting::GetCosts() +{ + return this->GetDoCommandCosts(); +} + +void AIAccounting::ResetCosts() +{ + this->SetDoCommandCosts(0); +} + +AIAccounting::AIAccounting() +{ + this->last_costs = this->GetDoCommandCosts(); + this->SetDoCommandCosts(0); +} + +AIAccounting::~AIAccounting() +{ + this->SetDoCommandCosts(this->last_costs); +} diff --git a/src/ai/api/ai_accounting.hpp b/src/ai/api/ai_accounting.hpp new file mode 100644 index 000000000..bffa051f1 --- /dev/null +++ b/src/ai/api/ai_accounting.hpp @@ -0,0 +1,54 @@ +/* $Id$ */ + +/** @file ai_accounting.hpp Everything to handle AI accounting things. */ + +#ifndef AI_ACCOUNTING_HPP +#define AI_ACCOUNTING_HPP + +#include "ai_object.hpp" + +/** + * Class that keeps track of the costs, so you can request how much a block of + * commands did cost in total. Works in both Execute as in Test mode. + * Example: + * { + * local costs = AIAccounting(); + * BuildRoad(from_here, to_here); + * BuildRoad(from_there, to_there); + * print("Costs for route is: " + costs.GetCosts()); + * } + */ +class AIAccounting : public AIObject { +public: + static const char *GetClassName() { return "AIAccounting"; } + + /** + * Creating instance of this class starts counting the costs of commands + * from zero. + * @note when the instance is destroyed, he restores the costs that was + * current when the instance was created! + */ + AIAccounting(); + + /** + * Destroying this instance reset the costs to the value it was + * in when the instance was created. + */ + ~AIAccounting(); + + /** + * Get the current value of the costs. + * @return The current costs. + */ + Money GetCosts(); + + /** + * Reset the costs to zero. + */ + void ResetCosts(); + +private: + Money last_costs; +}; + +#endif /* AI_ACCOUNTING_HPP */ diff --git a/src/ai/api/ai_accounting.hpp.sq b/src/ai/api/ai_accounting.hpp.sq new file mode 100644 index 000000000..18922b24c --- /dev/null +++ b/src/ai/api/ai_accounting.hpp.sq @@ -0,0 +1,26 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_accounting.hpp" + +namespace SQConvert { + /* Allow AIAccounting to be used as Squirrel parameter */ + template <> AIAccounting *GetParam(ForceType<AIAccounting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIAccounting *)instance; } + template <> AIAccounting &GetParam(ForceType<AIAccounting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIAccounting *)instance; } + template <> const AIAccounting *GetParam(ForceType<const AIAccounting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIAccounting *)instance; } + template <> const AIAccounting &GetParam(ForceType<const AIAccounting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIAccounting *)instance; } + template <> int Return<AIAccounting *>(HSQUIRRELVM vm, AIAccounting *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIAccounting", res, NULL, DefSQDestructorCallback<AIAccounting>); return 1; } +}; // namespace SQConvert + +void SQAIAccounting_Register(Squirrel *engine) { + DefSQClass <AIAccounting> SQAIAccounting("AIAccounting"); + SQAIAccounting.PreRegister(engine); + SQAIAccounting.AddConstructor<void (AIAccounting::*)(), 1>(engine, "x"); + + SQAIAccounting.DefSQStaticMethod(engine, &AIAccounting::GetClassName, "GetClassName", 1, "x"); + + SQAIAccounting.DefSQMethod(engine, &AIAccounting::GetCosts, "GetCosts", 1, "x"); + SQAIAccounting.DefSQMethod(engine, &AIAccounting::ResetCosts, "ResetCosts", 1, "x"); + + SQAIAccounting.PostRegister(engine); +} diff --git a/src/ai/api/ai_airport.cpp b/src/ai/api/ai_airport.cpp new file mode 100644 index 000000000..3e435d7bc --- /dev/null +++ b/src/ai/api/ai_airport.cpp @@ -0,0 +1,130 @@ +/* $Id$ */ + +/** @file ai_airport.cpp Implementation of AIAirport. */ + +#include "ai_airport.hpp" +#include "ai_station.hpp" +#include "ai_error.hpp" +#include "../../openttd.h" +#include "../../variables.h" +#include "../../station_map.h" +#include "../../company_func.h" +#include "../../settings_type.h" +#include "../../command_type.h" +#include "../../town.h" + +/* static */ bool AIAirport::IsValidAirportType(AirportType type) +{ + return type >= AT_SMALL && type <= AT_HELISTATION; +} + +/* static */ bool AIAirport::IsHangarTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsTileType(tile, MP_STATION) && ::IsHangar(tile); +} + +/* static */ bool AIAirport::IsAirportTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsTileType(tile, MP_STATION) && ::IsAirport(tile); +} + +/* static */ bool AIAirport::AirportAvailable(AirportType type) +{ + if (!IsValidAirportType(type)) return false; + + return HasBit(::GetValidAirports(), type); +} + +/* static */ int32 AIAirport::GetAirportWidth(AirportType type) +{ + if (!IsValidAirportType(type)) return -1; + + return ::GetAirport(type)->size_x; +} + +/* static */ int32 AIAirport::GetAirportHeight(AirportType type) +{ + if (!IsValidAirportType(type)) return -1; + + return ::GetAirport(type)->size_y; +} + +/* static */ int32 AIAirport::GetAirportCoverageRadius(AirportType type) +{ + if (!IsValidAirportType(type)) return -1; + + return _settings_game.station.modified_catchment ? ::GetAirport(type)->catchment : (uint)CA_UNMODIFIED; +} + +/* static */ bool AIAirport::BuildAirport(TileIndex tile, AirportType type, bool join_adjacent) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, IsValidAirportType(type)); + + return AIObject::DoCommand(tile, type, (INVALID_STATION << 16) | (join_adjacent ? 0 : 1), CMD_BUILD_AIRPORT); +} + +/* static */ bool AIAirport::RemoveAirport(TileIndex tile) +{ + EnforcePrecondition(false, ::IsValidTile(tile)) + EnforcePrecondition(false, IsAirportTile(tile) || IsHangarTile(tile)); + + return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR); +} + +/* static */ int32 AIAirport::GetNumHangars(TileIndex tile) +{ + if (!::IsValidTile(tile)) return -1; + if (!::IsTileType(tile, MP_STATION)) return -1; + + const Station *st = ::GetStationByTile(tile); + if (st->owner != _current_company) return -1; + if ((st->facilities & FACIL_AIRPORT) == 0) return -1; + + return st->Airport()->nof_depots; +} + +/* static */ TileIndex AIAirport::GetHangarOfAirport(TileIndex tile) +{ + if (!::IsValidTile(tile)) return INVALID_TILE; + if (!::IsTileType(tile, MP_STATION)) return INVALID_TILE; + if (GetNumHangars(tile) < 1) return INVALID_TILE; + + const Station *st = ::GetStationByTile(tile); + if (st->owner != _current_company) return INVALID_TILE; + if ((st->facilities & FACIL_AIRPORT) == 0) return INVALID_TILE; + + return ::ToTileIndexDiff(st->Airport()->airport_depots[0]) + st->xy; +} + +/* static */ AIAirport::AirportType AIAirport::GetAirportType(TileIndex tile) +{ + if (!AITile::IsStationTile(tile)) return AT_INVALID; + + StationID station_id = ::GetStationIndex(tile); + + if (!AIStation::HasStationType(station_id, AIStation::STATION_AIRPORT)) return AT_INVALID; + + return (AirportType)::GetStation(station_id)->airport_type; +} + + +/* static */ int AIAirport::GetNoiseLevelIncrease(TileIndex tile, AirportType type) +{ + extern uint8 GetAirportNoiseLevelForTown(const AirportFTAClass *afc, TileIndex town_tile, TileIndex tile); + + if (!::IsValidTile(tile)) return -1; + if (!IsValidAirportType(type)) return -1; + + if (_settings_game.economy.station_noise_level) { + const AirportFTAClass *afc = ::GetAirport(type); + const Town *t = ::ClosestTownFromTile(tile, UINT_MAX); + return GetAirportNoiseLevelForTown(afc, t->xy, tile); + } + + return 1; +} diff --git a/src/ai/api/ai_airport.hpp b/src/ai/api/ai_airport.hpp new file mode 100644 index 000000000..d1d18714b --- /dev/null +++ b/src/ai/api/ai_airport.hpp @@ -0,0 +1,164 @@ +/* $Id$ */ + +/** @file ai_airport.hpp Everything to query and build airports. */ + +#ifndef AI_AIRPORT_HPP +#define AI_AIRPORT_HPP + +#include "ai_object.hpp" + +/** + * Class that handles all airport related functions. + */ +class AIAirport : public AIObject { +public: + static const char *GetClassName() { return "AIAirport"; } + + /** + * The types of airports available in the game. + */ + enum AirportType { + /* Note: the values _are_ important as they represent an in-game value */ + AT_SMALL = 0, //!< The small airport. + AT_LARGE = 1, //!< The large airport. + AT_METROPOLITAN = 3, //!< The metropolitan airport. + AT_INTERNATIONAL = 4, //!< The international airport. + AT_COMMUTER = 5, //!< The commuter airport. + AT_INTERCON = 7, //!< The intercontinental airport. + + /* Next are the airports which only have helicopter platforms */ + AT_HELIPORT = 2, //!< The heliport. + AT_HELISTATION = 8, //!< The helistation. + AT_HELIDEPOT = 6, //!< The helidepot. + + AT_INVALID = 255, //!< Invalid airport. + }; + + /** + * All plane types available. + */ + enum PlaneType { + /* Note: the values _are_ important as they represent an in-game value */ + PT_HELICOPTER = 0, //!< A helicopter. + PT_SMALL_PLANE = 1, //!< A small plane. + PT_BIG_PLANE = 3, //!< A big plane. + + PT_INVALID = -1, //!< An invalid PlaneType + }; + + /** + * Checks whether the given AirportType is valid. + * @param type The AirportType to check. + * @return True if and only if the AirportTypeis valid. + */ + static bool IsValidAirportType(AirportType type); + + /** + * Checks whether the given tile is actually a tile with a hangar. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has a hangar. + */ + static bool IsHangarTile(TileIndex tile); + + /** + * Checks whether the given tile is actually a tile with a airport. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has a airport. + */ + static bool IsAirportTile(TileIndex tile); + + /** + * Check if a certain airport type is already available. + * @param type The type of airport to check. + */ + static bool AirportAvailable(AirportType type); + + /** + * Get the width of this type of airport. + * @param type The type of airport. + * @return The width in tiles. + */ + static int32 GetAirportWidth(AirportType type); + + /** + * Get the height of this type of airport. + * @param type The type of airport. + * @return The height in tiles. + */ + static int32 GetAirportHeight(AirportType type); + + /** + * Get the coverage radius of this type of airport. + * @param type The type of airport. + * @return The radius in tiles. + */ + static int32 GetAirportCoverageRadius(AirportType type); + + /** + * Get the number of hangars of the airport. + * @param tile Any tile of the airport. + * @pre AIMap::IsValidTile(tile). + * @return The number of hangars of the airport. + */ + static int32 GetNumHangars(TileIndex tile); + + /** + * Get the first hanger tile of the airport. + * @param tile Any tile of the airport. + * @pre AIMap::IsValidTile(tile). + * @pre GetNumHangars(tile) > 0. + * @return The first hanger tile of the airport. + * @note Possible there are more hangars, but you won't be able to find them + * without walking over all the tiles of the airport and using + * IsHangarTile() on them. + */ + static TileIndex GetHangarOfAirport(TileIndex tile); + + /** + * Builds a airport with tile at the topleft corner. + * @param tile The topleft corner of the airport. + * @param type The type of airport to build. + * @param join_adjacent When building next to an other station, don't create a new station when this flag is true. + * @pre AIMap::IsValidTile(tile). + * @pre AirportAvailable(type). + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIError::ERR_FLAT_LAND_REQUIRED + * @exception AIError::ERR_LOCAL_AUTHORITY_REFUSES + * @exception AIStation::ERR_STATION_TOO_LARGE + * @exception AIStation::ERR_STATION_TOO_CLOSE_TO_OTHER_STATION + * @return Whether the airport has been/can be build or not. + */ + static bool BuildAirport(TileIndex tile, AirportType type, bool join_adjacent); + + /** + * Removes a airport. + * @param tile Any tile of the airport. + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @return Whether the airport has been/can be removed or not. + */ + static bool RemoveAirport(TileIndex tile); + + /** + * Get the AirportType of an existing airport. + * @param tile Any tile of the airport. + * @pre AITile::IsStationTile(tile). + * @pre AIStation::HasStationType(AIStation.GetStationID(tile), AIStation::STATION_AIRPORT). + * @return The AirportType of the airport. + */ + static AirportType GetAirportType(TileIndex tile); + + /** + * Get the noise that will be added to the nearest town if an airport was + * built at this tile. + * @param tile The tile to check. + * @param type The AirportType to check. + * @return The TownID of the town closest to the tile. + * @note The noise will be added to the town with TownID AITile.GetClosestTown(tile). + */ + static int GetNoiseLevelIncrease(TileIndex tile, AirportType type); +}; + +#endif /* AI_AIRPORT_HPP */ diff --git a/src/ai/api/ai_airport.hpp.sq b/src/ai/api/ai_airport.hpp.sq new file mode 100644 index 000000000..355a2ab12 --- /dev/null +++ b/src/ai/api/ai_airport.hpp.sq @@ -0,0 +1,57 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_airport.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AIAirport::AirportType GetParam(ForceType<AIAirport::AirportType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIAirport::AirportType)tmp; } + template <> int Return<AIAirport::AirportType>(HSQUIRRELVM vm, AIAirport::AirportType res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AIAirport::PlaneType GetParam(ForceType<AIAirport::PlaneType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIAirport::PlaneType)tmp; } + template <> int Return<AIAirport::PlaneType>(HSQUIRRELVM vm, AIAirport::PlaneType res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AIAirport to be used as Squirrel parameter */ + template <> AIAirport *GetParam(ForceType<AIAirport *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIAirport *)instance; } + template <> AIAirport &GetParam(ForceType<AIAirport &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIAirport *)instance; } + template <> const AIAirport *GetParam(ForceType<const AIAirport *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIAirport *)instance; } + template <> const AIAirport &GetParam(ForceType<const AIAirport &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIAirport *)instance; } + template <> int Return<AIAirport *>(HSQUIRRELVM vm, AIAirport *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIAirport", res, NULL, DefSQDestructorCallback<AIAirport>); return 1; } +}; // namespace SQConvert + +void SQAIAirport_Register(Squirrel *engine) { + DefSQClass <AIAirport> SQAIAirport("AIAirport"); + SQAIAirport.PreRegister(engine); + SQAIAirport.AddConstructor<void (AIAirport::*)(), 1>(engine, "x"); + + SQAIAirport.DefSQConst(engine, AIAirport::AT_SMALL, "AT_SMALL"); + SQAIAirport.DefSQConst(engine, AIAirport::AT_LARGE, "AT_LARGE"); + SQAIAirport.DefSQConst(engine, AIAirport::AT_METROPOLITAN, "AT_METROPOLITAN"); + SQAIAirport.DefSQConst(engine, AIAirport::AT_INTERNATIONAL, "AT_INTERNATIONAL"); + SQAIAirport.DefSQConst(engine, AIAirport::AT_COMMUTER, "AT_COMMUTER"); + SQAIAirport.DefSQConst(engine, AIAirport::AT_INTERCON, "AT_INTERCON"); + SQAIAirport.DefSQConst(engine, AIAirport::AT_HELIPORT, "AT_HELIPORT"); + SQAIAirport.DefSQConst(engine, AIAirport::AT_HELISTATION, "AT_HELISTATION"); + SQAIAirport.DefSQConst(engine, AIAirport::AT_HELIDEPOT, "AT_HELIDEPOT"); + SQAIAirport.DefSQConst(engine, AIAirport::AT_INVALID, "AT_INVALID"); + SQAIAirport.DefSQConst(engine, AIAirport::PT_HELICOPTER, "PT_HELICOPTER"); + SQAIAirport.DefSQConst(engine, AIAirport::PT_SMALL_PLANE, "PT_SMALL_PLANE"); + SQAIAirport.DefSQConst(engine, AIAirport::PT_BIG_PLANE, "PT_BIG_PLANE"); + SQAIAirport.DefSQConst(engine, AIAirport::PT_INVALID, "PT_INVALID"); + + SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetClassName, "GetClassName", 1, "x"); + SQAIAirport.DefSQStaticMethod(engine, &AIAirport::IsValidAirportType, "IsValidAirportType", 2, "xi"); + SQAIAirport.DefSQStaticMethod(engine, &AIAirport::IsHangarTile, "IsHangarTile", 2, "xi"); + SQAIAirport.DefSQStaticMethod(engine, &AIAirport::IsAirportTile, "IsAirportTile", 2, "xi"); + SQAIAirport.DefSQStaticMethod(engine, &AIAirport::AirportAvailable, "AirportAvailable", 2, "xi"); + SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetAirportWidth, "GetAirportWidth", 2, "xi"); + SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetAirportHeight, "GetAirportHeight", 2, "xi"); + SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetAirportCoverageRadius, "GetAirportCoverageRadius", 2, "xi"); + SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetNumHangars, "GetNumHangars", 2, "xi"); + SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetHangarOfAirport, "GetHangarOfAirport", 2, "xi"); + SQAIAirport.DefSQStaticMethod(engine, &AIAirport::BuildAirport, "BuildAirport", 4, "xiib"); + SQAIAirport.DefSQStaticMethod(engine, &AIAirport::RemoveAirport, "RemoveAirport", 2, "xi"); + SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetAirportType, "GetAirportType", 2, "xi"); + SQAIAirport.DefSQStaticMethod(engine, &AIAirport::GetNoiseLevelIncrease, "GetNoiseLevelIncrease", 3, "xii"); + + SQAIAirport.PostRegister(engine); +} diff --git a/src/ai/api/ai_base.cpp b/src/ai/api/ai_base.cpp new file mode 100644 index 000000000..fa04bac36 --- /dev/null +++ b/src/ai/api/ai_base.cpp @@ -0,0 +1,43 @@ +/* $Id$ */ + +/** @file ai_base.cpp Implementation of AIBase. */ + +#include "ai_base.hpp" +#include "../../network/network.h" +#include "../../core/random_func.hpp" + +/* static */ uint32 AIBase::Rand() +{ + /* We pick RandomRange if we are in SP (so when saved, we do the same over and over) + * but we pick InteractiveRandomRange if we are a network_server or network-client. */ + if (_networking) return ::InteractiveRandom(); + return ::Random(); +} + +/* static */ uint32 AIBase::RandItem(int unused_param) +{ + return AIBase::Rand(); +} + +/* static */ uint AIBase::RandRange(uint max) +{ + /* We pick RandomRange if we are in SP (so when saved, we do the same over and over) + * but we pick InteractiveRandomRange if we are a network_server or network-client. */ + if (_networking) return ::InteractiveRandomRange(max); + return ::RandomRange(max); +} + +/* static */ uint32 AIBase::RandRangeItem(int unused_param, uint max) +{ + return AIBase::RandRange(max); +} + +/* static */ bool AIBase::Chance(uint out, uint max) +{ + return (uint16)Rand() <= (uint16)((65536 * out) / max); +} + +/* static */ bool AIBase::ChanceItem(int unused_param, uint out, uint max) +{ + return AIBase::Chance(out, max); +} diff --git a/src/ai/api/ai_base.hpp b/src/ai/api/ai_base.hpp new file mode 100644 index 000000000..9a3aad742 --- /dev/null +++ b/src/ai/api/ai_base.hpp @@ -0,0 +1,71 @@ +/* $Id$ */ + +/** @file ai_base.hpp Everything to query basic things. */ + +#ifndef AI_BASE_HPP +#define AI_BASE_HPP + +#include "ai_object.hpp" + +/** + * Class that handles some basic functions. + * + * @note The random functions are not called Random and RandomRange, because + * RANDOM_DEBUG does some tricky stuff, which messes with those names. + * @note In MP we cannot use Random because that will cause desyncs (AIs are + * ran on the server only, not on all clients). This means that + * we use InteractiveRandom in MP. Rand() takes care of this for you. + */ +class AIBase : public AIObject { +public: + static const char *GetClassName() { return "AIBase"; } + + /** + * Get a random value. + * @return A random value between 0 and MAX(uint32). + */ + static uint32 Rand(); + + /** + * Get a random value. + * @param unused_param This param is not used, but is needed to work with lists. + * @return A random value between 0 and MAX(uint32). + */ + static uint32 RandItem(int unused_param); + + /** + * Get a random value in a range. + * @param max The first number this function will never return (the maximum it returns is max - 1). + * @return A random value between 0 .. max - 1. + */ + static uint RandRange(uint max); + + /** + * Get a random value in a range. + * @param unused_param This param is not used, but is needed to work with lists. + * @param max The first number this function will never return (the maximum it returns is max - 1). + * @return A random value between 0 .. max - 1. + */ + static uint RandRangeItem(int unused_param, uint max); + + /** + * Returns approximatelly 'out' times true when called 'max' times. + * After all, it is a random function. + * @param out How many times it should return true. + * @param max Out of this many times. + * @return True if the chance worked out. + */ + static bool Chance(uint out, uint max); + + /** + * Returns approximatelly 'out' times true when called 'max' times. + * After all, it is a random function. + * @param unused_param This param is not used, but is needed to work with lists. + * @param out How many times it should return true. + * @param max Out of this many times. + * @return True if the chance worked out. + */ + static bool ChanceItem(int unused_param, uint out, uint max); +}; + +#endif /* AI_BASE_HPP */ diff --git a/src/ai/api/ai_base.hpp.sq b/src/ai/api/ai_base.hpp.sq new file mode 100644 index 000000000..577301936 --- /dev/null +++ b/src/ai/api/ai_base.hpp.sq @@ -0,0 +1,29 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_base.hpp" + +namespace SQConvert { + /* Allow AIBase to be used as Squirrel parameter */ + template <> AIBase *GetParam(ForceType<AIBase *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBase *)instance; } + template <> AIBase &GetParam(ForceType<AIBase &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBase *)instance; } + template <> const AIBase *GetParam(ForceType<const AIBase *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBase *)instance; } + template <> const AIBase &GetParam(ForceType<const AIBase &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBase *)instance; } + template <> int Return<AIBase *>(HSQUIRRELVM vm, AIBase *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIBase", res, NULL, DefSQDestructorCallback<AIBase>); return 1; } +}; // namespace SQConvert + +void SQAIBase_Register(Squirrel *engine) { + DefSQClass <AIBase> SQAIBase("AIBase"); + SQAIBase.PreRegister(engine); + SQAIBase.AddConstructor<void (AIBase::*)(), 1>(engine, "x"); + + SQAIBase.DefSQStaticMethod(engine, &AIBase::GetClassName, "GetClassName", 1, "x"); + SQAIBase.DefSQStaticMethod(engine, &AIBase::Rand, "Rand", 1, "x"); + SQAIBase.DefSQStaticMethod(engine, &AIBase::RandItem, "RandItem", 2, "xi"); + SQAIBase.DefSQStaticMethod(engine, &AIBase::RandRange, "RandRange", 2, "xi"); + SQAIBase.DefSQStaticMethod(engine, &AIBase::RandRangeItem, "RandRangeItem", 3, "xii"); + SQAIBase.DefSQStaticMethod(engine, &AIBase::Chance, "Chance", 3, "xii"); + SQAIBase.DefSQStaticMethod(engine, &AIBase::ChanceItem, "ChanceItem", 4, "xiii"); + + SQAIBase.PostRegister(engine); +} diff --git a/src/ai/api/ai_bridge.cpp b/src/ai/api/ai_bridge.cpp new file mode 100644 index 000000000..86eb5f6f1 --- /dev/null +++ b/src/ai/api/ai_bridge.cpp @@ -0,0 +1,177 @@ +/* $Id$ */ + +/** @file ai_bridge.cpp Implementation of AIBridge. */ + +#include "ai_bridge.hpp" +#include "ai_rail.hpp" +#include "../ai_instance.hpp" +#include "../../openttd.h" +#include "../../bridge_map.h" +#include "../../strings_func.h" +#include "../../core/alloc_func.hpp" +#include "../../economy_func.h" +#include "../../settings_type.h" +#include "../../road_map.h" +#include "table/strings.h" + +/* static */ bool AIBridge::IsValidBridge(BridgeID bridge_id) +{ + return bridge_id < MAX_BRIDGES; +} + +/* static */ bool AIBridge::IsBridgeTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + return ::IsBridgeTile(tile); +} + +static void _DoCommandReturnBuildBridge2(class AIInstance *instance) +{ + if (!AIBridge::_BuildBridgeRoad2()) { + AIObject::SetLastCommandRes(false); + AIInstance::DoCommandReturn(instance); + return; + } + + /* This can never happen, as in test-mode this callback is never executed, + * and in execute-mode, the other callback is called. */ + NOT_REACHED(); +} + +static void _DoCommandReturnBuildBridge1(class AIInstance *instance) +{ + if (!AIBridge::_BuildBridgeRoad1()) { + AIObject::SetLastCommandRes(false); + AIInstance::DoCommandReturn(instance); + return; + } + + /* This can never happen, as in test-mode this callback is never executed, + * and in execute-mode, the other callback is called. */ + NOT_REACHED(); +} + +/* static */ bool AIBridge::BuildBridge(AIVehicle::VehicleType vehicle_type, BridgeID bridge_id, TileIndex start, TileIndex end) +{ + EnforcePrecondition(false, start != end); + EnforcePrecondition(false, ::IsValidTile(start) && ::IsValidTile(end)); + EnforcePrecondition(false, TileX(start) == TileX(end) || TileY(start) == TileY(end)); + EnforcePrecondition(false, vehicle_type == AIVehicle::VEHICLE_ROAD || vehicle_type == AIVehicle::VEHICLE_RAIL || vehicle_type == AIVehicle::VEHICLE_WATER); + EnforcePrecondition(false, vehicle_type != AIVehicle::VEHICLE_RAIL || AIRail::IsRailTypeAvailable(AIRail::GetCurrentRailType())); + + uint type = 0; + switch (vehicle_type) { + case AIVehicle::VEHICLE_ROAD: + type |= (TRANSPORT_ROAD << 15); + type |= (RoadTypeToRoadTypes((::RoadType)AIObject::GetRoadType()) << 8); + break; + case AIVehicle::VEHICLE_RAIL: + type |= (TRANSPORT_RAIL << 15); + type |= (AIRail::GetCurrentRailType() << 8); + break; + case AIVehicle::VEHICLE_WATER: + type |= (TRANSPORT_WATER << 15); + break; + default: NOT_REACHED(); + } + + /* For rail and water we do nothing special */ + if (vehicle_type == AIVehicle::VEHICLE_RAIL || vehicle_type == AIVehicle::VEHICLE_WATER) { + return AIObject::DoCommand(end, start, type | bridge_id, CMD_BUILD_BRIDGE); + } + + AIObject::SetCallbackVariable(0, start); + if (!AIObject::DoCommand(end, start, type | bridge_id, CMD_BUILD_BRIDGE, NULL, &_DoCommandReturnBuildBridge1)) return false; + + /* In case of test-mode, test if we can build both road pieces */ + return _BuildBridgeRoad1(); +} + +/* static */ bool AIBridge::_BuildBridgeRoad1() +{ + /* Build the piece of road on the 'start' side of the bridge */ + TileIndex end = AIObject::GetCallbackVariable(0); + TileIndex start = AIBridge::GetOtherBridgeEnd(end); + + DiagDirection dir_1 = (DiagDirection)((::TileX(start) == ::TileX(end)) ? (::TileY(start) < ::TileY(end) ? DIAGDIR_NW : DIAGDIR_SE) : (::TileX(start) < ::TileX(end) ? DIAGDIR_NE : DIAGDIR_SW)); + DiagDirection dir_2 = ::ReverseDiagDir(dir_1); + + if (!AIObject::DoCommand(start + ::TileOffsByDiagDir(dir_1), ::DiagDirToRoadBits(dir_2) | (AIObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD, NULL, &_DoCommandReturnBuildBridge2)) return false; + + /* In case of test-mode, test the other road piece too */ + return _BuildBridgeRoad2(); +} + +/* static */ bool AIBridge::_BuildBridgeRoad2() +{ + /* Build the piece of road on the 'end' side of the bridge */ + TileIndex end = AIObject::GetCallbackVariable(0); + TileIndex start = AIBridge::GetOtherBridgeEnd(end); + + DiagDirection dir_1 = (DiagDirection)((::TileX(start) == ::TileX(end)) ? (::TileY(start) < ::TileY(end) ? DIAGDIR_NW : DIAGDIR_SE) : (::TileX(start) < ::TileX(end) ? DIAGDIR_NE : DIAGDIR_SW)); + DiagDirection dir_2 = ::ReverseDiagDir(dir_1); + + return AIObject::DoCommand(end + ::TileOffsByDiagDir(dir_2), ::DiagDirToRoadBits(dir_1) | (AIObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD); +} + +/* static */ bool AIBridge::RemoveBridge(TileIndex tile) +{ + EnforcePrecondition(false, IsBridgeTile(tile)); + return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR); +} + +/* static */ const char *AIBridge::GetName(BridgeID bridge_id) +{ + if (!IsValidBridge(bridge_id)) return NULL; + + static const int len = 64; + char *bridge_name = MallocT<char>(len); + + ::GetString(bridge_name, ::GetBridgeSpec(bridge_id)->transport_name[0], &bridge_name[len - 1]); + return bridge_name; +} + +/* static */ int32 AIBridge::GetMaxSpeed(BridgeID bridge_id) +{ + if (!IsValidBridge(bridge_id)) return -1; + + return ::GetBridgeSpec(bridge_id)->speed; +} + +/* static */ Money AIBridge::GetPrice(BridgeID bridge_id, uint length) +{ + if (!IsValidBridge(bridge_id)) return -1; + + return length * _price.build_bridge * ::GetBridgeSpec(bridge_id)->price >> 8; +} + +/* static */ int32 AIBridge::GetMaxLength(BridgeID bridge_id) +{ + if (!IsValidBridge(bridge_id)) return -1; + + uint max = ::GetBridgeSpec(bridge_id)->max_length; + if (max >= 16 && _settings_game.construction.longbridges) max = 100; + return max + 2; +} + +/* static */ int32 AIBridge::GetMinLength(BridgeID bridge_id) +{ + if (!IsValidBridge(bridge_id)) return -1; + + return ::GetBridgeSpec(bridge_id)->min_length + 2; +} + +/* static */ int32 AIBridge::GetYearAvailable(BridgeID bridge_id) +{ + if (!IsValidBridge(bridge_id)) return -1; + + return ::GetBridgeSpec(bridge_id)->avail_year; +} + +/* static */ TileIndex AIBridge::GetOtherBridgeEnd(TileIndex tile) +{ + if (!::IsValidTile(tile)) return INVALID_TILE; + if (!IsBridgeTile(tile)) return INVALID_TILE; + + return ::GetOtherBridgeEnd(tile); +} diff --git a/src/ai/api/ai_bridge.hpp b/src/ai/api/ai_bridge.hpp new file mode 100644 index 000000000..7bd637277 --- /dev/null +++ b/src/ai/api/ai_bridge.hpp @@ -0,0 +1,164 @@ +/* $Id$ */ + +/** @file ai_bridge.hpp Everything to query and build bridges. */ + +#ifndef AI_BRIDGE_HPP +#define AI_BRIDGE_HPP + +#include "ai_object.hpp" +#include "ai_vehicle.hpp" +#include "ai_error.hpp" + +/** + * Class that handles all bridge related functions. + */ +class AIBridge : public AIObject { +public: + /** + * All bridge related error messages. + */ + enum ErrorMessages { + /** Base for bridge related errors */ + ERR_BRIDGE_BASE = AIError::ERR_CAT_BRIDGE << AIError::ERR_CAT_BIT_SIZE, + + /** + * The bridge you want to build is not available yet, + * or it is not available for the requested length. + */ + ERR_BRIDGE_TYPE_UNAVAILABLE, // [STR_5015_CAN_T_BUILD_BRIDGE_HERE] + + /** One (or more) of the bridge head(s) ends in water. */ + ERR_BRIDGE_CANNOT_END_IN_WATER, // [STR_02A0_ENDS_OF_BRIDGE_MUST_BOTH] + + /** The bride heads need to be on the same height */ + ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT, // [STR_BRIDGEHEADS_NOT_SAME_HEIGHT] + }; + + static const char *GetClassName() { return "AIBridge"; } + + /** + * Checks whether the given bridge type is valid. + * @param bridge_id The bridge to check. + * @return True if and only if the bridge type is valid. + */ + static bool IsValidBridge(BridgeID bridge_id); + + /** + * Checks whether the given tile is actually a bridge start or end tile. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile is the beginning or end of a bridge. + */ + static bool IsBridgeTile(TileIndex tile); + + /** + * Get the name of a bridge. + * @param bridge_id The bridge to get the name of. + * @pre IsValidBridge(bridge_id). + * @return The name the bridge has. + */ + static const char *GetName(BridgeID bridge_id); + + /** + * Get the maximum speed of a bridge (in km/h). + * @param bridge_id The bridge to get the maximum speed of. + * @pre IsValidBridge(bridge_id). + * @return The maximum speed the bridge has. + */ + static int32 GetMaxSpeed(BridgeID bridge_id); + + /** + * Get the new cost of a bridge. + * @param bridge_id The bridge to get the new cost of. + * @param length The length of the bridge. + * @pre IsValidBridge(bridge_id). + * @return The new cost the bridge has. + */ + static Money GetPrice(BridgeID bridge_id, uint length); + + /** + * Get the maximum length of a bridge. + * @param bridge_id The bridge to get the maximum length of. + * @pre IsValidBridge(bridge_id). + * @returns The maximum length the bridge has. + */ + static int32 GetMaxLength(BridgeID bridge_id); + + /** + * Get the minimum length of a bridge. + * @param bridge_id The bridge to get the minimum length of. + * @pre IsValidBridge(bridge_id). + * @returns The minimum length the bridge has. + */ + static int32 GetMinLength(BridgeID bridge_id); + + /** + * Get the year in which a bridge becomes available. + * @param bridge_id The bridge to get the year of availability of. + * @pre IsValidBridge(bridge_id). + * @returns The year of availability the bridge has. + * @note Years are like 2010, -10 (10 B.C.), 1950, .. + */ + static int32 GetYearAvailable(BridgeID bridge_id); + +#ifndef DOXYGEN_SKIP + /** + * Internal function to help BuildBridge in case of road. + */ + static bool _BuildBridgeRoad1(); + + /** + * Internal function to help BuildBridge in case of road. + */ + static bool _BuildBridgeRoad2(); +#endif + + /** + * Build a bridge from one tile to the other. + * As an extra for road, this functions builds two half-pieces of road on + * each end of the bridge, making it easier for you to connect it to your + * network. + * @param vehicle_type The vehicle-type of bridge to build. + * @param bridge_id The bridge-type to build. + * @param start Where to start the bridge. + * @param end Where to end the bridge. + * @pre AIMap::IsValidTile(start). + * @pre AIMap::IsValidTile(end). + * @pre 'start' and 'end' are in a straight line, i.e. + * AIMap::GetTileX(start) == AIMap::GetTileX(end) or + * AIMap::GetTileY(start) == AIMap::GetTileY(end). + * @pre vehicle_type == AIVehicle::VEHICLE_ROAD || (vehicle_type == AIVehicle::VEHICLE_RAIL && + * AIRail::IsRailTypeAvailable(AIRail::GetCurrentRailType())) || vehicle_type == AIVehicle::VEHICLE_WATER. + * @exception AIError::ERR_ALREADY_BUILT + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIError::ERR_LAND_SLOPED_WRONG + * @exception AIError::ERR_VEHICLE_IN_THE_WAY + * @exception AIBridge::ERR_BRIDGE_TYPE_UNAVAILABLE + * @exception AIBridge::ERR_BRIDGE_CANNOT_END_IN_WATER + * @exception AIBridge::ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT + * @return Whether the bridge has been/can be build or not. + * @note No matter if the road pieces were build or not, if building the + * bridge succeeded, this function returns true. + */ + static bool BuildBridge(AIVehicle::VehicleType vehicle_type, BridgeID bridge_id, TileIndex start, TileIndex end); + + /** + * Removes a bridge, by executing it on either the start or end tile. + * @param tile An end or start tile of the bridge. + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @return Whether the bridge has been/can be removed or not. + */ + static bool RemoveBridge(TileIndex tile); + + /** + * Get the tile that is on the other end of a bridge starting at tile. + * @param tile The tile that is an end of a bridge. + * @pre AIMap::IsValidTile(tile). + * @pre IsBridgeTile(tile). + * @return The TileIndex that is the other end of the bridge. + */ + static TileIndex GetOtherBridgeEnd(TileIndex tile); +}; + +#endif /* AI_BRIDGE_HPP */ diff --git a/src/ai/api/ai_bridge.hpp.sq b/src/ai/api/ai_bridge.hpp.sq new file mode 100644 index 000000000..6388d82cf --- /dev/null +++ b/src/ai/api/ai_bridge.hpp.sq @@ -0,0 +1,51 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_bridge.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AIBridge::ErrorMessages GetParam(ForceType<AIBridge::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIBridge::ErrorMessages)tmp; } + template <> int Return<AIBridge::ErrorMessages>(HSQUIRRELVM vm, AIBridge::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AIBridge to be used as Squirrel parameter */ + template <> AIBridge *GetParam(ForceType<AIBridge *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBridge *)instance; } + template <> AIBridge &GetParam(ForceType<AIBridge &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBridge *)instance; } + template <> const AIBridge *GetParam(ForceType<const AIBridge *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBridge *)instance; } + template <> const AIBridge &GetParam(ForceType<const AIBridge &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBridge *)instance; } + template <> int Return<AIBridge *>(HSQUIRRELVM vm, AIBridge *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIBridge", res, NULL, DefSQDestructorCallback<AIBridge>); return 1; } +}; // namespace SQConvert + +void SQAIBridge_Register(Squirrel *engine) { + DefSQClass <AIBridge> SQAIBridge("AIBridge"); + SQAIBridge.PreRegister(engine); + SQAIBridge.AddConstructor<void (AIBridge::*)(), 1>(engine, "x"); + + SQAIBridge.DefSQConst(engine, AIBridge::ERR_BRIDGE_BASE, "ERR_BRIDGE_BASE"); + SQAIBridge.DefSQConst(engine, AIBridge::ERR_BRIDGE_TYPE_UNAVAILABLE, "ERR_BRIDGE_TYPE_UNAVAILABLE"); + SQAIBridge.DefSQConst(engine, AIBridge::ERR_BRIDGE_CANNOT_END_IN_WATER, "ERR_BRIDGE_CANNOT_END_IN_WATER"); + SQAIBridge.DefSQConst(engine, AIBridge::ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT, "ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT"); + + AIError::RegisterErrorMap(STR_5015_CAN_T_BUILD_BRIDGE_HERE, AIBridge::ERR_BRIDGE_TYPE_UNAVAILABLE); + AIError::RegisterErrorMap(STR_02A0_ENDS_OF_BRIDGE_MUST_BOTH, AIBridge::ERR_BRIDGE_CANNOT_END_IN_WATER); + AIError::RegisterErrorMap(STR_BRIDGEHEADS_NOT_SAME_HEIGHT, AIBridge::ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT); + + AIError::RegisterErrorMapString(AIBridge::ERR_BRIDGE_TYPE_UNAVAILABLE, "ERR_BRIDGE_TYPE_UNAVAILABLE"); + AIError::RegisterErrorMapString(AIBridge::ERR_BRIDGE_CANNOT_END_IN_WATER, "ERR_BRIDGE_CANNOT_END_IN_WATER"); + AIError::RegisterErrorMapString(AIBridge::ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT, "ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT"); + + SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetClassName, "GetClassName", 1, "x"); + SQAIBridge.DefSQStaticMethod(engine, &AIBridge::IsValidBridge, "IsValidBridge", 2, "xi"); + SQAIBridge.DefSQStaticMethod(engine, &AIBridge::IsBridgeTile, "IsBridgeTile", 2, "xi"); + SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetName, "GetName", 2, "xi"); + SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetMaxSpeed, "GetMaxSpeed", 2, "xi"); + SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetPrice, "GetPrice", 3, "xii"); + SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetMaxLength, "GetMaxLength", 2, "xi"); + SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetMinLength, "GetMinLength", 2, "xi"); + SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetYearAvailable, "GetYearAvailable", 2, "xi"); + SQAIBridge.DefSQStaticMethod(engine, &AIBridge::BuildBridge, "BuildBridge", 5, "xiiii"); + SQAIBridge.DefSQStaticMethod(engine, &AIBridge::RemoveBridge, "RemoveBridge", 2, "xi"); + SQAIBridge.DefSQStaticMethod(engine, &AIBridge::GetOtherBridgeEnd, "GetOtherBridgeEnd", 2, "xi"); + + SQAIBridge.PostRegister(engine); +} diff --git a/src/ai/api/ai_bridgelist.cpp b/src/ai/api/ai_bridgelist.cpp new file mode 100644 index 000000000..4f2fb2719 --- /dev/null +++ b/src/ai/api/ai_bridgelist.cpp @@ -0,0 +1,25 @@ +/* $Id$ */ + +/** @file ai_bridgelist.cpp Implementation of AIBridgeList and friends. */ + +#include "ai_bridgelist.hpp" +#include "ai_bridge.hpp" +#include "../../openttd.h" +#include "../../bridge.h" +#include "../../date_func.h" + +AIBridgeList::AIBridgeList() +{ + /* Add all bridges, no matter if they are available or not */ + for (byte j = 0; j < MAX_BRIDGES; j++) + if (::GetBridgeSpec(j)->avail_year <= _cur_year) + this->AddItem(j); +} + +AIBridgeList_Length::AIBridgeList_Length(uint length) +{ + for (byte j = 0; j < MAX_BRIDGES; j++) + if (::GetBridgeSpec(j)->avail_year <= _cur_year) + if (length >= (uint)AIBridge::GetMinLength(j) && length <= (uint)AIBridge::GetMaxLength(j)) + this->AddItem(j); +} diff --git a/src/ai/api/ai_bridgelist.hpp b/src/ai/api/ai_bridgelist.hpp new file mode 100644 index 000000000..b3ba39058 --- /dev/null +++ b/src/ai/api/ai_bridgelist.hpp @@ -0,0 +1,34 @@ +/* $Id$ */ + +/** @file ai_bridgelist.hpp List all the bridges. */ + +#ifndef AI_BRIDGELIST_HPP +#define AI_BRIDGELIST_HPP + +#include "ai_abstractlist.hpp" + +/** + * Create a list of bridges. + * @ingroup AIList + */ +class AIBridgeList : public AIAbstractList { +public: + static const char *GetClassName() { return "AIBridgeList"; } + AIBridgeList(); +}; + +/** + * Create a list of bridges that can be built on a specific length. + * @ingroup AIList + */ +class AIBridgeList_Length : public AIAbstractList { +public: + static const char *GetClassName() { return "AIBridgeList_Length"; } + + /** + * @param length The length of the bridge you want to build. + */ + AIBridgeList_Length(uint length); +}; + +#endif /* AI_BRIDGELIST_HPP */ diff --git a/src/ai/api/ai_bridgelist.hpp.sq b/src/ai/api/ai_bridgelist.hpp.sq new file mode 100644 index 000000000..f34b95518 --- /dev/null +++ b/src/ai/api/ai_bridgelist.hpp.sq @@ -0,0 +1,42 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_bridgelist.hpp" + +namespace SQConvert { + /* Allow AIBridgeList to be used as Squirrel parameter */ + template <> AIBridgeList *GetParam(ForceType<AIBridgeList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBridgeList *)instance; } + template <> AIBridgeList &GetParam(ForceType<AIBridgeList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBridgeList *)instance; } + template <> const AIBridgeList *GetParam(ForceType<const AIBridgeList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBridgeList *)instance; } + template <> const AIBridgeList &GetParam(ForceType<const AIBridgeList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBridgeList *)instance; } + template <> int Return<AIBridgeList *>(HSQUIRRELVM vm, AIBridgeList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIBridgeList", res, NULL, DefSQDestructorCallback<AIBridgeList>); return 1; } +}; // namespace SQConvert + +void SQAIBridgeList_Register(Squirrel *engine) { + DefSQClass <AIBridgeList> SQAIBridgeList("AIBridgeList"); + SQAIBridgeList.PreRegister(engine, "AIAbstractList"); + SQAIBridgeList.AddConstructor<void (AIBridgeList::*)(), 1>(engine, "x"); + + SQAIBridgeList.DefSQStaticMethod(engine, &AIBridgeList::GetClassName, "GetClassName", 1, "x"); + + SQAIBridgeList.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIBridgeList_Length to be used as Squirrel parameter */ + template <> AIBridgeList_Length *GetParam(ForceType<AIBridgeList_Length *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBridgeList_Length *)instance; } + template <> AIBridgeList_Length &GetParam(ForceType<AIBridgeList_Length &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBridgeList_Length *)instance; } + template <> const AIBridgeList_Length *GetParam(ForceType<const AIBridgeList_Length *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIBridgeList_Length *)instance; } + template <> const AIBridgeList_Length &GetParam(ForceType<const AIBridgeList_Length &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIBridgeList_Length *)instance; } + template <> int Return<AIBridgeList_Length *>(HSQUIRRELVM vm, AIBridgeList_Length *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIBridgeList_Length", res, NULL, DefSQDestructorCallback<AIBridgeList_Length>); return 1; } +}; // namespace SQConvert + +void SQAIBridgeList_Length_Register(Squirrel *engine) { + DefSQClass <AIBridgeList_Length> SQAIBridgeList_Length("AIBridgeList_Length"); + SQAIBridgeList_Length.PreRegister(engine, "AIAbstractList"); + SQAIBridgeList_Length.AddConstructor<void (AIBridgeList_Length::*)(uint length), 2>(engine, "xi"); + + SQAIBridgeList_Length.DefSQStaticMethod(engine, &AIBridgeList_Length::GetClassName, "GetClassName", 1, "x"); + + SQAIBridgeList_Length.PostRegister(engine); +} diff --git a/src/ai/api/ai_cargo.cpp b/src/ai/api/ai_cargo.cpp new file mode 100644 index 000000000..6843f7134 --- /dev/null +++ b/src/ai/api/ai_cargo.cpp @@ -0,0 +1,57 @@ +/* $Id$ */ + +/** @file ai_cargo.cpp Implementation of AICargo. */ + +#include "ai_cargo.hpp" +#include "../../openttd.h" +#include "../../cargotype.h" +#include "../../economy_func.h" +#include "../../core/alloc_func.hpp" +#include "../../core/bitmath_func.hpp" +#include "../../newgrf_cargo.h" + +/* static */ bool AICargo::IsValidCargo(CargoID cargo_type) +{ + return (cargo_type < NUM_CARGO && ::GetCargo(cargo_type)->IsValid()); +} + +/* static */ const char *AICargo::GetCargoLabel(CargoID cargo_type) +{ + if (!IsValidCargo(cargo_type)) return NULL; + const CargoSpec *cargo = ::GetCargo(cargo_type); + + /* cargo->label is a uint32 packing a 4 character non-terminated string, + * like "PASS", "COAL", "OIL_". New ones can be defined by NewGRFs */ + char *cargo_label = MallocT<char>(sizeof(cargo->label) + 1); + for (uint i = 0; i < sizeof(cargo->label); i++) { + cargo_label[i] = GB(cargo->label, (uint8)(sizeof(cargo->label) - i - 1) * 8, 8); + } + cargo_label[sizeof(cargo->label)] = '\0'; + return cargo_label; +} + +/* static */ bool AICargo::IsFreight(CargoID cargo_type) +{ + if (!IsValidCargo(cargo_type)) return false; + const CargoSpec *cargo = ::GetCargo(cargo_type); + return cargo->is_freight; +} + +/* static */ bool AICargo::HasCargoClass(CargoID cargo_type, CargoClass cargo_class) +{ + if (!IsValidCargo(cargo_type)) return false; + return ::IsCargoInClass(cargo_type, (::CargoClass)cargo_class); +} + +/* static */ AICargo::TownEffect AICargo::GetTownEffect(CargoID cargo_type) +{ + if (!IsValidCargo(cargo_type)) return TE_NONE; + + return (AICargo::TownEffect)GetCargo(cargo_type)->town_effect; +} + +/* static */ Money AICargo::GetCargoIncome(CargoID cargo_type, uint32 distance, uint32 days_in_transit) +{ + if (!IsValidCargo(cargo_type)) return -1; + return ::GetTransportedGoodsIncome(1, distance, Clamp(days_in_transit * 2 / 5, 0, 255), cargo_type); +} diff --git a/src/ai/api/ai_cargo.hpp b/src/ai/api/ai_cargo.hpp new file mode 100644 index 000000000..bde235758 --- /dev/null +++ b/src/ai/api/ai_cargo.hpp @@ -0,0 +1,94 @@ +/* $Id$ */ + +/** @file ai_cargo.hpp Everything to query cargos. */ + +#ifndef AI_CARGO_HPP +#define AI_CARGO_HPP + +#include "ai_object.hpp" + +/** + * Class that handles all cargo related functions. + */ +class AICargo : public AIObject { +public: + static const char *GetClassName() { return "AICargo"; } + + /** + * The classes of cargo (from newgrf_cargo.h). + */ + enum CargoClass { + CC_PASSENGERS = 1 << 0, ///< Passengers + CC_MAIL = 1 << 1, ///< Mail + CC_EXPRESS = 1 << 2, ///< Express cargo (Goods, Food, Candy, but also possible for passengers) + CC_ARMOURED = 1 << 3, ///< Armoured cargo (Valuables, Gold, Diamonds) + CC_BULK = 1 << 4, ///< Bulk cargo (Coal, Grain etc., Ores, Fruit) + CC_PIECE_GOODS = 1 << 5, ///< Piece goods (Livestock, Wood, Steel, Paper) + CC_LIQUID = 1 << 6, ///< Liquids (Oil, Water, Rubber) + CC_REFRIGERATED = 1 << 7, ///< Refrigerated cargo (Food, Fruit) + CC_HAZARDOUS = 1 << 8, ///< Hazardous cargo (Nuclear Fuel, Explosives, etc.) + CC_COVERED = 1 << 9, ///< Covered/Sheltered Freight (Transporation in Box Vans, Silo Wagons, etc.) + }; + + /** + * The effects a cargo can have on a town. + */ + enum TownEffect { + TE_NONE = 0, ///< This cargo has no effect on a town + TE_PASSENGERS = 1, ///< This cargo supplies passengers to a town + TE_MAIL = 2, ///< This cargo supplies mail to a town + TE_GOODS = 3, ///< This cargo supplies goods to a town + TE_WATER = 4, ///< This cargo supplies water to a town + TE_FOOD = 5, ///< This cargo supplies food to a town + }; + + /** + * Checks whether the given cargo type is valid. + * @param cargo_type The cargo to check. + * @return True if and only if the cargo type is valid. + */ + static bool IsValidCargo(CargoID cargo_type); + + /** + * Gets the string representation of the cargo label. + * @param cargo_type The cargo to get the string representation of. + * @return The cargo label. + * @note Never use this to check if it is a certain cargo. NewGRF can + * redefine all of the names. + */ + static const char *GetCargoLabel(CargoID cargo_type); + + /** + * Checks whether the give cargo is a freight or not. + * @param cargo_type The cargo to check on. + * @return True if and only if the cargo is freight. + */ + static bool IsFreight(CargoID cargo_type); + + /** + * Check if this cargo is in the requested cargo class. + * @param cargo_type The cargo to check on. + * @param cargo_class The class to check for. + * @return True if and only if the cargo is in the cargo class. + */ + static bool HasCargoClass(CargoID cargo_type, CargoClass cargo_class); + + /** + * Get the effect this cargo has on a town. + * @param cargo_type The cargo to check on. + * @return The effect this cargo has on a town, or TE_NONE if it has no effect. + */ + static TownEffect GetTownEffect(CargoID cargo_type); + + /** + * Get the income for transporting a piece of cargo over the + * given distance within the specified time. + * @param cargo_type The cargo to transport. + * @param distance The distance the cargo travels from begin to end. + * @param days_in_transit Amount of (game) days the cargo is in transit. The max value of this variable is 637. Any value higher returns the same as 637 would. + * @return The amount of money that would be earned by this trip. + */ + static Money GetCargoIncome(CargoID cargo_type, uint32 distance, uint32 days_in_transit); +}; + +#endif /* AI_CARGO_HPP */ diff --git a/src/ai/api/ai_cargo.hpp.sq b/src/ai/api/ai_cargo.hpp.sq new file mode 100644 index 000000000..015d112b7 --- /dev/null +++ b/src/ai/api/ai_cargo.hpp.sq @@ -0,0 +1,52 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_cargo.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AICargo::CargoClass GetParam(ForceType<AICargo::CargoClass>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AICargo::CargoClass)tmp; } + template <> int Return<AICargo::CargoClass>(HSQUIRRELVM vm, AICargo::CargoClass res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AICargo::TownEffect GetParam(ForceType<AICargo::TownEffect>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AICargo::TownEffect)tmp; } + template <> int Return<AICargo::TownEffect>(HSQUIRRELVM vm, AICargo::TownEffect res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AICargo to be used as Squirrel parameter */ + template <> AICargo *GetParam(ForceType<AICargo *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargo *)instance; } + template <> AICargo &GetParam(ForceType<AICargo &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargo *)instance; } + template <> const AICargo *GetParam(ForceType<const AICargo *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargo *)instance; } + template <> const AICargo &GetParam(ForceType<const AICargo &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargo *)instance; } + template <> int Return<AICargo *>(HSQUIRRELVM vm, AICargo *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AICargo", res, NULL, DefSQDestructorCallback<AICargo>); return 1; } +}; // namespace SQConvert + +void SQAICargo_Register(Squirrel *engine) { + DefSQClass <AICargo> SQAICargo("AICargo"); + SQAICargo.PreRegister(engine); + SQAICargo.AddConstructor<void (AICargo::*)(), 1>(engine, "x"); + + SQAICargo.DefSQConst(engine, AICargo::CC_PASSENGERS, "CC_PASSENGERS"); + SQAICargo.DefSQConst(engine, AICargo::CC_MAIL, "CC_MAIL"); + SQAICargo.DefSQConst(engine, AICargo::CC_EXPRESS, "CC_EXPRESS"); + SQAICargo.DefSQConst(engine, AICargo::CC_ARMOURED, "CC_ARMOURED"); + SQAICargo.DefSQConst(engine, AICargo::CC_BULK, "CC_BULK"); + SQAICargo.DefSQConst(engine, AICargo::CC_PIECE_GOODS, "CC_PIECE_GOODS"); + SQAICargo.DefSQConst(engine, AICargo::CC_LIQUID, "CC_LIQUID"); + SQAICargo.DefSQConst(engine, AICargo::CC_REFRIGERATED, "CC_REFRIGERATED"); + SQAICargo.DefSQConst(engine, AICargo::CC_HAZARDOUS, "CC_HAZARDOUS"); + SQAICargo.DefSQConst(engine, AICargo::CC_COVERED, "CC_COVERED"); + SQAICargo.DefSQConst(engine, AICargo::TE_NONE, "TE_NONE"); + SQAICargo.DefSQConst(engine, AICargo::TE_PASSENGERS, "TE_PASSENGERS"); + SQAICargo.DefSQConst(engine, AICargo::TE_MAIL, "TE_MAIL"); + SQAICargo.DefSQConst(engine, AICargo::TE_GOODS, "TE_GOODS"); + SQAICargo.DefSQConst(engine, AICargo::TE_WATER, "TE_WATER"); + SQAICargo.DefSQConst(engine, AICargo::TE_FOOD, "TE_FOOD"); + + SQAICargo.DefSQStaticMethod(engine, &AICargo::GetClassName, "GetClassName", 1, "x"); + SQAICargo.DefSQStaticMethod(engine, &AICargo::IsValidCargo, "IsValidCargo", 2, "xi"); + SQAICargo.DefSQStaticMethod(engine, &AICargo::GetCargoLabel, "GetCargoLabel", 2, "xi"); + SQAICargo.DefSQStaticMethod(engine, &AICargo::IsFreight, "IsFreight", 2, "xi"); + SQAICargo.DefSQStaticMethod(engine, &AICargo::HasCargoClass, "HasCargoClass", 3, "xii"); + SQAICargo.DefSQStaticMethod(engine, &AICargo::GetTownEffect, "GetTownEffect", 2, "xi"); + SQAICargo.DefSQStaticMethod(engine, &AICargo::GetCargoIncome, "GetCargoIncome", 4, "xiii"); + + SQAICargo.PostRegister(engine); +} diff --git a/src/ai/api/ai_cargolist.cpp b/src/ai/api/ai_cargolist.cpp new file mode 100644 index 000000000..e480895fc --- /dev/null +++ b/src/ai/api/ai_cargolist.cpp @@ -0,0 +1,46 @@ +/* $Id$ */ + +/** @file ai_cargolist.cpp Implementation of AICargoList and friends. */ + +#include "ai_cargolist.hpp" +#include "ai_industry.hpp" +#include "../../openttd.h" +#include "../../cargotype.h" +#include "../../tile_type.h" +#include "../../industry.h" + +AICargoList::AICargoList() +{ + for (byte i = 0; i < NUM_CARGO; i++) { + const CargoSpec *c = ::GetCargo(i); + if (c->IsValid()) { + this->AddItem(i); + } + } +} + +AICargoList_IndustryAccepting::AICargoList_IndustryAccepting(IndustryID industry_id) +{ + if (!AIIndustry::IsValidIndustry(industry_id)) return; + + Industry *ind = ::GetIndustry(industry_id); + for (uint i = 0; i < lengthof(ind->accepts_cargo); i++) { + CargoID cargo_id = ind->accepts_cargo[i]; + if (cargo_id != CT_INVALID) { + this->AddItem(cargo_id); + } + } +} + +AICargoList_IndustryProducing::AICargoList_IndustryProducing(IndustryID industry_id) +{ + if (!AIIndustry::IsValidIndustry(industry_id)) return; + + Industry *ind = ::GetIndustry(industry_id); + for (uint i = 0; i < lengthof(ind->produced_cargo); i++) { + CargoID cargo_id = ind->produced_cargo[i]; + if (cargo_id != CT_INVALID) { + this->AddItem(cargo_id); + } + } +} diff --git a/src/ai/api/ai_cargolist.hpp b/src/ai/api/ai_cargolist.hpp new file mode 100644 index 000000000..86a99a393 --- /dev/null +++ b/src/ai/api/ai_cargolist.hpp @@ -0,0 +1,48 @@ +/* $Id$ */ + +/** @file ai_cargolist.hpp List all the cargos. */ + +#ifndef AI_CARGOLIST_HPP +#define AI_CARGOLIST_HPP + +#include "ai_abstractlist.hpp" + +/** + * Creates a list of cargos that can be produced in the current game. + * @ingroup AIList + */ +class AICargoList : public AIAbstractList { +public: + static const char *GetClassName() { return "AICargoList"; } + AICargoList(); +}; + +/** + * Creates a list of cargos that the given industry accepts. + * @ingroup AIList + */ +class AICargoList_IndustryAccepting : public AIAbstractList { +public: + static const char *GetClassName() { return "AICargoList_IndustryAccepting"; } + + /** + * @param industry_id The industry to get the list of cargos it accepts from. + */ + AICargoList_IndustryAccepting(IndustryID industry_id); +}; + +/** + * Creates a list of cargos that the given industry can produce. + * @ingroup AIList + */ +class AICargoList_IndustryProducing : public AIAbstractList { +public: + static const char *GetClassName() { return "AICargoList_IndustryProducing"; } + + /** + * @param industry_id The industry to get the list of cargos it produces from. + */ + AICargoList_IndustryProducing(IndustryID industry_id); +}; + +#endif /* AI_CARGOLIST_HPP */ diff --git a/src/ai/api/ai_cargolist.hpp.sq b/src/ai/api/ai_cargolist.hpp.sq new file mode 100644 index 000000000..379afed9b --- /dev/null +++ b/src/ai/api/ai_cargolist.hpp.sq @@ -0,0 +1,61 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_cargolist.hpp" + +namespace SQConvert { + /* Allow AICargoList to be used as Squirrel parameter */ + template <> AICargoList *GetParam(ForceType<AICargoList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargoList *)instance; } + template <> AICargoList &GetParam(ForceType<AICargoList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargoList *)instance; } + template <> const AICargoList *GetParam(ForceType<const AICargoList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargoList *)instance; } + template <> const AICargoList &GetParam(ForceType<const AICargoList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargoList *)instance; } + template <> int Return<AICargoList *>(HSQUIRRELVM vm, AICargoList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AICargoList", res, NULL, DefSQDestructorCallback<AICargoList>); return 1; } +}; // namespace SQConvert + +void SQAICargoList_Register(Squirrel *engine) { + DefSQClass <AICargoList> SQAICargoList("AICargoList"); + SQAICargoList.PreRegister(engine, "AIAbstractList"); + SQAICargoList.AddConstructor<void (AICargoList::*)(), 1>(engine, "x"); + + SQAICargoList.DefSQStaticMethod(engine, &AICargoList::GetClassName, "GetClassName", 1, "x"); + + SQAICargoList.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AICargoList_IndustryAccepting to be used as Squirrel parameter */ + template <> AICargoList_IndustryAccepting *GetParam(ForceType<AICargoList_IndustryAccepting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargoList_IndustryAccepting *)instance; } + template <> AICargoList_IndustryAccepting &GetParam(ForceType<AICargoList_IndustryAccepting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargoList_IndustryAccepting *)instance; } + template <> const AICargoList_IndustryAccepting *GetParam(ForceType<const AICargoList_IndustryAccepting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargoList_IndustryAccepting *)instance; } + template <> const AICargoList_IndustryAccepting &GetParam(ForceType<const AICargoList_IndustryAccepting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargoList_IndustryAccepting *)instance; } + template <> int Return<AICargoList_IndustryAccepting *>(HSQUIRRELVM vm, AICargoList_IndustryAccepting *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AICargoList_IndustryAccepting", res, NULL, DefSQDestructorCallback<AICargoList_IndustryAccepting>); return 1; } +}; // namespace SQConvert + +void SQAICargoList_IndustryAccepting_Register(Squirrel *engine) { + DefSQClass <AICargoList_IndustryAccepting> SQAICargoList_IndustryAccepting("AICargoList_IndustryAccepting"); + SQAICargoList_IndustryAccepting.PreRegister(engine, "AIAbstractList"); + SQAICargoList_IndustryAccepting.AddConstructor<void (AICargoList_IndustryAccepting::*)(IndustryID industry_id), 2>(engine, "xi"); + + SQAICargoList_IndustryAccepting.DefSQStaticMethod(engine, &AICargoList_IndustryAccepting::GetClassName, "GetClassName", 1, "x"); + + SQAICargoList_IndustryAccepting.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AICargoList_IndustryProducing to be used as Squirrel parameter */ + template <> AICargoList_IndustryProducing *GetParam(ForceType<AICargoList_IndustryProducing *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargoList_IndustryProducing *)instance; } + template <> AICargoList_IndustryProducing &GetParam(ForceType<AICargoList_IndustryProducing &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargoList_IndustryProducing *)instance; } + template <> const AICargoList_IndustryProducing *GetParam(ForceType<const AICargoList_IndustryProducing *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICargoList_IndustryProducing *)instance; } + template <> const AICargoList_IndustryProducing &GetParam(ForceType<const AICargoList_IndustryProducing &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICargoList_IndustryProducing *)instance; } + template <> int Return<AICargoList_IndustryProducing *>(HSQUIRRELVM vm, AICargoList_IndustryProducing *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AICargoList_IndustryProducing", res, NULL, DefSQDestructorCallback<AICargoList_IndustryProducing>); return 1; } +}; // namespace SQConvert + +void SQAICargoList_IndustryProducing_Register(Squirrel *engine) { + DefSQClass <AICargoList_IndustryProducing> SQAICargoList_IndustryProducing("AICargoList_IndustryProducing"); + SQAICargoList_IndustryProducing.PreRegister(engine, "AIAbstractList"); + SQAICargoList_IndustryProducing.AddConstructor<void (AICargoList_IndustryProducing::*)(IndustryID industry_id), 2>(engine, "xi"); + + SQAICargoList_IndustryProducing.DefSQStaticMethod(engine, &AICargoList_IndustryProducing::GetClassName, "GetClassName", 1, "x"); + + SQAICargoList_IndustryProducing.PostRegister(engine); +} diff --git a/src/ai/api/ai_company.cpp b/src/ai/api/ai_company.cpp new file mode 100644 index 000000000..9a426bd2b --- /dev/null +++ b/src/ai/api/ai_company.cpp @@ -0,0 +1,200 @@ +/* $Id$ */ + +/** @file ai_company.cpp Implementation of AICompany. */ + +#include "ai_company.hpp" +#include "ai_error.hpp" +#include "ai_log.hpp" +#include "../../openttd.h" +#include "../../command_func.h" +#include "../../company_func.h" +#include "../../company_base.h" +#include "../../economy_func.h" +#include "../../strings_func.h" +#include "../../tile_map.h" +#include "../../variables.h" +#include "../../core/alloc_func.hpp" +#include "../../string_func.h" +#include "table/strings.h" + +/* static */ AICompany::CompanyID AICompany::ResolveCompanyID(AICompany::CompanyID company) +{ + if (company == MY_COMPANY) return (CompanyID)((byte)_current_company); + + return ::IsValidCompanyID((::CompanyID)company) ? company : INVALID_COMPANY; +} + +/* static */ bool AICompany::IsMine(AICompany::CompanyID company) +{ + return ResolveCompanyID(company) == ResolveCompanyID(MY_COMPANY); +} + +/* static */ bool AICompany::SetCompanyName(const char *name) +{ + AILog::Error("AICompany::SetCompanyName is obsolete. Use AICompany::SetName instead."); + return AICompany::SetName(name); +} + +/* static */ bool AICompany::SetName(const char *name) +{ + EnforcePrecondition(false, !::StrEmpty(name)); + EnforcePreconditionCustomError(false, ::strlen(name) < MAX_LENGTH_COMPANY_NAME_BYTES, AIError::ERR_PRECONDITION_STRING_TOO_LONG); + + return AIObject::DoCommand(0, 0, 0, CMD_RENAME_COMPANY, name); +} + +/* static */ const char *AICompany::GetCompanyName(AICompany::CompanyID company) +{ + AILog::Error("AICompany::GetCompanyName is obsolete. Use AICompany::GetName instead."); + return AICompany::GetName(company); +} + +/* static */ const char *AICompany::GetName(AICompany::CompanyID company) +{ + company = ResolveCompanyID(company); + if (company == INVALID_COMPANY) return NULL; + + static const int len = 64; + char *company_name = MallocT<char>(len); + + ::SetDParam(0, company); + ::GetString(company_name, STR_COMPANY_NAME, &company_name[len - 1]); + return company_name; +} + +/* static */ bool AICompany::SetPresidentName(const char *name) +{ + EnforcePrecondition(false, !::StrEmpty(name)); + + return AIObject::DoCommand(0, 0, 0, CMD_RENAME_PRESIDENT, name); +} + +/* static */ const char *AICompany::GetPresidentName(AICompany::CompanyID company) +{ + company = ResolveCompanyID(company); + + static const int len = 64; + char *president_name = MallocT<char>(len); + if (company != INVALID_COMPANY) { + ::SetDParam(0, company); + ::GetString(president_name, STR_PRESIDENT_NAME, &president_name[len - 1]); + } else { + *president_name = '\0'; + } + + return president_name; +} + +/* static */ Money AICompany::GetCompanyValue(AICompany::CompanyID company) +{ + company = ResolveCompanyID(company); + if (company == INVALID_COMPANY) return -1; + + return ::CalculateCompanyValue(::GetCompany((CompanyID)company)); +} + +/* static */ Money AICompany::GetBankBalance(AICompany::CompanyID company) +{ + company = ResolveCompanyID(company); + if (company == INVALID_COMPANY) return -1; + + return ::GetCompany((CompanyID)company)->money; +} + +/* static */ Money AICompany::GetLoanAmount() +{ + return ::GetCompany(_current_company)->current_loan; +} + +/* static */ Money AICompany::GetMaxLoanAmount() +{ + return _economy.max_loan; +} + +/* static */ Money AICompany::GetLoanInterval() +{ + return LOAN_INTERVAL; +} + +/* static */ bool AICompany::SetLoanAmount(int32 loan) +{ + EnforcePrecondition(false, loan >= 0); + EnforcePrecondition(false, (loan % GetLoanInterval()) == 0); + EnforcePrecondition(false, loan <= GetMaxLoanAmount()); + EnforcePrecondition(false, (loan - GetLoanAmount() + GetBankBalance(MY_COMPANY)) >= 0); + + if (loan == GetLoanAmount()) return true; + + return AIObject::DoCommand(0, + abs(loan - GetLoanAmount()), 2, + (loan > GetLoanAmount()) ? CMD_INCREASE_LOAN : CMD_DECREASE_LOAN); +} + +/* static */ bool AICompany::SetMinimumLoanAmount(int32 loan) +{ + EnforcePrecondition(false, loan >= 0); + + int32 over_interval = loan % GetLoanInterval(); + if (over_interval != 0) loan += GetLoanInterval() - over_interval; + + EnforcePrecondition(false, loan <= GetMaxLoanAmount()); + + SetLoanAmount(loan); + + return GetLoanAmount() == loan; +} + +/* static */ bool AICompany::BuildCompanyHQ(TileIndex tile) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + + return AIObject::DoCommand(tile, 0, 0, CMD_BUILD_COMPANY_HQ); +} + +/* static */ TileIndex AICompany::GetCompanyHQ(CompanyID company) +{ + company = ResolveCompanyID(company); + if (company == INVALID_COMPANY) return INVALID_TILE; + + TileIndex loc = ::GetCompany((CompanyID)company)->location_of_HQ; + return (loc == 0) ? INVALID_TILE : loc; +} + +/* static */ bool AICompany::SetAutoRenewStatus(bool autorenew) +{ + return AIObject::DoCommand(0, 0, autorenew ? 1 : 0, CMD_SET_AUTOREPLACE); +} + +/* static */ bool AICompany::GetAutoRenewStatus(CompanyID company) +{ + company = ResolveCompanyID(company); + if (company == INVALID_COMPANY) return false; + + return ::GetCompany((CompanyID)company)->engine_renew; +} + +/* static */ bool AICompany::SetAutoRenewMonths(int16 months) +{ + return AIObject::DoCommand(0, 1, months, CMD_SET_AUTOREPLACE); +} + +/* static */ int16 AICompany::GetAutoRenewMonths(CompanyID company) +{ + company = ResolveCompanyID(company); + if (company == INVALID_COMPANY) return 0; + + return ::GetCompany((CompanyID)company)->engine_renew_months; +} + +/* static */ bool AICompany::SetAutoRenewMoney(uint32 money) +{ + return AIObject::DoCommand(0, 2, money, CMD_SET_AUTOREPLACE); +} + +/* static */ uint32 AICompany::GetAutoRenewMoney(CompanyID company) +{ + company = ResolveCompanyID(company); + if (company == INVALID_COMPANY) return 0; + + return ::GetCompany((CompanyID)company)->engine_renew_money; +} diff --git a/src/ai/api/ai_company.hpp b/src/ai/api/ai_company.hpp new file mode 100644 index 000000000..2497e43b7 --- /dev/null +++ b/src/ai/api/ai_company.hpp @@ -0,0 +1,214 @@ +/* $Id$ */ + +/** @file ai_company.hpp Everything to query a company's financials and statistics or build company related buildings. */ + +#ifndef AI_COMPANY_HPP +#define AI_COMPANY_HPP + +#include "ai_object.hpp" + +/** + * Class that handles all company related functions. + */ +class AICompany : public AIObject { +public: + static const char *GetClassName() { return "AICompany"; } + + /** Different constants related to CompanyID. */ + enum CompanyID { + INVALID_COMPANY = -1, //!< An invalid company. + FIRST_COMPANY = 0, //!< The first available company. + LAST_COMPANY = 7, //!< The last available company. + MY_COMPANY = 8, //!< Constant that gets resolved to the correct company index for your company. + }; + + /** + * Resolved the given company index to the correct index for the company. If + * the company index was MY_COMPANY it will be resolved to the index of + * your company. If the company with the given index does not exist it will + * return INVALID_COMPANY. + * @param company The company index to resolve. + * @return The resolved company index. + */ + static CompanyID ResolveCompanyID(CompanyID company); + + /** + * Check if a CompanyID is your CompanyID, to ease up checks. + * @param company The company index to check. + * @return True if and only if this company is your CompanyID. + */ + static bool IsMine(CompanyID company); + + /** + * Obsolete, use AICompany::SetName instead. + */ + static bool SetCompanyName(const char *name); + + /** + * Set the name of your company. + * @param name The new name of the company. + * @pre 'name' must have at least one character. + * @pre 'name' must have at most 30 characters. + * @exception AIError::ERR_NAME_IS_NOT_UNIQUE + * @return True if the name was changed. + */ + static bool SetName(const char *name); + + /** + * Obsolete, use AICompany::GetName instead. + */ + static const char *GetCompanyName(CompanyID company); + + /** + * Get the name of the given company. + * @param company The company to get the name for. + * @pre ResolveCompanyID(company) != INVALID_COMPANY + * @return The name of the given company. + */ + static const char *GetName(CompanyID company); + + /** + * Set the name of your president. + * @param name The new name of the president. + * @pre 'name' must have at least one character. + * @exception AIError::ERR_NAME_IS_NOT_UNIQUE + * @return True if the name was changed. + */ + static bool SetPresidentName(const char *name); + + /** + * Get the name of the president of the given company. + * @param company The company to get the president's name for. + * @pre ResolveCompanyID(company) != INVALID_COMPANY + * @return The name of the president of the given company. + */ + static const char *GetPresidentName(CompanyID company); + + /** + * Sets the amount to loan. + * @param loan The amount to loan (multiplier of GetLoanInterval()). + * @pre 'loan' must be non-negative. + * @pre GetLoanInterval() must be a multiplier of GetLoanInterval(). + * @pre 'loan' must be below GetMaxLoanAmount(). + * @pre 'loan' - GetLoanAmount() + GetBankBalance() must be non-negative. + * @return True if the loan could be set to your requested amount. + */ + static bool SetLoanAmount(int32 loan); + + /** + * Sets the minimum amount to loan, i.e. the given amount of loan rounded up. + * @param loan The amount to loan (any positive number). + * @pre 'loan' must be non-negative. + * @pre 'loan' must be below GetMaxLoanAmount(). + * @return True if we could allocate a minimum of "loan" loan. + */ + static bool SetMinimumLoanAmount(int32 loan); + + /** + * Gets the amount your company have loaned. + * @return The amount loaned money. + * @post The return value is always non-negative. + * @post GetLoanInterval() is always a multiplier of the return value. + */ + static Money GetLoanAmount(); + + /** + * Gets the maximum amount your company can loan. + * @return The maximum amount your company can loan. + * @post The return value is always non-negative. + * @post GetLoanInterval() is always a multiplier of the return value. + */ + static Money GetMaxLoanAmount(); + + /** + * Gets the interval/loan step. + * @return The loan step. + * @post Return value is always positive. + */ + static Money GetLoanInterval(); + + /** + * Gets the current value of the given company. + * @param company The company to get the company value of. + * @pre ResolveCompanyID(company) != INVALID_COMPANY + * @return The current value of the given company. + */ + static Money GetCompanyValue(CompanyID company); + + /** + * Gets the bank balance. In other words, the amount of money the given company can spent. + * @param company The company to get the bank balance of. + * @pre ResolveCompanyID(company) != INVALID_COMPANY + * @return The actual bank balance. + */ + static Money GetBankBalance(CompanyID company); + + /** + * Build your company's HQ on the given tile. + * @param tile The tile to build your HQ on, this tile is the most nothern tile of your HQ. + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIError::ERR_FLAT_LAND_REQUIRED + * @return True if the HQ could be build. + * @note An HQ can not be removed, only by water or rebuilding; If an HQ is + * build again, the old one is removed. + */ + static bool BuildCompanyHQ(TileIndex tile); + + /** + * Return the location of a company's HQ. + * @param company The company the get the HQ of. + * @pre ResolveCompanyID(company) != INVALID_COMPANY. + * @return The tile of the company's HQ, this tile is the most nothern tile of that HQ, or INVALID_TILE if there is no HQ yet. + */ + static TileIndex GetCompanyHQ(CompanyID company); + + /** + * Set whether autorenew is enabled for your company. + * @param autorenew The new autorenew status. + * @return True if autorenew status has been modified. + */ + static bool SetAutoRenewStatus(bool autorenew); + + /** + * Return whether autorenew is enabled for a company. + * @param company The company to get the autorenew status of. + * @pre ResolveCompanyID(company) != INVALID_COMPANY. + * @return True if autorenew is enabled. + */ + static bool GetAutoRenewStatus(CompanyID company); + + /** + * Set the number of months before/after max age to autorenew an engine for your company. + * @param months The new months between autorenew. + * @return True if autorenew months has been modified. + */ + static bool SetAutoRenewMonths(int16 months); + + /** + * Return the number of months before/after max age to autorenew an engine for a company. + * @param company The company to get the autorenew months of. + * @pre ResolveCompanyID(company) != INVALID_COMPANY. + * @return The months before/after max age of engine. + */ + static int16 GetAutoRenewMonths(CompanyID company); + + /** + * Set the minimum money needed to autorenew an engine for your company. + * @param money The new minimum required money for autorenew to work. + * @return True if autorenew money has been modified. + */ + static bool SetAutoRenewMoney(uint32 money); + + /** + * Return the minimum money needed to autorenew an engine for a company. + * @param company The company to get the autorenew money of. + * @pre ResolveCompanyID(company) != INVALID_COMPANY. + * @return The minimum required money for autorenew to work. + */ + static uint32 GetAutoRenewMoney(CompanyID company); +}; + +DECLARE_POSTFIX_INCREMENT(AICompany::CompanyID); + +#endif /* AI_COMPANY_HPP */ diff --git a/src/ai/api/ai_company.hpp.sq b/src/ai/api/ai_company.hpp.sq new file mode 100644 index 000000000..0f0bb5ccf --- /dev/null +++ b/src/ai/api/ai_company.hpp.sq @@ -0,0 +1,55 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_company.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AICompany::CompanyID GetParam(ForceType<AICompany::CompanyID>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AICompany::CompanyID)tmp; } + template <> int Return<AICompany::CompanyID>(HSQUIRRELVM vm, AICompany::CompanyID res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AICompany to be used as Squirrel parameter */ + template <> AICompany *GetParam(ForceType<AICompany *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICompany *)instance; } + template <> AICompany &GetParam(ForceType<AICompany &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICompany *)instance; } + template <> const AICompany *GetParam(ForceType<const AICompany *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AICompany *)instance; } + template <> const AICompany &GetParam(ForceType<const AICompany &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AICompany *)instance; } + template <> int Return<AICompany *>(HSQUIRRELVM vm, AICompany *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AICompany", res, NULL, DefSQDestructorCallback<AICompany>); return 1; } +}; // namespace SQConvert + +void SQAICompany_Register(Squirrel *engine) { + DefSQClass <AICompany> SQAICompany("AICompany"); + SQAICompany.PreRegister(engine); + SQAICompany.AddConstructor<void (AICompany::*)(), 1>(engine, "x"); + + SQAICompany.DefSQConst(engine, AICompany::INVALID_COMPANY, "INVALID_COMPANY"); + SQAICompany.DefSQConst(engine, AICompany::FIRST_COMPANY, "FIRST_COMPANY"); + SQAICompany.DefSQConst(engine, AICompany::LAST_COMPANY, "LAST_COMPANY"); + SQAICompany.DefSQConst(engine, AICompany::MY_COMPANY, "MY_COMPANY"); + + SQAICompany.DefSQStaticMethod(engine, &AICompany::GetClassName, "GetClassName", 1, "x"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::ResolveCompanyID, "ResolveCompanyID", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::IsMine, "IsMine", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::SetCompanyName, "SetCompanyName", 2, "xs"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::SetName, "SetName", 2, "xs"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::GetCompanyName, "GetCompanyName", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::GetName, "GetName", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::SetPresidentName, "SetPresidentName", 2, "xs"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::GetPresidentName, "GetPresidentName", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::SetLoanAmount, "SetLoanAmount", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::SetMinimumLoanAmount, "SetMinimumLoanAmount", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::GetLoanAmount, "GetLoanAmount", 1, "x"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::GetMaxLoanAmount, "GetMaxLoanAmount", 1, "x"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::GetLoanInterval, "GetLoanInterval", 1, "x"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::GetCompanyValue, "GetCompanyValue", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::GetBankBalance, "GetBankBalance", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::BuildCompanyHQ, "BuildCompanyHQ", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::GetCompanyHQ, "GetCompanyHQ", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::SetAutoRenewStatus, "SetAutoRenewStatus", 2, "xb"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::GetAutoRenewStatus, "GetAutoRenewStatus", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::SetAutoRenewMonths, "SetAutoRenewMonths", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::GetAutoRenewMonths, "GetAutoRenewMonths", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::SetAutoRenewMoney, "SetAutoRenewMoney", 2, "xi"); + SQAICompany.DefSQStaticMethod(engine, &AICompany::GetAutoRenewMoney, "GetAutoRenewMoney", 2, "xi"); + + SQAICompany.PostRegister(engine); +} diff --git a/src/ai/api/ai_controller.cpp b/src/ai/api/ai_controller.cpp new file mode 100644 index 000000000..cb1fb6819 --- /dev/null +++ b/src/ai/api/ai_controller.cpp @@ -0,0 +1,85 @@ +/* $Id$ */ + +/** @file ai_controller.cpp Implementation of AIControler. */ + +#include "../../stdafx.h" +#include "../../openttd.h" +#include "../../company_func.h" +#include "../../core/alloc_func.hpp" +#include "../../string_func.h" +#include "../../settings_type.h" +#include "../../company_base.h" +#include "../../saveload/saveload.h" +#include "table/strings.h" + +#include "../ai.hpp" +#include "ai_controller.hpp" +#include "../ai_info.hpp" +#include "../ai_storage.hpp" +#include "../ai_instance.hpp" +#include "../ai_config.hpp" +#include "ai_log.hpp" + +/* static */ void AIController::SetCommandDelay(int ticks) +{ + if (ticks <= 0) return; + AIObject::SetDoCommandDelay(ticks); +} + +/* static */ void AIController::Sleep(int ticks) +{ + if (ticks <= 0) { + AILog::Warning("Sleep() value should be > 0. Assuming value 1."); + ticks = 1; + } + + throw AI_VMSuspend(ticks, NULL); +} + +/* static */ void AIController::Print(bool error_msg, const char *message) +{ + AILog::Log(error_msg ? AILog::LOG_SQ_ERROR : AILog::LOG_SQ_INFO, message); +} + +AIController::AIController() : + ticks(0), + loaded_library_count(0) +{ +} + +AIController::~AIController() +{ + for (LoadedLibraryList::iterator iter = this->loaded_library.begin(); iter != this->loaded_library.end(); iter++) { + free((void *)(*iter).second); + free((void *)(*iter).first); + } + + this->loaded_library.clear(); +} + +uint AIController::GetTick() +{ + return this->ticks; +} + +int AIController::GetSetting(const char *name) +{ + return AIConfig::GetConfig(_current_company)->GetSetting(name); +} + +bool AIController::LoadedLibrary(const char *library_name, int *next_number, char *fake_class_name, int fake_class_name_len) +{ + LoadedLibraryList::iterator iter = this->loaded_library.find(library_name); + if (iter == this->loaded_library.end()) { + *next_number = ++this->loaded_library_count; + return false; + } + + ttd_strlcpy(fake_class_name, (*iter).second, fake_class_name_len); + return true; +} + +void AIController::AddLoadedLibrary(const char *library_name, const char *fake_class_name) +{ + this->loaded_library[strdup(library_name)] = strdup(fake_class_name); +} diff --git a/src/ai/api/ai_controller.hpp b/src/ai/api/ai_controller.hpp new file mode 100644 index 000000000..1f1df743e --- /dev/null +++ b/src/ai/api/ai_controller.hpp @@ -0,0 +1,117 @@ +/* $Id$ */ + +/** @file ai_controller.hpp The controller of the AI. */ + +#ifndef AI_CONTROLLER_HPP +#define AI_CONTROLLER_HPP + +#include <map> +#ifndef AI_HPP +struct ltstr { bool operator()(const char *s1, const char *s2) const { return strcmp(s1, s2) < 0; } }; +#endif /* AI_HPP */ + +/** + * The Controller, the class each AI should extend. It creates the AI, makes + * sure the logic kicks in correctly, and that GetTick() has a valid value. + */ +class AIController { + friend class AIScanner; + friend class AIInstance; + +public: + static const char *GetClassName() { return "AIController"; } + + /** + * Initializer of the AIController. + */ + AIController(); + + /** + * Destructor of the AIController. + */ + ~AIController(); + + /** + * This function is called to start your AI. Your AI starts here. If you + * return from this function, your AI dies, so make sure that doesn't + * happen. + * @note Cannot be called from within your AI. + */ + void Start(); + + /** + * Find at which tick your AI currently is. + * @return returns the current tick. + */ + uint GetTick(); + + /** + * Get the value of one of your settings you set via info.nut. + * @param name The name of the setting. + * @return the value for the setting, or -1 if the setting is not known. + */ + int GetSetting(const char *name); + + /** + * Change the minimum amount of time the AI should be put in suspend mode + * when you execute a command. Normally in SP this is 1, and in MP it is + * what ever delay the server has been programmed to delay commands + * (normally between 1 and 5). To give a more 'real' effect to your AI, + * you can control that number here. + * @param ticks The minimum amount of ticks to wait. + * @pre Ticks should be positive. Too big values will influence performance of the AI. + * @note If the number is lower then the MP setting, the MP setting wins. + */ + static void SetCommandDelay(int ticks); + + /** + * Sleep for X ticks. The code continues after this line when the X AI ticks + * are passed. Mind that an AI tick is different from in-game ticks and + * differ per AI speed. + * @param ticks the ticks to wait + * @pre ticks > 0. + * @post the value of GetTick() will be changed exactly 'ticks' in value after + * calling this. + */ + static void Sleep(int ticks); + + /** + * When Squirrel triggers a print, this function is called. + * Squirrel calls this when 'print' is used, or when the script made an error. + * @param error_msg If true, it is a Squirrel error message. + * @param message The message Squirrel logged. + * @note Use AILog.Info/Warning/Error instead of 'print'. + */ + static void Print(bool error_msg, const char *message); + +private: + typedef std::map<const char *, const char *, ltstr> LoadedLibraryList; + + uint ticks; + LoadedLibraryList loaded_library; + int loaded_library_count; + + /** + * Register all classes that are known inside the NoAI API. + */ + void RegisterClasses(); + + /** + * Check if a library is already loaded. If found, fake_class_name is filled + * with the fake class name as given via AddLoadedLibrary. If not found, + * next_number is set to the next number available for the fake namespace. + * @param library_name The library to check if already loaded. + * @param next_number The next available number for a library if not already loaded. + * @param fake_class_name The name the library has if already loaded. + * @param fake_class_name_len The maximum length of fake_class_name. + * @return True if the library is already loaded. + */ + bool LoadedLibrary(const char *library_name, int *next_number, char *fake_class_name, int fake_class_name_len); + + /** + * Add a library as loaded. + */ + void AddLoadedLibrary(const char *library_name, const char *fake_class_name); +}; + +#endif /* AI_CONTROLLER_HPP */ diff --git a/src/ai/api/ai_controller.hpp.sq b/src/ai/api/ai_controller.hpp.sq new file mode 100644 index 000000000..d72574be7 --- /dev/null +++ b/src/ai/api/ai_controller.hpp.sq @@ -0,0 +1,12 @@ +#include "ai_controller.hpp" + +void SQAIController_Register(Squirrel *engine) { + DefSQClass <AIController> SQAIController("AIController"); + SQAIController.PreRegister(engine); + SQAIController.DefSQMethod(engine, &AIController::GetTick, "GetTick", 1, "x"); + SQAIController.DefSQStaticMethod(engine, &AIController::SetCommandDelay, "SetCommandDelay", 2, "xi"); + SQAIController.DefSQStaticMethod(engine, &AIController::Sleep, "Sleep", 2, "xi"); + SQAIController.DefSQStaticMethod(engine, &AIController::GetSetting, "GetSetting", 2, "xs"); + SQAIController.DefSQStaticMethod(engine, &AIController::Print, "Print", 3, "xbs"); + SQAIController.PostRegister(engine); +} diff --git a/src/ai/api/ai_date.cpp b/src/ai/api/ai_date.cpp new file mode 100644 index 000000000..b3ad552c9 --- /dev/null +++ b/src/ai/api/ai_date.cpp @@ -0,0 +1,47 @@ +/* $Id$ */ + +/** @file ai_date.cpp Implementation of AIDate. */ + +#include "ai_date.hpp" +#include "../../date_func.h" + +/* static */ int32 AIDate::GetCurrentDate() +{ + return ::_date; +} + +/* static */ int32 AIDate::GetYear(int32 date) +{ + if (date < 0) return -1; + + ::YearMonthDay ymd; + ::ConvertDateToYMD(date, &ymd); + return ymd.year; +} + +/* static */ int32 AIDate::GetMonth(int32 date) +{ + if (date < 0) return -1; + + ::YearMonthDay ymd; + ::ConvertDateToYMD(date, &ymd); + return ymd.month + 1; +} + +/* static */ int32 AIDate::GetDayOfMonth(int32 date) +{ + if (date < 0) return -1; + + ::YearMonthDay ymd; + ::ConvertDateToYMD(date, &ymd); + return ymd.day; +} + +/* static */ int32 AIDate::GetDate(int32 year, int32 month, int32 day_of_month) +{ + if (month < 1 || month > 12) return -1; + if (day_of_month < 1 || day_of_month > 31) return -1; + if (year < 0 || year > MAX_YEAR) return -1; + + return ::ConvertYMDToDate(year, month - 1, day_of_month); +} diff --git a/src/ai/api/ai_date.hpp b/src/ai/api/ai_date.hpp new file mode 100644 index 000000000..973b1b116 --- /dev/null +++ b/src/ai/api/ai_date.hpp @@ -0,0 +1,64 @@ +/* $Id$ */ + +/** @file ai_date.hpp Everything to query and manipulate date related information. */ + +#ifndef AI_DATE_HPP +#define AI_DATE_HPP + +#include "ai_object.hpp" + +/** + * Class that handles all date related (calculation) functions. + * + * @note Months and days of month are 1-based; the first month of the + * year is 1 and the first day of the month is also 1. + * @note Years are zero based; they start with the year 0. + * @note Dates can be used to determine the number of days between + * two different moments in time because they count the number + * of days since the year 0. + */ +class AIDate : public AIObject { +public: + static const char *GetClassName() { return "AIDate"; } + + /** + * Get the current date. + * This is the number of days since epoch under the assumption that + * there is a leap year every 4 years, except when dividable by + * 100 but not by 400. + * @return The current date. + */ + static int32 GetCurrentDate(); + + /** + * Get the year of the given date. + * @param date The date to get the year of. + * @return The year. + */ + static int32 GetYear(int32 date); + + /** + * Get the month of the given date. + * @param date The date to get the month of. + * @return The month. + */ + static int32 GetMonth(int32 date); + + /** + * Get the day (of the month) of the given date. + * @param date The date to get the day of. + * @return The day. + */ + static int32 GetDayOfMonth(int32 date); + + /** + * Get the date given a year, month and day of month. + * @param year The year of the to-be determined date. + * @param month The month of the to-be determined date. + * @param day_of_month The day of month of the to-be determined date. + * @return The date. + */ + static int32 GetDate(int32 year, int32 month, int32 day_of_month); +}; + +#endif /* AI_DATE_HPP */ diff --git a/src/ai/api/ai_date.hpp.sq b/src/ai/api/ai_date.hpp.sq new file mode 100644 index 000000000..5e6d7797d --- /dev/null +++ b/src/ai/api/ai_date.hpp.sq @@ -0,0 +1,28 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_date.hpp" + +namespace SQConvert { + /* Allow AIDate to be used as Squirrel parameter */ + template <> AIDate *GetParam(ForceType<AIDate *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIDate *)instance; } + template <> AIDate &GetParam(ForceType<AIDate &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIDate *)instance; } + template <> const AIDate *GetParam(ForceType<const AIDate *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIDate *)instance; } + template <> const AIDate &GetParam(ForceType<const AIDate &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIDate *)instance; } + template <> int Return<AIDate *>(HSQUIRRELVM vm, AIDate *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIDate", res, NULL, DefSQDestructorCallback<AIDate>); return 1; } +}; // namespace SQConvert + +void SQAIDate_Register(Squirrel *engine) { + DefSQClass <AIDate> SQAIDate("AIDate"); + SQAIDate.PreRegister(engine); + SQAIDate.AddConstructor<void (AIDate::*)(), 1>(engine, "x"); + + SQAIDate.DefSQStaticMethod(engine, &AIDate::GetClassName, "GetClassName", 1, "x"); + SQAIDate.DefSQStaticMethod(engine, &AIDate::GetCurrentDate, "GetCurrentDate", 1, "x"); + SQAIDate.DefSQStaticMethod(engine, &AIDate::GetYear, "GetYear", 2, "xi"); + SQAIDate.DefSQStaticMethod(engine, &AIDate::GetMonth, "GetMonth", 2, "xi"); + SQAIDate.DefSQStaticMethod(engine, &AIDate::GetDayOfMonth, "GetDayOfMonth", 2, "xi"); + SQAIDate.DefSQStaticMethod(engine, &AIDate::GetDate, "GetDate", 4, "xiii"); + + SQAIDate.PostRegister(engine); +} diff --git a/src/ai/api/ai_depotlist.cpp b/src/ai/api/ai_depotlist.cpp new file mode 100644 index 000000000..b93ec3aef --- /dev/null +++ b/src/ai/api/ai_depotlist.cpp @@ -0,0 +1,42 @@ +/* $Id$ */ + +/** @file ai_depotlist.cpp Implementation of AIDepotList and friends. */ + +#include "ai_depotlist.hpp" +#include "../../core/math_func.hpp" +#include "../../tile_map.h" +#include "../../company_func.h" +#include "../../depot_base.h" +#include "../../station_base.h" + +AIDepotList::AIDepotList(AITile::TransportType transport_type) +{ + ::TileType tile_type; + switch (transport_type) { + default: return; + + case AITile::TRANSPORT_ROAD: tile_type = ::MP_ROAD; break; + case AITile::TRANSPORT_RAIL: tile_type = ::MP_RAILWAY; break; + case AITile::TRANSPORT_WATER: tile_type = ::MP_WATER; break; + + case AITile::TRANSPORT_AIR: { + /* Hangars are not seen as real depots by the depot code. */ + const Station *st; + FOR_ALL_STATIONS(st) { + if (st->owner == ::_current_company) { + const AirportFTAClass *afc = st->Airport(); + for (uint i = 0; i < afc->nof_depots; i++) { + this->AddItem(st->xy + ToTileIndexDiff(afc->airport_depots[i])); + } + } + } + return; + } + } + + /* Handle 'standard' depots. */ + const Depot *depot; + FOR_ALL_DEPOTS(depot) { + if (::GetTileOwner(depot->xy) == ::_current_company && ::IsTileType(depot->xy, tile_type)) this->AddItem(depot->xy); + } +} diff --git a/src/ai/api/ai_depotlist.hpp b/src/ai/api/ai_depotlist.hpp new file mode 100644 index 000000000..0715b45bb --- /dev/null +++ b/src/ai/api/ai_depotlist.hpp @@ -0,0 +1,25 @@ +/* $Id$ */ + +/** @file ai_depotlist.hpp List all the depots (you own). */ + +#ifndef AI_DEPOTLIST_HPP +#define AI_DEPOTLIST_HPP + +#include "ai_abstractlist.hpp" +#include "ai_tile.hpp" + +/** + * Creates a list of the locations of the depots (and hangars) of which you are the owner. + * @ingroup AIList + */ +class AIDepotList : public AIAbstractList { +public: + static const char *GetClassName() { return "AIDepotList"; } + + /** + * @param transport_type The type of transport to make a list of depots for. + */ + AIDepotList(AITile::TransportType transport_type); +}; + +#endif /* AI_DEPOTLIST_HPP */ diff --git a/src/ai/api/ai_depotlist.hpp.sq b/src/ai/api/ai_depotlist.hpp.sq new file mode 100644 index 000000000..51f799490 --- /dev/null +++ b/src/ai/api/ai_depotlist.hpp.sq @@ -0,0 +1,23 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_depotlist.hpp" + +namespace SQConvert { + /* Allow AIDepotList to be used as Squirrel parameter */ + template <> AIDepotList *GetParam(ForceType<AIDepotList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIDepotList *)instance; } + template <> AIDepotList &GetParam(ForceType<AIDepotList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIDepotList *)instance; } + template <> const AIDepotList *GetParam(ForceType<const AIDepotList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIDepotList *)instance; } + template <> const AIDepotList &GetParam(ForceType<const AIDepotList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIDepotList *)instance; } + template <> int Return<AIDepotList *>(HSQUIRRELVM vm, AIDepotList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIDepotList", res, NULL, DefSQDestructorCallback<AIDepotList>); return 1; } +}; // namespace SQConvert + +void SQAIDepotList_Register(Squirrel *engine) { + DefSQClass <AIDepotList> SQAIDepotList("AIDepotList"); + SQAIDepotList.PreRegister(engine, "AIAbstractList"); + SQAIDepotList.AddConstructor<void (AIDepotList::*)(AITile::TransportType transport_type), 2>(engine, "xi"); + + SQAIDepotList.DefSQStaticMethod(engine, &AIDepotList::GetClassName, "GetClassName", 1, "x"); + + SQAIDepotList.PostRegister(engine); +} diff --git a/src/ai/api/ai_engine.cpp b/src/ai/api/ai_engine.cpp new file mode 100644 index 000000000..5215c95f3 --- /dev/null +++ b/src/ai/api/ai_engine.cpp @@ -0,0 +1,295 @@ +/* $Id$ */ + +/** @file ai_engine.cpp Implementation of AIEngine. */ + +#include "ai_engine.hpp" +#include "ai_cargo.hpp" +#include "ai_rail.hpp" +#include "../../openttd.h" +#include "../../company_func.h" +#include "../../strings_func.h" +#include "../../roadveh.h" +#include "../../train.h" +#include "../../ship.h" +#include "../../aircraft.h" +#include "../../vehicle_func.h" +#include "../../core/alloc_func.hpp" +#include "../../economy_func.h" +#include "../../core/bitmath_func.hpp" +#include "../../settings_type.h" +#include "../../articulated_vehicles.h" +#include "table/strings.h" + +/* static */ bool AIEngine::IsValidEngine(EngineID engine_id) +{ + return ::IsEngineIndex(engine_id) && HasBit(::GetEngine(engine_id)->company_avail, _current_company); +} + +/* static */ const char *AIEngine::GetName(EngineID engine_id) +{ + if (!IsValidEngine(engine_id)) return NULL; + + static const int len = 64; + char *engine_name = MallocT<char>(len); + + ::SetDParam(0, engine_id); + ::GetString(engine_name, STR_ENGINE_NAME, &engine_name[len - 1]); + return engine_name; +} + +/* static */ CargoID AIEngine::GetCargoType(EngineID engine_id) +{ + if (!IsValidEngine(engine_id)) return CT_INVALID; + + switch (::GetEngine(engine_id)->type) { + case VEH_ROAD: { + const RoadVehicleInfo *vi = ::RoadVehInfo(engine_id); + return vi->cargo_type; + } break; + + case VEH_TRAIN: { + const RailVehicleInfo *vi = ::RailVehInfo(engine_id); + return vi->cargo_type; + } break; + + case VEH_SHIP: { + const ShipVehicleInfo *vi = ::ShipVehInfo(engine_id); + return vi->cargo_type; + } break; + + case VEH_AIRCRAFT: { + return CT_PASSENGERS; + } break; + + default: NOT_REACHED(); + } +} + +/* static */ bool AIEngine::CanRefitCargo(EngineID engine_id, CargoID cargo_id) +{ + if (!IsValidEngine(engine_id)) return false; + if (!AICargo::IsValidCargo(cargo_id)) return false; + + if (GetCargoType(engine_id) == cargo_id) return true; + if (cargo_id == CT_MAIL && ::GetEngine(engine_id)->type == VEH_AIRCRAFT) return true; + if (::GetEngine(engine_id)->type == VEH_SHIP && !ShipVehInfo(engine_id)->refittable) return false; + return ::CanRefitTo(engine_id, cargo_id); +} + +/* static */ bool AIEngine::CanPullCargo(EngineID engine_id, CargoID cargo_id) +{ + if (!IsValidEngine(engine_id)) return false; + if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_RAIL) return false; + if (!AICargo::IsValidCargo(cargo_id)) return false; + + return (::RailVehInfo(engine_id)->ai_passenger_only != 1) || AICargo::HasCargoClass(cargo_id, AICargo::CC_PASSENGERS); +} + + +/* static */ int32 AIEngine::GetCapacity(EngineID engine_id) +{ + if (!IsValidEngine(engine_id)) return -1; + + switch (::GetEngine(engine_id)->type) { + case VEH_ROAD: + case VEH_TRAIN: { + uint16 *capacities = GetCapacityOfArticulatedParts(engine_id, ::GetEngine(engine_id)->type); + for (CargoID c = 0; c < NUM_CARGO; c++) { + if (capacities[c] == 0) continue; + return capacities[c]; + } + return -1; + } break; + + case VEH_SHIP: { + const ShipVehicleInfo *vi = ::ShipVehInfo(engine_id); + return vi->capacity; + } break; + + case VEH_AIRCRAFT: { + const AircraftVehicleInfo *vi = ::AircraftVehInfo(engine_id); + return vi->passenger_capacity; + } break; + + default: NOT_REACHED(); + } +} + +/* static */ int32 AIEngine::GetReliability(EngineID engine_id) +{ + if (!IsValidEngine(engine_id)) return -1; + + return (::GetEngine(engine_id)->reliability * 100 >> 16); +} + +/* static */ int32 AIEngine::GetMaxSpeed(EngineID engine_id) +{ + if (!IsValidEngine(engine_id)) return -1; + + switch (::GetEngine(engine_id)->type) { + case VEH_ROAD: { + const RoadVehicleInfo *vi = ::RoadVehInfo(engine_id); + /* Internal speeds are km/h * 2 */ + return vi->max_speed / 2; + } break; + + case VEH_TRAIN: { + const RailVehicleInfo *vi = ::RailVehInfo(engine_id); + return vi->max_speed; + } break; + + case VEH_SHIP: { + const ShipVehicleInfo *vi = ::ShipVehInfo(engine_id); + /* Internal speeds are km/h * 2 */ + return vi->max_speed / 2; + } break; + + case VEH_AIRCRAFT: { + const AircraftVehicleInfo *vi = ::AircraftVehInfo(engine_id); + return vi->max_speed / _settings_game.vehicle.plane_speed; + } break; + + default: NOT_REACHED(); + } +} + +/* static */ Money AIEngine::GetPrice(EngineID engine_id) +{ + if (!IsValidEngine(engine_id)) return -1; + + switch (::GetEngine(engine_id)->type) { + case VEH_ROAD: { + const RoadVehicleInfo *vi = ::RoadVehInfo(engine_id); + return (_price.roadveh_base >> 3) * vi->cost_factor >> 5; + } break; + + case VEH_TRAIN: { + const RailVehicleInfo *vi = ::RailVehInfo(engine_id); + return (_price.build_railvehicle >> 3) * vi->cost_factor >> 5; + } break; + + case VEH_SHIP: { + const ShipVehicleInfo *vi = ::ShipVehInfo(engine_id); + return (_price.ship_base >> 3) * vi->cost_factor >> 5; + } break; + + case VEH_AIRCRAFT: { + const AircraftVehicleInfo *vi = ::AircraftVehInfo(engine_id); + return (_price.aircraft_base >> 3) * vi->cost_factor >> 5; + } break; + + default: NOT_REACHED(); + } +} + +/* static */ int32 AIEngine::GetMaxAge(EngineID engine_id) +{ + if (!IsValidEngine(engine_id)) return -1; + + return ::GetEngine(engine_id)->lifelength * 366; +} + +/* static */ Money AIEngine::GetRunningCost(EngineID engine_id) +{ + if (!IsValidEngine(engine_id)) return -1; + + /* We need to create an instance in order to obtain GetRunningCost. + * This means we temporary allocate a vehicle in the pool, but + * there is no other way.. */ + Vehicle *vehicle; + switch (::GetEngine(engine_id)->type) { + case VEH_ROAD: { + vehicle = new RoadVehicle(); + } break; + + case VEH_TRAIN: { + vehicle = new Train(); + } break; + + case VEH_SHIP: { + vehicle = new Ship(); + } break; + + case VEH_AIRCRAFT: { + vehicle = new Aircraft(); + } break; + + default: NOT_REACHED(); + } + + vehicle->engine_type = engine_id; + Money runningCost = vehicle->GetRunningCost(); + delete vehicle; + return runningCost >> 8; +} + +/* static */ AIVehicle::VehicleType AIEngine::GetVehicleType(EngineID engine_id) +{ + if (!IsValidEngine(engine_id)) return AIVehicle::VEHICLE_INVALID; + + switch (::GetEngine(engine_id)->type) { + case VEH_ROAD: return AIVehicle::VEHICLE_ROAD; + case VEH_TRAIN: return AIVehicle::VEHICLE_RAIL; + case VEH_SHIP: return AIVehicle::VEHICLE_WATER; + case VEH_AIRCRAFT: return AIVehicle::VEHICLE_AIR; + default: NOT_REACHED(); + } +} + +/* static */ bool AIEngine::IsWagon(EngineID engine_id) +{ + if (!IsValidEngine(engine_id)) return false; + if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_RAIL) return false; + + return ::RailVehInfo(engine_id)->power == 0; +} + +/* static */ bool AIEngine::CanRunOnRail(EngineID engine_id, AIRail::RailType track_rail_type) +{ + if (!IsValidEngine(engine_id)) return false; + if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_RAIL) return false; + if (!AIRail::IsRailTypeAvailable(track_rail_type)) return false; + + return ::IsCompatibleRail((::RailType)::RailVehInfo(engine_id)->railtype, (::RailType)track_rail_type); +} + +/* static */ bool AIEngine::HasPowerOnRail(EngineID engine_id, AIRail::RailType track_rail_type) +{ + if (!IsValidEngine(engine_id)) return false; + if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_RAIL) return false; + if (!AIRail::IsRailTypeAvailable(track_rail_type)) return false; + + return ::HasPowerOnRail((::RailType)::RailVehInfo(engine_id)->railtype, (::RailType)track_rail_type); +} + +/* static */ AIRoad::RoadType AIEngine::GetRoadType(EngineID engine_id) +{ + if (!IsValidEngine(engine_id)) return AIRoad::ROADTYPE_INVALID; + if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_ROAD) return AIRoad::ROADTYPE_INVALID; + + return HasBit(::EngInfo(engine_id)->misc_flags, EF_ROAD_TRAM) ? AIRoad::ROADTYPE_TRAM : AIRoad::ROADTYPE_ROAD; +} + +/* static */ AIRail::RailType AIEngine::GetRailType(EngineID engine_id) +{ + if (!IsValidEngine(engine_id)) return AIRail::RAILTYPE_INVALID; + if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_RAIL) return AIRail::RAILTYPE_INVALID; + + return (AIRail::RailType)(uint)::RailVehInfo(engine_id)->railtype; +} + +/* static */ bool AIEngine::IsArticulated(EngineID engine_id) +{ + if (!IsValidEngine(engine_id)) return false; + if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_ROAD && GetVehicleType(engine_id) != AIVehicle::VEHICLE_RAIL) return false; + + return CountArticulatedParts(engine_id, true) != 0; +} + +/* static */ AIAirport::PlaneType AIEngine::GetPlaneType(EngineID engine_id) +{ + if (!IsValidEngine(engine_id)) return AIAirport::PT_INVALID; + if (GetVehicleType(engine_id) != AIVehicle::VEHICLE_AIR) return AIAirport::PT_INVALID; + + return (AIAirport::PlaneType)::AircraftVehInfo(engine_id)->subtype; +} diff --git a/src/ai/api/ai_engine.hpp b/src/ai/api/ai_engine.hpp new file mode 100644 index 000000000..67801107c --- /dev/null +++ b/src/ai/api/ai_engine.hpp @@ -0,0 +1,202 @@ +/* $Id$ */ + +/** @file ai_engine.hpp Everything to query and build engines. */ + +#ifndef AI_ENGINE_HPP +#define AI_ENGINE_HPP + +#include "ai_object.hpp" +#include "ai_vehicle.hpp" +#include "ai_road.hpp" +#include "ai_rail.hpp" +#include "ai_airport.hpp" + +/** + * Class that handles all engine related functions. + */ +class AIEngine : public AIObject { +public: + static const char *GetClassName() { return "AIEngine"; } + + /** + * Checks whether the given engine type is valid and buildable by you. + * @param engine_id The engine to check. + * @return True if and only if the engine type is valid. + */ + static bool IsValidEngine(EngineID engine_id); + + /** + * Get the name of an engine. + * @param engine_id The engine to get the name of. + * @pre IsValidEngine(engine_id). + * @return The name the engine has. + */ + static const char *GetName(EngineID engine_id); + + /** + * Get the cargo-type of an engine. In case it can transport 2 cargos, it + * returns the first. + * @param engine_id The engine to get the cargo-type of. + * @pre IsValidEngine(engine_id). + * @return The cargo-type of the engine. + */ + static CargoID GetCargoType(EngineID engine_id); + + /** + * Check if the cargo of an engine can be refitted to your requested. If + * the engine already allows this cargo, the function also returns true. + * @param engine_id The engine to check for refitting. + * @param cargo_id The cargo to check for refitting. + * @pre IsValidEngine(engine_id). + * @pre AICargo::IsValidCargo(cargo_id). + * @return True if the engine can carry this cargo, either via refit, or + * by default. + */ + static bool CanRefitCargo(EngineID engine_id, CargoID cargo_id); + + /** + * Check if the engine can pull a wagon with the given cargo. + * @param engine_id The engine to check. + * @param cargo_id The cargo to check. + * @pre IsValidEngine(engine_id). + * @pre GetVehicleType(engine_id) == AIVehicle.VEHICLE_RAIL. + * @pre AICargo::IsValidCargo(cargo_id). + * @return True if the engine can pull wagons carrying this cargo. + * @note This function is not exhaustive; a true here does not mean + * that the vehicle can pull the wagons, a false does mean it can't. + */ + static bool CanPullCargo(EngineID engine_id, CargoID cargo_id); + + /** + * Get the capacity of an engine. In case it can transport 2 cargos, it + * returns the first. + * @param engine_id The engine to get the capacity of. + * @pre IsValidEngine(engine_id). + * @return The capacity of the engine. + */ + static int32 GetCapacity(EngineID engine_id); + + /** + * Get the reliability of an engine. The value is between 0 and 100, where + * 100 means 100% reliability (never breaks down) and 0 means 0% + * reliability (you most likely don't want to buy it). + * @param engine_id The engine to get the reliability of. + * @pre IsValidEngine(engine_id). + * @return The reliability the engine has. + */ + static int32 GetReliability(EngineID engine_id); + + /** + * Get the maximum speed of an engine. + * @param engine_id The engine to get the maximum speed of. + * @pre IsValidEngine(engine_id). + * @return The maximum speed the engine has. + * @note The speed is in km/h. + */ + static int32 GetMaxSpeed(EngineID engine_id); + + /** + * Get the new cost of an engine. + * @param engine_id The engine to get the new cost of. + * @pre IsValidEngine(engine_id). + * @return The new cost the engine has. + */ + static Money GetPrice(EngineID engine_id); + + /** + * Get the maximum age of a brand new engine. + * @param engine_id The engine to get the maximum age of. + * @pre IsValidEngine(engine_id). + * @returns The maximum age of a new engine in days. + * @note Age is in days; divide by 366 to get per year. + */ + static int32 GetMaxAge(EngineID engine_id); + + /** + * Get the running cost of an engine. + * @param engine_id The engine to get the running cost of. + * @pre IsValidEngine(engine_id). + * @return The running cost of a vehicle per year. + * @note Cost is per year; divide by 364 to get per day. + */ + static Money GetRunningCost(EngineID engine_id); + + /** + * Get the type of an engine. + * @param engine_id The engine to get the type of. + * @pre IsValidEngine(engine_id). + * @return The type the engine has. + */ + static AIVehicle::VehicleType GetVehicleType(EngineID engine_id); + + /** + * Check if an engine is a wagon. + * @param engine_id The engine to check. + * @pre IsValidEngine(engine_id). + * @pre GetVehicleType(engine_id) == AIVehicle.VEHICLE_RAIL. + * @return Whether or not the engine is a wagon. + */ + static bool IsWagon(EngineID engine_id); + + /** + * Check if a train vehicle can run on a RailType. + * @param engine_id The engine to check. + * @param track_rail_type The type you want to check. + * @pre IsValidEngine(engine_id). + * @pre GetVehicleType(engine_id) == AIVehicle::VEHICLE_RAIL. + * @pre AIRail::IsRailTypeAvailable(track_rail_type). + * @return Whether an engine of type 'engine_id' can run on 'track_rail_type'. + * @note Even if a train can run on a RailType that doesn't mean that it'll be + * able to power the train. Use HasPowerOnRail for that. + */ + static bool CanRunOnRail(EngineID engine_id, AIRail::RailType track_rail_type); + + /** + * Check if a train engine has power on a RailType. + * @param engine_id The engine to check. + * @param track_rail_type Another RailType. + * @pre IsValidEngine(engine_id). + * @pre GetVehicleType(engine_id) == AIVehicle::VEHICLE_RAIL. + * @pre AIRail::IsRailTypeAvailable(track_rail_type). + * @return Whether an engine of type 'engine_id' has power on 'track_rail_type'. + */ + static bool HasPowerOnRail(EngineID engine_id, AIRail::RailType track_rail_type); + + /** + * Get the RoadType of the engine. + * @param engine_id The engine to get the RoadType of. + * @pre IsValidEngine(engine_id). + * @pre GetVehicleType(engine_id) == AIVehicle.VEHICLE_ROAD. + * @return The RoadType the engine has. + */ + static AIRoad::RoadType GetRoadType(EngineID engine_id); + + /** + * Get the RailType of the engine. + * @param engine_id The engine to get the RailType of. + * @pre IsValidEngine(engine_id). + * @pre GetVehicleType(engine_id) == AIVehicle.VEHICLE_RAIL. + * @return The RailType the engine has. + */ + static AIRail::RailType GetRailType(EngineID engine_id); + + /** + * Check if the engine is articulated. + * @param engine_id The engine to check. + * @pre IsValidEngine(engine_id). + * @pre GetVehicleType(engine_id) == AIVehicle.VEHICLE_ROAD || GetVehicleType(engine_id) == AIVehicle.VEHICLE_RAIL. + * @return True if the engine is articulated. + */ + static bool IsArticulated(EngineID engine_id); + + /** + * Get the PlaneType of the engine. + * @param engine_id The engine to get the PlaneType of. + * @pre IsValidEngine(engine_id). + * @pre GetVehicleType(engine_id) == AIVehicle.VEHICLE_AIR. + * @return The PlaneType the engine has. + */ + static AIAirport::PlaneType GetPlaneType(EngineID engine_id); +}; + +#endif /* AI_ENGINE_HPP */ diff --git a/src/ai/api/ai_engine.hpp.sq b/src/ai/api/ai_engine.hpp.sq new file mode 100644 index 000000000..067d3c65e --- /dev/null +++ b/src/ai/api/ai_engine.hpp.sq @@ -0,0 +1,42 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_engine.hpp" + +namespace SQConvert { + /* Allow AIEngine to be used as Squirrel parameter */ + template <> AIEngine *GetParam(ForceType<AIEngine *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEngine *)instance; } + template <> AIEngine &GetParam(ForceType<AIEngine &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEngine *)instance; } + template <> const AIEngine *GetParam(ForceType<const AIEngine *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEngine *)instance; } + template <> const AIEngine &GetParam(ForceType<const AIEngine &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEngine *)instance; } + template <> int Return<AIEngine *>(HSQUIRRELVM vm, AIEngine *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEngine", res, NULL, DefSQDestructorCallback<AIEngine>); return 1; } +}; // namespace SQConvert + +void SQAIEngine_Register(Squirrel *engine) { + DefSQClass <AIEngine> SQAIEngine("AIEngine"); + SQAIEngine.PreRegister(engine); + SQAIEngine.AddConstructor<void (AIEngine::*)(), 1>(engine, "x"); + + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetClassName, "GetClassName", 1, "x"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::IsValidEngine, "IsValidEngine", 2, "xi"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetName, "GetName", 2, "xi"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetCargoType, "GetCargoType", 2, "xi"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::CanRefitCargo, "CanRefitCargo", 3, "xii"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::CanPullCargo, "CanPullCargo", 3, "xii"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetCapacity, "GetCapacity", 2, "xi"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetReliability, "GetReliability", 2, "xi"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetMaxSpeed, "GetMaxSpeed", 2, "xi"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetPrice, "GetPrice", 2, "xi"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetMaxAge, "GetMaxAge", 2, "xi"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetRunningCost, "GetRunningCost", 2, "xi"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetVehicleType, "GetVehicleType", 2, "xi"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::IsWagon, "IsWagon", 2, "xi"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::CanRunOnRail, "CanRunOnRail", 3, "xii"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::HasPowerOnRail, "HasPowerOnRail", 3, "xii"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetRoadType, "GetRoadType", 2, "xi"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetRailType, "GetRailType", 2, "xi"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::IsArticulated, "IsArticulated", 2, "xi"); + SQAIEngine.DefSQStaticMethod(engine, &AIEngine::GetPlaneType, "GetPlaneType", 2, "xi"); + + SQAIEngine.PostRegister(engine); +} diff --git a/src/ai/api/ai_enginelist.cpp b/src/ai/api/ai_enginelist.cpp new file mode 100644 index 000000000..bd57025f6 --- /dev/null +++ b/src/ai/api/ai_enginelist.cpp @@ -0,0 +1,16 @@ +/* $Id$ */ + +/** @file ai_enginelist.cpp Implementation of AIEngineList and friends. */ + +#include "ai_enginelist.hpp" +#include "../../company_func.h" +#include "../../engine_base.h" +#include "../../core/bitmath_func.hpp" + +AIEngineList::AIEngineList(AIVehicle::VehicleType vehicle_type) +{ + Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, (::VehicleType)vehicle_type) { + if (HasBit(e->company_avail, _current_company)) this->AddItem(e->index); + } +} diff --git a/src/ai/api/ai_enginelist.hpp b/src/ai/api/ai_enginelist.hpp new file mode 100644 index 000000000..8c299caf3 --- /dev/null +++ b/src/ai/api/ai_enginelist.hpp @@ -0,0 +1,25 @@ +/* $Id$ */ + +/** @file ai_enginelist.hpp List all the engines. */ + +#ifndef AI_ENGINELIST_HPP +#define AI_ENGINELIST_HPP + +#include "ai_abstractlist.hpp" +#include "ai_vehicle.hpp" + +/** + * Create a list of engines based on a vehicle type. + * @ingroup AIList + */ +class AIEngineList : public AIAbstractList { +public: + static const char *GetClassName() { return "AIEngineList"; } + + /** + * @param vehicle_type The type of vehicle to make a list of engines for. + */ + AIEngineList(AIVehicle::VehicleType vehicle_type); +}; + +#endif /* AI_ENGINELIST_HPP */ diff --git a/src/ai/api/ai_enginelist.hpp.sq b/src/ai/api/ai_enginelist.hpp.sq new file mode 100644 index 000000000..98054d22d --- /dev/null +++ b/src/ai/api/ai_enginelist.hpp.sq @@ -0,0 +1,23 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_enginelist.hpp" + +namespace SQConvert { + /* Allow AIEngineList to be used as Squirrel parameter */ + template <> AIEngineList *GetParam(ForceType<AIEngineList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEngineList *)instance; } + template <> AIEngineList &GetParam(ForceType<AIEngineList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEngineList *)instance; } + template <> const AIEngineList *GetParam(ForceType<const AIEngineList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEngineList *)instance; } + template <> const AIEngineList &GetParam(ForceType<const AIEngineList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEngineList *)instance; } + template <> int Return<AIEngineList *>(HSQUIRRELVM vm, AIEngineList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEngineList", res, NULL, DefSQDestructorCallback<AIEngineList>); return 1; } +}; // namespace SQConvert + +void SQAIEngineList_Register(Squirrel *engine) { + DefSQClass <AIEngineList> SQAIEngineList("AIEngineList"); + SQAIEngineList.PreRegister(engine, "AIAbstractList"); + SQAIEngineList.AddConstructor<void (AIEngineList::*)(AIVehicle::VehicleType vehicle_type), 2>(engine, "xi"); + + SQAIEngineList.DefSQStaticMethod(engine, &AIEngineList::GetClassName, "GetClassName", 1, "x"); + + SQAIEngineList.PostRegister(engine); +} diff --git a/src/ai/api/ai_error.cpp b/src/ai/api/ai_error.cpp new file mode 100644 index 000000000..4c21db726 --- /dev/null +++ b/src/ai/api/ai_error.cpp @@ -0,0 +1,60 @@ +/* $Id$ */ + +/** @file ai_error.cpp Implementation of AIError. */ + +#include "ai_error.hpp" +#include "table/strings.h" +#include "../../core/bitmath_func.hpp" + +AIError::AIErrorMap AIError::error_map = AIError::AIErrorMap(); +AIError::AIErrorMapString AIError::error_map_string = AIError::AIErrorMapString(); + +/* static */ AIErrorType AIError::GetLastError() +{ + return AIObject::GetLastError(); +} + +/* static */ const char *AIError::GetLastErrorString() +{ + return (*error_map_string.find(AIError::GetLastError())).second; +} + +/* static */ AIErrorType AIError::StringToError(StringID internal_string_id) +{ + uint index = GB(internal_string_id, 11, 5); + switch (GB(internal_string_id, 11, 5)) { + case 26: case 28: case 29: case 30: // NewGRF strings. + return ERR_NEWGRF_SUPPLIED_ERROR; + + /* DO NOT SWAP case 14 and 4 because that will break StringToError due + * to the index dependency that relies on FALL THROUGHs. */ + case 14: if (index < 0xE4) break; // Player name + case 4: if (index < 0xC0) break; // Town name + case 15: // Custom name + case 31: // Dynamic strings + /* These strings are 'random' and have no meaning. + * They actually shouldn't even be returned as error messages. */ + return ERR_UNKNOWN; + + default: + break; + } + + AIErrorMap::iterator it = error_map.find(internal_string_id); + if (it == error_map.end()) return ERR_UNKNOWN; + return (*it).second; +} + +/* static */ void AIError::RegisterErrorMap(StringID internal_string_id, AIErrorType ai_error_msg) +{ + error_map[internal_string_id] = ai_error_msg; +} + +/* static */ void AIError::RegisterErrorMapString(AIErrorType ai_error_msg, const char *message) +{ + error_map_string[ai_error_msg] = message; +} + +/* static */ AIError::ErrorCategories AIError::GetErrorCategory() { + return (AIError::ErrorCategories)(GetLastError() >> (uint)ERR_CAT_BIT_SIZE); +} diff --git a/src/ai/api/ai_error.hpp b/src/ai/api/ai_error.hpp new file mode 100644 index 000000000..6a448ff3a --- /dev/null +++ b/src/ai/api/ai_error.hpp @@ -0,0 +1,171 @@ +/* $Id$ */ + +/** @file ai_error.hpp Everything to query errors. */ + +#ifndef AI_ERROR_HPP +#define AI_ERROR_HPP + +#include "ai_object.hpp" +#include <map> + +/** + * Helper to write precondition enforcers for the AI API in an abbreviated manner. + * @param returnval The value to return on failure. + * @param condition The condition that must be obeyed. + */ +#define EnforcePrecondition(returnval, condition) \ + if (!(condition)) { \ + AIObject::SetLastError(AIError::ERR_PRECONDITION_FAILED); \ + return returnval; \ + } + +/** + * Helper to write precondition enforcers for the AI API in an abbreviated manner. + * @param returnval The value to return on failure. + * @param condition The condition that must be obeyed. + * @param error_code The error code passed to AIObject::SetLastError. + */ +#define EnforcePreconditionCustomError(returnval, condition, error_code) \ + if (!(condition)) { \ + AIObject::SetLastError(error_code); \ + return returnval; \ + } + +/** + * Class that handles all error related functions. + */ +class AIError : public AIObject { +public: + static const char *GetClassName() { return "AIError"; } + + /** + * All categories errors can be divided in. + */ + enum ErrorCategories { + ERR_CAT_NONE = 0, //!< Error messages not related to any category. + ERR_CAT_GENERAL, //!< Error messages related to general things. + ERR_CAT_VEHICLE, //!< Error messages related to building / maintaining vehicles. + ERR_CAT_STATION, //!< Error messages related to building / maintaining stations. + ERR_CAT_BRIDGE, //!< Error messages related to building / removing bridges. + ERR_CAT_TUNNEL, //!< Error messages related to building / removing tunnels. + ERR_CAT_TILE, //!< Error messages related to raising / lowering and demolishing tiles. + ERR_CAT_SIGN, //!< Error messages related to building / removing signs. + ERR_CAT_RAIL, //!< Error messages related to building / maintaining rails. + ERR_CAT_ROAD, //!< Error messages related to building / maintaining roads. + ERR_CAT_ORDER, //!< Error messages related to managing orders. + ERR_CAT_MARINE, //!< Error messages related to building / removing ships, docks and channels. + + /** + * DO NOT USE! The error bitsize determines how many errors can be stored in + * a category and what the offsets are of all categories. + */ + ERR_CAT_BIT_SIZE = 8, + }; + + /** + * All general related error messages. + */ + enum ErrorMessages { + /** Initial error value */ + ERR_NONE = ERR_CAT_NONE << ERR_CAT_BIT_SIZE, // [] + /** If an error occured and the error wasn't mapped */ + ERR_UNKNOWN, // [] + /** If a precondition is not met */ + ERR_PRECONDITION_FAILED, // [] + /** A string supplied was too long */ + ERR_PRECONDITION_STRING_TOO_LONG, // [] + /** An error returned by a NewGRF. No possibility to get the exact error in an AI readable format */ + ERR_NEWGRF_SUPPLIED_ERROR, // [] + + /** Base for general errors */ + ERR_GENERAL_BASE = ERR_CAT_GENERAL << ERR_CAT_BIT_SIZE, + + /** Not enough cash to perform the previous action */ + ERR_NOT_ENOUGH_CASH, // [STR_0003_NOT_ENOUGH_CASH_REQUIRES] + + /** Local authority won't allow the previous action */ + ERR_LOCAL_AUTHORITY_REFUSES, // [STR_2009_LOCAL_AUTHORITY_REFUSES] + + /** The piece of infrastructure you tried to build is already in place */ + ERR_ALREADY_BUILT, // [STR_1007_ALREADY_BUILT, STR_5007_MUST_DEMOLISH_BRIDGE_FIRST] + + /** Area isn't clear, try to demolish the building on it */ + ERR_AREA_NOT_CLEAR, // [STR_2004_BUILDING_MUST_BE_DEMOLISHED, STR_5007_MUST_DEMOLISH_BRIDGE_FIRST, STR_300B_MUST_DEMOLISH_RAILROAD, STR_300E_MUST_DEMOLISH_AIRPORT_FIRST, STR_MUST_DEMOLISH_CARGO_TRAM_STATION, STR_3047_MUST_DEMOLISH_TRUCK_STATION, STR_MUST_DEMOLISH_PASSENGER_TRAM_STATION, STR_3046_MUST_DEMOLISH_BUS_STATION, STR_306A_BUOY_IN_THE_WAY, STR_304D_MUST_DEMOLISH_DOCK_FIRST, STR_4800_IN_THE_WAY, STR_5804_COMPANY_HEADQUARTERS_IN, STR_5800_OBJECT_IN_THE_WAY, STR_1801_MUST_REMOVE_ROAD_FIRST, STR_1008_MUST_REMOVE_RAILROAD_TRACK, STR_5007_MUST_DEMOLISH_BRIDGE_FIRST, STR_5006_MUST_DEMOLISH_TUNNEL_FIRST, STR_1002_EXCAVATION_WOULD_DAMAGE] + + /** Area / property is owned by another company */ + ERR_OWNED_BY_ANOTHER_COMPANY, // [STR_1024_AREA_IS_OWNED_BY_ANOTHER, STR_013B_OWNED_BY] + + /** The name given is not unique for the object type */ + ERR_NAME_IS_NOT_UNIQUE, // [STR_NAME_MUST_BE_UNIQUE] + + /** The building you want to build requires flat land */ + ERR_FLAT_LAND_REQUIRED, // [STR_0007_FLAT_LAND_REQUIRED] + + /** Land is sloped in the wrong direction for this build action */ + ERR_LAND_SLOPED_WRONG, // [STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION] + + /** A vehicle is in the way */ + ERR_VEHICLE_IN_THE_WAY, // [STR_8803_TRAIN_IN_THE_WAY, STR_9000_ROAD_VEHICLE_IN_THE_WAY, STR_980E_SHIP_IN_THE_WAY, STR_A015_AIRCRAFT_IN_THE_WAY] + + /** Site is unsuitable */ + ERR_SITE_UNSUITABLE, // [STR_0239_SITE_UNSUITABLE, STR_304B_SITE_UNSUITABLE] + + /** Too close to the edge of the map */ + ERR_TOO_CLOSE_TO_EDGE, // [STR_0002_TOO_CLOSE_TO_EDGE_OF_MAP] + + /** Station is too spread out */ + ERR_STATION_TOO_SPREAD_OUT, // [STR_306C_STATION_TOO_SPREAD_OUT] + }; + + /** + * Check the membership of the last thrown error. + * @return The category the error belongs to. + * @note The last throw error can be aquired by calling GetLastError(). + */ + static ErrorCategories GetErrorCategory(); + + /** + * Get the last error. + * @return An ErrorMessages enum value. + */ + static AIErrorType GetLastError(); + + /** + * Get the last error in string format (for human readability). + * @return An ErrorMessage enum item, as string. + */ + static const char *GetLastErrorString(); + + /** + * Get the error based on the OpenTTD StringID. + * @note DO NOT INVOKE THIS METHOD YOURSELF! + * @param internal_string_id The string to convert. + * @return The NoAI equivalent error message. + */ + static AIErrorType StringToError(StringID internal_string_id); + + /** + * Map an internal OpenTTD error message to it's NoAI equivalent. + * @note DO NOT INVOKE THIS METHOD YOURSELF! The calls are autogenerated. + * @param internal_string_id The OpenTTD StringID used for an error. + * @param ai_error_msg The NoAI equivalent error message. + */ + static void RegisterErrorMap(StringID internal_string_id, AIErrorType ai_error_msg); + + /** + * Map an internal OpenTTD error message to it's NoAI equivalent. + * @note DO NOT INVOKE THIS METHOD YOURSELF! The calls are autogenerated. + * @param ai_error_msg The NoAI error message representation. + * @param message The string representation of this error message, used for debug purposes. + */ + static void RegisterErrorMapString(AIErrorType ai_error_msg, const char *message); + +private: + typedef std::map<StringID, AIErrorType> AIErrorMap; + typedef std::map<AIErrorType, const char *> AIErrorMapString; + + static AIErrorMap error_map; + static AIErrorMapString error_map_string; +}; + +#endif /* AI_ERROR_HPP */ diff --git a/src/ai/api/ai_error.hpp.sq b/src/ai/api/ai_error.hpp.sq new file mode 100644 index 000000000..1955de76b --- /dev/null +++ b/src/ai/api/ai_error.hpp.sq @@ -0,0 +1,121 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_error.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AIError::ErrorCategories GetParam(ForceType<AIError::ErrorCategories>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIError::ErrorCategories)tmp; } + template <> int Return<AIError::ErrorCategories>(HSQUIRRELVM vm, AIError::ErrorCategories res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AIError::ErrorMessages GetParam(ForceType<AIError::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIError::ErrorMessages)tmp; } + template <> int Return<AIError::ErrorMessages>(HSQUIRRELVM vm, AIError::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AIError to be used as Squirrel parameter */ + template <> AIError *GetParam(ForceType<AIError *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIError *)instance; } + template <> AIError &GetParam(ForceType<AIError &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIError *)instance; } + template <> const AIError *GetParam(ForceType<const AIError *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIError *)instance; } + template <> const AIError &GetParam(ForceType<const AIError &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIError *)instance; } + template <> int Return<AIError *>(HSQUIRRELVM vm, AIError *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIError", res, NULL, DefSQDestructorCallback<AIError>); return 1; } +}; // namespace SQConvert + +void SQAIError_Register(Squirrel *engine) { + DefSQClass <AIError> SQAIError("AIError"); + SQAIError.PreRegister(engine); + SQAIError.AddConstructor<void (AIError::*)(), 1>(engine, "x"); + + SQAIError.DefSQConst(engine, AIError::ERR_CAT_NONE, "ERR_CAT_NONE"); + SQAIError.DefSQConst(engine, AIError::ERR_CAT_GENERAL, "ERR_CAT_GENERAL"); + SQAIError.DefSQConst(engine, AIError::ERR_CAT_VEHICLE, "ERR_CAT_VEHICLE"); + SQAIError.DefSQConst(engine, AIError::ERR_CAT_STATION, "ERR_CAT_STATION"); + SQAIError.DefSQConst(engine, AIError::ERR_CAT_BRIDGE, "ERR_CAT_BRIDGE"); + SQAIError.DefSQConst(engine, AIError::ERR_CAT_TUNNEL, "ERR_CAT_TUNNEL"); + SQAIError.DefSQConst(engine, AIError::ERR_CAT_TILE, "ERR_CAT_TILE"); + SQAIError.DefSQConst(engine, AIError::ERR_CAT_SIGN, "ERR_CAT_SIGN"); + SQAIError.DefSQConst(engine, AIError::ERR_CAT_RAIL, "ERR_CAT_RAIL"); + SQAIError.DefSQConst(engine, AIError::ERR_CAT_ROAD, "ERR_CAT_ROAD"); + SQAIError.DefSQConst(engine, AIError::ERR_CAT_ORDER, "ERR_CAT_ORDER"); + SQAIError.DefSQConst(engine, AIError::ERR_CAT_MARINE, "ERR_CAT_MARINE"); + SQAIError.DefSQConst(engine, AIError::ERR_CAT_BIT_SIZE, "ERR_CAT_BIT_SIZE"); + SQAIError.DefSQConst(engine, AIError::ERR_NONE, "ERR_NONE"); + SQAIError.DefSQConst(engine, AIError::ERR_UNKNOWN, "ERR_UNKNOWN"); + SQAIError.DefSQConst(engine, AIError::ERR_PRECONDITION_FAILED, "ERR_PRECONDITION_FAILED"); + SQAIError.DefSQConst(engine, AIError::ERR_PRECONDITION_STRING_TOO_LONG, "ERR_PRECONDITION_STRING_TOO_LONG"); + SQAIError.DefSQConst(engine, AIError::ERR_NEWGRF_SUPPLIED_ERROR, "ERR_NEWGRF_SUPPLIED_ERROR"); + SQAIError.DefSQConst(engine, AIError::ERR_GENERAL_BASE, "ERR_GENERAL_BASE"); + SQAIError.DefSQConst(engine, AIError::ERR_NOT_ENOUGH_CASH, "ERR_NOT_ENOUGH_CASH"); + SQAIError.DefSQConst(engine, AIError::ERR_LOCAL_AUTHORITY_REFUSES, "ERR_LOCAL_AUTHORITY_REFUSES"); + SQAIError.DefSQConst(engine, AIError::ERR_ALREADY_BUILT, "ERR_ALREADY_BUILT"); + SQAIError.DefSQConst(engine, AIError::ERR_AREA_NOT_CLEAR, "ERR_AREA_NOT_CLEAR"); + SQAIError.DefSQConst(engine, AIError::ERR_OWNED_BY_ANOTHER_COMPANY, "ERR_OWNED_BY_ANOTHER_COMPANY"); + SQAIError.DefSQConst(engine, AIError::ERR_NAME_IS_NOT_UNIQUE, "ERR_NAME_IS_NOT_UNIQUE"); + SQAIError.DefSQConst(engine, AIError::ERR_FLAT_LAND_REQUIRED, "ERR_FLAT_LAND_REQUIRED"); + SQAIError.DefSQConst(engine, AIError::ERR_LAND_SLOPED_WRONG, "ERR_LAND_SLOPED_WRONG"); + SQAIError.DefSQConst(engine, AIError::ERR_VEHICLE_IN_THE_WAY, "ERR_VEHICLE_IN_THE_WAY"); + SQAIError.DefSQConst(engine, AIError::ERR_SITE_UNSUITABLE, "ERR_SITE_UNSUITABLE"); + SQAIError.DefSQConst(engine, AIError::ERR_TOO_CLOSE_TO_EDGE, "ERR_TOO_CLOSE_TO_EDGE"); + SQAIError.DefSQConst(engine, AIError::ERR_STATION_TOO_SPREAD_OUT, "ERR_STATION_TOO_SPREAD_OUT"); + + AIError::RegisterErrorMap(STR_0003_NOT_ENOUGH_CASH_REQUIRES, AIError::ERR_NOT_ENOUGH_CASH); + AIError::RegisterErrorMap(STR_2009_LOCAL_AUTHORITY_REFUSES, AIError::ERR_LOCAL_AUTHORITY_REFUSES); + AIError::RegisterErrorMap(STR_1007_ALREADY_BUILT, AIError::ERR_ALREADY_BUILT); + AIError::RegisterErrorMap(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST, AIError::ERR_ALREADY_BUILT); + AIError::RegisterErrorMap(STR_2004_BUILDING_MUST_BE_DEMOLISHED, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_300B_MUST_DEMOLISH_RAILROAD, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_300E_MUST_DEMOLISH_AIRPORT_FIRST, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_MUST_DEMOLISH_CARGO_TRAM_STATION, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_3047_MUST_DEMOLISH_TRUCK_STATION, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_MUST_DEMOLISH_PASSENGER_TRAM_STATION, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_3046_MUST_DEMOLISH_BUS_STATION, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_306A_BUOY_IN_THE_WAY, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_304D_MUST_DEMOLISH_DOCK_FIRST, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_4800_IN_THE_WAY, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_5804_COMPANY_HEADQUARTERS_IN, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_5800_OBJECT_IN_THE_WAY, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_1801_MUST_REMOVE_ROAD_FIRST, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_1008_MUST_REMOVE_RAILROAD_TRACK, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_5006_MUST_DEMOLISH_TUNNEL_FIRST, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_1002_EXCAVATION_WOULD_DAMAGE, AIError::ERR_AREA_NOT_CLEAR); + AIError::RegisterErrorMap(STR_1024_AREA_IS_OWNED_BY_ANOTHER, AIError::ERR_OWNED_BY_ANOTHER_COMPANY); + AIError::RegisterErrorMap(STR_013B_OWNED_BY, AIError::ERR_OWNED_BY_ANOTHER_COMPANY); + AIError::RegisterErrorMap(STR_NAME_MUST_BE_UNIQUE, AIError::ERR_NAME_IS_NOT_UNIQUE); + AIError::RegisterErrorMap(STR_0007_FLAT_LAND_REQUIRED, AIError::ERR_FLAT_LAND_REQUIRED); + AIError::RegisterErrorMap(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION, AIError::ERR_LAND_SLOPED_WRONG); + AIError::RegisterErrorMap(STR_8803_TRAIN_IN_THE_WAY, AIError::ERR_VEHICLE_IN_THE_WAY); + AIError::RegisterErrorMap(STR_9000_ROAD_VEHICLE_IN_THE_WAY, AIError::ERR_VEHICLE_IN_THE_WAY); + AIError::RegisterErrorMap(STR_980E_SHIP_IN_THE_WAY, AIError::ERR_VEHICLE_IN_THE_WAY); + AIError::RegisterErrorMap(STR_A015_AIRCRAFT_IN_THE_WAY, AIError::ERR_VEHICLE_IN_THE_WAY); + AIError::RegisterErrorMap(STR_0239_SITE_UNSUITABLE, AIError::ERR_SITE_UNSUITABLE); + AIError::RegisterErrorMap(STR_304B_SITE_UNSUITABLE, AIError::ERR_SITE_UNSUITABLE); + AIError::RegisterErrorMap(STR_0002_TOO_CLOSE_TO_EDGE_OF_MAP, AIError::ERR_TOO_CLOSE_TO_EDGE); + AIError::RegisterErrorMap(STR_306C_STATION_TOO_SPREAD_OUT, AIError::ERR_STATION_TOO_SPREAD_OUT); + + AIError::RegisterErrorMapString(AIError::ERR_NONE, "ERR_NONE"); + AIError::RegisterErrorMapString(AIError::ERR_UNKNOWN, "ERR_UNKNOWN"); + AIError::RegisterErrorMapString(AIError::ERR_PRECONDITION_FAILED, "ERR_PRECONDITION_FAILED"); + AIError::RegisterErrorMapString(AIError::ERR_PRECONDITION_STRING_TOO_LONG, "ERR_PRECONDITION_STRING_TOO_LONG"); + AIError::RegisterErrorMapString(AIError::ERR_NEWGRF_SUPPLIED_ERROR, "ERR_NEWGRF_SUPPLIED_ERROR"); + AIError::RegisterErrorMapString(AIError::ERR_NOT_ENOUGH_CASH, "ERR_NOT_ENOUGH_CASH"); + AIError::RegisterErrorMapString(AIError::ERR_LOCAL_AUTHORITY_REFUSES, "ERR_LOCAL_AUTHORITY_REFUSES"); + AIError::RegisterErrorMapString(AIError::ERR_ALREADY_BUILT, "ERR_ALREADY_BUILT"); + AIError::RegisterErrorMapString(AIError::ERR_AREA_NOT_CLEAR, "ERR_AREA_NOT_CLEAR"); + AIError::RegisterErrorMapString(AIError::ERR_OWNED_BY_ANOTHER_COMPANY, "ERR_OWNED_BY_ANOTHER_COMPANY"); + AIError::RegisterErrorMapString(AIError::ERR_NAME_IS_NOT_UNIQUE, "ERR_NAME_IS_NOT_UNIQUE"); + AIError::RegisterErrorMapString(AIError::ERR_FLAT_LAND_REQUIRED, "ERR_FLAT_LAND_REQUIRED"); + AIError::RegisterErrorMapString(AIError::ERR_LAND_SLOPED_WRONG, "ERR_LAND_SLOPED_WRONG"); + AIError::RegisterErrorMapString(AIError::ERR_VEHICLE_IN_THE_WAY, "ERR_VEHICLE_IN_THE_WAY"); + AIError::RegisterErrorMapString(AIError::ERR_SITE_UNSUITABLE, "ERR_SITE_UNSUITABLE"); + AIError::RegisterErrorMapString(AIError::ERR_TOO_CLOSE_TO_EDGE, "ERR_TOO_CLOSE_TO_EDGE"); + AIError::RegisterErrorMapString(AIError::ERR_STATION_TOO_SPREAD_OUT, "ERR_STATION_TOO_SPREAD_OUT"); + + SQAIError.DefSQStaticMethod(engine, &AIError::GetClassName, "GetClassName", 1, "x"); + SQAIError.DefSQStaticMethod(engine, &AIError::GetErrorCategory, "GetErrorCategory", 1, "x"); + SQAIError.DefSQStaticMethod(engine, &AIError::GetLastError, "GetLastError", 1, "x"); + SQAIError.DefSQStaticMethod(engine, &AIError::GetLastErrorString, "GetLastErrorString", 1, "x"); + SQAIError.DefSQStaticMethod(engine, &AIError::StringToError, "StringToError", 2, "xi"); + SQAIError.DefSQStaticMethod(engine, &AIError::RegisterErrorMap, "RegisterErrorMap", 3, "xii"); + SQAIError.DefSQStaticMethod(engine, &AIError::RegisterErrorMapString, "RegisterErrorMapString", 3, "xis"); + + SQAIError.PostRegister(engine); +} diff --git a/src/ai/api/ai_event.cpp b/src/ai/api/ai_event.cpp new file mode 100644 index 000000000..b18fa4777 --- /dev/null +++ b/src/ai/api/ai_event.cpp @@ -0,0 +1,65 @@ +/* $Id$ */ + +/** @file ai_event.cpp Implementation of AIEvent. */ + +#include "ai_event.hpp" +#include "ai_event_types.hpp" + +#include <queue> +#include <set> + +struct AIEventData { + std::queue<AIEvent *> stack; +}; + +/* static */ void AIEventController::CreateEventPointer() +{ + assert(AIObject::GetEventPointer() == NULL); + + AIObject::GetEventPointer() = new AIEventData(); +} + +/* static */ void AIEventController::FreeEventPointer() +{ + AIEventData *data = (AIEventData *)AIObject::GetEventPointer(); + + /* Free all waiting events (if any) */ + while (!data->stack.empty()) { + AIEvent *e = data->stack.front(); + data->stack.pop(); + e->Release(); + } + + /* Now kill our data pointer */ + delete data; +} + +/* static */ bool AIEventController::IsEventWaiting() +{ + if (AIObject::GetEventPointer() == NULL) AIEventController::CreateEventPointer(); + AIEventData *data = (AIEventData *)AIObject::GetEventPointer(); + + return !data->stack.empty(); +} + +/* static */ AIEvent *AIEventController::GetNextEvent() +{ + if (AIObject::GetEventPointer() == NULL) AIEventController::CreateEventPointer(); + AIEventData *data = (AIEventData *)AIObject::GetEventPointer(); + + if (data->stack.empty()) return NULL; + + AIEvent *e = data->stack.front(); + data->stack.pop(); + return e; +} + +/* static */ void AIEventController::InsertEvent(AIEvent *event) +{ + if (AIObject::GetEventPointer() == NULL) AIEventController::CreateEventPointer(); + AIEventData *data = (AIEventData *)AIObject::GetEventPointer(); + + event->AddRef(); + data->stack.push(event); +} + diff --git a/src/ai/api/ai_event.hpp b/src/ai/api/ai_event.hpp new file mode 100644 index 000000000..a1304ec33 --- /dev/null +++ b/src/ai/api/ai_event.hpp @@ -0,0 +1,107 @@ +/* $Id$ */ + +/** @file ai_event.hpp Everything to handle events from the game. */ + +#ifndef AI_EVENT_HPP +#define AI_EVENT_HPP + +#include "ai_object.hpp" + +/** + * Class that handles all event related functions. + * You can lookup the type, and than convert it to the real event-class. + * That way you can request more detailed information about the event. + */ +class AIEvent : public AIObject { +public: + static const char *GetClassName() { return "AIEvent"; } + + /** + * The type of event. Needed to lookup the detailed class. + */ + enum AIEventType { + AI_ET_INVALID = 0, + AI_ET_TEST, + AI_ET_SUBSIDY_OFFER, + AI_ET_SUBSIDY_OFFER_EXPIRED, + AI_ET_SUBSIDY_AWARDED, + AI_ET_SUBSIDY_EXPIRED, + AI_ET_ENGINE_PREVIEW, + AI_ET_COMPANY_NEW, + AI_ET_COMPANY_IN_TROUBLE, + AI_ET_COMPANY_MERGER, + AI_ET_COMPANY_BANKRUPT, + AI_ET_VEHICLE_CRASHED, + AI_ET_VEHICLE_LOST, + AI_ET_VEHICLE_WAITING_IN_DEPOT, + AI_ET_VEHICLE_UNPROFITABLE, + AI_ET_INDUSTRY_OPEN, + AI_ET_INDUSTRY_CLOSE, + AI_ET_ENGINE_AVAILABLE, + AI_ET_STATION_FIRST_VEHICLE, + }; + + /** + * Constructor of AIEvent, to get the type of event. + */ + AIEvent(AIEvent::AIEventType type) : + type(type) + {} + + /** + * Get the event-type. + * @return The @c AIEventType. + */ + AIEventType GetEventType() { return this->type; } + +protected: + /** + * The type of this event. + */ + AIEventType type; +}; + +/** + * Class that handles all event related functions. + * @note it is not needed to create an instance of AIEvent to access it, as + * all members are static, and all data is stored AI-wide. + */ +class AIEventController : public AIObject { +public: + /** + * The name of the class, needed by several sub-processes. + */ + static const char *GetClassName() { return "AIEventController"; } + + /** + * Check if there is an event waiting. + * @return true if there is an event on the stack. + */ + static bool IsEventWaiting(); + + /** + * Get the next event. + * @return a class of the event-child issues. + */ + static AIEvent *GetNextEvent(); + + /** + * Insert an event to the queue for the company. + * @param event The event to insert. + */ + static void InsertEvent(AIEvent *event); + + /** + * Free the event pointer. + * @note DO NOT CALL YOURSELF; leave it to the internal AI programming. + */ + static void FreeEventPointer(); + +private: + /** + * Create the event pointer. + */ + static void CreateEventPointer(); +}; + +#endif /* AI_EVENT_HPP */ diff --git a/src/ai/api/ai_event.hpp.sq b/src/ai/api/ai_event.hpp.sq new file mode 100644 index 000000000..f5ac02203 --- /dev/null +++ b/src/ai/api/ai_event.hpp.sq @@ -0,0 +1,72 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_event.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AIEvent::AIEventType GetParam(ForceType<AIEvent::AIEventType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIEvent::AIEventType)tmp; } + template <> int Return<AIEvent::AIEventType>(HSQUIRRELVM vm, AIEvent::AIEventType res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AIEvent to be used as Squirrel parameter */ + template <> AIEvent *GetParam(ForceType<AIEvent *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEvent *)instance; } + template <> AIEvent &GetParam(ForceType<AIEvent &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEvent *)instance; } + template <> const AIEvent *GetParam(ForceType<const AIEvent *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEvent *)instance; } + template <> const AIEvent &GetParam(ForceType<const AIEvent &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEvent *)instance; } + template <> int Return<AIEvent *>(HSQUIRRELVM vm, AIEvent *res) { if (res == NULL) { sq_pushnull(vm); return 1; } Squirrel::CreateClassInstanceVM(vm, "AIEvent", res, NULL, DefSQDestructorCallback<AIEvent>); return 1; } +}; // namespace SQConvert + +void SQAIEvent_Register(Squirrel *engine) { + DefSQClass <AIEvent> SQAIEvent("AIEvent"); + SQAIEvent.PreRegister(engine); + SQAIEvent.AddConstructor<void (AIEvent::*)(AIEvent::AIEventType type), 2>(engine, "xi"); + + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_INVALID, "AI_ET_INVALID"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_TEST, "AI_ET_TEST"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_SUBSIDY_OFFER, "AI_ET_SUBSIDY_OFFER"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_SUBSIDY_OFFER_EXPIRED, "AI_ET_SUBSIDY_OFFER_EXPIRED"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_SUBSIDY_AWARDED, "AI_ET_SUBSIDY_AWARDED"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_SUBSIDY_EXPIRED, "AI_ET_SUBSIDY_EXPIRED"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_ENGINE_PREVIEW, "AI_ET_ENGINE_PREVIEW"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_COMPANY_NEW, "AI_ET_COMPANY_NEW"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_COMPANY_IN_TROUBLE, "AI_ET_COMPANY_IN_TROUBLE"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_COMPANY_MERGER, "AI_ET_COMPANY_MERGER"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_COMPANY_BANKRUPT, "AI_ET_COMPANY_BANKRUPT"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_VEHICLE_CRASHED, "AI_ET_VEHICLE_CRASHED"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_VEHICLE_LOST, "AI_ET_VEHICLE_LOST"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_VEHICLE_WAITING_IN_DEPOT, "AI_ET_VEHICLE_WAITING_IN_DEPOT"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_VEHICLE_UNPROFITABLE, "AI_ET_VEHICLE_UNPROFITABLE"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_INDUSTRY_OPEN, "AI_ET_INDUSTRY_OPEN"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_INDUSTRY_CLOSE, "AI_ET_INDUSTRY_CLOSE"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_ENGINE_AVAILABLE, "AI_ET_ENGINE_AVAILABLE"); + SQAIEvent.DefSQConst(engine, AIEvent::AI_ET_STATION_FIRST_VEHICLE, "AI_ET_STATION_FIRST_VEHICLE"); + + SQAIEvent.DefSQStaticMethod(engine, &AIEvent::GetClassName, "GetClassName", 1, "x"); + + SQAIEvent.DefSQMethod(engine, &AIEvent::GetEventType, "GetEventType", 1, "x"); + + SQAIEvent.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventController to be used as Squirrel parameter */ + template <> AIEventController *GetParam(ForceType<AIEventController *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventController *)instance; } + template <> AIEventController &GetParam(ForceType<AIEventController &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventController *)instance; } + template <> const AIEventController *GetParam(ForceType<const AIEventController *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventController *)instance; } + template <> const AIEventController &GetParam(ForceType<const AIEventController &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventController *)instance; } + template <> int Return<AIEventController *>(HSQUIRRELVM vm, AIEventController *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventController", res, NULL, DefSQDestructorCallback<AIEventController>); return 1; } +}; // namespace SQConvert + +void SQAIEventController_Register(Squirrel *engine) { + DefSQClass <AIEventController> SQAIEventController("AIEventController"); + SQAIEventController.PreRegister(engine); + SQAIEventController.AddConstructor<void (AIEventController::*)(), 1>(engine, "x"); + + SQAIEventController.DefSQStaticMethod(engine, &AIEventController::GetClassName, "GetClassName", 1, "x"); + SQAIEventController.DefSQStaticMethod(engine, &AIEventController::IsEventWaiting, "IsEventWaiting", 1, "x"); + SQAIEventController.DefSQStaticMethod(engine, &AIEventController::GetNextEvent, "GetNextEvent", 1, "x"); + SQAIEventController.DefSQStaticMethod(engine, &AIEventController::InsertEvent, "InsertEvent", 2, "xx"); + SQAIEventController.DefSQStaticMethod(engine, &AIEventController::FreeEventPointer, "FreeEventPointer", 1, "x"); + + SQAIEventController.PostRegister(engine); +} diff --git a/src/ai/api/ai_event_types.cpp b/src/ai/api/ai_event_types.cpp new file mode 100644 index 000000000..07b324cfa --- /dev/null +++ b/src/ai/api/ai_event_types.cpp @@ -0,0 +1,187 @@ +/* $Id$ */ + +/** @file ai_event_types.cpp Implementation of all EventTypes. */ + +#include "ai_event_types.hpp" +#include "../../openttd.h" +#include "../../core/alloc_func.hpp" +#include "../../strings_func.h" +#include "../../roadveh.h" +#include "../../train.h" +#include "../../ship.h" +#include "../../aircraft.h" +#include "../../settings_type.h" +#include "../../articulated_vehicles.h" +#include "table/strings.h" + +bool AIEventVehicleCrashed::CloneCrashedVehicle(TileIndex depot) +{ + return false; +} + +const char *AIEventEnginePreview::GetName() +{ + static const int len = 64; + char *engine_name = MallocT<char>(len); + + ::SetDParam(0, engine); + ::GetString(engine_name, STR_ENGINE_NAME, &engine_name[len - 1]); + return engine_name; +} + +CargoID AIEventEnginePreview::GetCargoType() +{ + switch (::GetEngine(engine)->type) { + case VEH_ROAD: { + const RoadVehicleInfo *vi = ::RoadVehInfo(engine); + return vi->cargo_type; + } break; + + case VEH_TRAIN: { + const RailVehicleInfo *vi = ::RailVehInfo(engine); + return vi->cargo_type; + } break; + + case VEH_SHIP: { + const ShipVehicleInfo *vi = ::ShipVehInfo(engine); + return vi->cargo_type; + } break; + + case VEH_AIRCRAFT: { + return CT_PASSENGERS; + } break; + + default: NOT_REACHED(); + } +} + +int32 AIEventEnginePreview::GetCapacity() +{ + switch (::GetEngine(engine)->type) { + case VEH_ROAD: + case VEH_TRAIN: { + uint16 *capacities = GetCapacityOfArticulatedParts(engine, ::GetEngine(engine)->type); + for (CargoID c = 0; c < NUM_CARGO; c++) { + if (capacities[c] == 0) continue; + return capacities[c]; + } + return -1; + } break; + + case VEH_SHIP: { + const ShipVehicleInfo *vi = ::ShipVehInfo(engine); + return vi->capacity; + } break; + + case VEH_AIRCRAFT: { + const AircraftVehicleInfo *vi = ::AircraftVehInfo(engine); + return vi->passenger_capacity; + } break; + + default: NOT_REACHED(); + } +} + +int32 AIEventEnginePreview::GetMaxSpeed() +{ + switch (::GetEngine(engine)->type) { + case VEH_ROAD: { + const RoadVehicleInfo *vi = ::RoadVehInfo(engine); + /* Internal speeds are km/h * 2 */ + return vi->max_speed / 2; + } break; + + case VEH_TRAIN: { + const RailVehicleInfo *vi = ::RailVehInfo(engine); + return vi->max_speed; + } break; + + case VEH_SHIP: { + const ShipVehicleInfo *vi = ::ShipVehInfo(engine); + /* Internal speeds are km/h * 2 */ + return vi->max_speed / 2; + } break; + + case VEH_AIRCRAFT: { + const AircraftVehicleInfo *vi = ::AircraftVehInfo(engine); + return vi->max_speed / _settings_game.vehicle.plane_speed; + } break; + + default: NOT_REACHED(); + } +} + +Money AIEventEnginePreview::GetPrice() +{ + switch (::GetEngine(engine)->type) { + case VEH_ROAD: { + const RoadVehicleInfo *vi = ::RoadVehInfo(engine); + return (_price.roadveh_base >> 3) * vi->cost_factor >> 5; + } break; + + case VEH_TRAIN: { + const RailVehicleInfo *vi = ::RailVehInfo(engine); + return (_price.build_railvehicle >> 3) * vi->cost_factor >> 5; + } break; + + case VEH_SHIP: { + const ShipVehicleInfo *vi = ::ShipVehInfo(engine); + return (_price.ship_base >> 3) * vi->cost_factor >> 5; + } break; + + case VEH_AIRCRAFT: { + const AircraftVehicleInfo *vi = ::AircraftVehInfo(engine); + return (_price.aircraft_base >> 3) * vi->cost_factor >> 5; + } break; + + default: NOT_REACHED(); + } +} + +Money AIEventEnginePreview::GetRunningCost() +{ + /* We need to create an instance in order to obtain GetRunningCost. + * This means we temporary allocate a vehicle in the pool, but + * there is no other way.. */ + Vehicle *vehicle; + switch (::GetEngine(engine)->type) { + case VEH_ROAD: { + vehicle = new RoadVehicle(); + } break; + + case VEH_TRAIN: { + vehicle = new Train(); + } break; + + case VEH_SHIP: { + vehicle = new Ship(); + } break; + + case VEH_AIRCRAFT: { + vehicle = new Aircraft(); + } break; + + default: NOT_REACHED(); + } + + vehicle->engine_type = engine; + Money runningCost = vehicle->GetRunningCost(); + delete vehicle; + return runningCost >> 8; +} + +AIVehicle::VehicleType AIEventEnginePreview::GetVehicleType() +{ + switch (::GetEngine(engine)->type) { + case VEH_ROAD: return AIVehicle::VEHICLE_ROAD; + case VEH_TRAIN: return AIVehicle::VEHICLE_RAIL; + case VEH_SHIP: return AIVehicle::VEHICLE_WATER; + case VEH_AIRCRAFT: return AIVehicle::VEHICLE_AIR; + default: NOT_REACHED(); + } +} + +bool AIEventEnginePreview::AcceptPreview() +{ + return AIObject::DoCommand(0, engine, 0, CMD_WANT_ENGINE_PREVIEW); +} diff --git a/src/ai/api/ai_event_types.hpp b/src/ai/api/ai_event_types.hpp new file mode 100644 index 000000000..c64b2a367 --- /dev/null +++ b/src/ai/api/ai_event_types.hpp @@ -0,0 +1,684 @@ +/* $Id$ */ + +/** @file ai_event_types.hpp The detailed types of all events. */ + +#ifndef AI_EVENT_TYPES_HPP +#define AI_EVENT_TYPES_HPP + +#include "ai_object.hpp" +#include "ai_event.hpp" +#include "ai_town.hpp" +#include "ai_industry.hpp" +#include "ai_engine.hpp" +#include "ai_subsidy.hpp" + +/** + * Event Test: a simple test event, to see if the event system is working. + * Triggered via AIEventController::Test(); + */ +class AIEventTest : public AIEvent { +public: + static const char *GetClassName() { return "AIEventTest"; } + + /** + * @param test A test value. + */ + AIEventTest(uint test) : + AIEvent(AI_ET_TEST), + test(test) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventTest *Convert(AIEvent *instance) { return (AIEventTest *)instance; } + + /** + * Return the test value. + * @return The test value. + */ + uint GetTest() { return this->test; } + +private: + uint test; +}; + +/** + * Event Vehicle Crash, indicating a vehicle of yours is crashed. + * It contains both the crash site as the vehicle crashed. It has a nice + * helper that creates a new vehicle in a depot with the same type + * and orders as the crashed one. In case the vehicle type isn't available + * anymore, it will find the next best. + */ +class AIEventVehicleCrashed : public AIEvent { +public: + static const char *GetClassName() { return "AIEventVehicleCrashed"; } + + /** + * @param vehicle The vehicle that crashed. + * @param crash_site Where the vehicle crashed. + */ + AIEventVehicleCrashed(VehicleID vehicle, TileIndex crash_site) : + AIEvent(AI_ET_VEHICLE_CRASHED), + crash_site(crash_site), + vehicle(vehicle) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventVehicleCrashed *Convert(AIEvent *instance) { return (AIEventVehicleCrashed *)instance; } + + /** + * Get the VehicleID of the crashed vehicle. + * @return The crashed vehicle. + */ + VehicleID GetVehicleID() { return vehicle; } + + /** + * Find the tile the vehicle crashed. + * @return The crash site. + */ + TileIndex GetCrashSite() { return crash_site; } + + /** + * Clone the crashed vehicle and send it on its way again. + * @param depot the depot to build the vehicle in. + * @return True when the cloning succeeded. + * @note This function isn't implemented yet. + */ + bool CloneCrashedVehicle(TileIndex depot); + +private: + TileIndex crash_site; + VehicleID vehicle; +}; + +/** + * Event Subsidy Offered, indicating someone offered a subsidy. + */ +class AIEventSubsidyOffer : public AIEvent { +public: + static const char *GetClassName() { return "AIEventSubsidyOffer"; } + + /** + * @param subsidy_id The index of this subsidy in the _subsidies array. + */ + AIEventSubsidyOffer(SubsidyID subsidy_id) : + AIEvent(AI_ET_SUBSIDY_OFFER), + subsidy_id(subsidy_id) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventSubsidyOffer *Convert(AIEvent *instance) { return (AIEventSubsidyOffer *)instance; } + + /** + * Get the SubsidyID of the subsidy. + * @return The subsidy id. + */ + SubsidyID GetSubsidyID() { return subsidy_id; } + +private: + SubsidyID subsidy_id; +}; + +/** + * Event Subsidy Offer Expired, indicating a subsidy will no longer be awarded. + */ +class AIEventSubsidyOfferExpired : public AIEvent { +public: + static const char *GetClassName() { return "AIEventSubsidyOfferExpired"; } + + /** + * @param subsidy_id The index of this subsidy in the _subsidies array. + */ + AIEventSubsidyOfferExpired(SubsidyID subsidy_id) : + AIEvent(AI_ET_SUBSIDY_OFFER_EXPIRED), + subsidy_id(subsidy_id) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventSubsidyOfferExpired *Convert(AIEvent *instance) { return (AIEventSubsidyOfferExpired *)instance; } + + /** + * Get the SubsidyID of the subsidy. + * @return The subsidy id. + */ + SubsidyID GetSubsidyID() { return subsidy_id; } + +private: + SubsidyID subsidy_id; +}; + +/** + * Event Subidy Awarded, indicating a subsidy is awarded to some company. + */ +class AIEventSubsidyAwarded : public AIEvent { +public: + static const char *GetClassName() { return "AIEventSubsidyAwarded"; } + + /** + * @param subsidy_id The index of this subsidy in the _subsidies array. + */ + AIEventSubsidyAwarded(SubsidyID subsidy_id) : + AIEvent(AI_ET_SUBSIDY_AWARDED), + subsidy_id(subsidy_id) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventSubsidyAwarded *Convert(AIEvent *instance) { return (AIEventSubsidyAwarded *)instance; } + + /** + * Get the SubsidyID of the subsidy. + * @return The subsidy id. + */ + SubsidyID GetSubsidyID() { return subsidy_id; } + +private: + SubsidyID subsidy_id; +}; + +/** + * Event Subsidy Expired, indicating a route that was once subsidized no longer is. + */ +class AIEventSubsidyExpired : public AIEvent { +public: + static const char *GetClassName() { return "AIEventSubsidyExpired"; } + + /** + * @param subsidy_id The index of this subsidy in the _subsidies array. + */ + AIEventSubsidyExpired(SubsidyID subsidy_id) : + AIEvent(AI_ET_SUBSIDY_EXPIRED), + subsidy_id(subsidy_id) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventSubsidyExpired *Convert(AIEvent *instance) { return (AIEventSubsidyExpired *)instance; } + + /** + * Get the SubsidyID of the subsidy. + * @return The subsidy id. + */ + SubsidyID GetSubsidyID() { return subsidy_id; } + +private: + SubsidyID subsidy_id; +}; + +/** + * Event Engine Preview, indicating a manufacturer offer you to test a new engine. + * You can get the same information about the offered engine as a real user + * would see in the offer window. And you can also accept the offer. + */ +class AIEventEnginePreview : public AIEvent { +public: + static const char *GetClassName() { return "AIEventEnginePreview"; } + + /** + * @param engine The engine offered to test. + */ + AIEventEnginePreview(EngineID engine) : + AIEvent(AI_ET_ENGINE_PREVIEW), + engine(engine) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventEnginePreview *Convert(AIEvent *instance) { return (AIEventEnginePreview *)instance; } + + /** + * Get the name of the offered engine. + * @return The name the engine has. + */ + const char *GetName(); + + /** + * Get the cargo-type of the offered engine. In case it can transport 2 cargos, it + * returns the first. + * @return The cargo-type of the engine. + */ + CargoID GetCargoType(); + + /** + * Get the capacity of the offered engine. In case it can transport 2 cargos, it + * returns the first. + * @return The capacity of the engine. + */ + int32 GetCapacity(); + + /** + * Get the maximum speed of the offered engine. + * @return The maximum speed the engine has. + * @note The speed is in km/h. + */ + int32 GetMaxSpeed(); + + /** + * Get the new cost of the offered engine. + * @return The new cost the engine has. + */ + Money GetPrice(); + + /** + * Get the running cost of the offered engine. + * @return The running cost of the vehicle per year. + * @note Cost is per year; divide by 364 to get per day. + */ + Money GetRunningCost(); + + /** + * Get the type of the offered engine. + * @return The type the engine has. + */ + AIVehicle::VehicleType GetVehicleType(); + + /** + * Accept the engine preview. + * @return True when the accepting succeeded. + */ + bool AcceptPreview(); + +private: + EngineID engine; +}; + +/** + * Event Company New, indicating a new company has been created. + */ +class AIEventCompanyNew : public AIEvent { +public: + static const char *GetClassName() { return "AIEventCompanyNew"; } + + /** + * @param owner The new company. + */ + AIEventCompanyNew(Owner owner) : + AIEvent(AI_ET_COMPANY_NEW), + owner((AICompany::CompanyID)(byte)owner) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventCompanyNew *Convert(AIEvent *instance) { return (AIEventCompanyNew *)instance; } + + /** + * Get the CompanyID of the company that has been created. + * @return The CompanyID of the company. + */ + AICompany::CompanyID GetCompanyID() { return owner; } + +private: + AICompany::CompanyID owner; +}; + +/** + * Event Company In Trouble, indicating a company is in trouble and might go + * bankrupt soon. + */ +class AIEventCompanyInTrouble : public AIEvent { +public: + static const char *GetClassName() { return "AIEventCompanyInTrouble"; } + + /** + * @param owner The company that is in trouble. + */ + AIEventCompanyInTrouble(Owner owner) : + AIEvent(AI_ET_COMPANY_IN_TROUBLE), + owner((AICompany::CompanyID)(byte)owner) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventCompanyInTrouble *Convert(AIEvent *instance) { return (AIEventCompanyInTrouble *)instance; } + + /** + * Get the CompanyID of the company that is in trouble. + * @return The CompanyID of the company in trouble. + */ + AICompany::CompanyID GetCompanyID() { return owner; } + +private: + AICompany::CompanyID owner; +}; + +/** + * Event Company Merger, indicating a company has been bought by another + * company. + */ +class AIEventCompanyMerger : public AIEvent { +public: + static const char *GetClassName() { return "AIEventCompanyMerger"; } + + /** + * @param old_owner The company bought off. + * @param new_owner The company that bougth owner. + */ + AIEventCompanyMerger(Owner old_owner, Owner new_owner) : + AIEvent(AI_ET_COMPANY_MERGER), + old_owner((AICompany::CompanyID)(byte)old_owner), + new_owner((AICompany::CompanyID)(byte)new_owner) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventCompanyMerger *Convert(AIEvent *instance) { return (AIEventCompanyMerger *)instance; } + + /** + * Get the CompanyID of the company that has been bought. + * @return The CompanyID of the company that has been bought. + * @note: The value below is not valid anymore as CompanyID, and + * AICompany::ResolveCompanyID will return INVALID_COMPANY. It's + * only usefull if you're keeping track of company's yourself. + */ + AICompany::CompanyID GetOldCompanyID() { return old_owner; } + + /** + * Get the CompanyID of the new owner. + * @return The CompanyID of the new owner. + */ + AICompany::CompanyID GetNewCompanyID() { return new_owner; } + +private: + AICompany::CompanyID old_owner; + AICompany::CompanyID new_owner; +}; + +/** + * Event Company Bankrupt, indicating a company has gone bankrupt. + */ +class AIEventCompanyBankrupt : public AIEvent { +public: + static const char *GetClassName() { return "AIEventCompanyBankrupt"; } + + /** + * @param owner The company that has gone bankrupt. + */ + AIEventCompanyBankrupt(Owner owner) : + AIEvent(AI_ET_COMPANY_BANKRUPT), + owner((AICompany::CompanyID)(byte)owner) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventCompanyBankrupt *Convert(AIEvent *instance) { return (AIEventCompanyBankrupt *)instance; } + + /** + * Get the CompanyID of the company that has gone bankrupt. + * @return The CompanyID of the company that has gone bankrupt. + */ + AICompany::CompanyID GetCompanyID() { return owner; } + +private: + AICompany::CompanyID owner; +}; + +/** + * Event Vehicle Lost, indicating a vehicle can't find its way to its destination. + */ +class AIEventVehicleLost : public AIEvent { +public: + static const char *GetClassName() { return "AIEventVehicleLost"; } + + /** + * @param vehicle_id The vehicle that is lost. + */ + AIEventVehicleLost(VehicleID vehicle_id) : + AIEvent(AI_ET_VEHICLE_LOST), + vehicle_id(vehicle_id) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventVehicleLost *Convert(AIEvent *instance) { return (AIEventVehicleLost *)instance; } + + /** + * Get the VehicleID of the vehicle that is lost. + * @return The VehicleID of the vehicle that is lost. + */ + VehicleID GetVehicleID() { return vehicle_id; } + +private: + VehicleID vehicle_id; +}; + +/** + * Event VehicleWaitingInDepot, indicating a vehicle has arrived a depot and is now waiting there. + */ +class AIEventVehicleWaitingInDepot : public AIEvent { +public: + static const char *GetClassName() { return "AIEventVehicleWaitingInDepot"; } + + /** + * @param vehicle_id The vehicle that is waiting in a depot. + */ + AIEventVehicleWaitingInDepot(VehicleID vehicle_id) : + AIEvent(AI_ET_VEHICLE_WAITING_IN_DEPOT), + vehicle_id(vehicle_id) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventVehicleWaitingInDepot *Convert(AIEvent *instance) { return (AIEventVehicleWaitingInDepot *)instance; } + + /** + * Get the VehicleID of the vehicle that is waiting in a depot. + * @return The VehicleID of the vehicle that is waiting in a depot. + */ + VehicleID GetVehicleID() { return vehicle_id; } + +private: + VehicleID vehicle_id; +}; + +/** + * Event Vehicle Unprofitable, indicating a vehicle lost money last year. + */ +class AIEventVehicleUnprofitable : public AIEvent { +public: + static const char *GetClassName() { return "AIEventVehicleUnprofitable"; } + + /** + * @param vehicle_id The vehicle that was unprofitable. + */ + AIEventVehicleUnprofitable(VehicleID vehicle_id) : + AIEvent(AI_ET_VEHICLE_UNPROFITABLE), + vehicle_id(vehicle_id) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventVehicleUnprofitable *Convert(AIEvent *instance) { return (AIEventVehicleUnprofitable *)instance; } + + /** + * Get the VehicleID of the vehicle that lost money. + * @return The VehicleID of the vehicle that lost money. + */ + VehicleID GetVehicleID() { return vehicle_id; } + +private: + VehicleID vehicle_id; +}; + +/** + * Event Industry Open, indicating a new industry has been created. + */ +class AIEventIndustryOpen : public AIEvent { +public: + static const char *GetClassName() { return "AIEventIndustryOpen"; } + + /** + * @param industry_id The new industry. + */ + AIEventIndustryOpen(IndustryID industry_id) : + AIEvent(AI_ET_INDUSTRY_OPEN), + industry_id(industry_id) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventIndustryOpen *Convert(AIEvent *instance) { return (AIEventIndustryOpen *)instance; } + + /** + * Get the IndustryID of the new industry. + * @return The IndustryID of the industry. + */ + IndustryID GetIndustryID() { return industry_id; } + +private: + IndustryID industry_id; +}; + +/** + * Event Industry Close, indicating an industry is going to be closed. + */ +class AIEventIndustryClose : public AIEvent { +public: + static const char *GetClassName() { return "AIEventIndustryClose"; } + + /** + * @param industry_id The new industry. + */ + AIEventIndustryClose(IndustryID industry_id) : + AIEvent(AI_ET_INDUSTRY_CLOSE), + industry_id(industry_id) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventIndustryClose *Convert(AIEvent *instance) { return (AIEventIndustryClose *)instance; } + + /** + * Get the IndustryID of the closing industry. + * @return The IndustryID of the industry. + */ + IndustryID GetIndustryID() { return industry_id; } + +private: + IndustryID industry_id; +}; + +/** + * Event Engine Available, indicating a new engine is available. + */ +class AIEventEngineAvailable : public AIEvent { +public: + static const char *GetClassName() { return "AIEventEngineAvailable"; } + + /** + * @param engine The engine that is available. + */ + AIEventEngineAvailable(EngineID engine) : + AIEvent(AI_ET_ENGINE_AVAILABLE), + engine(engine) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventEngineAvailable *Convert(AIEvent *instance) { return (AIEventEngineAvailable *)instance; } + + /** + * Get the EngineID of the new engine. + * @return The EngineID of the new engine. + */ + EngineID GetEngineID() { return engine; } + +private: + EngineID engine; +}; + +/** + * Event Station First Vehicle, indicating a station has been visited by a vehicle for the first time. + */ +class AIEventStationFirstVehicle : public AIEvent { +public: + static const char *GetClassName() { return "AIEventStationFirstVehicle"; } + + /** + * @param station The station visited for the first time. + * @param vehicle The vehicle visiting the station. + */ + AIEventStationFirstVehicle(StationID station, VehicleID vehicle) : + AIEvent(AI_ET_STATION_FIRST_VEHICLE), + station(station), + vehicle(vehicle) + {} + + /** + * Convert an AIEvent to the real instance. + * @param instance The instance to convert. + * @return The converted instance. + */ + static AIEventStationFirstVehicle *Convert(AIEvent *instance) { return (AIEventStationFirstVehicle *)instance; } + + /** + * Get the StationID of the visited station. + * @return The StationID of the visited station. + */ + StationID GetStationID() { return station; } + + /** + * Get the VehicleID of the first vehicle. + * @return The VehicleID of the first vehicle. + */ + VehicleID GetVehicleID() { return vehicle; } + +private: + StationID station; + VehicleID vehicle; +}; + +#endif /* AI_EVENT_TYPES_HPP */ diff --git a/src/ai/api/ai_event_types.hpp.sq b/src/ai/api/ai_event_types.hpp.sq new file mode 100644 index 000000000..84d8b722b --- /dev/null +++ b/src/ai/api/ai_event_types.hpp.sq @@ -0,0 +1,393 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_event_types.hpp" + +namespace SQConvert { + /* Allow AIEventTest to be used as Squirrel parameter */ + template <> AIEventTest *GetParam(ForceType<AIEventTest *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventTest *)instance; } + template <> AIEventTest &GetParam(ForceType<AIEventTest &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventTest *)instance; } + template <> const AIEventTest *GetParam(ForceType<const AIEventTest *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventTest *)instance; } + template <> const AIEventTest &GetParam(ForceType<const AIEventTest &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventTest *)instance; } + template <> int Return<AIEventTest *>(HSQUIRRELVM vm, AIEventTest *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventTest", res, NULL, DefSQDestructorCallback<AIEventTest>); return 1; } +}; // namespace SQConvert + +void SQAIEventTest_Register(Squirrel *engine) { + DefSQClass <AIEventTest> SQAIEventTest("AIEventTest"); + SQAIEventTest.PreRegister(engine, "AIEvent"); + + SQAIEventTest.DefSQStaticMethod(engine, &AIEventTest::GetClassName, "GetClassName", 1, "x"); + SQAIEventTest.DefSQStaticMethod(engine, &AIEventTest::Convert, "Convert", 2, "xx"); + + SQAIEventTest.DefSQMethod(engine, &AIEventTest::GetTest, "GetTest", 1, "x"); + + SQAIEventTest.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventVehicleCrashed to be used as Squirrel parameter */ + template <> AIEventVehicleCrashed *GetParam(ForceType<AIEventVehicleCrashed *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleCrashed *)instance; } + template <> AIEventVehicleCrashed &GetParam(ForceType<AIEventVehicleCrashed &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleCrashed *)instance; } + template <> const AIEventVehicleCrashed *GetParam(ForceType<const AIEventVehicleCrashed *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleCrashed *)instance; } + template <> const AIEventVehicleCrashed &GetParam(ForceType<const AIEventVehicleCrashed &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleCrashed *)instance; } + template <> int Return<AIEventVehicleCrashed *>(HSQUIRRELVM vm, AIEventVehicleCrashed *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventVehicleCrashed", res, NULL, DefSQDestructorCallback<AIEventVehicleCrashed>); return 1; } +}; // namespace SQConvert + +void SQAIEventVehicleCrashed_Register(Squirrel *engine) { + DefSQClass <AIEventVehicleCrashed> SQAIEventVehicleCrashed("AIEventVehicleCrashed"); + SQAIEventVehicleCrashed.PreRegister(engine, "AIEvent"); + + SQAIEventVehicleCrashed.DefSQStaticMethod(engine, &AIEventVehicleCrashed::GetClassName, "GetClassName", 1, "x"); + SQAIEventVehicleCrashed.DefSQStaticMethod(engine, &AIEventVehicleCrashed::Convert, "Convert", 2, "xx"); + + SQAIEventVehicleCrashed.DefSQMethod(engine, &AIEventVehicleCrashed::GetVehicleID, "GetVehicleID", 1, "x"); + SQAIEventVehicleCrashed.DefSQMethod(engine, &AIEventVehicleCrashed::GetCrashSite, "GetCrashSite", 1, "x"); + SQAIEventVehicleCrashed.DefSQMethod(engine, &AIEventVehicleCrashed::CloneCrashedVehicle, "CloneCrashedVehicle", 2, "xi"); + + SQAIEventVehicleCrashed.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventSubsidyOffer to be used as Squirrel parameter */ + template <> AIEventSubsidyOffer *GetParam(ForceType<AIEventSubsidyOffer *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyOffer *)instance; } + template <> AIEventSubsidyOffer &GetParam(ForceType<AIEventSubsidyOffer &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyOffer *)instance; } + template <> const AIEventSubsidyOffer *GetParam(ForceType<const AIEventSubsidyOffer *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyOffer *)instance; } + template <> const AIEventSubsidyOffer &GetParam(ForceType<const AIEventSubsidyOffer &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyOffer *)instance; } + template <> int Return<AIEventSubsidyOffer *>(HSQUIRRELVM vm, AIEventSubsidyOffer *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventSubsidyOffer", res, NULL, DefSQDestructorCallback<AIEventSubsidyOffer>); return 1; } +}; // namespace SQConvert + +void SQAIEventSubsidyOffer_Register(Squirrel *engine) { + DefSQClass <AIEventSubsidyOffer> SQAIEventSubsidyOffer("AIEventSubsidyOffer"); + SQAIEventSubsidyOffer.PreRegister(engine, "AIEvent"); + + SQAIEventSubsidyOffer.DefSQStaticMethod(engine, &AIEventSubsidyOffer::GetClassName, "GetClassName", 1, "x"); + SQAIEventSubsidyOffer.DefSQStaticMethod(engine, &AIEventSubsidyOffer::Convert, "Convert", 2, "xx"); + + SQAIEventSubsidyOffer.DefSQMethod(engine, &AIEventSubsidyOffer::GetSubsidyID, "GetSubsidyID", 1, "x"); + + SQAIEventSubsidyOffer.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventSubsidyOfferExpired to be used as Squirrel parameter */ + template <> AIEventSubsidyOfferExpired *GetParam(ForceType<AIEventSubsidyOfferExpired *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyOfferExpired *)instance; } + template <> AIEventSubsidyOfferExpired &GetParam(ForceType<AIEventSubsidyOfferExpired &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyOfferExpired *)instance; } + template <> const AIEventSubsidyOfferExpired *GetParam(ForceType<const AIEventSubsidyOfferExpired *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyOfferExpired *)instance; } + template <> const AIEventSubsidyOfferExpired &GetParam(ForceType<const AIEventSubsidyOfferExpired &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyOfferExpired *)instance; } + template <> int Return<AIEventSubsidyOfferExpired *>(HSQUIRRELVM vm, AIEventSubsidyOfferExpired *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventSubsidyOfferExpired", res, NULL, DefSQDestructorCallback<AIEventSubsidyOfferExpired>); return 1; } +}; // namespace SQConvert + +void SQAIEventSubsidyOfferExpired_Register(Squirrel *engine) { + DefSQClass <AIEventSubsidyOfferExpired> SQAIEventSubsidyOfferExpired("AIEventSubsidyOfferExpired"); + SQAIEventSubsidyOfferExpired.PreRegister(engine, "AIEvent"); + + SQAIEventSubsidyOfferExpired.DefSQStaticMethod(engine, &AIEventSubsidyOfferExpired::GetClassName, "GetClassName", 1, "x"); + SQAIEventSubsidyOfferExpired.DefSQStaticMethod(engine, &AIEventSubsidyOfferExpired::Convert, "Convert", 2, "xx"); + + SQAIEventSubsidyOfferExpired.DefSQMethod(engine, &AIEventSubsidyOfferExpired::GetSubsidyID, "GetSubsidyID", 1, "x"); + + SQAIEventSubsidyOfferExpired.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventSubsidyAwarded to be used as Squirrel parameter */ + template <> AIEventSubsidyAwarded *GetParam(ForceType<AIEventSubsidyAwarded *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyAwarded *)instance; } + template <> AIEventSubsidyAwarded &GetParam(ForceType<AIEventSubsidyAwarded &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyAwarded *)instance; } + template <> const AIEventSubsidyAwarded *GetParam(ForceType<const AIEventSubsidyAwarded *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyAwarded *)instance; } + template <> const AIEventSubsidyAwarded &GetParam(ForceType<const AIEventSubsidyAwarded &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyAwarded *)instance; } + template <> int Return<AIEventSubsidyAwarded *>(HSQUIRRELVM vm, AIEventSubsidyAwarded *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventSubsidyAwarded", res, NULL, DefSQDestructorCallback<AIEventSubsidyAwarded>); return 1; } +}; // namespace SQConvert + +void SQAIEventSubsidyAwarded_Register(Squirrel *engine) { + DefSQClass <AIEventSubsidyAwarded> SQAIEventSubsidyAwarded("AIEventSubsidyAwarded"); + SQAIEventSubsidyAwarded.PreRegister(engine, "AIEvent"); + + SQAIEventSubsidyAwarded.DefSQStaticMethod(engine, &AIEventSubsidyAwarded::GetClassName, "GetClassName", 1, "x"); + SQAIEventSubsidyAwarded.DefSQStaticMethod(engine, &AIEventSubsidyAwarded::Convert, "Convert", 2, "xx"); + + SQAIEventSubsidyAwarded.DefSQMethod(engine, &AIEventSubsidyAwarded::GetSubsidyID, "GetSubsidyID", 1, "x"); + + SQAIEventSubsidyAwarded.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventSubsidyExpired to be used as Squirrel parameter */ + template <> AIEventSubsidyExpired *GetParam(ForceType<AIEventSubsidyExpired *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyExpired *)instance; } + template <> AIEventSubsidyExpired &GetParam(ForceType<AIEventSubsidyExpired &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyExpired *)instance; } + template <> const AIEventSubsidyExpired *GetParam(ForceType<const AIEventSubsidyExpired *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyExpired *)instance; } + template <> const AIEventSubsidyExpired &GetParam(ForceType<const AIEventSubsidyExpired &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyExpired *)instance; } + template <> int Return<AIEventSubsidyExpired *>(HSQUIRRELVM vm, AIEventSubsidyExpired *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventSubsidyExpired", res, NULL, DefSQDestructorCallback<AIEventSubsidyExpired>); return 1; } +}; // namespace SQConvert + +void SQAIEventSubsidyExpired_Register(Squirrel *engine) { + DefSQClass <AIEventSubsidyExpired> SQAIEventSubsidyExpired("AIEventSubsidyExpired"); + SQAIEventSubsidyExpired.PreRegister(engine, "AIEvent"); + + SQAIEventSubsidyExpired.DefSQStaticMethod(engine, &AIEventSubsidyExpired::GetClassName, "GetClassName", 1, "x"); + SQAIEventSubsidyExpired.DefSQStaticMethod(engine, &AIEventSubsidyExpired::Convert, "Convert", 2, "xx"); + + SQAIEventSubsidyExpired.DefSQMethod(engine, &AIEventSubsidyExpired::GetSubsidyID, "GetSubsidyID", 1, "x"); + + SQAIEventSubsidyExpired.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventEnginePreview to be used as Squirrel parameter */ + template <> AIEventEnginePreview *GetParam(ForceType<AIEventEnginePreview *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventEnginePreview *)instance; } + template <> AIEventEnginePreview &GetParam(ForceType<AIEventEnginePreview &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventEnginePreview *)instance; } + template <> const AIEventEnginePreview *GetParam(ForceType<const AIEventEnginePreview *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventEnginePreview *)instance; } + template <> const AIEventEnginePreview &GetParam(ForceType<const AIEventEnginePreview &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventEnginePreview *)instance; } + template <> int Return<AIEventEnginePreview *>(HSQUIRRELVM vm, AIEventEnginePreview *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventEnginePreview", res, NULL, DefSQDestructorCallback<AIEventEnginePreview>); return 1; } +}; // namespace SQConvert + +void SQAIEventEnginePreview_Register(Squirrel *engine) { + DefSQClass <AIEventEnginePreview> SQAIEventEnginePreview("AIEventEnginePreview"); + SQAIEventEnginePreview.PreRegister(engine, "AIEvent"); + + SQAIEventEnginePreview.DefSQStaticMethod(engine, &AIEventEnginePreview::GetClassName, "GetClassName", 1, "x"); + SQAIEventEnginePreview.DefSQStaticMethod(engine, &AIEventEnginePreview::Convert, "Convert", 2, "xx"); + + SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::GetName, "GetName", 1, "x"); + SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::GetCargoType, "GetCargoType", 1, "x"); + SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::GetCapacity, "GetCapacity", 1, "x"); + SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::GetMaxSpeed, "GetMaxSpeed", 1, "x"); + SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::GetPrice, "GetPrice", 1, "x"); + SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::GetRunningCost, "GetRunningCost", 1, "x"); + SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::GetVehicleType, "GetVehicleType", 1, "x"); + SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::AcceptPreview, "AcceptPreview", 1, "x"); + + SQAIEventEnginePreview.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventCompanyNew to be used as Squirrel parameter */ + template <> AIEventCompanyNew *GetParam(ForceType<AIEventCompanyNew *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyNew *)instance; } + template <> AIEventCompanyNew &GetParam(ForceType<AIEventCompanyNew &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyNew *)instance; } + template <> const AIEventCompanyNew *GetParam(ForceType<const AIEventCompanyNew *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyNew *)instance; } + template <> const AIEventCompanyNew &GetParam(ForceType<const AIEventCompanyNew &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyNew *)instance; } + template <> int Return<AIEventCompanyNew *>(HSQUIRRELVM vm, AIEventCompanyNew *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventCompanyNew", res, NULL, DefSQDestructorCallback<AIEventCompanyNew>); return 1; } +}; // namespace SQConvert + +void SQAIEventCompanyNew_Register(Squirrel *engine) { + DefSQClass <AIEventCompanyNew> SQAIEventCompanyNew("AIEventCompanyNew"); + SQAIEventCompanyNew.PreRegister(engine, "AIEvent"); + + SQAIEventCompanyNew.DefSQStaticMethod(engine, &AIEventCompanyNew::GetClassName, "GetClassName", 1, "x"); + SQAIEventCompanyNew.DefSQStaticMethod(engine, &AIEventCompanyNew::Convert, "Convert", 2, "xx"); + + SQAIEventCompanyNew.DefSQMethod(engine, &AIEventCompanyNew::GetCompanyID, "GetCompanyID", 1, "x"); + + SQAIEventCompanyNew.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventCompanyInTrouble to be used as Squirrel parameter */ + template <> AIEventCompanyInTrouble *GetParam(ForceType<AIEventCompanyInTrouble *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyInTrouble *)instance; } + template <> AIEventCompanyInTrouble &GetParam(ForceType<AIEventCompanyInTrouble &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyInTrouble *)instance; } + template <> const AIEventCompanyInTrouble *GetParam(ForceType<const AIEventCompanyInTrouble *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyInTrouble *)instance; } + template <> const AIEventCompanyInTrouble &GetParam(ForceType<const AIEventCompanyInTrouble &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyInTrouble *)instance; } + template <> int Return<AIEventCompanyInTrouble *>(HSQUIRRELVM vm, AIEventCompanyInTrouble *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventCompanyInTrouble", res, NULL, DefSQDestructorCallback<AIEventCompanyInTrouble>); return 1; } +}; // namespace SQConvert + +void SQAIEventCompanyInTrouble_Register(Squirrel *engine) { + DefSQClass <AIEventCompanyInTrouble> SQAIEventCompanyInTrouble("AIEventCompanyInTrouble"); + SQAIEventCompanyInTrouble.PreRegister(engine, "AIEvent"); + + SQAIEventCompanyInTrouble.DefSQStaticMethod(engine, &AIEventCompanyInTrouble::GetClassName, "GetClassName", 1, "x"); + SQAIEventCompanyInTrouble.DefSQStaticMethod(engine, &AIEventCompanyInTrouble::Convert, "Convert", 2, "xx"); + + SQAIEventCompanyInTrouble.DefSQMethod(engine, &AIEventCompanyInTrouble::GetCompanyID, "GetCompanyID", 1, "x"); + + SQAIEventCompanyInTrouble.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventCompanyMerger to be used as Squirrel parameter */ + template <> AIEventCompanyMerger *GetParam(ForceType<AIEventCompanyMerger *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyMerger *)instance; } + template <> AIEventCompanyMerger &GetParam(ForceType<AIEventCompanyMerger &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyMerger *)instance; } + template <> const AIEventCompanyMerger *GetParam(ForceType<const AIEventCompanyMerger *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyMerger *)instance; } + template <> const AIEventCompanyMerger &GetParam(ForceType<const AIEventCompanyMerger &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyMerger *)instance; } + template <> int Return<AIEventCompanyMerger *>(HSQUIRRELVM vm, AIEventCompanyMerger *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventCompanyMerger", res, NULL, DefSQDestructorCallback<AIEventCompanyMerger>); return 1; } +}; // namespace SQConvert + +void SQAIEventCompanyMerger_Register(Squirrel *engine) { + DefSQClass <AIEventCompanyMerger> SQAIEventCompanyMerger("AIEventCompanyMerger"); + SQAIEventCompanyMerger.PreRegister(engine, "AIEvent"); + + SQAIEventCompanyMerger.DefSQStaticMethod(engine, &AIEventCompanyMerger::GetClassName, "GetClassName", 1, "x"); + SQAIEventCompanyMerger.DefSQStaticMethod(engine, &AIEventCompanyMerger::Convert, "Convert", 2, "xx"); + + SQAIEventCompanyMerger.DefSQMethod(engine, &AIEventCompanyMerger::GetOldCompanyID, "GetOldCompanyID", 1, "x"); + SQAIEventCompanyMerger.DefSQMethod(engine, &AIEventCompanyMerger::GetNewCompanyID, "GetNewCompanyID", 1, "x"); + + SQAIEventCompanyMerger.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventCompanyBankrupt to be used as Squirrel parameter */ + template <> AIEventCompanyBankrupt *GetParam(ForceType<AIEventCompanyBankrupt *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyBankrupt *)instance; } + template <> AIEventCompanyBankrupt &GetParam(ForceType<AIEventCompanyBankrupt &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyBankrupt *)instance; } + template <> const AIEventCompanyBankrupt *GetParam(ForceType<const AIEventCompanyBankrupt *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyBankrupt *)instance; } + template <> const AIEventCompanyBankrupt &GetParam(ForceType<const AIEventCompanyBankrupt &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyBankrupt *)instance; } + template <> int Return<AIEventCompanyBankrupt *>(HSQUIRRELVM vm, AIEventCompanyBankrupt *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventCompanyBankrupt", res, NULL, DefSQDestructorCallback<AIEventCompanyBankrupt>); return 1; } +}; // namespace SQConvert + +void SQAIEventCompanyBankrupt_Register(Squirrel *engine) { + DefSQClass <AIEventCompanyBankrupt> SQAIEventCompanyBankrupt("AIEventCompanyBankrupt"); + SQAIEventCompanyBankrupt.PreRegister(engine, "AIEvent"); + + SQAIEventCompanyBankrupt.DefSQStaticMethod(engine, &AIEventCompanyBankrupt::GetClassName, "GetClassName", 1, "x"); + SQAIEventCompanyBankrupt.DefSQStaticMethod(engine, &AIEventCompanyBankrupt::Convert, "Convert", 2, "xx"); + + SQAIEventCompanyBankrupt.DefSQMethod(engine, &AIEventCompanyBankrupt::GetCompanyID, "GetCompanyID", 1, "x"); + + SQAIEventCompanyBankrupt.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventVehicleLost to be used as Squirrel parameter */ + template <> AIEventVehicleLost *GetParam(ForceType<AIEventVehicleLost *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleLost *)instance; } + template <> AIEventVehicleLost &GetParam(ForceType<AIEventVehicleLost &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleLost *)instance; } + template <> const AIEventVehicleLost *GetParam(ForceType<const AIEventVehicleLost *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleLost *)instance; } + template <> const AIEventVehicleLost &GetParam(ForceType<const AIEventVehicleLost &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleLost *)instance; } + template <> int Return<AIEventVehicleLost *>(HSQUIRRELVM vm, AIEventVehicleLost *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventVehicleLost", res, NULL, DefSQDestructorCallback<AIEventVehicleLost>); return 1; } +}; // namespace SQConvert + +void SQAIEventVehicleLost_Register(Squirrel *engine) { + DefSQClass <AIEventVehicleLost> SQAIEventVehicleLost("AIEventVehicleLost"); + SQAIEventVehicleLost.PreRegister(engine, "AIEvent"); + + SQAIEventVehicleLost.DefSQStaticMethod(engine, &AIEventVehicleLost::GetClassName, "GetClassName", 1, "x"); + SQAIEventVehicleLost.DefSQStaticMethod(engine, &AIEventVehicleLost::Convert, "Convert", 2, "xx"); + + SQAIEventVehicleLost.DefSQMethod(engine, &AIEventVehicleLost::GetVehicleID, "GetVehicleID", 1, "x"); + + SQAIEventVehicleLost.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventVehicleWaitingInDepot to be used as Squirrel parameter */ + template <> AIEventVehicleWaitingInDepot *GetParam(ForceType<AIEventVehicleWaitingInDepot *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleWaitingInDepot *)instance; } + template <> AIEventVehicleWaitingInDepot &GetParam(ForceType<AIEventVehicleWaitingInDepot &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleWaitingInDepot *)instance; } + template <> const AIEventVehicleWaitingInDepot *GetParam(ForceType<const AIEventVehicleWaitingInDepot *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleWaitingInDepot *)instance; } + template <> const AIEventVehicleWaitingInDepot &GetParam(ForceType<const AIEventVehicleWaitingInDepot &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleWaitingInDepot *)instance; } + template <> int Return<AIEventVehicleWaitingInDepot *>(HSQUIRRELVM vm, AIEventVehicleWaitingInDepot *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventVehicleWaitingInDepot", res, NULL, DefSQDestructorCallback<AIEventVehicleWaitingInDepot>); return 1; } +}; // namespace SQConvert + +void SQAIEventVehicleWaitingInDepot_Register(Squirrel *engine) { + DefSQClass <AIEventVehicleWaitingInDepot> SQAIEventVehicleWaitingInDepot("AIEventVehicleWaitingInDepot"); + SQAIEventVehicleWaitingInDepot.PreRegister(engine, "AIEvent"); + + SQAIEventVehicleWaitingInDepot.DefSQStaticMethod(engine, &AIEventVehicleWaitingInDepot::GetClassName, "GetClassName", 1, "x"); + SQAIEventVehicleWaitingInDepot.DefSQStaticMethod(engine, &AIEventVehicleWaitingInDepot::Convert, "Convert", 2, "xx"); + + SQAIEventVehicleWaitingInDepot.DefSQMethod(engine, &AIEventVehicleWaitingInDepot::GetVehicleID, "GetVehicleID", 1, "x"); + + SQAIEventVehicleWaitingInDepot.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventVehicleUnprofitable to be used as Squirrel parameter */ + template <> AIEventVehicleUnprofitable *GetParam(ForceType<AIEventVehicleUnprofitable *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleUnprofitable *)instance; } + template <> AIEventVehicleUnprofitable &GetParam(ForceType<AIEventVehicleUnprofitable &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleUnprofitable *)instance; } + template <> const AIEventVehicleUnprofitable *GetParam(ForceType<const AIEventVehicleUnprofitable *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleUnprofitable *)instance; } + template <> const AIEventVehicleUnprofitable &GetParam(ForceType<const AIEventVehicleUnprofitable &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleUnprofitable *)instance; } + template <> int Return<AIEventVehicleUnprofitable *>(HSQUIRRELVM vm, AIEventVehicleUnprofitable *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventVehicleUnprofitable", res, NULL, DefSQDestructorCallback<AIEventVehicleUnprofitable>); return 1; } +}; // namespace SQConvert + +void SQAIEventVehicleUnprofitable_Register(Squirrel *engine) { + DefSQClass <AIEventVehicleUnprofitable> SQAIEventVehicleUnprofitable("AIEventVehicleUnprofitable"); + SQAIEventVehicleUnprofitable.PreRegister(engine, "AIEvent"); + + SQAIEventVehicleUnprofitable.DefSQStaticMethod(engine, &AIEventVehicleUnprofitable::GetClassName, "GetClassName", 1, "x"); + SQAIEventVehicleUnprofitable.DefSQStaticMethod(engine, &AIEventVehicleUnprofitable::Convert, "Convert", 2, "xx"); + + SQAIEventVehicleUnprofitable.DefSQMethod(engine, &AIEventVehicleUnprofitable::GetVehicleID, "GetVehicleID", 1, "x"); + + SQAIEventVehicleUnprofitable.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventIndustryOpen to be used as Squirrel parameter */ + template <> AIEventIndustryOpen *GetParam(ForceType<AIEventIndustryOpen *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventIndustryOpen *)instance; } + template <> AIEventIndustryOpen &GetParam(ForceType<AIEventIndustryOpen &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventIndustryOpen *)instance; } + template <> const AIEventIndustryOpen *GetParam(ForceType<const AIEventIndustryOpen *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventIndustryOpen *)instance; } + template <> const AIEventIndustryOpen &GetParam(ForceType<const AIEventIndustryOpen &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventIndustryOpen *)instance; } + template <> int Return<AIEventIndustryOpen *>(HSQUIRRELVM vm, AIEventIndustryOpen *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventIndustryOpen", res, NULL, DefSQDestructorCallback<AIEventIndustryOpen>); return 1; } +}; // namespace SQConvert + +void SQAIEventIndustryOpen_Register(Squirrel *engine) { + DefSQClass <AIEventIndustryOpen> SQAIEventIndustryOpen("AIEventIndustryOpen"); + SQAIEventIndustryOpen.PreRegister(engine, "AIEvent"); + + SQAIEventIndustryOpen.DefSQStaticMethod(engine, &AIEventIndustryOpen::GetClassName, "GetClassName", 1, "x"); + SQAIEventIndustryOpen.DefSQStaticMethod(engine, &AIEventIndustryOpen::Convert, "Convert", 2, "xx"); + + SQAIEventIndustryOpen.DefSQMethod(engine, &AIEventIndustryOpen::GetIndustryID, "GetIndustryID", 1, "x"); + + SQAIEventIndustryOpen.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventIndustryClose to be used as Squirrel parameter */ + template <> AIEventIndustryClose *GetParam(ForceType<AIEventIndustryClose *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventIndustryClose *)instance; } + template <> AIEventIndustryClose &GetParam(ForceType<AIEventIndustryClose &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventIndustryClose *)instance; } + template <> const AIEventIndustryClose *GetParam(ForceType<const AIEventIndustryClose *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventIndustryClose *)instance; } + template <> const AIEventIndustryClose &GetParam(ForceType<const AIEventIndustryClose &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventIndustryClose *)instance; } + template <> int Return<AIEventIndustryClose *>(HSQUIRRELVM vm, AIEventIndustryClose *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventIndustryClose", res, NULL, DefSQDestructorCallback<AIEventIndustryClose>); return 1; } +}; // namespace SQConvert + +void SQAIEventIndustryClose_Register(Squirrel *engine) { + DefSQClass <AIEventIndustryClose> SQAIEventIndustryClose("AIEventIndustryClose"); + SQAIEventIndustryClose.PreRegister(engine, "AIEvent"); + + SQAIEventIndustryClose.DefSQStaticMethod(engine, &AIEventIndustryClose::GetClassName, "GetClassName", 1, "x"); + SQAIEventIndustryClose.DefSQStaticMethod(engine, &AIEventIndustryClose::Convert, "Convert", 2, "xx"); + + SQAIEventIndustryClose.DefSQMethod(engine, &AIEventIndustryClose::GetIndustryID, "GetIndustryID", 1, "x"); + + SQAIEventIndustryClose.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventEngineAvailable to be used as Squirrel parameter */ + template <> AIEventEngineAvailable *GetParam(ForceType<AIEventEngineAvailable *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventEngineAvailable *)instance; } + template <> AIEventEngineAvailable &GetParam(ForceType<AIEventEngineAvailable &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventEngineAvailable *)instance; } + template <> const AIEventEngineAvailable *GetParam(ForceType<const AIEventEngineAvailable *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventEngineAvailable *)instance; } + template <> const AIEventEngineAvailable &GetParam(ForceType<const AIEventEngineAvailable &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventEngineAvailable *)instance; } + template <> int Return<AIEventEngineAvailable *>(HSQUIRRELVM vm, AIEventEngineAvailable *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventEngineAvailable", res, NULL, DefSQDestructorCallback<AIEventEngineAvailable>); return 1; } +}; // namespace SQConvert + +void SQAIEventEngineAvailable_Register(Squirrel *engine) { + DefSQClass <AIEventEngineAvailable> SQAIEventEngineAvailable("AIEventEngineAvailable"); + SQAIEventEngineAvailable.PreRegister(engine, "AIEvent"); + + SQAIEventEngineAvailable.DefSQStaticMethod(engine, &AIEventEngineAvailable::GetClassName, "GetClassName", 1, "x"); + SQAIEventEngineAvailable.DefSQStaticMethod(engine, &AIEventEngineAvailable::Convert, "Convert", 2, "xx"); + + SQAIEventEngineAvailable.DefSQMethod(engine, &AIEventEngineAvailable::GetEngineID, "GetEngineID", 1, "x"); + + SQAIEventEngineAvailable.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIEventStationFirstVehicle to be used as Squirrel parameter */ + template <> AIEventStationFirstVehicle *GetParam(ForceType<AIEventStationFirstVehicle *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventStationFirstVehicle *)instance; } + template <> AIEventStationFirstVehicle &GetParam(ForceType<AIEventStationFirstVehicle &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventStationFirstVehicle *)instance; } + template <> const AIEventStationFirstVehicle *GetParam(ForceType<const AIEventStationFirstVehicle *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventStationFirstVehicle *)instance; } + template <> const AIEventStationFirstVehicle &GetParam(ForceType<const AIEventStationFirstVehicle &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventStationFirstVehicle *)instance; } + template <> int Return<AIEventStationFirstVehicle *>(HSQUIRRELVM vm, AIEventStationFirstVehicle *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventStationFirstVehicle", res, NULL, DefSQDestructorCallback<AIEventStationFirstVehicle>); return 1; } +}; // namespace SQConvert + +void SQAIEventStationFirstVehicle_Register(Squirrel *engine) { + DefSQClass <AIEventStationFirstVehicle> SQAIEventStationFirstVehicle("AIEventStationFirstVehicle"); + SQAIEventStationFirstVehicle.PreRegister(engine, "AIEvent"); + + SQAIEventStationFirstVehicle.DefSQStaticMethod(engine, &AIEventStationFirstVehicle::GetClassName, "GetClassName", 1, "x"); + SQAIEventStationFirstVehicle.DefSQStaticMethod(engine, &AIEventStationFirstVehicle::Convert, "Convert", 2, "xx"); + + SQAIEventStationFirstVehicle.DefSQMethod(engine, &AIEventStationFirstVehicle::GetStationID, "GetStationID", 1, "x"); + SQAIEventStationFirstVehicle.DefSQMethod(engine, &AIEventStationFirstVehicle::GetVehicleID, "GetVehicleID", 1, "x"); + + SQAIEventStationFirstVehicle.PostRegister(engine); +} diff --git a/src/ai/api/ai_execmode.cpp b/src/ai/api/ai_execmode.cpp new file mode 100644 index 000000000..941718bee --- /dev/null +++ b/src/ai/api/ai_execmode.cpp @@ -0,0 +1,26 @@ +/* $Id$ */ + +/** @file ai_execmode.cpp Implementation of AIExecMode. */ + +#include "ai_execmode.hpp" +#include "../../command_type.h" + +bool AIExecMode::ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCost costs) +{ + /* In execution mode we only return 'true', telling the DoCommand it + * should continue with the real execution of the command. */ + return true; +} + +AIExecMode::AIExecMode() +{ + this->last_mode = this->GetDoCommandMode(); + this->last_instance = this->GetDoCommandModeInstance(); + this->SetDoCommandMode(&AIExecMode::ModeProc, this); +} + +AIExecMode::~AIExecMode() +{ + assert(this->GetDoCommandModeInstance() == this); + this->SetDoCommandMode(this->last_mode, this->last_instance); +} diff --git a/src/ai/api/ai_execmode.hpp b/src/ai/api/ai_execmode.hpp new file mode 100644 index 000000000..4f6a47bc4 --- /dev/null +++ b/src/ai/api/ai_execmode.hpp @@ -0,0 +1,46 @@ +/* $Id$ */ + +/** @file ai_execmode.hpp Switch the AI to Execute Mode. */ + +#ifndef AI_EXECMODE_HPP +#define AI_EXECMODE_HPP + +#include "ai_object.hpp" + +/** + * Class to switch current mode to Execute Mode. + * If you create an instance of this class, the mode will be switched to + * Execute. The original mode is stored and recovered from when ever the + * instance is destroyed. + * In Execute mode all commands you do are executed for real. + */ +class AIExecMode : public AIObject { +public: + static const char *GetClassName() { return "AIExecMode"; } + +private: + AIModeProc *last_mode; + AIObject *last_instance; + +protected: + /** + * The callback proc for Execute mode. + */ + static bool ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCost costs); + +public: + /** + * Creating instance of this class switches the build mode to Execute. + * @note When the instance is destroyed, he restores the mode that was + * current when the instance was created! + */ + AIExecMode(); + + /** + * Destroying this instance reset the building mode to the mode it was + * in when the instance was created. + */ + ~AIExecMode(); +}; + +#endif /* AI_EXECMODE_HPP */ diff --git a/src/ai/api/ai_execmode.hpp.sq b/src/ai/api/ai_execmode.hpp.sq new file mode 100644 index 000000000..de78e9b6b --- /dev/null +++ b/src/ai/api/ai_execmode.hpp.sq @@ -0,0 +1,23 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_execmode.hpp" + +namespace SQConvert { + /* Allow AIExecMode to be used as Squirrel parameter */ + template <> AIExecMode *GetParam(ForceType<AIExecMode *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIExecMode *)instance; } + template <> AIExecMode &GetParam(ForceType<AIExecMode &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIExecMode *)instance; } + template <> const AIExecMode *GetParam(ForceType<const AIExecMode *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIExecMode *)instance; } + template <> const AIExecMode &GetParam(ForceType<const AIExecMode &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIExecMode *)instance; } + template <> int Return<AIExecMode *>(HSQUIRRELVM vm, AIExecMode *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIExecMode", res, NULL, DefSQDestructorCallback<AIExecMode>); return 1; } +}; // namespace SQConvert + +void SQAIExecMode_Register(Squirrel *engine) { + DefSQClass <AIExecMode> SQAIExecMode("AIExecMode"); + SQAIExecMode.PreRegister(engine); + SQAIExecMode.AddConstructor<void (AIExecMode::*)(), 1>(engine, "x"); + + SQAIExecMode.DefSQStaticMethod(engine, &AIExecMode::GetClassName, "GetClassName", 1, "x"); + + SQAIExecMode.PostRegister(engine); +} diff --git a/src/ai/api/ai_gamesettings.cpp b/src/ai/api/ai_gamesettings.cpp new file mode 100644 index 000000000..81b187d73 --- /dev/null +++ b/src/ai/api/ai_gamesettings.cpp @@ -0,0 +1,38 @@ +/* $Id$ */ + +/** @file ai_gamesettings.cpp Implementation of AIGameSettings. */ + +#include "ai_gamesettings.hpp" +#include "../../settings_internal.h" +#include "../../saveload/saveload.h" + +/* static */ bool AIGameSettings::IsValid(const char *setting) +{ + uint i; + const SettingDesc *sd = GetPatchFromName(setting, &i); + return sd != NULL && sd->desc.cmd != SDT_STRING; +} + +/* static */ int32 AIGameSettings::GetValue(const char *setting) +{ + if (!IsValid(setting)) return -1; + + uint i; + const SettingDesc *sd = GetPatchFromName(setting, &i); + + void *ptr = GetVariableAddress(&_settings_game, &sd->save); + if (sd->desc.cmd == SDT_BOOLX) return *(bool*)ptr; + + return (int32)ReadValue(ptr, sd->save.conv); +} + +/* static */ bool AIGameSettings::IsDisabledVehicleType(AIVehicle::VehicleType vehicle_type) +{ + switch (vehicle_type) { + case AIVehicle::VEHICLE_RAIL: return _settings_game.ai.ai_disable_veh_train; + case AIVehicle::VEHICLE_ROAD: return _settings_game.ai.ai_disable_veh_roadveh; + case AIVehicle::VEHICLE_WATER: return _settings_game.ai.ai_disable_veh_ship; + case AIVehicle::VEHICLE_AIR: return _settings_game.ai.ai_disable_veh_aircraft; + default: return true; + } +} diff --git a/src/ai/api/ai_gamesettings.hpp b/src/ai/api/ai_gamesettings.hpp new file mode 100644 index 000000000..67c8d964e --- /dev/null +++ b/src/ai/api/ai_gamesettings.hpp @@ -0,0 +1,67 @@ +/* $Id$ */ + +/** @file ai_gamesettings.hpp Everything to read game settings. */ + +#ifndef AI_GAMESETTINGS_HPP +#define AI_GAMESETTINGS_HPP + +#include "ai_object.hpp" +#include "ai_vehicle.hpp" + +/** + * Class that handles all game settings related functions. + * + * @note AIGameSettings::IsValid and AIGameSettings::GetValue are functions + * that rely on the settings as OpenTTD stores them in savegame and + * openttd.cfg. No guarantees can be given on the long term validity, + * consistency and stability of the names, values and value ranges. + * Using these settings can be dangerous and could cause issues in + * future versions. To make sure that a setting still exists in the + * current version you have to run AIGameSettings::IsValid before + * accessing it. + * + * @note The names of the setting for AIGameSettings::IsValid and + * AIGameSettings::GetValue are the same ones as those that are shown by + * the list_patches command in the in-game console. Settings that are + * string based are NOT supported and AIGAmeSettings::IsValid will return + * false for them. These settings will not be supported either because + * they have no relevance for the AI (default client names, server IPs, + * etc.). + */ +class AIGameSettings : public AIObject { +public: + static const char *GetClassName() { return "AIGameSettings"; } + + /** + * Is the given game setting a valid setting for this instance of OpenTTD? + * @param setting The setting to check for existence. + * @warning Results of this function are not governed by the API. This means + * that a setting that previously existed can be gone or has + * changed it's name. + * @note Results achieved in the past offer no gurantee for the future. + * @return True if and only if the setting is valid. + */ + static bool IsValid(const char *setting); + + /** + * Gets the value of the game setting. + * @param setting The setting to get the value of. + * @pre IsValid(setting). + * @warning Results of this function are not governed by the API. This means + * that the value of settings may be out of the expected range. It + * also means that a setting that previously existed can be gone or + * has changed it's name/characteristics. + * @note Results achieved in the past offer no gurantee for the future. + * @return The value for the setting. + */ + static int32 GetValue(const char *setting); + + /** + * Checks whether the given vehicle-type is disabled for AIs. + * @param vehicle_type The vehicle-type to check. + * @return True if the vehicle-type is disabled. + */ + static bool IsDisabledVehicleType(AIVehicle::VehicleType vehicle_type); +}; + +#endif /* AI_GAMESETTINGS_HPP */ diff --git a/src/ai/api/ai_gamesettings.hpp.sq b/src/ai/api/ai_gamesettings.hpp.sq new file mode 100644 index 000000000..e21812c08 --- /dev/null +++ b/src/ai/api/ai_gamesettings.hpp.sq @@ -0,0 +1,26 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_gamesettings.hpp" + +namespace SQConvert { + /* Allow AIGameSettings to be used as Squirrel parameter */ + template <> AIGameSettings *GetParam(ForceType<AIGameSettings *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIGameSettings *)instance; } + template <> AIGameSettings &GetParam(ForceType<AIGameSettings &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIGameSettings *)instance; } + template <> const AIGameSettings *GetParam(ForceType<const AIGameSettings *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIGameSettings *)instance; } + template <> const AIGameSettings &GetParam(ForceType<const AIGameSettings &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIGameSettings *)instance; } + template <> int Return<AIGameSettings *>(HSQUIRRELVM vm, AIGameSettings *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIGameSettings", res, NULL, DefSQDestructorCallback<AIGameSettings>); return 1; } +}; // namespace SQConvert + +void SQAIGameSettings_Register(Squirrel *engine) { + DefSQClass <AIGameSettings> SQAIGameSettings("AIGameSettings"); + SQAIGameSettings.PreRegister(engine); + SQAIGameSettings.AddConstructor<void (AIGameSettings::*)(), 1>(engine, "x"); + + SQAIGameSettings.DefSQStaticMethod(engine, &AIGameSettings::GetClassName, "GetClassName", 1, "x"); + SQAIGameSettings.DefSQStaticMethod(engine, &AIGameSettings::IsValid, "IsValid", 2, "xs"); + SQAIGameSettings.DefSQStaticMethod(engine, &AIGameSettings::GetValue, "GetValue", 2, "xs"); + SQAIGameSettings.DefSQStaticMethod(engine, &AIGameSettings::IsDisabledVehicleType, "IsDisabledVehicleType", 2, "xi"); + + SQAIGameSettings.PostRegister(engine); +} diff --git a/src/ai/api/ai_group.cpp b/src/ai/api/ai_group.cpp new file mode 100644 index 000000000..a6365450b --- /dev/null +++ b/src/ai/api/ai_group.cpp @@ -0,0 +1,128 @@ +/* $Id$ */ + +/** @file ai_group.cpp Implementation of AIGroup. */ + +#include "ai_group.hpp" +#include "ai_vehicle.hpp" +#include "ai_engine.hpp" +#include "../ai_instance.hpp" +#include "../../openttd.h" +#include "../../company_func.h" +#include "../../group.h" +#include "../../string_func.h" +#include "../../strings_func.h" +#include "../../core/alloc_func.hpp" +#include "../../command_func.h" +#include "../../autoreplace_func.h" +#include "table/strings.h" + +/* static */ bool AIGroup::IsValidGroup(GroupID group_id) +{ + return ::IsValidGroupID(group_id) && ::GetGroup(group_id)->owner == _current_company; +} + +/* static */ AIGroup::GroupID AIGroup::CreateGroup(AIVehicle::VehicleType vehicle_type) +{ + if (!AIObject::DoCommand(0, (::VehicleType)vehicle_type, 0, CMD_CREATE_GROUP, NULL, &AIInstance::DoCommandReturnGroupID)) return INVALID_GROUP; + + /* In case of test-mode, we return GroupID 0 */ + return (AIGroup::GroupID)0; +} + +/* static */ bool AIGroup::DeleteGroup(GroupID group_id) +{ + EnforcePrecondition(false, IsValidGroup(group_id)); + + return AIObject::DoCommand(0, group_id, 0, CMD_DELETE_GROUP); +} + +/* static */ AIVehicle::VehicleType AIGroup::GetVehicleType(GroupID group_id) +{ + if (!IsValidGroup(group_id)) return AIVehicle::VEHICLE_INVALID; + + return (AIVehicle::VehicleType)((::VehicleType)::GetGroup(group_id)->vehicle_type); +} + +/* static */ bool AIGroup::SetName(GroupID group_id, const char *name) +{ + EnforcePrecondition(false, IsValidGroup(group_id)); + EnforcePrecondition(false, !::StrEmpty(name)); + EnforcePreconditionCustomError(false, ::strlen(name) < MAX_LENGTH_GROUP_NAME_BYTES, AIError::ERR_PRECONDITION_STRING_TOO_LONG); + + return AIObject::DoCommand(0, group_id, 0, CMD_RENAME_GROUP, name); +} + +/* static */ const char *AIGroup::GetName(GroupID group_id) +{ + if (!IsValidGroup(group_id)) return NULL; + + static const int len = 64; + char *group_name = MallocT<char>(len); + + ::SetDParam(0, group_id); + ::GetString(group_name, STR_GROUP_NAME, &group_name[len - 1]); + return group_name; +} + +/* static */ bool AIGroup::EnableAutoReplaceProtection(GroupID group_id, bool enable) +{ + EnforcePrecondition(false, IsValidGroup(group_id)); + + return AIObject::DoCommand(0, group_id, enable ? 1 : 0, CMD_SET_GROUP_REPLACE_PROTECTION); +} + +/* static */ bool AIGroup::GetAutoReplaceProtection(GroupID group_id) +{ + if (!IsValidGroup(group_id)) return false; + + return ::GetGroup(group_id)->replace_protection; +} + +/* static */ int32 AIGroup::GetNumEngines(GroupID group_id, EngineID engine_id) +{ + if (!IsValidGroup(group_id) && group_id != DEFAULT_GROUP && group_id != ALL_GROUP) return -1; + + return GetGroupNumEngines(_current_company, group_id, engine_id); +} + +/* static */ bool AIGroup::MoveVehicle(GroupID group_id, VehicleID vehicle_id) +{ + EnforcePrecondition(false, IsValidGroup(group_id) || group_id == DEFAULT_GROUP); + EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id)); + + return AIObject::DoCommand(0, group_id, vehicle_id, CMD_ADD_VEHICLE_GROUP); +} + +/* static */ bool AIGroup::EnableWagonRemoval(bool enable_removal) +{ + if (HasWagonRemoval() == enable_removal) return true; + + return AIObject::DoCommand(0, 5, enable_removal ? 1 : 0, CMD_SET_AUTOREPLACE); +} + +/* static */ bool AIGroup::HasWagonRemoval() +{ + return ::GetCompany(_current_company)->renew_keep_length; +} + +/* static */ bool AIGroup::SetAutoReplace(GroupID group_id, EngineID engine_id_old, EngineID engine_id_new) +{ + EnforcePrecondition(false, IsValidGroup(group_id) || group_id == ALL_GROUP); + EnforcePrecondition(false, AIEngine::IsValidEngine(engine_id_new)); + + return AIObject::DoCommand(0, 3 | (group_id << 16), (engine_id_new << 16) | engine_id_old, CMD_SET_AUTOREPLACE); +} + +/* static */ EngineID AIGroup::GetEngineReplacement(GroupID group_id, EngineID engine_id) +{ + if (!IsValidGroup(group_id) && group_id != ALL_GROUP) return ::INVALID_ENGINE; + + return ::EngineReplacementForCompany(GetCompany(_current_company), engine_id, group_id); +} + +/* static */ bool AIGroup::StopAutoReplace(GroupID group_id, EngineID engine_id) +{ + EnforcePrecondition(false, IsValidGroup(group_id) || group_id == ALL_GROUP); + + return AIObject::DoCommand(0, 3 | (group_id << 16), (::INVALID_ENGINE << 16) | engine_id, CMD_SET_AUTOREPLACE); +} diff --git a/src/ai/api/ai_group.hpp b/src/ai/api/ai_group.hpp new file mode 100644 index 000000000..84569c562 --- /dev/null +++ b/src/ai/api/ai_group.hpp @@ -0,0 +1,170 @@ +/* $Id$ */ + +/** @file ai_group.hpp Everything to put vehicles into groups. */ + +#ifndef AI_GROUP_HPP +#define AI_GROUP_HPP + +#include "ai_object.hpp" +#include "ai_vehicle.hpp" + +/** + * Class that handles all group related functions. + */ +class AIGroup : public AIObject { +public: + static const char *GetClassName() { return "AIGroup"; } + + /** + * The group IDs of some special groups. + */ + enum GroupID { + /* Values are important, as they represent the internal state of the game (see group_type.h). */ + ALL_GROUP = 0xFFFD, //!< All vehicles are in this group. + DEFAULT_GROUP = 0xFFFE, //!< Vehicles not put in any other group are in this one. + INVALID_GROUP = 0xFFFF, //!< An invalid group id. + }; + + /** + * Checks whether the given group is valid. + * @param group_id The group to check. + * @pre group_id != DEFAULT_GROUP && group_id != ALL_GROUP. + * @return True if and only if the group is valid. + */ + static bool IsValidGroup(GroupID group_id); + + /** + * Create a new group. + * @param vehicle_type The type of vehicle to create a group for. + * @return The GroupID of the new group, or an invalid GroupID when + * it failed. Check the return value using IsValidGroup(). In test-mode + * 0 is returned if it was successful; any other value indicates failure. + */ + static GroupID CreateGroup(AIVehicle::VehicleType vehicle_type); + + /** + * Delete the given group. When the deletion succeeds all vehicles in the + * given group will move to the DEFAULT_GROUP. + * @param group_id The group to delete. + * @pre IsValidGroup(group_id). + * @return True if and only if the group was succesfully deleted. + */ + static bool DeleteGroup(GroupID group_id); + + /** + * Get the vehicle type of a group. + * @param group_id The group to get the type from. + * @pre IsValidGroup(group_id). + * @return The vehicletype of the given group. + */ + static AIVehicle::VehicleType GetVehicleType(GroupID group_id); + + /** + * Set the name of a group. + * @param group_id The group to set the name for. + * @param name The name for the group. + * @pre IsValidGroup(group_id). + * @pre 'name' must have at least one character. + * @pre 'name' must have at most 30 characters. + * @exception AIError::ERR_NAME_IS_NOT_UNIQUE + * @return True if and only if the name was changed. + */ + static bool SetName(GroupID group_id, const char *name); + + /** + * Get the name of a group. + * @param group_id The group to get the name of. + * @pre IsValidGroup(group_id). + * @return The name the group has. + */ + static const char *GetName(GroupID group_id); + + /** + * Enable or disable autoreplace protected. If the protection is + * enabled, global autoreplace won't affect vehicles in this group. + * @param group_id The group to change the protection for. + * @param enable True if protection should be enabled. + * @pre IsValidGroup(group_id). + * @return True if and only if the protection was succesfully changed. + */ + static bool EnableAutoReplaceProtection(GroupID group_id, bool enable); + + /** + * Get the autoreplace protection status. + * @param group_id The group to get the protection status for. + * @pre IsValidGroup(group_id). + * @return The autoreplace protection status for the given group. + */ + static bool GetAutoReplaceProtection(GroupID group_id); + + /** + * Get the number of engines in a given group. + * @param group_id The group to get the number of engines in. + * @param engine_id The engine id to count. + * @pre IsValidGroup(group_id) || group_id == ALL_GROUP || group_id == DEFAULT_GROUP. + * @return The number of engines with id engine_id in the group with id group_id. + */ + static int32 GetNumEngines(GroupID group_id, EngineID engine_id); + + /** + * Move a vehicle to a group. + * @param group_id The group to move the vehicel to. + * @param vehicle_id The vehicle to move to the group. + * @pre IsValidGroup(group_id) || group_id == DEFAULT_GROUP. + * @pre AIVehicle::IsValidVehicle(vehicle_id). + * @return True if and only if the vehicle was succesfully moved to the group. + * @note A vehicle can be in only one group at the same time. To remove it from + * a group, move it to another or to DEFAULT_GROUP. Moving the vehicle to the + * given group means removing it from another group. + */ + static bool MoveVehicle(GroupID group_id, VehicleID vehicle_id); + + /** + * Enable or disable the removal of wagons when a (part of a) vehicle is + * (auto)replaced with a longer variant (longer wagons or longer engines) + * If enabled, wagons are removed from the end of the vehicle until it + * fits in the same number of tiles as it did before. + * @param keep_length If true, wagons will be removed if the a new engine is longer. + * @return True if and only if the value was succesfully changed. + */ + static bool EnableWagonRemoval(bool keep_length); + + /** + * Get the current status of wagon removal. + * @return Whether or not wagon removal is enabled. + */ + static bool HasWagonRemoval(); + + /** + * Start replacing all vehicles with a specified engine with another engine. + * @param group_id The group to replace vehicles from. Use ALL_GROUP to replace + * vehicles from all groups that haven't set autoreplace protection. + * @param engine_id_old The engine id to start replacing. + * @param engine_id_new The engine id to replace with. + * @pre IsValidGroup(group_id) || group_id == ALL_GROUP. + * @pre AIEngine.IsValidEngine(engine_id_new). + * @note To stop autoreplacing engine_id_old, call StopAutoReplace(group_id, engine_id_old). + */ + static bool SetAutoReplace(GroupID group_id, EngineID engine_id_old, EngineID engine_id_new); + + /** + * Get the EngineID the given EngineID is replaced with. + * @param group_id The group to get the replacement from. + * @param engine_id The engine that is being replaced. + * @pre IsValidGroup(group_id) || group_id == ALL_GROUP. + * @return The EngineID that is replacing engine_id or an invalid EngineID + * in case engine_id is not begin replaced. + */ + static EngineID GetEngineReplacement(GroupID group_id, EngineID engine_id); + + /** + * Stop replacing a certain engine in the specified group. + * @param group_id The group to stop replacing the engine in. + * @param engine_id The engine id to stop replacing with another engine. + * @pre IsValidGroup(group_id) || group_id == ALL_GROUP. + * @return True if and if the replacing was succesfully stopped. + */ + static bool StopAutoReplace(GroupID group_id, EngineID engine_id); +}; + +#endif /* AI_GROUP_HPP */ diff --git a/src/ai/api/ai_group.hpp.sq b/src/ai/api/ai_group.hpp.sq new file mode 100644 index 000000000..3556f5cc9 --- /dev/null +++ b/src/ai/api/ai_group.hpp.sq @@ -0,0 +1,46 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_group.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AIGroup::GroupID GetParam(ForceType<AIGroup::GroupID>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIGroup::GroupID)tmp; } + template <> int Return<AIGroup::GroupID>(HSQUIRRELVM vm, AIGroup::GroupID res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AIGroup to be used as Squirrel parameter */ + template <> AIGroup *GetParam(ForceType<AIGroup *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIGroup *)instance; } + template <> AIGroup &GetParam(ForceType<AIGroup &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIGroup *)instance; } + template <> const AIGroup *GetParam(ForceType<const AIGroup *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIGroup *)instance; } + template <> const AIGroup &GetParam(ForceType<const AIGroup &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIGroup *)instance; } + template <> int Return<AIGroup *>(HSQUIRRELVM vm, AIGroup *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIGroup", res, NULL, DefSQDestructorCallback<AIGroup>); return 1; } +}; // namespace SQConvert + +void SQAIGroup_Register(Squirrel *engine) { + DefSQClass <AIGroup> SQAIGroup("AIGroup"); + SQAIGroup.PreRegister(engine); + SQAIGroup.AddConstructor<void (AIGroup::*)(), 1>(engine, "x"); + + SQAIGroup.DefSQConst(engine, AIGroup::ALL_GROUP, "ALL_GROUP"); + SQAIGroup.DefSQConst(engine, AIGroup::DEFAULT_GROUP, "DEFAULT_GROUP"); + SQAIGroup.DefSQConst(engine, AIGroup::INVALID_GROUP, "INVALID_GROUP"); + + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetClassName, "GetClassName", 1, "x"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::IsValidGroup, "IsValidGroup", 2, "xi"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::CreateGroup, "CreateGroup", 2, "xi"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::DeleteGroup, "DeleteGroup", 2, "xi"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetVehicleType, "GetVehicleType", 2, "xi"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::SetName, "SetName", 3, "xis"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetName, "GetName", 2, "xi"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::EnableAutoReplaceProtection, "EnableAutoReplaceProtection", 3, "xib"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetAutoReplaceProtection, "GetAutoReplaceProtection", 2, "xi"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetNumEngines, "GetNumEngines", 3, "xii"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::MoveVehicle, "MoveVehicle", 3, "xii"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::EnableWagonRemoval, "EnableWagonRemoval", 2, "xb"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::HasWagonRemoval, "HasWagonRemoval", 1, "x"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::SetAutoReplace, "SetAutoReplace", 4, "xiii"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetEngineReplacement, "GetEngineReplacement", 3, "xii"); + SQAIGroup.DefSQStaticMethod(engine, &AIGroup::StopAutoReplace, "StopAutoReplace", 3, "xii"); + + SQAIGroup.PostRegister(engine); +} diff --git a/src/ai/api/ai_grouplist.cpp b/src/ai/api/ai_grouplist.cpp new file mode 100644 index 000000000..be57b3750 --- /dev/null +++ b/src/ai/api/ai_grouplist.cpp @@ -0,0 +1,16 @@ +/* $Id$ */ + +/** @file ai_grouplist.cpp Implementation of AIGroupList and friends. */ + +#include "ai_grouplist.hpp" +#include "../../openttd.h" +#include "../../company_func.h" +#include "../../group.h" + +AIGroupList::AIGroupList() +{ + Group *g; + FOR_ALL_GROUPS(g) { + if (g->owner == _current_company) this->AddItem(g->index); + } +} diff --git a/src/ai/api/ai_grouplist.hpp b/src/ai/api/ai_grouplist.hpp new file mode 100644 index 000000000..d97ef85c7 --- /dev/null +++ b/src/ai/api/ai_grouplist.hpp @@ -0,0 +1,21 @@ +/* $Id$ */ + +/** @file ai_grouplist.hpp List all the groups (you own). */ + +#ifndef AI_GROUPLIST_HPP +#define AI_GROUPLIST_HPP + +#include "ai_abstractlist.hpp" + +/** + * Creates a list of groups of which you are the owner. + * @note Neither AIGroup.ALL_GROUP nor AIGroup.DEFAULT_GROUP is in this list. + * @ingroup AIList + */ +class AIGroupList : public AIAbstractList { +public: + static const char *GetClassName() { return "AIGroupList"; } + AIGroupList(); +}; + +#endif /* AI_GROUPLIST_HPP */ diff --git a/src/ai/api/ai_grouplist.hpp.sq b/src/ai/api/ai_grouplist.hpp.sq new file mode 100644 index 000000000..b5c4d945d --- /dev/null +++ b/src/ai/api/ai_grouplist.hpp.sq @@ -0,0 +1,23 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_grouplist.hpp" + +namespace SQConvert { + /* Allow AIGroupList to be used as Squirrel parameter */ + template <> AIGroupList *GetParam(ForceType<AIGroupList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIGroupList *)instance; } + template <> AIGroupList &GetParam(ForceType<AIGroupList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIGroupList *)instance; } + template <> const AIGroupList *GetParam(ForceType<const AIGroupList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIGroupList *)instance; } + template <> const AIGroupList &GetParam(ForceType<const AIGroupList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIGroupList *)instance; } + template <> int Return<AIGroupList *>(HSQUIRRELVM vm, AIGroupList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIGroupList", res, NULL, DefSQDestructorCallback<AIGroupList>); return 1; } +}; // namespace SQConvert + +void SQAIGroupList_Register(Squirrel *engine) { + DefSQClass <AIGroupList> SQAIGroupList("AIGroupList"); + SQAIGroupList.PreRegister(engine, "AIAbstractList"); + SQAIGroupList.AddConstructor<void (AIGroupList::*)(), 1>(engine, "x"); + + SQAIGroupList.DefSQStaticMethod(engine, &AIGroupList::GetClassName, "GetClassName", 1, "x"); + + SQAIGroupList.PostRegister(engine); +} diff --git a/src/ai/api/ai_industry.cpp b/src/ai/api/ai_industry.cpp new file mode 100644 index 000000000..64687af51 --- /dev/null +++ b/src/ai/api/ai_industry.cpp @@ -0,0 +1,170 @@ +/* $Id$ */ + +/** @file ai_industry.cpp Implementation of AIIndustry. */ + +#include "ai_industry.hpp" +#include "ai_cargo.hpp" +#include "ai_map.hpp" +#include "../../openttd.h" +#include "../../tile_type.h" +#include "../../industry.h" +#include "../../strings_func.h" +#include "../../station_func.h" +#include "table/strings.h" + +/* static */ IndustryID AIIndustry::GetMaxIndustryID() +{ + return ::GetMaxIndustryIndex(); +} + +/* static */ int32 AIIndustry::GetIndustryCount() +{ + return ::GetNumIndustries(); +} + +/* static */ bool AIIndustry::IsValidIndustry(IndustryID industry_id) +{ + return ::IsValidIndustryID(industry_id); +} + +/* static */ const char *AIIndustry::GetName(IndustryID industry_id) +{ + if (!IsValidIndustry(industry_id)) return NULL; + static const int len = 64; + char *industry_name = MallocT<char>(len); + + ::SetDParam(0, industry_id); + ::GetString(industry_name, STR_INDUSTRY, &industry_name[len - 1]); + + return industry_name; +} + +/* static */ int32 AIIndustry::GetProduction(IndustryID industry_id, CargoID cargo_id) +{ + if (!IsValidIndustry(industry_id)) return -1; + if (!AICargo::IsValidCargo(cargo_id)) return -1; + + const Industry *i = ::GetIndustry(industry_id); + const IndustrySpec *indsp = ::GetIndustrySpec(i->type); + + for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) + if (indsp->produced_cargo[j] == cargo_id) return i->production_rate[j] * 8; + + return -1; +} + +/* static */ bool AIIndustry::IsCargoAccepted(IndustryID industry_id, CargoID cargo_id) +{ + if (!IsValidIndustry(industry_id)) return false; + if (!AICargo::IsValidCargo(cargo_id)) return false; + + const Industry *i = ::GetIndustry(industry_id); + const IndustrySpec *indsp = ::GetIndustrySpec(i->type); + + for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) + if (indsp->accepts_cargo[j] == cargo_id) return true; + + return false; +} + +/* static */ int32 AIIndustry::GetStockpiledCargo(IndustryID industry_id, CargoID cargo_id) +{ + if (!IsValidIndustry(industry_id)) return -1; + if (!AICargo::IsValidCargo(cargo_id)) return -1; + + Industry *ind = ::GetIndustry(industry_id); + for (uint i = 0; i < lengthof(ind->accepts_cargo); i++) { + CargoID cid = ind->accepts_cargo[i]; + if (cid == cargo_id) { + return ind->incoming_cargo_waiting[i]; + } + } + + return -1; +} + +/* static */ int32 AIIndustry::GetLastMonthProduction(IndustryID industry_id, CargoID cargo_id) +{ + if (!IsValidIndustry(industry_id)) return -1; + if (!AICargo::IsValidCargo(cargo_id)) return -1; + + const Industry *i = ::GetIndustry(industry_id); + const IndustrySpec *indsp = ::GetIndustrySpec(i->type); + + for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) + if (indsp->produced_cargo[j] == cargo_id) return i->last_month_production[j]; + + return -1; +} + +/* static */ int32 AIIndustry::GetLastMonthTransported(IndustryID industry_id, CargoID cargo_id) +{ + if (!IsValidIndustry(industry_id)) return -1; + if (!AICargo::IsValidCargo(cargo_id)) return -1; + + const Industry *i = ::GetIndustry(industry_id); + const IndustrySpec *indsp = ::GetIndustrySpec(i->type); + + for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) + if (indsp->produced_cargo[j] == cargo_id) return i->last_month_transported[j]; + + return -1; +} + +/* static */ TileIndex AIIndustry::GetLocation(IndustryID industry_id) +{ + if (!IsValidIndustry(industry_id)) return INVALID_TILE; + + return ::GetIndustry(industry_id)->xy; +} + +/* static */ int32 AIIndustry::GetAmountOfStationsAround(IndustryID industry_id) +{ + if (!IsValidIndustry(industry_id)) return -1; + + Industry *ind = ::GetIndustry(industry_id); + return (int32)::FindStationsAroundTiles(ind->xy, ind->width, ind->height).size(); +} + +/* static */ int32 AIIndustry::GetDistanceManhattanToTile(IndustryID industry_id, TileIndex tile) +{ + if (!IsValidIndustry(industry_id)) return -1; + + return AIMap::DistanceManhattan(tile, GetLocation(industry_id)); +} + +/* static */ int32 AIIndustry::GetDistanceSquareToTile(IndustryID industry_id, TileIndex tile) +{ + if (!IsValidIndustry(industry_id)) return -1; + + return AIMap::DistanceSquare(tile, GetLocation(industry_id)); +} + +/* static */ bool AIIndustry::IsBuiltOnWater(IndustryID industry_id) +{ + if (!IsValidIndustry(industry_id)) return false; + + return (::GetIndustrySpec(::GetIndustry(industry_id)->type)->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0; +} + +/* static */ bool AIIndustry::HasHeliportAndDock(IndustryID industry_id) +{ + if (!IsValidIndustry(industry_id)) return false; + + return (::GetIndustrySpec(::GetIndustry(industry_id)->type)->behaviour & INDUSTRYBEH_AI_AIRSHIP_ROUTES) != 0; +} + +/* static */ TileIndex AIIndustry::GetHeliportAndDockLocation(IndustryID industry_id) +{ + if (!IsValidIndustry(industry_id)) return INVALID_TILE; + if (!HasHeliportAndDock(industry_id)) return INVALID_TILE; + + return ::GetIndustry(industry_id)->xy; +} + +/* static */ IndustryType AIIndustry::GetIndustryType(IndustryID industry_id) +{ + if (!IsValidIndustry(industry_id)) return INVALID_INDUSTRYTYPE; + + return ::GetIndustry(industry_id)->type; +} diff --git a/src/ai/api/ai_industry.hpp b/src/ai/api/ai_industry.hpp new file mode 100644 index 000000000..1b3365a97 --- /dev/null +++ b/src/ai/api/ai_industry.hpp @@ -0,0 +1,171 @@ +/* $Id$ */ + +/** @file ai_industry.hpp Everything to query and build industries. */ + +#ifndef AI_INDUSTRY_HPP +#define AI_INDUSTRY_HPP + +#include "ai_object.hpp" + +/** + * Class that handles all industry related functions. + */ +class AIIndustry : public AIObject { +public: + static const char *GetClassName() { return "AIIndustry"; } + + /** + * Gets the maximum industry index; there are no valid industries with a + * higher index. + * @return The maximum industry index. + * @post Return value is always non-negative. + */ + static IndustryID GetMaxIndustryID(); + + /** + * Gets the number of industries. This is different than GetMaxIndustryID() + * because of the way OpenTTD works internally. + * @return The number of industries. + * @post Return value is always non-negative. + */ + static int32 GetIndustryCount(); + + /** + * Checks whether the given industry index is valid. + * @param industry_id The index to check. + * @return True if and only if the industry is valid. + */ + static bool IsValidIndustry(IndustryID industry_id); + + /** + * Get the name of the industry. + * @param industry_id The industry to get the name of. + * @pre IsValidIndustry(industry_id). + * @return The name of the industry. + */ + static const char *GetName(IndustryID industry_id); + + /** + * Gets the production of a cargo of the industry. + * @param industry_id The index of the industry. + * @param cargo_id The index of the cargo. + * @pre IsValidIndustry(industry_id). + * @pre AICargo::IsValidCargo(cargo_id). + * @return The production of the cargo for this industry, or -1 if + * this industry doesn't produce this cargo. + */ + static int32 GetProduction(IndustryID industry_id, CargoID cargo_id); + + /** + * See if an industry accepts a certain cargo. + * @param industry_id The index of the industry. + * @param cargo_id The index of the cargo. + * @pre IsValidIndustry(industry_id). + * @pre AICargo::IsValidCargo(cargo_id). + * @return The production of the cargo for this industry. + */ + static bool IsCargoAccepted(IndustryID industry_id, CargoID cargo_id); + + /** + * Get the amount of cargo stockpiled for processing. + * @param industry_id The index of the industry. + * @param cargo_id The index of the cargo. + * @pre IsValidIndustry(industry_id). + * @pre AICargo::IsValidCargo(cargo_id). + * @return The amount of cargo that is waiting for processing. + */ + static int32 GetStockpiledCargo(IndustryID industry_id, CargoID cargo_id); + + /** + * Get the total last month's production of the given cargo at an industry. + * @param industry_id The index of the industry. + * @param cargo_id The index of the cargo. + * @pre IsValidIndustry(industry_id). + * @pre AICargo::IsValidCargo(cargo_id). + * @return The last month's production of the given cargo for this industry. + */ + static int32 GetLastMonthProduction(IndustryID industry_id, CargoID cargo_id); + + /** + * Get the total amount of cargo transported from an industry last month. + * @param industry_id The index of the industry. + * @param cargo_id The index of the cargo. + * @pre IsValidIndustry(industry_id). + * @pre AICargo::IsValidCargo(cargo_id). + * @return The amount of given cargo transported from this industry last month. + */ + static int32 GetLastMonthTransported(IndustryID industry_id, CargoID cargo_id); + + /** + * Gets the location of the industry. + * @param industry_id The index of the industry. + * @pre IsValidIndustry(industry_id). + * @return The location of the industry. + */ + static TileIndex GetLocation(IndustryID industry_id); + + /** + * Get the number of stations around an industry. + * @param industry_id The index of the industry. + * @pre IsValidIndustry(industry_id). + * @return The number of stations around an industry. + */ + static int32 GetAmountOfStationsAround(IndustryID industry_id); + + /** + * Get the manhattan distance from the tile to the AIIndustry::GetLocation() + * of the industry. + * @param industry_id The industry to get the distance to. + * @param tile The tile to get the distance to. + * @pre IsValidIndustry(industry_id). + * @pre AIMap::IsValidTile(tile). + * @return The distance between industry and tile. + */ + static int32 GetDistanceManhattanToTile(IndustryID industry_id, TileIndex tile); + + /** + * Get the square distance from the tile to the AIIndustry::GetLocation() + * of the industry. + * @param industry_id The industry to get the distance to. + * @param tile The tile to get the distance to. + * @pre IsValidIndustry(industry_id). + * @pre AIMap::IsValidTile(tile). + * @return The distance between industry and tile. + */ + static int32 GetDistanceSquareToTile(IndustryID industry_id, TileIndex tile); + + /** + * Is this industry built on water. + * @param industry_id The index of the industry. + * @pre IsValidIndustry(industry_id). + * @return True when the industry is built on water. + */ + static bool IsBuiltOnWater(IndustryID industry_id); + + /** + * Does this industry have a heliport and dock? + * @param industry_id The index of the industry. + * @pre IsValidIndustry(industry_id). + * @return True when the industry has a heliport and dock. + */ + static bool HasHeliportAndDock(IndustryID industry_id); + + /** + * Gets the location of the industry's heliport/dock. + * @param industry_id The index of the industry. + * @pre IsValidIndustry(industry_id). + * @pre HasHeliportAndDock(industry_id). + * @return The location of the industry's heliport/dock. + */ + static TileIndex GetHeliportAndDockLocation(IndustryID industry_id); + + /** + * Get the IndustryType of the industry. + * @param industry_id The index of the industry. + * @pre IsValidIndustry(industry_id). + * @return The IndustryType of the industry. + */ + static IndustryType GetIndustryType(IndustryID industry_id); +}; + +#endif /* AI_INDUSTRY_HPP */ diff --git a/src/ai/api/ai_industry.hpp.sq b/src/ai/api/ai_industry.hpp.sq new file mode 100644 index 000000000..dc0493efb --- /dev/null +++ b/src/ai/api/ai_industry.hpp.sq @@ -0,0 +1,40 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_industry.hpp" + +namespace SQConvert { + /* Allow AIIndustry to be used as Squirrel parameter */ + template <> AIIndustry *GetParam(ForceType<AIIndustry *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustry *)instance; } + template <> AIIndustry &GetParam(ForceType<AIIndustry &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustry *)instance; } + template <> const AIIndustry *GetParam(ForceType<const AIIndustry *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustry *)instance; } + template <> const AIIndustry &GetParam(ForceType<const AIIndustry &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustry *)instance; } + template <> int Return<AIIndustry *>(HSQUIRRELVM vm, AIIndustry *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIIndustry", res, NULL, DefSQDestructorCallback<AIIndustry>); return 1; } +}; // namespace SQConvert + +void SQAIIndustry_Register(Squirrel *engine) { + DefSQClass <AIIndustry> SQAIIndustry("AIIndustry"); + SQAIIndustry.PreRegister(engine); + SQAIIndustry.AddConstructor<void (AIIndustry::*)(), 1>(engine, "x"); + + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetClassName, "GetClassName", 1, "x"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetMaxIndustryID, "GetMaxIndustryID", 1, "x"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetIndustryCount, "GetIndustryCount", 1, "x"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::IsValidIndustry, "IsValidIndustry", 2, "xi"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetName, "GetName", 2, "xi"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetProduction, "GetProduction", 3, "xii"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::IsCargoAccepted, "IsCargoAccepted", 3, "xii"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetStockpiledCargo, "GetStockpiledCargo", 3, "xii"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetLastMonthProduction, "GetLastMonthProduction", 3, "xii"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetLastMonthTransported, "GetLastMonthTransported", 3, "xii"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetLocation, "GetLocation", 2, "xi"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetAmountOfStationsAround, "GetAmountOfStationsAround", 2, "xi"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetDistanceManhattanToTile, "GetDistanceManhattanToTile", 3, "xii"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetDistanceSquareToTile, "GetDistanceSquareToTile", 3, "xii"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::IsBuiltOnWater, "IsBuiltOnWater", 2, "xi"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::HasHeliportAndDock, "HasHeliportAndDock", 2, "xi"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetHeliportAndDockLocation, "GetHeliportAndDockLocation", 2, "xi"); + SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetIndustryType, "GetIndustryType", 2, "xi"); + + SQAIIndustry.PostRegister(engine); +} diff --git a/src/ai/api/ai_industrylist.cpp b/src/ai/api/ai_industrylist.cpp new file mode 100644 index 000000000..be8fbcfa4 --- /dev/null +++ b/src/ai/api/ai_industrylist.cpp @@ -0,0 +1,42 @@ +/* $Id$ */ + +/** @file ai_industrylist.cpp Implementation of AIIndustryList and friends. */ + +#include "ai_industrylist.hpp" +#include "../../openttd.h" +#include "../../tile_type.h" +#include "../../industry.h" + +AIIndustryList::AIIndustryList() +{ + Industry *i; + FOR_ALL_INDUSTRIES(i) { + this->AddItem(i->index); + } +} + +AIIndustryList_CargoAccepting::AIIndustryList_CargoAccepting(CargoID cargo_id) +{ + const Industry *i; + const IndustrySpec *indsp; + + FOR_ALL_INDUSTRIES(i) { + indsp = ::GetIndustrySpec(i->type); + + for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) + if (indsp->accepts_cargo[j] == cargo_id) this->AddItem(i->index); + } +} + +AIIndustryList_CargoProducing::AIIndustryList_CargoProducing(CargoID cargo_id) +{ + const Industry *i; + const IndustrySpec *indsp; + + FOR_ALL_INDUSTRIES(i) { + indsp = ::GetIndustrySpec(i->type); + + for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) + if (indsp->produced_cargo[j] == cargo_id) this->AddItem(i->index); + } +} diff --git a/src/ai/api/ai_industrylist.hpp b/src/ai/api/ai_industrylist.hpp new file mode 100644 index 000000000..2afca7b97 --- /dev/null +++ b/src/ai/api/ai_industrylist.hpp @@ -0,0 +1,49 @@ +/* $Id$ */ + +/** @file ai_industrylist.hpp List all the industries. */ + +#ifndef AI_INDUSTRYLIST_HPP +#define AI_INDUSTRYLIST_HPP + +#include "ai_abstractlist.hpp" + +/** + * Creates a list of industries that are currently on the map. + * @ingroup AIList + */ +class AIIndustryList : public AIAbstractList { +public: + static const char *GetClassName() { return "AIIndustryList"; } + AIIndustryList(); +}; + +/** + * Creates a list of industries that accepts a given cargo. + * @ingroup AIList + */ +class AIIndustryList_CargoAccepting : public AIAbstractList { +public: + static const char *GetClassName() { return "AIIndustryList_CargoAccepting"; } + + /** + * @param cargo_id The cargo this industry should accept. + */ + AIIndustryList_CargoAccepting(CargoID cargo_id); +}; + +/** + * Creates a list of industries that can produce a given cargo. + * @note It also contains industries that currently produces 0 units of the cargo. + * @ingroup AIList + */ +class AIIndustryList_CargoProducing : public AIAbstractList { +public: + static const char *GetClassName() { return "AIIndustryList_CargoProducing"; } + + /** + * @param cargo_id The cargo this industry should produce. + */ + AIIndustryList_CargoProducing(CargoID cargo_id); +}; + +#endif /* AI_INDUSTRYLIST_HPP */ diff --git a/src/ai/api/ai_industrylist.hpp.sq b/src/ai/api/ai_industrylist.hpp.sq new file mode 100644 index 000000000..1abc9c549 --- /dev/null +++ b/src/ai/api/ai_industrylist.hpp.sq @@ -0,0 +1,61 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_industrylist.hpp" + +namespace SQConvert { + /* Allow AIIndustryList to be used as Squirrel parameter */ + template <> AIIndustryList *GetParam(ForceType<AIIndustryList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryList *)instance; } + template <> AIIndustryList &GetParam(ForceType<AIIndustryList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryList *)instance; } + template <> const AIIndustryList *GetParam(ForceType<const AIIndustryList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryList *)instance; } + template <> const AIIndustryList &GetParam(ForceType<const AIIndustryList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryList *)instance; } + template <> int Return<AIIndustryList *>(HSQUIRRELVM vm, AIIndustryList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIIndustryList", res, NULL, DefSQDestructorCallback<AIIndustryList>); return 1; } +}; // namespace SQConvert + +void SQAIIndustryList_Register(Squirrel *engine) { + DefSQClass <AIIndustryList> SQAIIndustryList("AIIndustryList"); + SQAIIndustryList.PreRegister(engine, "AIAbstractList"); + SQAIIndustryList.AddConstructor<void (AIIndustryList::*)(), 1>(engine, "x"); + + SQAIIndustryList.DefSQStaticMethod(engine, &AIIndustryList::GetClassName, "GetClassName", 1, "x"); + + SQAIIndustryList.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIIndustryList_CargoAccepting to be used as Squirrel parameter */ + template <> AIIndustryList_CargoAccepting *GetParam(ForceType<AIIndustryList_CargoAccepting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryList_CargoAccepting *)instance; } + template <> AIIndustryList_CargoAccepting &GetParam(ForceType<AIIndustryList_CargoAccepting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryList_CargoAccepting *)instance; } + template <> const AIIndustryList_CargoAccepting *GetParam(ForceType<const AIIndustryList_CargoAccepting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryList_CargoAccepting *)instance; } + template <> const AIIndustryList_CargoAccepting &GetParam(ForceType<const AIIndustryList_CargoAccepting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryList_CargoAccepting *)instance; } + template <> int Return<AIIndustryList_CargoAccepting *>(HSQUIRRELVM vm, AIIndustryList_CargoAccepting *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIIndustryList_CargoAccepting", res, NULL, DefSQDestructorCallback<AIIndustryList_CargoAccepting>); return 1; } +}; // namespace SQConvert + +void SQAIIndustryList_CargoAccepting_Register(Squirrel *engine) { + DefSQClass <AIIndustryList_CargoAccepting> SQAIIndustryList_CargoAccepting("AIIndustryList_CargoAccepting"); + SQAIIndustryList_CargoAccepting.PreRegister(engine, "AIAbstractList"); + SQAIIndustryList_CargoAccepting.AddConstructor<void (AIIndustryList_CargoAccepting::*)(CargoID cargo_id), 2>(engine, "xi"); + + SQAIIndustryList_CargoAccepting.DefSQStaticMethod(engine, &AIIndustryList_CargoAccepting::GetClassName, "GetClassName", 1, "x"); + + SQAIIndustryList_CargoAccepting.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIIndustryList_CargoProducing to be used as Squirrel parameter */ + template <> AIIndustryList_CargoProducing *GetParam(ForceType<AIIndustryList_CargoProducing *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryList_CargoProducing *)instance; } + template <> AIIndustryList_CargoProducing &GetParam(ForceType<AIIndustryList_CargoProducing &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryList_CargoProducing *)instance; } + template <> const AIIndustryList_CargoProducing *GetParam(ForceType<const AIIndustryList_CargoProducing *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryList_CargoProducing *)instance; } + template <> const AIIndustryList_CargoProducing &GetParam(ForceType<const AIIndustryList_CargoProducing &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryList_CargoProducing *)instance; } + template <> int Return<AIIndustryList_CargoProducing *>(HSQUIRRELVM vm, AIIndustryList_CargoProducing *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIIndustryList_CargoProducing", res, NULL, DefSQDestructorCallback<AIIndustryList_CargoProducing>); return 1; } +}; // namespace SQConvert + +void SQAIIndustryList_CargoProducing_Register(Squirrel *engine) { + DefSQClass <AIIndustryList_CargoProducing> SQAIIndustryList_CargoProducing("AIIndustryList_CargoProducing"); + SQAIIndustryList_CargoProducing.PreRegister(engine, "AIAbstractList"); + SQAIIndustryList_CargoProducing.AddConstructor<void (AIIndustryList_CargoProducing::*)(CargoID cargo_id), 2>(engine, "xi"); + + SQAIIndustryList_CargoProducing.DefSQStaticMethod(engine, &AIIndustryList_CargoProducing::GetClassName, "GetClassName", 1, "x"); + + SQAIIndustryList_CargoProducing.PostRegister(engine); +} diff --git a/src/ai/api/ai_industrytype.cpp b/src/ai/api/ai_industrytype.cpp new file mode 100644 index 000000000..34d69f5f8 --- /dev/null +++ b/src/ai/api/ai_industrytype.cpp @@ -0,0 +1,115 @@ +/* $Id$ */ + +/** @file ai_industrytype.cpp Implementation of AIIndustryType. */ + +#include "ai_industrytype.hpp" +#include "ai_map.hpp" +#include "../../openttd.h" +#include "../../command_type.h" +#include "../../settings_type.h" +#include "../../strings_func.h" +#include "../../tile_type.h" +#include "../../industry.h" + +/* static */ bool AIIndustryType::IsValidIndustryType(IndustryType industry_type) +{ + if (industry_type >= NUM_INDUSTRYTYPES) return false; + + return ::GetIndustrySpec(industry_type)->enabled; +} + +/* static */ bool AIIndustryType::IsRawIndustry(IndustryType industry_type) +{ + if (!IsValidIndustryType(industry_type)) return false; + + return ::GetIndustrySpec(industry_type)->IsRawIndustry(); +} + +/* static */ bool AIIndustryType::ProductionCanIncrease(IndustryType industry_type) +{ + if (!IsValidIndustryType(industry_type)) return false; + + if (_settings_game.game_creation.landscape != LT_TEMPERATE) return true; + return (::GetIndustrySpec(industry_type)->behaviour & INDUSTRYBEH_DONT_INCR_PROD) == 0; +} + +/* static */ Money AIIndustryType::GetConstructionCost(IndustryType industry_type) +{ + if (!IsValidIndustryType(industry_type)) return false; + + return ::GetIndustrySpec(industry_type)->GetConstructionCost(); +} + +/* static */ const char *AIIndustryType::GetName(IndustryType industry_type) +{ + if (!IsValidIndustryType(industry_type)) return NULL; + static const int len = 64; + char *industrytype_name = MallocT<char>(len); + + ::GetString(industrytype_name, ::GetIndustrySpec(industry_type)->name, &industrytype_name[len - 1]); + + return industrytype_name; +} + +/* static */ AIList *AIIndustryType::GetProducedCargo(IndustryType industry_type) +{ + if (!IsValidIndustryType(industry_type)) return NULL; + + const IndustrySpec *ins = ::GetIndustrySpec(industry_type); + + AIList *list = new AIList(); + for (int i = 0; i < 2; i++) { + if (ins->produced_cargo[i] != CT_INVALID) list->AddItem(ins->produced_cargo[i], 0); + } + + return list; +} + +/* static */ AIList *AIIndustryType::GetAcceptedCargo(IndustryType industry_type) +{ + if (!IsValidIndustryType(industry_type)) return NULL; + + const IndustrySpec *ins = ::GetIndustrySpec(industry_type); + + AIList *list = new AIList(); + for (int i = 0; i < 3; i++) { + if (ins->accepts_cargo[i] != CT_INVALID) list->AddItem(ins->accepts_cargo[i], 0); + } + + return list; +} + +/* static */ bool AIIndustryType::CanBuildIndustry(IndustryType industry_type) +{ + if (!IsValidIndustryType(industry_type)) return false; + if (!::GetIndustrySpec(industry_type)->IsRawIndustry()) return true; + + /* raw_industry_construction == 1 means "Build as other industries" */ + return _settings_game.construction.raw_industry_construction == 1; +} + +/* static */ bool AIIndustryType::CanProspectIndustry(IndustryType industry_type) +{ + if (!IsValidIndustryType(industry_type)) return false; + if (!::GetIndustrySpec(industry_type)->IsRawIndustry()) return false; + + /* raw_industry_construction == 2 means "prospect" */ + return _settings_game.construction.raw_industry_construction == 2; +} + +/* static */ bool AIIndustryType::BuildIndustry(IndustryType industry_type, TileIndex tile) +{ + EnforcePrecondition(false, CanBuildIndustry(industry_type)); + EnforcePrecondition(false, AIMap::IsValidTile(tile)); + + uint32 seed = ::InteractiveRandom(); + return AIObject::DoCommand(tile, (::InteractiveRandomRange(::GetIndustrySpec(industry_type)->num_table) << 16) | industry_type, seed, CMD_BUILD_INDUSTRY); +} + +/* static */ bool AIIndustryType::ProspectIndustry(IndustryType industry_type) +{ + EnforcePrecondition(false, CanProspectIndustry(industry_type)); + + uint32 seed = ::InteractiveRandom(); + return AIObject::DoCommand(0, industry_type, seed, CMD_BUILD_INDUSTRY); +} diff --git a/src/ai/api/ai_industrytype.hpp b/src/ai/api/ai_industrytype.hpp new file mode 100644 index 000000000..88ba6d730 --- /dev/null +++ b/src/ai/api/ai_industrytype.hpp @@ -0,0 +1,114 @@ +/* $Id$ */ + +/** @file ai_industrytype.hpp Everything to query and build industries. */ + +#ifndef AI_INDUSTRYTYPE_HPP +#define AI_INDUSTRYTYPE_HPP + +#include "ai_object.hpp" +#include "ai_error.hpp" +#include "ai_list.hpp" + +/** + * Class that handles all industry-type related functions. + */ +class AIIndustryType : public AIObject { +public: + static const char *GetClassName() { return "AIIndustryType"; } + + /** + * Checks whether the given industry-type is valid. + * @param industry_type The type check. + * @return True if and only if the industry-type is valid. + */ + static bool IsValidIndustryType(IndustryType industry_type); + + /** + * Get the name of an industry-type. + * @param industry_type The type to get the name for. + * @pre IsValidIndustryType(industry_type). + * @return The name of an industry. + */ + static const char *GetName(IndustryType industry_type); + + /** + * Get a list of CargoID possible produced by this industry-type. + * @param industry_type The type to get the CargoIDs for. + * @pre IsValidIndustryType(industry_type). + * @return The CargoIDs of all cargotypes this industry could produce. + */ + static AIList *GetProducedCargo(IndustryType industry_type); + + /** + * Get a list of CargoID accepted by this industry-type. + * @param industry_type The type to get the CargoIDs for. + * @pre IsValidIndustryType(industry_type). + * @return The CargoIDs of all cargotypes this industry accepts. + */ + static AIList *GetAcceptedCargo(IndustryType industry_type); + + /** + * Is this industry type a raw industry? + * @param industry_type The type of the industry. + * @pre IsValidIndustryType(industry_type). + * @return True if it should be handled as a raw industry. + */ + static bool IsRawIndustry(IndustryType industry_type); + + /** + * Can the production of this industry increase? + * @param industry_type The type of the industry. + * @pre IsValidIndustryType(industry_type). + * @return True if the production of this industry can increase. + */ + static bool ProductionCanIncrease(IndustryType industry_type); + + /** + * Get the cost for building this industry-type. + * @param industry_type The type of the industry. + * @pre IsValidIndustryType(industry_type). + * @return The cost for building this industry-type. + */ + static Money GetConstructionCost(IndustryType industry_type); + + /** + * Can you build this type of industry? + * @param industry_type The type of the industry. + * @pre IsValidIndustryType(industry_type). + * @return True if you can prospect this type of industry. + * @note Returns false if you can only prospect this type of industry. + */ + static bool CanBuildIndustry(IndustryType industry_type); + + /** + * Can you prospect this type of industry? + * @param industry_type The type of the industry. + * @pre IsValidIndustryType(industry_type). + * @return True if you can prospect this type of industry. + * @note If the patch setting "Manual primary industry construction method" is set + * to either "None" or "as other industries" this function always returns false. + */ + static bool CanProspectIndustry(IndustryType industry_type); + + /** + * Build an industry of the specified type. + * @param industry_type The type of the industry to build. + * @param tile The tile to build the industry on. + * @pre CanBuildIndustry(industry_type). + * @return True if the industry was succesfully build. + */ + static bool BuildIndustry(IndustryType industry_type, TileIndex tile); + + /** + * Prospect an industry of this type. Prospecting an industries let the game try to create + * an industry on a random place on the map. + * @param industry_type The type of the industry. + * @pre CanProspectIndustry(industry_type). + * @return True if no error occured while trying to prospect. + * @note Even if true is returned there is no guarantee a new industry is build. + * @note If true is returned the money is paid, whether a new industry was build or not. + */ + static bool ProspectIndustry(IndustryType industry_type); +}; + +#endif /* AI_INDUSTRYTYPE_HPP */ diff --git a/src/ai/api/ai_industrytype.hpp.sq b/src/ai/api/ai_industrytype.hpp.sq new file mode 100644 index 000000000..ed5353776 --- /dev/null +++ b/src/ai/api/ai_industrytype.hpp.sq @@ -0,0 +1,34 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_industrytype.hpp" + +namespace SQConvert { + /* Allow AIIndustryType to be used as Squirrel parameter */ + template <> AIIndustryType *GetParam(ForceType<AIIndustryType *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryType *)instance; } + template <> AIIndustryType &GetParam(ForceType<AIIndustryType &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryType *)instance; } + template <> const AIIndustryType *GetParam(ForceType<const AIIndustryType *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryType *)instance; } + template <> const AIIndustryType &GetParam(ForceType<const AIIndustryType &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryType *)instance; } + template <> int Return<AIIndustryType *>(HSQUIRRELVM vm, AIIndustryType *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIIndustryType", res, NULL, DefSQDestructorCallback<AIIndustryType>); return 1; } +}; // namespace SQConvert + +void SQAIIndustryType_Register(Squirrel *engine) { + DefSQClass <AIIndustryType> SQAIIndustryType("AIIndustryType"); + SQAIIndustryType.PreRegister(engine); + SQAIIndustryType.AddConstructor<void (AIIndustryType::*)(), 1>(engine, "x"); + + SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::GetClassName, "GetClassName", 1, "x"); + SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::IsValidIndustryType, "IsValidIndustryType", 2, "xi"); + SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::GetName, "GetName", 2, "xi"); + SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::GetProducedCargo, "GetProducedCargo", 2, "xi"); + SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::GetAcceptedCargo, "GetAcceptedCargo", 2, "xi"); + SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::IsRawIndustry, "IsRawIndustry", 2, "xi"); + SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::ProductionCanIncrease, "ProductionCanIncrease", 2, "xi"); + SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::GetConstructionCost, "GetConstructionCost", 2, "xi"); + SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::CanBuildIndustry, "CanBuildIndustry", 2, "xi"); + SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::CanProspectIndustry, "CanProspectIndustry", 2, "xi"); + SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::BuildIndustry, "BuildIndustry", 3, "xii"); + SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::ProspectIndustry, "ProspectIndustry", 2, "xi"); + + SQAIIndustryType.PostRegister(engine); +} diff --git a/src/ai/api/ai_industrytypelist.cpp b/src/ai/api/ai_industrytypelist.cpp new file mode 100644 index 000000000..b5ef34b50 --- /dev/null +++ b/src/ai/api/ai_industrytypelist.cpp @@ -0,0 +1,15 @@ +/* $Id$ */ + +/** @file ai_industrytypelist.cpp Implementation of AIIndustryTypeList. */ + +#include "ai_industrytypelist.hpp" +#include "../../openttd.h" +#include "../../tile_type.h" +#include "../../industry.h" + +AIIndustryTypeList::AIIndustryTypeList() +{ + for (int i = 0; i < NUM_INDUSTRYTYPES; i++) { + if (AIIndustryType::IsValidIndustryType(i)) this->AddItem(i); + } +} diff --git a/src/ai/api/ai_industrytypelist.hpp b/src/ai/api/ai_industrytypelist.hpp new file mode 100644 index 000000000..55c582c9d --- /dev/null +++ b/src/ai/api/ai_industrytypelist.hpp @@ -0,0 +1,22 @@ +/* $Id$ */ + +/** @file ai_industrytypelist.hpp List all available industry types. */ + +#ifndef AI_INDUSTRYTYPELIST_HPP +#define AI_INDUSTRYTYPELIST_HPP + +#include "ai_abstractlist.hpp" +#include "ai_industrytype.hpp" + +/** + * Creates a list of valid industry types. + * @ingroup AIList + */ +class AIIndustryTypeList : public AIAbstractList { +public: + static const char *GetClassName() { return "AIIndustryTypeList"; } + AIIndustryTypeList(); +}; + + +#endif /* AI_INDUSTRYTYPELIST_HPP */ diff --git a/src/ai/api/ai_industrytypelist.hpp.sq b/src/ai/api/ai_industrytypelist.hpp.sq new file mode 100644 index 000000000..901ee4f68 --- /dev/null +++ b/src/ai/api/ai_industrytypelist.hpp.sq @@ -0,0 +1,23 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_industrytypelist.hpp" + +namespace SQConvert { + /* Allow AIIndustryTypeList to be used as Squirrel parameter */ + template <> AIIndustryTypeList *GetParam(ForceType<AIIndustryTypeList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryTypeList *)instance; } + template <> AIIndustryTypeList &GetParam(ForceType<AIIndustryTypeList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryTypeList *)instance; } + template <> const AIIndustryTypeList *GetParam(ForceType<const AIIndustryTypeList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryTypeList *)instance; } + template <> const AIIndustryTypeList &GetParam(ForceType<const AIIndustryTypeList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryTypeList *)instance; } + template <> int Return<AIIndustryTypeList *>(HSQUIRRELVM vm, AIIndustryTypeList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIIndustryTypeList", res, NULL, DefSQDestructorCallback<AIIndustryTypeList>); return 1; } +}; // namespace SQConvert + +void SQAIIndustryTypeList_Register(Squirrel *engine) { + DefSQClass <AIIndustryTypeList> SQAIIndustryTypeList("AIIndustryTypeList"); + SQAIIndustryTypeList.PreRegister(engine, "AIAbstractList"); + SQAIIndustryTypeList.AddConstructor<void (AIIndustryTypeList::*)(), 1>(engine, "x"); + + SQAIIndustryTypeList.DefSQStaticMethod(engine, &AIIndustryTypeList::GetClassName, "GetClassName", 1, "x"); + + SQAIIndustryTypeList.PostRegister(engine); +} diff --git a/src/ai/api/ai_list.cpp b/src/ai/api/ai_list.cpp new file mode 100644 index 000000000..57690d341 --- /dev/null +++ b/src/ai/api/ai_list.cpp @@ -0,0 +1,45 @@ +/* $Id$ */ + +/** @file ai_list.cpp Implementation of AIList. */ + +#include <squirrel.h> +#include "ai_list.hpp" + +void AIList::AddItem(int32 item, int32 value) +{ + AIAbstractList::AddItem(item); + this->SetValue(item, value); +} + +void AIList::ChangeItem(int32 item, int32 value) +{ + this->SetValue(item, value); +} + +void AIList::RemoveItem(int32 item) +{ + AIAbstractList::RemoveItem(item); +} + +SQInteger AIList::_set(HSQUIRRELVM vm) { + if (sq_gettype(vm, 2) != OT_INTEGER) return SQ_ERROR; + if (sq_gettype(vm, 3) != OT_INTEGER || sq_gettype(vm, 3) == OT_NULL) { + return sq_throwerror(vm, _SC("you can only assign integers to this list")); + } + + SQInteger idx, val; + sq_getinteger(vm, 2, &idx); + if (sq_gettype(vm, 3) == OT_NULL) { + this->RemoveItem(idx); + return 0; + } + + sq_getinteger(vm, 3, &val); + if (!this->HasItem(idx)) { + this->AddItem(idx, val); + return 0; + } + + this->ChangeItem(idx, val); + return 0; +} diff --git a/src/ai/api/ai_list.hpp b/src/ai/api/ai_list.hpp new file mode 100644 index 000000000..9ce99a8e0 --- /dev/null +++ b/src/ai/api/ai_list.hpp @@ -0,0 +1,47 @@ +/* $Id$ */ + +/** @file ai_list.hpp List custom entries. */ + +#ifndef AI_LIST_HPP +#define AI_LIST_HPP + +#include "ai_abstractlist.hpp" + +/** + * Creates an empty list, in which you can add integers. + * @ingroup AIList + */ +class AIList : public AIAbstractList { +public: + static const char *GetClassName() { return "AIList"; } + +public: + /** + * Add an item to the list. + * @param item the item to add. + * @param value the value to assign. + */ + void AddItem(int32 item, int32 value); + + /** + * Change the value of an item in the list. + * @param item the item to change + * @param value the value to assign. + */ + void ChangeItem(int32 item, int32 value); + + /** + * Remove the item from the list. + * @param item the item to remove. + */ + void RemoveItem(int32 item); + +#ifndef DOXYGEN_SKIP + /** + * Used for [] set from Squirrel. + */ + SQInteger _set(HSQUIRRELVM vm); +#endif /* DOXYGEN_SKIP */ +}; + +#endif /* AI_LIST_HPP */ diff --git a/src/ai/api/ai_list.hpp.sq b/src/ai/api/ai_list.hpp.sq new file mode 100644 index 000000000..dfa270bbf --- /dev/null +++ b/src/ai/api/ai_list.hpp.sq @@ -0,0 +1,28 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_list.hpp" + +namespace SQConvert { + /* Allow AIList to be used as Squirrel parameter */ + template <> AIList *GetParam(ForceType<AIList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIList *)instance; } + template <> AIList &GetParam(ForceType<AIList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIList *)instance; } + template <> const AIList *GetParam(ForceType<const AIList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIList *)instance; } + template <> const AIList &GetParam(ForceType<const AIList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIList *)instance; } + template <> int Return<AIList *>(HSQUIRRELVM vm, AIList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIList", res, NULL, DefSQDestructorCallback<AIList>); return 1; } +}; // namespace SQConvert + +void SQAIList_Register(Squirrel *engine) { + DefSQClass <AIList> SQAIList("AIList"); + SQAIList.PreRegister(engine, "AIAbstractList"); + SQAIList.AddConstructor<void (AIList::*)(), 1>(engine, "x"); + + SQAIList.DefSQStaticMethod(engine, &AIList::GetClassName, "GetClassName", 1, "x"); + + SQAIList.DefSQMethod(engine, &AIList::AddItem, "AddItem", 3, "xii"); + SQAIList.DefSQMethod(engine, &AIList::ChangeItem, "ChangeItem", 3, "xii"); + SQAIList.DefSQMethod(engine, &AIList::RemoveItem, "RemoveItem", 2, "xi"); + SQAIList.DefSQAdvancedMethod(engine, &AIList::_set, "_set"); + + SQAIList.PostRegister(engine); +} diff --git a/src/ai/api/ai_log.cpp b/src/ai/api/ai_log.cpp new file mode 100644 index 000000000..6dc9cb365 --- /dev/null +++ b/src/ai/api/ai_log.cpp @@ -0,0 +1,84 @@ +/* $Id$ */ + +/** @file ai_log.cpp Implementation of AILog. */ + +#include "ai_log.hpp" +#include "../../core/alloc_func.hpp" +#include "../../company_func.h" +#include "../../debug.h" +#include "../../window_func.h" + +/* static */ void AILog::Info(const char *message) +{ + AILog::Log(LOG_INFO, message); +} + +/* static */ void AILog::Warning(const char *message) +{ + AILog::Log(LOG_WARNING, message); +} + +/* static */ void AILog::Error(const char *message) +{ + AILog::Log(LOG_ERROR, message); +} + +/* static */ void AILog::Log(AILog::AILogType level, const char *message) +{ + if (AIObject::GetLogPointer() == NULL) { + AIObject::GetLogPointer() = new LogData(); + LogData *log = (LogData *)AIObject::GetLogPointer(); + + log->lines = CallocT<char *>(80); + log->type = CallocT<AILog::AILogType>(80); + log->count = 80; + log->pos = log->count; + log->used = 0; + } + LogData *log = (LogData *)AIObject::GetLogPointer(); + + /* Go to the next log-line */ + log->pos = (log->pos + 1) % log->count; + + if (log->used != log->count) log->used++; + + /* Free last message, and write new message */ + free(log->lines[log->pos]); + log->lines[log->pos] = strdup(message); + log->type[log->pos] = level; + + /* Cut string after first \n */ + char *p; + while ((p = strchr(log->lines[log->pos], '\n')) != NULL) { + *p = '\0'; + break; + } + + char logc; + + switch (level) { + case LOG_SQ_ERROR: logc = 'S'; break; + case LOG_ERROR: logc = 'E'; break; + case LOG_SQ_INFO: logc = 'P'; break; + case LOG_WARNING: logc = 'W'; break; + case LOG_INFO: logc = 'I'; break; + default: logc = '?'; break; + } + + /* Also still print to debug window */ + DEBUG(ai, level, "[%d] [%c] %s", (uint)_current_company, logc, log->lines[log->pos]); + InvalidateWindowData(WC_AI_DEBUG, 0, _current_company); +} + +/* static */ void AILog::FreeLogPointer() +{ + LogData *log = (LogData *)AIObject::GetLogPointer(); + + for (int i = 0; i < log->count; i++) { + free(log->lines[i]); + } + + free(log->lines); + free(log->type); + delete log; +} diff --git a/src/ai/api/ai_log.hpp b/src/ai/api/ai_log.hpp new file mode 100644 index 000000000..7fd1cdb64 --- /dev/null +++ b/src/ai/api/ai_log.hpp @@ -0,0 +1,76 @@ +/* $Id$ */ + +/** @file ai_log.hpp Everything to handle and issue log messages. */ + +#ifndef AI_LOG_HPP +#define AI_LOG_HPP + +#include "ai_object.hpp" + +/** + * Class that handles all log related functions. + */ +class AILog : public AIObject { + /* AIController needs access to Enum and Log, in order to keep the flow from + * OpenTTD core to NoAI API clear and simple. */ + friend class AIController; + +public: + static const char *GetClassName() { return "AILog"; } + + /** + * Log levels; The value is also feed to DEBUG() lvl. + * This has no use for you, as AI writer. + */ + enum AILogType { + LOG_SQ_ERROR = 0, //!< Squirrel printed an error. + LOG_ERROR = 1, //!< User printed an error. + LOG_SQ_INFO = 2, //!< Squirrel printed some info. + LOG_WARNING = 3, //!< User printed some warning. + LOG_INFO = 4, //!< User printed some info. + }; + + /** + * Internal representation of the log-data inside the AI. + * This has no use for you, as AI writer. + */ + struct LogData { + char **lines; //!< The log-lines. + AILog::AILogType *type; //!< Per line, which type of log it was. + int count; //!< Total amount of log-lines possible. + int pos; //!< Current position in lines. + int used; //!< Total amount of used log-lines. + }; + + /** + * Print an Info message to the logs. + * @param message The message to log. + */ + static void Info(const char *message); + + /** + * Print a Warning message to the logs. + * @param message The message to log. + */ + static void Warning(const char *message); + + /** + * Print an Error message to the logs. + * @param message The message to log. + */ + static void Error(const char *message); + + /** + * Free the log pointer. + * @note DO NOT CALL YOURSELF; leave it to the internal AI programming. + */ + static void FreeLogPointer(); + +private: + /** + * Internal command to log the message in a common way. + */ + static void Log(AILog::AILogType level, const char *message); +}; + +#endif /* AI_LOG_HPP */ diff --git a/src/ai/api/ai_log.hpp.sq b/src/ai/api/ai_log.hpp.sq new file mode 100644 index 000000000..7b84dc8f1 --- /dev/null +++ b/src/ai/api/ai_log.hpp.sq @@ -0,0 +1,37 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_log.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AILog::AILogType GetParam(ForceType<AILog::AILogType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AILog::AILogType)tmp; } + template <> int Return<AILog::AILogType>(HSQUIRRELVM vm, AILog::AILogType res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AILog to be used as Squirrel parameter */ + template <> AILog *GetParam(ForceType<AILog *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AILog *)instance; } + template <> AILog &GetParam(ForceType<AILog &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AILog *)instance; } + template <> const AILog *GetParam(ForceType<const AILog *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AILog *)instance; } + template <> const AILog &GetParam(ForceType<const AILog &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AILog *)instance; } + template <> int Return<AILog *>(HSQUIRRELVM vm, AILog *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AILog", res, NULL, DefSQDestructorCallback<AILog>); return 1; } +}; // namespace SQConvert + +void SQAILog_Register(Squirrel *engine) { + DefSQClass <AILog> SQAILog("AILog"); + SQAILog.PreRegister(engine); + SQAILog.AddConstructor<void (AILog::*)(), 1>(engine, "x"); + + SQAILog.DefSQConst(engine, AILog::LOG_SQ_ERROR, "LOG_SQ_ERROR"); + SQAILog.DefSQConst(engine, AILog::LOG_ERROR, "LOG_ERROR"); + SQAILog.DefSQConst(engine, AILog::LOG_SQ_INFO, "LOG_SQ_INFO"); + SQAILog.DefSQConst(engine, AILog::LOG_WARNING, "LOG_WARNING"); + SQAILog.DefSQConst(engine, AILog::LOG_INFO, "LOG_INFO"); + + SQAILog.DefSQStaticMethod(engine, &AILog::GetClassName, "GetClassName", 1, "x"); + SQAILog.DefSQStaticMethod(engine, &AILog::Info, "Info", 2, "xs"); + SQAILog.DefSQStaticMethod(engine, &AILog::Warning, "Warning", 2, "xs"); + SQAILog.DefSQStaticMethod(engine, &AILog::Error, "Error", 2, "xs"); + SQAILog.DefSQStaticMethod(engine, &AILog::FreeLogPointer, "FreeLogPointer", 1, "x"); + + SQAILog.PostRegister(engine); +} diff --git a/src/ai/api/ai_map.cpp b/src/ai/api/ai_map.cpp new file mode 100644 index 000000000..2c8547f95 --- /dev/null +++ b/src/ai/api/ai_map.cpp @@ -0,0 +1,62 @@ +/* $Id$ */ + +/** @file ai_map.cpp Implementation of AIMap. */ + +#include "ai_map.hpp" +#include "../../map_func.h" +#include "../../tile_map.h" + +/* static */ bool AIMap::IsValidTile(TileIndex t) +{ + return ::IsValidTile(t); +} + +/* static */ TileIndex AIMap::GetMapSize() +{ + return ::MapSize(); +} + +/* static */ uint32 AIMap::GetMapSizeX() +{ + return ::MapSizeX(); +} + +/* static */ uint32 AIMap::GetMapSizeY() +{ + return ::MapSizeY(); +} + +/* static */ uint32 AIMap::GetTileX(TileIndex t) +{ + return ::TileX(t); +} + +/* static */ uint32 AIMap::GetTileY(TileIndex t) +{ + return ::TileY(t); +} + +/* static */ TileIndex AIMap::GetTileIndex(uint32 x, uint32 y) +{ + return ::TileXY(x, y); +} + +/* static */ uint32 AIMap::DistanceManhattan(TileIndex t1, TileIndex t2) +{ + return ::DistanceManhattan(t1, t2); +} + +/* static */ uint32 AIMap::DistanceMax(TileIndex t1, TileIndex t2) +{ + return ::DistanceMax(t1, t2); +} + +/* static */ uint32 AIMap::DistanceSquare(TileIndex t1, TileIndex t2) +{ + return ::DistanceSquare(t1, t2); +} + +/* static */ uint32 AIMap::DistanceFromEdge(TileIndex t) +{ + return ::DistanceFromEdge(t); +} diff --git a/src/ai/api/ai_map.hpp b/src/ai/api/ai_map.hpp new file mode 100644 index 000000000..f14c360eb --- /dev/null +++ b/src/ai/api/ai_map.hpp @@ -0,0 +1,117 @@ +/* $Id$ */ + +/** @file ai_map.hpp Everything to query and manipulate map metadata. */ + +#ifndef AI_MAP_HPP +#define AI_MAP_HPP + +#include "ai_object.hpp" + +/** + * Class that handles all map related functions. + */ +class AIMap : public AIObject { +public: + static const char *GetClassName() { return "AIMap"; } + + /** + * Checks whether the given tile is valid. + * @param tile The tile to check. + * @return True is the tile it within the boundaries of the map. + */ + static bool IsValidTile(TileIndex tile); + + /** + * Gets the number of tiles in the map. + * @return The size of the map in tiles. + * @post Return value is always positive. + */ + static TileIndex GetMapSize(); + + /** + * Gets the amount of tiles along the SW and NE border. + * @return The length along the SW and NE borders. + * @post Return value is always positive. + */ + static uint32 GetMapSizeX(); + + /** + * Gets the amount of tiles along the SE and NW border. + * @return The length along the SE and NW borders. + * @post Return value is always positive. + */ + static uint32 GetMapSizeY(); + + /** + * Gets the place along the SW/NE border (X-value). + * @param tile The tile to get the X-value of. + * @pre IsValidTile(tile). + * @return The X-value. + * @post Return value is always lower than GetMapSizeX(). + */ + static uint32 GetTileX(TileIndex tile); + + /** + * Gets the place along the SE/NW border (Y-value). + * @param tile The tile to get the Y-value of. + * @pre IsValidTile(tile). + * @return The Y-value. + * @post Return value is always lower than GetMapSizeY(). + */ + static uint32 GetTileY(TileIndex tile); + + /** + * Gets the TileIndex given a x,y-coordinate. + * @param x The X coordinate. + * @param y The Y coordinate. + * @pre x < GetMapSizeX(). + * @pre y < GetMapSizeY(). + * @return The TileIndex for the given (x,y) coordinate. + */ + static TileIndex GetTileIndex(uint32 x, uint32 y); + + /** + * Calculates the Manhattan distance; the difference of + * the X and Y added together. + * @param tile_from The start tile. + * @param tile_to The destination tile. + * @pre IsValidTile(tile_from). + * @pre IsValidTile(tile_to). + * @return The Manhattan distance between the tiles. + */ + static uint32 DistanceManhattan(TileIndex tile_from, TileIndex tile_to); + + /** + * Calculates the distance between two tiles via 1D calculation. + * This means the distance between X or the distance between Y, depending + * on which one is bigger. + * @param tile_from The start tile. + * @param tile_to The destination tile. + * @pre IsValidTile(tile_from). + * @pre IsValidTile(tile_to). + * @return The maximum distance between the tiles. + */ + static uint32 DistanceMax(TileIndex tile_from, TileIndex tile_to); + + /** + * The squared distance between the two tiles. + * This is the distance is the length of the shortest straight line + * between both points. + * @param tile_from The start tile. + * @param tile_to The destination tile. + * @pre IsValidTile(tile_from). + * @pre IsValidTile(tile_to). + * @return The squared distance between the tiles. + */ + static uint32 DistanceSquare(TileIndex tile_from, TileIndex tile_to); + + /** + * Calculates the shortest distance to the edge. + * @param tile From where the distance has to be calculated. + * @pre IsValidTile(tile). + * @return The distances to the closest edge. + */ + static uint32 DistanceFromEdge(TileIndex tile); +}; + +#endif /* AI_MAP_HPP */ diff --git a/src/ai/api/ai_map.hpp.sq b/src/ai/api/ai_map.hpp.sq new file mode 100644 index 000000000..f65368c8f --- /dev/null +++ b/src/ai/api/ai_map.hpp.sq @@ -0,0 +1,34 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_map.hpp" + +namespace SQConvert { + /* Allow AIMap to be used as Squirrel parameter */ + template <> AIMap *GetParam(ForceType<AIMap *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIMap *)instance; } + template <> AIMap &GetParam(ForceType<AIMap &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIMap *)instance; } + template <> const AIMap *GetParam(ForceType<const AIMap *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIMap *)instance; } + template <> const AIMap &GetParam(ForceType<const AIMap &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIMap *)instance; } + template <> int Return<AIMap *>(HSQUIRRELVM vm, AIMap *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIMap", res, NULL, DefSQDestructorCallback<AIMap>); return 1; } +}; // namespace SQConvert + +void SQAIMap_Register(Squirrel *engine) { + DefSQClass <AIMap> SQAIMap("AIMap"); + SQAIMap.PreRegister(engine); + SQAIMap.AddConstructor<void (AIMap::*)(), 1>(engine, "x"); + + SQAIMap.DefSQStaticMethod(engine, &AIMap::GetClassName, "GetClassName", 1, "x"); + SQAIMap.DefSQStaticMethod(engine, &AIMap::IsValidTile, "IsValidTile", 2, "xi"); + SQAIMap.DefSQStaticMethod(engine, &AIMap::GetMapSize, "GetMapSize", 1, "x"); + SQAIMap.DefSQStaticMethod(engine, &AIMap::GetMapSizeX, "GetMapSizeX", 1, "x"); + SQAIMap.DefSQStaticMethod(engine, &AIMap::GetMapSizeY, "GetMapSizeY", 1, "x"); + SQAIMap.DefSQStaticMethod(engine, &AIMap::GetTileX, "GetTileX", 2, "xi"); + SQAIMap.DefSQStaticMethod(engine, &AIMap::GetTileY, "GetTileY", 2, "xi"); + SQAIMap.DefSQStaticMethod(engine, &AIMap::GetTileIndex, "GetTileIndex", 3, "xii"); + SQAIMap.DefSQStaticMethod(engine, &AIMap::DistanceManhattan, "DistanceManhattan", 3, "xii"); + SQAIMap.DefSQStaticMethod(engine, &AIMap::DistanceMax, "DistanceMax", 3, "xii"); + SQAIMap.DefSQStaticMethod(engine, &AIMap::DistanceSquare, "DistanceSquare", 3, "xii"); + SQAIMap.DefSQStaticMethod(engine, &AIMap::DistanceFromEdge, "DistanceFromEdge", 2, "xi"); + + SQAIMap.PostRegister(engine); +} diff --git a/src/ai/api/ai_marine.cpp b/src/ai/api/ai_marine.cpp new file mode 100644 index 000000000..274ab5f6b --- /dev/null +++ b/src/ai/api/ai_marine.cpp @@ -0,0 +1,143 @@ +/* $Id$ */ + +/** @file ai_marine.cpp Implementation of AIMarine. */ + +#include "ai_marine.hpp" +#include "../../openttd.h" +#include "../../command_type.h" +#include "../../variables.h" +#include "../../station_map.h" +#include "../../water_map.h" +#include "../../tile_cmd.h" + + +/* static */ bool AIMarine::IsWaterDepotTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsTileType(tile, MP_WATER) && ::GetWaterTileType(tile) == WATER_TILE_DEPOT; +} + +/* static */ bool AIMarine::IsDockTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsTileType(tile, MP_STATION) && ::IsDock(tile); +} + +/* static */ bool AIMarine::IsBuoyTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsTileType(tile, MP_STATION) && ::IsBuoy(tile); +} + +/* static */ bool AIMarine::IsLockTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsTileType(tile, MP_WATER) && ::GetWaterTileType(tile) == WATER_TILE_LOCK; +} + +/* static */ bool AIMarine::IsCanalTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsTileType(tile, MP_WATER) && ::IsCanal(tile); +} + +/* static */ bool AIMarine::AreWaterTilesConnected(TileIndex t1, TileIndex t2) +{ + if (!::IsValidTile(t1)) return false; + if (!::IsValidTile(t2)) return false; + + /* Tiles not neighbouring */ + if (::DistanceManhattan(t1, t2) != 1) return false; + if (t1 > t2) Swap(t1, t2); + + uint32 gtts1 = ::GetTileTrackStatus(t1, TRANSPORT_WATER, 0); + uint32 gtts2 = ::GetTileTrackStatus(t2, TRANSPORT_WATER, 0); + + /* Ship can't travel on one of the tiles. */ + if (gtts1 == 0 || gtts2 == 0) return false; + + DiagDirection to_other_tile = (TileX(t1) == TileX(t2)) ? DIAGDIR_SE : DIAGDIR_SW; + + /* Check whether we can 'leave' the tile at the border and 'enter' the other tile at the border */ + return (gtts1 & DiagdirReachesTrackdirs(ReverseDiagDir(to_other_tile))) != 0 && (gtts2 & DiagdirReachesTrackdirs(to_other_tile)) != 0; +} + +/* static */ bool AIMarine::BuildWaterDepot(TileIndex tile, bool vertical) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + + return AIObject::DoCommand(tile, vertical, 0, CMD_BUILD_SHIP_DEPOT); +} + +/* static */ bool AIMarine::BuildDock(TileIndex tile, bool join_adjacent) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + + return AIObject::DoCommand(tile, join_adjacent ? 0 : 1, INVALID_STATION << 16, CMD_BUILD_DOCK); +} + +/* static */ bool AIMarine::BuildBuoy(TileIndex tile) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + + return AIObject::DoCommand(tile, 0, 0, CMD_BUILD_BUOY); +} + +/* static */ bool AIMarine::BuildLock(TileIndex tile) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + + return AIObject::DoCommand(tile, 0, 0, CMD_BUILD_LOCK); +} + +/* static */ bool AIMarine::BuildCanal(TileIndex tile) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + + return AIObject::DoCommand(tile, tile, 0, CMD_BUILD_CANAL); +} + +/* static */ bool AIMarine::RemoveWaterDepot(TileIndex tile) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, IsWaterDepotTile(tile)); + + return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR); +} + +/* static */ bool AIMarine::RemoveDock(TileIndex tile) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, IsDockTile(tile)); + + return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR); +} + +/* static */ bool AIMarine::RemoveBuoy(TileIndex tile) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, IsBuoyTile(tile)); + + return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR); +} + +/* static */ bool AIMarine::RemoveLock(TileIndex tile) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, IsLockTile(tile)); + + return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR); +} + +/* static */ bool AIMarine::RemoveCanal(TileIndex tile) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, IsCanalTile(tile)); + + return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR); +} diff --git a/src/ai/api/ai_marine.hpp b/src/ai/api/ai_marine.hpp new file mode 100644 index 000000000..9f871887d --- /dev/null +++ b/src/ai/api/ai_marine.hpp @@ -0,0 +1,187 @@ +/* $Id$ */ + +/** @file ai_marine.hpp Everything to query and build marine. */ + +#ifndef AI_MARINE_HPP +#define AI_MARINE_HPP + +#include "ai_object.hpp" +#include "ai_error.hpp" + +/** + * Class that handles all marine related functions. + */ +class AIMarine : public AIObject { +public: + static const char *GetClassName() { return "AIMarine"; } + + /** + * All marine related error messages. + */ + enum ErrorMessages { + /** Base for marine related errors */ + ERR_MARINE_BASE = AIError::ERR_CAT_MARINE << AIError::ERR_CAT_BIT_SIZE, + + /** Infrastructure must be built on water */ + ERR_MARINE_MUST_BE_BUILT_ON_WATER, // [STR_3801_MUST_BE_BUILT_ON_WATER] + }; + + /** + * Checks whether the given tile is actually a tile with a water depot. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has a water depot. + */ + static bool IsWaterDepotTile(TileIndex tile); + + /** + * Checks whether the given tile is actually a tile with a dock. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has a dock. + */ + static bool IsDockTile(TileIndex tile); + + /** + * Checks whether the given tile is actually a tile with a buoy. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has a buoy. + */ + static bool IsBuoyTile(TileIndex tile); + + /** + * Checks whether the given tile is actually a tile with a lock. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has a lock. + */ + static bool IsLockTile(TileIndex tile); + + /** + * Checks whether the given tile is actually a tile with a canal. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has a canal. + */ + static bool IsCanalTile(TileIndex tile); + + /** + * Checks whether the given tiles are directly connected, i.e. whether + * a ship vehicle can travel from the center of the first tile to the + * center of the second tile. + * @param tile_from The source tile. + * @param tile_to The destination tile. + * @pre AIMap::IsValidTile(tile_from). + * @pre AIMap::IsValidTile(tile_to). + * @pre 'tile_from' and 'tile_to' are directly neighbouring tiles. + * @return True if and only if a ship can go from tile_from to tile_to. + */ + static bool AreWaterTilesConnected(TileIndex tile_from, TileIndex tile_to); + + /** + * Builds a water depot on tile. + * @param tile The tile where the water depot will be build. + * @param vertical If true, depot will be vertical, else horizontal. + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIError::ERR_SITE_UNSUITABLE + * @exception AIMarine::ERR_MARINE_MUST_BE_BUILT_ON_WATER + * @return Whether the water depot has been/can be build or not. + * @note A WaterDepot is 1 tile in width, and 2 tiles in length. + */ + static bool BuildWaterDepot(TileIndex tile, bool vertical); + + /** + * Builds a dock where tile is the tile still on land. + * @param tile The tile still on land of the dock. + * @param join_adjacent When building next to an other station, don't create a new station when this flag is true. + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIError::ERR_SITE_UNSUITABLE + * @exception AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION + * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS + * @return Whether the dock has been/can be build or not. + */ + static bool BuildDock(TileIndex tile, bool join_adjacent); + + /** + * Builds a buoy on tile. + * @param tile The tile where the buoy will be build. + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIError::ERR_SITE_UNSUITABLE + * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS + * @return Whether the buoy has been/can be build or not. + */ + static bool BuildBuoy(TileIndex tile); + + /** + * Builds a lock on tile. + * @param tile The tile where the lock will be build. + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_LAND_SLOPED_WRONG + * @exception AIError::ERR_SITE_UNSUITABLE + * @return Whether the lock has been/can be build or not. + */ + static bool BuildLock(TileIndex tile); + + /** + * Builds a canal on tile. + * @param tile The tile where the canal will be build. + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIError::ERR_LAND_SLOPED_WRONG + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @exception AIError::ERR_ALREADY_BUILT + * @return Whether the canal has been/can be build or not. + */ + static bool BuildCanal(TileIndex tile); + + /** + * Removes a water depot. + * @param tile Any tile of the water depot. + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @return Whether the water depot has been/can be removed or not. + */ + static bool RemoveWaterDepot(TileIndex tile); + + /** + * Removes a dock. + * @param tile Any tile of the dock. + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @return Whether the dock has been/can be removed or not. + */ + static bool RemoveDock(TileIndex tile); + + /** + * Removes a buoy. + * @param tile Any tile of the buoy. + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @return Whether the buoy has been/can be removed or not. + */ + static bool RemoveBuoy(TileIndex tile); + + /** + * Removes a lock. + * @param tile Any tile of the lock. + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @return Whether the lock has been/can be removed or not. + */ + static bool RemoveLock(TileIndex tile); + + /** + * Removes a canal. + * @param tile Any tile of the canal. + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @return Whether the canal has been/can be removed or not. + */ + static bool RemoveCanal(TileIndex tile); +}; + +#endif /* AI_MARINE_HPP */ diff --git a/src/ai/api/ai_marine.hpp.sq b/src/ai/api/ai_marine.hpp.sq new file mode 100644 index 000000000..269c99604 --- /dev/null +++ b/src/ai/api/ai_marine.hpp.sq @@ -0,0 +1,50 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_marine.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AIMarine::ErrorMessages GetParam(ForceType<AIMarine::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIMarine::ErrorMessages)tmp; } + template <> int Return<AIMarine::ErrorMessages>(HSQUIRRELVM vm, AIMarine::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AIMarine to be used as Squirrel parameter */ + template <> AIMarine *GetParam(ForceType<AIMarine *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIMarine *)instance; } + template <> AIMarine &GetParam(ForceType<AIMarine &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIMarine *)instance; } + template <> const AIMarine *GetParam(ForceType<const AIMarine *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIMarine *)instance; } + template <> const AIMarine &GetParam(ForceType<const AIMarine &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIMarine *)instance; } + template <> int Return<AIMarine *>(HSQUIRRELVM vm, AIMarine *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIMarine", res, NULL, DefSQDestructorCallback<AIMarine>); return 1; } +}; // namespace SQConvert + +void SQAIMarine_Register(Squirrel *engine) { + DefSQClass <AIMarine> SQAIMarine("AIMarine"); + SQAIMarine.PreRegister(engine); + SQAIMarine.AddConstructor<void (AIMarine::*)(), 1>(engine, "x"); + + SQAIMarine.DefSQConst(engine, AIMarine::ERR_MARINE_BASE, "ERR_MARINE_BASE"); + SQAIMarine.DefSQConst(engine, AIMarine::ERR_MARINE_MUST_BE_BUILT_ON_WATER, "ERR_MARINE_MUST_BE_BUILT_ON_WATER"); + + AIError::RegisterErrorMap(STR_3801_MUST_BE_BUILT_ON_WATER, AIMarine::ERR_MARINE_MUST_BE_BUILT_ON_WATER); + + AIError::RegisterErrorMapString(AIMarine::ERR_MARINE_MUST_BE_BUILT_ON_WATER, "ERR_MARINE_MUST_BE_BUILT_ON_WATER"); + + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::GetClassName, "GetClassName", 1, "x"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::IsWaterDepotTile, "IsWaterDepotTile", 2, "xi"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::IsDockTile, "IsDockTile", 2, "xi"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::IsBuoyTile, "IsBuoyTile", 2, "xi"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::IsLockTile, "IsLockTile", 2, "xi"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::IsCanalTile, "IsCanalTile", 2, "xi"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::AreWaterTilesConnected, "AreWaterTilesConnected", 3, "xii"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::BuildWaterDepot, "BuildWaterDepot", 3, "xib"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::BuildDock, "BuildDock", 3, "xib"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::BuildBuoy, "BuildBuoy", 2, "xi"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::BuildLock, "BuildLock", 2, "xi"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::BuildCanal, "BuildCanal", 2, "xi"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::RemoveWaterDepot, "RemoveWaterDepot", 2, "xi"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::RemoveDock, "RemoveDock", 2, "xi"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::RemoveBuoy, "RemoveBuoy", 2, "xi"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::RemoveLock, "RemoveLock", 2, "xi"); + SQAIMarine.DefSQStaticMethod(engine, &AIMarine::RemoveCanal, "RemoveCanal", 2, "xi"); + + SQAIMarine.PostRegister(engine); +} diff --git a/src/ai/api/ai_object.cpp b/src/ai/api/ai_object.cpp new file mode 100644 index 000000000..664c24ecc --- /dev/null +++ b/src/ai/api/ai_object.cpp @@ -0,0 +1,264 @@ +/* $Id$ */ + +/** @file ai_object.cpp Implementation of AIObject. */ + +#include "ai_object.hpp" +#include "ai_log.hpp" +#include "ai_error.hpp" +#include "table/strings.h" +#include "../../openttd.h" +#include "../../command_func.h" +#include "../../map_func.h" +#include "../../network/network.h" +#include "../../company_func.h" +#include "../../signs_func.h" +#include "../../tunnelbridge.h" +#include "../../vehicle_func.h" +#include "../../group.h" +#include "../ai.hpp" +#include "ai_controller.hpp" +#include "../ai_storage.hpp" +#include "../ai_instance.hpp" + +#include <vector> + +static AIStorage *GetStorage() +{ + return AIInstance::GetStorage(); +} + +void AIObject::SetDoCommandDelay(uint ticks) +{ + assert(ticks > 0); + GetStorage()->delay = ticks; +} + +uint AIObject::GetDoCommandDelay() +{ + return GetStorage()->delay; +} + +void AIObject::SetDoCommandMode(AIModeProc *proc, AIObject *instance) +{ + GetStorage()->mode = proc; + GetStorage()->mode_instance = instance; +} + +AIModeProc *AIObject::GetDoCommandMode() +{ + return GetStorage()->mode; +} + +AIObject *AIObject::GetDoCommandModeInstance() +{ + return GetStorage()->mode_instance; +} + +void AIObject::SetDoCommandCosts(Money value) +{ + GetStorage()->costs = CommandCost(value); +} + +void AIObject::IncreaseDoCommandCosts(Money value) +{ + GetStorage()->costs.AddCost(value); +} + +Money AIObject::GetDoCommandCosts() +{ + return GetStorage()->costs.GetCost(); +} + +void AIObject::SetLastError(AIErrorType last_error) +{ + GetStorage()->last_error = last_error; +} + +AIErrorType AIObject::GetLastError() +{ + return GetStorage()->last_error; +} + +void AIObject::SetLastCost(Money last_cost) +{ + GetStorage()->last_cost = last_cost; +} + +Money AIObject::GetLastCost() +{ + return GetStorage()->last_cost; +} + +void AIObject::SetRoadType(RoadType road_type) +{ + GetStorage()->road_type = road_type; +} + +RoadType AIObject::GetRoadType() +{ + return GetStorage()->road_type; +} + +void AIObject::SetRailType(RailType rail_type) +{ + GetStorage()->rail_type = rail_type; +} + +RailType AIObject::GetRailType() +{ + return GetStorage()->rail_type; +} + +void AIObject::SetLastCommandRes(bool res) +{ + GetStorage()->last_command_res = res; + /* Also store the results of various global variables */ + SetNewVehicleID(_new_vehicle_id); + SetNewSignID(_new_sign_id); + SetNewTunnelEndtile(_build_tunnel_endtile); + SetNewGroupID(_new_group_id); +} + +bool AIObject::GetLastCommandRes() +{ + return GetStorage()->last_command_res; +} + +void AIObject::SetNewVehicleID(VehicleID vehicle_id) +{ + GetStorage()->new_vehicle_id = vehicle_id; +} + +VehicleID AIObject::GetNewVehicleID() +{ + return GetStorage()->new_vehicle_id; +} + +void AIObject::SetNewSignID(SignID sign_id) +{ + GetStorage()->new_sign_id = sign_id; +} + +SignID AIObject::GetNewSignID() +{ + return GetStorage()->new_sign_id; +} + +void AIObject::SetNewTunnelEndtile(TileIndex tile) +{ + GetStorage()->new_tunnel_endtile = tile; +} + +TileIndex AIObject::GetNewTunnelEndtile() +{ + return GetStorage()->new_tunnel_endtile; +} + +void AIObject::SetNewGroupID(GroupID group_id) +{ + GetStorage()->new_group_id = group_id; +} + +GroupID AIObject::GetNewGroupID() +{ + return GetStorage()->new_group_id; +} + +void AIObject::SetAllowDoCommand(bool allow) +{ + GetStorage()->allow_do_command = allow; +} + +bool AIObject::GetAllowDoCommand() +{ + return GetStorage()->allow_do_command; +} + +void *&AIObject::GetEventPointer() +{ + return GetStorage()->event_data; +} + +void *&AIObject::GetLogPointer() +{ + return GetStorage()->log_data; +} + +void AIObject::SetCallbackVariable(int index, int value) +{ + if ((size_t)index >= GetStorage()->callback_value.size()) GetStorage()->callback_value.resize(index + 1); + GetStorage()->callback_value[index] = value; +} + +int AIObject::GetCallbackVariable(int index) +{ + return GetStorage()->callback_value[index]; +} + +bool AIObject::DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint cmd, const char *text, AISuspendCallbackProc *callback) +{ + if (AIObject::GetAllowDoCommand() == false) { + AILog::Error("You are not allowed to execute any DoCommand (even indirect) in your constructor, Save(), and Load().\n"); + return false; + } + + CompanyID old_company; + CommandCost res; + + /* Set the default callback to return a true/false result of the DoCommand */ + if (callback == NULL) callback = &AIInstance::DoCommandReturn; + + /* Make sure the last error is reset, so we don't give faulty warnings */ + SetLastError(AIError::ERR_NONE); + + /* First, do a test-run to see if we can do this */ + res = ::DoCommand(tile, p1, p2, CommandFlagsToDCFlags(GetCommandFlags(cmd)), cmd, text); + /* The command failed, so return */ + if (::CmdFailed(res)) { + SetLastError(AIError::StringToError(_error_message)); + return false; + } + + /* Check what the callback wants us to do */ + if (GetDoCommandMode() != NULL && !GetDoCommandMode()(tile, p1, p2, cmd, res)) { + IncreaseDoCommandCosts(res.GetCost()); + return true; + } + +#ifdef ENABLE_NETWORK + /* Send the command */ + if (_networking) { + /* NetworkSend_Command needs _local_company to be set correctly, so + * adjust it, and put it back right after the function */ + old_company = _local_company; + _local_company = _current_company; + ::NetworkSend_Command(tile, p1, p2, cmd, CcAI, text); + _local_company = old_company; + SetLastCost(res.GetCost()); + + /* Suspend the AI till the command is really executed */ + throw AI_VMSuspend(-(int)GetDoCommandDelay(), callback); + } else { +#else + { +#endif + /* For SinglePlayer we execute the command immediatly */ + if (!::DoCommandP(tile, p1, p2, cmd, NULL, text)) res = CMD_ERROR; + SetLastCommandRes(!::CmdFailed(res)); + + if (::CmdFailed(res)) { + SetLastError(AIError::StringToError(_error_message)); + return false; + } + SetLastCost(res.GetCost()); + IncreaseDoCommandCosts(res.GetCost()); + + /* Suspend the AI player for 1+ ticks, so it simulates multiplayer. This + * both avoids confusion when a developer launched his AI in a + * multiplayer game, but also gives time for the GUI and human player + * to interact with the game. */ + throw AI_VMSuspend(GetDoCommandDelay(), callback); + } + + NOT_REACHED(); +} diff --git a/src/ai/api/ai_object.hpp b/src/ai/api/ai_object.hpp new file mode 100644 index 000000000..9dcb0f4cc --- /dev/null +++ b/src/ai/api/ai_object.hpp @@ -0,0 +1,202 @@ +/* $Id$ */ + +/** @file ai_object.hpp Main object, on which all objects depend. */ + +#ifndef AI_OBJECT_HPP +#define AI_OBJECT_HPP + +#include "../../stdafx.h" +#include "../../misc/countedptr.hpp" +#include "../../road_type.h" +#include "../../rail_type.h" + +#include "ai_types.hpp" + +/** + * The callback function when an AI suspends. + */ +typedef void (AISuspendCallbackProc)(class AIInstance *instance); + +/** + * The callback function for Mode-classes. + */ +typedef bool (AIModeProc)(TileIndex tile, uint32 p1, uint32 p2, uint cmd, CommandCost costs); + +/** + * Uper-parent object of all API classes. You should never use this class in + * your AI, as it doesn't publish any public functions. It is used + * internally to have a common place to handle general things, like internal + * command processing, and command-validation checks. + */ +class AIObject : public SimpleCountedObject { +friend void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2); +friend class AIInstance; +friend class AIController; +protected: + /** + * Executes a raw DoCommand for the AI. + */ + static bool DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint cmd, const char *text = NULL, AISuspendCallbackProc *callback = NULL); + + /** + * Sets the DoCommand costs counter to a value. + */ + static void SetDoCommandCosts(Money value); + + /** + * Increase the current value of the DoCommand costs counter. + */ + static void IncreaseDoCommandCosts(Money value); + + /** + * Get the current DoCommand costs counter. + */ + static Money GetDoCommandCosts(); + + /** + * Set the DoCommand last error. + */ + static void SetLastError(AIErrorType last_error); + + /** + * Get the DoCommand last error. + */ + static AIErrorType GetLastError(); + + /** + * Set the road type. + */ + static void SetRoadType(RoadType road_type); + + /** + * Get the road type. + */ + static RoadType GetRoadType(); + + /** + * Set the rail type. + */ + static void SetRailType(RailType rail_type); + + /** + * Get the rail type. + */ + static RailType GetRailType(); + + /** + * Set the current mode of your AI to this proc. + */ + static void SetDoCommandMode(AIModeProc *proc, AIObject *instance); + + /** + * Get the current mode your AI is currently under. + */ + static AIModeProc *GetDoCommandMode(); + + /** + * Get the instance of the current mode your AI is currently under. + */ + static AIObject *GetDoCommandModeInstance(); + + /** + * Set the delay of the DoCommand. + */ + static void SetDoCommandDelay(uint ticks); + + /** + * Get the delay of the DoCommand. + */ + static uint GetDoCommandDelay(); + + /** + * Get the latest result of a DoCommand. + */ + static bool GetLastCommandRes(); + + /** + * Get the latest stored new_vehicle_id. + */ + static VehicleID GetNewVehicleID(); + + /** + * Get the latest stored new_sign_id. + */ + static SignID GetNewSignID(); + + /** + * Get the latest stored new_tunnel_endtile. + */ + static TileIndex GetNewTunnelEndtile(); + + /** + * Get the latest stored new_group_id. + */ + static GroupID GetNewGroupID(); + + /** + * Get the latest stored allow_do_command. + * If this is false, you are not allowed to do any DoCommands. + */ + static bool GetAllowDoCommand(); + + /** + * Get the pointer to store event data in. + */ + static void *&GetEventPointer(); + + static void SetLastCost(Money last_cost); + static Money GetLastCost(); + static void SetCallbackVariable(int index, int value); + static int GetCallbackVariable(int index); + +public: + /** + * Store the latest result of a DoCommand per company. + * @note NEVER use this yourself in your AI! + * @param res The result of the last command. + */ + static void SetLastCommandRes(bool res); + + /** + * Store a new_vehicle_id per company. + * @note NEVER use this yourself in your AI! + * @param vehicle_id The new VehicleID. + */ + static void SetNewVehicleID(VehicleID vehicle_id); + + /** + * Store a new_sign_id per company. + * @note NEVER use this yourself in your AI! + * @param sign_id The new SignID. + */ + static void SetNewSignID(SignID sign_id); + + /** + * Store a new_tunnel_endtile per company. + * @note NEVER use this yourself in your AI! + * @param tile The new TileIndex. + */ + static void SetNewTunnelEndtile(TileIndex tile); + + /** + * Store a new_group_id per company. + * @note NEVER use this yourself in your AI! + * @param group_id The new GroupID. + */ + static void SetNewGroupID(GroupID group_id); + + /** + * Store a allow_do_command per company. + * @note NEVER use this yourself in your AI! + * @param allow The new allow. + */ + static void SetAllowDoCommand(bool allow); + + /** + * Get the pointer to store log message in. + * @note NEVER use this yourself in your AI! + */ + static void *&GetLogPointer(); +}; + +#endif /* AI_OBJECT_HPP */ diff --git a/src/ai/api/ai_order.cpp b/src/ai/api/ai_order.cpp new file mode 100644 index 000000000..f48c5e392 --- /dev/null +++ b/src/ai/api/ai_order.cpp @@ -0,0 +1,296 @@ +/* $Id$ */ + +/** @file ai_order.cpp Implementation of AIOrder. */ + +#include "ai_order.hpp" +#include "ai_map.hpp" +#include "ai_vehicle.hpp" +#include "../ai_instance.hpp" +#include "../../openttd.h" +#include "../../debug.h" +#include "../../vehicle_base.h" +#include "../../depot_base.h" +#include "../../landscape.h" +#include "../../rail_map.h" +#include "../../road_map.h" +#include "../../station_map.h" +#include "../../water_map.h" +#include "../../waypoint.h" + +/** + * Gets the order type given a tile + * @param t the tile to get the order from + * @return the order type, or OT_END when there is no order + */ +static OrderType GetOrderTypeByTile(TileIndex t) +{ + if (!::IsValidTile(t)) return OT_END; + + switch (::GetTileType(t)) { + default: break; + case MP_STATION: return OT_GOTO_STATION; break; + case MP_WATER: if (::IsShipDepot(t)) return OT_GOTO_DEPOT; break; + case MP_ROAD: if (::GetRoadTileType(t) == ROAD_TILE_DEPOT) return OT_GOTO_DEPOT; break; + case MP_RAILWAY: + switch (::GetRailTileType(t)) { + case RAIL_TILE_DEPOT: return OT_GOTO_DEPOT; + case RAIL_TILE_WAYPOINT: return OT_GOTO_WAYPOINT; + default: break; + } + break; + } + + return OT_END; +} + +/* static */ bool AIOrder::IsValidVehicleOrder(VehicleID vehicle_id, OrderPosition order_position) +{ + return AIVehicle::IsValidVehicle(vehicle_id) && order_position >= 0 && (order_position < ::GetVehicle(vehicle_id)->GetNumOrders() || order_position == CURRENT_ORDER); +} + +/* static */ AIOrder::OrderPosition AIOrder::ResolveOrderPosition(VehicleID vehicle_id, OrderPosition order_position) +{ + if (!AIVehicle::IsValidVehicle(vehicle_id)) return INVALID_ORDER; + + if (order_position == CURRENT_ORDER) return (AIOrder::OrderPosition)::GetVehicle(vehicle_id)->cur_order_index; + return (order_position >= 0 && order_position < ::GetVehicle(vehicle_id)->GetNumOrders()) ? order_position : INVALID_ORDER; +} + + +/* static */ bool AIOrder::AreOrderFlagsValid(TileIndex destination, AIOrderFlags order_flags) +{ + switch (::GetOrderTypeByTile(destination)) { + case OT_GOTO_STATION: + return ((order_flags & ~(AIOF_NON_STOP_FLAGS | AIOF_UNLOAD_FLAGS | AIOF_LOAD_FLAGS)) == 0) && + /* Test the different mutual exclusive flags. */ + (((order_flags & AIOF_TRANSFER) == 0) || ((order_flags & AIOF_UNLOAD) == 0)) && + (((order_flags & AIOF_TRANSFER) == 0) || ((order_flags & AIOF_NO_UNLOAD) == 0)) && + (((order_flags & AIOF_UNLOAD) == 0) || ((order_flags & AIOF_NO_UNLOAD) == 0)) && + (((order_flags & AIOF_UNLOAD) == 0) || ((order_flags & AIOF_NO_UNLOAD) == 0)) && + (((order_flags & AIOF_NO_UNLOAD) == 0) || ((order_flags & AIOF_NO_LOAD) == 0)) && + (((order_flags & AIOF_FULL_LOAD_ANY) == 0) || ((order_flags & AIOF_NO_LOAD) == 0)); + + case OT_GOTO_DEPOT: return (order_flags & ~(AIOF_NON_STOP_FLAGS | AIOF_SERVICE_IF_NEEDED)) == 0; + case OT_GOTO_WAYPOINT: return (order_flags & ~(AIOF_NON_STOP_FLAGS)) == 0; + default: return false; + } +} + +/* static */ int32 AIOrder::GetOrderCount(VehicleID vehicle_id) +{ + return AIVehicle::IsValidVehicle(vehicle_id) ? ::GetVehicle(vehicle_id)->GetNumOrders() : -1; +} + +/* static */ TileIndex AIOrder::GetOrderDestination(VehicleID vehicle_id, OrderPosition order_position) +{ + if (!IsValidVehicleOrder(vehicle_id, order_position)) return INVALID_TILE; + + const Order *order; + const Vehicle *v = ::GetVehicle(vehicle_id); + if (order_position == CURRENT_ORDER) { + order = &v->current_order; + } else { + order = v->GetFirstOrder(); + for (int i = 0; i < order_position; i++) order = order->next; + } + + switch (order->GetType()) { + case OT_GOTO_DEPOT: + if (v->type != VEH_AIRCRAFT) return ::GetDepot(order->GetDestination())->xy; + /* FALL THROUGH: aircraft's hangars are referenced by StationID, not DepotID */ + + case OT_GOTO_STATION: return ::GetStation(order->GetDestination())->xy; + case OT_GOTO_WAYPOINT: return ::GetWaypoint(order->GetDestination())->xy; + default: return INVALID_TILE; + } +} + +/* static */ AIOrder::AIOrderFlags AIOrder::GetOrderFlags(VehicleID vehicle_id, OrderPosition order_position) +{ + if (!IsValidVehicleOrder(vehicle_id, order_position)) return AIOF_INVALID; + + const Order *order; + if (order_position == CURRENT_ORDER) { + order = &::GetVehicle(vehicle_id)->current_order; + } else { + order = ::GetVehicle(vehicle_id)->GetFirstOrder(); + for (int i = 0; i < order_position; i++) order = order->next; + } + + AIOrderFlags order_flags = AIOF_NONE; + order_flags |= (AIOrderFlags)order->GetNonStopType(); + switch (order->GetType()) { + case OT_GOTO_DEPOT: + if (order->GetDepotOrderType() & ODTFB_SERVICE) order_flags |= AIOF_SERVICE_IF_NEEDED; + break; + + case OT_GOTO_STATION: + order_flags |= (AIOrderFlags)(order->GetLoadType() << 5); + order_flags |= (AIOrderFlags)(order->GetUnloadType() << 2); + break; + + default: break; + } + + return order_flags; +} + +/* static */ bool AIOrder::AppendOrder(VehicleID vehicle_id, TileIndex destination, AIOrderFlags order_flags) +{ + EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id)); + return InsertOrder(vehicle_id, (AIOrder::OrderPosition)::GetVehicle(vehicle_id)->GetNumOrders(), destination, order_flags); +} + +/* static */ bool AIOrder::InsertOrder(VehicleID vehicle_id, OrderPosition order_position, TileIndex destination, AIOrder::AIOrderFlags order_flags) +{ + /* IsValidVehicleOrder is not good enough because it does not allow appending. */ + if (order_position == CURRENT_ORDER) order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position); + + EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id)); + EnforcePrecondition(false, order_position >= 0 && order_position <= ::GetVehicle(vehicle_id)->GetNumOrders()); + EnforcePrecondition(false, AreOrderFlagsValid(destination, order_flags)); + + Order order; + switch (::GetOrderTypeByTile(destination)) { + case OT_GOTO_DEPOT: + order.MakeGoToDepot(::GetDepotByTile(destination)->index, (OrderDepotTypeFlags)(ODTFB_PART_OF_ORDERS | ((order_flags & AIOF_SERVICE_IF_NEEDED) ? ODTFB_SERVICE : 0))); + break; + + case OT_GOTO_STATION: + order.MakeGoToStation(::GetStationIndex(destination)); + order.SetLoadType((OrderLoadFlags)GB(order_flags, 5, 3)); + order.SetUnloadType((OrderUnloadFlags)GB(order_flags, 2, 3)); + break; + + case OT_GOTO_WAYPOINT: + order.MakeGoToWaypoint(::GetWaypointIndex(destination)); + break; + + default: + return false; + } + + order.SetNonStopType((OrderNonStopFlags)GB(order_flags, 0, 2)); + + return AIObject::DoCommand(0, vehicle_id | (order_position << 16), order.Pack(), CMD_INSERT_ORDER); +} + +/* static */ bool AIOrder::RemoveOrder(VehicleID vehicle_id, OrderPosition order_position) +{ + order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position); + + EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position)); + + return AIObject::DoCommand(0, vehicle_id, order_position, CMD_DELETE_ORDER); +} + +/** + * Callback handler as ChangeOrder possibly needs multiple DoCommand calls + * to be able to set all order flags correctly. As we need to wait till the + * command has completed before we know the next bits to change we need to + * call the function multiple times. Each time it'll reduce the difference + * between the wanted and the current order. + * @param instance The AI we are doing the callback for. + */ +static void _DoCommandReturnChangeOrder(class AIInstance *instance) +{ + AIObject::SetLastCommandRes(AIOrder::_ChangeOrder()); + AIInstance::DoCommandReturn(instance); +} + +/* static */ bool AIOrder::_ChangeOrder() +{ + /* Make sure we don't go into an infinite loop */ + int retry = AIObject::GetCallbackVariable(3) - 1; + if (retry < 0) { + DEBUG(ai, 0, "Possible infinite loop in ChangeOrder detected"); + return false; + } + AIObject::SetCallbackVariable(3, retry); + + VehicleID vehicle_id = (VehicleID)AIObject::GetCallbackVariable(0); + OrderPosition order_position = (OrderPosition)AIObject::GetCallbackVariable(1); + AIOrderFlags order_flags = (AIOrderFlags)AIObject::GetCallbackVariable(2); + + order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position); + + EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position)); + EnforcePrecondition(false, AreOrderFlagsValid(GetOrderDestination(vehicle_id, order_position), order_flags)); + + Order *order = ::GetVehicle(vehicle_id)->GetFirstOrder(); + for (int i = 0; i < order_position; i++) order = order->next; + + AIOrderFlags current = GetOrderFlags(vehicle_id, order_position); + + if ((current & AIOF_NON_STOP_FLAGS) != (order_flags & AIOF_NON_STOP_FLAGS)) { + return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (order_flags & AIOF_NON_STOP_FLAGS) << 4 | MOF_NON_STOP, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnChangeOrder); + } + + switch (order->GetType()) { + case OT_GOTO_DEPOT: + if ((current & AIOF_SERVICE_IF_NEEDED) != (order_flags & AIOF_SERVICE_IF_NEEDED)) { + return AIObject::DoCommand(0, vehicle_id | (order_position << 16), MOF_DEPOT_ACTION, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnChangeOrder); + } + break; + + case OT_GOTO_STATION: + if ((current & AIOF_UNLOAD_FLAGS) != (order_flags & AIOF_UNLOAD_FLAGS)) { + return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (order_flags & AIOF_UNLOAD_FLAGS) << 2 | MOF_UNLOAD, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnChangeOrder); + } + if ((current & AIOF_LOAD_FLAGS) != (order_flags & AIOF_LOAD_FLAGS)) { + return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (order_flags & AIOF_LOAD_FLAGS) >> 1 | MOF_LOAD, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnChangeOrder); + } + break; + + default: break; + } + + assert(GetOrderFlags(vehicle_id, order_position) == order_flags); + + return true; +} + +/* static */ bool AIOrder::ChangeOrder(VehicleID vehicle_id, OrderPosition order_position, AIOrder::AIOrderFlags order_flags) +{ + AIObject::SetCallbackVariable(0, vehicle_id); + AIObject::SetCallbackVariable(1, order_position); + AIObject::SetCallbackVariable(2, order_flags); + /* In case another client(s) change orders at the same time we could + * end in an infinite loop. This stops that from happening ever. */ + AIObject::SetCallbackVariable(3, 8); + return AIOrder::_ChangeOrder(); +} + +/* static */ bool AIOrder::MoveOrder(VehicleID vehicle_id, OrderPosition order_position_move, OrderPosition order_position_target) +{ + order_position_move = AIOrder::ResolveOrderPosition(vehicle_id, order_position_move); + order_position_target = AIOrder::ResolveOrderPosition(vehicle_id, order_position_target); + + EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position_move)); + EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position_target)); + + return AIObject::DoCommand(0, vehicle_id, order_position_move | (order_position_target << 16), CMD_MOVE_ORDER); +} + +/* static */ bool AIOrder::CopyOrders(VehicleID vehicle_id, VehicleID main_vehicle_id) +{ + EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id)); + EnforcePrecondition(false, AIVehicle::IsValidVehicle(main_vehicle_id)); + + return AIObject::DoCommand(0, vehicle_id | (main_vehicle_id << 16), CO_COPY, CMD_CLONE_ORDER); +} + +/* static */ bool AIOrder::ShareOrders(VehicleID vehicle_id, VehicleID main_vehicle_id) +{ + EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id)); + EnforcePrecondition(false, AIVehicle::IsValidVehicle(main_vehicle_id)); + + return AIObject::DoCommand(0, vehicle_id | (main_vehicle_id << 16), CO_SHARE, CMD_CLONE_ORDER); +} + +/* static */ bool AIOrder::UnshareOrders(VehicleID vehicle_id) +{ + EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id)); + + return AIObject::DoCommand(0, vehicle_id, CO_UNSHARE, CMD_CLONE_ORDER); +} diff --git a/src/ai/api/ai_order.hpp b/src/ai/api/ai_order.hpp new file mode 100644 index 000000000..fbda26892 --- /dev/null +++ b/src/ai/api/ai_order.hpp @@ -0,0 +1,253 @@ +/* $Id$ */ + +/** @file ai_order.hpp Everything to query and build orders. */ + +#ifndef AI_ORDER_HPP +#define AI_ORDER_HPP + +#include "ai_object.hpp" +#include "ai_error.hpp" + +/** + * Class that handles all order related functions. + */ +class AIOrder : public AIObject { +public: + static const char *GetClassName() { return "AIOrder"; } + + /** + * All order related error messages. + */ + enum ErrorMessages { + /** Base for all order related errors */ + ERR_ORDER_BASE = AIError::ERR_CAT_ORDER << AIError::ERR_CAT_BIT_SIZE, + + /** No more space for orders */ + ERR_ORDER_TOO_MANY, // [STR_8831_NO_MORE_SPACE_FOR_ORDERS] + + /** Destination of new order is to far away from the previous order */ + ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION, // [STR_0210_TOO_FAR_FROM_PREVIOUS_DESTINATIO] + }; + + /** + * Flags that can be used to modify the behaviour of orders. + */ + enum AIOrderFlags { + /** Just go to the station/depot, stop unload if possible and load if needed. */ + AIOF_NONE = 0, + + /** Do not stop at the stations that are passed when going to the destination. Only for trains and road vehicles. */ + AIOF_NON_STOP_INTERMEDIATE = 1 << 0, + /** Do not stop at the destionation station. Only for trains and road vehicles. */ + AIOF_NON_STOP_DESTINATION = 1 << 1, + + /** Always unload the vehicle; only for stations. Cannot be set when AIOF_TRANSFER or AIOF_NO_UNLOAD is set. */ + AIOF_UNLOAD = 1 << 2, + /** Transfer instead of deliver the goods; only for stations. Cannot be set when AIOF_UNLOAD or AIOF_NO_UNLOAD is set. */ + AIOF_TRANSFER = 1 << 3, + /** Never unload the vehicle; only for stations. Cannot be set when AIOF_UNLOAD, AIOF_TRANSFER or AIOF_NO_LOAD is set. */ + AIOF_NO_UNLOAD = 1 << 4, + + /** Wait till the vehicle is fully loaded; only for stations. Cannot be set when AIOF_NO_LOAD is set. */ + AIOF_FULL_LOAD = 2 << 5, + /** Wait till at least one cargo of the vehicle is fully loaded; only for stations. Cannot be set when AIOF_NO_LOAD is set. */ + AIOF_FULL_LOAD_ANY = 3 << 5, + /** Do not load any cargo; only for stations. Cannot be set when AIOF_NO_UNLOAD, AIOF_FULL_LOAD or AIOF_FULL_LOAD_ANY is set. */ + AIOF_NO_LOAD = 1 << 7, + + /** Service the vehicle when needed, otherwise skip this order; only for depots. */ + AIOF_SERVICE_IF_NEEDED = 1 << 2, + + /** All flags related to non-stop settings. */ + AIOF_NON_STOP_FLAGS = AIOF_NON_STOP_INTERMEDIATE | AIOF_NON_STOP_DESTINATION, + /** All flags related to unloading. */ + AIOF_UNLOAD_FLAGS = AIOF_TRANSFER | AIOF_UNLOAD | AIOF_NO_UNLOAD, + /** All flags related to loading. */ + AIOF_LOAD_FLAGS = AIOF_FULL_LOAD | AIOF_FULL_LOAD_ANY | AIOF_NO_LOAD, + + /** For marking invalid order flags */ + AIOF_INVALID = 0xFFFF, + }; + + /** Different constants related to the OrderPosition */ + enum OrderPosition { + CURRENT_ORDER = 0xFF, //!< Constant that gets resolved to the current order. + INVALID_ORDER = -1, //!< An invalid order. + }; + + /** + * Checks whether the given order id is valid for the given vehicle. + * @param vehicle_id The vehicle to check the order index for. + * @param order_position The order index to check. + * @pre AIVehicle::IsValidVehicle(vehicle_id). + * @return True if and only if the order_position is valid for the given vehicle. + */ + static bool IsValidVehicleOrder(VehicleID vehicle_id, OrderPosition order_position); + + /** + * Resolves the given order index to the correct index for the given vehicle. + * If the order index was CURRENT_ORDER it will be resolved to the index of + * the current order (as shown in the order list). If the order with the + * given index does not exist it will return INVALID_ORDER. + * @param vehicle_id The vehicle to check the order index for. + * @param order_position The order index to resolve. + * @pre AIVehicle::IsValidVehicle(vehicle_id). + * @return The resolved order index. + */ + static OrderPosition ResolveOrderPosition(VehicleID vehicle_id, OrderPosition order_position); + + /** + * Checks whether the given order flags are valid for the given destination. + * @param destination The destination of the order. + * @param order_flags The flags given to the order. + * @return True if and only if the order_flags are valid for the given location. + */ + static bool AreOrderFlagsValid(TileIndex destination, AIOrderFlags order_flags); + + /** + * Returns the number of orders for the given vehicle. + * @param vehicle_id The vehicle to get the order count of. + * @pre AIVehicle::IsValidVehicle(vehicle_id). + * @return The number of orders for the given vehicle or a negative + * value when the vehicle does not exist. + */ + static int32 GetOrderCount(VehicleID vehicle_id); + + /** + * Gets the destination of the given order for the given vehicle. + * @param vehicle_id The vehicle to get the destination for. + * @param order_position The order to get the destination for. + * @pre IsValidVehicleOrder(vehicle_id, order_position). + * @note Giving CURRENT_ORDER as order_position will give the order that is + * currently being executed by the vehicle. This is not necessarily the + * current order as given by ResolveOrderPosition (the current index in the + * order list) as manual or autoservicing depot orders do not show up + * in the orderlist, but they can be the current order of a vehicle. + * @return The destination tile of the order. + */ + static TileIndex GetOrderDestination(VehicleID vehicle_id, OrderPosition order_position); + + /** + * Gets the AIOrderFlags of the given order for the given vehicle. + * @param vehicle_id The vehicle to get the destination for. + * @param order_position The order to get the destination for. + * @pre IsValidVehicleOrder(vehicle_id, order_position). + * @note Giving CURRENT_ORDER as order_position will give the order that is + * currently being executed by the vehicle. This is not necessarily the + * current order as given by ResolveOrderPosition (the current index in the + * order list) as manual or autoservicing depot orders do not show up + * in the orderlist, but they can be the current order of a vehicle. + * @return The AIOrderFlags of the order. + */ + static AIOrderFlags GetOrderFlags(VehicleID vehicle_id, OrderPosition order_position); + + /** + * Appends an order to the end of the vehicle's order list. + * @param vehicle_id The vehicle to append the order to. + * @param destination The destination of the order. + * @param order_flags The flags given to the order. + * @pre AIVehicle::IsValidVehicle(vehicle_id). + * @pre AreOrderFlagsValid(destination, order_flags). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @exception AIOrder::ERR_ORDER_NO_MORE_SPACE + * @exception AIOrder::ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION + * @return True if and only if the order was appended. + */ + static bool AppendOrder(VehicleID vehicle_id, TileIndex destination, AIOrderFlags order_flags); + + /** + * Inserts an order before the given order_position into the vehicle's order list. + * @param vehicle_id The vehicle to add the order to. + * @param order_position The order to place the new order before. + * @param destination The destination of the order. + * @param order_flags The flags given to the order. + * @pre IsValidVehicleOrder(vehicle_id, order_position). + * @pre AreOrderFlagsValid(destination, order_flags). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @exception AIOrder::ERR_ORDER_NO_MORE_SPACE + * @exception AIOrder::ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION + * @return True if and only if the order was inserted. + */ + static bool InsertOrder(VehicleID vehicle_id, OrderPosition order_position, TileIndex destination, AIOrderFlags order_flags); + + /** + * Removes an order from the vehicle's order list. + * @param vehicle_id The vehicle to remove the order from. + * @param order_position The order to remove from the order list. + * @pre AIVehicle::IsValidVehicleOrder(vehicle_id, order_position). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @return True if and only if the order was removed. + */ + static bool RemoveOrder(VehicleID vehicle_id, OrderPosition order_position); + +#ifndef DOXYGEN_SKIP + /** + * Internal function to help ChangeOrder. + */ + static bool _ChangeOrder(); +#endif + + /** + * Changes the order flags of the given order. + * @param vehicle_id The vehicle to change the order of. + * @param order_position The order to change. + * @param order_flags The new flags given to the order. + * @pre IsValidVehicleOrder(vehicle_id, order_position). + * @pre AreOrderFlagsValid(GetOrderDestination(vehicle_id, order_position), order_flags). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @return True if and only if the order was changed. + */ + static bool ChangeOrder(VehicleID vehicle_id, OrderPosition order_position, AIOrderFlags order_flags); + + /** + * Move an order inside the orderlist + * @param vehicle_id The vehicle to move the orders. + * @param order_position_move The order to move. + * @param order_position_target The target order + * @pre IsValidVehicleOrder(vehicle_id, order_position_move). + * @pre IsValidVehicleOrder(vehicle_id, order_position_target). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @return True if and only if the order was moved. + * @note If the order is moved to a lower place (e.g. from 7 to 2) + * the target order is moved upwards (e.g. 3). If the order is moved + * to a higher place (e.g. from 7 to 9) the target will be moved + * downwards (e.g. 8). + */ + static bool MoveOrder(VehicleID vehicle_id, OrderPosition order_position_move, OrderPosition order_position_target); + + /** + * Copies the orders from another vehicle. The orders of the main vehicle + * are going to be the orders of the changed vehicle. + * @param vehicle_id The vehicle to copy the orders to. + * @param main_vehicle_id The vehicle to copy the orders from. + * @pre AIVehicle::IsValidVehicle(vehicle_id). + * @pre AIVehicle::IsValidVehicle(main_vehicle_id). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @exception AIOrder::ERR_ORDER_NO_MORE_SPACE + * @return True if and only if the copying succeeded. + */ + static bool CopyOrders(VehicleID vehicle_id, VehicleID main_vehicle_id); + + /** + * Shares the orders between two vehicles. The orders of the main + * vehicle are going to be the orders of the changed vehicle. + * @param vehicle_id The vehicle to add to the shared order list. + * @param main_vehicle_id The vehicle to share the orders with. + * @pre AIVehicle::IsValidVehicle(vehicle_id). + * @pre AIVehicle::IsValidVehicle(main_vehicle_id). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @return True if and only if the sharing succeeded. + */ + static bool ShareOrders(VehicleID vehicle_id, VehicleID main_vehicle_id); + + /** + * Removes the given vehicle from a shared orders list. + * @param vehicle_id The vehicle to remove from the shared order list. + * @pre AIVehicle::IsValidVehicle(vehicle_id). + * @return True if and only if the unsharing succeeded. + */ + static bool UnshareOrders(VehicleID vehicle_id); +}; +DECLARE_ENUM_AS_BIT_SET(AIOrder::AIOrderFlags); + +#endif /* AI_ORDER_HPP */ diff --git a/src/ai/api/ai_order.hpp.sq b/src/ai/api/ai_order.hpp.sq new file mode 100644 index 000000000..4128ae029 --- /dev/null +++ b/src/ai/api/ai_order.hpp.sq @@ -0,0 +1,71 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_order.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AIOrder::ErrorMessages GetParam(ForceType<AIOrder::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIOrder::ErrorMessages)tmp; } + template <> int Return<AIOrder::ErrorMessages>(HSQUIRRELVM vm, AIOrder::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AIOrder::AIOrderFlags GetParam(ForceType<AIOrder::AIOrderFlags>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIOrder::AIOrderFlags)tmp; } + template <> int Return<AIOrder::AIOrderFlags>(HSQUIRRELVM vm, AIOrder::AIOrderFlags res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AIOrder::OrderPosition GetParam(ForceType<AIOrder::OrderPosition>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIOrder::OrderPosition)tmp; } + template <> int Return<AIOrder::OrderPosition>(HSQUIRRELVM vm, AIOrder::OrderPosition res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AIOrder to be used as Squirrel parameter */ + template <> AIOrder *GetParam(ForceType<AIOrder *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIOrder *)instance; } + template <> AIOrder &GetParam(ForceType<AIOrder &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIOrder *)instance; } + template <> const AIOrder *GetParam(ForceType<const AIOrder *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIOrder *)instance; } + template <> const AIOrder &GetParam(ForceType<const AIOrder &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIOrder *)instance; } + template <> int Return<AIOrder *>(HSQUIRRELVM vm, AIOrder *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIOrder", res, NULL, DefSQDestructorCallback<AIOrder>); return 1; } +}; // namespace SQConvert + +void SQAIOrder_Register(Squirrel *engine) { + DefSQClass <AIOrder> SQAIOrder("AIOrder"); + SQAIOrder.PreRegister(engine); + SQAIOrder.AddConstructor<void (AIOrder::*)(), 1>(engine, "x"); + + SQAIOrder.DefSQConst(engine, AIOrder::ERR_ORDER_BASE, "ERR_ORDER_BASE"); + SQAIOrder.DefSQConst(engine, AIOrder::ERR_ORDER_TOO_MANY, "ERR_ORDER_TOO_MANY"); + SQAIOrder.DefSQConst(engine, AIOrder::ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION, "ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION"); + SQAIOrder.DefSQConst(engine, AIOrder::AIOF_NONE, "AIOF_NONE"); + SQAIOrder.DefSQConst(engine, AIOrder::AIOF_NON_STOP_INTERMEDIATE, "AIOF_NON_STOP_INTERMEDIATE"); + SQAIOrder.DefSQConst(engine, AIOrder::AIOF_NON_STOP_DESTINATION, "AIOF_NON_STOP_DESTINATION"); + SQAIOrder.DefSQConst(engine, AIOrder::AIOF_UNLOAD, "AIOF_UNLOAD"); + SQAIOrder.DefSQConst(engine, AIOrder::AIOF_TRANSFER, "AIOF_TRANSFER"); + SQAIOrder.DefSQConst(engine, AIOrder::AIOF_NO_UNLOAD, "AIOF_NO_UNLOAD"); + SQAIOrder.DefSQConst(engine, AIOrder::AIOF_FULL_LOAD, "AIOF_FULL_LOAD"); + SQAIOrder.DefSQConst(engine, AIOrder::AIOF_FULL_LOAD_ANY, "AIOF_FULL_LOAD_ANY"); + SQAIOrder.DefSQConst(engine, AIOrder::AIOF_NO_LOAD, "AIOF_NO_LOAD"); + SQAIOrder.DefSQConst(engine, AIOrder::AIOF_SERVICE_IF_NEEDED, "AIOF_SERVICE_IF_NEEDED"); + SQAIOrder.DefSQConst(engine, AIOrder::AIOF_NON_STOP_FLAGS, "AIOF_NON_STOP_FLAGS"); + SQAIOrder.DefSQConst(engine, AIOrder::AIOF_UNLOAD_FLAGS, "AIOF_UNLOAD_FLAGS"); + SQAIOrder.DefSQConst(engine, AIOrder::AIOF_LOAD_FLAGS, "AIOF_LOAD_FLAGS"); + SQAIOrder.DefSQConst(engine, AIOrder::AIOF_INVALID, "AIOF_INVALID"); + SQAIOrder.DefSQConst(engine, AIOrder::CURRENT_ORDER, "CURRENT_ORDER"); + SQAIOrder.DefSQConst(engine, AIOrder::INVALID_ORDER, "INVALID_ORDER"); + + AIError::RegisterErrorMap(STR_8831_NO_MORE_SPACE_FOR_ORDERS, AIOrder::ERR_ORDER_TOO_MANY); + AIError::RegisterErrorMap(STR_0210_TOO_FAR_FROM_PREVIOUS_DESTINATIO, AIOrder::ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION); + + AIError::RegisterErrorMapString(AIOrder::ERR_ORDER_TOO_MANY, "ERR_ORDER_TOO_MANY"); + AIError::RegisterErrorMapString(AIOrder::ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION, "ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION"); + + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::GetClassName, "GetClassName", 1, "x"); + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::IsValidVehicleOrder, "IsValidVehicleOrder", 3, "xii"); + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::ResolveOrderPosition, "ResolveOrderPosition", 3, "xii"); + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::AreOrderFlagsValid, "AreOrderFlagsValid", 3, "xii"); + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::GetOrderCount, "GetOrderCount", 2, "xi"); + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::GetOrderDestination, "GetOrderDestination", 3, "xii"); + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::GetOrderFlags, "GetOrderFlags", 3, "xii"); + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::AppendOrder, "AppendOrder", 4, "xiii"); + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::InsertOrder, "InsertOrder", 5, "xiiii"); + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::RemoveOrder, "RemoveOrder", 3, "xii"); + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::ChangeOrder, "ChangeOrder", 4, "xiii"); + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::MoveOrder, "MoveOrder", 4, "xiii"); + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::CopyOrders, "CopyOrders", 3, "xii"); + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::ShareOrders, "ShareOrders", 3, "xii"); + SQAIOrder.DefSQStaticMethod(engine, &AIOrder::UnshareOrders, "UnshareOrders", 2, "xi"); + + SQAIOrder.PostRegister(engine); +} diff --git a/src/ai/api/ai_rail.cpp b/src/ai/api/ai_rail.cpp new file mode 100644 index 000000000..f35f98de3 --- /dev/null +++ b/src/ai/api/ai_rail.cpp @@ -0,0 +1,442 @@ +/* $Id$ */ + +/** @file ai_rail.cpp Implementation of AIRail. */ + +#include "ai_rail.hpp" +#include "ai_object.hpp" +#include "ai_map.hpp" +#include "../../openttd.h" +#include "../../debug.h" +#include "../../rail_map.h" +#include "../../road_map.h" +#include "../../command_type.h" +#include "../../station_map.h" +#include "../../company_func.h" +#include "../../core/math_func.hpp" +#include "../../waypoint.h" +#include "../../newgrf.h" +#include "../../newgrf_generic.h" +#include "../../newgrf_station.h" +#include "../../newgrf_callbacks.h" + +/* static */ bool AIRail::IsRailTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return (::IsTileType(tile, MP_RAILWAY) && !::IsRailDepot(tile)) || + (::IsRailwayStationTile(tile) && !::IsStationTileBlocked(tile)) || ::IsLevelCrossingTile(tile); +} + +/* static */ bool AIRail::IsLevelCrossingTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsLevelCrossingTile(tile); +} + +/* static */ bool AIRail::IsRailDepotTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsRailDepotTile(tile); +} + +/* static */ bool AIRail::IsRailStationTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsRailwayStationTile(tile); +} + +/* static */ bool AIRail::IsRailWaypointTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsTileType(tile, MP_RAILWAY) && ::IsRailWaypointTile(tile); +} + +/* static */ bool AIRail::IsRailTypeAvailable(RailType rail_type) +{ + if ((::RailType)rail_type < RAILTYPE_BEGIN || (::RailType)rail_type >= RAILTYPE_END) return false; + + return ::HasRailtypeAvail(_current_company, (::RailType)rail_type); +} + +/* static */ AIRail::RailType AIRail::GetCurrentRailType() +{ + return (RailType)AIObject::GetRailType(); +} + +/* static */ void AIRail::SetCurrentRailType(RailType rail_type) +{ + if (!IsRailTypeAvailable(rail_type)) return; + + AIObject::SetRailType((::RailType)rail_type); +} + +/* static */ bool AIRail::TrainCanRunOnRail(AIRail::RailType engine_rail_type, AIRail::RailType track_rail_type) +{ + if (!AIRail::IsRailTypeAvailable(engine_rail_type)) return false; + if (!AIRail::IsRailTypeAvailable(track_rail_type)) return false; + + return ::IsCompatibleRail((::RailType)engine_rail_type, (::RailType)track_rail_type); +} + +/* static */ bool AIRail::TrainHasPowerOnRail(AIRail::RailType engine_rail_type, AIRail::RailType track_rail_type) +{\ + if (!AIRail::IsRailTypeAvailable(engine_rail_type)) return false; + if (!AIRail::IsRailTypeAvailable(track_rail_type)) return false; + + return ::HasPowerOnRail((::RailType)engine_rail_type, (::RailType)track_rail_type); +} + +/* static */ AIRail::RailType AIRail::GetRailType(TileIndex tile) +{ + if (!AITile::HasTransportType(tile, AITile::TRANSPORT_RAIL)) return RAILTYPE_INVALID; + + return (RailType)::GetRailType(tile); +} + +/* static */ bool AIRail::ConvertRailType(TileIndex start_tile, TileIndex end_tile, AIRail::RailType convert_to) +{ + EnforcePrecondition(false, ::IsValidTile(start_tile)); + EnforcePrecondition(false, ::IsValidTile(end_tile)); + EnforcePrecondition(false, IsRailTypeAvailable(convert_to)); + + return AIObject::DoCommand(start_tile, end_tile, convert_to, CMD_CONVERT_RAIL); +} + +/* static */ TileIndex AIRail::GetRailDepotFrontTile(TileIndex depot) +{ + if (!IsRailDepotTile(depot)) return INVALID_TILE; + + return depot + ::TileOffsByDiagDir(::GetRailDepotDirection(depot)); +} + +/* static */ AIRail::RailTrack AIRail::GetRailStationDirection(TileIndex tile) +{ + if (!IsRailStationTile(tile)) return RAILTRACK_INVALID; + + return (RailTrack)::GetRailStationTrack(tile); +} + +/* static */ bool AIRail::BuildRailDepot(TileIndex tile, TileIndex front) +{ + EnforcePrecondition(false, tile != front); + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, ::IsValidTile(front)); + EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front)); + EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType())); + + uint entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0); + + return AIObject::DoCommand(tile, AIObject::GetRailType(), entrance_dir, CMD_BUILD_TRAIN_DEPOT); +} + +/* static */ bool AIRail::BuildRailStation(TileIndex tile, RailTrack direction, uint num_platforms, uint platform_length, bool join_adjacent) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, direction == RAILTRACK_NW_SE || direction == RAILTRACK_NE_SW); + EnforcePrecondition(false, num_platforms > 0 && num_platforms <= 0xFF); + EnforcePrecondition(false, platform_length > 0 && platform_length <= 0xFF); + EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType())); + + uint32 p1 = GetCurrentRailType() | (platform_length << 16) | (num_platforms << 8); + if (direction == RAILTRACK_NW_SE) p1 |= (1 << 4); + if (!join_adjacent) p1 |= (1 << 24); + return AIObject::DoCommand(tile, p1, INVALID_STATION << 16, CMD_BUILD_RAILROAD_STATION); +} + +/* static */ bool AIRail::BuildNewGRFRailStation(TileIndex tile, RailTrack direction, uint num_platforms, uint platform_length, bool join_adjacent, CargoID cargo_id, IndustryType source_industry, IndustryType goal_industry, int distance, bool source_station) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, direction == RAILTRACK_NW_SE || direction == RAILTRACK_NE_SW); + EnforcePrecondition(false, num_platforms > 0 && num_platforms <= 0xFF); + EnforcePrecondition(false, platform_length > 0 && platform_length <= 0xFF); + EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType())); + + uint32 p1 = GetCurrentRailType() | (platform_length << 16) | (num_platforms << 8); + if (direction == RAILTRACK_NW_SE) p1 |= 1 << 4; + if (!join_adjacent) p1 |= (1 << 24); + + const GRFFile *file; + uint16 res = GetAiPurchaseCallbackResult(GSF_STATION, cargo_id, 0, source_industry, goal_industry, min(255, distance / 2), AICE_STATION_GET_STATION_ID, source_station ? 0 : 1, min(15, num_platforms) << 4 | min(15, platform_length), &file); + uint32 p2 = INVALID_STATION << 16; + if (res != CALLBACK_FAILED) { + extern StationClass _station_classes[STAT_CLASS_MAX]; + const StationSpec *spec = GetCustomStationSpecByGrf(file->grfid, res); + int index = -1; + for (uint j = 0; j < _station_classes[spec->sclass].stations; j++) { + if (spec == _station_classes[spec->sclass].spec[j]) { + index = j; + break; + } + } + if (index == -1) { + DEBUG(grf, 1, "%s returned an invalid station ID for 'AI construction/purchase selection (18)' callback", file->filename); + } else { + p2 |= spec->sclass | index << 8; + } + + } + return AIObject::DoCommand(tile, p1, p2, CMD_BUILD_RAILROAD_STATION); +} + +/* static */ bool AIRail::RemoveRailStationTileRect(TileIndex tile, TileIndex tile2) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, ::IsValidTile(tile2)); + + return AIObject::DoCommand(tile, tile2, 0, CMD_REMOVE_FROM_RAILROAD_STATION); +} + +/* static */ uint AIRail::GetRailTracks(TileIndex tile) +{ + if (!IsRailTile(tile)) return RAILTRACK_INVALID; + + if (IsRailWaypointTile(tile)) return ::GetRailWaypointBits(tile); + if (IsRailStationTile(tile)) return ::TrackToTrackBits(::GetRailStationTrack(tile)); + if (IsLevelCrossingTile(tile)) return ::GetCrossingRailBits(tile); + return ::GetTrackBits(tile); +} + +/* static */ bool AIRail::BuildRailTrack(TileIndex tile, RailTrack rail_track) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, rail_track != 0); + EnforcePrecondition(false, (rail_track & ~::TRACK_BIT_ALL) == 0); + EnforcePrecondition(false, KillFirstBit((uint)rail_track) == 0); + EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType())); + + return AIObject::DoCommand(tile, tile, GetCurrentRailType() | (FindFirstTrack((::TrackBits)rail_track) << 4), CMD_BUILD_RAILROAD_TRACK); +} + +/* static */ bool AIRail::RemoveRailTrack(TileIndex tile, RailTrack rail_track) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, ::IsTileType(tile, MP_RAILWAY) && ::IsPlainRailTile(tile)); + EnforcePrecondition(false, GetRailTracks(tile) & rail_track); + EnforcePrecondition(false, KillFirstBit((uint)rail_track) == 0); + + return AIObject::DoCommand(tile, tile, GetCurrentRailType() | (FindFirstTrack((::TrackBits)rail_track) << 4), CMD_REMOVE_RAILROAD_TRACK); +} + +/* static */ bool AIRail::AreTilesConnected(TileIndex from, TileIndex tile, TileIndex to) +{ + if (!IsRailTile(tile)) return false; + if (from == to || AIMap::DistanceManhattan(from, tile) != 1 || AIMap::DistanceManhattan(tile, to) != 1) return false; + + if (to < from) ::Swap(from, to); + + if (tile - from == 1) { + if (to - tile == 1) return (GetRailTracks(tile) & RAILTRACK_NE_SW) != 0; + if (to - tile == ::MapSizeX()) return (GetRailTracks(tile) & RAILTRACK_NE_SE) != 0; + } else if (tile - from == ::MapSizeX()) { + if (tile - to == 1) return (GetRailTracks(tile) & RAILTRACK_NW_NE) != 0; + if (to - tile == 1) return (GetRailTracks(tile) & RAILTRACK_NW_SW) != 0; + if (to - tile == ::MapSizeX()) return (GetRailTracks(tile) & RAILTRACK_NW_SE) != 0; + } else { + return (GetRailTracks(tile) & RAILTRACK_SW_SE) != 0; + } + + NOT_REACHED(); +} + +/** + * Prepare the second parameter for CmdBuildRailroadTrack and CmdRemoveRailroadTrack. The direction + * depends on all three tiles. Sometimes the third tile needs to be adjusted. + */ +static uint32 SimulateDrag(TileIndex from, TileIndex tile, TileIndex *to) +{ + int diag_offset = abs(abs((int)::TileX(*to) - (int)::TileX(tile)) - abs((int)::TileY(*to) - (int)::TileY(tile))); + uint32 p2 = AIRail::GetCurrentRailType(); + if (::TileY(from) == ::TileY(*to)) { + p2 |= (TRACK_X << 4); + *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1); + } else if (::TileX(from) == ::TileX(*to)) { + p2 |= (TRACK_Y << 4); + *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1); + } else if (::TileY(from) < ::TileY(tile)) { + if (::TileX(*to) < ::TileX(tile)) { + p2 |= (TRACK_UPPER << 4); + } else { + p2 |= (TRACK_LEFT << 4); + } + if (diag_offset) { + *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1); + } else { + *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1); + } + } else if (::TileY(from) > ::TileY(tile)) { + if (::TileX(*to) < ::TileX(tile)) { + p2 |= (TRACK_RIGHT << 4); + } else { + p2 |= (TRACK_LOWER << 4); + } + if (diag_offset) { + *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1); + } else { + *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1); + } + } else if (::TileX(from) < ::TileX(tile)) { + if (::TileY(*to) < ::TileY(tile)) { + p2 |= (TRACK_UPPER << 4); + } else { + p2 |= (TRACK_RIGHT << 4); + } + if (!diag_offset) { + *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1); + } else { + *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1); + } + } else if (::TileX(from) > ::TileX(tile)) { + if (::TileY(*to) < ::TileY(tile)) { + p2 |= (TRACK_LEFT << 4); + } else { + p2 |= (TRACK_LOWER << 4); + } + if (!diag_offset) { + *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1); + } else { + *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1); + } + } + return p2; +} + +/* static */ bool AIRail::BuildRail(TileIndex from, TileIndex tile, TileIndex to) +{ + EnforcePrecondition(false, ::IsValidTile(from)); + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, ::IsValidTile(to)); + EnforcePrecondition(false, ::DistanceManhattan(from, tile) == 1); + EnforcePrecondition(false, ::DistanceManhattan(tile,to) >= 1); + EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType())); + int diag_offset = abs(abs((int)::TileX(to) - (int)::TileX(tile)) - abs((int)::TileY(to) - (int)::TileY(tile))); + EnforcePrecondition(false, diag_offset <= 1 || + (::TileX(from) == ::TileX(tile) && ::TileX(tile) == ::TileX(to)) || + (::TileY(from) == ::TileY(tile) && ::TileY(tile) == ::TileY(to))); + + uint32 p2 = SimulateDrag(from, tile, &to); + return AIObject::DoCommand(tile, to, p2, CMD_BUILD_RAILROAD_TRACK); +} + +/* static */ bool AIRail::RemoveRail(TileIndex from, TileIndex tile, TileIndex to) +{ + EnforcePrecondition(false, ::IsValidTile(from)); + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, ::IsValidTile(to)); + EnforcePrecondition(false, ::DistanceManhattan(from, tile) == 1); + EnforcePrecondition(false, ::DistanceManhattan(tile,to) >= 1); + int diag_offset = abs(abs((int)::TileX(to) - (int)::TileX(tile)) - abs((int)::TileY(to) - (int)::TileY(tile))); + EnforcePrecondition(false, diag_offset <= 1 || + (::TileX(from) == ::TileX(tile) && ::TileX(tile) == ::TileX(to)) || + (::TileY(from) == ::TileY(tile) && ::TileY(tile) == ::TileY(to))); + + if (!IsRailTypeAvailable(GetCurrentRailType())) SetCurrentRailType(GetRailType(tile)); + uint32 p2 = SimulateDrag(from, tile, &to); + return AIObject::DoCommand(tile, to, p2, CMD_REMOVE_RAILROAD_TRACK); +} + +/** + * Contains information about the trackdir that belongs to a track when entering + * from a specific direction. + */ +struct AIRailSignalData { + Track track; //!< The track that will be taken to travel. + Trackdir trackdir; //!< The Trackdir belonging to that track. + uint signal_cycles; //!< How many times the signal should be cycled in order to build it in the correct direction. +}; + +static const int NUM_TRACK_DIRECTIONS = 3; //!< The number of directions you can go when entering a tile. + +/** + * List information about the trackdir and number of needed cycles for building signals when + * entering a track from a specific direction. The first index is the difference between the + * TileIndex of the previous and current tile, where (-)MapSizeX is replaced with -2 / 2 and + * 2 it added. + */ +static const AIRailSignalData _possible_trackdirs[5][NUM_TRACK_DIRECTIONS] = { + {{TRACK_UPPER, TRACKDIR_UPPER_E, 0}, {TRACK_Y, TRACKDIR_Y_SE, 0}, {TRACK_LEFT, TRACKDIR_LEFT_S, 1}}, + {{TRACK_RIGHT, TRACKDIR_RIGHT_S, 1}, {TRACK_X, TRACKDIR_X_SW, 1}, {TRACK_UPPER, TRACKDIR_UPPER_W, 1}}, + {{INVALID_TRACK, INVALID_TRACKDIR, 0}, {INVALID_TRACK, INVALID_TRACKDIR, 0}, {INVALID_TRACK, INVALID_TRACKDIR, 0}}, + {{TRACK_LOWER, TRACKDIR_LOWER_E, 0}, {TRACK_X, TRACKDIR_X_NE, 0}, {TRACK_LEFT, TRACKDIR_LEFT_N, 0}}, + {{TRACK_RIGHT, TRACKDIR_RIGHT_N, 0}, {TRACK_Y, TRACKDIR_Y_NW, 1}, {TRACK_LOWER, TRACKDIR_LOWER_W, 1}} +}; + +/* static */ AIRail::SignalType AIRail::GetSignalType(TileIndex tile, TileIndex front) +{ + if (AIMap::DistanceManhattan(tile, front) != 1) return SIGNALTYPE_NONE; + if (!::IsTileType(tile, MP_RAILWAY) || !::HasSignals(tile)) return SIGNALTYPE_NONE; + + int data_index = 2 + (::TileX(front) - ::TileX(tile)) + 2 * (::TileY(front) - ::TileY(tile)); + + for (int i = 0; i < NUM_TRACK_DIRECTIONS; i++) { + const Track &track = _possible_trackdirs[data_index][i].track; + if (!(::TrackToTrackBits(track) & GetRailTracks(tile))) continue; + if (!HasSignalOnTrack(tile, track)) continue; + if (!HasSignalOnTrackdir(tile, _possible_trackdirs[data_index][i].trackdir)) continue; + SignalType st = (SignalType)::GetSignalType(tile, track); + if (HasSignalOnTrackdir(tile, ::ReverseTrackdir(_possible_trackdirs[data_index][i].trackdir))) st = (SignalType)(st | SIGNALTYPE_TWOWAY); + return st; + } + + return SIGNALTYPE_NONE; +} + +/** + * Check if signal_type is a valid SignalType. + */ +static bool IsValidSignalType(int signal_type) +{ + if (signal_type < AIRail::SIGNALTYPE_NORMAL || signal_type > AIRail::SIGNALTYPE_COMBO_TWOWAY) return false; + if (signal_type > AIRail::SIGNALTYPE_PBS_ONEWAY && signal_type < AIRail::SIGNALTYPE_NORMAL_TWOWAY) return false; + return true; +} + +/* static */ bool AIRail::BuildSignal(TileIndex tile, TileIndex front, SignalType signal) +{ + EnforcePrecondition(false, AIMap::DistanceManhattan(tile, front) == 1) + EnforcePrecondition(false, ::IsTileType(tile, MP_RAILWAY) && ::IsPlainRailTile(tile)); + EnforcePrecondition(false, ::IsValidSignalType(signal)); + + Track track = INVALID_TRACK; + uint signal_cycles; + + int data_index = 2 + (::TileX(front) - ::TileX(tile)) + 2 * (::TileY(front) - ::TileY(tile)); + for (int i = 0; i < NUM_TRACK_DIRECTIONS; i++) { + const Track &t = _possible_trackdirs[data_index][i].track; + if (!(::TrackToTrackBits(t) & GetRailTracks(tile))) continue; + track = t; + signal_cycles = _possible_trackdirs[data_index][i].signal_cycles; + break; + } + EnforcePrecondition(false, track != INVALID_TRACK); + + uint p1 = track; + if (signal < SIGNALTYPE_TWOWAY) { + if (signal != SIGNALTYPE_PBS && signal != SIGNALTYPE_PBS_ONEWAY) signal_cycles++; + p1 |= (signal_cycles << 15); + } + p1 |= ((signal >= SIGNALTYPE_TWOWAY ? signal ^ SIGNALTYPE_TWOWAY : signal) << 5); + + return AIObject::DoCommand(tile, p1, 0, CMD_BUILD_SIGNALS); +} + +/* static */ bool AIRail::RemoveSignal(TileIndex tile, TileIndex front) +{ + EnforcePrecondition(false, AIMap::DistanceManhattan(tile, front) == 1) + EnforcePrecondition(false, GetSignalType(tile, front) != SIGNALTYPE_NONE); + + Track track = INVALID_TRACK; + int data_index = 2 + (::TileX(front) - ::TileX(tile)) + 2 * (::TileY(front) - ::TileY(tile)); + for (int i = 0; i < NUM_TRACK_DIRECTIONS; i++) { + const Track &t = _possible_trackdirs[data_index][i].track; + if (!(::TrackToTrackBits(t) & GetRailTracks(tile))) continue; + track = t; + break; + } + EnforcePrecondition(false, track != INVALID_TRACK); + + return AIObject::DoCommand(tile, track, 0, CMD_REMOVE_SIGNALS); +} diff --git a/src/ai/api/ai_rail.hpp b/src/ai/api/ai_rail.hpp new file mode 100644 index 000000000..5acb995ff --- /dev/null +++ b/src/ai/api/ai_rail.hpp @@ -0,0 +1,390 @@ +/* $Id$ */ + +/** @file ai_rail.hpp Everything to query and build rails. */ + +#ifndef AI_RAIL_HPP +#define AI_RAIL_HPP + +#include "ai_object.hpp" +#include "ai_error.hpp" +#include "ai_tile.hpp" + +/** + * Class that handles all rail related functions. + */ +class AIRail : public AIObject { +public: + static const char *GetClassName() { return "AIRail"; } + + /** + * All rail related error messages. + */ + enum ErrorMessages { + /** Base for rail building / maintaining errors */ + ERR_RAIL_BASE = AIError::ERR_CAT_RAIL << AIError::ERR_CAT_BIT_SIZE, + + /** One-way roads cannot have crossings */ + ERR_CROSSING_ON_ONEWAY_ROAD, // [STR_ERR_CROSSING_ON_ONEWAY_ROAD] + + /** Track not suitable for signals */ + ERR_UNSUITABLE_TRACK, // [STR_1005_NO_SUITABLE_RAILROAD_TRACK] + + /** Non-uniform stations is diabled */ + ERR_NONUNIFORM_STATIONS_DISABLED, // [STR_NONUNIFORM_STATIONS_DISALLOWED] + }; + + /** + * Types of rail known to the game. + */ + enum RailType { + /* Note: the values _are_ important as they represent an in-game value */ + RAILTYPE_INVALID = 0xFF, //!< Invalid RailType. + }; + + /** + * A bitmap with all possible rail tracks on a tile. + */ + enum RailTrack { + /* Note: the values _are_ important as they represent an in-game value */ + RAILTRACK_NE_SW = 1 << 0, //!< Track along the x-axis (north-east to south-west). + RAILTRACK_NW_SE = 1 << 1, //!< Track along the y-axis (north-west to south-east). + RAILTRACK_NW_NE = 1 << 2, //!< Track in the upper corner of the tile (north). + RAILTRACK_SW_SE = 1 << 3, //!< Track in the lower corner of the tile (south). + RAILTRACK_NW_SW = 1 << 4, //!< Track in the left corner of the tile (west). + RAILTRACK_NE_SE = 1 << 5, //!< Track in the right corner of the tile (east). + RAILTRACK_INVALID = 0xFF, //!< Flag for an invalid track. + }; + + /** + * Types of signal known to the game. + */ + enum SignalType { + /* Note: the values _are_ important as they represent an in-game value */ + SIGNALTYPE_NORMAL = 0, //!< Normal signal. + SIGNALTYPE_ENTRY = 1, //!< Entry presignal. + SIGNALTYPE_EXIT = 2, //!< Exit signal. + SIGNALTYPE_COMBO = 3, //!< Combo signal. + SIGNALTYPE_PBS = 4, //!< Normal PBS signal. + SIGNALTYPE_PBS_ONEWAY = 5, //!< No-entry PBS signal. + SIGNALTYPE_TWOWAY = 8, //!< Bit mask for twoway signal. + SIGNALTYPE_NORMAL_TWOWAY = SIGNALTYPE_NORMAL | SIGNALTYPE_TWOWAY, //!< Normal twoway signal. + SIGNALTYPE_ENTRY_TWOWAY = SIGNALTYPE_ENTRY | SIGNALTYPE_TWOWAY, //!< Entry twoway signal. + SIGNALTYPE_EXIT_TWOWAY = SIGNALTYPE_EXIT | SIGNALTYPE_TWOWAY, //!< Exit twoway signal. + SIGNALTYPE_COMBO_TWOWAY = SIGNALTYPE_COMBO | SIGNALTYPE_TWOWAY, //!< Combo twoway signal. + SIGNALTYPE_NONE = 0xFF, //!< No signal. + }; + + /** + * Checks whether the given tile is actually a tile with rail that can be + * used to traverse a tile. This excludes rail depots but includes + * stations and waypoints. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has rail. + */ + static bool IsRailTile(TileIndex tile); + + /** + * Checks whether there is a road / rail crossing on a tile. + * @param tile The tile to check. + * @return True if and only if there is a road / rail crossing. + */ + static bool IsLevelCrossingTile(TileIndex tile); + + /** + * Checks whether the given tile is actually a tile with a rail depot. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has a rail depot. + */ + static bool IsRailDepotTile(TileIndex tile); + + /** + * Checks whether the given tile is actually a tile with a rail station. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has a rail station. + */ + static bool IsRailStationTile(TileIndex tile); + + /** + * Checks whether the given tile is actually a tile with a rail waypoint. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has a rail waypoint. + */ + static bool IsRailWaypointTile(TileIndex tile); + + /** + * Check if a given RailType is available. + * @param rail_type The RailType to check for. + * @return True if this RailType can be used. + */ + static bool IsRailTypeAvailable(RailType rail_type); + + /** + * Get the current RailType set for all AIRail functions. + * @return The RailType currently set. + */ + static RailType GetCurrentRailType(); + + /** + * Set the RailType for all further AIRail functions. + * @param rail_type The RailType to set. + */ + static void SetCurrentRailType(RailType rail_type); + + /** + * Check if a train build for a rail type can run on another rail type. + * @param engine_rail_type The rail type the train is build for. + * @param track_rail_type The type you want to check. + * @pre AIRail::IsRailTypeAvailable(engine_rail_type). + * @pre AIRail::IsRailTypeAvailable(track_rail_type). + * @return Whether a train build for 'engine_rail_type' can run on 'track_rail_type'. + * @note Even if a train can run on a RailType that doesn't mean that it'll be + * able to power the train. Use TrainHasPowerOnRail for that. + */ + static bool TrainCanRunOnRail(AIRail::RailType engine_rail_type, AIRail::RailType track_rail_type); + + /** + * Check if a train build for a rail type has power on another rail type. + * @param engine_rail_type The rail type the train is build for. + * @param track_rail_type The type you want to check. + * @pre AIRail::IsRailTypeAvailable(engine_rail_type). + * @pre AIRail::IsRailTypeAvailable(track_rail_type). + * @return Whether a train build for 'engine_rail_type' has power on 'track_rail_type'. + */ + static bool TrainHasPowerOnRail(AIRail::RailType engine_rail_type, AIRail::RailType track_rail_type); + + /** + * Get the RailType that is used on a tile. + * @param tile The tile to check. + * @pre AITile::HasTransportType(tile, AITile.TRANSPORT_RAIL). + * @return The RailType that is used on a tile. + */ + static RailType GetRailType(TileIndex tile); + + /** + * Convert the tracks on all tiles within a rectangle to another RailType. + * @param start_tile One corner of the rectangle. + * @param end_tile The opposite corner of the rectangle. + * @param convert_to The RailType you want to convert the rails to. + * @pre AIMap::IsValidTile(start_tile). + * @pre AIMap::IsValidTile(end_tile). + * @pre IsRailTypeAvailable(convert_to). + * @exception AIRail::ERR_UNSUITABLE_TRACK + * @return Whether at least some rail has been converted succesfully. + */ + static bool ConvertRailType(TileIndex start_tile, TileIndex end_tile, AIRail::RailType convert_to); + + /** + * Gets the tile in front of a rail depot. + * @param depot The rail depot tile. + * @pre IsRailDepotTile(depot). + * @return The tile in front of the depot. + */ + static TileIndex GetRailDepotFrontTile(TileIndex depot); + + /** + * Gets the direction of a rail station tile. + * @param tile The rail station tile. + * @pre IsRailStationTile(tile). + * @return The direction of the station (either RAILTRACK_NE_SW or RAILTRACK_NW_SE). + */ + static RailTrack GetRailStationDirection(TileIndex tile); + + /** + * Builds a rail depot. + * @param tile Place to build the depot. + * @param front The tile exactly in front of the depot. + * @pre AIMap::IsValidTile(tile). + * @pre AIMap::IsValidTile(front). + * @pre 'tile' is not equal to 'front', but in a straight line of it. + * @pre IsRailTypeAvailable(GetCurrentRailType()). + * @exception AIError::ERR_FLAT_LAND_REQUIRED + * @exception AIError::ERR_AREA_NOT_CLEAR + * @return Whether the rail depot has been/can be build or not. + */ + static bool BuildRailDepot(TileIndex tile, TileIndex front); + + /** + * Build a rail station. + * @param tile Place to build the station. + * @param direction The direction to build the station. + * @param num_platforms The number of platforms to build. + * @param platform_length The length of each platform. + * @param join_adjacent When building next to an other station, don't create a new station when this flag is true. + * @pre IsRailTypeAvailable(GetCurrentRailType()). + * @pre AIMap::IsValidTile(tile). + * @pre direction == RAILTRACK_NW_SE || direction == RAILTRACK_NE_SW. + * @pre num_platforms > 0 && num_platforms <= 255. + * @pre platform_length > 0 && platform_length <= 255. + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIError::ERR_FLAT_LAND_REQUIRED + * @exception AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION + * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS + * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN + * @return Whether the station has been/can be build or not. + */ + static bool BuildRailStation(TileIndex tile, RailTrack direction, uint num_platforms, uint platform_length, bool join_adjacent); + + /** + * Build a NewGRF rail station. This calls callback 18 to let a NewGRF + * provide the station class / id to build, so we don't end up with + * only the default stations on the map. + * @param tile Place to build the station. + * @param direction The direction to build the station. + * @param num_platforms The number of platforms to build. + * @param platform_length The length of each platform. + * @param join_adjacent When building next to an other station, don't create a new station when this flag is true. + * @param cargo_id The CargoID of the cargo that will be transported from / to this station. + * @param source_industry The IndustryType of the industry you'll transport goods from. + * @param goal_industry The IndustryType of the industry you'll transport goods to. + * @param distance The manhattan distance you'll transport the cargo over. + * @param source_station True if this is the source station, false otherwise. + * @pre IsRailTypeAvailable(GetCurrentRailType()). + * @pre AIMap::IsValidTile(tile). + * @pre direction == RAILTRACK_NW_SE || direction == RAILTRACK_NE_SW. + * @pre num_platforms > 0 && num_platforms <= 255. + * @pre platform_length > 0 && platform_length <= 255. + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIError::ERR_FLAT_LAND_REQUIRED + * @exception AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION + * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS + * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN + * @return Whether the station has been/can be build or not. + */ + static bool BuildNewGRFRailStation(TileIndex tile, RailTrack direction, uint num_platforms, uint platform_length, bool join_adjacent, CargoID cargo_id, IndustryType source_industry, IndustryType goal_industry, int distance, bool source_station); + + /** + * Remove a rectangle of platform pieces from a rail station. + * @param tile One corner of the rectangle to clear. + * @param tile2 The oppposite corner. + * @pre IsValidTile(tile). + * @pre IsValidTile(tile2). + * @return Whether at least one tile has been/can be cleared or not. + */ + static bool RemoveRailStationTileRect(TileIndex tile, TileIndex tile2); + + /** + * Get all RailTracks on the given tile. + * @param tile The tile to check. + * @pre IsRailTile(tile). + * @return A bitmask of RailTrack with all RailTracks on the tile. + */ + static uint GetRailTracks(TileIndex tile); + + /** + * Build rail on the given tile. + * @param tile The tile to build on. + * @param rail_track The RailTrack to build. + * @pre AIMap::IsValidTile(tile). + * @pre IsRailTypeAvailable(GetCurrentRailType()). + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIError::ERR_LAND_SLOPED_WRONG + * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS + * @exception AIRail::ERR_CROSSING_ON_ONEWAY_ROAD + * @exception AIError::ERR_ALREADY_BUILT + * @return Whether the rail has been/can be build or not. + * @note You can only build a single track with this function so do not + * use the values from RailTrack as bitmask. + */ + static bool BuildRailTrack(TileIndex tile, RailTrack rail_track); + + /** + * Remove rail on the given tile. + * @param tile The tile to remove rail from. + * @param rail_track The RailTrack to remove. + * @pre AIMap::IsValidTile(tile). + * @pre (GetRailTracks(tile) & rail_track) != 0. + * @return Whether the rail has been/can be removed or not. + * @note You can only remove a single track with this function so do not + * use the values from RailTrack as bitmask. + */ + static bool RemoveRailTrack(TileIndex tile, RailTrack rail_track); + + /** + * Check if a tile connects two adjacent tiles. + * @param from The first tile to connect. + * @param tile The tile that is checked. + * @param to The second tile to connect. + * @pre from != to. + * @pre AIMap::DistanceManhattan(from, tile) == 1. + * @pre AIMap::DistanceManhattan(to, tile) == 1. + * @return True if 'tile' connects 'from' and 'to'. + */ + static bool AreTilesConnected(TileIndex from, TileIndex tile, TileIndex to); + + /** + * Build a rail connection between two tiles. + * @param from The tile just before the tile to build on. + * @param tile The first tile to build on. + * @param to The tile just after the last tile to build on. + * @pre from != to. + * @pre AIMap::DistanceManhattan(from, tile) == 1. + * @pre AIMap::DistanceManhattan(to, tile) >= 1. + * @pre (abs(abs(AIMap::GetTileX(to) - AIMap::GetTileX(tile)) - + * abs(AIMap::GetTileY(to) - AIMap::GetTileY(tile))) <= 1) || + * (AIMap::GetTileX(from) == AIMap::GetTileX(tile) && AIMap::GetTileX(tile) == AIMap::GetTileX(to)) || + * (AIMap::GetTileY(from) == AIMap::GetTileY(tile) && AIMap::GetTileY(tile) == AIMap::GetTileY(to)). + * @pre IsRailTypeAvailable(GetCurrentRailType()). + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIError::ERR_LAND_SLOPED_WRONG + * @exception AIRail::ERR_CROSSING_ON_ONEWAY_ROAD + * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS + * @exception AIError::ERR_ALREADY_BUILT + * @return Whether the rail has been/can be build or not. + */ + static bool BuildRail(TileIndex from, TileIndex tile, TileIndex to); + + /** + * Remove a rail connection between two tiles. + * @param from The tile just before the tile to remove rail from. + * @param tile The first tile to remove rail from. + * @param to The tile just after the last tile to remove rail from. + * @pre from != to. + * @pre AIMap::DistanceManhattan(from, tile) == 1. + * @pre AIMap::DistanceManhattan(to, tile) >= 1. + * @pre (abs(abs(AIMap::GetTileX(to) - AIMap::GetTileX(tile)) - + * abs(AIMap::GetTileY(to) - AIMap::GetTileY(tile))) <= 1) || + * (AIMap::GetTileX(from) == AIMap::GetTileX(tile) && AIMap::GetTileX(tile) == AIMap::GetTileX(to)) || + * (AIMap::GetTileY(from) == AIMap::GetTileY(tile) && AIMap::GetTileY(tile) == AIMap::GetTileY(to)). + * @return Whether the rail has been/can be removed or not. + */ + static bool RemoveRail(TileIndex from, TileIndex tile, TileIndex to); + + /** + * Get the SignalType of the signal on a tile or SIGNALTYPE_NONE if there is no signal. + * @pre AIMap::DistanceManhattan(tile, front) == 1. + * @param tile The tile that might have a signal. + * @param front The tile in front of 'tile'. + * @return The SignalType of the signal on 'tile' with it's front to 'front'. + */ + static SignalType GetSignalType(TileIndex tile, TileIndex front); + + /** + * Build a signal on a tile. + * @param tile The tile to build on. + * @param front The tile in front of the signal. + * @param signal The SignalType to build. + * @pre AIMap::DistanceManhattan(tile, front) == 1. + * @pre IsRailTile(tile) && !IsRailStationTile(tile) && !IsRailWaypointTile(tile). + * @exception AIRail::ERR_UNSUITABLE_TRACK + * @return Whether the signal has been/can be build or not. + */ + static bool BuildSignal(TileIndex tile, TileIndex front, SignalType signal); + + /** + * Remove a signal. + * @param tile The tile to remove the signal from. + * @param front The tile in front of the signal. + * @pre AIMap::DistanceManhattan(tile, front) == 1. + * @pre GetSignalType(tile, front) != SIGNALTYPE_NONE. + * @return Whether the signal has been/can be removed or not. + */ + static bool RemoveSignal(TileIndex tile, TileIndex front); +}; + +#endif /* AI_RAIL_HPP */ diff --git a/src/ai/api/ai_rail.hpp.sq b/src/ai/api/ai_rail.hpp.sq new file mode 100644 index 000000000..a77d34c70 --- /dev/null +++ b/src/ai/api/ai_rail.hpp.sq @@ -0,0 +1,93 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_rail.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AIRail::ErrorMessages GetParam(ForceType<AIRail::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIRail::ErrorMessages)tmp; } + template <> int Return<AIRail::ErrorMessages>(HSQUIRRELVM vm, AIRail::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AIRail::RailType GetParam(ForceType<AIRail::RailType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIRail::RailType)tmp; } + template <> int Return<AIRail::RailType>(HSQUIRRELVM vm, AIRail::RailType res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AIRail::RailTrack GetParam(ForceType<AIRail::RailTrack>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIRail::RailTrack)tmp; } + template <> int Return<AIRail::RailTrack>(HSQUIRRELVM vm, AIRail::RailTrack res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AIRail::SignalType GetParam(ForceType<AIRail::SignalType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIRail::SignalType)tmp; } + template <> int Return<AIRail::SignalType>(HSQUIRRELVM vm, AIRail::SignalType res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AIRail to be used as Squirrel parameter */ + template <> AIRail *GetParam(ForceType<AIRail *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIRail *)instance; } + template <> AIRail &GetParam(ForceType<AIRail &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIRail *)instance; } + template <> const AIRail *GetParam(ForceType<const AIRail *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIRail *)instance; } + template <> const AIRail &GetParam(ForceType<const AIRail &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIRail *)instance; } + template <> int Return<AIRail *>(HSQUIRRELVM vm, AIRail *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIRail", res, NULL, DefSQDestructorCallback<AIRail>); return 1; } +}; // namespace SQConvert + +void SQAIRail_Register(Squirrel *engine) { + DefSQClass <AIRail> SQAIRail("AIRail"); + SQAIRail.PreRegister(engine); + SQAIRail.AddConstructor<void (AIRail::*)(), 1>(engine, "x"); + + SQAIRail.DefSQConst(engine, AIRail::ERR_RAIL_BASE, "ERR_RAIL_BASE"); + SQAIRail.DefSQConst(engine, AIRail::ERR_CROSSING_ON_ONEWAY_ROAD, "ERR_CROSSING_ON_ONEWAY_ROAD"); + SQAIRail.DefSQConst(engine, AIRail::ERR_UNSUITABLE_TRACK, "ERR_UNSUITABLE_TRACK"); + SQAIRail.DefSQConst(engine, AIRail::ERR_NONUNIFORM_STATIONS_DISABLED, "ERR_NONUNIFORM_STATIONS_DISABLED"); + SQAIRail.DefSQConst(engine, AIRail::RAILTYPE_INVALID, "RAILTYPE_INVALID"); + SQAIRail.DefSQConst(engine, AIRail::RAILTRACK_NE_SW, "RAILTRACK_NE_SW"); + SQAIRail.DefSQConst(engine, AIRail::RAILTRACK_NW_SE, "RAILTRACK_NW_SE"); + SQAIRail.DefSQConst(engine, AIRail::RAILTRACK_NW_NE, "RAILTRACK_NW_NE"); + SQAIRail.DefSQConst(engine, AIRail::RAILTRACK_SW_SE, "RAILTRACK_SW_SE"); + SQAIRail.DefSQConst(engine, AIRail::RAILTRACK_NW_SW, "RAILTRACK_NW_SW"); + SQAIRail.DefSQConst(engine, AIRail::RAILTRACK_NE_SE, "RAILTRACK_NE_SE"); + SQAIRail.DefSQConst(engine, AIRail::RAILTRACK_INVALID, "RAILTRACK_INVALID"); + SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_NORMAL, "SIGNALTYPE_NORMAL"); + SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_ENTRY, "SIGNALTYPE_ENTRY"); + SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_EXIT, "SIGNALTYPE_EXIT"); + SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_COMBO, "SIGNALTYPE_COMBO"); + SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_PBS, "SIGNALTYPE_PBS"); + SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_PBS_ONEWAY, "SIGNALTYPE_PBS_ONEWAY"); + SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_TWOWAY, "SIGNALTYPE_TWOWAY"); + SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_NORMAL_TWOWAY, "SIGNALTYPE_NORMAL_TWOWAY"); + SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_ENTRY_TWOWAY, "SIGNALTYPE_ENTRY_TWOWAY"); + SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_EXIT_TWOWAY, "SIGNALTYPE_EXIT_TWOWAY"); + SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_COMBO_TWOWAY, "SIGNALTYPE_COMBO_TWOWAY"); + SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_NONE, "SIGNALTYPE_NONE"); + + AIError::RegisterErrorMap(STR_ERR_CROSSING_ON_ONEWAY_ROAD, AIRail::ERR_CROSSING_ON_ONEWAY_ROAD); + AIError::RegisterErrorMap(STR_1005_NO_SUITABLE_RAILROAD_TRACK, AIRail::ERR_UNSUITABLE_TRACK); + AIError::RegisterErrorMap(STR_NONUNIFORM_STATIONS_DISALLOWED, AIRail::ERR_NONUNIFORM_STATIONS_DISABLED); + + AIError::RegisterErrorMapString(AIRail::ERR_CROSSING_ON_ONEWAY_ROAD, "ERR_CROSSING_ON_ONEWAY_ROAD"); + AIError::RegisterErrorMapString(AIRail::ERR_UNSUITABLE_TRACK, "ERR_UNSUITABLE_TRACK"); + AIError::RegisterErrorMapString(AIRail::ERR_NONUNIFORM_STATIONS_DISABLED, "ERR_NONUNIFORM_STATIONS_DISABLED"); + + SQAIRail.DefSQStaticMethod(engine, &AIRail::GetClassName, "GetClassName", 1, "x"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::IsRailTile, "IsRailTile", 2, "xi"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::IsLevelCrossingTile, "IsLevelCrossingTile", 2, "xi"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::IsRailDepotTile, "IsRailDepotTile", 2, "xi"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::IsRailStationTile, "IsRailStationTile", 2, "xi"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::IsRailWaypointTile, "IsRailWaypointTile", 2, "xi"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::IsRailTypeAvailable, "IsRailTypeAvailable", 2, "xi"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::GetCurrentRailType, "GetCurrentRailType", 1, "x"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::SetCurrentRailType, "SetCurrentRailType", 2, "xi"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::TrainCanRunOnRail, "TrainCanRunOnRail", 3, "xii"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::TrainHasPowerOnRail, "TrainHasPowerOnRail", 3, "xii"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::GetRailType, "GetRailType", 2, "xi"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::ConvertRailType, "ConvertRailType", 4, "xiii"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::GetRailDepotFrontTile, "GetRailDepotFrontTile", 2, "xi"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::GetRailStationDirection, "GetRailStationDirection", 2, "xi"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::BuildRailDepot, "BuildRailDepot", 3, "xii"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::BuildRailStation, "BuildRailStation", 6, "xiiiib"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::BuildNewGRFRailStation, "BuildNewGRFRailStation", 11, "xiiiibiiiib"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::RemoveRailStationTileRect, "RemoveRailStationTileRect", 3, "xii"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::GetRailTracks, "GetRailTracks", 2, "xi"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::BuildRailTrack, "BuildRailTrack", 3, "xii"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::RemoveRailTrack, "RemoveRailTrack", 3, "xii"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::AreTilesConnected, "AreTilesConnected", 4, "xiii"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::BuildRail, "BuildRail", 4, "xiii"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::RemoveRail, "RemoveRail", 4, "xiii"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::GetSignalType, "GetSignalType", 3, "xii"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::BuildSignal, "BuildSignal", 4, "xiii"); + SQAIRail.DefSQStaticMethod(engine, &AIRail::RemoveSignal, "RemoveSignal", 3, "xii"); + + SQAIRail.PostRegister(engine); +} diff --git a/src/ai/api/ai_railtypelist.cpp b/src/ai/api/ai_railtypelist.cpp new file mode 100644 index 000000000..494cb5222 --- /dev/null +++ b/src/ai/api/ai_railtypelist.cpp @@ -0,0 +1,14 @@ +/* $Id$ */ + +/** @file ai_railtypelist.cpp Implementation of AIRailTypeList and friends. */ + +#include "ai_railtypelist.hpp" +#include "../../rail.h" +#include "../../company_func.h" + +AIRailTypeList::AIRailTypeList() +{ + for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) { + if (::HasRailtypeAvail(_current_company, rt)) this->AddItem(rt); + } +} diff --git a/src/ai/api/ai_railtypelist.hpp b/src/ai/api/ai_railtypelist.hpp new file mode 100644 index 000000000..f9837d7b6 --- /dev/null +++ b/src/ai/api/ai_railtypelist.hpp @@ -0,0 +1,20 @@ +/* $Id$ */ + +/** @file ai_railtypelist.hpp List all available railtypes. */ + +#ifndef AI_RAILTYPELIST_HPP +#define AI_RAILTYPELIST_HPP + +#include "ai_abstractlist.hpp" + +/** + * Creates a list of all available railtypes. + * @ingroup AIList + */ +class AIRailTypeList : public AIAbstractList { +public: + static const char *GetClassName() { return "AIRailTypeList"; } + AIRailTypeList(); +}; + +#endif /* AI_RAILTYPELIST_HPP */ diff --git a/src/ai/api/ai_railtypelist.hpp.sq b/src/ai/api/ai_railtypelist.hpp.sq new file mode 100644 index 000000000..255124bf6 --- /dev/null +++ b/src/ai/api/ai_railtypelist.hpp.sq @@ -0,0 +1,23 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_railtypelist.hpp" + +namespace SQConvert { + /* Allow AIRailTypeList to be used as Squirrel parameter */ + template <> AIRailTypeList *GetParam(ForceType<AIRailTypeList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIRailTypeList *)instance; } + template <> AIRailTypeList &GetParam(ForceType<AIRailTypeList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIRailTypeList *)instance; } + template <> const AIRailTypeList *GetParam(ForceType<const AIRailTypeList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIRailTypeList *)instance; } + template <> const AIRailTypeList &GetParam(ForceType<const AIRailTypeList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIRailTypeList *)instance; } + template <> int Return<AIRailTypeList *>(HSQUIRRELVM vm, AIRailTypeList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIRailTypeList", res, NULL, DefSQDestructorCallback<AIRailTypeList>); return 1; } +}; // namespace SQConvert + +void SQAIRailTypeList_Register(Squirrel *engine) { + DefSQClass <AIRailTypeList> SQAIRailTypeList("AIRailTypeList"); + SQAIRailTypeList.PreRegister(engine, "AIAbstractList"); + SQAIRailTypeList.AddConstructor<void (AIRailTypeList::*)(), 1>(engine, "x"); + + SQAIRailTypeList.DefSQStaticMethod(engine, &AIRailTypeList::GetClassName, "GetClassName", 1, "x"); + + SQAIRailTypeList.PostRegister(engine); +} diff --git a/src/ai/api/ai_road.cpp b/src/ai/api/ai_road.cpp new file mode 100644 index 000000000..a6a14656c --- /dev/null +++ b/src/ai/api/ai_road.cpp @@ -0,0 +1,548 @@ +/* $Id$ */ + +/** @file ai_road.cpp Implementation of AIRoad. */ + +#include "ai_road.hpp" +#include "ai_map.hpp" +#include "ai_list.hpp" +#include "../../openttd.h" +#include "../../road_map.h" +#include "../../station_map.h" +#include "../../tunnelbridge_map.h" +#include "../../command_type.h" +#include "../../company_func.h" +#include "../../settings_type.h" +#include "../../script/squirrel_helper_type.hpp" + +/* static */ bool AIRoad::IsRoadTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return (::IsTileType(tile, MP_ROAD) && ::GetRoadTileType(tile) != ROAD_TILE_DEPOT) || + IsDriveThroughRoadStationTile(tile); +} + +/* static */ bool AIRoad::IsRoadDepotTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsTileType(tile, MP_ROAD) && ::GetRoadTileType(tile) == ROAD_TILE_DEPOT && + (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0; +} + +/* static */ bool AIRoad::IsRoadStationTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsRoadStopTile(tile) && (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0; +} + +/* static */ bool AIRoad::IsDriveThroughRoadStationTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsDriveThroughStopTile(tile) && (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0; +} + +/* static */ bool AIRoad::IsRoadTypeAvailable(RoadType road_type) +{ + return ::HasRoadTypesAvail(_current_company, ::RoadTypeToRoadTypes((::RoadType)road_type)); +} + +/* static */ AIRoad::RoadType AIRoad::GetCurrentRoadType() +{ + return (RoadType)AIObject::GetRoadType(); +} + +/* static */ void AIRoad::SetCurrentRoadType(RoadType road_type) +{ + if (!IsRoadTypeAvailable(road_type)) return; + + AIObject::SetRoadType((::RoadType)road_type); +} + +/* static */ bool AIRoad::HasRoadType(TileIndex tile, RoadType road_type) +{ + if (!AIMap::IsValidTile(tile)) return false; + if (!IsRoadTypeAvailable(road_type)) return false; + return ::GetAnyRoadBits(tile, (::RoadType)road_type, false) != ROAD_NONE; +} + +/* static */ bool AIRoad::AreRoadTilesConnected(TileIndex t1, TileIndex t2) +{ + if (!::IsValidTile(t1)) return false; + if (!::IsValidTile(t2)) return false; + + /* Tiles not neighbouring */ + if ((abs((int)::TileX(t1) - (int)::TileX(t2)) + abs((int)::TileY(t1) - (int)::TileY(t2))) != 1) return false; + + RoadBits r1 = ::GetAnyRoadBits(t1, AIObject::GetRoadType()); + RoadBits r2 = ::GetAnyRoadBits(t2, AIObject::GetRoadType()); + + uint dir_1 = (::TileX(t1) == ::TileX(t2)) ? (::TileY(t1) < ::TileY(t2) ? 2 : 0) : (::TileX(t1) < ::TileX(t2) ? 1 : 3); + uint dir_2 = 2 ^ dir_1; + + DisallowedRoadDirections drd2 = IsNormalRoadTile(t2) ? GetDisallowedRoadDirections(t2) : DRD_NONE; + + return HasBit(r1, dir_1) && HasBit(r2, dir_2) && drd2 != DRD_BOTH && drd2 != (dir_1 > dir_2 ? DRD_SOUTHBOUND : DRD_NORTHBOUND); +} + +/* Helper functions for AIRoad::CanBuildConnectedRoadParts(). */ + +/** + * Check whether the given existing bits the start and end part can be build. + * As the function assumes the bits being build on a slope that does not + * allow level foundations all of the existing parts will always be in + * a straight line. This also needs to hold for the start and end parts, + * otherwise it is for sure not valid. Finally a check will be done to + * determine whether the existing road parts match the to-be-build parts. + * As they can only be placed in one direction, just checking the start + * part with the first existing part is enough. + * @param existing The existing road parts. + * @param start The part that should be build first. + * @param end The part that will be build second. + * @return True if and only if the road bits can be build. + */ +static bool CheckAutoExpandedRoadBits(const Array *existing, int32 start, int32 end) +{ + return (start + end == 0) && (existing->size == 0 || existing->array[0] == start || existing->array[0] == end); +} + +/** + * Lookup function for building road parts when building on slopes is disabled. + * @param slope The slope of the tile to examine. + * @param existing The existing road parts. + * @param start The part that should be build first. + * @param end The part that will be build second. + * @return 0 when the build parts do not connect, 1 when they do connect once + * they are build or 2 when building the first part automatically + * builds the second part. + */ +static int32 LookupWithoutBuildOnSlopes(::Slope slope, const Array *existing, int32 start, int32 end) +{ + switch (slope) { + /* Flat slopes can always be build. */ + case SLOPE_FLAT: + return 1; + + /* Only 4 of the slopes can be build upon. Testing the existing bits is + * necessary because these bits can be something else when the settings + * in the game have been changed. + */ + case SLOPE_NE: case SLOPE_SW: + return (CheckAutoExpandedRoadBits(existing, start, end) && (start == 1 || end == 1)) ? (existing->size == 0 ? 2 : 1) : 0; + case SLOPE_SE: case SLOPE_NW: + return (CheckAutoExpandedRoadBits(existing, start, end) && (start != 1 && end != 1)) ? (existing->size == 0 ? 2 : 1) : 0; + + /* Any other tile cannot be built on. */ + default: + return 0; + } +} + +/** + * Rotate a neighbour bit a single time clockwise. + * @param neighbour The neighbour. + * @return The rotate neighbour data. + */ +static int32 RotateNeighbour(int32 neighbour) +{ + switch (neighbour) { + case -2: return -1; + case -1: return 2; + case 1: return -2; + case 2: return 1; + default: NOT_REACHED(); + } +} + +/** + * Convert a neighbour to a road bit representation for easy internal use. + * @param neighbour The neighbour. + * @return The bits representing the direction. + */ +static RoadBits NeighbourToRoadBits(int32 neighbour) +{ + switch (neighbour) { + case -2: return ROAD_NW; + case -1: return ROAD_NE; + case 2: return ROAD_SE; + case 1: return ROAD_SW; + default: NOT_REACHED(); + } +} + +/** + * Lookup function for building road parts when building on slopes is enabled. + * @param slope The slope of the tile to examine. + * @param existing The existing neighbours. + * @param start The part that should be build first. + * @param end The part that will be build second. + * @return 0 when the build parts do not connect, 1 when they do connect once + * they are build or 2 when building the first part automatically + * builds the second part. + */ +static int32 LookupWithBuildOnSlopes(::Slope slope, Array *existing, int32 start, int32 end) +{ + if (::IsSteepSlope(slope)) { + switch (slope) { + /* On steep slopes one can only build straight roads that will be + * automatically expanded to a straight road. Just check that the existing + * road parts are in the same direction. */ + case SLOPE_STEEP_S: + case SLOPE_STEEP_W: + case SLOPE_STEEP_N: + case SLOPE_STEEP_E: + return CheckAutoExpandedRoadBits(existing, start, end) ? (existing->size == 0 ? 2 : 1) : 0; + + /* All other slopes are invalid slopes!. */ + default: + return -1; + } + } + + /* The slope is not steep. Furthermore lots of slopes are generally the + * same but are only rotated. So to reduce the amount of lookup work that + * needs to be done the data is made uniform. This means rotating the + * existing parts and updating the slope. */ + static const ::Slope base_slopes[] = { + SLOPE_FLAT, SLOPE_W, SLOPE_W, SLOPE_SW, + SLOPE_W, SLOPE_EW, SLOPE_SW, SLOPE_WSE, + SLOPE_W, SLOPE_SW, SLOPE_EW, SLOPE_WSE, + SLOPE_SW, SLOPE_WSE, SLOPE_WSE}; + static const byte base_rotates[] = {0, 0, 1, 0, 2, 0, 1, 0, 3, 3, 2, 3, 2, 2, 1}; + + if (slope >= (::Slope)lengthof(base_slopes)) { + /* This slope is an invalid slope, so ignore it. */ + return -1; + } + byte base_rotate = base_rotates[slope]; + slope = base_slopes[slope]; + + /* Some slopes don't need rotating, so return early when we know we do + * not need to rotate. */ + switch (slope) { + case SLOPE_FLAT: + /* Flat slopes can always be build. */ + return 1; + + case SLOPE_EW: + case SLOPE_WSE: + /* A slope similar to a SLOPE_EW or SLOPE_WSE will always cause + * foundations which makes them accessible from all sides. */ + return 1; + + case SLOPE_W: + case SLOPE_SW: + /* A slope for which we need perform some calculations. */ + break; + + default: + /* An invalid slope. */ + return -1; + } + + /* Now perform the actual rotation. */ + for (int j = 0; j < base_rotate; j++) { + for (int i = 0; i < existing->size; i++) { + existing->array[i] = RotateNeighbour(existing->array[i]); + } + start = RotateNeighbour(start); + end = RotateNeighbour(end); + } + + /* Create roadbits out of the data for easier handling. */ + RoadBits start_roadbits = NeighbourToRoadBits(start); + RoadBits new_roadbits = start_roadbits | NeighbourToRoadBits(end); + RoadBits existing_roadbits = ROAD_NONE; + for (int i = 0; i < existing->size; i++) { + existing_roadbits |= NeighbourToRoadBits(existing->array[i]); + } + + switch (slope) { + case SLOPE_W: + /* A slope similar to a SLOPE_W. */ + switch (new_roadbits) { + case 6: // ROAD_SE | ROAD_SW: + case 9: // ROAD_NE | ROAD_NW: + case 12: // ROAD_NE | ROAD_SE: + /* Cannot build anything with a turn from the low side. */ + return 0; + + case 5: // ROAD_SE | ROAD_NW: + case 10: // ROAD_NE | ROAD_SW: + /* A 'sloped' tile is going to be build. */ + if ((existing_roadbits | new_roadbits) != new_roadbits) { + /* There is already a foundation on the tile, or at least + * another slope that is not compatible with the new one. */ + return 0; + } + /* If the start is in the low part, it is automatically + * building the second part too. */ + return ((start_roadbits & (ROAD_NE | ROAD_SE)) && !(existing_roadbits & (ROAD_SW | ROAD_NW))) ? 2 : 1; + + default: + /* Roadbits causing a foundation are going to be build. + * When the existing roadbits are slopes (the lower bits + * are used), this cannot be done. */ + if ((existing_roadbits | new_roadbits) == new_roadbits) return 1; + return (existing_roadbits & (ROAD_NE | ROAD_SE)) ? 0 : 1; + } + + case SLOPE_SW: + /* A slope similar to a SLOPE_SW. */ + switch (new_roadbits) { + case 9: // ROAD_NE | ROAD_NW: + case 12: // ROAD_NE | ROAD_SE: + /* Cannot build anything with a turn from the low side. */ + return 0; + + case 10: // ROAD_NE | ROAD_SW: + /* A 'sloped' tile is going to be build. */ + if ((existing_roadbits | new_roadbits) != new_roadbits) { + /* There is already a foundation on the tile, or at least + * another slope that is not compatible with the new one. */ + return 0; + } + /* If the start is in the low part, it is automatically + * building the second part too. */ + return ((start_roadbits & ROAD_NE) && !(existing_roadbits & ROAD_SW)) ? 2 : 1; + + default: + /* Roadbits causing a foundation are going to be build. + * When the existing roadbits are slopes (the lower bits + * are used), this cannot be done. */ + return (existing_roadbits & ROAD_NE) ? 0 : 1; + } + + default: + NOT_REACHED(); + } +} + +/** + * Normalise all input data so we can easily handle it without needing + * to call the API lots of times or create large if-elseif-elseif-else + * constructs. + * In this case it means that a TileXY(0, -1) becomes -2 and TileXY(0, 1) + * becomes 2. TileXY(-1, 0) and TileXY(1, 0) stay respectively -1 and 1. + * Any other value means that it is an invalid tile offset. + * @param tile The tile to normalise. + * @return True if and only if the tile offset is valid. + */ +static bool NormaliseTileOffset(int32 *tile) +{ + if (*tile == 1 || *tile == -1) return true; + if (*tile == ::TileDiffXY(0, -1)) { + *tile = -2; + return true; + } + if (*tile == ::TileDiffXY(0, 1)) { + *tile = 2; + return true; + } + return false; +} + +/* static */ int32 AIRoad::CanBuildConnectedRoadParts(AITile::Slope slope_, Array *existing, TileIndex start_, TileIndex end_) +{ + ::Slope slope = (::Slope)slope_; + int32 start = start_; + int32 end = end_; + + /* The start tile and end tile cannot be the same tile either. */ + if (start == end) return -1; + + for (int i = 0; i < existing->size; i++) { + if (!NormaliseTileOffset(&existing->array[i])) return -1; + } + + if (!NormaliseTileOffset(&start)) return -1; + if (!NormaliseTileOffset(&end)) return -1; + + /* Without build on slopes the characteristics are vastly different, so use + * a different helper function (one that is much simpler). */ + return _settings_game.construction.build_on_slopes ? LookupWithBuildOnSlopes(slope, existing, start, end) : LookupWithoutBuildOnSlopes(slope, existing, start, end); +} + +/* static */ int32 AIRoad::CanBuildConnectedRoadPartsHere(TileIndex tile, TileIndex start, TileIndex end) +{ + if (!::IsValidTile(tile) || !::IsValidTile(start) || !::IsValidTile(end)) return -1; + if (::DistanceManhattan(tile, start) != 1 || ::DistanceManhattan(tile, end) != 1) return -1; + + /* ROAD_NW ROAD_SW ROAD_SE ROAD_NE */ + static const TileIndex neighbours[] = {::TileDiffXY(0, -1), ::TileDiffXY(1, 0), ::TileDiffXY(0, 1), ::TileDiffXY(-1, 0)}; + Array *existing = (Array*)alloca(sizeof(Array) + lengthof(neighbours) * sizeof(int32)); + existing->size = 0; + + ::RoadBits rb = ::ROAD_NONE; + if (::IsNormalRoadTile(tile)) { + rb = ::GetAllRoadBits(tile); + } else { + for (::RoadType rt = ::ROADTYPE_BEGIN; rt < ::ROADTYPE_END; rt++) rb |= ::GetAnyRoadBits(tile, rt); + } + for (uint i = 0; i < lengthof(neighbours); i++) { + if (HasBit(rb, i)) existing->array[existing->size++] = neighbours[i]; + } + + return AIRoad::CanBuildConnectedRoadParts(AITile::GetSlope(tile), existing, start - tile, end - tile); +} + +/** + * Check whether one can reach (possibly by building) a road piece the center + * of the neighbouring tile. This includes roads and (drive through) stations. + * @param start_tile The tile to "enter" the neighbouring tile. + * @param neighbour The direction to the neighbouring tile to "enter". + * @return true if and only if the tile is reachable. + */ +static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, DiagDirection neighbour) +{ + TileIndex neighbour_tile = ::TileAddByDiagDir(start_tile, neighbour); + if ((rts & ::GetRoadTypes(neighbour_tile)) == 0) return false; + + switch (::GetTileType(neighbour_tile)) { + case MP_ROAD: + return (::GetRoadTileType(neighbour_tile) != ROAD_TILE_DEPOT); + + case MP_STATION: + if (::IsDriveThroughStopTile(neighbour_tile)) { + return (::DiagDirToAxis(neighbour) == ::DiagDirToAxis(::GetRoadStopDir(neighbour_tile))); + } + return false; + + default: + return false; + } +} + +/* static */ int32 AIRoad::GetNeighbourRoadCount(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + ::RoadTypes rts = ::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()); + int32 neighbour = 0; + + if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_NE)) neighbour++; + if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SE)) neighbour++; + if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SW)) neighbour++; + if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_NW)) neighbour++; + + return neighbour; +} + +/* static */ TileIndex AIRoad::GetRoadDepotFrontTile(TileIndex depot) +{ + if (!IsRoadDepotTile(depot)) return INVALID_TILE; + + return depot + ::TileOffsByDiagDir(::GetRoadDepotDirection(depot)); +} + +/* static */ TileIndex AIRoad::GetRoadStationFrontTile(TileIndex station) +{ + if (!IsRoadStationTile(station)) return INVALID_TILE; + + return station + ::TileOffsByDiagDir(::GetRoadStopDir(station)); +} + +/* static */ TileIndex AIRoad::GetDriveThroughBackTile(TileIndex station) +{ + if (!IsDriveThroughRoadStationTile(station)) return INVALID_TILE; + + return station + ::TileOffsByDiagDir(::ReverseDiagDir(::GetRoadStopDir(station))); +} + +/* static */ bool AIRoad::_BuildRoadInternal(TileIndex start, TileIndex end, bool one_way, bool full) +{ + EnforcePrecondition(false, start != end); + EnforcePrecondition(false, ::IsValidTile(start)); + EnforcePrecondition(false, ::IsValidTile(end)); + EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end)); + EnforcePrecondition(false, !one_way || AIObject::GetRoadType() == ::ROADTYPE_ROAD); + + return AIObject::DoCommand(end, start, (::TileY(start) != ::TileY(end) ? 4 : 0) | (((start < end) == !full) ? 1 : 2) | (AIObject::GetRoadType() << 3) | ((one_way ? 1 : 0) << 5), CMD_BUILD_LONG_ROAD); +} + +/* static */ bool AIRoad::BuildRoad(TileIndex start, TileIndex end) +{ + return _BuildRoadInternal(start, end, false, false); +} + +/* static */ bool AIRoad::BuildOneWayRoad(TileIndex start, TileIndex end) +{ + return _BuildRoadInternal(start, end, true, false); +} + +/* static */ bool AIRoad::BuildRoadFull(TileIndex start, TileIndex end) +{ + return _BuildRoadInternal(start, end, false, true); +} + +/* static */ bool AIRoad::BuildOneWayRoadFull(TileIndex start, TileIndex end) +{ + return _BuildRoadInternal(start, end, true, true); +} + +/* static */ bool AIRoad::BuildRoadDepot(TileIndex tile, TileIndex front) +{ + EnforcePrecondition(false, tile != front); + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, ::IsValidTile(front)); + EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front)); + + uint entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0); + + return AIObject::DoCommand(tile, entrance_dir | (AIObject::GetRoadType() << 2), 0, CMD_BUILD_ROAD_DEPOT); +} + +/* static */ bool AIRoad::BuildRoadStation(TileIndex tile, TileIndex front, bool truck, bool drive_through, bool join_adjacent) +{ + EnforcePrecondition(false, tile != front); + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, ::IsValidTile(front)); + EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front)); + + uint entrance_dir; + if (drive_through) { + entrance_dir = ::TileY(tile) != ::TileY(front); + } else { + entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0); + } + + return AIObject::DoCommand(tile, entrance_dir, (join_adjacent ? 0 : 32) | (drive_through ? 2 : 0) | (truck ? 1 : 0) | (::RoadTypeToRoadTypes(AIObject::GetRoadType()) << 2) | (INVALID_STATION << 16), CMD_BUILD_ROAD_STOP); +} + +/* static */ bool AIRoad::RemoveRoad(TileIndex start, TileIndex end) +{ + EnforcePrecondition(false, ::IsValidTile(start)); + EnforcePrecondition(false, ::IsValidTile(end)); + EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end)); + + return AIObject::DoCommand(end, start, (::TileY(start) != ::TileY(end) ? 4 : 0) | (start < end ? 1 : 2) | (AIObject::GetRoadType() << 3), CMD_REMOVE_LONG_ROAD); +} + +/* static */ bool AIRoad::RemoveRoadFull(TileIndex start, TileIndex end) +{ + EnforcePrecondition(false, ::IsValidTile(start)); + EnforcePrecondition(false, ::IsValidTile(end)); + EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end)); + + return AIObject::DoCommand(end, start, (::TileY(start) != ::TileY(end) ? 4 : 0) | (start < end ? 2 : 1) | (AIObject::GetRoadType() << 3), CMD_REMOVE_LONG_ROAD); +} + +/* static */ bool AIRoad::RemoveRoadDepot(TileIndex tile) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, IsTileType(tile, MP_ROAD)) + EnforcePrecondition(false, GetRoadTileType(tile) == ROAD_TILE_DEPOT); + + return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR); +} + +/* static */ bool AIRoad::RemoveRoadStation(TileIndex tile) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, IsTileType(tile, MP_STATION)); + EnforcePrecondition(false, IsRoadStop(tile)); + + return AIObject::DoCommand(tile, 0, GetRoadStopType(tile), CMD_REMOVE_ROAD_STOP); +} diff --git a/src/ai/api/ai_road.hpp b/src/ai/api/ai_road.hpp new file mode 100644 index 000000000..c80f63177 --- /dev/null +++ b/src/ai/api/ai_road.hpp @@ -0,0 +1,408 @@ +/* $Id$ */ + +/** @file ai_road.hpp Everything to query and build roads. */ + +#ifndef AI_ROAD_HPP +#define AI_ROAD_HPP + +#include "ai_object.hpp" +#include "ai_error.hpp" +#include "ai_tile.hpp" + +/** + * Class that handles all road related functions. + */ +class AIRoad : public AIObject { +public: + static const char *GetClassName() { return "AIRoad"; } + + /** + * All road related error messages. + */ + enum ErrorMessages { + /** Base for road building / maintaining errors */ + ERR_ROAD_BASE = AIError::ERR_CAT_ROAD << AIError::ERR_CAT_BIT_SIZE, + + /** Road works are in progress */ + ERR_ROAD_WORKS_IN_PROGRESS, // [STR_ROAD_WORKS_IN_PROGRESS] + + /** Drive through is in the wrong direction */ + ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, // [STR_DRIVE_THROUGH_ERROR_DIRECTION] + + /** Drive through roads can't be build on town owned roads */ + ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, // [STR_DRIVE_THROUGH_ERROR_ON_TOWN_ROAD] + + + /** One way roads can't have junctions */ + ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, // [STR_ERR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION] + }; + + /** + * Types of road known to the game. + */ + enum RoadType { + /* Values are important, as they represent the internal state of the game. */ + ROADTYPE_ROAD = 0, //!< Build road objects. + ROADTYPE_TRAM = 1, //!< Build tram objects. + + ROADTYPE_INVALID = -1, //!< Invalid RoadType. + }; + + /** + * Checks whether the given tile is actually a tile with road that can be + * used to traverse a tile. This excludes road depots and 'normal' road + * stations, but includes drive through stations. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has road. + */ + static bool IsRoadTile(TileIndex tile); + + /** + * Checks whether the given tile is actually a tile with a road depot. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has a road depot. + */ + static bool IsRoadDepotTile(TileIndex tile); + + /** + * Checks whether the given tile is actually a tile with a road station. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has a road station. + */ + static bool IsRoadStationTile(TileIndex tile); + + /** + * Checks whether the given tile is actually a tile with a drive through + * road station. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile has a drive through road station. + */ + static bool IsDriveThroughRoadStationTile(TileIndex tile); + + /** + * Check if a given RoadType is available. + * @param road_type The RoadType to check for. + * @return True if this RoadType can be used. + */ + static bool IsRoadTypeAvailable(RoadType road_type); + + /** + * Get the current RoadType set for all AIRoad functions. + * @return The RoadType currently set. + */ + static RoadType GetCurrentRoadType(); + + /** + * Set the RoadType for all further AIRoad functions. + * @param road_type The RoadType to set. + */ + static void SetCurrentRoadType(RoadType road_type); + + /** + * Check if a given tile has RoadType. + * @param tile The tile to check. + * @param road_type The RoadType to check for. + * @pre AIMap::IsValidTile(tile). + * @pre IsRoadTypeAvailable(road_type). + * @return True if the tile contains a RoadType object. + */ + static bool HasRoadType(TileIndex tile, RoadType road_type); + + /** + * Checks whether the given tiles are directly connected, i.e. whether + * a road vehicle can travel from the center of the first tile to the + * center of the second tile. + * @param tile_from The source tile. + * @param tile_to The destination tile. + * @pre AIMap::IsValidTile(tile_from). + * @pre AIMap::IsValidTile(tile_to). + * @pre 'tile_from' and 'tile_to' are directly neighbouring tiles. + * @return True if and only if a road vehicle can go from tile_from to tile_to. + */ + static bool AreRoadTilesConnected(TileIndex tile_from, TileIndex tile_to); + + /** + * Lookup function for building road parts independend on whether the + * "building on slopes" setting is enabled or not. + * This implementation can be used for abstract reasoning about a tile as + * it needs the slope and existing road parts of the tile as information. + * @param slope The slope of the tile to examine. + * @param existing An array with the existing neighbours in the same format + * as "start" and "end", e.g. AIMap.GetTileIndex(0, 1). + * As a result of this all values of the existing array + * must be of type integer. + * @param start The tile from where the 'tile to be considered' will be + * entered. This is a relative tile, so valid parameters are: + * AIMap.GetTileIndex(0, 1), AIMap.GetTileIndex(0, -1), + * AIMap.GetTileIndex(1, 0) and AIMap.GetTileIndex(-1, 0). + * @param end The tile from where the 'tile to be considered' will be + * exited. This is a relative tile, sovalid parameters are: + * AIMap.GetTileIndex(0, 1), AIMap.GetTileIndex(0, -1), + * AIMap.GetTileIndex(1, 0) and AIMap.GetTileIndex(-1, 0). + * @pre start != end. + * @pre slope must be a valid slope, i.e. one specified in AITile::Slope. + * @note Passing data that would be invalid in-game, e.g. existing containing + * road parts that can not be build on a tile with the given slope, + * does not necessarily means that -1 is returned, i.e. not all + * preconditions written here or assumed by the game are extensively + * checked to make sure the data entered is valid. + * @return 0 when the build parts do not connect, 1 when they do connect once + * they are build or 2 when building the first part automatically + * builds the second part. -1 means the preconditions are not met. + */ + static int32 CanBuildConnectedRoadParts(AITile::Slope slope, struct Array *existing, TileIndex start, TileIndex end); + + /** + * Lookup function for building road parts independend on whether the + * "building on slopes" setting is enabled or not. + * This implementation can be used for reasoning about an existing tile. + * @param tile The the tile to examine. + * @param start The tile from where "tile" will be entered. + * @param end The tile from where "tile" will be exited. + * @pre start != end. + * @pre tile != start. + * @pre tile != end. + * @pre AIMap.IsValidTile(tile). + * @pre AIMap.IsValidTile(start). + * @pre AIMap.IsValidTile(end). + * @pre AIMap.GetDistanceManhattanToTile(tile, start) == 1. + * @pre AIMap.GetDistanceManhattanToTile(tile, end) == 1. + * @return 0 when the build parts do not connect, 1 when they do connect once + * they are build or 2 when building the first part automatically + * builds the second part. -1 means the preconditions are not met. + */ + static int32 CanBuildConnectedRoadPartsHere(TileIndex tile, TileIndex start, TileIndex end); + + /** + * Count how many neighbours are road. + * @param tile The tile to check on. + * @pre AIMap::IsValidTile(tile). + * @return 0 means no neighbour road; max value is 4. + */ + static int32 GetNeighbourRoadCount(TileIndex tile); + + /** + * Gets the tile in front of a road depot. + * @param depot The road depot tile. + * @pre IsRoadDepotTile(depot). + * @return The tile in front of the depot. + */ + static TileIndex GetRoadDepotFrontTile(TileIndex depot); + + /** + * Gets the tile in front of a road station. + * @param station The road station tile. + * @pre IsRoadStationTile(station). + * @return The tile in front of the road station. + */ + static TileIndex GetRoadStationFrontTile(TileIndex station); + + /** + * Gets the tile at the back of a drive through road station. + * So, one side of the drive through station is retrieved with + * GetTileInFrontOfStation, the other with this function. + * @param station The road station tile. + * @pre IsDriveThroughRoadStationTile(station). + * @return The tile at the back of the drive through road station. + */ + static TileIndex GetDriveThroughBackTile(TileIndex station); + + /** + * Builds a road from the center of tile start to the center of tile end. + * @param start The start tile of the road. + * @param end The end tile of the road. + * @pre 'start' is not equal to 'end'. + * @pre AIMap::IsValidTile(start). + * @pre AIMap::IsValidTile(end). + * @pre 'start' and 'end' are in a straight line, i.e. + * AIMap::GetTileX(start) == AIMap::GetTileX(end) or + * AIMap::GetTileY(start) == AIMap::GetTileY(end). + * @exception AIError::ERR_ALREADY_BUILT + * @exception AIError::ERR_LAND_SLOPED_WRONG + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS + * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS + * @exception AIError::ERR_VEHICLE_IN_THE_WAY + * @return Whether the road has been/can be build or not. + */ + static bool BuildRoad(TileIndex start, TileIndex end); + + /** + * Builds a one-way road from the center of tile start to the center + * of tile end. If the road already exists, it is made one-way road. + * If the road already exists and is already one-way in this direction, + * the road is made two-way again. If the road already exists but is + * one-way in the other direction, it's made a 'no'-way road (it's + * forbidden to enter the tile from any direction). + * @param start The start tile of the road. + * @param end The end tile of the road. + * @pre 'start' is not equal to 'end'. + * @pre AIMap::IsValidTile(start). + * @pre AIMap::IsValidTile(end). + * @pre 'start' and 'end' are in a straight line, i.e. + * AIMap::GetTileX(start) == AIMap::GetTileX(end) or + * AIMap::GetTileY(start) == AIMap::GetTileY(end). + * @pre GetCurrentRoadType() == ROADTYPE_ROAD. + * @exception AIError::ERR_ALREADY_BUILT + * @exception AIError::ERR_LAND_SLOPED_WRONG + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS + * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS + * @exception AIError::ERR_VEHICLE_IN_THE_WAY + * @return Whether the road has been/can be build or not. + */ + static bool BuildOneWayRoad(TileIndex start, TileIndex end); + + /** + * Builds a road from the edge of tile start to the edge of tile end (both + * included). + * @param start The start tile of the road. + * @param end The end tile of the road. + * @pre 'start' is not equal to 'end'. + * @pre AIMap::IsValidTile(start). + * @pre AIMap::IsValidTile(end). + * @pre 'start' and 'end' are in a straight line, i.e. + * AIMap::GetTileX(start) == AIMap::GetTileX(end) or + * AIMap::GetTileY(start) == AIMap::GetTileY(end). + * @exception AIError::ERR_ALREADY_BUILT + * @exception AIError::ERR_LAND_SLOPED_WRONG + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS + * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS + * @exception AIError::ERR_VEHICLE_IN_THE_WAY + * @return Whether the road has been/can be build or not. + */ + static bool BuildRoadFull(TileIndex start, TileIndex end); + + /** + * Builds a one-way road from the edge of tile start to the edge of tile end + * (both included). If the road already exists, it is made one-way road. + * If the road already exists and is already one-way in this direction, + * the road is made two-way again. If the road already exists but is + * one-way in the other direction, it's made a 'no'-way road (it's + * forbidden to enter the tile from any direction). + * @param start The start tile of the road. + * @param start The start tile of the road. + * @param end The end tile of the road. + * @pre 'start' is not equal to 'end'. + * @pre AIMap::IsValidTile(start). + * @pre AIMap::IsValidTile(end). + * @pre 'start' and 'end' are in a straight line, i.e. + * AIMap::GetTileX(start) == AIMap::GetTileX(end) or + * AIMap::GetTileY(start) == AIMap::GetTileY(end). + * @pre GetCurrentRoadType() == ROADTYPE_ROAD. + * @exception AIError::ERR_ALREADY_BUILT + * @exception AIError::ERR_LAND_SLOPED_WRONG + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS + * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS + * @exception AIError::ERR_VEHICLE_IN_THE_WAY + * @return Whether the road has been/can be build or not. + */ + static bool BuildOneWayRoadFull(TileIndex start, TileIndex end); + + /** + * Builds a road depot. + * @param tile Place to build the depot. + * @param front The tile exactly in front of the depot. + * @pre AIMap::IsValidTile(tile). + * @pre AIMap::IsValidTile(front). + * @pre 'tile' is not equal to 'front', but in a straight line of it. + * @exception AIError::ERR_FLAT_LAND_REQUIRED + * @exception AIError::ERR_AREA_NOT_CLEAR + * @return Whether the road depot has been/can be build or not. + */ + static bool BuildRoadDepot(TileIndex tile, TileIndex front); + + /** + * Builds a road bus or truck station. + * @param tile Place to build the depot. + * @param front The tile exactly in front of the station. + * For drive-through stations either entrance side can be used. + * @param truck Whether to build a truck (true) or bus (false) station. + * @param drive_through Whether to make the station drive through or not. + * @param join_adjacent When building next to an other station, don't create a new station when this flag is true. + * @pre AIMap::IsValidTile(tile). + * @pre AIMap::IsValidTile(front). + * @pre 'tile' is not equal to 'front', but in a straight line of it. + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIError::ERR_FLAT_LAND_REQUIRED + * @exception AIRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION + * @exception AIRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD + * @exception AIError:ERR_VEHICLE_IN_THE_WAY + * @exception AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION + * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS + * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN + * @return Whether the station has been/can be build or not. + */ + static bool BuildRoadStation(TileIndex tile, TileIndex front, bool truck, bool drive_through, bool join_adjacent); + + /** + * Removes a road from the center of tile start to the center of tile end. + * @param start The start tile of the road. + * @param end The end tile of the road. + * @pre AIMap::IsValidTile(start). + * @pre AIMap::IsValidTile(end). + * @pre 'start' and 'end' are in a straight line, i.e. + * AIMap::GetTileX(start) == AIMap::GetTileX(end) or + * AIMap::GetTileY(start) == AIMap::GetTileY(end). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @exception AIError::ERR_VEHICLE_IN_THE_WAY + * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS + * @return Whether the road has been/can be removed or not. + */ + static bool RemoveRoad(TileIndex start, TileIndex end); + + /** + * Removes a road from the edge of tile start to the edge of tile end (both + * included). + * @param start The start tile of the road. + * @param end The end tile of the road. + * @pre AIMap::IsValidTile(start). + * @pre AIMap::IsValidTile(end). + * @pre 'start' and 'end' are in a straight line, i.e. + * AIMap::GetTileX(start) == AIMap::GetTileX(end) or + * AIMap::GetTileY(start) == AIMap::GetTileY(end). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @exception AIError::ERR_VEHICLE_IN_THE_WAY + * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS + * @return Whether the road has been/can be removed or not. + */ + static bool RemoveRoadFull(TileIndex start, TileIndex end); + + /** + * Removes a road depot. + * @param tile Place to remove the depot from. + * @pre AIMap::IsValidTile(tile). + * @pre Tile is a road depot. + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @exception AIError::ERR_VEHICLE_IN_THE_WAY + * @return Whether the road depot has been/can be removed or not. + */ + static bool RemoveRoadDepot(TileIndex tile); + + /** + * Removes a road bus or truck station. + * @param tile Place to remove the station from. + * @pre AIMap::IsValidTile(tile). + * @pre Tile is a road station. + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @exception AIError::ERR_VEHICLE_IN_THE_WAY + * @return Whether the station has been/can be removed or not. + */ + static bool RemoveRoadStation(TileIndex tile); + +private: + + /** + * Internal function used by Build(OneWay)Road(Full). + */ + static bool _BuildRoadInternal(TileIndex start, TileIndex end, bool one_way, bool full); +}; + +#endif /* AI_ROAD_HPP */ diff --git a/src/ai/api/ai_road.hpp.sq b/src/ai/api/ai_road.hpp.sq new file mode 100644 index 000000000..6439ae193 --- /dev/null +++ b/src/ai/api/ai_road.hpp.sq @@ -0,0 +1,73 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_road.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AIRoad::ErrorMessages GetParam(ForceType<AIRoad::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIRoad::ErrorMessages)tmp; } + template <> int Return<AIRoad::ErrorMessages>(HSQUIRRELVM vm, AIRoad::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AIRoad::RoadType GetParam(ForceType<AIRoad::RoadType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIRoad::RoadType)tmp; } + template <> int Return<AIRoad::RoadType>(HSQUIRRELVM vm, AIRoad::RoadType res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AIRoad to be used as Squirrel parameter */ + template <> AIRoad *GetParam(ForceType<AIRoad *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIRoad *)instance; } + template <> AIRoad &GetParam(ForceType<AIRoad &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIRoad *)instance; } + template <> const AIRoad *GetParam(ForceType<const AIRoad *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIRoad *)instance; } + template <> const AIRoad &GetParam(ForceType<const AIRoad &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIRoad *)instance; } + template <> int Return<AIRoad *>(HSQUIRRELVM vm, AIRoad *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIRoad", res, NULL, DefSQDestructorCallback<AIRoad>); return 1; } +}; // namespace SQConvert + +void SQAIRoad_Register(Squirrel *engine) { + DefSQClass <AIRoad> SQAIRoad("AIRoad"); + SQAIRoad.PreRegister(engine); + SQAIRoad.AddConstructor<void (AIRoad::*)(), 1>(engine, "x"); + + SQAIRoad.DefSQConst(engine, AIRoad::ERR_ROAD_BASE, "ERR_ROAD_BASE"); + SQAIRoad.DefSQConst(engine, AIRoad::ERR_ROAD_WORKS_IN_PROGRESS, "ERR_ROAD_WORKS_IN_PROGRESS"); + SQAIRoad.DefSQConst(engine, AIRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, "ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION"); + SQAIRoad.DefSQConst(engine, AIRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, "ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD"); + SQAIRoad.DefSQConst(engine, AIRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, "ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS"); + SQAIRoad.DefSQConst(engine, AIRoad::ROADTYPE_ROAD, "ROADTYPE_ROAD"); + SQAIRoad.DefSQConst(engine, AIRoad::ROADTYPE_TRAM, "ROADTYPE_TRAM"); + SQAIRoad.DefSQConst(engine, AIRoad::ROADTYPE_INVALID, "ROADTYPE_INVALID"); + + AIError::RegisterErrorMap(STR_ROAD_WORKS_IN_PROGRESS, AIRoad::ERR_ROAD_WORKS_IN_PROGRESS); + AIError::RegisterErrorMap(STR_DRIVE_THROUGH_ERROR_DIRECTION, AIRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION); + AIError::RegisterErrorMap(STR_DRIVE_THROUGH_ERROR_ON_TOWN_ROAD, AIRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD); + AIError::RegisterErrorMap(STR_ERR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION, AIRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS); + + AIError::RegisterErrorMapString(AIRoad::ERR_ROAD_WORKS_IN_PROGRESS, "ERR_ROAD_WORKS_IN_PROGRESS"); + AIError::RegisterErrorMapString(AIRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, "ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION"); + AIError::RegisterErrorMapString(AIRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, "ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD"); + AIError::RegisterErrorMapString(AIRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, "ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS"); + + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::GetClassName, "GetClassName", 1, "x"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::IsRoadTile, "IsRoadTile", 2, "xi"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::IsRoadDepotTile, "IsRoadDepotTile", 2, "xi"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::IsRoadStationTile, "IsRoadStationTile", 2, "xi"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::IsDriveThroughRoadStationTile, "IsDriveThroughRoadStationTile", 2, "xi"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::IsRoadTypeAvailable, "IsRoadTypeAvailable", 2, "xi"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::GetCurrentRoadType, "GetCurrentRoadType", 1, "x"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::SetCurrentRoadType, "SetCurrentRoadType", 2, "xi"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::HasRoadType, "HasRoadType", 3, "xii"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::AreRoadTilesConnected, "AreRoadTilesConnected", 3, "xii"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::CanBuildConnectedRoadParts, "CanBuildConnectedRoadParts", 5, "xiaii"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::CanBuildConnectedRoadPartsHere, "CanBuildConnectedRoadPartsHere", 4, "xiii"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::GetNeighbourRoadCount, "GetNeighbourRoadCount", 2, "xi"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::GetRoadDepotFrontTile, "GetRoadDepotFrontTile", 2, "xi"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::GetRoadStationFrontTile, "GetRoadStationFrontTile", 2, "xi"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::GetDriveThroughBackTile, "GetDriveThroughBackTile", 2, "xi"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::BuildRoad, "BuildRoad", 3, "xii"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::BuildOneWayRoad, "BuildOneWayRoad", 3, "xii"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::BuildRoadFull, "BuildRoadFull", 3, "xii"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::BuildOneWayRoadFull, "BuildOneWayRoadFull", 3, "xii"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::BuildRoadDepot, "BuildRoadDepot", 3, "xii"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::BuildRoadStation, "BuildRoadStation", 6, "xiibbb"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::RemoveRoad, "RemoveRoad", 3, "xii"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::RemoveRoadFull, "RemoveRoadFull", 3, "xii"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::RemoveRoadDepot, "RemoveRoadDepot", 2, "xi"); + SQAIRoad.DefSQStaticMethod(engine, &AIRoad::RemoveRoadStation, "RemoveRoadStation", 2, "xi"); + + SQAIRoad.PostRegister(engine); +} diff --git a/src/ai/api/ai_sign.cpp b/src/ai/api/ai_sign.cpp new file mode 100644 index 000000000..561b5c857 --- /dev/null +++ b/src/ai/api/ai_sign.cpp @@ -0,0 +1,73 @@ +/* $Id$ */ + +/** @file ai_sign.cpp Implementation of AISign. */ + +#include "ai_sign.hpp" +#include "table/strings.h" +#include "../ai_instance.hpp" +#include "../../openttd.h" +#include "../../command_func.h" +#include "../../core/alloc_func.hpp" +#include "../../signs_base.h" +#include "../../string_func.h" +#include "../../strings_func.h" +#include "../../tile_map.h" +#include "../../company_func.h" + +/* static */ SignID AISign::GetMaxSignID() +{ + return ::GetMaxSignIndex(); +} + +/* static */ bool AISign::IsValidSign(SignID sign_id) +{ + return ::IsValidSignID(sign_id) && ::GetSign(sign_id)->owner == _current_company; +} + +/* static */ bool AISign::SetName(SignID sign_id, const char *name) +{ + EnforcePrecondition(false, IsValidSign(sign_id)); + EnforcePrecondition(false, !::StrEmpty(name)); + EnforcePreconditionCustomError(false, ::strlen(name) < MAX_LENGTH_SIGN_NAME_BYTES, AIError::ERR_PRECONDITION_STRING_TOO_LONG); + + return AIObject::DoCommand(0, sign_id, 0, CMD_RENAME_SIGN, name); +} + +/* static */ const char *AISign::GetName(SignID sign_id) +{ + if (!IsValidSign(sign_id)) return NULL; + + static const int len = 64; + char *sign_name = MallocT<char>(len); + + ::SetDParam(0, sign_id); + ::GetString(sign_name, STR_SIGN_NAME, &sign_name[len - 1]); + + return sign_name; +} + +/* static */ TileIndex AISign::GetLocation(SignID sign_id) +{ + if (!IsValidSign(sign_id)) return INVALID_TILE; + + const Sign *sign = ::GetSign(sign_id); + return ::TileVirtXY(sign->x, sign->y); +} + +/* static */ bool AISign::RemoveSign(SignID sign_id) +{ + EnforcePrecondition(false, IsValidSign(sign_id)); + return AIObject::DoCommand(0, sign_id, 0, CMD_RENAME_SIGN, ""); +} + +/* static */ SignID AISign::BuildSign(TileIndex location, const char *text) +{ + EnforcePrecondition(INVALID_SIGN, ::IsValidTile(location)); + EnforcePrecondition(INVALID_SIGN, !::StrEmpty(text)); + EnforcePreconditionCustomError(false, ::strlen(text) < MAX_LENGTH_SIGN_NAME_BYTES, AIError::ERR_PRECONDITION_STRING_TOO_LONG); + + if (!AIObject::DoCommand(location, 0, 0, CMD_PLACE_SIGN, text, &AIInstance::DoCommandReturnSignID)) return INVALID_SIGN; + + /* In case of test-mode, we return SignID 0 */ + return 0; +} diff --git a/src/ai/api/ai_sign.hpp b/src/ai/api/ai_sign.hpp new file mode 100644 index 000000000..8bf7e0b79 --- /dev/null +++ b/src/ai/api/ai_sign.hpp @@ -0,0 +1,96 @@ +/* $Id$ */ + +/** @file ai_sign.hpp Everything to query and build signs. */ + +#ifndef AI_SIGN_HPP +#define AI_SIGN_HPP + +#include "ai_object.hpp" +#include "ai_error.hpp" +#include "ai_company.hpp" + +/** + * Class that handles all sign related functions. + */ +class AISign : public AIObject { +public: + static const char *GetClassName() { return "AISign"; } + + /** + * All sign related error messages. + */ + enum ErrorMessages { + + /** Base for sign building related errors */ + ERR_SIGN_BASE = AIError::ERR_CAT_SIGN << AIError::ERR_CAT_BIT_SIZE, + + /** Too many signs have been placed */ + ERR_SIGN_TOO_MANY_SIGNS, // [STR_2808_TOO_MANY_SIGNS] + }; + + /** + * Gets the maximum sign index; there are no valid signs with a higher index. + * @return The maximum sign index. + * @post Return value is always non-negative. + */ + static SignID GetMaxSignID(); + + /** + * Checks whether the given sign index is valid. + * @param sign_id The index to check. + * @return True if and only if the sign is valid. + */ + static bool IsValidSign(SignID sign_id); + + /** + * Set the name of a sign. + * @param sign_id The sign to set the name for. + * @param name The name for the sign. + * @pre IsValidSign(sign_id). + * @pre 'name' must have at least one character. + * @pre 'name' must have at most 30 characters. + * @exception AIError::ERR_NAME_IS_NOT_UNIQUE + * @return True if and only if the name was changed. + */ + static bool SetName(SignID sign_id, const char *name); + + /** + * Get the name of the sign. + * @param sign_id The sign to get the name of. + * @pre IsValidSign(sign_id). + * @return The name of the sign. + */ + static const char *GetName(SignID sign_id); + + /** + * Gets the location of the sign. + * @param sign_id The sign to get the location of. + * @pre IsValidSign(sign_id). + * @return The location of the sign. + */ + static TileIndex GetLocation(SignID sign_id); + + /** + * Builds a sign on the map. + * @param location The place to build the sign. + * @param text The text to place on the sign. + * @pre AIMap::IsValidTile(location). + * @pre 'text' must have at least one character. + * @pre 'text' must have at most 30 characters. + * @exception AISign::ERR_SIGN_TOO_MANY_SIGNS + * @return The SignID of the build sign (use IsValidSign() to check for validity). + * In test-mode it returns 0 if successful, or any other value to indicate + * failure. + */ + static SignID BuildSign(TileIndex location, const char *text); + + /** + * Removes a sign from the map. + * @param sign_id The sign to remove. + * @pre IsValidSign(sign_id). + * @return True if and only if the sign has been removed. + */ + static bool RemoveSign(SignID sign_id); +}; + +#endif /* AI_SIGN_HPP */ diff --git a/src/ai/api/ai_sign.hpp.sq b/src/ai/api/ai_sign.hpp.sq new file mode 100644 index 000000000..4a46e1bd8 --- /dev/null +++ b/src/ai/api/ai_sign.hpp.sq @@ -0,0 +1,41 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_sign.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AISign::ErrorMessages GetParam(ForceType<AISign::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AISign::ErrorMessages)tmp; } + template <> int Return<AISign::ErrorMessages>(HSQUIRRELVM vm, AISign::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AISign to be used as Squirrel parameter */ + template <> AISign *GetParam(ForceType<AISign *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AISign *)instance; } + template <> AISign &GetParam(ForceType<AISign &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AISign *)instance; } + template <> const AISign *GetParam(ForceType<const AISign *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AISign *)instance; } + template <> const AISign &GetParam(ForceType<const AISign &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AISign *)instance; } + template <> int Return<AISign *>(HSQUIRRELVM vm, AISign *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AISign", res, NULL, DefSQDestructorCallback<AISign>); return 1; } +}; // namespace SQConvert + +void SQAISign_Register(Squirrel *engine) { + DefSQClass <AISign> SQAISign("AISign"); + SQAISign.PreRegister(engine); + SQAISign.AddConstructor<void (AISign::*)(), 1>(engine, "x"); + + SQAISign.DefSQConst(engine, AISign::ERR_SIGN_BASE, "ERR_SIGN_BASE"); + SQAISign.DefSQConst(engine, AISign::ERR_SIGN_TOO_MANY_SIGNS, "ERR_SIGN_TOO_MANY_SIGNS"); + + AIError::RegisterErrorMap(STR_2808_TOO_MANY_SIGNS, AISign::ERR_SIGN_TOO_MANY_SIGNS); + + AIError::RegisterErrorMapString(AISign::ERR_SIGN_TOO_MANY_SIGNS, "ERR_SIGN_TOO_MANY_SIGNS"); + + SQAISign.DefSQStaticMethod(engine, &AISign::GetClassName, "GetClassName", 1, "x"); + SQAISign.DefSQStaticMethod(engine, &AISign::GetMaxSignID, "GetMaxSignID", 1, "x"); + SQAISign.DefSQStaticMethod(engine, &AISign::IsValidSign, "IsValidSign", 2, "xi"); + SQAISign.DefSQStaticMethod(engine, &AISign::SetName, "SetName", 3, "xis"); + SQAISign.DefSQStaticMethod(engine, &AISign::GetName, "GetName", 2, "xi"); + SQAISign.DefSQStaticMethod(engine, &AISign::GetLocation, "GetLocation", 2, "xi"); + SQAISign.DefSQStaticMethod(engine, &AISign::BuildSign, "BuildSign", 3, "xis"); + SQAISign.DefSQStaticMethod(engine, &AISign::RemoveSign, "RemoveSign", 2, "xi"); + + SQAISign.PostRegister(engine); +} diff --git a/src/ai/api/ai_station.cpp b/src/ai/api/ai_station.cpp new file mode 100644 index 000000000..ab171a307 --- /dev/null +++ b/src/ai/api/ai_station.cpp @@ -0,0 +1,146 @@ +/* $Id$ */ + +/** @file ai_station.cpp Implementation of AIStation. */ + +#include "ai_station.hpp" +#include "ai_cargo.hpp" +#include "ai_map.hpp" +#include "ai_town.hpp" +#include "../../openttd.h" +#include "../../command_func.h" +#include "../../debug.h" +#include "../../station_map.h" +#include "../../variables.h" +#include "../../string_func.h" +#include "../../strings_func.h" +#include "../../core/alloc_func.hpp" +#include "../../company_func.h" +#include "../../settings_type.h" +#include "../../town.h" +#include "table/strings.h" + +/* static */ bool AIStation::IsValidStation(StationID station_id) +{ + return ::IsValidStationID(station_id) && ::GetStation(station_id)->owner == _current_company; +} + +/* static */ StationID AIStation::GetStationID(TileIndex tile) +{ + if (!::IsTileType(tile, MP_STATION)) return INVALID_STATION; + return ::GetStationIndex(tile); +} + +/* static */ const char *AIStation::GetName(StationID station_id) +{ + if (!IsValidStation(station_id)) return NULL; + + static const int len = 64; + char *station_name = MallocT<char>(len); + + ::SetDParam(0, GetStation(station_id)->index); + ::GetString(station_name, STR_STATION, &station_name[len - 1]); + return station_name; +} + +/* static */ bool AIStation::SetName(StationID station_id, const char *name) +{ + EnforcePrecondition(false, IsValidStation(station_id)); + EnforcePrecondition(false, !::StrEmpty(name)); + EnforcePreconditionCustomError(false, ::strlen(name) < MAX_LENGTH_STATION_NAME_BYTES, AIError::ERR_PRECONDITION_STRING_TOO_LONG); + + return AIObject::DoCommand(0, station_id, 0, CMD_RENAME_STATION, name); +} + +/* static */ TileIndex AIStation::GetLocation(StationID station_id) +{ + if (!IsValidStation(station_id)) return INVALID_TILE; + + return ::GetStation(station_id)->xy; +} + +/* static */ int32 AIStation::GetCargoWaiting(StationID station_id, CargoID cargo_id) +{ + if (!IsValidStation(station_id)) return -1; + if (!AICargo::IsValidCargo(cargo_id)) return -1; + + return ::GetStation(station_id)->goods[cargo_id].cargo.Count(); +} + +/* static */ int32 AIStation::GetCargoRating(StationID station_id, CargoID cargo_id) +{ + if (!IsValidStation(station_id)) return -1; + if (!AICargo::IsValidCargo(cargo_id)) return -1; + + return ::GetStation(station_id)->goods[cargo_id].rating * 101 >> 8; +} + +/* static */ int32 AIStation::GetCoverageRadius(AIStation::StationType station_type) +{ + if (station_type == STATION_AIRPORT) { + DEBUG(ai, 0, "GetCoverageRadius(): coverage radius of airports needs to be requested via AIAirport::GetAirportCoverageRadius(), as it requires AirportType"); + return -1; + } + if (CountBits(station_type) != 1) return -1; + if (!_settings_game.station.modified_catchment) return CA_UNMODIFIED; + + switch (station_type) { + case STATION_TRAIN: return CA_TRAIN; + case STATION_TRUCK_STOP: return CA_TRUCK; + case STATION_BUS_STOP: return CA_BUS; + case STATION_DOCK: return CA_DOCK; + default: return CA_NONE; + } +} + +/* static */ int32 AIStation::GetDistanceManhattanToTile(StationID station_id, TileIndex tile) +{ + if (!IsValidStation(station_id)) return -1; + + return AIMap::DistanceManhattan(tile, GetLocation(station_id)); +} + +/* static */ int32 AIStation::GetDistanceSquareToTile(StationID station_id, TileIndex tile) +{ + if (!IsValidStation(station_id)) return -1; + + return AIMap::DistanceSquare(tile, GetLocation(station_id)); +} + +/* static */ bool AIStation::IsWithinTownInfluence(StationID station_id, TownID town_id) +{ + if (!IsValidStation(station_id)) return false; + + return AITown::IsWithinTownInfluence(town_id, GetLocation(station_id)); +} + +/* static */ bool AIStation::HasStationType(StationID station_id, StationType station_type) +{ + if (!IsValidStation(station_id)) return false; + if (CountBits(station_type) != 1) return false; + + return (::GetStation(station_id)->facilities & station_type) != 0; +} + +/* static */ bool AIStation::HasRoadType(StationID station_id, AIRoad::RoadType road_type) +{ + if (!IsValidStation(station_id)) return false; + if (!AIRoad::IsRoadTypeAvailable(road_type)) return false; + + ::RoadTypes r = RoadTypeToRoadTypes((::RoadType)road_type); + + for (const RoadStop *rs = ::GetStation(station_id)->GetPrimaryRoadStop(ROADSTOP_BUS); rs != NULL; rs = rs->next) { + if ((::GetRoadTypes(rs->xy) & r) != 0) return true; + } + for (const RoadStop *rs = ::GetStation(station_id)->GetPrimaryRoadStop(ROADSTOP_TRUCK); rs != NULL; rs = rs->next) { + if ((::GetRoadTypes(rs->xy) & r) != 0) return true; + } + + return false; +} + +/* static */ TownID AIStation::GetNearestTown(StationID station_id) +{ + if (!IsValidStation(station_id)) return INVALID_TOWN; + + return ::GetStation(station_id)->town->index; +} diff --git a/src/ai/api/ai_station.hpp b/src/ai/api/ai_station.hpp new file mode 100644 index 000000000..6e263c912 --- /dev/null +++ b/src/ai/api/ai_station.hpp @@ -0,0 +1,184 @@ +/* $Id$ */ + +/** @file ai_station.hpp Everything to query and build stations. */ + +#ifndef AI_STATION_HPP +#define AI_STATION_HPP + +#include "ai_object.hpp" +#include "ai_error.hpp" +#include "ai_road.hpp" + +/** + * Class that handles all station related functions. + */ +class AIStation : public AIObject { +public: + static const char *GetClassName() { return "AIStation"; } + + /** + * All station related error messages. + */ + enum ErrorMessages { + /** Base for station related errors */ + ERR_STATION_BASE = AIError::ERR_CAT_STATION << AIError::ERR_CAT_BIT_SIZE, + + /** The station size exceeds the station spread */ + ERR_STATION_TOO_LARGE, // [STR_306C_STATION_TOO_SPREAD_OUT] + + /** The station is build too close to another station, airport or dock */ + ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION, // [STR_300D_TOO_CLOSE_TO_ANOTHER_AIRPORT, STR_3009_TOO_CLOSE_TO_ANOTHER_STATION, STR_304C_TOO_CLOSE_TO_ANOTHER_DOCK] + + /** There are too many stations, airports and docks in the game */ + ERR_STATION_TOO_MANY_STATIONS, // [STR_3008_TOO_MANY_STATIONS_LOADING, STR_TOO_MANY_TRUCK_STOPS, STR_TOO_MANY_BUS_STOPS] + + /** There are too many stations, airports of docks in a town */ + ERR_STATION_TOO_MANY_STATIONS_IN_TOWN, // [STR_3007_TOO_MANY_STATIONS_LOADING] + }; + + /** + * Type of stations known in the game. + */ + enum StationType { + /* Values are important, as they represent the internal state of the game. */ + STATION_TRAIN = 0x01, //!< Train station + STATION_TRUCK_STOP = 0x02, //!< Truck station + STATION_BUS_STOP = 0x04, //!< Bus station + STATION_AIRPORT = 0x08, //!< Airport + STATION_DOCK = 0x10, //!< Dock + STATION_ANY = 0x1F, //!< All station types + }; + + /** + * Checks whether the given station is valid and owned by you. + * @param station_id The station to check. + * @return True if and only if the station is valid. + */ + static bool IsValidStation(StationID station_id); + + /** + * Get the StationID of a tile, if there is a station. + * @param tile The tile to find the stationID of + * @return StationID of the station. + * @post Use IsValidStation() to see if the station is valid. + */ + static StationID GetStationID(TileIndex tile); + + /** + * Get the name of a station. + * @param station_id The station to get the name of. + * @pre IsValidStation(station_id). + * @return The name of the station. + */ + static const char *GetName(StationID station_id); + + /** + * Set the name this station. + * @param station_id The station to set the name of. + * @param name The new name of the station. + * @pre IsValidStation(station_id). + * @pre 'name' must have at least one character. + * @pre 'name' must have at most 30 characters. + * @exception AIError::ERR_NAME_IS_NOT_UNIQUE + * @return True if the name was changed. + */ + static bool SetName(StationID station_id, const char *name); + + /** + * Get the current location of a station. + * @param station_id The station to get the location of. + * @pre IsValidStation(station_id). + * @return The tile the station is currently on. + */ + static TileIndex GetLocation(StationID station_id); + + /** + * See how much cargo there is waiting on a station. + * @param station_id The station to get the cargo-waiting of. + * @param cargo_id The cargo to get the cargo-waiting of. + * @pre IsValidStation(station_id). + * @pre IsValidCargo(cargo_id). + * @return The amount of units waiting at the station. + */ + static int32 GetCargoWaiting(StationID station_id, CargoID cargo_id); + + /** + * See how high the rating is of a cargo on a station. + * @param station_id The station to get the cargo-rating of. + * @param cargo_id The cargo to get the cargo-rating of. + * @pre IsValidStation(station_id). + * @pre IsValidCargo(cargo_id). + * @return The rating in percent of the cargo on the station. + */ + static int32 GetCargoRating(StationID station_id, CargoID cargo_id); + + /** + * Get the coverage radius of this type of station. + * @param station_type The type of station. + * @return The radius in tiles. + */ + static int32 GetCoverageRadius(AIStation::StationType station_type); + + /** + * Get the manhattan distance from the tile to the AIStation::GetLocation() + * of the station. + * @param station_id The station to get the distance to. + * @param tile The tile to get the distance to. + * @pre IsValidStation(station_id). + * @return The distance between station and tile. + */ + static int32 GetDistanceManhattanToTile(StationID station_id, TileIndex tile); + + /** + * Get the square distance from the tile to the AIStation::GetLocation() + * of the station. + * @param station_id The station to get the distance to. + * @param tile The tile to get the distance to. + * @pre IsValidStation(station_id). + * @return The distance between station and tile. + */ + static int32 GetDistanceSquareToTile(StationID station_id, TileIndex tile); + + /** + * Find out if this station is within the rating influence of a town. + * Stations within the radius influence the rating of the town. + * @param station_id The station to check. + * @param town_id The town to check. + * @return True if the tile is within the rating influence of the town. + */ + static bool IsWithinTownInfluence(StationID station_id, TownID town_id); + + /** + * Check if any part of the station contains a station of the type + * StationType + * @param station_id The station to look at. + * @param station_type The StationType to look for. + * @return True if the station has a station part of the type StationType. + */ + static bool HasStationType(StationID station_id, StationType station_type); + + /** + * Check if any part of the station contains a station of the type + * RoadType. + * @param station_id The station to look at. + * @param road_type The RoadType to look for. + * @return True if the station has a station part of the type RoadType. + */ + static bool HasRoadType(StationID station_id, AIRoad::RoadType road_type); + + /** + * Get the town that was nearest to the given station when the station was built. + * @param station_id The station to look at. + * @return The TownID of the town whose center tile was closest to the station + * at the time the station was built. + * @note There is no guarantee that the station is even near the returned town + * nor that the returns town is closest to the station now. A station that was + * 'walked' to the other end of the map will still return the same town. Also, + * towns grow, towns change. So don't depend on this value too much. + */ + static TownID GetNearestTown(StationID station_id); +}; + +DECLARE_ENUM_AS_BIT_SET(AIStation::StationType); + +#endif /* AI_STATION_HPP */ diff --git a/src/ai/api/ai_station.hpp.sq b/src/ai/api/ai_station.hpp.sq new file mode 100644 index 000000000..f752d8d9a --- /dev/null +++ b/src/ai/api/ai_station.hpp.sq @@ -0,0 +1,69 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_station.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AIStation::ErrorMessages GetParam(ForceType<AIStation::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIStation::ErrorMessages)tmp; } + template <> int Return<AIStation::ErrorMessages>(HSQUIRRELVM vm, AIStation::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AIStation::StationType GetParam(ForceType<AIStation::StationType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIStation::StationType)tmp; } + template <> int Return<AIStation::StationType>(HSQUIRRELVM vm, AIStation::StationType res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AIStation to be used as Squirrel parameter */ + template <> AIStation *GetParam(ForceType<AIStation *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIStation *)instance; } + template <> AIStation &GetParam(ForceType<AIStation &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIStation *)instance; } + template <> const AIStation *GetParam(ForceType<const AIStation *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIStation *)instance; } + template <> const AIStation &GetParam(ForceType<const AIStation &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIStation *)instance; } + template <> int Return<AIStation *>(HSQUIRRELVM vm, AIStation *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIStation", res, NULL, DefSQDestructorCallback<AIStation>); return 1; } +}; // namespace SQConvert + +void SQAIStation_Register(Squirrel *engine) { + DefSQClass <AIStation> SQAIStation("AIStation"); + SQAIStation.PreRegister(engine); + SQAIStation.AddConstructor<void (AIStation::*)(), 1>(engine, "x"); + + SQAIStation.DefSQConst(engine, AIStation::ERR_STATION_BASE, "ERR_STATION_BASE"); + SQAIStation.DefSQConst(engine, AIStation::ERR_STATION_TOO_LARGE, "ERR_STATION_TOO_LARGE"); + SQAIStation.DefSQConst(engine, AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION, "ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION"); + SQAIStation.DefSQConst(engine, AIStation::ERR_STATION_TOO_MANY_STATIONS, "ERR_STATION_TOO_MANY_STATIONS"); + SQAIStation.DefSQConst(engine, AIStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN, "ERR_STATION_TOO_MANY_STATIONS_IN_TOWN"); + SQAIStation.DefSQConst(engine, AIStation::STATION_TRAIN, "STATION_TRAIN"); + SQAIStation.DefSQConst(engine, AIStation::STATION_TRUCK_STOP, "STATION_TRUCK_STOP"); + SQAIStation.DefSQConst(engine, AIStation::STATION_BUS_STOP, "STATION_BUS_STOP"); + SQAIStation.DefSQConst(engine, AIStation::STATION_AIRPORT, "STATION_AIRPORT"); + SQAIStation.DefSQConst(engine, AIStation::STATION_DOCK, "STATION_DOCK"); + SQAIStation.DefSQConst(engine, AIStation::STATION_ANY, "STATION_ANY"); + + AIError::RegisterErrorMap(STR_306C_STATION_TOO_SPREAD_OUT, AIStation::ERR_STATION_TOO_LARGE); + AIError::RegisterErrorMap(STR_300D_TOO_CLOSE_TO_ANOTHER_AIRPORT, AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION); + AIError::RegisterErrorMap(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION, AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION); + AIError::RegisterErrorMap(STR_304C_TOO_CLOSE_TO_ANOTHER_DOCK, AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION); + AIError::RegisterErrorMap(STR_3008_TOO_MANY_STATIONS_LOADING, AIStation::ERR_STATION_TOO_MANY_STATIONS); + AIError::RegisterErrorMap(STR_TOO_MANY_TRUCK_STOPS, AIStation::ERR_STATION_TOO_MANY_STATIONS); + AIError::RegisterErrorMap(STR_TOO_MANY_BUS_STOPS, AIStation::ERR_STATION_TOO_MANY_STATIONS); + AIError::RegisterErrorMap(STR_3007_TOO_MANY_STATIONS_LOADING, AIStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN); + + AIError::RegisterErrorMapString(AIStation::ERR_STATION_TOO_LARGE, "ERR_STATION_TOO_LARGE"); + AIError::RegisterErrorMapString(AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION, "ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION"); + AIError::RegisterErrorMapString(AIStation::ERR_STATION_TOO_MANY_STATIONS, "ERR_STATION_TOO_MANY_STATIONS"); + AIError::RegisterErrorMapString(AIStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN, "ERR_STATION_TOO_MANY_STATIONS_IN_TOWN"); + + SQAIStation.DefSQStaticMethod(engine, &AIStation::GetClassName, "GetClassName", 1, "x"); + SQAIStation.DefSQStaticMethod(engine, &AIStation::IsValidStation, "IsValidStation", 2, "xi"); + SQAIStation.DefSQStaticMethod(engine, &AIStation::GetStationID, "GetStationID", 2, "xi"); + SQAIStation.DefSQStaticMethod(engine, &AIStation::GetName, "GetName", 2, "xi"); + SQAIStation.DefSQStaticMethod(engine, &AIStation::SetName, "SetName", 3, "xis"); + SQAIStation.DefSQStaticMethod(engine, &AIStation::GetLocation, "GetLocation", 2, "xi"); + SQAIStation.DefSQStaticMethod(engine, &AIStation::GetCargoWaiting, "GetCargoWaiting", 3, "xii"); + SQAIStation.DefSQStaticMethod(engine, &AIStation::GetCargoRating, "GetCargoRating", 3, "xii"); + SQAIStation.DefSQStaticMethod(engine, &AIStation::GetCoverageRadius, "GetCoverageRadius", 2, "xi"); + SQAIStation.DefSQStaticMethod(engine, &AIStation::GetDistanceManhattanToTile, "GetDistanceManhattanToTile", 3, "xii"); + SQAIStation.DefSQStaticMethod(engine, &AIStation::GetDistanceSquareToTile, "GetDistanceSquareToTile", 3, "xii"); + SQAIStation.DefSQStaticMethod(engine, &AIStation::IsWithinTownInfluence, "IsWithinTownInfluence", 3, "xii"); + SQAIStation.DefSQStaticMethod(engine, &AIStation::HasStationType, "HasStationType", 3, "xii"); + SQAIStation.DefSQStaticMethod(engine, &AIStation::HasRoadType, "HasRoadType", 3, "xii"); + SQAIStation.DefSQStaticMethod(engine, &AIStation::GetNearestTown, "GetNearestTown", 2, "xi"); + + SQAIStation.PostRegister(engine); +} diff --git a/src/ai/api/ai_stationlist.cpp b/src/ai/api/ai_stationlist.cpp new file mode 100644 index 000000000..43cb33878 --- /dev/null +++ b/src/ai/api/ai_stationlist.cpp @@ -0,0 +1,29 @@ +/* $Id$ */ + +/** @file ai_stationlist.cpp Implementation of AIStationList and friends. */ + +#include "ai_stationlist.hpp" +#include "ai_vehicle.hpp" +#include "../../openttd.h" +#include "../../company_func.h" +#include "../../station_base.h" +#include "../../vehicle_base.h" + +AIStationList::AIStationList(AIStation::StationType station_type) +{ + Station *st; + FOR_ALL_STATIONS(st) { + if (st->owner == _current_company && (st->facilities & station_type) != 0) this->AddItem(st->index); + } +} + +AIStationList_Vehicle::AIStationList_Vehicle(VehicleID vehicle_id) +{ + if (!AIVehicle::IsValidVehicle(vehicle_id)) return; + + Vehicle *v = ::GetVehicle(vehicle_id); + + for (Order *o = v->GetFirstOrder(); o != NULL; o = o->next) { + if (o->IsType(OT_GOTO_STATION)) this->AddItem(o->GetDestination()); + } +} diff --git a/src/ai/api/ai_stationlist.hpp b/src/ai/api/ai_stationlist.hpp new file mode 100644 index 000000000..b6b1a7d1f --- /dev/null +++ b/src/ai/api/ai_stationlist.hpp @@ -0,0 +1,39 @@ +/* $Id$ */ + +/** @file ai_stationlist.hpp List all the stations (you own). */ + +#ifndef AI_STATIONLIST_HPP +#define AI_STATIONLIST_HPP + +#include "ai_abstractlist.hpp" +#include "ai_station.hpp" + +/** + * Creates a list of stations of which you are the owner. + * @ingroup AIList + */ +class AIStationList : public AIAbstractList { +public: + static const char *GetClassName() { return "AIStationList"; } + + /** + * @param station_type The type of station to make a list of stations for. + */ + AIStationList(AIStation::StationType station_type); +}; + +/** + * Creates a list of stations which the vehicle has in its orders. + * @ingroup AIList + */ +class AIStationList_Vehicle : public AIAbstractList { +public: + static const char *GetClassName() { return "AIStationList_Vehicle"; } + + /** + * @param vehicle_id The vehicle to get the list of stations he has in its orders from. + */ + AIStationList_Vehicle(VehicleID vehicle_id); +}; + +#endif /* AI_STATIONLIST_HPP */ diff --git a/src/ai/api/ai_stationlist.hpp.sq b/src/ai/api/ai_stationlist.hpp.sq new file mode 100644 index 000000000..d3e5bcd4c --- /dev/null +++ b/src/ai/api/ai_stationlist.hpp.sq @@ -0,0 +1,42 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_stationlist.hpp" + +namespace SQConvert { + /* Allow AIStationList to be used as Squirrel parameter */ + template <> AIStationList *GetParam(ForceType<AIStationList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIStationList *)instance; } + template <> AIStationList &GetParam(ForceType<AIStationList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIStationList *)instance; } + template <> const AIStationList *GetParam(ForceType<const AIStationList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIStationList *)instance; } + template <> const AIStationList &GetParam(ForceType<const AIStationList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIStationList *)instance; } + template <> int Return<AIStationList *>(HSQUIRRELVM vm, AIStationList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIStationList", res, NULL, DefSQDestructorCallback<AIStationList>); return 1; } +}; // namespace SQConvert + +void SQAIStationList_Register(Squirrel *engine) { + DefSQClass <AIStationList> SQAIStationList("AIStationList"); + SQAIStationList.PreRegister(engine, "AIAbstractList"); + SQAIStationList.AddConstructor<void (AIStationList::*)(AIStation::StationType station_type), 2>(engine, "xi"); + + SQAIStationList.DefSQStaticMethod(engine, &AIStationList::GetClassName, "GetClassName", 1, "x"); + + SQAIStationList.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIStationList_Vehicle to be used as Squirrel parameter */ + template <> AIStationList_Vehicle *GetParam(ForceType<AIStationList_Vehicle *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIStationList_Vehicle *)instance; } + template <> AIStationList_Vehicle &GetParam(ForceType<AIStationList_Vehicle &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIStationList_Vehicle *)instance; } + template <> const AIStationList_Vehicle *GetParam(ForceType<const AIStationList_Vehicle *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIStationList_Vehicle *)instance; } + template <> const AIStationList_Vehicle &GetParam(ForceType<const AIStationList_Vehicle &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIStationList_Vehicle *)instance; } + template <> int Return<AIStationList_Vehicle *>(HSQUIRRELVM vm, AIStationList_Vehicle *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIStationList_Vehicle", res, NULL, DefSQDestructorCallback<AIStationList_Vehicle>); return 1; } +}; // namespace SQConvert + +void SQAIStationList_Vehicle_Register(Squirrel *engine) { + DefSQClass <AIStationList_Vehicle> SQAIStationList_Vehicle("AIStationList_Vehicle"); + SQAIStationList_Vehicle.PreRegister(engine, "AIAbstractList"); + SQAIStationList_Vehicle.AddConstructor<void (AIStationList_Vehicle::*)(VehicleID vehicle_id), 2>(engine, "xi"); + + SQAIStationList_Vehicle.DefSQStaticMethod(engine, &AIStationList_Vehicle::GetClassName, "GetClassName", 1, "x"); + + SQAIStationList_Vehicle.PostRegister(engine); +} diff --git a/src/ai/api/ai_subsidy.cpp b/src/ai/api/ai_subsidy.cpp new file mode 100644 index 000000000..893b65e7d --- /dev/null +++ b/src/ai/api/ai_subsidy.cpp @@ -0,0 +1,94 @@ +/* $Id$ */ + +/** @file ai_subsidy.cpp Implementation of AISubsidy. */ + +#include "ai_subsidy.hpp" +#include "ai_error.hpp" +#include "ai_company.hpp" +#include "ai_date.hpp" +#include "../../openttd.h" +#include "../../economy_func.h" +#include "../../station_base.h" +#include "../../cargotype.h" + +/* static */ bool AISubsidy::IsValidSubsidy(SubsidyID subsidy_id) +{ + return subsidy_id < lengthof(_subsidies) && _subsidies[subsidy_id].cargo_type != CT_INVALID; +} + +/* static */ bool AISubsidy::IsAwarded(SubsidyID subsidy_id) +{ + if (!IsValidSubsidy(subsidy_id)) return false; + + return _subsidies[subsidy_id].age >= 12; +} + +/* static */ AICompany::CompanyID AISubsidy::GetAwardedTo(SubsidyID subsidy_id) +{ + if (!IsAwarded(subsidy_id)) return AICompany::INVALID_COMPANY; + + return (AICompany::CompanyID)((byte)GetStation(_subsidies[subsidy_id].from)->owner); +} + +/* static */ int32 AISubsidy::GetExpireDate(SubsidyID subsidy_id) +{ + if (!IsValidSubsidy(subsidy_id)) return -1; + + int year = AIDate::GetYear(AIDate::GetCurrentDate()); + int month = AIDate::GetMonth(AIDate::GetCurrentDate()); + + if (IsAwarded(subsidy_id)) { + month += 24 - _subsidies[subsidy_id].age; + } else { + month += 12 - _subsidies[subsidy_id].age; + } + + year += (month - 1) / 12; + month = ((month - 1) % 12) + 1; + + return AIDate::GetDate(year, month, 1); +} + +/* static */ CargoID AISubsidy::GetCargoType(SubsidyID subsidy_id) +{ + if (!IsValidSubsidy(subsidy_id)) return CT_INVALID; + + return _subsidies[subsidy_id].cargo_type; +} + +/* static */ bool AISubsidy::SourceIsTown(SubsidyID subsidy_id) +{ + if (!IsValidSubsidy(subsidy_id) || IsAwarded(subsidy_id)) return false; + + return GetCargo(GetCargoType(subsidy_id))->town_effect == TE_PASSENGERS || + GetCargo(GetCargoType(subsidy_id))->town_effect == TE_MAIL; +} + +/* static */ int32 AISubsidy::GetSource(SubsidyID subsidy_id) +{ + if (!IsValidSubsidy(subsidy_id)) return INVALID_STATION; + + return _subsidies[subsidy_id].from; +} + +/* static */ bool AISubsidy::DestinationIsTown(SubsidyID subsidy_id) +{ + if (!IsValidSubsidy(subsidy_id) || IsAwarded(subsidy_id)) return false; + + switch (GetCargo(GetCargoType(subsidy_id))->town_effect) { + case TE_PASSENGERS: + case TE_MAIL: + case TE_GOODS: + case TE_FOOD: + return true; + default: + return false; + } +} + +/* static */ int32 AISubsidy::GetDestination(SubsidyID subsidy_id) +{ + if (!IsValidSubsidy(subsidy_id)) return INVALID_STATION; + + return _subsidies[subsidy_id].to; +} diff --git a/src/ai/api/ai_subsidy.hpp b/src/ai/api/ai_subsidy.hpp new file mode 100644 index 000000000..7be86b0e8 --- /dev/null +++ b/src/ai/api/ai_subsidy.hpp @@ -0,0 +1,101 @@ +/* $Id$ */ + +/** @file ai_subsidy.hpp Everything to query subsidies. */ + +#ifndef AI_SUBSIDY_HPP +#define AI_SUBSIDY_HPP + +#include "ai_object.hpp" +#include "ai_company.hpp" + +/** + * Class that handles all subsidy related functions. + */ +class AISubsidy : public AIObject { +public: + static const char *GetClassName() { return "AISubsidy"; } + + /** + * Check whether this is a valid SubsidyID. + * @param subsidy_id The SubsidyID to check. + * @return True if and only if this subsidy is still valid. + */ + static bool IsValidSubsidy(SubsidyID subsidy_id); + + /** + * Checks whether this subsidy is already awarded to some company. + * @param subsidy_id The SubsidyID to check. + * @pre IsValidSubsidy(subsidy). + * @return True if and only if this subsidy is already awarded. + */ + static bool IsAwarded(SubsidyID subsidy_id); + + /** + * Get the company index of the company this subsidy is awarded to. + * @param subsidy_id The SubsidyID to check. + * @pre IsAwarded(subsidy_id). + * @return The companyindex of the company this subsidy is awarded to. + */ + static AICompany::CompanyID GetAwardedTo(SubsidyID subsidy_id); + + /** + * Get the date this subsidy expires. In case the subsidy is already + * awarded, return the date the subsidy expires, else, return the date the + * offer expires. + * @param subsidy_id The SubsidyID to check. + * @pre IsValidSubsidy(subsidy_id). + * @return The last valid date of this subsidy. + * @note The return value of this function will change if the subsidy is + * awarded. + */ + static int32 GetExpireDate(SubsidyID subsidy_id); + + /** + * Get the cargo type that has to be transported in order to be awarded this + * subsidy. + * @param subsidy_id The SubsidyID to check. + * @pre IsValidSubsidy(subsidy_id). + * @return The cargo type to transport. + */ + static CargoID GetCargoType(SubsidyID subsidy_id); + + /** + * Is the source of the subsidy a town or an industry. + * @param subsidy_id The SubsidyID to check. + * @pre IsValidSubsidy(subsidy_id) && !IsAwarded(subsidy_id). + * @return True if the source is a town, false if it is an industry. + */ + static bool SourceIsTown(SubsidyID subsidy_id); + + /** + * Return the source TownID/IndustryID/StationID the subsidy is for. + * 1) IsAwarded(subsidy_id) -> return the StationID the subsidy is awarded to. + * 2) !IsAwarded(subsidy_id) && SourceIsTown(subsidy_id) -> return the TownID. + * 3) !IsAwarded(subsidy_id) && !SourceIsTown(subsidy_id) -> return the IndustryID. + * @param subsidy_id The SubsidyID to check. + * @pre IsValidSubsidy(subsidy_id). + * @return One of TownID/IndustryID/StationID. + */ + static int32 GetSource(SubsidyID subsidy_id); + + /** + * Is the destination of the subsidy a town or an industry. + * @param subsidy_id The SubsidyID to check. + * @pre IsValidSubsidy(subsidy_id) && !IsAwarded(subsidy_id). + * @return True if the destination is a town, false if it is an industry. + */ + static bool DestinationIsTown(SubsidyID subsidy_id); + + /** + * Return the destination TownID/IndustryID/StationID the subsidy is for. + * 1) IsAwarded(subsidy_id) -> return the StationID the subsidy is awarded to. + * 2) !IsAwarded(subsidy_id) && SourceIsTown(subsidy_id) -> return the TownID. + * 3) !IsAwarded(subsidy_id) && !SourceIsTown(subsidy_id) -> return the IndustryID. + * @param subsidy_id the SubsidyID to check. + * @pre IsValidSubsidy(subsidy_id). + * @return One of TownID/IndustryID/StationID. + */ + static int32 GetDestination(SubsidyID subsidy_id); +}; + +#endif /* AI_SUBSIDY_HPP */ diff --git a/src/ai/api/ai_subsidy.hpp.sq b/src/ai/api/ai_subsidy.hpp.sq new file mode 100644 index 000000000..41ed75077 --- /dev/null +++ b/src/ai/api/ai_subsidy.hpp.sq @@ -0,0 +1,32 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_subsidy.hpp" + +namespace SQConvert { + /* Allow AISubsidy to be used as Squirrel parameter */ + template <> AISubsidy *GetParam(ForceType<AISubsidy *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AISubsidy *)instance; } + template <> AISubsidy &GetParam(ForceType<AISubsidy &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AISubsidy *)instance; } + template <> const AISubsidy *GetParam(ForceType<const AISubsidy *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AISubsidy *)instance; } + template <> const AISubsidy &GetParam(ForceType<const AISubsidy &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AISubsidy *)instance; } + template <> int Return<AISubsidy *>(HSQUIRRELVM vm, AISubsidy *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AISubsidy", res, NULL, DefSQDestructorCallback<AISubsidy>); return 1; } +}; // namespace SQConvert + +void SQAISubsidy_Register(Squirrel *engine) { + DefSQClass <AISubsidy> SQAISubsidy("AISubsidy"); + SQAISubsidy.PreRegister(engine); + SQAISubsidy.AddConstructor<void (AISubsidy::*)(), 1>(engine, "x"); + + SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::GetClassName, "GetClassName", 1, "x"); + SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::IsValidSubsidy, "IsValidSubsidy", 2, "xi"); + SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::IsAwarded, "IsAwarded", 2, "xi"); + SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::GetAwardedTo, "GetAwardedTo", 2, "xi"); + SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::GetExpireDate, "GetExpireDate", 2, "xi"); + SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::GetCargoType, "GetCargoType", 2, "xi"); + SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::SourceIsTown, "SourceIsTown", 2, "xi"); + SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::GetSource, "GetSource", 2, "xi"); + SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::DestinationIsTown, "DestinationIsTown", 2, "xi"); + SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::GetDestination, "GetDestination", 2, "xi"); + + SQAISubsidy.PostRegister(engine); +} diff --git a/src/ai/api/ai_subsidylist.cpp b/src/ai/api/ai_subsidylist.cpp new file mode 100644 index 000000000..8e5d96ad9 --- /dev/null +++ b/src/ai/api/ai_subsidylist.cpp @@ -0,0 +1,14 @@ +/* $Id$ */ + +/** @file ai_subsidylist.cpp Implementation of AISubsidyList. */ + +#include "ai_subsidylist.hpp" +#include "ai_subsidy.hpp" +#include "../../economy_func.h" + +AISubsidyList::AISubsidyList() +{ + for (uint i = 0; i < lengthof(_subsidies); i++) { + if (AISubsidy::IsValidSubsidy(i)) this->AddItem(i); + } +} diff --git a/src/ai/api/ai_subsidylist.hpp b/src/ai/api/ai_subsidylist.hpp new file mode 100644 index 000000000..dde3ba351 --- /dev/null +++ b/src/ai/api/ai_subsidylist.hpp @@ -0,0 +1,20 @@ +/* $Id$ */ + +/** @file ai_subsidylist.hpp List all the subsidies. */ + +#ifndef AI_SUBSIDYLIST_HPP +#define AI_SUBSIDYLIST_HPP + +#include "ai_abstractlist.hpp" + +/** + * Creates a list of all current subsidies. + * @ingroup AIList + */ +class AISubsidyList : public AIAbstractList { +public: + static const char *GetClassName() { return "AISubsidyList"; } + AISubsidyList(); +}; + +#endif /* AI_SUBSIDYLIST_HPP */ diff --git a/src/ai/api/ai_subsidylist.hpp.sq b/src/ai/api/ai_subsidylist.hpp.sq new file mode 100644 index 000000000..db86a6ab6 --- /dev/null +++ b/src/ai/api/ai_subsidylist.hpp.sq @@ -0,0 +1,23 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_subsidylist.hpp" + +namespace SQConvert { + /* Allow AISubsidyList to be used as Squirrel parameter */ + template <> AISubsidyList *GetParam(ForceType<AISubsidyList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AISubsidyList *)instance; } + template <> AISubsidyList &GetParam(ForceType<AISubsidyList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AISubsidyList *)instance; } + template <> const AISubsidyList *GetParam(ForceType<const AISubsidyList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AISubsidyList *)instance; } + template <> const AISubsidyList &GetParam(ForceType<const AISubsidyList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AISubsidyList *)instance; } + template <> int Return<AISubsidyList *>(HSQUIRRELVM vm, AISubsidyList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AISubsidyList", res, NULL, DefSQDestructorCallback<AISubsidyList>); return 1; } +}; // namespace SQConvert + +void SQAISubsidyList_Register(Squirrel *engine) { + DefSQClass <AISubsidyList> SQAISubsidyList("AISubsidyList"); + SQAISubsidyList.PreRegister(engine, "AIAbstractList"); + SQAISubsidyList.AddConstructor<void (AISubsidyList::*)(), 1>(engine, "x"); + + SQAISubsidyList.DefSQStaticMethod(engine, &AISubsidyList::GetClassName, "GetClassName", 1, "x"); + + SQAISubsidyList.PostRegister(engine); +} diff --git a/src/ai/api/ai_testmode.cpp b/src/ai/api/ai_testmode.cpp new file mode 100644 index 000000000..1b09fa699 --- /dev/null +++ b/src/ai/api/ai_testmode.cpp @@ -0,0 +1,26 @@ +/* $Id$ */ + +/** @file ai_testmode.cpp Implementation of AITestMode. */ + +#include "ai_testmode.hpp" +#include "../../command_type.h" + +bool AITestMode::ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCost costs) +{ + /* In test mode we only return 'false', telling the DoCommand it + * should stop after testing the command and return with that result. */ + return false; +} + +AITestMode::AITestMode() +{ + this->last_mode = this->GetDoCommandMode(); + this->last_instance = this->GetDoCommandModeInstance(); + this->SetDoCommandMode(&AITestMode::ModeProc, this); +} + +AITestMode::~AITestMode() +{ + assert(this->GetDoCommandModeInstance() == this); + this->SetDoCommandMode(this->last_mode, this->last_instance); +} diff --git a/src/ai/api/ai_testmode.hpp b/src/ai/api/ai_testmode.hpp new file mode 100644 index 000000000..c65c501d9 --- /dev/null +++ b/src/ai/api/ai_testmode.hpp @@ -0,0 +1,48 @@ +/* $Id$ */ + +/** @file ai_testmode.hpp Switch the AI to Test Mode. */ + +#ifndef AI_TESTMODE_HPP +#define AI_TESTMODE_HPP + +#include "ai_object.hpp" + +/** + * Class to switch current mode to Test Mode. + * If you create an instance of this class, the mode will be switched to + * Testing. The original mode is stored and recovered from when ever the + * instance is destroyed. + * In Test mode all the commands you execute aren't really executed. The + * system only checks if it would be able to execute your requests, and what + * the cost would be. + */ +class AITestMode : public AIObject { +public: + static const char *GetClassName() { return "AITestMode"; } + +private: + AIModeProc *last_mode; + AIObject *last_instance; + +protected: + /** + * The callback proc for Testing mode. + */ + static bool ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCost costs); + +public: + /** + * Creating instance of this class switches the build mode to Testing. + * @note When the instance is destroyed, he restores the mode that was + * current when the instance was created! + */ + AITestMode(); + + /** + * Destroying this instance reset the building mode to the mode it was + * in when the instance was created. + */ + ~AITestMode(); +}; + +#endif /* AI_TESTMODE_HPP */ diff --git a/src/ai/api/ai_testmode.hpp.sq b/src/ai/api/ai_testmode.hpp.sq new file mode 100644 index 000000000..5343d8cab --- /dev/null +++ b/src/ai/api/ai_testmode.hpp.sq @@ -0,0 +1,23 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_testmode.hpp" + +namespace SQConvert { + /* Allow AITestMode to be used as Squirrel parameter */ + template <> AITestMode *GetParam(ForceType<AITestMode *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITestMode *)instance; } + template <> AITestMode &GetParam(ForceType<AITestMode &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITestMode *)instance; } + template <> const AITestMode *GetParam(ForceType<const AITestMode *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITestMode *)instance; } + template <> const AITestMode &GetParam(ForceType<const AITestMode &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITestMode *)instance; } + template <> int Return<AITestMode *>(HSQUIRRELVM vm, AITestMode *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITestMode", res, NULL, DefSQDestructorCallback<AITestMode>); return 1; } +}; // namespace SQConvert + +void SQAITestMode_Register(Squirrel *engine) { + DefSQClass <AITestMode> SQAITestMode("AITestMode"); + SQAITestMode.PreRegister(engine); + SQAITestMode.AddConstructor<void (AITestMode::*)(), 1>(engine, "x"); + + SQAITestMode.DefSQStaticMethod(engine, &AITestMode::GetClassName, "GetClassName", 1, "x"); + + SQAITestMode.PostRegister(engine); +} diff --git a/src/ai/api/ai_tile.cpp b/src/ai/api/ai_tile.cpp new file mode 100644 index 000000000..868cc679a --- /dev/null +++ b/src/ai/api/ai_tile.cpp @@ -0,0 +1,239 @@ +/* $Id$ */ + +/** @file ai_tile.cpp Implementation of AITile. */ + +#include "ai_tile.hpp" +#include "ai_map.hpp" +#include "ai_town.hpp" +#include "../../openttd.h" +#include "../../tile_map.h" +#include "../../tile_cmd.h" +#include "../../map_func.h" +#include "../../variables.h" +#include "../../station_func.h" +#include "../../command_type.h" +#include "../../settings_type.h" +#include "../../company_func.h" +#include "../../road_map.h" +#include "../../water_map.h" +#include "../../clear_map.h" +#include "../../town.h" + +/* static */ bool AITile::IsBuildable(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + switch (::GetTileType(tile)) { + default: return false; + case MP_CLEAR: return true; + case MP_TREES: return true; + case MP_WATER: return IsCoast(tile); + case MP_ROAD: + /* Tram bits aren't considered buildable */ + if (::GetRoadTypes(tile) != ROADTYPES_ROAD) return false; + /* Depots and crossings aren't considered buildable */ + if (::GetRoadTileType(tile) != ROAD_TILE_NORMAL) return false; + if (CountBits(::GetRoadBits(tile, ROADTYPE_ROAD)) != 1) return false; + if (::IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN)) return true; + if (::IsRoadOwner(tile, ROADTYPE_ROAD, _current_company)) return true; + return false; + } +} + +/* static */ bool AITile::IsBuildableRectangle(TileIndex tile, uint width, uint height) +{ + uint tx, ty; + + tx = AIMap::GetTileX(tile); + ty = AIMap::GetTileY(tile); + + for (uint x = tx; x < width + tx; x++) { + for (uint y = ty; y < height + ty; y++) { + if (!IsBuildable(AIMap::GetTileIndex(x, y))) return false; + } + } + + return true; +} + +/* static */ bool AITile::IsWaterTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsTileType(tile, MP_WATER) && !::IsCoast(tile); +} + +/* static */ bool AITile::IsCoastTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsTileType(tile, MP_WATER) && ::IsCoast(tile); +} + +/* static */ bool AITile::IsStationTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::IsTileType(tile, MP_STATION); +} + +/* static */ bool AITile::IsSteepSlope(Slope slope) +{ + if (slope == SLOPE_INVALID) return false; + + return ::IsSteepSlope((::Slope)slope); +} + +/* static */ bool AITile::IsHalftileSlope(Slope slope) +{ + if (slope == SLOPE_INVALID) return false; + + return ::IsHalftileSlope((::Slope)slope); +} + +/* static */ bool AITile::HasTreeOnTile(TileIndex tile) +{ + return ::IsTileType(tile, MP_TREES); +} + +/* static */ bool AITile::IsFarmTile(TileIndex tile) +{ + return (::IsTileType(tile, MP_CLEAR) && ::IsClearGround(tile, CLEAR_FIELDS)); +} + +/* static */ bool AITile::IsRockTile(TileIndex tile) +{ + return (::IsTileType(tile, MP_CLEAR) && ::IsClearGround(tile, CLEAR_ROCKS)); +} + +/* static */ bool AITile::IsRoughTile(TileIndex tile) +{ + return (::IsTileType(tile, MP_CLEAR) && ::IsClearGround(tile, CLEAR_ROUGH)); +} + +/* static */ bool AITile::IsSnowTile(TileIndex tile) +{ + return (::IsTileType(tile, MP_CLEAR) && ::IsClearGround(tile, CLEAR_SNOW)); +} + +/* static */ bool AITile::IsDesertTile(TileIndex tile) +{ + return (::IsTileType(tile, MP_CLEAR) && ::IsClearGround(tile, CLEAR_DESERT)); +} + +/* static */ AITile::Slope AITile::GetSlope(TileIndex tile) +{ + if (!::IsValidTile(tile)) return SLOPE_INVALID; + + return (Slope)::GetTileSlope(tile, NULL); +} + +/* static */ AITile::Slope AITile::GetComplementSlope(Slope slope) +{ + if (slope == SLOPE_INVALID) return SLOPE_INVALID; + if (IsSteepSlope(slope)) return SLOPE_INVALID; + if (IsHalftileSlope(slope)) return SLOPE_INVALID; + + return (Slope)::ComplementSlope((::Slope)slope); +} + +/* static */ int32 AITile::GetHeight(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + + return ::TileHeight(tile); +} + +/* static */ AICompany::CompanyID AITile::GetOwner(TileIndex tile) +{ + if (!::IsValidTile(tile)) return AICompany::INVALID_COMPANY; + if (::IsTileType(tile, MP_HOUSE)) return AICompany::INVALID_COMPANY; + if (::IsTileType(tile, MP_INDUSTRY)) return AICompany::INVALID_COMPANY; + + return AICompany::ResolveCompanyID((AICompany::CompanyID)(byte)::GetTileOwner(tile)); +} + +/* static */ bool AITile::HasTransportType(TileIndex tile, TransportType transport_type) +{ + if (!::IsValidTile(tile)) return false; + + return GB(::GetTileTrackStatus(tile, (::TransportType)transport_type, UINT32_MAX), 0, 16) != 0; +} + +/* static */ int32 AITile::GetCargoAcceptance(TileIndex tile, CargoID cargo_type, uint width, uint height, uint radius) +{ + if (!::IsValidTile(tile)) return false; + + AcceptedCargo accepts; + ::GetAcceptanceAroundTiles(accepts, tile, width, height, _settings_game.station.modified_catchment ? radius : (uint)CA_UNMODIFIED); + return accepts[cargo_type]; +} + +/* static */ int32 AITile::GetCargoProduction(TileIndex tile, CargoID cargo_type, uint width, uint height, uint radius) +{ + if (!::IsValidTile(tile)) return false; + + AcceptedCargo produced; + ::GetProductionAroundTiles(produced, tile, width, height, _settings_game.station.modified_catchment ? radius : (uint)CA_UNMODIFIED); + return produced[cargo_type]; +} + +/* static */ int32 AITile::GetDistanceManhattanToTile(TileIndex tile_from, TileIndex tile_to) +{ + return AIMap::DistanceManhattan(tile_from, tile_to); +} + +/* static */ int32 AITile::GetDistanceSquareToTile(TileIndex tile_from, TileIndex tile_to) +{ + return AIMap::DistanceSquare(tile_from, tile_to); +} + +/* static */ bool AITile::RaiseTile(TileIndex tile, int32 slope) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + + return AIObject::DoCommand(tile, slope, 1, CMD_TERRAFORM_LAND); +} + +/* static */ bool AITile::LowerTile(TileIndex tile, int32 slope) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + + return AIObject::DoCommand(tile, slope, 0, CMD_TERRAFORM_LAND); +} + +/* static */ bool AITile::DemolishTile(TileIndex tile) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + + return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR); +} + +/* static */ bool AITile::PlantTree(TileIndex tile) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + + return AIObject::DoCommand(tile, -1, tile, CMD_PLANT_TREE); +} + +/* static */ bool AITile::PlantTreeRectangle(TileIndex tile, uint width, uint height) +{ + EnforcePrecondition(false, ::IsValidTile(tile)); + EnforcePrecondition(false, width >= 1 && width <= 20); + EnforcePrecondition(false, height >= 1 && height <= 20); + TileIndex end_tile = tile + ::TileDiffXY(width - 1, height - 1); + + return AIObject::DoCommand(tile, -1, end_tile, CMD_PLANT_TREE); +} + +/* static */ bool AITile::IsWithinTownInfluence(TileIndex tile, TownID town_id) +{ + return AITown::IsWithinTownInfluence(town_id, tile); +} + +/* static */ TownID AITile::GetClosestTown(TileIndex tile) +{ + if (!::IsValidTile(tile)) return INVALID_TOWN; + + return ::ClosestTownFromTile(tile, UINT_MAX)->index; +} diff --git a/src/ai/api/ai_tile.hpp b/src/ai/api/ai_tile.hpp new file mode 100644 index 000000000..298de75cb --- /dev/null +++ b/src/ai/api/ai_tile.hpp @@ -0,0 +1,362 @@ +/* $Id$ */ + +/** @file ai_tile.hpp Everything to query and manipulate tiles. */ + +#ifndef AI_TILE_HPP +#define AI_TILE_HPP + +#include "ai_abstractlist.hpp" +#include "ai_error.hpp" +#include "ai_company.hpp" + +/** + * Class that handles all tile related functions. + */ +class AITile : public AIObject { +public: + static const char *GetClassName() { return "AITile"; } + + /** + * Error messages related to modifying tiles. + */ + enum ErrorMessages { + + /** Base for tile related errors */ + ERR_TILE_BASE = AIError::ERR_CAT_TILE << AIError::ERR_CAT_BIT_SIZE, + + /** Tile can't be raised any higher */ + ERR_TILE_TOO_HIGH, // [STR_1003_ALREADY_AT_SEA_LEVEL] + + /** Tile can't be lowered any lower */ + ERR_TILE_TOO_LOW, // [STR_1003_ALREADY_AT_SEA_LEVEL] + }; + + /** + * Enumeration for the slope-type (from slopes.h). + * + * This enumeration use the chars N, E, S, W corresponding the + * direction North, East, South and West. The top corner of a tile + * is the north-part of the tile. + */ + enum Slope { + /* Values are important, as they represent the internal state of the game. */ + SLOPE_FLAT = 0x00, //!< A flat tile + SLOPE_W = 0x01, //!< The west corner of the tile is raised + SLOPE_S = 0x02, //!< The south corner of the tile is raised + SLOPE_E = 0x04, //!< The east corner of the tile is raised + SLOPE_N = 0x08, //!< The north corner of the tile is raised + SLOPE_STEEP = 0x10, //!< Indicates the slope is steep + SLOPE_NW = SLOPE_N | SLOPE_W, //!< North and west corner are raised + SLOPE_SW = SLOPE_S | SLOPE_W, //!< South and west corner are raised + SLOPE_SE = SLOPE_S | SLOPE_E, //!< South and east corner are raised + SLOPE_NE = SLOPE_N | SLOPE_E, //!< North and east corner are raised + SLOPE_EW = SLOPE_E | SLOPE_W, //!< East and west corner are raised + SLOPE_NS = SLOPE_N | SLOPE_S, //!< North and south corner are raised + SLOPE_ELEVATED = SLOPE_N | SLOPE_E | SLOPE_S | SLOPE_W, //!< All corner are raised, similar to SLOPE_FLAT + SLOPE_NWS = SLOPE_N | SLOPE_W | SLOPE_S, //!< North, west and south corner are raised + SLOPE_WSE = SLOPE_W | SLOPE_S | SLOPE_E, //!< West, south and east corner are raised + SLOPE_SEN = SLOPE_S | SLOPE_E | SLOPE_N, //!< South, east and north corner are raised + SLOPE_ENW = SLOPE_E | SLOPE_N | SLOPE_W, //!< East, north and west corner are raised + SLOPE_STEEP_W = SLOPE_STEEP | SLOPE_NWS, //!< A steep slope falling to east (from west) + SLOPE_STEEP_S = SLOPE_STEEP | SLOPE_WSE, //!< A steep slope falling to north (from south) + SLOPE_STEEP_E = SLOPE_STEEP | SLOPE_SEN, //!< A steep slope falling to west (from east) + SLOPE_STEEP_N = SLOPE_STEEP | SLOPE_ENW, //!< A steep slope falling to south (from north) + + SLOPE_INVALID = 0xFF, //!< An invalid slope + }; + + /** + * The different transport types a tile can have. + */ + enum TransportType { + /* Values are important, as they represent the internal state of the game. */ + TRANSPORT_RAIL = 0, //!< Tile with rail. + TRANSPORT_ROAD = 1, //!< Tile with road. + TRANSPORT_WATER = 2, //!< Tile with navigable waterways. + TRANSPORT_AIR = 3, //!< Tile with airport. + + INVALID_TRANSPORT = -1, //!< Tile without any transport type. + }; + + /** + * Check if this tile is buildable, i.e. no things on it that needs + * demolishing. + * @param tile The tile to check on. + * @pre AIMap::IsValidTile(tile). + * @return True if it is buildable, false if not. + * @note For trams you also might want to check for AIRoad::IsRoad(), + * as you can build tram-rails on road-tiles. + * @note For rail you also might want to check for AIRoad::IsRoad(), + * as in some cases you can build rails on road-tiles. + */ + static bool IsBuildable(TileIndex tile); + + /** + * Check if this tile is buildable in a rectangle around a tile, with the + * entry in the list as top-left. + * @param tile The tile to check on. + * @param width The width of the rectangle. + * @param height The height of the rectangle. + * @pre AIMap::IsValidTile(tile). + * @return True if it is buildable, false if not. + */ + static bool IsBuildableRectangle(TileIndex tile, uint width, uint height); + + /** + * Checks whether the given tile is actually a water tile. + * @param tile The tile to check on. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile is a water tile. + */ + static bool IsWaterTile(TileIndex tile); + + /** + * Checks whether the given tile is actually a coast tile. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile is a coast tile. + * @note Building on coast tiles in general is more expensive. + */ + static bool IsCoastTile(TileIndex tile); + + /** + * Checks whether the given tile is a station tile of any station. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile is a station tile. + */ + static bool IsStationTile(TileIndex tile); + + /** + * Check if a tile has a steep slope. + * @param slope The slope to check on. + * @pre slope != SLOPE_INVALID. + * @return True if the slope is a steep slope. + */ + static bool IsSteepSlope(Slope slope); + + /** + * Check if a tile has a halftile slope. + * @param slope The slope to check on. + * @pre slope != SLOPE_INVALID. + * @return True if the slope is a halftile slope. + */ + static bool IsHalftileSlope(Slope slope); + + /** + * Check if the tile has any tree on it. + * @param tile The tile to check on. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if there is a tree on the tile. + */ + static bool HasTreeOnTile(TileIndex tile); + + /** + * Check if the tile is a farmland tile. + * @param tile The tile to check on. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile is farmland. + */ + static bool IsFarmTile(TileIndex tile); + + /** + * Check if the tile is a rock tile. + * @param tile The tile to check on. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile is rock tile. + */ + static bool IsRockTile(TileIndex tile); + + /** + * Check if the tile is a rough tile. + * @param tile The tile to check on. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile is rough tile. + */ + static bool IsRoughTile(TileIndex tile); + + /** + * Check if the tile is a snow tile. + * @param tile The tile to check on. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile is snow tile. + */ + static bool IsSnowTile(TileIndex tile); + + /** + * Check if the tile is a desert tile. + * @param tile The tile to check on. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile is desert tile. + */ + static bool IsDesertTile(TileIndex tile); + + /** + * Get the slope of a tile. + * @param tile The tile to check on. + * @pre AIMap::IsValidTile(tile). + * @return 0 means flat, others indicate internal state of slope. + */ + static Slope GetSlope(TileIndex tile); + + /** + * Get the complement of the slope. + * @param slope The slope to get the complement of. + * @pre slope != SLOPE_INVALID. + * @pre !IsSteepSlope(slope). + * @pre !IsHalftileSlope(slope). + * @return The complement of a slope. This means that all corners that + * weren't raised, are raised, and visa versa. + */ + static Slope GetComplementSlope(Slope slope); + + /** + * Get the height of the tile. + * @param tile The tile to check on. + * @pre AIMap::IsValidTile(tile). + * @return The height of the tile, ranging from 0 to 15. + */ + static int32 GetHeight(TileIndex tile); + + /** + * Get the owner of the tile. + * @param tile The tile to get the owner from. + * @pre AIMap::IsValidTile(tile). + * @return The CompanyID of the owner of the tile, or INVALID_COMPANY if + * there is no owner (grass/industry/water tiles, etc.). + */ + static AICompany::CompanyID GetOwner(TileIndex tile); + + /** + * Checks whether the given tile contains parts suitable for the given + * TransportType. + * @param tile The tile to check. + * @param transport_type The TransportType to check against. + * @pre AIMap::IsValidTile(tile). + * @note Returns false on tiles with roadworks and on road tiles with only + * a single piece of road as these tiles cannot be used to transport + * anything on. It furthermore returns true on some coast tile for + * TRANSPORT_WATER because ships can navigate over them. + * @return True if and only if the tile has the given TransportType. + */ + static bool HasTransportType(TileIndex tile, TransportType transport_type); + + /** + * Check how much cargo this tile accepts. + * It creates a radius around the tile, and adds up all acceptance of this + * cargo. + * @param tile The tile to check on. + * @param cargo_type The cargo to check the acceptance of. + * @param width The width of the station. + * @param height The height of the station. + * @param radius The radius of the station. + * @pre AIMap::IsValidTile(tile). + * @return Value below 8 means no acceptance; the more the better. + */ + static int32 GetCargoAcceptance(TileIndex tile, CargoID cargo_type, uint width, uint height, uint radius); + + /** + * Checks how many tiles in the radius produces this cargo. + * It creates a radius around the tile, and adds up all tiles that produce + * this cargo. + * @param tile The tile to check on. + * @param cargo_type The cargo to check the production of. + * @param width The width of the station. + * @param height The height of the station. + * @param radius The radius of the station. + * @pre AIMap::IsValidTile(tile). + * @return The tiles that produce this cargo within radius of the tile. + * @note Town(houses) are not included in the value. + */ + static int32 GetCargoProduction(TileIndex tile, CargoID cargo_type, uint width, uint height, uint radius); + + /** + * Get the manhattan distance from the tile to the tile. + * @param tile_from The tile to get the distance to. + * @param tile_to The tile to get the distance to. + * @return The distance between the two tiles. + */ + static int32 GetDistanceManhattanToTile(TileIndex tile_from, TileIndex tile_to); + + /** + * Get the square distance from the tile to the tile. + * @param tile_from The tile to get the distance to. + * @param tile_to The tile to get the distance to. + * @return The distance between the two tiles. + */ + static int32 GetDistanceSquareToTile(TileIndex tile_from, TileIndex tile_to); + + /** + * Raise the given corners of the tile. The corners can be combined, + * for example: SLOPE_N | SLOPE_W (= SLOPE_NW) + * @param tile The tile to raise. + * @param slope Corners to raise (SLOPE_xxx). + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIError::ERR_TOO_CLOSE_TO_EDGE + * @exception AITile::ERR_TILE_TOO_HIGH + * @return 0 means failed, 1 means success. + */ + static bool RaiseTile(TileIndex tile, int32 slope); + + /** + * Lower the given corners of the tile. The corners can be combined, + * for example: SLOPE_N | SLOPE_W (= SLOPE_NW) + * @param tile The tile to lower. + * @param slope Corners to lower (SLOPE_xxx). + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AIError::ERR_TOO_CLOSE_TO_EDGE + * @exception AITile::ERR_TILE_TOO_LOW + * @return 0 means failed, 1 means success. + */ + static bool LowerTile(TileIndex tile, int32 slope); + + /** + * Destroy everything on the given tile. + * @param tile The tile to demolish. + * @pre AIMap::IsValidTile(tile). + * @exception AIError::ERR_AREA_NOT_CLEAR + * @return True if and only if the tile was demolished. + */ + static bool DemolishTile(TileIndex tile); + + /** + * Create a random tree on a tile. + * @param tile The tile to build a tree on. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if a tree was added on the tile. + */ + static bool PlantTree(TileIndex tile); + + /** + * Create a random tree on a rectangle of tiles. + * @param tile The top left tile of the rectangle. + * @param width The width of the rectangle. + * @param height The height of the rectangle. + * @pre AIMap::IsValidTile(tile). + * @pre width >= 1 && width <= 20. + * @pre height >= 1 && height <= 20. + * @return True if and only if a tree was added on any of the tiles in the rectangle. + */ + static bool PlantTreeRectangle(TileIndex tile, uint width, uint height); + + /** + * Find out if this tile is within the rating influence of a town. + * Stations on this tile influence the rating of the town. + * @param tile The tile to check. + * @param town_id The town to check. + * @return True if the tile is within the rating influence of the town. + */ + static bool IsWithinTownInfluence(TileIndex tile, TownID town_id); + + /** + * Find the town that is closest to a tile. Stations you build at this tile + * will belong to this town. + * @param tile The tile to check. + * @return The TownID of the town closest to the tile. + */ + static TownID GetClosestTown(TileIndex tile); +}; + +#endif /* AI_TILE_HPP */ diff --git a/src/ai/api/ai_tile.hpp.sq b/src/ai/api/ai_tile.hpp.sq new file mode 100644 index 000000000..920dc6543 --- /dev/null +++ b/src/ai/api/ai_tile.hpp.sq @@ -0,0 +1,97 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_tile.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AITile::ErrorMessages GetParam(ForceType<AITile::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AITile::ErrorMessages)tmp; } + template <> int Return<AITile::ErrorMessages>(HSQUIRRELVM vm, AITile::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AITile::Slope GetParam(ForceType<AITile::Slope>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AITile::Slope)tmp; } + template <> int Return<AITile::Slope>(HSQUIRRELVM vm, AITile::Slope res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AITile::TransportType GetParam(ForceType<AITile::TransportType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AITile::TransportType)tmp; } + template <> int Return<AITile::TransportType>(HSQUIRRELVM vm, AITile::TransportType res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AITile to be used as Squirrel parameter */ + template <> AITile *GetParam(ForceType<AITile *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITile *)instance; } + template <> AITile &GetParam(ForceType<AITile &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITile *)instance; } + template <> const AITile *GetParam(ForceType<const AITile *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITile *)instance; } + template <> const AITile &GetParam(ForceType<const AITile &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITile *)instance; } + template <> int Return<AITile *>(HSQUIRRELVM vm, AITile *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITile", res, NULL, DefSQDestructorCallback<AITile>); return 1; } +}; // namespace SQConvert + +void SQAITile_Register(Squirrel *engine) { + DefSQClass <AITile> SQAITile("AITile"); + SQAITile.PreRegister(engine); + SQAITile.AddConstructor<void (AITile::*)(), 1>(engine, "x"); + + SQAITile.DefSQConst(engine, AITile::ERR_TILE_BASE, "ERR_TILE_BASE"); + SQAITile.DefSQConst(engine, AITile::ERR_TILE_TOO_HIGH, "ERR_TILE_TOO_HIGH"); + SQAITile.DefSQConst(engine, AITile::ERR_TILE_TOO_LOW, "ERR_TILE_TOO_LOW"); + SQAITile.DefSQConst(engine, AITile::SLOPE_FLAT, "SLOPE_FLAT"); + SQAITile.DefSQConst(engine, AITile::SLOPE_W, "SLOPE_W"); + SQAITile.DefSQConst(engine, AITile::SLOPE_S, "SLOPE_S"); + SQAITile.DefSQConst(engine, AITile::SLOPE_E, "SLOPE_E"); + SQAITile.DefSQConst(engine, AITile::SLOPE_N, "SLOPE_N"); + SQAITile.DefSQConst(engine, AITile::SLOPE_STEEP, "SLOPE_STEEP"); + SQAITile.DefSQConst(engine, AITile::SLOPE_NW, "SLOPE_NW"); + SQAITile.DefSQConst(engine, AITile::SLOPE_SW, "SLOPE_SW"); + SQAITile.DefSQConst(engine, AITile::SLOPE_SE, "SLOPE_SE"); + SQAITile.DefSQConst(engine, AITile::SLOPE_NE, "SLOPE_NE"); + SQAITile.DefSQConst(engine, AITile::SLOPE_EW, "SLOPE_EW"); + SQAITile.DefSQConst(engine, AITile::SLOPE_NS, "SLOPE_NS"); + SQAITile.DefSQConst(engine, AITile::SLOPE_ELEVATED, "SLOPE_ELEVATED"); + SQAITile.DefSQConst(engine, AITile::SLOPE_NWS, "SLOPE_NWS"); + SQAITile.DefSQConst(engine, AITile::SLOPE_WSE, "SLOPE_WSE"); + SQAITile.DefSQConst(engine, AITile::SLOPE_SEN, "SLOPE_SEN"); + SQAITile.DefSQConst(engine, AITile::SLOPE_ENW, "SLOPE_ENW"); + SQAITile.DefSQConst(engine, AITile::SLOPE_STEEP_W, "SLOPE_STEEP_W"); + SQAITile.DefSQConst(engine, AITile::SLOPE_STEEP_S, "SLOPE_STEEP_S"); + SQAITile.DefSQConst(engine, AITile::SLOPE_STEEP_E, "SLOPE_STEEP_E"); + SQAITile.DefSQConst(engine, AITile::SLOPE_STEEP_N, "SLOPE_STEEP_N"); + SQAITile.DefSQConst(engine, AITile::SLOPE_INVALID, "SLOPE_INVALID"); + SQAITile.DefSQConst(engine, AITile::TRANSPORT_RAIL, "TRANSPORT_RAIL"); + SQAITile.DefSQConst(engine, AITile::TRANSPORT_ROAD, "TRANSPORT_ROAD"); + SQAITile.DefSQConst(engine, AITile::TRANSPORT_WATER, "TRANSPORT_WATER"); + SQAITile.DefSQConst(engine, AITile::TRANSPORT_AIR, "TRANSPORT_AIR"); + SQAITile.DefSQConst(engine, AITile::INVALID_TRANSPORT, "INVALID_TRANSPORT"); + + AIError::RegisterErrorMap(STR_1003_ALREADY_AT_SEA_LEVEL, AITile::ERR_TILE_TOO_HIGH); + AIError::RegisterErrorMap(STR_1003_ALREADY_AT_SEA_LEVEL, AITile::ERR_TILE_TOO_LOW); + + AIError::RegisterErrorMapString(AITile::ERR_TILE_TOO_HIGH, "ERR_TILE_TOO_HIGH"); + AIError::RegisterErrorMapString(AITile::ERR_TILE_TOO_LOW, "ERR_TILE_TOO_LOW"); + + SQAITile.DefSQStaticMethod(engine, &AITile::GetClassName, "GetClassName", 1, "x"); + SQAITile.DefSQStaticMethod(engine, &AITile::IsBuildable, "IsBuildable", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::IsBuildableRectangle, "IsBuildableRectangle", 4, "xiii"); + SQAITile.DefSQStaticMethod(engine, &AITile::IsWaterTile, "IsWaterTile", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::IsCoastTile, "IsCoastTile", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::IsStationTile, "IsStationTile", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::IsSteepSlope, "IsSteepSlope", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::IsHalftileSlope, "IsHalftileSlope", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::HasTreeOnTile, "HasTreeOnTile", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::IsFarmTile, "IsFarmTile", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::IsRockTile, "IsRockTile", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::IsRoughTile, "IsRoughTile", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::IsSnowTile, "IsSnowTile", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::IsDesertTile, "IsDesertTile", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::GetSlope, "GetSlope", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::GetComplementSlope, "GetComplementSlope", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::GetHeight, "GetHeight", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::GetOwner, "GetOwner", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::HasTransportType, "HasTransportType", 3, "xii"); + SQAITile.DefSQStaticMethod(engine, &AITile::GetCargoAcceptance, "GetCargoAcceptance", 6, "xiiiii"); + SQAITile.DefSQStaticMethod(engine, &AITile::GetCargoProduction, "GetCargoProduction", 6, "xiiiii"); + SQAITile.DefSQStaticMethod(engine, &AITile::GetDistanceManhattanToTile, "GetDistanceManhattanToTile", 3, "xii"); + SQAITile.DefSQStaticMethod(engine, &AITile::GetDistanceSquareToTile, "GetDistanceSquareToTile", 3, "xii"); + SQAITile.DefSQStaticMethod(engine, &AITile::RaiseTile, "RaiseTile", 3, "xii"); + SQAITile.DefSQStaticMethod(engine, &AITile::LowerTile, "LowerTile", 3, "xii"); + SQAITile.DefSQStaticMethod(engine, &AITile::DemolishTile, "DemolishTile", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::PlantTree, "PlantTree", 2, "xi"); + SQAITile.DefSQStaticMethod(engine, &AITile::PlantTreeRectangle, "PlantTreeRectangle", 4, "xiii"); + SQAITile.DefSQStaticMethod(engine, &AITile::IsWithinTownInfluence, "IsWithinTownInfluence", 3, "xii"); + SQAITile.DefSQStaticMethod(engine, &AITile::GetClosestTown, "GetClosestTown", 2, "xi"); + + SQAITile.PostRegister(engine); +} diff --git a/src/ai/api/ai_tilelist.cpp b/src/ai/api/ai_tilelist.cpp new file mode 100644 index 000000000..24575c697 --- /dev/null +++ b/src/ai/api/ai_tilelist.cpp @@ -0,0 +1,175 @@ +/* $Id$ */ + +/** @file ai_tilelist.cpp Implementation of AITileList and friends. */ + +#include "ai_tilelist.hpp" +#include "ai_industry.hpp" +#include "../../openttd.h" +#include "../../landscape.h" +#include "../../settings_type.h" +#include "../../station_func.h" +#include "../../map_func.h" +#include "../../tile_map.h" +#include "../../industry_map.h" +#include "../../station_base.h" +#include "../../station_map.h" + +void AITileList::FixRectangleSpan(TileIndex &t1, TileIndex &t2) +{ + uint x1 = ::TileX(t1); + uint x2 = ::TileX(t2); + + uint y1 = ::TileY(t1); + uint y2 = ::TileY(t2); + + if (x1 >= x2) ::Swap(x1, x2); + if (y1 >= y2) ::Swap(y1, y2); + + t1 = ::TileXY(x1, y1); + t2 = ::TileXY(x2, y2); +} + +void AITileList::AddRectangle(TileIndex t1, TileIndex t2) +{ + if (!::IsValidTile(t1)) return; + if (!::IsValidTile(t2)) return; + + this->FixRectangleSpan(t1, t2); + + uint w = TileX(t2) - TileX(t1) + 1; + uint h = TileY(t2) - TileY(t1) + 1; + + BEGIN_TILE_LOOP(t, w, h, t1) { + this->AddItem(t); + } END_TILE_LOOP(t, w, h, t1) +} + +void AITileList::AddTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return; + + this->AddItem(tile); +} + +void AITileList::RemoveRectangle(TileIndex t1, TileIndex t2) +{ + if (!::IsValidTile(t1)) return; + if (!::IsValidTile(t2)) return; + + this->FixRectangleSpan(t1, t2); + + uint w = TileX(t2) - TileX(t1) + 1; + uint h = TileY(t2) - TileY(t1) + 1; + + BEGIN_TILE_LOOP(t, w, h, t1) { + this->RemoveItem(t); + } END_TILE_LOOP(t, w, h, t1) +} + +void AITileList::RemoveTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return; + + this->RemoveItem(tile); +} + +AITileList_IndustryAccepting::AITileList_IndustryAccepting(IndustryID industry_id, uint radius) +{ + if (!AIIndustry::IsValidIndustry(industry_id)) return; + + const Industry *i = ::GetIndustry(industry_id); + const IndustrySpec *indsp = ::GetIndustrySpec(i->type); + + /* Check if this industry accepts anything */ + { + bool cargo_accepts = false; + for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) { + if (indsp->accepts_cargo[j] != CT_INVALID) cargo_accepts = true; + } + if (!cargo_accepts) return; + } + + if (!_settings_game.station.modified_catchment) radius = CA_UNMODIFIED; + + BEGIN_TILE_LOOP(cur_tile, i->width + radius * 2, i->height + radius * 2, i->xy - ::TileDiffXY(radius, radius)) { + if (!::IsValidTile(cur_tile)) continue; + /* Exclude all tiles that belong to this industry */ + if (::IsTileType(cur_tile, MP_INDUSTRY) && ::GetIndustryIndex(cur_tile) == industry_id) continue; + + /* Only add the tile if it accepts the cargo (sometimes just 1 tile of an + * industry triggers the acceptance). */ + AcceptedCargo accepts; + ::GetAcceptanceAroundTiles(accepts, cur_tile, 1, 1, radius); + { + bool cargo_accepts = false; + for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) { + if (indsp->accepts_cargo[j] != CT_INVALID && accepts[indsp->accepts_cargo[j]] != 0) cargo_accepts = true; + } + if (!cargo_accepts) continue; + } + + this->AddTile(cur_tile); + } END_TILE_LOOP(cur_tile, i->width + radius * 2, i->height + radius * 2, i->xy - ::TileDiffXY(radius, radius)) +} + +AITileList_IndustryProducing::AITileList_IndustryProducing(IndustryID industry_id, uint radius) +{ + if (!AIIndustry::IsValidIndustry(industry_id)) return; + + const Industry *i = ::GetIndustry(industry_id); + const IndustrySpec *indsp = ::GetIndustrySpec(i->type); + + /* Check if this industry produces anything */ + { + bool cargo_produces = false; + for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) { + if (indsp->produced_cargo[j] != CT_INVALID) cargo_produces = true; + } + if (!cargo_produces) return; + } + + if (!_settings_game.station.modified_catchment) radius = CA_UNMODIFIED; + + BEGIN_TILE_LOOP(cur_tile, i->width + radius * 2, i->height + radius * 2, i->xy - ::TileDiffXY(radius, radius)) { + if (!::IsValidTile(cur_tile)) continue; + /* Exclude all tiles that belong to this industry */ + if (::IsTileType(cur_tile, MP_INDUSTRY) && ::GetIndustryIndex(cur_tile) == industry_id) continue; + + /* Only add the tile if it produces the cargo (a bug in OpenTTD makes this + * inconsitance). */ + AcceptedCargo produces; + ::GetProductionAroundTiles(produces, cur_tile, 1, 1, radius); + { + bool cargo_produces = false; + for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) { + if (indsp->produced_cargo[j] != CT_INVALID && produces[indsp->produced_cargo[j]] != 0) cargo_produces = true; + } + if (!cargo_produces) continue; + } + + this->AddTile(cur_tile); + } END_TILE_LOOP(cur_tile, i->width + radius * 2, i->height + radius * 2, i->xy - ::TileDiffXY(radius, radius)) +} + +AITileList_StationType::AITileList_StationType(StationID station_id, AIStation::StationType station_type) +{ + if (!AIStation::IsValidStation(station_id)) return; + + const StationRect *rect = &::GetStation(station_id)->rect; + + uint station_type_value = 0; + /* Convert AIStation::StationType to ::StationType, but do it in a + * bitmask, so we can scan for multiple entries at the same time. */ + if ((station_type & AIStation::STATION_TRAIN) != 0) station_type_value |= (1 << ::STATION_RAIL); + if ((station_type & AIStation::STATION_TRUCK_STOP) != 0) station_type_value |= (1 << ::STATION_TRUCK); + if ((station_type & AIStation::STATION_BUS_STOP) != 0) station_type_value |= (1 << ::STATION_BUS); + if ((station_type & AIStation::STATION_AIRPORT) != 0) station_type_value |= (1 << ::STATION_AIRPORT) | (1 << ::STATION_OILRIG); + if ((station_type & AIStation::STATION_DOCK) != 0) station_type_value |= (1 << ::STATION_DOCK) | (1 << ::STATION_OILRIG); + + BEGIN_TILE_LOOP(cur_tile, rect->right - rect->left + 1, rect->bottom - rect->top + 1, ::TileXY(rect->left, rect->top)) { + if (!::IsTileType(cur_tile, MP_STATION)) continue; + if (::GetStationIndex(cur_tile) != station_id) continue; + if (!HasBit(station_type_value, ::GetStationType(cur_tile))) continue; + this->AddTile(cur_tile); + } END_TILE_LOOP(cur_tile, rect->right - rect->left + 1, rect->bottom - rect->top + 1, ::TileXY(rect->left, rect->top)) +} diff --git a/src/ai/api/ai_tilelist.hpp b/src/ai/api/ai_tilelist.hpp new file mode 100644 index 000000000..f3d2f658d --- /dev/null +++ b/src/ai/api/ai_tilelist.hpp @@ -0,0 +1,110 @@ +/* $Id$ */ + +/** @file ai_tilelist.hpp List tiles. */ + +#ifndef AI_TILELIST_HPP +#define AI_TILELIST_HPP + +#include "ai_abstractlist.hpp" +#include "ai_station.hpp" + +/** + * Creates an empty list, in which you can add tiles. + * @ingroup AIList + */ +class AITileList : public AIAbstractList { +public: + static const char *GetClassName() { return "AITileList"; } + +private: + /** + * Make sure t1.x is smaller than t2.x and t1.y is smaller than t2.y. + * They are swapped to ensure they are after calling this function. + * @param t1 one of the corners of the rectangle. + * @param t2 the other corner of the rectangle. + */ + void FixRectangleSpan(TileIndex &t1, TileIndex &t2); + +public: + /** + * Adds the rectangle between tile_from and tile_to to the to-be-evaluated tiles. + * @param tile_from One corner of the tiles to add. + * @param tile_to The other corner of the tiles to add. + * @pre AIMap::IsValidTile(tile_from). + * @pre AIMap::IsValidTile(tile_to). + */ + void AddRectangle(TileIndex tile_from, TileIndex tile_to); + + /** + * Add a tile to the to-be-evaluated tiles. + * @param tile The tile to add. + * @pre AIMap::IsValidTile(tile). + */ + void AddTile(TileIndex tile); + + /** + * Remove the tiles inside the rectangle between tile_from and tile_to form the list. + * @param tile_from One corner of the tiles to remove. + * @param tile_to The other corner of the files to remove. + * @pre AIMap::IsValidTile(tile_from). + * @pre AIMap::IsValidTile(tile_to). + */ + void RemoveRectangle(TileIndex tile_from, TileIndex tile_to); + + /** + * Remove a tile from the list. + * @param tile The tile to remove. + * @pre AIMap::IsValidTile(tile). + */ + void RemoveTile(TileIndex tile); +}; + +/** + * Creates a list of tiles that will accept cargo for the given industry. + * @note If a simular industry is close, it might happen that this industry receives the cargo. + * @ingroup AIList + */ +class AITileList_IndustryAccepting : public AITileList { +public: + static const char *GetClassName() { return "AITileList_IndustryAccepting"; } + + /** + * @param industry_id The industry to create the AITileList around. + * @param radius The radius of the station you will be using. + */ + AITileList_IndustryAccepting(IndustryID industry_id, uint radius); +}; + +/** + * Creates a list of tiles which the industry checks to see if a station is + * there to receive cargo produced by this industry. + * @ingroup AIList + */ +class AITileList_IndustryProducing : public AITileList { +public: + static const char *GetClassName() { return "AITileList_IndustryProducing"; } + + /** + * @param industry_id The industry to create the AITileList around. + * @param radius The radius of the station you will be using. + */ + AITileList_IndustryProducing(IndustryID industry_id, uint radius); +}; + +/** + * Creates a list of tiles which have the requested StationType of the + * StationID. + * @ingroup AIList + */ +class AITileList_StationType : public AITileList { +public: + static const char *GetClassName() { return "AITileList_StationType"; } + + /** + * @param station_id The station to create the AITileList for. + * @param station_type The StationType to create the AIList for. + */ + AITileList_StationType(StationID station_id, AIStation::StationType station_type); +}; + +#endif /* AI_TILELIST_HPP */ diff --git a/src/ai/api/ai_tilelist.hpp.sq b/src/ai/api/ai_tilelist.hpp.sq new file mode 100644 index 000000000..4e6bfdf30 --- /dev/null +++ b/src/ai/api/ai_tilelist.hpp.sq @@ -0,0 +1,85 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_tilelist.hpp" + +namespace SQConvert { + /* Allow AITileList to be used as Squirrel parameter */ + template <> AITileList *GetParam(ForceType<AITileList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList *)instance; } + template <> AITileList &GetParam(ForceType<AITileList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList *)instance; } + template <> const AITileList *GetParam(ForceType<const AITileList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList *)instance; } + template <> const AITileList &GetParam(ForceType<const AITileList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList *)instance; } + template <> int Return<AITileList *>(HSQUIRRELVM vm, AITileList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITileList", res, NULL, DefSQDestructorCallback<AITileList>); return 1; } +}; // namespace SQConvert + +void SQAITileList_Register(Squirrel *engine) { + DefSQClass <AITileList> SQAITileList("AITileList"); + SQAITileList.PreRegister(engine, "AIAbstractList"); + SQAITileList.AddConstructor<void (AITileList::*)(), 1>(engine, "x"); + + SQAITileList.DefSQStaticMethod(engine, &AITileList::GetClassName, "GetClassName", 1, "x"); + + SQAITileList.DefSQMethod(engine, &AITileList::AddRectangle, "AddRectangle", 3, "xii"); + SQAITileList.DefSQMethod(engine, &AITileList::AddTile, "AddTile", 2, "xi"); + SQAITileList.DefSQMethod(engine, &AITileList::RemoveRectangle, "RemoveRectangle", 3, "xii"); + SQAITileList.DefSQMethod(engine, &AITileList::RemoveTile, "RemoveTile", 2, "xi"); + + SQAITileList.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AITileList_IndustryAccepting to be used as Squirrel parameter */ + template <> AITileList_IndustryAccepting *GetParam(ForceType<AITileList_IndustryAccepting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList_IndustryAccepting *)instance; } + template <> AITileList_IndustryAccepting &GetParam(ForceType<AITileList_IndustryAccepting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList_IndustryAccepting *)instance; } + template <> const AITileList_IndustryAccepting *GetParam(ForceType<const AITileList_IndustryAccepting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList_IndustryAccepting *)instance; } + template <> const AITileList_IndustryAccepting &GetParam(ForceType<const AITileList_IndustryAccepting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList_IndustryAccepting *)instance; } + template <> int Return<AITileList_IndustryAccepting *>(HSQUIRRELVM vm, AITileList_IndustryAccepting *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITileList_IndustryAccepting", res, NULL, DefSQDestructorCallback<AITileList_IndustryAccepting>); return 1; } +}; // namespace SQConvert + +void SQAITileList_IndustryAccepting_Register(Squirrel *engine) { + DefSQClass <AITileList_IndustryAccepting> SQAITileList_IndustryAccepting("AITileList_IndustryAccepting"); + SQAITileList_IndustryAccepting.PreRegister(engine, "AITileList"); + SQAITileList_IndustryAccepting.AddConstructor<void (AITileList_IndustryAccepting::*)(IndustryID industry_id, uint radius), 3>(engine, "xii"); + + SQAITileList_IndustryAccepting.DefSQStaticMethod(engine, &AITileList_IndustryAccepting::GetClassName, "GetClassName", 1, "x"); + + SQAITileList_IndustryAccepting.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AITileList_IndustryProducing to be used as Squirrel parameter */ + template <> AITileList_IndustryProducing *GetParam(ForceType<AITileList_IndustryProducing *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList_IndustryProducing *)instance; } + template <> AITileList_IndustryProducing &GetParam(ForceType<AITileList_IndustryProducing &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList_IndustryProducing *)instance; } + template <> const AITileList_IndustryProducing *GetParam(ForceType<const AITileList_IndustryProducing *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList_IndustryProducing *)instance; } + template <> const AITileList_IndustryProducing &GetParam(ForceType<const AITileList_IndustryProducing &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList_IndustryProducing *)instance; } + template <> int Return<AITileList_IndustryProducing *>(HSQUIRRELVM vm, AITileList_IndustryProducing *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITileList_IndustryProducing", res, NULL, DefSQDestructorCallback<AITileList_IndustryProducing>); return 1; } +}; // namespace SQConvert + +void SQAITileList_IndustryProducing_Register(Squirrel *engine) { + DefSQClass <AITileList_IndustryProducing> SQAITileList_IndustryProducing("AITileList_IndustryProducing"); + SQAITileList_IndustryProducing.PreRegister(engine, "AITileList"); + SQAITileList_IndustryProducing.AddConstructor<void (AITileList_IndustryProducing::*)(IndustryID industry_id, uint radius), 3>(engine, "xii"); + + SQAITileList_IndustryProducing.DefSQStaticMethod(engine, &AITileList_IndustryProducing::GetClassName, "GetClassName", 1, "x"); + + SQAITileList_IndustryProducing.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AITileList_StationType to be used as Squirrel parameter */ + template <> AITileList_StationType *GetParam(ForceType<AITileList_StationType *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList_StationType *)instance; } + template <> AITileList_StationType &GetParam(ForceType<AITileList_StationType &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList_StationType *)instance; } + template <> const AITileList_StationType *GetParam(ForceType<const AITileList_StationType *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList_StationType *)instance; } + template <> const AITileList_StationType &GetParam(ForceType<const AITileList_StationType &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList_StationType *)instance; } + template <> int Return<AITileList_StationType *>(HSQUIRRELVM vm, AITileList_StationType *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITileList_StationType", res, NULL, DefSQDestructorCallback<AITileList_StationType>); return 1; } +}; // namespace SQConvert + +void SQAITileList_StationType_Register(Squirrel *engine) { + DefSQClass <AITileList_StationType> SQAITileList_StationType("AITileList_StationType"); + SQAITileList_StationType.PreRegister(engine, "AITileList"); + SQAITileList_StationType.AddConstructor<void (AITileList_StationType::*)(StationID station_id, AIStation::StationType station_type), 3>(engine, "xii"); + + SQAITileList_StationType.DefSQStaticMethod(engine, &AITileList_StationType::GetClassName, "GetClassName", 1, "x"); + + SQAITileList_StationType.PostRegister(engine); +} diff --git a/src/ai/api/ai_town.cpp b/src/ai/api/ai_town.cpp new file mode 100644 index 000000000..8c8ab9c02 --- /dev/null +++ b/src/ai/api/ai_town.cpp @@ -0,0 +1,197 @@ +/* $Id$ */ + +/** @file ai_town.cpp Implementation of AITown. */ + +#include "ai_town.hpp" +#include "ai_map.hpp" +#include "ai_cargo.hpp" +#include "ai_error.hpp" +#include "../../command_type.h" +#include "../../openttd.h" +#include "../../town.h" +#include "../../strings_func.h" +#include "../../core/alloc_func.hpp" +#include "../../company_func.h" +#include "../../station_base.h" +#include "table/strings.h" + +/* static */ TownID AITown::GetMaxTownID() +{ + return ::GetMaxTownIndex(); +} + +/* static */ int32 AITown::GetTownCount() +{ + return ::GetNumTowns(); +} + +/* static */ bool AITown::IsValidTown(TownID town_id) +{ + return ::IsValidTownID(town_id); +} + +/* static */ const char *AITown::GetName(TownID town_id) +{ + if (!IsValidTown(town_id)) return NULL; + static const int len = 64; + char *town_name = MallocT<char>(len); + + ::SetDParam(0, town_id); + ::GetString(town_name, STR_TOWN, &town_name[len - 1]); + + return town_name; +} + +/* static */ int32 AITown::GetPopulation(TownID town_id) +{ + if (!IsValidTown(town_id)) return -1; + const Town *t = ::GetTown(town_id); + return t->population; +} + +/* static */ int32 AITown::GetHouseCount(TownID town_id) +{ + if (!IsValidTown(town_id)) return -1; + const Town *t = ::GetTown(town_id); + return t->num_houses; +} + +/* static */ TileIndex AITown::GetLocation(TownID town_id) +{ + if (!IsValidTown(town_id)) return INVALID_TILE; + const Town *t = ::GetTown(town_id); + return t->xy; +} + +/* static */ int32 AITown::GetLastMonthProduction(TownID town_id, CargoID cargo_id) +{ + if (!IsValidTown(town_id)) return -1; + if (!AICargo::IsValidCargo(cargo_id)) return -1; + + const Town *t = ::GetTown(town_id); + + switch(AICargo::GetTownEffect(cargo_id)) { + case AICargo::TE_PASSENGERS: return t->act_pass; + case AICargo::TE_MAIL: return t->act_mail; + default: return -1; + } +} + +/* static */ int32 AITown::GetLastMonthTransported(TownID town_id, CargoID cargo_id) +{ + if (!IsValidTown(town_id)) return -1; + if (!AICargo::IsValidCargo(cargo_id)) return -1; + + const Town *t = ::GetTown(town_id); + + switch(AICargo::GetTownEffect(cargo_id)) { + case AICargo::TE_PASSENGERS: return t->pct_pass_transported; + case AICargo::TE_MAIL: return t->pct_mail_transported; + default: return -1; + } +} + +/* static */ int32 AITown::GetMaxProduction(TownID town_id, CargoID cargo_id) +{ + if (!IsValidTown(town_id)) return -1; + if (!AICargo::IsValidCargo(cargo_id)) return -1; + + const Town *t = ::GetTown(town_id); + + switch(AICargo::GetTownEffect(cargo_id)) { + case AICargo::TE_PASSENGERS: return t->max_pass; + case AICargo::TE_MAIL: return t->max_mail; + default: return -1; + } +} + +/* static */ int32 AITown::GetDistanceManhattanToTile(TownID town_id, TileIndex tile) +{ + return AIMap::DistanceManhattan(tile, GetLocation(town_id)); +} + +/* static */ int32 AITown::GetDistanceSquareToTile(TownID town_id, TileIndex tile) +{ + return AIMap::DistanceSquare(tile, GetLocation(town_id)); +} + +/* static */ bool AITown::IsWithinTownInfluence(TownID town_id, TileIndex tile) +{ + if (!IsValidTown(town_id)) return false; + + const Town *t = ::GetTown(town_id); + return ((uint32)GetDistanceSquareToTile(town_id, tile) <= t->squared_town_zone_radius[0]); +} + +/* static */ bool AITown::HasStatue(TownID town_id) +{ + if (!IsValidTown(town_id)) return false; + + return ::HasBit(::GetTown(town_id)->statues, _current_company); +} + +/* static */ int AITown::GetRoadReworkDuration(TownID town_id) +{ + if (!IsValidTown(town_id)) return -1; + + return ::GetTown(town_id)->road_build_months; +} + +/* static */ AICompany::CompanyID AITown::GetExclusiveRightsCompany(TownID town_id) +{ + if (!IsValidTown(town_id)) return AICompany::INVALID_COMPANY; + + return (AICompany::CompanyID)(int8)::GetTown(town_id)->exclusivity; +} + +/* static */ int32 AITown::GetExclusiveRightsDuration(TownID town_id) +{ + if (!IsValidTown(town_id)) return -1; + + return ::GetTown(town_id)->exclusive_counter; +} + +extern uint GetMaskOfTownActions(int *nump, CompanyID cid, const Town *t); + +/* static */ bool AITown::IsActionAvailable(TownID town_id, TownAction town_action) +{ + if (!IsValidTown(town_id)) return false; + + return HasBit(::GetMaskOfTownActions(NULL, _current_company, ::GetTown(town_id)), town_action); +} + +/* static */ bool AITown::PerformTownAction(TownID town_id, TownAction town_action) +{ + EnforcePrecondition(false, IsValidTown(town_id)); + EnforcePrecondition(false, IsActionAvailable(town_id, town_action)); + + return AIObject::DoCommand(::GetTown(town_id)->xy, town_id, town_action, CMD_DO_TOWN_ACTION); +} + +/* static */ AITown::TownRating AITown::GetRating(TownID town_id, AICompany::CompanyID company_id) +{ + if (!IsValidTown(town_id)) return INVALID_TOWN_RATING; + AICompany::CompanyID company = AICompany::ResolveCompanyID(company_id); + if (company == AICompany::INVALID_COMPANY) return INVALID_TOWN_RATING; + + const Town *t = ::GetTown(town_id); + if (!HasBit(t->have_ratings, company)) return TOWN_RATING_NONE; + return max(TOWN_RATING_APPALLING, (TownRating)((t->ratings[company] / 200) + 3)); +} + +/* static */ int AITown::GetAllowedNoise(TownID town_id) +{ + if (!IsValidTown(town_id)) return -1; + + const Town *t = ::GetTown(town_id); + if (_settings_game.economy.station_noise_level) { + return t->MaxTownNoise() - t->noise_reached; + } + + int num = 0; + const Station *st; + FOR_ALL_STATIONS(st) { + if (st->town == t && st->facilities & FACIL_AIRPORT && st->airport_type != AT_OILRIG) num++; + } + return max(0, 2 - num); +} diff --git a/src/ai/api/ai_town.hpp b/src/ai/api/ai_town.hpp new file mode 100644 index 000000000..94ba91e17 --- /dev/null +++ b/src/ai/api/ai_town.hpp @@ -0,0 +1,284 @@ +/* $Id$ */ + +/** @file ai_town.hpp Everything to query towns. */ + +#ifndef AI_TOWN_HPP +#define AI_TOWN_HPP + +#include "ai_object.hpp" +#include "ai_company.hpp" + +/** + * Class that handles all town related functions. + */ +class AITown : public AIObject { +public: + static const char *GetClassName() { return "AITown"; } + + /** + * Actions that one can perform on a town. + */ + enum TownAction { + /* Values are important, as they represent the internal state of the game. */ + + /** + * The cargo ratings temporary gains 25% of rating (in + * absolute percentage, so 10% becomes 35%, with a max of 99%) + * for all stations within 10 tiles. + */ + TOWN_ACTION_ADVERTISE_SMALL = 0, + + /** + * The cargo ratings temporary gains 44% of rating (in + * absolute percentage, so 10% becomes 54%, with a max of 99%) + * for all stations within 15 tiles. + */ + TOWN_ACTION_ADVERTISE_MEDIUM = 1, + + /** + * The cargo ratings temporary gains 63% of rating (in + * absolute percentage, so 10% becomes 73%, with a max of 99%) + * for all stations within 20 tiles. + */ + TOWN_ACTION_ADVERTISE_LARGE = 2, + + /** + * Rebuild the roads of this town for 6 months. + */ + TOWN_ACTION_ROAD_REBUILD = 3, + + /** + * Build a statue in this town. + */ + TOWN_ACTION_BUILD_STATUE = 4, + + /** + * Fund the creation of extra buildings for 3 months. + */ + TOWN_ACTION_FUND_BUILDINGS = 5, + + /** + * Buy exclusive rights for this town for 12 months. + */ + TOWN_ACTION_BUY_RIGHTS = 6, + + /** + * Bribe the town in order to get a higher rating. + */ + TOWN_ACTION_BRIBE = 7, + }; + + /** + * Different ratings one could have in a town. + */ + enum TownRating { + TOWN_RATING_NONE, ///< The company got no rating in the town. + TOWN_RATING_APPALLING, ///< The company got an appalling rating in the town . + TOWN_RATING_VERY_POOR, ///< The company got an very poor rating in the town. + TOWN_RATING_POOR, ///< The company got an poor rating in the town. + TOWN_RATING_MEDIOCRE, ///< The company got an mediocre rating in the town. + TOWN_RATING_GOOD, ///< The company got an good rating in the town. + TOWN_RATING_VERY_GOOD, ///< The company got an very good rating in the town. + TOWN_RATING_EXCELLENT, ///< The company got an excellent rating in the town. + TOWN_RATING_OUTSTANDING, ///< The company got an outstanding rating in the town. + INVALID_TOWN_RATING = -1, ///< The town rating for invalid towns/companies. + }; + + /** + * Gets the maximum town index; there are no valid towns with a higher index. + * @return The maximum town index. + * @post Return value is always non-negative. + */ + static TownID GetMaxTownID(); + + /** + * Gets the number of towns. This is different than GetMaxTownID() + * because of the way OpenTTD works internally. + * @return The number of towns. + * @post Return value is always non-negative. + */ + static int32 GetTownCount(); + + /** + * Checks whether the given town index is valid. + * @param town_id The index to check. + * @return True if and only if the town is valid. + */ + static bool IsValidTown(TownID town_id); + + /** + * Get the name of the town. + * @param town_id The town to get the name of. + * @pre IsValidTown(town_id). + * @return The name of the town. + */ + static const char *GetName(TownID town_id); + + /** + * Gets the number of inhabitants in the town. + * @param town_id The town to get the population of. + * @pre IsValidTown(town_id). + * @return The number of inhabitants. + * @post Return value is always non-negative. + */ + static int32 GetPopulation(TownID town_id); + + /** + * Gets the number of houses in the town. + * @param town_id The town to get the number of houses of. + * @pre IsValidTown(town_id). + * @return The number of houses. + * @post Return value is always non-negative. + */ + static int32 GetHouseCount(TownID town_id); + + /** + * Gets the location of the town. + * @param town_id The town to get the location of. + * @pre IsValidTown(town_id). + * @return The location of the town. + */ + static TileIndex GetLocation(TownID town_id); + + /** + * Get the total last month's production of the given cargo at a town. + * @param town_id The index of the town. + * @param cargo_id The index of the cargo. + * @pre IsValidTown(town_id). + * @pre AICargo::IsValidCargo(cargo_id). + * @pre AICargo::GetTownEffect(cargo_id) == TE_PASSENGERS || AICargo::GetTownEffect(cargo_id) == TE_MAIL. + * @return The last month's production of the given cargo for this town. + * @post Return value is always non-negative. + */ + static int32 GetLastMonthProduction(TownID town_id, CargoID cargo_id); + + /** + * Get the total amount of cargo transported from a town last month. + * @param town_id The index of the industry. + * @param cargo_id The index of the cargo. + * @pre IsValidTown(town_id). + * @pre AICargo::IsValidCargo(cargo_id). + * @pre AICargo::GetTownEffect(cargo_id) == TE_PASSENGERS || AICargo::GetTownEffect(cargo_id) == TE_MAIL. + * @return The amount of given cargo transported from this town last month. + * @post Return value is always non-negative. + */ + static int32 GetLastMonthTransported(TownID town_id, CargoID cargo_id); + + /** + * Get the maximum production of the given cargo at a town. + * @param town_id The index of the town. + * @param cargo_id The index of the cargo. + * @pre IsValidTown(town_id). + * @pre AICargo::IsValidCargo(cargo_id). + * @pre AICargo::GetTownEffect(cargo_id) == TE_PASSENGERS || AICargo::GetTownEffect(cargo_id) == TE_MAIL. + * @return The maximum production of the given cargo for this town. + * @post Return value is always non-negative. + */ + static int32 GetMaxProduction(TownID town_id, CargoID cargo_id); + + /** + * Get the manhattan distance from the tile to the AITown::GetLocation() + * of the town. + * @param town_id The town to get the distance to. + * @param tile The tile to get the distance to. + * @pre IsValidTown(town_id). + * @return The distance between town and tile. + */ + static int32 GetDistanceManhattanToTile(TownID town_id, TileIndex tile); + + /** + * Get the square distance from the tile to the AITown::GetLocation() + * of the town. + * @param town_id The town to get the distance to. + * @param tile The tile to get the distance to. + * @pre IsValidTown(town_id). + * @return The distance between town and tile. + */ + static int32 GetDistanceSquareToTile(TownID town_id, TileIndex tile); + + /** + * Find out if this tile is within the rating influence of a town. + * Stations on this tile influence the rating of the town. + * @param town_id The town to check. + * @param tile The tile to check. + * @pre IsValidTown(town_id). + * @return True if the tile is within the rating influence of the town. + */ + static bool IsWithinTownInfluence(TownID town_id, TileIndex tile); + + /** + * Find out if this town has a statue for the current company. + * @param town_id The town to check. + * @pre IsValidTown(town_id). + * @return True if the town has a statue. + */ + static bool HasStatue(TownID town_id); + + /** + * Find out how long the town is undergoing road reconstructions. + * @param town_id The town to check. + * @pre IsValidTown(town_id). + * @return The number of months the road reworks are still going to take. + * The value 0 means that there are currently no road reworks. + */ + static int GetRoadReworkDuration(TownID town_id); + + /** + * Find out which company currently has the exclusive rights of this town. + * @param town_id The town to check. + * @pre IsValidTown(town_id). + * @return The company that has the exclusive rights. The value + * AICompany::INVALID_COMPANY means that there are currently no + * exclusive rights given out to anyone. + */ + static AICompany::CompanyID GetExclusiveRightsCompany(TownID town_id); + + /** + * Find out how long the town is under influence of the exclusive rights. + * @param town_id The town to check. + * @pre IsValidTown(town_id). + * @return The number of months the exclusive rights hold. + * The value 0 means that there are currently no exclusive rights + * given out to anyone. + */ + static int32 GetExclusiveRightsDuration(TownID town_id); + + /** + * Find out if an action can currently be performed on the town. + * @param town_id The town to perform the action on. + * @param town_action The action to perform on the town. + * @pre IsValidTown(town_id). + * @return True if and only if the action can performed. + */ + static bool IsActionAvailable(TownID town_id, TownAction town_action); + + /** + * Perform a town action on this town. + * @param town_id The town to perform the action on. + * @param town_action The action to perform on the town. + * @pre IsValidTown(town_id). + * @pre IsActionAvailable(town_id, town_action). + * @return True if the action succeeded. + */ + static bool PerformTownAction(TownID town_id, TownAction town_action); + + /** + * Get the rating of a company within a town. + * @param town_id The town to get the rating for. + * @param company_id The company to get the rating for. + * @pre IsValidTown(town_id). + * @pre AICompany.ResolveCompanyID(company) != AICompany::INVALID_COMPANY. + * @return The rating as shown to humans. + */ + static TownRating GetRating(TownID town_id, AICompany::CompanyID company_id); + + /** + * Get the maximum level of noise that still can be added by airports + * before the town start to refuse building a new airport. + * @param town_id The town to get the allowed noise from. + * @return The noise that still can be added. + */ + static int GetAllowedNoise(TownID town_id); +}; + +#endif /* AI_TOWN_HPP */ diff --git a/src/ai/api/ai_town.hpp.sq b/src/ai/api/ai_town.hpp.sq new file mode 100644 index 000000000..664503923 --- /dev/null +++ b/src/ai/api/ai_town.hpp.sq @@ -0,0 +1,69 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_town.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AITown::TownAction GetParam(ForceType<AITown::TownAction>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AITown::TownAction)tmp; } + template <> int Return<AITown::TownAction>(HSQUIRRELVM vm, AITown::TownAction res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AITown::TownRating GetParam(ForceType<AITown::TownRating>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AITown::TownRating)tmp; } + template <> int Return<AITown::TownRating>(HSQUIRRELVM vm, AITown::TownRating res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AITown to be used as Squirrel parameter */ + template <> AITown *GetParam(ForceType<AITown *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITown *)instance; } + template <> AITown &GetParam(ForceType<AITown &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITown *)instance; } + template <> const AITown *GetParam(ForceType<const AITown *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITown *)instance; } + template <> const AITown &GetParam(ForceType<const AITown &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITown *)instance; } + template <> int Return<AITown *>(HSQUIRRELVM vm, AITown *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITown", res, NULL, DefSQDestructorCallback<AITown>); return 1; } +}; // namespace SQConvert + +void SQAITown_Register(Squirrel *engine) { + DefSQClass <AITown> SQAITown("AITown"); + SQAITown.PreRegister(engine); + SQAITown.AddConstructor<void (AITown::*)(), 1>(engine, "x"); + + SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_ADVERTISE_SMALL, "TOWN_ACTION_ADVERTISE_SMALL"); + SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_ADVERTISE_MEDIUM, "TOWN_ACTION_ADVERTISE_MEDIUM"); + SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_ADVERTISE_LARGE, "TOWN_ACTION_ADVERTISE_LARGE"); + SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_ROAD_REBUILD, "TOWN_ACTION_ROAD_REBUILD"); + SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_BUILD_STATUE, "TOWN_ACTION_BUILD_STATUE"); + SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_FUND_BUILDINGS, "TOWN_ACTION_FUND_BUILDINGS"); + SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_BUY_RIGHTS, "TOWN_ACTION_BUY_RIGHTS"); + SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_BRIBE, "TOWN_ACTION_BRIBE"); + SQAITown.DefSQConst(engine, AITown::TOWN_RATING_NONE, "TOWN_RATING_NONE"); + SQAITown.DefSQConst(engine, AITown::TOWN_RATING_APPALLING, "TOWN_RATING_APPALLING"); + SQAITown.DefSQConst(engine, AITown::TOWN_RATING_VERY_POOR, "TOWN_RATING_VERY_POOR"); + SQAITown.DefSQConst(engine, AITown::TOWN_RATING_POOR, "TOWN_RATING_POOR"); + SQAITown.DefSQConst(engine, AITown::TOWN_RATING_MEDIOCRE, "TOWN_RATING_MEDIOCRE"); + SQAITown.DefSQConst(engine, AITown::TOWN_RATING_GOOD, "TOWN_RATING_GOOD"); + SQAITown.DefSQConst(engine, AITown::TOWN_RATING_VERY_GOOD, "TOWN_RATING_VERY_GOOD"); + SQAITown.DefSQConst(engine, AITown::TOWN_RATING_EXCELLENT, "TOWN_RATING_EXCELLENT"); + SQAITown.DefSQConst(engine, AITown::TOWN_RATING_OUTSTANDING, "TOWN_RATING_OUTSTANDING"); + SQAITown.DefSQConst(engine, AITown::INVALID_TOWN_RATING, "INVALID_TOWN_RATING"); + + SQAITown.DefSQStaticMethod(engine, &AITown::GetClassName, "GetClassName", 1, "x"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetMaxTownID, "GetMaxTownID", 1, "x"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetTownCount, "GetTownCount", 1, "x"); + SQAITown.DefSQStaticMethod(engine, &AITown::IsValidTown, "IsValidTown", 2, "xi"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetName, "GetName", 2, "xi"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetPopulation, "GetPopulation", 2, "xi"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetHouseCount, "GetHouseCount", 2, "xi"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetLocation, "GetLocation", 2, "xi"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetLastMonthProduction, "GetLastMonthProduction", 3, "xii"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetLastMonthTransported, "GetLastMonthTransported", 3, "xii"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetMaxProduction, "GetMaxProduction", 3, "xii"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetDistanceManhattanToTile, "GetDistanceManhattanToTile", 3, "xii"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetDistanceSquareToTile, "GetDistanceSquareToTile", 3, "xii"); + SQAITown.DefSQStaticMethod(engine, &AITown::IsWithinTownInfluence, "IsWithinTownInfluence", 3, "xii"); + SQAITown.DefSQStaticMethod(engine, &AITown::HasStatue, "HasStatue", 2, "xi"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetRoadReworkDuration, "GetRoadReworkDuration", 2, "xi"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetExclusiveRightsCompany, "GetExclusiveRightsCompany", 2, "xi"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetExclusiveRightsDuration, "GetExclusiveRightsDuration", 2, "xi"); + SQAITown.DefSQStaticMethod(engine, &AITown::IsActionAvailable, "IsActionAvailable", 3, "xii"); + SQAITown.DefSQStaticMethod(engine, &AITown::PerformTownAction, "PerformTownAction", 3, "xii"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetRating, "GetRating", 3, "xii"); + SQAITown.DefSQStaticMethod(engine, &AITown::GetAllowedNoise, "GetAllowedNoise", 2, "xi"); + + SQAITown.PostRegister(engine); +} diff --git a/src/ai/api/ai_townlist.cpp b/src/ai/api/ai_townlist.cpp new file mode 100644 index 000000000..34612d857 --- /dev/null +++ b/src/ai/api/ai_townlist.cpp @@ -0,0 +1,15 @@ +/* $Id$ */ + +/** @file ai_townlist.cpp Implementation of AITownList and friends. */ + +#include "ai_townlist.hpp" +#include "../../openttd.h" +#include "../../town.h" + +AITownList::AITownList() +{ + Town *t; + FOR_ALL_TOWNS(t) { + this->AddItem(t->index); + } +} diff --git a/src/ai/api/ai_townlist.hpp b/src/ai/api/ai_townlist.hpp new file mode 100644 index 000000000..0e2eb1530 --- /dev/null +++ b/src/ai/api/ai_townlist.hpp @@ -0,0 +1,20 @@ +/* $Id$ */ + +/** @file ai_townlist.hpp List all the towns. */ + +#ifndef AI_TOWNLIST_HPP +#define AI_TOWNLIST_HPP + +#include "ai_abstractlist.hpp" + +/** + * Creates a list of towns that are currently on the map. + * @ingroup AIList + */ +class AITownList : public AIAbstractList { +public: + static const char *GetClassName() { return "AITownList"; } + AITownList(); +}; + +#endif /* AI_TOWNLIST_HPP */ diff --git a/src/ai/api/ai_townlist.hpp.sq b/src/ai/api/ai_townlist.hpp.sq new file mode 100644 index 000000000..31be65144 --- /dev/null +++ b/src/ai/api/ai_townlist.hpp.sq @@ -0,0 +1,23 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_townlist.hpp" + +namespace SQConvert { + /* Allow AITownList to be used as Squirrel parameter */ + template <> AITownList *GetParam(ForceType<AITownList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITownList *)instance; } + template <> AITownList &GetParam(ForceType<AITownList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITownList *)instance; } + template <> const AITownList *GetParam(ForceType<const AITownList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITownList *)instance; } + template <> const AITownList &GetParam(ForceType<const AITownList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITownList *)instance; } + template <> int Return<AITownList *>(HSQUIRRELVM vm, AITownList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITownList", res, NULL, DefSQDestructorCallback<AITownList>); return 1; } +}; // namespace SQConvert + +void SQAITownList_Register(Squirrel *engine) { + DefSQClass <AITownList> SQAITownList("AITownList"); + SQAITownList.PreRegister(engine, "AIAbstractList"); + SQAITownList.AddConstructor<void (AITownList::*)(), 1>(engine, "x"); + + SQAITownList.DefSQStaticMethod(engine, &AITownList::GetClassName, "GetClassName", 1, "x"); + + SQAITownList.PostRegister(engine); +} diff --git a/src/ai/api/ai_tunnel.cpp b/src/ai/api/ai_tunnel.cpp new file mode 100644 index 000000000..1991ef89b --- /dev/null +++ b/src/ai/api/ai_tunnel.cpp @@ -0,0 +1,119 @@ +/* $Id$ */ + +/** @file ai_tunnel.cpp Implementation of AITunnel. */ + +#include "ai_tunnel.hpp" +#include "ai_map.hpp" +#include "ai_rail.hpp" +#include "../ai_instance.hpp" +#include "../../openttd.h" +#include "../../landscape.h" +#include "../../tunnel_map.h" +#include "../../road_type.h" +#include "../../command_func.h" +#include "../../tunnelbridge.h" +#include "../../road_func.h" + +/* static */ bool AITunnel::IsTunnelTile(TileIndex tile) +{ + if (!::IsValidTile(tile)) return false; + return ::IsTunnelTile(tile); +} + +/* static */ TileIndex AITunnel::GetOtherTunnelEnd(TileIndex tile) +{ + if (!::IsValidTile(tile)) return INVALID_TILE; + + /* If it's a tunnel alread, take the easy way out! */ + if (IsTunnelTile(tile)) return ::GetOtherTunnelEnd(tile); + + ::DoCommand(tile, 0, 0, DC_AUTO, CMD_BUILD_TUNNEL); + return _build_tunnel_endtile == 0 ? INVALID_TILE : _build_tunnel_endtile; +} + +static void _DoCommandReturnBuildTunnel2(class AIInstance *instance) +{ + if (!AITunnel::_BuildTunnelRoad2()) { + AIObject::SetLastCommandRes(false); + AIInstance::DoCommandReturn(instance); + return; + } + + /* This can never happen, as in test-mode this callback is never executed, + * and in execute-mode, the other callback is called. */ + NOT_REACHED(); +} + +static void _DoCommandReturnBuildTunnel1(class AIInstance *instance) +{ + if (!AITunnel::_BuildTunnelRoad1()) { + AIObject::SetLastCommandRes(false); + AIInstance::DoCommandReturn(instance); + return; + } + + /* This can never happen, as in test-mode this callback is never executed, + * and in execute-mode, the other callback is called. */ + NOT_REACHED(); +} + +/* static */ bool AITunnel::BuildTunnel(AIVehicle::VehicleType vehicle_type, TileIndex start) +{ + EnforcePrecondition(false, ::IsValidTile(start)); + EnforcePrecondition(false, vehicle_type == AIVehicle::VEHICLE_RAIL || vehicle_type == AIVehicle::VEHICLE_ROAD); + EnforcePrecondition(false, vehicle_type != AIVehicle::VEHICLE_RAIL || AIRail::IsRailTypeAvailable(AIRail::GetCurrentRailType())); + + uint type = 0; + if (vehicle_type == AIVehicle::VEHICLE_ROAD) { + type |= (TRANSPORT_ROAD << 9); + type |= RoadTypeToRoadTypes((::RoadType)AIObject::GetRoadType()); + } else { + type |= (TRANSPORT_RAIL << 9); + type |= AIRail::GetCurrentRailType(); + } + + /* For rail we do nothing special */ + if (vehicle_type == AIVehicle::VEHICLE_RAIL) { + return AIObject::DoCommand(start, type, 0, CMD_BUILD_TUNNEL); + } + + AIObject::SetCallbackVariable(0, start); + if (!AIObject::DoCommand(start, type, 0, CMD_BUILD_TUNNEL, NULL, &_DoCommandReturnBuildTunnel1)) return false; + + /* In case of test-mode, test if we can build both road pieces */ + return _BuildTunnelRoad1(); +} + +/* static */ bool AITunnel::_BuildTunnelRoad1() +{ + /* Build the piece of road on the 'start' side of the tunnel */ + TileIndex end = AIObject::GetCallbackVariable(0); + TileIndex start = AITunnel::GetOtherTunnelEnd(end); + + DiagDirection dir_1 = (DiagDirection)((::TileX(start) == ::TileX(end)) ? (::TileY(start) < ::TileY(end) ? DIAGDIR_NW : DIAGDIR_SE) : (::TileX(start) < ::TileX(end) ? DIAGDIR_NE : DIAGDIR_SW)); + DiagDirection dir_2 = ::ReverseDiagDir(dir_1); + + if (!AIObject::DoCommand(start + ::TileOffsByDiagDir(dir_1), ::DiagDirToRoadBits(dir_2) | (AIObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD, NULL, &_DoCommandReturnBuildTunnel2)) return false; + + /* In case of test-mode, test the other road piece too */ + return _BuildTunnelRoad2(); +} + +/* static */ bool AITunnel::_BuildTunnelRoad2() +{ + /* Build the piece of road on the 'end' side of the tunnel */ + TileIndex end = AIObject::GetCallbackVariable(0); + TileIndex start = AITunnel::GetOtherTunnelEnd(end); + + DiagDirection dir_1 = (DiagDirection)((::TileX(start) == ::TileX(end)) ? (::TileY(start) < ::TileY(end) ? DIAGDIR_NW : DIAGDIR_SE) : (::TileX(start) < ::TileX(end) ? DIAGDIR_NE : DIAGDIR_SW)); + DiagDirection dir_2 = ::ReverseDiagDir(dir_1); + + return AIObject::DoCommand(end + ::TileOffsByDiagDir(dir_2), ::DiagDirToRoadBits(dir_1) | (AIObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD); +} + +/* static */ bool AITunnel::RemoveTunnel(TileIndex tile) +{ + EnforcePrecondition(false, IsTunnelTile(tile)); + + return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR); +} diff --git a/src/ai/api/ai_tunnel.hpp b/src/ai/api/ai_tunnel.hpp new file mode 100644 index 000000000..625237905 --- /dev/null +++ b/src/ai/api/ai_tunnel.hpp @@ -0,0 +1,103 @@ +/* $Id$ */ + +/** @file ai_tunnel.hpp Everything to query and build tunnels. */ + +#ifndef AI_TUNNEL_HPP +#define AI_TUNNEL_HPP + +#include "ai_object.hpp" +#include "ai_vehicle.hpp" + +/** + * Class that handles all tunnel related functions. + */ +class AITunnel : public AIObject { +public: + static const char *GetClassName() { return "AITunnel"; } + + /** + * All tunnel related errors. + */ + enum ErrorMessages { + + /** Base for bridge related errors */ + ERR_TUNNEL_BASE = AIError::ERR_CAT_TUNNEL << AIError::ERR_CAT_BIT_SIZE, + + /** Can't build tunnels on water */ + ERR_TUNNEL_CANNOT_BUILD_ON_WATER, // [STR_3807_CAN_T_BUILD_ON_WATER] + + /** The start tile must slope either North, South, West or East */ + ERR_TUNNEL_START_SITE_UNSUITABLE, // [STR_500B_SITE_UNSUITABLE_FOR_TUNNEL] + + /** An other tunnel is in the way */ + ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY, // [STR_5003_ANOTHER_TUNNEL_IN_THE_WAY] + + /** Unable to excavate land at the end to create the tunnel's exit */ + ERR_TUNNEL_END_SITE_UNSUITABLE, // [STR_5005_UNABLE_TO_EXCAVATE_LAND] + }; + + /** + * Check whether the tile is an entrance to a tunnel. + * @param tile The tile to check. + * @pre AIMap::IsValidTile(tile). + * @return True if and only if the tile is the beginning or end of a tunnel. + */ + static bool IsTunnelTile(TileIndex tile); + + /** + * Get the tile that exits on the other end of a (would be) tunnel starting + * at tile. + * @param tile The tile that is an entrance to a tunnel or the tile where you may want to build a tunnel. + * @pre AIMap::IsValidTile(tile). + * @return The TileIndex that is the other end of the (would be) tunnel, or + * INVALID_TILE if no other end was found (can't build tunnel). + */ + static TileIndex GetOtherTunnelEnd(TileIndex tile); + +#ifndef DOXYGEN_SKIP + /** + * Internal function to help BuildTunnel in case of road. + */ + static bool _BuildTunnelRoad1(); + + /** + * Internal function to help BuildTunnel in case of road. + */ + static bool _BuildTunnelRoad2(); +#endif + + /** + * Builds a tunnel starting at start. The direction of the tunnel depends + * on the slope of the start tile. Tunnels can be created for either + * rails or roads; use the appropriate AIVehicle::VehicleType. + * As an extra for road, this functions builds two half-pieces of road on + * each end of the tunnel, making it easier for you to connect it to your + * network. + * @param start Where to start the tunnel. + * @param vehicle_type The vehicle-type of tunnel to build. + * @pre AIMap::IsValidTile(start). + * @pre vehicle_type == AIVehicle::VEHICLE_ROAD || (vehicle_type == AIVehicle::VEHICLE_RAIL && + * AIRail::IsRailTypeAvailable(AIRail::GetCurrentRailType())). + * @exception AIError::ERR_AREA_NOT_CLEAR + * @exception AITunnel::ERR_TUNNEL_CANNOT_BUILD_ON_WATER + * @exception AITunnel::ERR_TUNNEL_START_SITE_UNSUITABLE + * @exception AITunnel::ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY + * @exception AITunnel::ERR_TUNNEL_END_SITE_UNSUITABLE + * @return Whether the tunnel has been/can be build or not. + * @note The slope of a tile can be determined by AITile::GetSlope(TileIndex). + * @note No matter if the road pieces were build or not, if building the + * tunnel succeeded, this function returns true. + */ + static bool BuildTunnel(AIVehicle::VehicleType vehicle_type, TileIndex start); + + /** + * Remove the tunnel whose entrance is located at tile. + * @param tile The tile that is an entrance to a tunnel. + * @pre AIMap::IsValidTile(tile) && IsTunnelTile(tile). + * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY + * @return Whether the tunnel has been/can be removed or not. + */ + static bool RemoveTunnel(TileIndex tile); +}; + +#endif /* AI_TUNNEL_HPP */ diff --git a/src/ai/api/ai_tunnel.hpp.sq b/src/ai/api/ai_tunnel.hpp.sq new file mode 100644 index 000000000..fd2f6959c --- /dev/null +++ b/src/ai/api/ai_tunnel.hpp.sq @@ -0,0 +1,47 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_tunnel.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AITunnel::ErrorMessages GetParam(ForceType<AITunnel::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AITunnel::ErrorMessages)tmp; } + template <> int Return<AITunnel::ErrorMessages>(HSQUIRRELVM vm, AITunnel::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AITunnel to be used as Squirrel parameter */ + template <> AITunnel *GetParam(ForceType<AITunnel *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITunnel *)instance; } + template <> AITunnel &GetParam(ForceType<AITunnel &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITunnel *)instance; } + template <> const AITunnel *GetParam(ForceType<const AITunnel *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITunnel *)instance; } + template <> const AITunnel &GetParam(ForceType<const AITunnel &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITunnel *)instance; } + template <> int Return<AITunnel *>(HSQUIRRELVM vm, AITunnel *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITunnel", res, NULL, DefSQDestructorCallback<AITunnel>); return 1; } +}; // namespace SQConvert + +void SQAITunnel_Register(Squirrel *engine) { + DefSQClass <AITunnel> SQAITunnel("AITunnel"); + SQAITunnel.PreRegister(engine); + SQAITunnel.AddConstructor<void (AITunnel::*)(), 1>(engine, "x"); + + SQAITunnel.DefSQConst(engine, AITunnel::ERR_TUNNEL_BASE, "ERR_TUNNEL_BASE"); + SQAITunnel.DefSQConst(engine, AITunnel::ERR_TUNNEL_CANNOT_BUILD_ON_WATER, "ERR_TUNNEL_CANNOT_BUILD_ON_WATER"); + SQAITunnel.DefSQConst(engine, AITunnel::ERR_TUNNEL_START_SITE_UNSUITABLE, "ERR_TUNNEL_START_SITE_UNSUITABLE"); + SQAITunnel.DefSQConst(engine, AITunnel::ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY, "ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY"); + SQAITunnel.DefSQConst(engine, AITunnel::ERR_TUNNEL_END_SITE_UNSUITABLE, "ERR_TUNNEL_END_SITE_UNSUITABLE"); + + AIError::RegisterErrorMap(STR_3807_CAN_T_BUILD_ON_WATER, AITunnel::ERR_TUNNEL_CANNOT_BUILD_ON_WATER); + AIError::RegisterErrorMap(STR_500B_SITE_UNSUITABLE_FOR_TUNNEL, AITunnel::ERR_TUNNEL_START_SITE_UNSUITABLE); + AIError::RegisterErrorMap(STR_5003_ANOTHER_TUNNEL_IN_THE_WAY, AITunnel::ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY); + AIError::RegisterErrorMap(STR_5005_UNABLE_TO_EXCAVATE_LAND, AITunnel::ERR_TUNNEL_END_SITE_UNSUITABLE); + + AIError::RegisterErrorMapString(AITunnel::ERR_TUNNEL_CANNOT_BUILD_ON_WATER, "ERR_TUNNEL_CANNOT_BUILD_ON_WATER"); + AIError::RegisterErrorMapString(AITunnel::ERR_TUNNEL_START_SITE_UNSUITABLE, "ERR_TUNNEL_START_SITE_UNSUITABLE"); + AIError::RegisterErrorMapString(AITunnel::ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY, "ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY"); + AIError::RegisterErrorMapString(AITunnel::ERR_TUNNEL_END_SITE_UNSUITABLE, "ERR_TUNNEL_END_SITE_UNSUITABLE"); + + SQAITunnel.DefSQStaticMethod(engine, &AITunnel::GetClassName, "GetClassName", 1, "x"); + SQAITunnel.DefSQStaticMethod(engine, &AITunnel::IsTunnelTile, "IsTunnelTile", 2, "xi"); + SQAITunnel.DefSQStaticMethod(engine, &AITunnel::GetOtherTunnelEnd, "GetOtherTunnelEnd", 2, "xi"); + SQAITunnel.DefSQStaticMethod(engine, &AITunnel::BuildTunnel, "BuildTunnel", 3, "xii"); + SQAITunnel.DefSQStaticMethod(engine, &AITunnel::RemoveTunnel, "RemoveTunnel", 2, "xi"); + + SQAITunnel.PostRegister(engine); +} diff --git a/src/ai/api/ai_types.hpp b/src/ai/api/ai_types.hpp new file mode 100644 index 000000000..086cf75e0 --- /dev/null +++ b/src/ai/api/ai_types.hpp @@ -0,0 +1,40 @@ +/* $Id$ */ + +/** @file ai_types.hpp Defines all the types of the game, like VehicleID, .... */ + +#ifndef AI_TYPES_HPP +#define AI_TYPES_HPP + +#include "../../core/overflowsafe_type.hpp" +#include "../../company_type.h" + +/* Define all types here, so we don't have to include the whole _type.h maze */ +typedef uint BridgeType; //!< Internal name, not of any use for you. +typedef byte CargoID; //!< The ID of a cargo. +class CommandCost; //!< The cost of a command. +typedef uint16 EngineID; //!< The ID of an engine. +typedef uint16 GroupID; //!< The ID of a group. +typedef uint16 IndustryID; //!< The ID of an industry. +typedef uint8 IndustryType; //!< The ID of an industry-type. +typedef OverflowSafeInt64 Money; //!< Money, stored in a 32bit/64bit safe way. For AIs money is always in pounds. +typedef uint16 SignID; //!< The ID of a sign. +typedef uint16 StationID; //!< The ID of a station. +typedef uint16 StringID; //!< The ID of a string. +typedef uint32 TileIndex; //!< The ID of a tile (just named differently). +typedef uint16 TownID; //!< The ID of a town. +typedef uint16 VehicleID; //!< The ID of a vehicle. + +/* Types we defined ourself, as the OpenTTD core doesn't have them (yet) */ +typedef uint AIErrorType; //!< The types of errors inside the NoAI framework. +typedef BridgeType BridgeID; //!< The ID of a bridge. +typedef uint16 SubsidyID; //!< The ID of a subsidy. + +#ifndef _SQUIRREL_H_ +/* Life becomes easier when we can tell about a function it needs the VM, but + * without really including 'squirrel.h'. */ +typedef struct SQVM *HSQUIRRELVM; //!< Pointer to Squirrel Virtual Machine. +typedef int SQInteger; //!< Squirrel Integer. +typedef struct SQObject HSQOBJECT; //!< Squirrel Object (fake declare) +#endif + +#endif /* AI_TYPES_HPP */ diff --git a/src/ai/api/ai_vehicle.cpp b/src/ai/api/ai_vehicle.cpp new file mode 100644 index 000000000..9532e3caf --- /dev/null +++ b/src/ai/api/ai_vehicle.cpp @@ -0,0 +1,389 @@ +/* $Id$ */ + +/** @file ai_vehicle.cpp Implementation of AIVehicle. */ + +#include "ai_vehicle.hpp" +#include "ai_engine.hpp" +#include "ai_cargo.hpp" +#include "ai_order.hpp" +#include "ai_gamesettings.hpp" +#include "../ai_instance.hpp" +#include "../../openttd.h" +#include "../../company_func.h" +#include "../../aircraft.h" +#include "../../string_func.h" +#include "../../strings_func.h" +#include "../../core/alloc_func.hpp" +#include "../../command_func.h" +#include "../../roadveh.h" +#include "../../train.h" +#include "../../vehicle_func.h" +#include "table/strings.h" + +/* static */ bool AIVehicle::IsValidVehicle(VehicleID vehicle_id) +{ + if (!::IsValidVehicleID(vehicle_id)) return false; + const Vehicle *v = ::GetVehicle(vehicle_id); + return v->owner == _current_company && (v->IsPrimaryVehicle() || (v->type == VEH_TRAIN && ::IsFreeWagon(v))); +} + +/* static */ int32 AIVehicle::GetNumWagons(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + + int num = 1; + if (::GetVehicle(vehicle_id)->type == VEH_TRAIN) { + const Vehicle *v = ::GetVehicle(vehicle_id); + while ((v = GetNextUnit(v)) != NULL) num++; + } + + return num; +} + +/* static */ int AIVehicle::GetLength(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + + const Vehicle *v = ::GetVehicle(vehicle_id); + switch (v->type) { + case VEH_ROAD: return v->u.road.cached_veh_length; + case VEH_TRAIN: return v->u.rail.cached_total_length; + default: return -1; + } +} + +/* static */ VehicleID AIVehicle::BuildVehicle(TileIndex depot, EngineID engine_id) +{ + EnforcePrecondition(INVALID_VEHICLE, AIEngine::IsValidEngine(engine_id)); + + ::VehicleType type = ::GetEngine(engine_id)->type; + + EnforcePreconditionCustomError(INVALID_VEHICLE, !AIGameSettings::IsDisabledVehicleType((AIVehicle::VehicleType)type), AIVehicle::ERR_VEHICLE_BUILD_DISABLED); + + if (!AIObject::DoCommand(depot, engine_id, 0, ::GetCmdBuildVeh(type), NULL, &AIInstance::DoCommandReturnVehicleID)) return INVALID_VEHICLE; + + /* In case of test-mode, we return VehicleID 0 */ + return 0; +} + +/* static */ VehicleID AIVehicle::CloneVehicle(TileIndex depot, VehicleID vehicle_id, bool share_orders) +{ + EnforcePrecondition(false, IsValidVehicle(vehicle_id)); + + if (!AIObject::DoCommand(depot, vehicle_id, share_orders, CMD_CLONE_VEHICLE, NULL, &AIInstance::DoCommandReturnVehicleID)) return INVALID_VEHICLE; + + /* In case of test-mode, we return VehicleID 0 */ + return 0; +} + +/* static */ bool AIVehicle::MoveWagon(VehicleID source_vehicle_id, int source_wagon, bool move_attached_wagons, int dest_vehicle_id, int dest_wagon) +{ + EnforcePrecondition(false, IsValidVehicle(source_vehicle_id) && source_wagon < GetNumWagons(source_vehicle_id)); + EnforcePrecondition(false, dest_vehicle_id == -1 || (IsValidVehicle(dest_vehicle_id) && dest_wagon < GetNumWagons(dest_vehicle_id))); + EnforcePrecondition(false, ::GetVehicle(source_vehicle_id)->type == VEH_TRAIN); + EnforcePrecondition(false, dest_vehicle_id == -1 || ::GetVehicle(dest_vehicle_id)->type == VEH_TRAIN); + + const Vehicle *v = ::GetVehicle(source_vehicle_id); + while (source_wagon-- > 0) v = GetNextUnit(v); + const Vehicle *w = NULL; + if (dest_vehicle_id != -1) { + w = ::GetVehicle(dest_vehicle_id); + while (dest_wagon-- > 0) w = GetNextUnit(w); + } + + return AIObject::DoCommand(0, v->index | ((w == NULL ? INVALID_VEHICLE : w->index) << 16), move_attached_wagons ? 1 : 0, CMD_MOVE_RAIL_VEHICLE); +} + +/* static */ int AIVehicle::GetRefitCapacity(VehicleID vehicle_id, CargoID cargo) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + if (!AICargo::IsValidCargo(cargo)) return -1; + + CommandCost res = ::DoCommand(0, vehicle_id, cargo, DC_QUERY_COST, GetCmdRefitVeh(::GetVehicle(vehicle_id))); + return CmdSucceeded(res) ? _returned_refit_capacity : -1; +} + +/* static */ bool AIVehicle::RefitVehicle(VehicleID vehicle_id, CargoID cargo) +{ + EnforcePrecondition(false, IsValidVehicle(vehicle_id) && AICargo::IsValidCargo(cargo)); + + return AIObject::DoCommand(0, vehicle_id, cargo, GetCmdRefitVeh(::GetVehicle(vehicle_id))); +} + + +/* static */ bool AIVehicle::SellVehicle(VehicleID vehicle_id) +{ + EnforcePrecondition(false, IsValidVehicle(vehicle_id)); + + const Vehicle *v = ::GetVehicle(vehicle_id); + return AIObject::DoCommand(0, vehicle_id, v->type == VEH_TRAIN ? 1 : 0, GetCmdSellVeh(v)); +} + +/* static */ bool AIVehicle::SellWagon(VehicleID vehicle_id, int wagon, bool sell_attached_wagons) +{ + EnforcePrecondition(false, IsValidVehicle(vehicle_id) && wagon < GetNumWagons(vehicle_id)); + EnforcePrecondition(false, ::GetVehicle(vehicle_id)->type == VEH_TRAIN); + + const Vehicle *v = ::GetVehicle(vehicle_id); + while (wagon-- > 0) v = GetNextUnit(v); + + return AIObject::DoCommand(0, v->index, sell_attached_wagons ? 1 : 0, CMD_SELL_RAIL_WAGON); +} + +/* static */ bool AIVehicle::SendVehicleToDepot(VehicleID vehicle_id) +{ + EnforcePrecondition(false, IsValidVehicle(vehicle_id)); + + return AIObject::DoCommand(0, vehicle_id, 0, GetCmdSendToDepot(::GetVehicle(vehicle_id))); +} + +/* static */ bool AIVehicle::IsInDepot(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return false; + return ::GetVehicle(vehicle_id)->IsInDepot(); +} + +/* static */ bool AIVehicle::IsStoppedInDepot(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return false; + return ::GetVehicle(vehicle_id)->IsStoppedInDepot(); +} + +/* static */ bool AIVehicle::StartStopVehicle(VehicleID vehicle_id) +{ + EnforcePrecondition(false, IsValidVehicle(vehicle_id)); + + return AIObject::DoCommand(0, vehicle_id, 0, CMD_START_STOP_VEHICLE); +} + +/* static */ bool AIVehicle::SkipToVehicleOrder(VehicleID vehicle_id, AIOrder::OrderPosition order_position) +{ + order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position); + + EnforcePrecondition(false, AIOrder::IsValidVehicleOrder(vehicle_id, order_position)); + + return AIObject::DoCommand(0, vehicle_id, order_position, CMD_SKIP_TO_ORDER); +} + +/* static */ bool AIVehicle::ReverseVehicle(VehicleID vehicle_id) +{ + EnforcePrecondition(false, IsValidVehicle(vehicle_id)); + EnforcePrecondition(false, ::GetVehicle(vehicle_id)->type == VEH_ROAD || ::GetVehicle(vehicle_id)->type == VEH_TRAIN); + + switch (::GetVehicle(vehicle_id)->type) { + case VEH_ROAD: return AIObject::DoCommand(0, vehicle_id, 0, CMD_TURN_ROADVEH); + case VEH_TRAIN: return AIObject::DoCommand(0, vehicle_id, 0, CMD_REVERSE_TRAIN_DIRECTION); + default: NOT_REACHED(); + } +} + +/* static */ bool AIVehicle::SetName(VehicleID vehicle_id, const char *name) +{ + EnforcePrecondition(false, IsValidVehicle(vehicle_id)); + EnforcePrecondition(false, !::StrEmpty(name)); + EnforcePreconditionCustomError(false, ::strlen(name) < MAX_LENGTH_VEHICLE_NAME_BYTES, AIError::ERR_PRECONDITION_STRING_TOO_LONG); + + return AIObject::DoCommand(0, vehicle_id, 0, CMD_RENAME_VEHICLE, name); +} + +/* static */ TileIndex AIVehicle::GetLocation(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return INVALID_TILE; + + const Vehicle *v = ::GetVehicle(vehicle_id); + if (v->type == VEH_AIRCRAFT) { + uint x = Clamp(v->x_pos / TILE_SIZE, 0, ::MapSizeX() - 2); + uint y = Clamp(v->y_pos / TILE_SIZE, 0, ::MapSizeY() - 2); + return ::TileXY(x, y); + } + + return v->tile; +} + +/* static */ EngineID AIVehicle::GetEngineType(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return INVALID_ENGINE; + + return ::GetVehicle(vehicle_id)->engine_type; +} + +/* static */ EngineID AIVehicle::GetWagonEngineType(VehicleID vehicle_id, int wagon) +{ + if (!IsValidVehicle(vehicle_id)) return INVALID_ENGINE; + if (wagon >= GetNumWagons(vehicle_id)) return INVALID_ENGINE; + + const Vehicle *v = ::GetVehicle(vehicle_id); + if (v->type == VEH_TRAIN) { + while (wagon-- > 0) v = GetNextUnit(v); + } + return v->engine_type; +} + +/* static */ int32 AIVehicle::GetUnitNumber(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + + return ::GetVehicle(vehicle_id)->unitnumber; +} + +/* static */ const char *AIVehicle::GetName(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return NULL; + + static const int len = 64; + char *vehicle_name = MallocT<char>(len); + + ::SetDParam(0, vehicle_id); + ::GetString(vehicle_name, STR_VEHICLE_NAME, &vehicle_name[len - 1]); + return vehicle_name; +} + +/* static */ int32 AIVehicle::GetAge(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + + return ::GetVehicle(vehicle_id)->age; +} + +/* static */ int32 AIVehicle::GetWagonAge(VehicleID vehicle_id, int wagon) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + if (wagon >= GetNumWagons(vehicle_id)) return -1; + + const Vehicle *v = ::GetVehicle(vehicle_id); + if (v->type == VEH_TRAIN) { + while (wagon-- > 0) v = GetNextUnit(v); + } + return v->age; +} + +/* static */ int32 AIVehicle::GetMaxAge(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + + return ::GetVehicle(vehicle_id)->max_age; +} + +/* static */ int32 AIVehicle::GetAgeLeft(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + + return ::GetVehicle(vehicle_id)->max_age - ::GetVehicle(vehicle_id)->age; +} + +/* static */ int32 AIVehicle::GetCurrentSpeed(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + + return ::GetVehicle(vehicle_id)->GetDisplaySpeed(); +} + +/* static */ AIVehicle::VehicleState AIVehicle::GetState(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return AIVehicle::VS_INVALID; + + const Vehicle *v = ::GetVehicle(vehicle_id); + byte vehstatus = v->vehstatus; + + if (vehstatus & ::VS_CRASHED) return AIVehicle::VS_CRASHED; + if (v->breakdown_ctr != 0) return AIVehicle::VS_BROKEN; + if (v->IsStoppedInDepot()) return AIVehicle::VS_IN_DEPOT; + if (vehstatus & ::VS_STOPPED) return AIVehicle::VS_STOPPED; + if (v->current_order.IsType(OT_LOADING)) return AIVehicle::VS_AT_STATION; + return AIVehicle::VS_RUNNING; +} + +/* static */ Money AIVehicle::GetRunningCost(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + + return ::GetVehicle(vehicle_id)->GetRunningCost() >> 8; +} + +/* static */ Money AIVehicle::GetProfitThisYear(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + + return ::GetVehicle(vehicle_id)->GetDisplayProfitThisYear(); +} + +/* static */ Money AIVehicle::GetProfitLastYear(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + + return ::GetVehicle(vehicle_id)->GetDisplayProfitLastYear(); +} + +/* static */ Money AIVehicle::GetCurrentValue(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + + return ::GetVehicle(vehicle_id)->value; +} + +/* static */ AIVehicle::VehicleType AIVehicle::GetVehicleType(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return VEHICLE_INVALID; + + switch (::GetVehicle(vehicle_id)->type) { + case VEH_ROAD: return VEHICLE_ROAD; + case VEH_TRAIN: return VEHICLE_RAIL; + case VEH_SHIP: return VEHICLE_WATER; + case VEH_AIRCRAFT: return VEHICLE_AIR; + default: return VEHICLE_INVALID; + } +} + +/* static */ AIRoad::RoadType AIVehicle::GetRoadType(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return AIRoad::ROADTYPE_INVALID; + if (GetVehicleType(vehicle_id) != VEHICLE_ROAD) return AIRoad::ROADTYPE_INVALID; + + return (AIRoad::RoadType)::GetVehicle(vehicle_id)->u.road.roadtype; +} + +/* static */ int32 AIVehicle::GetCapacity(VehicleID vehicle_id, CargoID cargo) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + if (!AICargo::IsValidCargo(cargo)) return -1; + + uint32 amount = 0; + for (const Vehicle *v = ::GetVehicle(vehicle_id); v != NULL; v = v->Next()) { + if (v->cargo_type == cargo) amount += v->cargo_cap; + } + + return amount; +} + +/* static */ int32 AIVehicle::GetCargoLoad(VehicleID vehicle_id, CargoID cargo) +{ + if (!IsValidVehicle(vehicle_id)) return -1; + if (!AICargo::IsValidCargo(cargo)) return -1; + + uint32 amount = 0; + for (const Vehicle *v = ::GetVehicle(vehicle_id); v != NULL; v = v->Next()) { + if (v->cargo_type == cargo) amount += v->cargo.Count(); + } + + return amount; +} + +/* static */ GroupID AIVehicle::GetGroupID(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return ::INVALID_GROUP; + + return ::GetVehicle(vehicle_id)->group_id; +} + +/* static */ bool AIVehicle::IsArticulated(VehicleID vehicle_id) +{ + if (!IsValidVehicle(vehicle_id)) return false; + if (GetVehicleType(vehicle_id) != VEHICLE_ROAD && GetVehicleType(vehicle_id) != VEHICLE_RAIL) return false; + + const Vehicle *v = ::GetVehicle(vehicle_id); + switch (v->type) { + case VEH_ROAD: return RoadVehHasArticPart(v); + case VEH_TRAIN: return EngineHasArticPart(v); + default: NOT_REACHED(); + } +} diff --git a/src/ai/api/ai_vehicle.hpp b/src/ai/api/ai_vehicle.hpp new file mode 100644 index 000000000..8120fe947 --- /dev/null +++ b/src/ai/api/ai_vehicle.hpp @@ -0,0 +1,477 @@ +/* $Id$ */ + +/** @file ai_vehicle.hpp Everything to query and build vehicles. */ + +#ifndef AI_VEHICLE_HPP +#define AI_VEHICLE_HPP + +#include "ai_object.hpp" +#include "ai_error.hpp" +#include "ai_road.hpp" +#include "ai_order.hpp" + +/** + * Class that handles all vehicle related functions. + */ +class AIVehicle : public AIObject { +public: + static const char *GetClassName() { return "AIVehicle"; } + + /** + * All vehicle related error messages. + */ + enum ErrorMessages { + /** Base for vehicle related errors */ + ERR_VEHICLE_BASE = AIError::ERR_CAT_VEHICLE << AIError::ERR_CAT_BIT_SIZE, + + /** Too many vehicles in the game, can't build any more. */ + ERR_VEHICLE_TOO_MANY, // [STR_00E1_TOO_MANY_VEHICLES_IN_GAME] + + /** Vehicle is not available */ + ERR_VEHICLE_NOT_AVAILABLE, // [STR_AIRCRAFT_NOT_AVAILABLE, STR_ROAD_VEHICLE_NOT_AVAILABLE, STR_SHIP_NOT_AVAILABLE, STR_RAIL_VEHICLE_NOT_AVAILABLE] + + /** Vehicle can't be build due to game settigns */ + ERR_VEHICLE_BUILD_DISABLED, // [STR_A008_CAN_T_BUILD_AIRCRAFT, STR_980D_CAN_T_BUILD_SHIP, STR_9009_CAN_T_BUILD_ROAD_VEHICLE, STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE] + + /** Vehicle can't be build in the selected depot */ + ERR_VEHICLE_WRONG_DEPOT, // [STR_DEPOT_WRONG_DEPOT_TYPE] + + /** Vehicle can't return to the depot */ + ERR_VEHICLE_CANNOT_SEND_TO_DEPOT, // [STR_8830_CAN_T_SEND_TRAIN_TO_DEPOT, STR_9018_CAN_T_SEND_VEHICLE_TO_DEPOT, STR_9819_CAN_T_SEND_SHIP_TO_DEPOT, STR_A012_CAN_T_SEND_AIRCRAFT_TO] + + /** Vehicle can't start / stop */ + ERR_VEHICLE_CANNOT_START_STOP, // [STR_883B_CAN_T_STOP_START_TRAIN, STR_9015_CAN_T_STOP_START_ROAD_VEHICLE, STR_9818_CAN_T_STOP_START_SHIP, STR_A016_CAN_T_STOP_START_AIRCRAFT] + + /** Vehicle can't turn */ + ERR_VEHICLE_CANNOT_TURN, // [STR_8869_CAN_T_REVERSE_DIRECTION, STR_9033_CAN_T_MAKE_VEHICLE_TURN] + + /** Vehicle can't be refit */ + ERR_VEHICLE_CANNOT_REFIT, // [STR_RAIL_CAN_T_REFIT_VEHICLE, STR_REFIT_ROAD_VEHICLE_CAN_T, STR_9841_CAN_T_REFIT_SHIP, STR_A042_CAN_T_REFIT_AIRCRAFT] + + /** Vehicle is destroyed */ + ERR_VEHICLE_IS_DESTROYED, // [STR_CAN_T_REFIT_DESTROYED_VEHICLE, STR_CAN_T_SELL_DESTROYED_VEHICLE] + + /** Vehicle is not in a depot */ + ERR_VEHICLE_NOT_IN_DEPOT, // [STR_A01B_AIRCRAFT_MUST_BE_STOPPED, STR_9013_MUST_BE_STOPPED_INSIDE, STR_TRAIN_MUST_BE_STOPPED, STR_980B_SHIP_MUST_BE_STOPPED_IN] + + /** Vehicle is flying */ + ERR_VEHICLE_IN_FLIGHT, // [STR_A017_AIRCRAFT_IS_IN_FLIGHT] + + /** Vehicle is without power */ + ERR_VEHCILE_NO_POWER, // [STR_TRAIN_START_NO_CATENARY] + + }; + + /** + * The type of a vehicle available in the game. Trams for example are + * road vehicles, as maglev is a rail vehicle. + */ + enum VehicleType { + /* Order IS important, as it now matches the internal state of the game for vehicle type */ + VEHICLE_RAIL, //!< Rail type vehicle. + VEHICLE_ROAD, //!< Road type vehicle (bus / truck). + VEHICLE_WATER, //!< Water type vehicle. + VEHICLE_AIR, //!< Air type vehicle. + VEHICLE_INVALID = 0xFF, //!< Invalid vehicle type. + }; + + /** + * The different states a vehicle can be in. + */ + enum VehicleState { + VS_RUNNING, //!< The vehicle is currently running. + VS_STOPPED, //!< The vehicle is stopped manually. + VS_IN_DEPOT, //!< The vehicle is stopped in the depot. + VS_AT_STATION, //!< The vehicle is stopped at a station and is currently loading or unloading. + VS_BROKEN, //!< The vehicle has broken down and will start running again in a while. + VS_CRASHED, //!< The vehicle is crashed (and will never run again). + + VS_INVALID = 0xFF, //!< An invalid vehicle state. + }; + + /** + * Checks whether the given vehicle is valid and owned by you. + * @param vehicle_id The vehicle to check. + * @return True if and only if the vehicle is valid. + */ + static bool IsValidVehicle(VehicleID vehicle_id); + + /** + * Get the number of wagons a vehicle has. + * @param vehicle_id The vehicle to get the number of wagons from. + * @pre IsValidVehicle(vehicle_id). + * @return The number of wagons the vehicle has. + */ + static int32 GetNumWagons(VehicleID vehicle_id); + + /** + * Set the name of a vehicle. + * @param vehicle_id The vehicle to set the name for. + * @param name The name for the vehicle. + * @pre IsValidVehicle(vehicle_id). + * @pre 'name' must have at least one character. + * @pre 'name' must have at most 30 characters. + * @exception AIError::ERR_NAME_IS_NOT_UNIQUE + * @return True if and only if the name was changed. + */ + static bool SetName(VehicleID vehicle_id, const char *name); + + /** + * Get the name of a vehicle. + * @param vehicle_id The vehicle to get the name of. + * @pre IsValidVehicle(vehicle_id). + * @return The name the vehicle has. + */ + static const char *GetName(VehicleID vehicle_id); + + /** + * Get the current location of a vehicle. + * @param vehicle_id The vehicle to get the location of. + * @pre IsValidVehicle(vehicle_id). + * @return The tile the vehicle is currently on. + */ + static TileIndex GetLocation(VehicleID vehicle_id); + + /** + * Get the engine-type of a vehicle. + * @param vehicle_id The vehicle to get the engine-type of. + * @pre IsValidVehicle(vehicle_id). + * @return The engine type the vehicle has. + */ + static EngineID GetEngineType(VehicleID vehicle_id); + + /** + * Get the engine-type of a wagon. + * @param vehicle_id The vehicle to get the engine-type of. + * @param wagon The wagon in the vehicle to get the engine-type of. + * @pre IsValidVehicle(vehicle_id). + * @pre wagon < GetNumWagons(vehicle_id). + * @return The engine type the vehicle has. + */ + static EngineID GetWagonEngineType(VehicleID vehicle_id, int wagon); + + /** + * Get the unitnumber of a vehicle. + * @param vehicle_id The vehicle to get the unitnumber of. + * @pre IsValidVehicle(vehicle_id). + * @return The unitnumber the vehicle has. + */ + static int32 GetUnitNumber(VehicleID vehicle_id); + + /** + * Get the current age of a vehicle. + * @param vehicle_id The vehicle to get the age of. + * @pre IsValidVehicle(vehicle_id). + * @return The current age the vehicle has. + * @note The age is in days. + */ + static int32 GetAge(VehicleID vehicle_id); + + /** + * Get the current age of a second (or third, etc.) engine in a train vehicle. + * @param vehicle_id The vehicle to get the age of. + * @param wagon The wagon in the vehicle to get the age of. + * @pre IsValidVehicle(vehicle_id). + * @pre wagon < GetNumWagons(vehicle_id). + * @return The current age the vehicle has. + * @note The age is in days. + */ + static int32 GetWagonAge(VehicleID vehicle_id, int wagon); + + /** + * Get the maximum age of a vehicle. + * @param vehicle_id The vehicle to get the age of. + * @pre IsValidVehicle(vehicle_id). + * @return The maximum age the vehicle has. + * @note The age is in days. + */ + static int32 GetMaxAge(VehicleID vehicle_id); + + /** + * Get the age a vehicle has left (maximum - current). + * @param vehicle_id The vehicle to get the age of. + * @pre IsValidVehicle(vehicle_id). + * @return The age the vehicle has left. + * @note The age is in days. + */ + static int32 GetAgeLeft(VehicleID vehicle_id); + + /** + * Get the current speed of a vehicle. + * @param vehicle_id The vehicle to get the age of. + * @pre IsValidVehicle(vehicle_id). + * @return The current speed of the vehicle. + * @note Speed is in km/h. + */ + static int32 GetCurrentSpeed(VehicleID vehicle_id); + + /** + * Get the current state of a vehicle. + * @param vehicle_id The vehicle to get the state of. + * @pre IsValidVehicle(vehicle_id). + * @return The current state of the vehicle. + */ + static VehicleState GetState(VehicleID vehicle_id); + + /** + * Get the running cost of this vehicle. + * @param vehicle_id The vehicle to get the age of. + * @pre IsValidVehicle(vehicle_id). + * @return The running cost of the vehicle per year. + * @note Cost is per year; divide by 364 to get per day. + * @note This is not equal to AIEngine::GetRunningCost for Trains, because + * wagons and second engines can add up in the calculation too. + */ + static Money GetRunningCost(VehicleID vehicle_id); + + /** + * Get the current profit of a vehicle. + * @param vehicle_id The vehicle to get the profit of. + * @pre IsValidVehicle(vehicle_id). + * @return The current profit the vehicle has. + */ + static Money GetProfitThisYear(VehicleID vehicle_id); + + /** + * Get the profit of last year of a vehicle. + * @param vehicle_id The vehicle to get the profit of. + * @pre IsValidVehicle(vehicle_id). + * @return The profit the vehicle had last year. + */ + static Money GetProfitLastYear(VehicleID vehicle_id); + + + /** + * Get the current value of a vehicle. + * @param vehicle_id The vehicle to get the value of. + * @pre IsValidVehicle(vehicle_id). + * @return The value the vehicle currently has (the amount you should get + * when you would sell the vehicle right now). + */ + static Money GetCurrentValue(VehicleID vehicle_id); + + /** + * Get the type of vehicle. + * @param vehicle_id The vehicle to get the type of. + * @pre IsValidVehicle(vehicle_id). + * @return The vehicle type. + */ + static AIVehicle::VehicleType GetVehicleType(VehicleID vehicle_id); + + /** + * Get the RoadType of the vehicle. + * @param vehicle_id The vehicle to get the RoadType of. + * @pre IsValidVehicle(vehicle_id). + * @pre GetVehicleType(vehicle_id) == VEHICLE_ROAD. + * @return The RoadType the vehicle has. + */ + static AIRoad::RoadType GetRoadType(VehicleID vehicle_id); + + /** + * Check if a vehicle is in a depot. + * @param vehicle_id The vehicle to check. + * @pre IsValidVehicle(vehicle_id). + * @return True if and only if the vehicle is in a depot. + */ + static bool IsInDepot(VehicleID vehicle_id); + + /** + * Check if a vehicle is in a depot and stopped. + * @param vehicle_id The vehicle to check. + * @pre IsValidVehicle(vehicle_id). + * @return True if and only if the vehicle is in a depot and stopped. + */ + static bool IsStoppedInDepot(VehicleID vehicle_id); + + /** + * Builds a vehicle with the given engine at the given depot. + * @param depot The depot where the vehicle will be build. + * @param engine_id The engine to use for this vehicle. + * @pre The tile at depot has a depot that can build the engine and + * is owned by you. + * @pre IsValidEngine(engine_id). + * @exception AIVehicle::ERR_VEHICLE_TOO_MANY + * @exception AIVehicle::ERR_VEHICLE_BUILD_DISABLED + * @exception AIVehicle::ERR_VEHICLE_WRONG_DEPOT + * @return The VehicleID of the new vehicle, or an invalid VehicleID when + * it failed. Check the return value using IsValidVehicle. In test-mode + * 0 is returned if it was successful; any other value indicates failure. + * @note In Test Mode it means you can't assign orders yet to this vehicle, + * as the vehicle isn't really built yet. Build it for real first before + * assigning orders. + */ + static VehicleID BuildVehicle(TileIndex depot, EngineID engine_id); + + /** + * Clones a vehicle at the given depot, copying or cloning it's orders. + * @param depot The depot where the vehicle will be build. + * @param vehicle_id The vehicle to use as example for the new vehicle. + * @param share_orders Should the orders be copied or shared? + * @pre The tile 'depot' has a depot on it, allowing 'vehicle_id'-type vehicles. + * @pre IsValidVehicle(vehicle_id). + * @exception AIVehicle::ERR_VEHICLE_TOO_MANY + * @exception AIVehicle::ERR_VEHICLE_BUILD_DISABLED + * @exception AIVehicle::ERR_VEHICLE_WRONG_DEPOT + * @return The VehicleID of the new vehicle, or an invalid VehicleID when + * it failed. Check the return value using IsValidVehicle. In test-mode + * 0 is returned if it was successful; any other value indicates failure. + */ + static VehicleID CloneVehicle(TileIndex depot, VehicleID vehicle_id, bool share_orders); + + /** + * Move a wagon after another wagon. + * @param source_vehicle_id The vehicle to move a wagon away from. + * @param source_wagon The wagon in source_vehicle to move. + * @param move_attached_wagons Also move all wagons attached to the wagon to move. + * @param dest_vehicle_id The vehicle to move the wagon to, or -1 to create a new vehicle. + * @param dest_wagon The wagon in dest_vehicle to place source_wagon after. + * @pre IsValidVehicle(source_vehicle_id). + * @pre source_wagon < GetNumWagons(source_vehicle_id). + * @pre dest_vehicle_id == -1 || (IsValidVehicle(dest_vehicle_id) && dest_wagon < GetNumWagons(dest_vehicle_id)). + * @pre GetVehicleType(source_vehicle_id) == VEHICLE_RAIL. + * @pre dest_vehicle_id == -1 || GetVehicleType(dest_vehicle_id) == VEHICLE_RAIL. + * @return Whether or not moving the wagon(s) succeeded. + */ + static bool MoveWagon(VehicleID source_vehicle_id, int source_wagon, bool move_attached_wagons, int dest_vehicle_id, int dest_wagon); + + /** + * Gets the capacity of the given vehicle when refited to the given cargo type. + * @param vehicle_id The vehicle to refit. + * @param cargo The cargo to refit to. + * @pre IsValidVehicle(vehicle_id). + * @pre AICargo::IsValidCargo(cargo). + * @pre You must own the vehicle. + * @pre The vehicle must be stopped in the depot. + * @return The capacity the vehicle will have when refited. + */ + static int GetRefitCapacity(VehicleID vehicle_id, CargoID cargo); + + /** + * Refits a vehicle to the given cargo type. + * @param vehicle_id The vehicle to refit. + * @param cargo The cargo to refit to. + * @pre IsValidVehicle(vehicle_id). + * @pre AICargo::IsValidCargo(cargo). + * @pre You must own the vehicle. + * @pre The vehicle must be stopped in the depot. + * @exception AIVehicle::ERR_VEHICLE_CANNOT_REFIT + * @exception AIVehicle::ERR_VEHICLE_IS_DESTROYED + * @exception AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT + * @return True if and only if the refit succeeded. + */ + static bool RefitVehicle(VehicleID vehicle_id, CargoID cargo); + + /** + * Sells the given vehicle. + * @param vehicle_id The vehicle to sell. + * @pre IsValidVehicle(vehicle_id). + * @pre You must own the vehicle. + * @pre The vehicle must be stopped in the depot. + * @exception AIVehicle::ERR_VEHICLE_IS_DESTROYED + * @exception AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT + * @return True if and only if the vehicle has been sold. + */ + static bool SellVehicle(VehicleID vehicle_id); + + /** + * Sells the given wagon from the vehicle. + * @param vehicle_id The vehicle to sell a wagon from. + * @param wagon The wagon to sell. + * @param sell_attached_wagons Sell all wagons attached to the one we want to sell. + * @pre IsValidVehicle(vehicle_id). + * @pre wagon < GetNumWagons(vehicle_id). + * @pre You must own the vehicle. + * @pre The vehicle must be stopped in the depot. + * @exception AIVehicle::ERR_VEHICLE_IS_DESTROYED + * @exception AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT + * @return True if and only if the wagon(s) has been sold. + */ + static bool SellWagon(VehicleID vehicle_id, int wagon, bool sell_attached_wagons); + + /** + * Sends the given vehicle to a depot. + * @param vehicle_id The vehicle to send to a depot. + * @pre IsValidVehicle(vehicle_id). + * @exception AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT + * @return True if and only if the vehicle has been sent to a depot. + */ + static bool SendVehicleToDepot(VehicleID vehicle_id); + + /** + * Starts or stops the given vehicle depending on the current state. + * @param vehicle_id The vehicle to start/stop. + * @pre IsValidVehicle(vehicle_id). + * @exception AIVehicle::ERR_VEHICLE_CANNOT_START_STOP + * @exception (For aircraft only): AIVehicle::ERR_VEHICLE_IN_FLIGHT + * @exception (For trains only): AIVehicle::ERR_VEHICLE_NO_POWER + * @return True if and only if the vehicle has been started or stopped. + */ + static bool StartStopVehicle(VehicleID vehicle_id); + + /** + * Skips the current order of the given vehicle. + * @param vehicle_id The vehicle to skip the order for. + * @param order_position The selected order to which we want to skip. + * @pre IsValidVehicleOrder(vehicle_id, order_position). + * @return True if and only if the order has been skipped. + */ + static bool SkipToVehicleOrder(VehicleID vehicle_id, AIOrder::OrderPosition order_position); + + /** + * Turn the given vehicle so it'll drive the other way. + * @param vehicle_id The vehicle to turn. + * @pre IsValidVehicle(vehicle_id). + * @pre GetVehicleType(vehicle_id) == VEHICLE_ROAD || GetVehicleType(vehicle_id) == VEHICLE_RAIL. + * @return True if and only if the vehicle has started to turn. + * @note Vehicles cannot always be reversed. For example busses and trucks need to be running + * and not be inside a depot. + */ + static bool ReverseVehicle(VehicleID vehicle_id); + + /** + * Get the maximum amount of a specific cargo the given vehicle can transport. + * @param vehicle_id The vehicle to get the capacity of. + * @param cargo The cargo to get the capacity for. + * @pre IsValidVehicle(vehicle_id). + * @pre AICargo::IsValidCargo(cargo). + * @return The maximum amount of the given cargo the vehicle can transport. + */ + static int32 GetCapacity(VehicleID vehicle_id, CargoID cargo); + + /** + * Get the length of a the total vehicle in 1/16's of a tile. + * @param vehicle_id The vehicle to get the length of. + * @pre IsValidVehicle(vehicle_id). + * @pre GetVehicleType(vehicle_id) == AIVehicle.VEHICLE_ROAD || GetVehicleType(vehicle_id) == AIVehicle.VEHICLE_RAIL. + * @return The length of the engine. + */ + static int GetLength(VehicleID vehicle_id); + + /** + * Get the amount of a specific cargo the given vehicle transports. + * @param vehicle_id The vehicle to get the load amount of. + * @param cargo The cargo to get the load amount for. + * @pre IsValidVehicle(vehicle_id). + * @pre AICargo::IsValidCargo(cargo). + * @return The amount of the given cargo the vehicle currently transports. + */ + static int32 GetCargoLoad(VehicleID vehicle_id, CargoID cargo); + + /** + * Get the group of a given vehicle. + * @param vehicle_id The vehicle to get the group from. + * @return The group of the given vehicle. + */ + static GroupID GetGroupID(VehicleID vehicle_id); + + /** + * Check if the vehicle is articulated. + * @param vehicle_id The vehicle to check. + * @pre IsValidVehicle(vehicle_id). + * @pre GetVehicleType(vehicle_id) == VEHICLE_ROAD || GetVehicleType(vehicle_id) == VEHICLE_RAIL. + * @return True if the vehicle is articulated. + */ + static bool IsArticulated(VehicleID vehicle_id); +}; + +#endif /* AI_VEHICLE_HPP */ diff --git a/src/ai/api/ai_vehicle.hpp.sq b/src/ai/api/ai_vehicle.hpp.sq new file mode 100644 index 000000000..87dce4893 --- /dev/null +++ b/src/ai/api/ai_vehicle.hpp.sq @@ -0,0 +1,141 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_vehicle.hpp" + +namespace SQConvert { + /* Allow enums to be used as Squirrel parameters */ + template <> AIVehicle::ErrorMessages GetParam(ForceType<AIVehicle::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIVehicle::ErrorMessages)tmp; } + template <> int Return<AIVehicle::ErrorMessages>(HSQUIRRELVM vm, AIVehicle::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AIVehicle::VehicleType GetParam(ForceType<AIVehicle::VehicleType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIVehicle::VehicleType)tmp; } + template <> int Return<AIVehicle::VehicleType>(HSQUIRRELVM vm, AIVehicle::VehicleType res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> AIVehicle::VehicleState GetParam(ForceType<AIVehicle::VehicleState>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIVehicle::VehicleState)tmp; } + template <> int Return<AIVehicle::VehicleState>(HSQUIRRELVM vm, AIVehicle::VehicleState res) { sq_pushinteger(vm, (int32)res); return 1; } + + /* Allow AIVehicle to be used as Squirrel parameter */ + template <> AIVehicle *GetParam(ForceType<AIVehicle *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIVehicle *)instance; } + template <> AIVehicle &GetParam(ForceType<AIVehicle &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIVehicle *)instance; } + template <> const AIVehicle *GetParam(ForceType<const AIVehicle *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIVehicle *)instance; } + template <> const AIVehicle &GetParam(ForceType<const AIVehicle &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIVehicle *)instance; } + template <> int Return<AIVehicle *>(HSQUIRRELVM vm, AIVehicle *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIVehicle", res, NULL, DefSQDestructorCallback<AIVehicle>); return 1; } +}; // namespace SQConvert + +void SQAIVehicle_Register(Squirrel *engine) { + DefSQClass <AIVehicle> SQAIVehicle("AIVehicle"); + SQAIVehicle.PreRegister(engine); + SQAIVehicle.AddConstructor<void (AIVehicle::*)(), 1>(engine, "x"); + + SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_BASE, "ERR_VEHICLE_BASE"); + SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_TOO_MANY, "ERR_VEHICLE_TOO_MANY"); + SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_NOT_AVAILABLE, "ERR_VEHICLE_NOT_AVAILABLE"); + SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_BUILD_DISABLED, "ERR_VEHICLE_BUILD_DISABLED"); + SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_WRONG_DEPOT, "ERR_VEHICLE_WRONG_DEPOT"); + SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT, "ERR_VEHICLE_CANNOT_SEND_TO_DEPOT"); + SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_CANNOT_START_STOP, "ERR_VEHICLE_CANNOT_START_STOP"); + SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_CANNOT_TURN, "ERR_VEHICLE_CANNOT_TURN"); + SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_CANNOT_REFIT, "ERR_VEHICLE_CANNOT_REFIT"); + SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_IS_DESTROYED, "ERR_VEHICLE_IS_DESTROYED"); + SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT, "ERR_VEHICLE_NOT_IN_DEPOT"); + SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_IN_FLIGHT, "ERR_VEHICLE_IN_FLIGHT"); + SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHCILE_NO_POWER, "ERR_VEHCILE_NO_POWER"); + SQAIVehicle.DefSQConst(engine, AIVehicle::VEHICLE_RAIL, "VEHICLE_RAIL"); + SQAIVehicle.DefSQConst(engine, AIVehicle::VEHICLE_ROAD, "VEHICLE_ROAD"); + SQAIVehicle.DefSQConst(engine, AIVehicle::VEHICLE_WATER, "VEHICLE_WATER"); + SQAIVehicle.DefSQConst(engine, AIVehicle::VEHICLE_AIR, "VEHICLE_AIR"); + SQAIVehicle.DefSQConst(engine, AIVehicle::VEHICLE_INVALID, "VEHICLE_INVALID"); + SQAIVehicle.DefSQConst(engine, AIVehicle::VS_RUNNING, "VS_RUNNING"); + SQAIVehicle.DefSQConst(engine, AIVehicle::VS_STOPPED, "VS_STOPPED"); + SQAIVehicle.DefSQConst(engine, AIVehicle::VS_IN_DEPOT, "VS_IN_DEPOT"); + SQAIVehicle.DefSQConst(engine, AIVehicle::VS_AT_STATION, "VS_AT_STATION"); + SQAIVehicle.DefSQConst(engine, AIVehicle::VS_BROKEN, "VS_BROKEN"); + SQAIVehicle.DefSQConst(engine, AIVehicle::VS_CRASHED, "VS_CRASHED"); + SQAIVehicle.DefSQConst(engine, AIVehicle::VS_INVALID, "VS_INVALID"); + + AIError::RegisterErrorMap(STR_00E1_TOO_MANY_VEHICLES_IN_GAME, AIVehicle::ERR_VEHICLE_TOO_MANY); + AIError::RegisterErrorMap(STR_AIRCRAFT_NOT_AVAILABLE, AIVehicle::ERR_VEHICLE_NOT_AVAILABLE); + AIError::RegisterErrorMap(STR_ROAD_VEHICLE_NOT_AVAILABLE, AIVehicle::ERR_VEHICLE_NOT_AVAILABLE); + AIError::RegisterErrorMap(STR_SHIP_NOT_AVAILABLE, AIVehicle::ERR_VEHICLE_NOT_AVAILABLE); + AIError::RegisterErrorMap(STR_RAIL_VEHICLE_NOT_AVAILABLE, AIVehicle::ERR_VEHICLE_NOT_AVAILABLE); + AIError::RegisterErrorMap(STR_A008_CAN_T_BUILD_AIRCRAFT, AIVehicle::ERR_VEHICLE_BUILD_DISABLED); + AIError::RegisterErrorMap(STR_980D_CAN_T_BUILD_SHIP, AIVehicle::ERR_VEHICLE_BUILD_DISABLED); + AIError::RegisterErrorMap(STR_9009_CAN_T_BUILD_ROAD_VEHICLE, AIVehicle::ERR_VEHICLE_BUILD_DISABLED); + AIError::RegisterErrorMap(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE, AIVehicle::ERR_VEHICLE_BUILD_DISABLED); + AIError::RegisterErrorMap(STR_DEPOT_WRONG_DEPOT_TYPE, AIVehicle::ERR_VEHICLE_WRONG_DEPOT); + AIError::RegisterErrorMap(STR_8830_CAN_T_SEND_TRAIN_TO_DEPOT, AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT); + AIError::RegisterErrorMap(STR_9018_CAN_T_SEND_VEHICLE_TO_DEPOT, AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT); + AIError::RegisterErrorMap(STR_9819_CAN_T_SEND_SHIP_TO_DEPOT, AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT); + AIError::RegisterErrorMap(STR_A012_CAN_T_SEND_AIRCRAFT_TO, AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT); + AIError::RegisterErrorMap(STR_883B_CAN_T_STOP_START_TRAIN, AIVehicle::ERR_VEHICLE_CANNOT_START_STOP); + AIError::RegisterErrorMap(STR_9015_CAN_T_STOP_START_ROAD_VEHICLE, AIVehicle::ERR_VEHICLE_CANNOT_START_STOP); + AIError::RegisterErrorMap(STR_9818_CAN_T_STOP_START_SHIP, AIVehicle::ERR_VEHICLE_CANNOT_START_STOP); + AIError::RegisterErrorMap(STR_A016_CAN_T_STOP_START_AIRCRAFT, AIVehicle::ERR_VEHICLE_CANNOT_START_STOP); + AIError::RegisterErrorMap(STR_8869_CAN_T_REVERSE_DIRECTION, AIVehicle::ERR_VEHICLE_CANNOT_TURN); + AIError::RegisterErrorMap(STR_9033_CAN_T_MAKE_VEHICLE_TURN, AIVehicle::ERR_VEHICLE_CANNOT_TURN); + AIError::RegisterErrorMap(STR_RAIL_CAN_T_REFIT_VEHICLE, AIVehicle::ERR_VEHICLE_CANNOT_REFIT); + AIError::RegisterErrorMap(STR_REFIT_ROAD_VEHICLE_CAN_T, AIVehicle::ERR_VEHICLE_CANNOT_REFIT); + AIError::RegisterErrorMap(STR_9841_CAN_T_REFIT_SHIP, AIVehicle::ERR_VEHICLE_CANNOT_REFIT); + AIError::RegisterErrorMap(STR_A042_CAN_T_REFIT_AIRCRAFT, AIVehicle::ERR_VEHICLE_CANNOT_REFIT); + AIError::RegisterErrorMap(STR_CAN_T_REFIT_DESTROYED_VEHICLE, AIVehicle::ERR_VEHICLE_IS_DESTROYED); + AIError::RegisterErrorMap(STR_CAN_T_SELL_DESTROYED_VEHICLE, AIVehicle::ERR_VEHICLE_IS_DESTROYED); + AIError::RegisterErrorMap(STR_A01B_AIRCRAFT_MUST_BE_STOPPED, AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT); + AIError::RegisterErrorMap(STR_9013_MUST_BE_STOPPED_INSIDE, AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT); + AIError::RegisterErrorMap(STR_TRAIN_MUST_BE_STOPPED, AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT); + AIError::RegisterErrorMap(STR_980B_SHIP_MUST_BE_STOPPED_IN, AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT); + AIError::RegisterErrorMap(STR_A017_AIRCRAFT_IS_IN_FLIGHT, AIVehicle::ERR_VEHICLE_IN_FLIGHT); + AIError::RegisterErrorMap(STR_TRAIN_START_NO_CATENARY, AIVehicle::ERR_VEHCILE_NO_POWER); + + AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_TOO_MANY, "ERR_VEHICLE_TOO_MANY"); + AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_NOT_AVAILABLE, "ERR_VEHICLE_NOT_AVAILABLE"); + AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_BUILD_DISABLED, "ERR_VEHICLE_BUILD_DISABLED"); + AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_WRONG_DEPOT, "ERR_VEHICLE_WRONG_DEPOT"); + AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT, "ERR_VEHICLE_CANNOT_SEND_TO_DEPOT"); + AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_CANNOT_START_STOP, "ERR_VEHICLE_CANNOT_START_STOP"); + AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_CANNOT_TURN, "ERR_VEHICLE_CANNOT_TURN"); + AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_CANNOT_REFIT, "ERR_VEHICLE_CANNOT_REFIT"); + AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_IS_DESTROYED, "ERR_VEHICLE_IS_DESTROYED"); + AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT, "ERR_VEHICLE_NOT_IN_DEPOT"); + AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_IN_FLIGHT, "ERR_VEHICLE_IN_FLIGHT"); + AIError::RegisterErrorMapString(AIVehicle::ERR_VEHCILE_NO_POWER, "ERR_VEHCILE_NO_POWER"); + + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetClassName, "GetClassName", 1, "x"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::IsValidVehicle, "IsValidVehicle", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetNumWagons, "GetNumWagons", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::SetName, "SetName", 3, "xis"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetName, "GetName", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetLocation, "GetLocation", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetEngineType, "GetEngineType", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetWagonEngineType, "GetWagonEngineType", 3, "xii"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetUnitNumber, "GetUnitNumber", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetAge, "GetAge", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetWagonAge, "GetWagonAge", 3, "xii"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetMaxAge, "GetMaxAge", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetAgeLeft, "GetAgeLeft", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetCurrentSpeed, "GetCurrentSpeed", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetState, "GetState", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetRunningCost, "GetRunningCost", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetProfitThisYear, "GetProfitThisYear", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetProfitLastYear, "GetProfitLastYear", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetCurrentValue, "GetCurrentValue", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetVehicleType, "GetVehicleType", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetRoadType, "GetRoadType", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::IsInDepot, "IsInDepot", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::IsStoppedInDepot, "IsStoppedInDepot", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::BuildVehicle, "BuildVehicle", 3, "xii"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::CloneVehicle, "CloneVehicle", 4, "xiib"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::MoveWagon, "MoveWagon", 6, "xiibii"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetRefitCapacity, "GetRefitCapacity", 3, "xii"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::RefitVehicle, "RefitVehicle", 3, "xii"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::SellVehicle, "SellVehicle", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::SellWagon, "SellWagon", 4, "xiib"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::SendVehicleToDepot, "SendVehicleToDepot", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::StartStopVehicle, "StartStopVehicle", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::SkipToVehicleOrder, "SkipToVehicleOrder", 3, "xii"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::ReverseVehicle, "ReverseVehicle", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetCapacity, "GetCapacity", 3, "xii"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetLength, "GetLength", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetCargoLoad, "GetCargoLoad", 3, "xii"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetGroupID, "GetGroupID", 2, "xi"); + SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::IsArticulated, "IsArticulated", 2, "xi"); + + SQAIVehicle.PostRegister(engine); +} diff --git a/src/ai/api/ai_vehiclelist.cpp b/src/ai/api/ai_vehiclelist.cpp new file mode 100644 index 000000000..127ac77e9 --- /dev/null +++ b/src/ai/api/ai_vehiclelist.cpp @@ -0,0 +1,37 @@ +/* $Id$ */ + +/** @file ai_vehiclelist.cpp Implementation of AIVehicleList and friends. */ + +#include "ai_vehiclelist.hpp" +#include "ai_station.hpp" +#include "../../openttd.h" +#include "../../company_func.h" +#include "../../vehicle_base.h" + +AIVehicleList::AIVehicleList() +{ + Vehicle *v; + FOR_ALL_VEHICLES(v) { + if (v->owner == _current_company && v->IsPrimaryVehicle()) this->AddItem(v->index); + } +} + +AIVehicleList_Station::AIVehicleList_Station(StationID station_id) +{ + if (!AIStation::IsValidStation(station_id)) return; + + Vehicle *v; + + FOR_ALL_VEHICLES(v) { + if (v->owner == _current_company && v->IsPrimaryVehicle()) { + const Order *order; + + FOR_VEHICLE_ORDERS(v, order) { + if (order->IsType(OT_GOTO_STATION) && order->GetDestination() == station_id) { + this->AddItem(v->index); + break; + } + } + } + } +} diff --git a/src/ai/api/ai_vehiclelist.hpp b/src/ai/api/ai_vehiclelist.hpp new file mode 100644 index 000000000..0e91d75bc --- /dev/null +++ b/src/ai/api/ai_vehiclelist.hpp @@ -0,0 +1,34 @@ +/* $Id$ */ + +/** @file ai_vehiclelist.hpp List all the vehicles (you own). */ + +#ifndef AI_VEHICLELIST_HPP +#define AI_VEHICLELIST_HPP + +#include "ai_abstractlist.hpp" + +/** + * Creates a list of vehicles of which you are the owner. + * @ingroup AIList + */ +class AIVehicleList : public AIAbstractList { +public: + static const char *GetClassName() { return "AIVehicleList"; } + AIVehicleList(); +}; + +/** + * Creates a list of vehicles that have orders to a given station. + * @ingroup AIList + */ +class AIVehicleList_Station : public AIAbstractList { +public: + static const char *GetClassName() { return "AIVehicleList_Station"; } + + /** + * @param station_id The station to get the list of vehicles that have orders to him from. + */ + AIVehicleList_Station(StationID station_id); +}; + +#endif /* AI_VEHICLELIST_HPP */ diff --git a/src/ai/api/ai_vehiclelist.hpp.sq b/src/ai/api/ai_vehiclelist.hpp.sq new file mode 100644 index 000000000..5a5f930cb --- /dev/null +++ b/src/ai/api/ai_vehiclelist.hpp.sq @@ -0,0 +1,42 @@ +/* $Id$ */ +/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */ + +#include "ai_vehiclelist.hpp" + +namespace SQConvert { + /* Allow AIVehicleList to be used as Squirrel parameter */ + template <> AIVehicleList *GetParam(ForceType<AIVehicleList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIVehicleList *)instance; } + template <> AIVehicleList &GetParam(ForceType<AIVehicleList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIVehicleList *)instance; } + template <> const AIVehicleList *GetParam(ForceType<const AIVehicleList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIVehicleList *)instance; } + template <> const AIVehicleList &GetParam(ForceType<const AIVehicleList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIVehicleList *)instance; } + template <> int Return<AIVehicleList *>(HSQUIRRELVM vm, AIVehicleList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIVehicleList", res, NULL, DefSQDestructorCallback<AIVehicleList>); return 1; } +}; // namespace SQConvert + +void SQAIVehicleList_Register(Squirrel *engine) { + DefSQClass <AIVehicleList> SQAIVehicleList("AIVehicleList"); + SQAIVehicleList.PreRegister(engine, "AIAbstractList"); + SQAIVehicleList.AddConstructor<void (AIVehicleList::*)(), 1>(engine, "x"); + + SQAIVehicleList.DefSQStaticMethod(engine, &AIVehicleList::GetClassName, "GetClassName", 1, "x"); + + SQAIVehicleList.PostRegister(engine); +} + +namespace SQConvert { + /* Allow AIVehicleList_Station to be used as Squirrel parameter */ + template <> AIVehicleList_Station *GetParam(ForceType<AIVehicleList_Station *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIVehicleList_Station *)instance; } + template <> AIVehicleList_Station &GetParam(ForceType<AIVehicleList_Station &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIVehicleList_Station *)instance; } + template <> const AIVehicleList_Station *GetParam(ForceType<const AIVehicleList_Station *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIVehicleList_Station *)instance; } + template <> const AIVehicleList_Station &GetParam(ForceType<const AIVehicleList_Station &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIVehicleList_Station *)instance; } + template <> int Return<AIVehicleList_Station *>(HSQUIRRELVM vm, AIVehicleList_Station *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIVehicleList_Station", res, NULL, DefSQDestructorCallback<AIVehicleList_Station>); return 1; } +}; // namespace SQConvert + +void SQAIVehicleList_Station_Register(Squirrel *engine) { + DefSQClass <AIVehicleList_Station> SQAIVehicleList_Station("AIVehicleList_Station"); + SQAIVehicleList_Station.PreRegister(engine, "AIAbstractList"); + SQAIVehicleList_Station.AddConstructor<void (AIVehicleList_Station::*)(StationID station_id), 2>(engine, "xi"); + + SQAIVehicleList_Station.DefSQStaticMethod(engine, &AIVehicleList_Station::GetClassName, "GetClassName", 1, "x"); + + SQAIVehicleList_Station.PostRegister(engine); +} diff --git a/src/ai/api/squirrel_export.awk b/src/ai/api/squirrel_export.awk new file mode 100644 index 000000000..877586722 --- /dev/null +++ b/src/ai/api/squirrel_export.awk @@ -0,0 +1,385 @@ +# $Id$ +# +# Awk script to automatically generate the code needed +# to export the AI API to Squirrel. +# +# Note that arrays are 1 based... +# + +# Simple insertion sort. +function array_sort(ARRAY, ELEMENTS, temp, i, j) { + for (i = 2; i <= ELEMENTS; i++) + for (j = i; ARRAY[j - 1] > ARRAY[j]; --j) { + temp = ARRAY[j] + ARRAY[j] = ARRAY[j - 1] + ARRAY[j - 1] = temp + } + return +} + +function dump_class_templates(name) { + print " template <> " name " *GetParam(ForceType<" name " *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (" name " *)instance; }" + print " template <> " name " &GetParam(ForceType<" name " &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(" name " *)instance; }" + print " template <> const " name " *GetParam(ForceType<const " name " *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (" name " *)instance; }" + print " template <> const " name " &GetParam(ForceType<const " name " &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(" name " *)instance; }" + if (name == "AIEvent") { + print " template <> int Return<" name " *>(HSQUIRRELVM vm, " name " *res) { if (res == NULL) { sq_pushnull(vm); return 1; } Squirrel::CreateClassInstanceVM(vm, \"" name "\", res, NULL, DefSQDestructorCallback<" name ">); return 1; }" + } else { + print " template <> int Return<" name " *>(HSQUIRRELVM vm, " name " *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, \"" name "\", res, NULL, DefSQDestructorCallback<" name ">); return 1; }" + } +} + +BEGIN { + enum_size = 0 + enum_value_size = 0 + enum_string_to_error_size = 0 + enum_error_to_string_size = 0 + struct_size = 0 + method_size = 0 + static_method_size = 0 + virtual_class = "false" + super_cls = "" + cls = "" + start_squirrel_define_on_next_line = "false" + cls_level = 0 + RS = "\r|\n" +} + +/@file/ { + # Break it in two lines, so SVN doesn't replace it + printf "/* $I" + print "d$ */" + print "/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */" + print "" + print "#include \"" $3 "\"" +} + +# Remove the old squirrel stuff +/#ifdef DEFINE_SQUIRREL_CLASS/ { squirrel_stuff = "true"; next; } +/^#endif \/\* DEFINE_SQUIRREL_CLASS \*\// { if (squirrel_stuff == "true") { squirrel_stuff = "false"; next; } } +{ if (squirrel_stuff == "true") next; } + +# Ignore forward declarations of classes +/^( *)class(.*);/ { next; } +# We only want to have public functions exported for now +/^( *)class/ { + if (cls_level == 0) { + public = "false" + cls_param[0] = "" + cls_param[1] = 1 + cls_param[2] = "x" + cls = $2 + if (match($4, "public") || match($4, "protected") || match($4, "private")) { + super_cls = $5 + } else { + super_cls = $4 + } + } else if (cls_level == 1) { + struct_size++ + structs[struct_size] = cls "::" $2 + } + cls_level++ + next +} +/^( *)public/ { if (cls_level == 1) public = "true"; next; } +/^( *)protected/ { if (cls_level == 1) public = "false"; next; } +/^( *)private/ { if (cls_level == 1) public = "false"; next; } + +# Ignore special doxygen blocks +/^#ifndef DOXYGEN_SKIP/ { doxygen_skip = "next"; next; } +/^#ifdef DOXYGEN_SKIP/ { doxygen_skip = "true"; next; } +/^#endif/ { doxygen_skip = "false"; next; } +/^#else/ { + if (doxygen_skip == "next") { + doxygen_skip = "true"; + } else { + doxygen_skip = "false"; + } + next; +} +{ if (doxygen_skip == "true") next } + +# Ignore the comments +/^#/ { next; } +/\/\*.*\*\// { comment = "false"; next; } +/\/\*/ { comment = "true"; next; } +/\*\// { comment = "false"; next; } +{ if (comment == "true") next } + +# We need to make specialized conversions for structs +/^( *)struct/ { + cls_level++ + if (public == "false") next + if (cls_level != 1) next + struct_size++ + structs[struct_size] = cls "::" $2 + next +} + +# We need to make specialized conversions for enums +/^( *)enum/ { + cls_level++ + if (public == "false") next; + in_enum = "true" + enum_size++ + enums[enum_size] = cls "::" $2 + next +} + +# Maybe the end of the class, if so we can start with the Squirrel export pretty soon +/};/ { + cls_level-- + if (cls_level != 0) { + in_enum = "false"; + next; + } + if (cls == "") { + next; + } + start_squirrel_define_on_next_line = "true" + next; +} + +# Empty/white lines. When we may do the Squirrel export, do that export. +/^([ ]*)$/ { + if (start_squirrel_define_on_next_line == "false") next + spaces = " "; + public = "false" + namespace_opened = "false" + + print "" + + # First check whether we have enums to print + if (enum_size != 0) { + if (namespace_opened == "false") { + print "namespace SQConvert {" + namespace_opened = "true" + } + print " /* Allow enums to be used as Squirrel parameters */" + for (i = 1; i <= enum_size; i++) { + print " template <> " enums[i] " GetParam(ForceType<" enums[i] ">, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (" enums[i] ")tmp; }" + print " template <> int Return<" enums[i] ">(HSQUIRRELVM vm, " enums[i] " res) { sq_pushinteger(vm, (int32)res); return 1; }" + delete enums[i] + } + } + + # Then check whether we have structs/classes to print + if (struct_size != 0) { + if (namespace_opened == "false") { + print "namespace SQConvert {" + namespace_opened = "true" + } + print " /* Allow inner classes/structs to be used as Squirrel parameters */" + for (i = 1; i <= struct_size; i++) { + dump_class_templates(structs[i]) + delete structs[i] + } + } + + if (namespace_opened == "false") { + print "namespace SQConvert {" + namespace_opened = "true" + } else { + print "" + } + print " /* Allow " cls " to be used as Squirrel parameter */" + dump_class_templates(cls) + + print "}; // namespace SQConvert" + + print ""; + # Then do the registration functions of the class. */ + print "void SQ" cls "_Register(Squirrel *engine) {" + print " DefSQClass <" cls "> SQ" cls "(\"" cls "\");" + if (super_cls == "AIObject" || super_cls == "AIAbstractList::Valuator") { + print " SQ" cls ".PreRegister(engine);" + } else { + print " SQ" cls ".PreRegister(engine, \"" super_cls "\");" + } + if (virtual_class == "false" && super_cls != "AIEvent") { + print " SQ" cls ".AddConstructor<void (" cls "::*)(" cls_param[0] "), " cls_param[1]">(engine, \"" cls_param[2] "\");" + } + print "" + + # Enum values + mlen = 0 + for (i = 1; i <= enum_value_size; i++) { + if (mlen <= length(enum_value[i])) mlen = length(enum_value[i]) + } + for (i = 1; i <= enum_value_size; i++) { + print " SQ" cls ".DefSQConst(engine, " cls "::" enum_value[i] ", " substr(spaces, 1, mlen - length(enum_value[i])) "\"" enum_value[i] "\");" + delete enum_value[i] + } + if (enum_value_size != 0) print "" + + # Mapping of OTTD strings to errors + mlen = 0 + for (i = 1; i <= enum_string_to_error_size; i++) { + if (mlen <= length(enum_string_to_error_mapping_string[i])) mlen = length(enum_string_to_error_mapping_string[i]) + } + for (i = 1; i <= enum_string_to_error_size; i++) { + print " AIError::RegisterErrorMap(" enum_string_to_error_mapping_string[i] ", " substr(spaces, 1, mlen - length(enum_string_to_error_mapping_string[i])) cls "::" enum_string_to_error_mapping_error[i] ");" + + delete enum_string_to_error_mapping_string[i] + } + if (enum_string_to_error_size != 0) print "" + + # Mapping of errors to human 'readable' strings. + mlen = 0 + for (i = 1; i <= enum_error_to_string_size; i++) { + if (mlen <= length(enum_error_to_string_mapping[i])) mlen = length(enum_error_to_string_mapping[i]) + } + for (i = 1; i <= enum_error_to_string_size; i++) { + print " AIError::RegisterErrorMapString(" cls "::" enum_error_to_string_mapping[i] ", " substr(spaces, 1, mlen - length(enum_error_to_string_mapping[i])) "\"" enum_error_to_string_mapping[i] "\");" + delete enum_error_to_string_mapping[i] + } + if (enum_error_to_string_size != 0) print "" + + # Static methods + mlen = 0 + for (i = 1; i <= static_method_size; i++) { + if (mlen <= length(static_methods[i, 0])) mlen = length(static_methods[i, 0]) + } + for (i = 1; i <= static_method_size; i++) { + print " SQ" cls ".DefSQStaticMethod(engine, &" cls "::" static_methods[i, 0] ", " substr(spaces, 1, mlen - length(static_methods[i, 0])) "\"" static_methods[i, 0] "\", " substr(spaces, 1, mlen - length(static_methods[i, 0])) "" static_methods[i, 1] ", \"" static_methods[i, 2] "\");" + delete static_methods[i] + } + if (static_method_size != 0) print "" + + if (virtual_class == "false") { + # Non-static methods + mlen = 0 + for (i = 1; i <= method_size; i++) { + if (mlen <= length(methods[i, 0])) mlen = length(methods[i, 0]) + } + for (i = 1; i <= method_size; i++) { + if (methods[i, 2] == "v") { + print " SQ" cls ".DefSQAdvancedMethod(engine, &" cls "::" methods[i, 0] ", " substr(spaces, 1, mlen - length(methods[i, 0]) - 8) "\"" methods[i, 0] "\");" + } else { + print " SQ" cls ".DefSQMethod(engine, &" cls "::" methods[i, 0] ", " substr(spaces, 1, mlen - length(methods[i, 0])) "\"" methods[i, 0] "\", " substr(spaces, 1, mlen - length(methods[i, 0])) "" methods[i, 1] ", \"" methods[i, 2] "\");" + } + delete methods[i] + } + if (method_size != 0) print "" + } + print " SQ" cls ".PostRegister(engine);" + print "}" + + enum_size = 0 + enum_value_size = 0 + enum_string_to_error_size = 0 + enum_error_to_string_size = 0 + struct_size = 0 + method_size = 0 + static_method_size = 0 + virtual_class = "false" + cls = "" + start_squirrel_define_on_next_line = "false" + cls_level = 0 +} + +# Skip non-public functions +{ if (public == "false") next } + +# Add enums +{ + if (in_enum == "true") { + enum_value_size++ + sub(",", "", $1) + enum_value[enum_value_size] = $1 + + # Check if this a special error enum + if (match(enums[enum_size], ".*::ErrorMessages") != 0) { + # syntax: + # enum ErrorMessages { + # ERR_SOME_ERROR, // [STR_ITEM1, STR_ITEM2, ...] + # } + + # Set the mappings + if (match($0, "\\[.*\\]") != 0) { + mappings = substr($0, RSTART, RLENGTH); + gsub("([\\[[:space:]\\]])", "", mappings); + + split(mappings, mapitems, ","); + for (i = 1; i <= length(mapitems); i++) { + enum_string_to_error_size++ + enum_string_to_error_mapping_string[enum_string_to_error_size] = mapitems[i] + enum_string_to_error_mapping_error[enum_string_to_error_size] = $1 + } + + enum_error_to_string_size++ + enum_error_to_string_mapping[enum_error_to_string_size] = $1 + } + } + next + } +} + +# Add a method to the list +/^.*\(.*\).*$/ { + if (cls_level != 1) next + if (match($0, "~")) next + + is_static = match($0, "static") + if (match($0, "virtual")) { + virtual_class = "true" + } + gsub("virtual", "", $0) + gsub("static", "", $0) + gsub("const", "", $0) + gsub("{.*", "", $0) + param_s = $0 + gsub("\\*", "", $0) + gsub("\\(.*", "", $0) + + sub(".*\\(", "", param_s) + sub("\\).*", "", param_s) + + funcname = $2 + if ($1 == cls && funcname == "") { + cls_param[0] = param_s + if (param_s == "") next + } else if (funcname == "") next + + split(param_s, params, ",") + types = "x" + for (len = 1; params[len] != ""; len++) { + sub("^[ ]*", "", params[len]) + if (match(params[len], "\\*") || match(params[len], "&")) { + if (match(params[len], "^char")) { + types = types "s" + } else if (match(params[len], "^void")) { + types = types "p" + } else if (match(params[len], "^Array")) { + types = types "a" + } else if (match(params[len], "^struct Array")) { + types = types "a" + } else { + types = types "x" + } + } else if (match(params[len], "^bool")) { + types = types "b" + } else if (match(params[len], "^HSQUIRRELVM")) { + types = "v" + } else { + types = types "i" + } + } + + if ($1 == cls && funcname == "") { + cls_param[1] = len; + cls_param[2] = types; + } else if (substr(funcname, 0, 1) == "_" && types != "v") { + } else if (is_static) { + static_method_size++ + static_methods[static_method_size, 0] = funcname + static_methods[static_method_size, 1] = len + static_methods[static_method_size, 2] = types + } else { + method_size++ + methods[method_size, 0] = funcname + methods[method_size, 1] = len + methods[method_size, 2] = types + } + next +} diff --git a/src/ai/api/squirrel_export.sh b/src/ai/api/squirrel_export.sh new file mode 100644 index 000000000..ac324390f --- /dev/null +++ b/src/ai/api/squirrel_export.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +# This must be called from within the src/ai/api directory. + +if [ -z "$1" ]; then + for f in `ls *.hpp`; do + case "${f}" in + # these files should not be changed by this script + "ai_controller.hpp" | "ai_object.hpp" | "ai_types.hpp" ) continue; + esac + awk -f squirrel_export.awk ${f} > ${f}.tmp + if ! [ -f "${f}.sq" ] || [ -n "`diff -I '$Id' -b ${f}.tmp ${f}.sq 2> /dev/null || echo boo`" ]; then + mv ${f}.tmp ${f}.sq + echo "Updated: ${f}.sq" + svn add ${f}.sq > /dev/null 2>&1 + svn propset svn:eol-style native ${f}.sq > /dev/null 2>&1 + svn propset svn:keywords Id ${f}.sq > /dev/null 2>&1 + else + rm -f ${f}.tmp + fi + done +else + awk -f squirrel_export.awk $1 > $1.tmp + if ! [ -f "${f}.sq" ] || [ -n "`diff -I '$Id' -b $1.sq $1.tmp 2> /dev/null || echo boo`" ]; then + mv $1.tmp $1.sq + echo "Updated: $1.sq" + svn add $1.sq > /dev/null 2>&1 + svn propset svn:eol-style native $1.sq > /dev/null 2>&1 + svn propset svn:keywords Id $1.sq > /dev/null 2>&1 + else + rm -f $1.tmp + fi +fi + +# Remove .hpp.sq if .hpp doesn't exist anymore +for f in `ls *.hpp.sq`; do + f=`echo ${f} | sed "s/.hpp.sq$/.hpp/"` + if [ ! -f ${f} ];then + echo "Deleted: ${f}.sq" + svn del --force ${f}.sq > /dev/null 2>&1 + fi +done + +# Add stuff to ai_instance.cpp +f='../ai_instance.cpp' + +functions=`` + +echo " +{ } +/.hpp.sq/ { next } +/squirrel_register_std/ { next } +/SQAIController_Register/ { print \$0; next } +/SQAI.*_Register/ { next } + +/Note: this line a marker in squirrel_export.sh. Do not change!/ { + print \$0 + gsub(\"^.*/\", \"\") + split(\"`grep '^void SQAI.*_Register(Squirrel \*engine) {$' *.hpp.sq | sed 's/:.*$//' | sort | uniq | tr -d '\r' | tr '\n' ' '`\", files, \" \") + + for (i = 1; files[i] != \"\"; i++) { + print \"#include \\\"api/\" files[i] \"\\\"\" \$0 + } + + next; +} + +/\/\* Register all classes \*\// { + print \$0 + gsub(\"^.*/\", \"\") + print \" squirrel_register_std(this->engine);\" \$0 + split(\"`grep '^void SQAI.*_Register(Squirrel \*engine) {$' *.hpp.sq | sed 's/^.*void //;s/Squirrel \*/this->/;s/ {/;/;s/_Register/0000Register/g;' | sort | sed 's/0000Register/_Register/g' | tr -d '\r' | tr '\n' ' '`\", regs, \" \") + + for (i = 1; regs[i] != \"\"; i++) { + if (regs[i] == \"SQAIController_Register(this->engine);\") continue + print \" \" regs[i] \$0 + } + + next +} + +{ print \$0; } +" > ${f}.awk + +awk -f ${f}.awk ${f} > ${f}.tmp + +if ! [ -f "${f}" ] || [ -n "`diff -I '$Id' -b ${f} ${f}.tmp 2> /dev/null || echo boo`" ]; then + mv ${f}.tmp ${f} + echo "Updated: ${f}" +else + rm -f ${f}.tmp +fi +rm -f ${f}.awk diff --git a/src/ai/default/default.cpp b/src/ai/default/default.cpp deleted file mode 100644 index 74d9b7ac5..000000000 --- a/src/ai/default/default.cpp +++ /dev/null @@ -1,4028 +0,0 @@ -/* $Id$ */ - -/** @file default.cpp The original AI. */ - -#include "../../stdafx.h" -#include "../../openttd.h" -#include "../../aircraft.h" -#include "../../bridge_map.h" -#include "../../tile_cmd.h" -#include "../../landscape.h" -#include "../../rail_map.h" -#include "../../road_map.h" -#include "../../roadveh.h" -#include "../../station_map.h" -#include "../../tunnel_map.h" -#include "../../command_func.h" -#include "../../town.h" -#include "../../industry.h" -#include "../../pathfind.h" -#include "../../airport.h" -#include "../../variables.h" -#include "../../bridge.h" -#include "../../date_func.h" -#include "../../tunnelbridge_map.h" -#include "../../window_func.h" -#include "../../vehicle_func.h" -#include "../../functions.h" -#include "../../company_func.h" -#include "../../company_base.h" -#include "../../settings_type.h" -#include "default.h" -#include "../../tunnelbridge.h" -#include "../../order_func.h" - -#include "../../table/ai_rail.h" - -// remove some day perhaps? -static uint _ai_service_interval; -CompanyAI _companies_ai[MAX_COMPANIES]; - -typedef void AiStateAction(Company *c); - -enum { - AIS_0 = 0, - AIS_1 = 1, - AIS_VEH_LOOP = 2, - AIS_VEH_CHECK_REPLACE_VEHICLE = 3, - AIS_VEH_DO_REPLACE_VEHICLE = 4, - AIS_WANT_NEW_ROUTE = 5, - AIS_BUILD_DEFAULT_RAIL_BLOCKS = 6, - AIS_BUILD_RAIL = 7, - AIS_BUILD_RAIL_VEH = 8, - AIS_DELETE_RAIL_BLOCKS = 9, - AIS_BUILD_DEFAULT_ROAD_BLOCKS = 10, - AIS_BUILD_ROAD = 11, - AIS_BUILD_ROAD_VEHICLES = 12, - AIS_DELETE_ROAD_BLOCKS = 13, - AIS_AIRPORT_STUFF = 14, - AIS_BUILD_DEFAULT_AIRPORT_BLOCKS = 15, - AIS_BUILD_AIRCRAFT_VEHICLES = 16, - AIS_CHECK_SHIP_STUFF = 17, - AIS_BUILD_DEFAULT_SHIP_BLOCKS = 18, - AIS_DO_SHIP_STUFF = 19, - AIS_SELL_VEHICLE = 20, - AIS_REMOVE_STATION = 21, - AIS_REMOVE_TRACK = 22, - AIS_REMOVE_SINGLE_RAIL_TILE = 23 -}; - - -static inline TrackBits GetRailTrackStatus(TileIndex tile) -{ - return TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)); -} - - -static void AiCase0(Company *c) -{ - _companies_ai[c->index].state = AIS_REMOVE_TRACK; - _companies_ai[c->index].state_counter = 0; -} - -static void AiCase1(Company *c) -{ - _companies_ai[c->index].cur_veh = NULL; - _companies_ai[c->index].state = AIS_VEH_LOOP; -} - -static void AiStateVehLoop(Company *c) -{ - Vehicle *v; - uint index; - - index = (_companies_ai[c->index].cur_veh == NULL) ? 0 : _companies_ai[c->index].cur_veh->index + 1; - - FOR_ALL_VEHICLES_FROM(v, index) { - if (v->owner != _current_company) continue; - - if ((v->type == VEH_TRAIN && v->subtype == 0) || - v->type == VEH_ROAD || - (v->type == VEH_AIRCRAFT && IsNormalAircraft(v)) || - v->type == VEH_SHIP) { - /* replace engine? */ - if (v->type == VEH_TRAIN && v->engine_type < 3 && - (_price.build_railvehicle >> 3) < c->money) { - _companies_ai[c->index].state = AIS_VEH_CHECK_REPLACE_VEHICLE; - _companies_ai[c->index].cur_veh = v; - return; - } - - /* not profitable? */ - if (v->age >= 730 && - v->profit_last_year < _price.station_value * 5 * 256 && - v->profit_this_year < _price.station_value * 5 * 256) { - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].state = AIS_SELL_VEHICLE; - _companies_ai[c->index].cur_veh = v; - return; - } - - /* not reliable? */ - if (v->age >= v->max_age || ( - v->age != 0 && - GetEngine(v->engine_type)->reliability < 35389 - )) { - _companies_ai[c->index].state = AIS_VEH_CHECK_REPLACE_VEHICLE; - _companies_ai[c->index].cur_veh = v; - return; - } - } - } - - _companies_ai[c->index].state = AIS_WANT_NEW_ROUTE; - _companies_ai[c->index].state_counter = 0; -} - -static EngineID AiChooseTrainToBuild(RailType railtype, Money money, byte flag, TileIndex tile) -{ - EngineID best_veh_index = INVALID_ENGINE; - byte best_veh_score = 0; - const Engine *e; - - FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) { - EngineID i = e->index; - const RailVehicleInfo *rvi = &e->u.rail; - - if (!IsCompatibleRail(rvi->railtype, railtype) || - rvi->railveh_type == RAILVEH_WAGON || - (rvi->railveh_type == RAILVEH_MULTIHEAD && flag & 1) || - !HasBit(e->company_avail, _current_company) || - e->reliability < 0x8A3D) { - continue; - } - - /* Don't choose an engine designated for passenger use for freight. */ - if (rvi->ai_passenger_only != 0 && flag == 1) continue; - - CommandCost ret = DoCommand(tile, i, 0, 0, CMD_BUILD_RAIL_VEHICLE); - if (CmdSucceeded(ret) && ret.GetCost() <= money && rvi->ai_rank >= best_veh_score) { - best_veh_score = rvi->ai_rank; - best_veh_index = i; - } - } - - return best_veh_index; -} - -static EngineID AiChooseRoadVehToBuild(CargoID cargo, Money money, TileIndex tile) -{ - EngineID best_veh_index = INVALID_ENGINE; - int32 best_veh_rating = 0; - const Engine *e; - - FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { - EngineID i = e->index; - const RoadVehicleInfo *rvi = &e->u.road; - - if (!HasBit(e->company_avail, _current_company) || e->reliability < 0x8A3D) { - continue; - } - - /* Skip vehicles which can't take our cargo type */ - if (rvi->cargo_type != cargo && !CanRefitTo(i, cargo)) continue; - - /* Rate and compare the engine by speed & capacity */ - int rating = rvi->max_speed * rvi->capacity; - if (rating <= best_veh_rating) continue; - - CommandCost ret = DoCommand(tile, i, 0, 0, CMD_BUILD_ROAD_VEH); - if (CmdFailed(ret)) continue; - - /* Add the cost of refitting */ - if (rvi->cargo_type != cargo) ret.AddCost(GetRefitCost(i)); - if (ret.GetCost() > money) continue; - - best_veh_rating = rating; - best_veh_index = i; - } - - return best_veh_index; -} - -/** - * Choose aircraft to build. - * @param money current AI money - * @param forbidden forbidden flags - AIR_HELI = 0 (always allowed), AIR_CTOL = 1 (bit 0), AIR_FAST = 2 (bit 1) - * @return EngineID of aircraft to build - */ -static EngineID AiChooseAircraftToBuild(Money money, byte forbidden) -{ - EngineID best_veh_index = INVALID_ENGINE; - Money best_veh_cost = 0; - const Engine *e; - - FOR_ALL_ENGINES_OF_TYPE(e, VEH_AIRCRAFT) { - EngineID i = e->index; - const AircraftVehicleInfo *avi = &e->u.air; - - if (!HasBit(e->company_avail, _current_company) || e->reliability < 0x8A3D) { - continue; - } - - if ((avi->subtype & forbidden) != 0) continue; - - CommandCost ret = DoCommand(0, i, 0, DC_QUERY_COST, CMD_BUILD_AIRCRAFT); - if (CmdSucceeded(ret) && ret.GetCost() <= money && ret.GetCost() >= best_veh_cost) { - best_veh_cost = ret.GetCost(); - best_veh_index = i; - } - } - - return best_veh_index; -} - -static Money AiGetBasePrice(const Company *c) -{ - Money base = _price.station_value; - - // adjust base price when more expensive vehicles are available - switch (_companies_ai[c->index].railtype_to_use) { - default: NOT_REACHED(); - case RAILTYPE_RAIL: break; - case RAILTYPE_ELECTRIC: break; - case RAILTYPE_MONO: base = (base * 3) >> 1; break; - case RAILTYPE_MAGLEV: base *= 2; break; - } - - return base; -} - -static EngineID AiChooseRoadVehToReplaceWith(const Company *c, const Vehicle *v) -{ - Money avail_money = c->money + v->value; - return AiChooseRoadVehToBuild(v->cargo_type, avail_money, v->tile); -} - -static EngineID AiChooseAircraftToReplaceWith(const Company *c, const Vehicle *v) -{ - Money avail_money = c->money + v->value; - - /* determine forbidden aircraft bits */ - byte forbidden = 0; - const Order *o; - - FOR_VEHICLE_ORDERS(v, o) { - if (!o->IsValid()) continue; - if (!IsValidStationID(o->GetDestination())) continue; - const Station *st = GetStation(o->GetDestination()); - if (!(st->facilities & FACIL_AIRPORT)) continue; - - AirportFTAClass::Flags flags = st->Airport()->flags; - if (!(flags & AirportFTAClass::AIRPLANES)) forbidden |= AIR_CTOL | AIR_FAST; // no planes for heliports / oil rigs - if (flags & AirportFTAClass::SHORT_STRIP) forbidden |= AIR_FAST; // no fast planes for small airports - } - - return AiChooseAircraftToBuild( - avail_money, forbidden - ); -} - -static EngineID AiChooseTrainToReplaceWith(const Company *c, const Vehicle *v) -{ - Money avail_money = c->money + v->value; - const Vehicle *u = v; - int num = 0; - - while (++num, u->Next() != NULL) { - u = u->Next(); - } - - // XXX: check if a wagon - return AiChooseTrainToBuild(v->u.rail.railtype, avail_money, 0, v->tile); -} - -static EngineID AiChooseShipToReplaceWith(const Company *c, const Vehicle *v) -{ - /* Ships are not implemented in this (broken) AI */ - return INVALID_ENGINE; -} - -static void AiHandleGotoDepot(Company *c, int cmd) -{ - if (!_companies_ai[c->index].cur_veh->current_order.IsType(OT_GOTO_DEPOT)) - DoCommand(0, _companies_ai[c->index].cur_veh->index, 0, DC_EXEC, cmd); - - if (++_companies_ai[c->index].state_counter <= 1387) { - _companies_ai[c->index].state = AIS_VEH_DO_REPLACE_VEHICLE; - return; - } - - if (_companies_ai[c->index].cur_veh->current_order.IsType(OT_GOTO_DEPOT)) { - _companies_ai[c->index].cur_veh->current_order.MakeDummy(); - InvalidateWindow(WC_VEHICLE_VIEW, _companies_ai[c->index].cur_veh->index); - } -} - -static void AiRestoreVehicleOrders(Vehicle *v, BackuppedOrders *bak) -{ - if (bak->order == NULL) return; - - for (uint i = 0; !bak->order[i].IsType(OT_NOTHING); i++) { - if (!DoCommandP(0, v->index + (i << 16), bak->order[i].Pack(), CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK)) - break; - } -} - -static void AiHandleReplaceTrain(Company *c) -{ - const Vehicle *v = _companies_ai[c->index].cur_veh; - BackuppedOrders orderbak; - EngineID veh; - - // wait until the vehicle reaches the depot. - if (!IsRailDepotTile(v->tile) || v->u.rail.track != TRACK_BIT_DEPOT || !(v->vehstatus & VS_STOPPED)) { - AiHandleGotoDepot(c, CMD_SEND_TRAIN_TO_DEPOT); - return; - } - - veh = AiChooseTrainToReplaceWith(c, v); - if (veh != INVALID_ENGINE) { - TileIndex tile; - - BackupVehicleOrders(v, &orderbak); - tile = v->tile; - - if (CmdSucceeded(DoCommand(0, v->index, 2, DC_EXEC, CMD_SELL_RAIL_WAGON)) && - CmdSucceeded(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE))) { - VehicleID veh = _new_vehicle_id; - AiRestoreVehicleOrders(GetVehicle(veh), &orderbak); - DoCommand(0, veh, 0, DC_EXEC, CMD_START_STOP_VEHICLE); - - DoCommand(0, veh, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT); - } - } -} - -static void AiHandleReplaceRoadVeh(Company *c) -{ - const Vehicle *v = _companies_ai[c->index].cur_veh; - BackuppedOrders orderbak; - EngineID veh; - - if (!v->IsStoppedInDepot()) { - AiHandleGotoDepot(c, CMD_SEND_ROADVEH_TO_DEPOT); - return; - } - - veh = AiChooseRoadVehToReplaceWith(c, v); - if (veh != INVALID_ENGINE) { - TileIndex tile; - - BackupVehicleOrders(v, &orderbak); - tile = v->tile; - - if (CmdSucceeded(DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH)) && - CmdSucceeded(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_ROAD_VEH))) { - VehicleID veh = _new_vehicle_id; - - AiRestoreVehicleOrders(GetVehicle(veh), &orderbak); - DoCommand(0, veh, 0, DC_EXEC, CMD_START_STOP_VEHICLE); - DoCommand(0, veh, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT); - } - } -} - -static void AiHandleReplaceAircraft(Company *c) -{ - const Vehicle *v = _companies_ai[c->index].cur_veh; - BackuppedOrders orderbak; - EngineID veh; - - if (!v->IsStoppedInDepot()) { - AiHandleGotoDepot(c, CMD_SEND_AIRCRAFT_TO_HANGAR); - return; - } - - veh = AiChooseAircraftToReplaceWith(c, v); - if (veh != INVALID_ENGINE) { - TileIndex tile; - - BackupVehicleOrders(v, &orderbak); - tile = v->tile; - - if (CmdSucceeded(DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_AIRCRAFT)) && - CmdSucceeded(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_AIRCRAFT))) { - VehicleID veh = _new_vehicle_id; - AiRestoreVehicleOrders(GetVehicle(veh), &orderbak); - DoCommand(0, veh, 0, DC_EXEC, CMD_START_STOP_VEHICLE); - - DoCommand(0, veh, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT); - } - } -} - -static void AiHandleReplaceShip(Company *c) -{ - /* Ships are not implemented in this (broken) AI */ -} - -typedef EngineID CheckReplaceProc(const Company *c, const Vehicle *v); - -static CheckReplaceProc * const _veh_check_replace_proc[] = { - AiChooseTrainToReplaceWith, - AiChooseRoadVehToReplaceWith, - AiChooseShipToReplaceWith, - AiChooseAircraftToReplaceWith, -}; - -typedef void DoReplaceProc(Company *c); -static DoReplaceProc * const _veh_do_replace_proc[] = { - AiHandleReplaceTrain, - AiHandleReplaceRoadVeh, - AiHandleReplaceShip, - AiHandleReplaceAircraft -}; - -static void AiStateCheckReplaceVehicle(Company *c) -{ - const Vehicle *v = _companies_ai[c->index].cur_veh; - - if (!v->IsValid() || - v->owner != _current_company || - v->type > VEH_SHIP || - _veh_check_replace_proc[v->type - VEH_TRAIN](c, v) == INVALID_ENGINE) { - _companies_ai[c->index].state = AIS_VEH_LOOP; - } else { - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].state = AIS_VEH_DO_REPLACE_VEHICLE; - } -} - -static void AiStateDoReplaceVehicle(Company *c) -{ - const Vehicle *v = _companies_ai[c->index].cur_veh; - - _companies_ai[c->index].state = AIS_VEH_LOOP; - // vehicle is not owned by the company anymore, something went very wrong. - if (!v->IsValid() || v->owner != _current_company) return; - _veh_do_replace_proc[v->type - VEH_TRAIN](c); -} - -struct FoundRoute { - int distance; - CargoID cargo; - void *from; - void *to; -}; - -static Town *AiFindRandomTown() -{ - return GetRandomTown(); -} - -static Industry *AiFindRandomIndustry() -{ - int num = RandomRange(GetMaxIndustryIndex()); - if (IsValidIndustryID(num)) return GetIndustry(num); - - return NULL; -} - -static void AiFindSubsidyIndustryRoute(FoundRoute *fr) -{ - uint i; - CargoID cargo; - const Subsidy *s; - Industry *from; - TileIndex to_xy; - - // initially error - fr->distance = -1; - - // Randomize subsidy index.. - i = RandomRange(lengthof(_subsidies) * 3); - if (i >= lengthof(_subsidies)) return; - - s = &_subsidies[i]; - - // Don't want passengers or mail - cargo = s->cargo_type; - if (cargo == CT_INVALID || - cargo == CT_PASSENGERS || - cargo == CT_MAIL || - s->age > 7) { - return; - } - fr->cargo = cargo; - - fr->from = from = GetIndustry(s->from); - - if (cargo == CT_GOODS || cargo == CT_FOOD) { - Town *to_tow = GetTown(s->to); - - if (to_tow->population < (cargo == CT_FOOD ? 200U : 900U)) return; // error - fr->to = to_tow; - to_xy = to_tow->xy; - } else { - Industry *to_ind = GetIndustry(s->to); - - fr->to = to_ind; - to_xy = to_ind->xy; - } - - fr->distance = DistanceManhattan(from->xy, to_xy); -} - -static void AiFindSubsidyPassengerRoute(FoundRoute *fr) -{ - uint i; - const Subsidy *s; - Town *from, *to; - - // initially error - fr->distance = -1; - - // Randomize subsidy index.. - i = RandomRange(lengthof(_subsidies) * 3); - if (i >= lengthof(_subsidies)) return; - - s = &_subsidies[i]; - - // Only want passengers - if (s->cargo_type != CT_PASSENGERS || s->age > 7) return; - fr->cargo = s->cargo_type; - - fr->from = from = GetTown(s->from); - fr->to = to = GetTown(s->to); - - // They must be big enough - if (from->population < 400 || to->population < 400) return; - - fr->distance = DistanceManhattan(from->xy, to->xy); -} - -static void AiFindRandomIndustryRoute(FoundRoute *fr) -{ - Industry *i; - uint32 r; - CargoID cargo; - - // initially error - fr->distance = -1; - - r = Random(); - - // pick a source - fr->from = i = AiFindRandomIndustry(); - if (i == NULL) return; - - // pick a random produced cargo - cargo = i->produced_cargo[0]; - if (r & 1 && i->produced_cargo[1] != CT_INVALID) cargo = i->produced_cargo[1]; - - fr->cargo = cargo; - - // don't allow passengers - if (cargo == CT_INVALID || cargo == CT_PASSENGERS) return; - - if (cargo != CT_GOODS && cargo != CT_FOOD) { - // pick a dest, and see if it can receive - Industry *i2 = AiFindRandomIndustry(); - if (i2 == NULL || i == i2 || - (i2->accepts_cargo[0] != cargo && - i2->accepts_cargo[1] != cargo && - i2->accepts_cargo[2] != cargo)) { - return; - } - - fr->to = i2; - fr->distance = DistanceManhattan(i->xy, i2->xy); - } else { - // pick a dest town, and see if it's big enough - Town *t = AiFindRandomTown(); - - if (t == NULL || t->population < (cargo == CT_FOOD ? 200U : 900U)) return; - - fr->to = t; - fr->distance = DistanceManhattan(i->xy, t->xy); - } -} - -static void AiFindRandomPassengerRoute(FoundRoute *fr) -{ - Town *source; - Town *dest; - - // initially error - fr->distance = -1; - - fr->from = source = AiFindRandomTown(); - if (source == NULL || source->population < 400) return; - - fr->to = dest = AiFindRandomTown(); - if (dest == NULL || source == dest || dest->population < 400) return; - - fr->distance = DistanceManhattan(source->xy, dest->xy); -} - -// Warn: depends on 'xy' being the first element in both Town and Industry -#define GET_TOWN_OR_INDUSTRY_TILE(p) (((Town*)(p))->xy) - -static bool AiCheckIfRouteIsGood(Company *c, FoundRoute *fr, byte bitmask) -{ - TileIndex from_tile, to_tile; - Station *st; - int dist; - uint same_station = 0; - - from_tile = GET_TOWN_OR_INDUSTRY_TILE(fr->from); - to_tile = GET_TOWN_OR_INDUSTRY_TILE(fr->to); - - dist = 0xFFFF; - FOR_ALL_STATIONS(st) { - int cur; - - if (st->owner != _current_company) continue; - cur = DistanceMax(from_tile, st->xy); - if (cur < dist) dist = cur; - cur = DistanceMax(to_tile, st->xy); - if (cur < dist) dist = cur; - if (to_tile == from_tile && st->xy == to_tile) same_station++; - } - - // To prevent the AI from building ten busstations in the same town, do some calculations - // For each road or airport station, we want 350 of population! - if ((bitmask == 2 || bitmask == 4) && - same_station > 2 && - ((Town*)fr->from)->population < same_station * 350) { - return false; - } - - /* Requiring distance to nearest station to be always under 37 tiles may be suboptimal, - * Especially for longer aircraft routes that start and end pretty at any arbitrary place on map - * While it may be nice for AI to cluster their creations together, hardcoded limit is not ideal. - * If AI will randomly start on some isolated spot, it will never get out of there. - * AI will have chance of randomly rejecting routes further than 37 tiles from their network, - * so there will be some attempt to cluster the network together */ - - /* Random value between 37 and 292. Low values are exponentially more likely - * With 50% chance the value will be under 52 tiles */ - int min_distance = 36 + (1 << (Random() % 9)); // 0..8 - - /* Make sure distance to closest station is < min_distance tiles. */ - if (dist != 0xFFFF && dist > min_distance) return false; - - if (_companies_ai[c->index].route_type_mask != 0 && - !(_companies_ai[c->index].route_type_mask & bitmask) && - !Chance16(1, 5)) { - return false; - } - - if (fr->cargo == CT_PASSENGERS || fr->cargo == CT_MAIL) { - const Town *from = (const Town*)fr->from; - const Town *to = (const Town*)fr->to; - - if (from->pct_pass_transported > 0x99 || - to->pct_pass_transported > 0x99) { - return false; - } - - // Make sure it has a reasonably good rating - if (from->ratings[_current_company] < -100 || - to->ratings[_current_company] < -100) { - return false; - } - } else { - const Industry *i = (const Industry*)fr->from; - - if (i->last_month_pct_transported[fr->cargo != i->produced_cargo[0]] > 0x99 || - i->last_month_production[fr->cargo != i->produced_cargo[0]] == 0) { - return false; - } - } - - _companies_ai[c->index].route_type_mask |= bitmask; - return true; -} - -static byte AiGetDirectionBetweenTiles(TileIndex a, TileIndex b) -{ - byte i = (TileX(a) < TileX(b)) ? 1 : 0; - if (TileY(a) >= TileY(b)) i ^= 3; - return i; -} - -static TileIndex AiGetPctTileBetween(TileIndex a, TileIndex b, byte pct) -{ - return TileXY( - TileX(a) + ((TileX(b) - TileX(a)) * pct >> 8), - TileY(a) + ((TileY(b) - TileY(a)) * pct >> 8) - ); -} - -static void AiWantLongIndustryRoute(Company *c) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyIndustryRoute(&fr); - if (IsInsideMM(fr.distance, 60, 90 + 1)) break; - - // try a random one - AiFindRandomIndustryRoute(&fr); - if (IsInsideMM(fr.distance, 60, 90 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - if (!AiCheckIfRouteIsGood(c, &fr, 1)) return; - - // Fill the source field - _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - - _companies_ai[c->index].src.use_tile = 0; - _companies_ai[c->index].src.rand_rng = 9; - _companies_ai[c->index].src.cur_building_rule = 0xFF; - _companies_ai[c->index].src.unk6 = 1; - _companies_ai[c->index].src.unk7 = 0; - _companies_ai[c->index].src.buildcmd_a = 0x24; - _companies_ai[c->index].src.buildcmd_b = 0xFF; - _companies_ai[c->index].src.direction = AiGetDirectionBetweenTiles( - _companies_ai[c->index].src.spec_tile, - _companies_ai[c->index].dst.spec_tile - ); - _companies_ai[c->index].src.cargo = fr.cargo | 0x80; - - // Fill the dest field - - _companies_ai[c->index].dst.use_tile = 0; - _companies_ai[c->index].dst.rand_rng = 9; - _companies_ai[c->index].dst.cur_building_rule = 0xFF; - _companies_ai[c->index].dst.unk6 = 1; - _companies_ai[c->index].dst.unk7 = 0; - _companies_ai[c->index].dst.buildcmd_a = 0x34; - _companies_ai[c->index].dst.buildcmd_b = 0xFF; - _companies_ai[c->index].dst.direction = AiGetDirectionBetweenTiles( - _companies_ai[c->index].dst.spec_tile, - _companies_ai[c->index].src.spec_tile - ); - _companies_ai[c->index].dst.cargo = fr.cargo; - - // Fill middle field 1 - _companies_ai[c->index].mid1.spec_tile = AiGetPctTileBetween( - _companies_ai[c->index].src.spec_tile, - _companies_ai[c->index].dst.spec_tile, - 0x55 - ); - _companies_ai[c->index].mid1.use_tile = 0; - _companies_ai[c->index].mid1.rand_rng = 6; - _companies_ai[c->index].mid1.cur_building_rule = 0xFF; - _companies_ai[c->index].mid1.unk6 = 2; - _companies_ai[c->index].mid1.unk7 = 1; - _companies_ai[c->index].mid1.buildcmd_a = 0x30; - _companies_ai[c->index].mid1.buildcmd_b = 0xFF; - _companies_ai[c->index].mid1.direction = _companies_ai[c->index].src.direction; - _companies_ai[c->index].mid1.cargo = fr.cargo; - - // Fill middle field 2 - _companies_ai[c->index].mid2.spec_tile = AiGetPctTileBetween( - _companies_ai[c->index].src.spec_tile, - _companies_ai[c->index].dst.spec_tile, - 0xAA - ); - _companies_ai[c->index].mid2.use_tile = 0; - _companies_ai[c->index].mid2.rand_rng = 6; - _companies_ai[c->index].mid2.cur_building_rule = 0xFF; - _companies_ai[c->index].mid2.unk6 = 2; - _companies_ai[c->index].mid2.unk7 = 1; - _companies_ai[c->index].mid2.buildcmd_a = 0xFF; - _companies_ai[c->index].mid2.buildcmd_b = 0xFF; - _companies_ai[c->index].mid2.direction = _companies_ai[c->index].dst.direction; - _companies_ai[c->index].mid2.cargo = fr.cargo; - - // Fill common fields - _companies_ai[c->index].cargo_type = fr.cargo; - _companies_ai[c->index].num_wagons = 3; - _companies_ai[c->index].build_kind = 2; - _companies_ai[c->index].num_build_rec = 4; - _companies_ai[c->index].num_loco_to_build = 2; - _companies_ai[c->index].num_want_fullload = 2; - _companies_ai[c->index].wagon_list[0] = INVALID_VEHICLE; - _companies_ai[c->index].order_list_blocks[0] = 0; - _companies_ai[c->index].order_list_blocks[1] = 1; - _companies_ai[c->index].order_list_blocks[2] = 255; - - _companies_ai[c->index].state = AIS_BUILD_DEFAULT_RAIL_BLOCKS; - _companies_ai[c->index].state_mode = UCHAR_MAX; - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].timeout_counter = 0; -} - -static void AiWantMediumIndustryRoute(Company *c) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyIndustryRoute(&fr); - if (IsInsideMM(fr.distance, 40, 60 + 1)) break; - - // try a random one - AiFindRandomIndustryRoute(&fr); - if (IsInsideMM(fr.distance, 40, 60 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - if (!AiCheckIfRouteIsGood(c, &fr, 1)) return; - - // Fill the source field - _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - _companies_ai[c->index].src.use_tile = 0; - _companies_ai[c->index].src.rand_rng = 9; - _companies_ai[c->index].src.cur_building_rule = 0xFF; - _companies_ai[c->index].src.unk6 = 1; - _companies_ai[c->index].src.unk7 = 0; - _companies_ai[c->index].src.buildcmd_a = 0x10; - _companies_ai[c->index].src.buildcmd_b = 0xFF; - _companies_ai[c->index].src.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.from), - GET_TOWN_OR_INDUSTRY_TILE(fr.to) - ); - _companies_ai[c->index].src.cargo = fr.cargo | 0x80; - - // Fill the dest field - _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - _companies_ai[c->index].dst.use_tile = 0; - _companies_ai[c->index].dst.rand_rng = 9; - _companies_ai[c->index].dst.cur_building_rule = 0xFF; - _companies_ai[c->index].dst.unk6 = 1; - _companies_ai[c->index].dst.unk7 = 0; - _companies_ai[c->index].dst.buildcmd_a = 0xFF; - _companies_ai[c->index].dst.buildcmd_b = 0xFF; - _companies_ai[c->index].dst.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.to), - GET_TOWN_OR_INDUSTRY_TILE(fr.from) - ); - _companies_ai[c->index].dst.cargo = fr.cargo; - - // Fill common fields - _companies_ai[c->index].cargo_type = fr.cargo; - _companies_ai[c->index].num_wagons = 3; - _companies_ai[c->index].build_kind = 1; - _companies_ai[c->index].num_build_rec = 2; - _companies_ai[c->index].num_loco_to_build = 1; - _companies_ai[c->index].num_want_fullload = 1; - _companies_ai[c->index].wagon_list[0] = INVALID_VEHICLE; - _companies_ai[c->index].order_list_blocks[0] = 0; - _companies_ai[c->index].order_list_blocks[1] = 1; - _companies_ai[c->index].order_list_blocks[2] = 255; - _companies_ai[c->index].state = AIS_BUILD_DEFAULT_RAIL_BLOCKS; - _companies_ai[c->index].state_mode = UCHAR_MAX; - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].timeout_counter = 0; -} - -static void AiWantShortIndustryRoute(Company *c) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyIndustryRoute(&fr); - if (IsInsideMM(fr.distance, 15, 40 + 1)) break; - - // try a random one - AiFindRandomIndustryRoute(&fr); - if (IsInsideMM(fr.distance, 15, 40 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - if (!AiCheckIfRouteIsGood(c, &fr, 1)) return; - - // Fill the source field - _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - _companies_ai[c->index].src.use_tile = 0; - _companies_ai[c->index].src.rand_rng = 9; - _companies_ai[c->index].src.cur_building_rule = 0xFF; - _companies_ai[c->index].src.unk6 = 1; - _companies_ai[c->index].src.unk7 = 0; - _companies_ai[c->index].src.buildcmd_a = 0x10; - _companies_ai[c->index].src.buildcmd_b = 0xFF; - _companies_ai[c->index].src.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.from), - GET_TOWN_OR_INDUSTRY_TILE(fr.to) - ); - _companies_ai[c->index].src.cargo = fr.cargo | 0x80; - - // Fill the dest field - _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - _companies_ai[c->index].dst.use_tile = 0; - _companies_ai[c->index].dst.rand_rng = 9; - _companies_ai[c->index].dst.cur_building_rule = 0xFF; - _companies_ai[c->index].dst.unk6 = 1; - _companies_ai[c->index].dst.unk7 = 0; - _companies_ai[c->index].dst.buildcmd_a = 0xFF; - _companies_ai[c->index].dst.buildcmd_b = 0xFF; - _companies_ai[c->index].dst.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.to), - GET_TOWN_OR_INDUSTRY_TILE(fr.from) - ); - _companies_ai[c->index].dst.cargo = fr.cargo; - - // Fill common fields - _companies_ai[c->index].cargo_type = fr.cargo; - _companies_ai[c->index].num_wagons = 2; - _companies_ai[c->index].build_kind = 1; - _companies_ai[c->index].num_build_rec = 2; - _companies_ai[c->index].num_loco_to_build = 1; - _companies_ai[c->index].num_want_fullload = 1; - _companies_ai[c->index].wagon_list[0] = INVALID_VEHICLE; - _companies_ai[c->index].order_list_blocks[0] = 0; - _companies_ai[c->index].order_list_blocks[1] = 1; - _companies_ai[c->index].order_list_blocks[2] = 255; - _companies_ai[c->index].state = AIS_BUILD_DEFAULT_RAIL_BLOCKS; - _companies_ai[c->index].state_mode = UCHAR_MAX; - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].timeout_counter = 0; -} - -static void AiWantMailRoute(Company *c) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyPassengerRoute(&fr); - if (IsInsideMM(fr.distance, 60, 110 + 1)) break; - - // try a random one - AiFindRandomPassengerRoute(&fr); - if (IsInsideMM(fr.distance, 60, 110 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - fr.cargo = CT_MAIL; - if (!AiCheckIfRouteIsGood(c, &fr, 1)) return; - - // Fill the source field - _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - _companies_ai[c->index].src.use_tile = 0; - _companies_ai[c->index].src.rand_rng = 7; - _companies_ai[c->index].src.cur_building_rule = 0xFF; - _companies_ai[c->index].src.unk6 = 1; - _companies_ai[c->index].src.unk7 = 0; - _companies_ai[c->index].src.buildcmd_a = 0x24; - _companies_ai[c->index].src.buildcmd_b = 0xFF; - _companies_ai[c->index].src.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.from), - GET_TOWN_OR_INDUSTRY_TILE(fr.to) - ); - _companies_ai[c->index].src.cargo = fr.cargo; - - // Fill the dest field - _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - _companies_ai[c->index].dst.use_tile = 0; - _companies_ai[c->index].dst.rand_rng = 7; - _companies_ai[c->index].dst.cur_building_rule = 0xFF; - _companies_ai[c->index].dst.unk6 = 1; - _companies_ai[c->index].dst.unk7 = 0; - _companies_ai[c->index].dst.buildcmd_a = 0x34; - _companies_ai[c->index].dst.buildcmd_b = 0xFF; - _companies_ai[c->index].dst.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.to), - GET_TOWN_OR_INDUSTRY_TILE(fr.from) - ); - _companies_ai[c->index].dst.cargo = fr.cargo; - - // Fill middle field 1 - _companies_ai[c->index].mid1.spec_tile = AiGetPctTileBetween( - GET_TOWN_OR_INDUSTRY_TILE(fr.from), - GET_TOWN_OR_INDUSTRY_TILE(fr.to), - 0x55 - ); - _companies_ai[c->index].mid1.use_tile = 0; - _companies_ai[c->index].mid1.rand_rng = 6; - _companies_ai[c->index].mid1.cur_building_rule = 0xFF; - _companies_ai[c->index].mid1.unk6 = 2; - _companies_ai[c->index].mid1.unk7 = 1; - _companies_ai[c->index].mid1.buildcmd_a = 0x30; - _companies_ai[c->index].mid1.buildcmd_b = 0xFF; - _companies_ai[c->index].mid1.direction = _companies_ai[c->index].src.direction; - _companies_ai[c->index].mid1.cargo = fr.cargo; - - // Fill middle field 2 - _companies_ai[c->index].mid2.spec_tile = AiGetPctTileBetween( - GET_TOWN_OR_INDUSTRY_TILE(fr.from), - GET_TOWN_OR_INDUSTRY_TILE(fr.to), - 0xAA - ); - _companies_ai[c->index].mid2.use_tile = 0; - _companies_ai[c->index].mid2.rand_rng = 6; - _companies_ai[c->index].mid2.cur_building_rule = 0xFF; - _companies_ai[c->index].mid2.unk6 = 2; - _companies_ai[c->index].mid2.unk7 = 1; - _companies_ai[c->index].mid2.buildcmd_a = 0xFF; - _companies_ai[c->index].mid2.buildcmd_b = 0xFF; - _companies_ai[c->index].mid2.direction = _companies_ai[c->index].dst.direction; - _companies_ai[c->index].mid2.cargo = fr.cargo; - - // Fill common fields - _companies_ai[c->index].cargo_type = fr.cargo; - _companies_ai[c->index].num_wagons = 3; - _companies_ai[c->index].build_kind = 2; - _companies_ai[c->index].num_build_rec = 4; - _companies_ai[c->index].num_loco_to_build = 2; - _companies_ai[c->index].num_want_fullload = 0; - _companies_ai[c->index].wagon_list[0] = INVALID_VEHICLE; - _companies_ai[c->index].order_list_blocks[0] = 0; - _companies_ai[c->index].order_list_blocks[1] = 1; - _companies_ai[c->index].order_list_blocks[2] = 255; - _companies_ai[c->index].state = AIS_BUILD_DEFAULT_RAIL_BLOCKS; - _companies_ai[c->index].state_mode = UCHAR_MAX; - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].timeout_counter = 0; -} - -static void AiWantPassengerRoute(Company *c) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyPassengerRoute(&fr); - if (IsInsideMM(fr.distance, 0, 55 + 1)) break; - - // try a random one - AiFindRandomPassengerRoute(&fr); - if (IsInsideMM(fr.distance, 0, 55 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - fr.cargo = CT_PASSENGERS; - if (!AiCheckIfRouteIsGood(c, &fr, 1)) return; - - // Fill the source field - _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - _companies_ai[c->index].src.use_tile = 0; - _companies_ai[c->index].src.rand_rng = 7; - _companies_ai[c->index].src.cur_building_rule = 0xFF; - _companies_ai[c->index].src.unk6 = 1; - _companies_ai[c->index].src.unk7 = 0; - _companies_ai[c->index].src.buildcmd_a = 0x10; - _companies_ai[c->index].src.buildcmd_b = 0xFF; - _companies_ai[c->index].src.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.from), - GET_TOWN_OR_INDUSTRY_TILE(fr.to) - ); - _companies_ai[c->index].src.cargo = fr.cargo; - - // Fill the dest field - _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - _companies_ai[c->index].dst.use_tile = 0; - _companies_ai[c->index].dst.rand_rng = 7; - _companies_ai[c->index].dst.cur_building_rule = 0xFF; - _companies_ai[c->index].dst.unk6 = 1; - _companies_ai[c->index].dst.unk7 = 0; - _companies_ai[c->index].dst.buildcmd_a = 0xFF; - _companies_ai[c->index].dst.buildcmd_b = 0xFF; - _companies_ai[c->index].dst.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.to), - GET_TOWN_OR_INDUSTRY_TILE(fr.from) - ); - _companies_ai[c->index].dst.cargo = fr.cargo; - - // Fill common fields - _companies_ai[c->index].cargo_type = fr.cargo; - _companies_ai[c->index].num_wagons = 2; - _companies_ai[c->index].build_kind = 1; - _companies_ai[c->index].num_build_rec = 2; - _companies_ai[c->index].num_loco_to_build = 1; - _companies_ai[c->index].num_want_fullload = 0; - _companies_ai[c->index].wagon_list[0] = INVALID_VEHICLE; - _companies_ai[c->index].order_list_blocks[0] = 0; - _companies_ai[c->index].order_list_blocks[1] = 1; - _companies_ai[c->index].order_list_blocks[2] = 255; - _companies_ai[c->index].state = AIS_BUILD_DEFAULT_RAIL_BLOCKS; - _companies_ai[c->index].state_mode = UCHAR_MAX; - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].timeout_counter = 0; -} - -static void AiWantTrainRoute(Company *c) -{ - uint16 r = GB(Random(), 0, 16); - - _companies_ai[c->index].railtype_to_use = GetBestRailtype(c->index); - - if (r > 0xD000) { - AiWantLongIndustryRoute(c); - } else if (r > 0x6000) { - AiWantMediumIndustryRoute(c); - } else if (r > 0x1000) { - AiWantShortIndustryRoute(c); - } else if (r > 0x800) { - AiWantPassengerRoute(c); - } else { - AiWantMailRoute(c); - } -} - -static void AiWantLongRoadIndustryRoute(Company *c) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyIndustryRoute(&fr); - if (IsInsideMM(fr.distance, 35, 55 + 1)) break; - - // try a random one - AiFindRandomIndustryRoute(&fr); - if (IsInsideMM(fr.distance, 35, 55 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - if (!AiCheckIfRouteIsGood(c, &fr, 2)) return; - - // Fill the source field - _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - _companies_ai[c->index].src.use_tile = 0; - _companies_ai[c->index].src.rand_rng = 9; - _companies_ai[c->index].src.cur_building_rule = 0xFF; - _companies_ai[c->index].src.buildcmd_a = 1; - _companies_ai[c->index].src.direction = 0; - _companies_ai[c->index].src.cargo = fr.cargo | 0x80; - - // Fill the dest field - _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - _companies_ai[c->index].dst.use_tile = 0; - _companies_ai[c->index].dst.rand_rng = 9; - _companies_ai[c->index].dst.cur_building_rule = 0xFF; - _companies_ai[c->index].dst.buildcmd_a = 0xFF; - _companies_ai[c->index].dst.direction = 0; - _companies_ai[c->index].dst.cargo = fr.cargo; - - // Fill common fields - _companies_ai[c->index].cargo_type = fr.cargo; - _companies_ai[c->index].num_build_rec = 2; - _companies_ai[c->index].num_loco_to_build = 5; - _companies_ai[c->index].num_want_fullload = 5; - -// _companies_ai[c->index].loco_id = INVALID_VEHICLE; - _companies_ai[c->index].order_list_blocks[0] = 0; - _companies_ai[c->index].order_list_blocks[1] = 1; - _companies_ai[c->index].order_list_blocks[2] = 255; - - _companies_ai[c->index].state = AIS_BUILD_DEFAULT_ROAD_BLOCKS; - _companies_ai[c->index].state_mode = UCHAR_MAX; - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].timeout_counter = 0; -} - -static void AiWantMediumRoadIndustryRoute(Company *c) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyIndustryRoute(&fr); - if (IsInsideMM(fr.distance, 15, 40 + 1)) break; - - // try a random one - AiFindRandomIndustryRoute(&fr); - if (IsInsideMM(fr.distance, 15, 40 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - if (!AiCheckIfRouteIsGood(c, &fr, 2)) return; - - // Fill the source field - _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - _companies_ai[c->index].src.use_tile = 0; - _companies_ai[c->index].src.rand_rng = 9; - _companies_ai[c->index].src.cur_building_rule = 0xFF; - _companies_ai[c->index].src.buildcmd_a = 1; - _companies_ai[c->index].src.direction = 0; - _companies_ai[c->index].src.cargo = fr.cargo | 0x80; - - // Fill the dest field - _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - _companies_ai[c->index].dst.use_tile = 0; - _companies_ai[c->index].dst.rand_rng = 9; - _companies_ai[c->index].dst.cur_building_rule = 0xFF; - _companies_ai[c->index].dst.buildcmd_a = 0xFF; - _companies_ai[c->index].dst.direction = 0; - _companies_ai[c->index].dst.cargo = fr.cargo; - - // Fill common fields - _companies_ai[c->index].cargo_type = fr.cargo; - _companies_ai[c->index].num_build_rec = 2; - _companies_ai[c->index].num_loco_to_build = 3; - _companies_ai[c->index].num_want_fullload = 3; - -// _companies_ai[c->index].loco_id = INVALID_VEHICLE; - _companies_ai[c->index].order_list_blocks[0] = 0; - _companies_ai[c->index].order_list_blocks[1] = 1; - _companies_ai[c->index].order_list_blocks[2] = 255; - - _companies_ai[c->index].state = AIS_BUILD_DEFAULT_ROAD_BLOCKS; - _companies_ai[c->index].state_mode = UCHAR_MAX; - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].timeout_counter = 0; -} - -static void AiWantLongRoadPassengerRoute(Company *c) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyPassengerRoute(&fr); - if (IsInsideMM(fr.distance, 55, 180 + 1)) break; - - // try a random one - AiFindRandomPassengerRoute(&fr); - if (IsInsideMM(fr.distance, 55, 180 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - fr.cargo = CT_PASSENGERS; - - if (!AiCheckIfRouteIsGood(c, &fr, 2)) return; - - // Fill the source field - _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - _companies_ai[c->index].src.use_tile = 0; - _companies_ai[c->index].src.rand_rng = 10; - _companies_ai[c->index].src.cur_building_rule = 0xFF; - _companies_ai[c->index].src.buildcmd_a = 1; - _companies_ai[c->index].src.direction = 0; - _companies_ai[c->index].src.cargo = CT_PASSENGERS; - - // Fill the dest field - _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - _companies_ai[c->index].dst.use_tile = 0; - _companies_ai[c->index].dst.rand_rng = 10; - _companies_ai[c->index].dst.cur_building_rule = 0xFF; - _companies_ai[c->index].dst.buildcmd_a = 0xFF; - _companies_ai[c->index].dst.direction = 0; - _companies_ai[c->index].dst.cargo = CT_PASSENGERS; - - // Fill common fields - _companies_ai[c->index].cargo_type = CT_PASSENGERS; - _companies_ai[c->index].num_build_rec = 2; - _companies_ai[c->index].num_loco_to_build = 4; - _companies_ai[c->index].num_want_fullload = 0; - -// _companies_ai[c->index].loco_id = INVALID_VEHICLE; - _companies_ai[c->index].order_list_blocks[0] = 0; - _companies_ai[c->index].order_list_blocks[1] = 1; - _companies_ai[c->index].order_list_blocks[2] = 255; - - _companies_ai[c->index].state = AIS_BUILD_DEFAULT_ROAD_BLOCKS; - _companies_ai[c->index].state_mode = UCHAR_MAX; - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].timeout_counter = 0; -} - -static void AiWantPassengerRouteInsideTown(Company *c) -{ - int i; - FoundRoute fr; - Town *t; - - i = 60; - for (;;) { - // Find a town big enough - t = AiFindRandomTown(); - if (t != NULL && t->population >= 700) break; - - // only test 60 times - if (--i == 0) return; - } - - fr.cargo = CT_PASSENGERS; - fr.from = fr.to = t; - - if (!AiCheckIfRouteIsGood(c, &fr, 2)) return; - - // Fill the source field - _companies_ai[c->index].src.spec_tile = t->xy; - _companies_ai[c->index].src.use_tile = 0; - _companies_ai[c->index].src.rand_rng = 10; - _companies_ai[c->index].src.cur_building_rule = 0xFF; - _companies_ai[c->index].src.buildcmd_a = 1; - _companies_ai[c->index].src.direction = 0; - _companies_ai[c->index].src.cargo = CT_PASSENGERS; - - // Fill the dest field - _companies_ai[c->index].dst.spec_tile = t->xy; - _companies_ai[c->index].dst.use_tile = 0; - _companies_ai[c->index].dst.rand_rng = 10; - _companies_ai[c->index].dst.cur_building_rule = 0xFF; - _companies_ai[c->index].dst.buildcmd_a = 0xFF; - _companies_ai[c->index].dst.direction = 0; - _companies_ai[c->index].dst.cargo = CT_PASSENGERS; - - // Fill common fields - _companies_ai[c->index].cargo_type = CT_PASSENGERS; - _companies_ai[c->index].num_build_rec = 2; - _companies_ai[c->index].num_loco_to_build = 2; - _companies_ai[c->index].num_want_fullload = 0; - -// _companies_ai[c->index].loco_id = INVALID_VEHICLE; - _companies_ai[c->index].order_list_blocks[0] = 0; - _companies_ai[c->index].order_list_blocks[1] = 1; - _companies_ai[c->index].order_list_blocks[2] = 255; - - _companies_ai[c->index].state = AIS_BUILD_DEFAULT_ROAD_BLOCKS; - _companies_ai[c->index].state_mode = UCHAR_MAX; - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].timeout_counter = 0; -} - -static void AiWantRoadRoute(Company *c) -{ - uint16 r = GB(Random(), 0, 16); - - if (r > 0x4000) { - AiWantLongRoadIndustryRoute(c); - } else if (r > 0x2000) { - AiWantMediumRoadIndustryRoute(c); - } else if (r > 0x1000) { - AiWantLongRoadPassengerRoute(c); - } else { - AiWantPassengerRouteInsideTown(c); - } -} - -static void AiWantPassengerAircraftRoute(Company *c) -{ - FoundRoute fr; - int i; - - /* Get aircraft that would be bought for this route - * (probably, as conditions may change before the route is fully built, - * like running out of money and having to select different aircraft, etc ...) */ - EngineID veh = AiChooseAircraftToBuild(c->money, _companies_ai[c->index].build_kind != 0 ? AIR_CTOL : 0); - - /* No aircraft buildable mean no aircraft route */ - if (veh == INVALID_ENGINE) return; - - const AircraftVehicleInfo *avi = AircraftVehInfo(veh); - - /* For passengers, "optimal" number of days in transit is about 80 to 100 - * Calculate "maximum optimal number of squares" from speed for 80 days - * 20 days should be enough for takeoff, land, taxi, etc ... - * - * "A vehicle traveling at 100kph will cross 5.6 tiles per day" -> - * Since in table aircraft speeds are in "real km/h", this should be accurate - * We get max_squares = avi->max_speed * 5.6 / 100.0 * 80 */ - int max_squares = avi->max_speed * 448 / 100; - - /* For example this will be 10456 tiles for 2334 km/h aircrafts with realistic aircraft speeds - * and 836 with "unrealistic" speeds, much more than the original 95 squares limit - * - * Size of the map, if not rectangular, it is the larger dimension of it - */ - int map_size = max(MapSizeX(), MapSizeY()); - - /* Minimum distance between airports is half of map size, clamped between 1% and 20% of optimum. - * May prevent building plane routes at all on small maps, but they will be ineffective there, so - * it is feature, not a bug. - * On smaller distances, buses or trains are usually more effective approach anyway. - * Additional safeguard is needing at least 20 squares, - * which may trigger in highly unusual configurations */ - int min_squares = max(20, max(max_squares / 100, min(max_squares / 5, map_size / 2))); - - /* Should not happen, unless aircraft with real speed under approx. 5 km/h is selected. - * No such exist, unless using some NewGRF with ballons, zeppelins or similar - * slow-moving stuff. In that case, bail out, it is faster to walk by foot anyway :). */ - if (max_squares < min_squares) return; - - i = 60; - for (;;) { - - // look for one from the subsidy list - AiFindSubsidyPassengerRoute(&fr); - if (IsInsideMM(fr.distance, min_squares, max_squares + 1)) break; - - // try a random one - AiFindRandomPassengerRoute(&fr); - if (IsInsideMM(fr.distance, min_squares, max_squares + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - fr.cargo = CT_PASSENGERS; - if (!AiCheckIfRouteIsGood(c, &fr, 4)) return; - - - // Fill the source field - _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - _companies_ai[c->index].src.use_tile = 0; - _companies_ai[c->index].src.rand_rng = 12; - _companies_ai[c->index].src.cur_building_rule = 0xFF; - _companies_ai[c->index].src.cargo = fr.cargo; - - // Fill the dest field - _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - _companies_ai[c->index].dst.use_tile = 0; - _companies_ai[c->index].dst.rand_rng = 12; - _companies_ai[c->index].dst.cur_building_rule = 0xFF; - _companies_ai[c->index].dst.cargo = fr.cargo; - - // Fill common fields - _companies_ai[c->index].cargo_type = fr.cargo; - _companies_ai[c->index].build_kind = 0; - _companies_ai[c->index].num_build_rec = 2; - _companies_ai[c->index].num_loco_to_build = 1; - /* Using full load always may not be the best. - * Pick random value and rely on selling the vehicle & route - * afterwards if the choice was utterly wrong (or maybe altering the value if AI is improved) - * When traffic is very low or very assymetric, is is better not to full load - * When traffic is high, full/non-full make no difference - * It should be better to run with aircraft only one way full 6 times per year, - * rather than two way full 1 times. - * Practical experiments with AI show that the non-full-load aircrafts are usually - * those that survive - * Also, non-full load is more resistant against starving (by building better stations - * or using exclusive rights) - */ - _companies_ai[c->index].num_want_fullload = Chance16(1, 5); // 20% chance -// _companies_ai[c->index].loco_id = INVALID_VEHICLE; - _companies_ai[c->index].order_list_blocks[0] = 0; - _companies_ai[c->index].order_list_blocks[1] = 1; - _companies_ai[c->index].order_list_blocks[2] = 255; - - _companies_ai[c->index].state = AIS_AIRPORT_STUFF; - _companies_ai[c->index].timeout_counter = 0; -} - -static void AiWantOilRigAircraftRoute(Company *c) -{ - int i; - FoundRoute fr; - Town *t; - Industry *in; - - i = 60; - for (;;) { - // Find a town - t = AiFindRandomTown(); - if (t != NULL) { - // Find a random oil rig industry - in = AiFindRandomIndustry(); - if (in != NULL && GetIndustrySpec(in->type)->behaviour & INDUSTRYBEH_AI_AIRSHIP_ROUTES) { - if (DistanceManhattan(t->xy, in->xy) < 60) - break; - } - } - - // only test 60 times - if (--i == 0) return; - } - - fr.cargo = CT_PASSENGERS; - fr.from = fr.to = t; - - if (!AiCheckIfRouteIsGood(c, &fr, 4)) return; - - // Fill the source field - _companies_ai[c->index].src.spec_tile = t->xy; - _companies_ai[c->index].src.use_tile = 0; - _companies_ai[c->index].src.rand_rng = 12; - _companies_ai[c->index].src.cur_building_rule = 0xFF; - _companies_ai[c->index].src.cargo = CT_PASSENGERS; - - // Fill the dest field - _companies_ai[c->index].dst.spec_tile = in->xy; - _companies_ai[c->index].dst.use_tile = 0; - _companies_ai[c->index].dst.rand_rng = 5; - _companies_ai[c->index].dst.cur_building_rule = 0xFF; - _companies_ai[c->index].dst.cargo = CT_PASSENGERS; - - // Fill common fields - _companies_ai[c->index].cargo_type = CT_PASSENGERS; - _companies_ai[c->index].build_kind = 1; - _companies_ai[c->index].num_build_rec = 2; - _companies_ai[c->index].num_loco_to_build = 1; - _companies_ai[c->index].num_want_fullload = 0; -// _companies_ai[c->index].loco_id = INVALID_VEHICLE; - _companies_ai[c->index].order_list_blocks[0] = 0; - _companies_ai[c->index].order_list_blocks[1] = 1; - _companies_ai[c->index].order_list_blocks[2] = 255; - - _companies_ai[c->index].state = AIS_AIRPORT_STUFF; - _companies_ai[c->index].timeout_counter = 0; -} - -static void AiWantAircraftRoute(Company *c) -{ - uint16 r = (uint16)Random(); - - if (r >= 0x2AAA || _date < 0x3912 + DAYS_TILL_ORIGINAL_BASE_YEAR) { - AiWantPassengerAircraftRoute(c); - } else { - AiWantOilRigAircraftRoute(c); - } -} - - - -static void AiStateWantNewRoute(Company *c) -{ - uint16 r; - int i; - - if (c->money < AiGetBasePrice(c) * 500) { - _companies_ai[c->index].state = AIS_0; - return; - } - - i = 200; - for (;;) { - r = (uint16)Random(); - - if (_settings_game.ai.ai_disable_veh_train && - _settings_game.ai.ai_disable_veh_roadveh && - _settings_game.ai.ai_disable_veh_aircraft && - _settings_game.ai.ai_disable_veh_ship) { - return; - } - - if (r < 0x7626) { - if (_settings_game.ai.ai_disable_veh_train) continue; - AiWantTrainRoute(c); - } else if (r < 0xC4EA) { - if (_settings_game.ai.ai_disable_veh_roadveh) continue; - AiWantRoadRoute(c); - } else if (r < 0xD89B) { - if (_settings_game.ai.ai_disable_veh_aircraft) continue; - AiWantAircraftRoute(c); - } else { - /* Ships are not implemented in this (broken) AI */ - } - - // got a route? - if (_companies_ai[c->index].state != AIS_WANT_NEW_ROUTE) break; - - // time out? - if (--i == 0) { - if (++_companies_ai[c->index].state_counter == 556) _companies_ai[c->index].state = AIS_0; - break; - } - } -} - -static bool AiCheckTrackResources(TileIndex tile, const AiDefaultBlockData *p, byte cargo) -{ - uint rad = (_settings_game.station.modified_catchment) ? CA_TRAIN : CA_UNMODIFIED; - - for (; p->mode != 4; p++) { - AcceptedCargo values; - TileIndex tile2; - uint w; - uint h; - - if (p->mode != 1) continue; - - tile2 = TILE_ADD(tile, ToTileIndexDiff(p->tileoffs)); - w = GB(p->attr, 1, 3); - h = GB(p->attr, 4, 3); - - if (p->attr & 1) Swap(w, h); - - if (cargo & 0x80) { - GetProductionAroundTiles(values, tile2, w, h, rad); - return values[cargo & 0x7F] != 0; - } else { - GetAcceptanceAroundTiles(values, tile2, w, h, rad); - if (!(values[cargo] & ~7)) - return false; - if (cargo != CT_MAIL) - return true; - return !!((values[cargo] >> 1) & ~7); - } - } - - return true; -} - -static CommandCost AiDoBuildDefaultRailTrack(TileIndex tile, const AiDefaultBlockData *p, RailType railtype, uint32 flag) -{ - CommandCost ret; - CommandCost total_cost(EXPENSES_CONSTRUCTION); - Town *t = NULL; - int rating = 0; - int i, j, k; - - for (;;) { - // This will seldomly overflow for valid reasons. Mask it to be on the safe side. - uint c = TILE_MASK(tile + ToTileIndexDiff(p->tileoffs)); - - _cleared_town = NULL; - - if (p->mode < 2) { - if (p->mode == 0) { - // Depot - ret = DoCommand(c, railtype, p->attr, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_TRAIN_DEPOT); - } else { - // Station - ret = DoCommand(c, (railtype & 0x0f) | (p->attr & 1) << 4 | (p->attr >> 4) << 8 | (p->attr >> 1 & 7) << 16, (INVALID_STATION << 16), flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_RAILROAD_STATION); - } - - if (CmdFailed(ret)) return CMD_ERROR; - total_cost.AddCost(ret); - -clear_town_stuff:; - if (_cleared_town != NULL) { - if (t != NULL && t != _cleared_town) - return CMD_ERROR; - t = _cleared_town; - rating += _cleared_town_rating; - } - } else if (p->mode == 2) { - /* Rail */ - if (IsTileType(c, MP_RAILWAY)) return CMD_ERROR; - - j = p->attr; - k = 0; - - /* Build the rail - * note: FOR_EACH_SET_BIT cannot be used here - */ - for (i = 0; i != 6; i++, j >>= 1) { - if (j & 1) { - k = i; - ret = DoCommand(c, railtype, i, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL); - if (CmdFailed(ret)) return CMD_ERROR; - total_cost.AddCost(ret); - } - } - - /* signals too? */ - if (j & 3) { - // Can't build signals on a road. - if (IsTileType(c, MP_ROAD)) return CMD_ERROR; - - if (flag & DC_EXEC) { - j = 4 - j; - do { - ret = DoCommand(c, k, 0, flag, CMD_BUILD_SIGNALS); - } while (--j); - } else { - ret.AddCost(_price.build_signals); - } - if (CmdFailed(ret)) return CMD_ERROR; - total_cost.AddCost(ret); - } - } else if (p->mode == 3) { - //Clear stuff and then build single rail. - if (GetTileSlope(c, NULL) != SLOPE_FLAT) return CMD_ERROR; - ret = DoCommand(c, 0, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_LANDSCAPE_CLEAR); - if (CmdFailed(ret)) return CMD_ERROR; - total_cost.AddCost(ret); - total_cost.AddCost(_price.build_rail); - - if (flag & DC_EXEC) { - DoCommand(c, railtype, p->attr & 1, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_SINGLE_RAIL); - } - - goto clear_town_stuff; - } else { - // Unk - break; - } - - p++; - } - - if (!(flag & DC_EXEC)) { - if (t != NULL && rating > t->ratings[_current_company]) { - return CMD_ERROR; - } - } - - return total_cost; -} - -// Returns rule and cost -static int AiBuildDefaultRailTrack(TileIndex tile, byte p0, byte p1, byte p2, byte p3, byte dir, byte cargo, RailType railtype, CommandCost *cost) -{ - int i; - const AiDefaultRailBlock *p; - - for (i = 0; (p = _default_rail_track_data[i]) != NULL; i++) { - if (p->p0 == p0 && p->p1 == p1 && p->p2 == p2 && p->p3 == p3 && - (p->dir == 0xFF || p->dir == dir || ((p->dir - 1) & 3) == dir)) { - *cost = AiDoBuildDefaultRailTrack(tile, p->data, railtype, DC_NO_TOWN_RATING); - if (CmdSucceeded(*cost) && AiCheckTrackResources(tile, p->data, cargo)) - return i; - } - } - - return -1; -} - -static const byte _terraform_up_flags[] = { - 14, 13, 12, 11, - 10, 9, 8, 7, - 6, 5, 4, 3, - 2, 1, 0, 1, - 2, 1, 4, 1, - 2, 1, 8, 1, - 2, 1, 4, 2, - 2, 1 -}; - -static const byte _terraform_down_flags[] = { - 1, 2, 3, 4, - 5, 6, 1, 8, - 9, 10, 8, 12, - 4, 2, 0, 0, - 1, 2, 3, 4, - 5, 6, 2, 8, - 9, 10, 1, 12, - 8, 4 -}; - -static void AiDoTerraformLand(TileIndex tile, DiagDirection dir, int unk, int mode) -{ - CompanyID old_company; - uint32 r; - Slope slope; - uint h; - - old_company = _current_company; - _current_company = OWNER_NONE; - - r = Random(); - - unk &= (int)r; - - do { - tile = TILE_MASK(tile + TileOffsByDiagDir(dir)); - - r >>= 2; - if (r & 2) { - dir = ChangeDiagDir(dir, (r & 1) ? DIAGDIRDIFF_90LEFT : DIAGDIRDIFF_90RIGHT); - } - } while (--unk >= 0); - - slope = GetTileSlope(tile, &h); - - if (slope != SLOPE_FLAT) { - if (mode > 0 || (mode == 0 && !(r & 0xC))) { - // Terraform up - DoCommand(tile, _terraform_up_flags[slope - 1], 1, - DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_TERRAFORM_LAND); - } else if (h != 0) { - // Terraform down - DoCommand(tile, _terraform_down_flags[slope - 1], 0, - DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_TERRAFORM_LAND); - } - } - - _current_company = old_company; -} - -static void AiStateBuildDefaultRailBlocks(Company *c) -{ - uint i; - int j; - AiBuildRec *aib; - int rule; - CommandCost cost; - - // time out? - if (++_companies_ai[c->index].timeout_counter == 1388) { - _companies_ai[c->index].state = AIS_DELETE_RAIL_BLOCKS; - return; - } - - // do the following 8 times - for (i = 0; i < 8; i++) { - // check if we can build the default track - aib = &_companies_ai[c->index].src; - j = _companies_ai[c->index].num_build_rec; - do { - // this item has already been built? - if (aib->cur_building_rule != 255) continue; - - // adjust the coordinate randomly, - // to make sure that we find a position. - aib->use_tile = AdjustTileCoordRandomly(aib->spec_tile, aib->rand_rng); - - // check if the track can be build there. - rule = AiBuildDefaultRailTrack(aib->use_tile, - _companies_ai[c->index].build_kind, _companies_ai[c->index].num_wagons, - aib->unk6, aib->unk7, - aib->direction, aib->cargo, - _companies_ai[c->index].railtype_to_use, - &cost - ); - - if (rule == -1) { - // cannot build, terraform after a while - if (_companies_ai[c->index].state_counter >= 600) { - AiDoTerraformLand(aib->use_tile, (DiagDirection)(Random() & 3), 3, (int8)_companies_ai[c->index].state_mode); - } - // also try the other terraform direction - if (++_companies_ai[c->index].state_counter >= 1000) { - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].state_mode = -_companies_ai[c->index].state_mode; - } - } else if (CheckCompanyHasMoney(cost)) { - // company has money, build it. - aib->cur_building_rule = rule; - - AiDoBuildDefaultRailTrack( - aib->use_tile, - _default_rail_track_data[rule]->data, - _companies_ai[c->index].railtype_to_use, - DC_EXEC | DC_NO_TOWN_RATING - ); - } - } while (++aib, --j); - } - - // check if we're done with all of them - aib = &_companies_ai[c->index].src; - j = _companies_ai[c->index].num_build_rec; - do { - if (aib->cur_building_rule == 255) return; - } while (++aib, --j); - - // yep, all are done. switch state to the rail building state. - _companies_ai[c->index].state = AIS_BUILD_RAIL; - _companies_ai[c->index].state_mode = 255; -} - -static TileIndex AiGetEdgeOfDefaultRailBlock(byte rule, TileIndex tile, byte cmd, DiagDirection *dir) -{ - const AiDefaultBlockData *p = _default_rail_track_data[rule]->data; - - while (p->mode != 3 || !((--cmd) & 0x80)) p++; - - return tile + ToTileIndexDiff(p->tileoffs) - TileOffsByDiagDir(*dir = p->attr); -} - -struct AiRailPathFindData { - TileIndex tile; - TileIndex tile2; - int count; - bool flag; -}; - -static bool AiEnumFollowTrack(TileIndex tile, AiRailPathFindData *a, int track, uint length) -{ - if (a->flag) return true; - - if (length > 20 || tile == a->tile) { - a->flag = true; - return true; - } - - if (DistanceMax(tile, a->tile2) < 4) a->count++; - - return false; -} - -static bool AiDoFollowTrack(const Company *c) -{ - AiRailPathFindData arpfd; - - arpfd.tile = _companies_ai[c->index].start_tile_a; - arpfd.tile2 = _companies_ai[c->index].cur_tile_a; - arpfd.flag = false; - arpfd.count = 0; - FollowTrack(_companies_ai[c->index].cur_tile_a + TileOffsByDiagDir(_companies_ai[c->index].cur_dir_a), PATHFIND_FLAGS_NONE, TRANSPORT_RAIL, 0, ReverseDiagDir(_companies_ai[c->index].cur_dir_a), - (TPFEnumProc*)AiEnumFollowTrack, NULL, &arpfd); - return arpfd.count > 8; -} - -struct AiRailFinder { - TileIndex final_tile; - DiagDirection final_dir; - byte depth; - byte recursive_mode; - DiagDirection cur_best_dir; - DiagDirection best_dir; - byte cur_best_depth; - byte best_depth; - uint cur_best_dist; - const byte *best_ptr; - uint best_dist; - TileIndex cur_best_tile, best_tile; - TileIndex bridge_end_tile; - Company *company; -}; - -static const byte _ai_table_15[4][8] = { - {0, 0, 4, 3, 3, 1, 128 + 0, 64}, - {1, 1, 2, 0, 4, 2, 128 + 1, 65}, - {0, 2, 2, 3, 5, 1, 128 + 2, 66}, - {1, 3, 5, 0, 3, 2, 128 + 3, 67} -}; - - -static bool AiIsTileBanned(const Company *c, TileIndex tile, byte val) -{ - int i; - - for (i = 0; i != _companies_ai[c->index].banned_tile_count; i++) { - if (_companies_ai[c->index].banned_tiles[i] == tile && _companies_ai[c->index].banned_val[i] == val) { - return true; - } - } - return false; -} - -static void AiBanTile(Company *c, TileIndex tile, byte val) -{ - uint i; - - for (i = lengthof(_companies_ai[c->index].banned_tiles) - 1; i != 0; i--) { - _companies_ai[c->index].banned_tiles[i] = _companies_ai[c->index].banned_tiles[i - 1]; - _companies_ai[c->index].banned_val[i] = _companies_ai[c->index].banned_val[i - 1]; - } - - _companies_ai[c->index].banned_tiles[0] = tile; - _companies_ai[c->index].banned_val[0] = val; - - if (_companies_ai[c->index].banned_tile_count != lengthof(_companies_ai[c->index].banned_tiles)) { - _companies_ai[c->index].banned_tile_count++; - } -} - -static void AiBuildRailRecursive(AiRailFinder *arf, TileIndex tile, DiagDirection dir); - -static bool AiCheckRailPathBetter(AiRailFinder *arf, const byte *p) -{ - bool better = false; - - if (arf->recursive_mode < 1) { - // Mode is 0. This means destination has not been found yet. - // If the found path is shorter than the current one, remember it. - if (arf->cur_best_dist < arf->best_dist) { - arf->best_dir = arf->cur_best_dir; - arf->best_dist = arf->cur_best_dist; - arf->best_ptr = p; - arf->best_tile = arf->cur_best_tile; - better = true; - } - } else if (arf->recursive_mode > 1) { - // Mode is 2. - if (arf->best_dist != 0 || arf->cur_best_depth < arf->best_depth) { - arf->best_depth = arf->cur_best_depth; - arf->best_dist = 0; - arf->best_ptr = p; - arf->best_tile = 0; - better = true; - } - } - arf->recursive_mode = 0; - arf->cur_best_dist = UINT_MAX; - arf->cur_best_depth = 0xff; - - return better; -} - -static inline void AiCheckBuildRailBridgeHere(AiRailFinder *arf, TileIndex tile, const byte *p) -{ - Slope tileh; - uint z; - bool flag; - - DiagDirection dir2 = (DiagDirection)(p[0] & 3); - - tileh = GetTileSlope(tile, &z); - if (tileh == InclinedSlope(ReverseDiagDir(dir2)) || (tileh == SLOPE_FLAT && z != 0)) { - TileIndex tile_new = tile; - - // Allow bridges directly over bottom tiles - flag = z == 0; - for (;;) { - TileType type; - - if ((TileIndexDiff)tile_new < -TileOffsByDiagDir(dir2)) return; // Wraping around map, no bridge possible! - tile_new = TILE_MASK(tile_new + TileOffsByDiagDir(dir2)); - type = GetTileType(tile_new); - - if (type == MP_CLEAR || type == MP_TREES || GetTileSlope(tile_new, NULL) != SLOPE_FLAT) { - if (!flag) return; - break; - } - if (type != MP_WATER && type != MP_RAILWAY && type != MP_ROAD) return; - flag = true; - } - - // Is building a (rail)bridge possible at this place (type doesn't matter)? - if (CmdFailed(DoCommand(tile_new, tile, _companies_ai[arf->company->index].railtype_to_use << 8 | TRANSPORT_RAIL << 15, DC_AUTO, CMD_BUILD_BRIDGE))) { - return; - } - AiBuildRailRecursive(arf, tile_new, dir2); - - // At the bottom depth, check if the new path is better than the old one. - if (arf->depth == 1) { - if (AiCheckRailPathBetter(arf, p)) arf->bridge_end_tile = tile_new; - } - } -} - -static inline void AiCheckBuildRailTunnelHere(AiRailFinder *arf, TileIndex tile, const byte *p) -{ - uint z; - - if (GetTileSlope(tile, &z) == InclinedSlope((DiagDirection)(p[0] & 3)) && z != 0) { - CommandCost cost = DoCommand(tile, _companies_ai[arf->company->index].railtype_to_use, 0, DC_AUTO, CMD_BUILD_TUNNEL); - - if (CmdSucceeded(cost) && cost.GetCost() <= (arf->company->money >> 4)) { - AiBuildRailRecursive(arf, _build_tunnel_endtile, (DiagDirection)(p[0] & 3)); - if (arf->depth == 1) AiCheckRailPathBetter(arf, p); - } - } -} - - -static void AiBuildRailRecursive(AiRailFinder *arf, TileIndex tile, DiagDirection dir) -{ - const byte *p; - - tile = TILE_MASK(tile + TileOffsByDiagDir(dir)); - - // Reached destination? - if (tile == arf->final_tile) { - if (arf->final_dir != ReverseDiagDir(dir)) { - if (arf->recursive_mode != 2) arf->recursive_mode = 1; - } else if (arf->recursive_mode != 2) { - arf->recursive_mode = 2; - arf->cur_best_depth = arf->depth; - } else { - if (arf->depth < arf->cur_best_depth) arf->cur_best_depth = arf->depth; - } - return; - } - - // Depth too deep? - if (arf->depth >= 4) { - uint dist = DistanceMaxPlusManhattan(tile, arf->final_tile); - - if (dist < arf->cur_best_dist) { - // Store the tile that is closest to the final position. - arf->cur_best_depth = arf->depth; - arf->cur_best_dist = dist; - arf->cur_best_tile = tile; - arf->cur_best_dir = dir; - } - return; - } - - // Increase recursion depth - arf->depth++; - - // Grab pointer to list of stuff that is possible to build - p = _ai_table_15[dir]; - - // Try to build a single rail in all directions. - if (GetTileZ(tile) == 0) { - p += 6; - } else { - do { - // Make sure the tile is not in the list of banned tiles and that a rail can be built here. - if (!AiIsTileBanned(arf->company, tile, p[0]) && - CmdSucceeded(DoCommand(tile, _companies_ai[arf->company->index].railtype_to_use, p[0], DC_AUTO | DC_NO_WATER | DC_NO_RAIL_OVERLAP, CMD_BUILD_SINGLE_RAIL))) { - AiBuildRailRecursive(arf, tile, (DiagDirection)p[1]); - } - - // At the bottom depth? - if (arf->depth == 1) AiCheckRailPathBetter(arf, p); - - p += 2; - } while (!(p[0] & 0x80)); - } - - AiCheckBuildRailBridgeHere(arf, tile, p); - AiCheckBuildRailTunnelHere(arf, tile, p + 1); - - arf->depth--; -} - - -static void AiBuildRailConstruct(Company *c) -{ - AiRailFinder arf; - int i; - - // Check too much lookahead? - if (AiDoFollowTrack(c)) { - _companies_ai[c->index].state_counter = (Random()&0xE)+6; // Destruct this amount of blocks - _companies_ai[c->index].state_mode = 1; // Start destruct - - // Ban this tile and don't reach it for a while. - AiBanTile(c, _companies_ai[c->index].cur_tile_a, FindFirstBit(GetRailTrackStatus(_companies_ai[c->index].cur_tile_a))); - return; - } - - // Setup recursive finder and call it. - arf.company = c; - arf.final_tile = _companies_ai[c->index].cur_tile_b; - arf.final_dir = _companies_ai[c->index].cur_dir_b; - arf.depth = 0; - arf.recursive_mode = 0; - arf.best_ptr = NULL; - arf.cur_best_dist = UINT_MAX; - arf.cur_best_depth = 0xff; - arf.best_dist = UINT_MAX; - arf.best_depth = 0xff; - arf.cur_best_tile = 0; - arf.best_tile = 0; - AiBuildRailRecursive(&arf, _companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_dir_a); - - // Reached destination? - if (arf.recursive_mode == 2 && arf.cur_best_depth == 0) { - _companies_ai[c->index].state_mode = 255; - return; - } - - // Didn't find anything to build? - if (arf.best_ptr == NULL) { - // Terraform some - for (i = 0; i != 5; i++) { - AiDoTerraformLand(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_dir_a, 3, 0); - } - - if (++_companies_ai[c->index].state_counter == 21) { - _companies_ai[c->index].state_counter = 40; - _companies_ai[c->index].state_mode = 1; - - // Ban this tile - AiBanTile(c, _companies_ai[c->index].cur_tile_a, FindFirstBit(GetRailTrackStatus(_companies_ai[c->index].cur_tile_a))); - } - return; - } - - _companies_ai[c->index].cur_tile_a += TileOffsByDiagDir(_companies_ai[c->index].cur_dir_a); - - if (arf.best_ptr[0] & 0x80) { - TileIndex t1 = _companies_ai[c->index].cur_tile_a; - TileIndex t2 = arf.bridge_end_tile; - - int32 bridge_len = GetTunnelBridgeLength(t1, t2); - - DiagDirection dir = (TileX(t1) == TileX(t2) ? DIAGDIR_SE : DIAGDIR_SW); - Track track = DiagDirToDiagTrack(dir); - - if (t2 < t1) dir = ReverseDiagDir(dir); - - /* try to build a long rail instead of bridge... */ - bool fail = false; - CommandCost cost; - TileIndex t = t1; - - /* try to build one rail on each tile - can't use CMD_BUILD_RAILROAD_TRACK now, it can build one part of track without failing */ - do { - cost = DoCommand(t, _companies_ai[c->index].railtype_to_use, track, DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL); - /* do not allow building over existing track */ - if (CmdFailed(cost) || IsTileType(t, MP_RAILWAY)) { - fail = true; - break; - } - t += TileOffsByDiagDir(dir); - } while (t != t2); - - /* can we build long track? */ - if (!fail) cost = DoCommand(t1, t2, _companies_ai[c->index].railtype_to_use | (track << 4), DC_AUTO | DC_NO_WATER, CMD_BUILD_RAILROAD_TRACK); - - if (!fail && CmdSucceeded(cost) && cost.GetCost() <= c->money) { - DoCommand(t1, t2, _companies_ai[c->index].railtype_to_use | (track << 4), DC_AUTO | DC_NO_WATER | DC_EXEC, CMD_BUILD_RAILROAD_TRACK); - } else { - - /* Figure out which (rail)bridge type to build - * start with best bridge, then go down to worse and worse bridges - * unnecessary to check for worst bridge (i=0), since AI will always build that. */ - int i; - for (i = MAX_BRIDGES - 1; i != 0; i--) { - if (CheckBridge_Stuff(i, bridge_len)) { - CommandCost cost = DoCommand(t1, t2, i | _companies_ai[c->index].railtype_to_use << 8 | TRANSPORT_RAIL << 15, DC_AUTO, CMD_BUILD_BRIDGE); - if (CmdSucceeded(cost) && cost.GetCost() < (c->money >> 1) && cost.GetCost() < ((c->money + _economy.max_loan - c->current_loan) >> 5)) break; - } - } - - /* Build it */ - DoCommand(t1, t2, i | _companies_ai[c->index].railtype_to_use << 8 | TRANSPORT_RAIL << 15, DC_AUTO | DC_EXEC, CMD_BUILD_BRIDGE); - } - - _companies_ai[c->index].cur_tile_a = t2; - _companies_ai[c->index].state_counter = 0; - } else if (arf.best_ptr[0] & 0x40) { - // tunnel - DoCommand(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].railtype_to_use, 0, DC_AUTO | DC_EXEC, CMD_BUILD_TUNNEL); - _companies_ai[c->index].cur_tile_a = _build_tunnel_endtile; - _companies_ai[c->index].state_counter = 0; - } else { - // rail - _companies_ai[c->index].cur_dir_a = (DiagDirection)(arf.best_ptr[1] & 3); - DoCommand(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].railtype_to_use, arf.best_ptr[0], - DC_EXEC | DC_AUTO | DC_NO_WATER | DC_NO_RAIL_OVERLAP, CMD_BUILD_SINGLE_RAIL); - _companies_ai[c->index].state_counter = 0; - } - - if (arf.best_tile != 0) { - for (i = 0; i != 2; i++) { - AiDoTerraformLand(arf.best_tile, arf.best_dir, 3, 0); - } - } -} - -static bool AiRemoveTileAndGoForward(Company *c) -{ - const byte *ptr; - TileIndex tile = _companies_ai[c->index].cur_tile_a; - TileIndex tilenew; - - if (IsTileType(tile, MP_TUNNELBRIDGE)) { - if (IsTunnel(tile)) { - // Clear the tunnel and continue at the other side of it. - if (CmdFailed(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) - return false; - _companies_ai[c->index].cur_tile_a = TILE_MASK(_build_tunnel_endtile - TileOffsByDiagDir(_companies_ai[c->index].cur_dir_a)); - return true; - } else { // IsBridge(tile) - // Check if the bridge points in the right direction. - // This is not really needed the first place AiRemoveTileAndGoForward is called. - if (DiagDirToAxis(GetTunnelBridgeDirection(tile)) != (_companies_ai[c->index].cur_dir_a & 1)) return false; - - tile = GetOtherBridgeEnd(tile); - - tilenew = TILE_MASK(tile - TileOffsByDiagDir(_companies_ai[c->index].cur_dir_a)); - // And clear the bridge. - if (CmdFailed(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) - return false; - _companies_ai[c->index].cur_tile_a = tilenew; - return true; - } - } - - // Find the railtype at the position. Quit if no rail there. - TrackBits bits = GetRailTrackStatus(tile) & DiagdirReachesTracks(ReverseDiagDir(_companies_ai[c->index].cur_dir_a)); - if (bits == TRACK_BIT_NONE) return false; - - // Convert into a bit position that CMD_REMOVE_SINGLE_RAIL expects. - Track track = FindFirstTrack(bits); - - // Then remove and signals if there are any. - if (IsTileType(tile, MP_RAILWAY) && - GetRailTileType(tile) == RAIL_TILE_SIGNALS) { - DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_SIGNALS); - } - - // And also remove the rail. - if (CmdFailed(DoCommand(tile, 0, track, DC_EXEC, CMD_REMOVE_SINGLE_RAIL))) - return false; - - // Find the direction at the other edge of the rail. - ptr = _ai_table_15[ReverseDiagDir(_companies_ai[c->index].cur_dir_a)]; - while (ptr[0] != track) ptr += 2; - _companies_ai[c->index].cur_dir_a = ReverseDiagDir((DiagDirection)ptr[1]); - - // And then also switch tile. - _companies_ai[c->index].cur_tile_a = TILE_MASK(_companies_ai[c->index].cur_tile_a - TileOffsByDiagDir(_companies_ai[c->index].cur_dir_a)); - - return true; -} - - -static void AiBuildRailDestruct(Company *c) -{ - // Decrease timeout. - if (!--_companies_ai[c->index].state_counter) { - _companies_ai[c->index].state_mode = 2; - _companies_ai[c->index].state_counter = 0; - } - - // Don't do anything if the destination is already reached. - if (_companies_ai[c->index].cur_tile_a == _companies_ai[c->index].start_tile_a) return; - - AiRemoveTileAndGoForward(c); -} - - -static void AiBuildRail(Company *c) -{ - switch (_companies_ai[c->index].state_mode) { - case 0: // Construct mode, build new rail. - AiBuildRailConstruct(c); - break; - - case 1: // Destruct mode, destroy the rail currently built. - AiBuildRailDestruct(c); - break; - - case 2: { - uint i; - - // Terraform some and then try building again. - for (i = 0; i != 4; i++) { - AiDoTerraformLand(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_dir_a, 3, 0); - } - - if (++_companies_ai[c->index].state_counter == 4) { - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].state_mode = 0; - } - } - - default: break; - } -} - -static void AiStateBuildRail(Company *c) -{ - int num; - AiBuildRec *aib; - byte cmd; - TileIndex tile; - DiagDirection dir; - - // time out? - if (++_companies_ai[c->index].timeout_counter == 1388) { - _companies_ai[c->index].state = AIS_DELETE_RAIL_BLOCKS; - return; - } - - // Currently building a rail between two points? - if (_companies_ai[c->index].state_mode != 255) { - AiBuildRail(c); - - // Alternate between edges - Swap(_companies_ai[c->index].start_tile_a, _companies_ai[c->index].start_tile_b); - Swap(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_tile_b); - Swap(_companies_ai[c->index].start_dir_a, _companies_ai[c->index].start_dir_b); - Swap(_companies_ai[c->index].cur_dir_a, _companies_ai[c->index].cur_dir_b); - return; - } - - // Now, find two new points to build between - num = _companies_ai[c->index].num_build_rec; - aib = &_companies_ai[c->index].src; - - for (;;) { - cmd = aib->buildcmd_a; - aib->buildcmd_a = 255; - if (cmd != 255) break; - - cmd = aib->buildcmd_b; - aib->buildcmd_b = 255; - if (cmd != 255) break; - - aib++; - if (--num == 0) { - _companies_ai[c->index].state = AIS_BUILD_RAIL_VEH; - _companies_ai[c->index].state_counter = 0; // timeout - return; - } - } - - // Find first edge to build from. - tile = AiGetEdgeOfDefaultRailBlock(aib->cur_building_rule, aib->use_tile, cmd & 3, &dir); - _companies_ai[c->index].start_tile_a = tile; - _companies_ai[c->index].cur_tile_a = tile; - _companies_ai[c->index].start_dir_a = dir; - _companies_ai[c->index].cur_dir_a = dir; - DoCommand(TILE_MASK(tile + TileOffsByDiagDir(dir)), 0, (dir & 1) ? 1 : 0, DC_EXEC, CMD_REMOVE_SINGLE_RAIL); - - assert(TILE_MASK(tile) != 0xFF00); - - // Find second edge to build to - aib = (&_companies_ai[c->index].src) + ((cmd >> 4) & 0xF); - tile = AiGetEdgeOfDefaultRailBlock(aib->cur_building_rule, aib->use_tile, (cmd >> 2) & 3, &dir); - _companies_ai[c->index].start_tile_b = tile; - _companies_ai[c->index].cur_tile_b = tile; - _companies_ai[c->index].start_dir_b = dir; - _companies_ai[c->index].cur_dir_b = dir; - DoCommand(TILE_MASK(tile + TileOffsByDiagDir(dir)), 0, (dir & 1) ? 1 : 0, DC_EXEC, CMD_REMOVE_SINGLE_RAIL); - - assert(TILE_MASK(tile) != 0xFF00); - - // And setup state. - _companies_ai[c->index].state_mode = 2; - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].banned_tile_count = 0; -} - -static StationID AiGetStationIdByDef(TileIndex tile, int id) -{ - const AiDefaultBlockData *p = _default_rail_track_data[id]->data; - while (p->mode != 1) p++; - return GetStationIndex(TILE_ADD(tile, ToTileIndexDiff(p->tileoffs))); -} - -static EngineID AiFindBestWagon(CargoID cargo, RailType railtype) -{ - EngineID best_veh_index = INVALID_ENGINE; - uint16 best_capacity = 0; - uint16 best_speed = 0; - uint speed; - const Engine *e; - - FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) { - EngineID i = e->index; - const RailVehicleInfo *rvi = &e->u.rail; - - if (!IsCompatibleRail(rvi->railtype, railtype) || - rvi->railveh_type != RAILVEH_WAGON || - !HasBit(e->company_avail, _current_company)) { - continue; - } - - if (rvi->cargo_type != cargo) continue; - - /* max_speed of 0 indicates no speed limit */ - speed = rvi->max_speed == 0 ? 0xFFFF : rvi->max_speed; - - if (rvi->capacity >= best_capacity && speed >= best_speed) { - best_capacity = rvi->capacity; - best_speed = best_speed; - best_veh_index = i; - } - } - - return best_veh_index; -} - -static void AiStateBuildRailVeh(Company *c) -{ - const AiDefaultBlockData *ptr; - TileIndex tile; - EngineID veh; - int i; - CargoID cargo; - CommandCost cost; - Vehicle *v; - VehicleID loco_id; - - ptr = _default_rail_track_data[_companies_ai[c->index].src.cur_building_rule]->data; - while (ptr->mode != 0) ptr++; - - tile = TILE_ADD(_companies_ai[c->index].src.use_tile, ToTileIndexDiff(ptr->tileoffs)); - - - cargo = _companies_ai[c->index].cargo_type; - for (i = 0;;) { - if (_companies_ai[c->index].wagon_list[i] == INVALID_VEHICLE) { - veh = AiFindBestWagon(cargo, _companies_ai[c->index].railtype_to_use); - /* veh will return INVALID_ENGINE if no suitable wagon is available. - * We shall treat this in the same way as having no money */ - if (veh == INVALID_ENGINE) goto handle_nocash; - cost = DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE); - if (CmdFailed(cost)) goto handle_nocash; - _companies_ai[c->index].wagon_list[i] = _new_vehicle_id; - _companies_ai[c->index].wagon_list[i + 1] = INVALID_VEHICLE; - return; - } - if (cargo == CT_MAIL) cargo = CT_PASSENGERS; - if (++i == _companies_ai[c->index].num_wagons * 2 - 1) break; - } - - // Which locomotive to build? - veh = AiChooseTrainToBuild(_companies_ai[c->index].railtype_to_use, c->money, cargo != CT_PASSENGERS ? 1 : 0, tile); - if (veh == INVALID_ENGINE) { -handle_nocash: - // after a while, if AI still doesn't have cash, get out of this block by selling the wagons. - if (++_companies_ai[c->index].state_counter == 1000) { - for (i = 0; _companies_ai[c->index].wagon_list[i] != INVALID_VEHICLE; i++) { - cost = DoCommand(tile, _companies_ai[c->index].wagon_list[i], 0, DC_EXEC, CMD_SELL_RAIL_WAGON); - assert(CmdSucceeded(cost)); - } - _companies_ai[c->index].state = AIS_0; - } - return; - } - - // Try to build the locomotive - cost = DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE); - assert(CmdSucceeded(cost)); - loco_id = _new_vehicle_id; - - // Sell a vehicle if the train is double headed. - v = GetVehicle(loco_id); - if (v->Next() != NULL) { - i = _companies_ai[c->index].wagon_list[_companies_ai[c->index].num_wagons * 2 - 2]; - _companies_ai[c->index].wagon_list[_companies_ai[c->index].num_wagons * 2 - 2] = INVALID_VEHICLE; - DoCommand(tile, i, 0, DC_EXEC, CMD_SELL_RAIL_WAGON); - } - - // Move the wagons onto the train - for (i = 0; _companies_ai[c->index].wagon_list[i] != INVALID_VEHICLE; i++) { - DoCommand(tile, _companies_ai[c->index].wagon_list[i] | (loco_id << 16), 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); - } - - for (i = 0; _companies_ai[c->index].order_list_blocks[i] != 0xFF; i++) { - const AiBuildRec *aib = &_companies_ai[c->index].src + _companies_ai[c->index].order_list_blocks[i]; - bool is_pass = ( - _companies_ai[c->index].cargo_type == CT_PASSENGERS || - _companies_ai[c->index].cargo_type == CT_MAIL || - (_settings_game.game_creation.landscape == LT_TEMPERATE && _companies_ai[c->index].cargo_type == CT_VALUABLES) - ); - Order order; - - order.MakeGoToStation(AiGetStationIdByDef(aib->use_tile, aib->cur_building_rule)); - - if (!is_pass && i == 1) order.SetUnloadType(OUFB_UNLOAD); - if (_companies_ai[c->index].num_want_fullload != 0 && (is_pass || i == 0)) - order.SetLoadType(OLFB_FULL_LOAD); - - DoCommand(0, loco_id + (i << 16), order.Pack(), DC_EXEC, CMD_INSERT_ORDER); - } - - DoCommand(0, loco_id, 0, DC_EXEC, CMD_START_STOP_VEHICLE); - - DoCommand(0, loco_id, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT); - - if (_companies_ai[c->index].num_want_fullload != 0) _companies_ai[c->index].num_want_fullload--; - - if (--_companies_ai[c->index].num_loco_to_build != 0) { -// _companies_ai[c->index].loco_id = INVALID_VEHICLE; - _companies_ai[c->index].wagon_list[0] = INVALID_VEHICLE; - } else { - _companies_ai[c->index].state = AIS_0; - } -} - -static void AiStateDeleteRailBlocks(Company *c) -{ - const AiBuildRec *aib = &_companies_ai[c->index].src; - uint num = _companies_ai[c->index].num_build_rec; - - do { - const AiDefaultBlockData *b; - - if (aib->cur_building_rule == 255) continue; - for (b = _default_rail_track_data[aib->cur_building_rule]->data; b->mode != 4; b++) { - DoCommand(TILE_ADD(aib->use_tile, ToTileIndexDiff(b->tileoffs)), 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); - } - } while (++aib, --num); - - _companies_ai[c->index].state = AIS_0; -} - -static bool AiCheckRoadResources(TileIndex tile, const AiDefaultBlockData *p, byte cargo) -{ - uint values[NUM_CARGO]; - int rad; - - if (_settings_game.station.modified_catchment) { - rad = CA_TRUCK; // Same as CA_BUS at the moment? - } else { // change that at some point? - rad = 4; - } - - for (;; p++) { - if (p->mode == 4) { - return true; - } else if (p->mode == 1) { - TileIndex tile2 = TILE_ADD(tile, ToTileIndexDiff(p->tileoffs)); - - if (cargo & 0x80) { - GetProductionAroundTiles(values, tile2, 1, 1, rad); - return values[cargo & 0x7F] != 0; - } else { - GetAcceptanceAroundTiles(values, tile2, 1, 1, rad); - return (values[cargo]&~7) != 0; - } - } - } -} - -static bool _want_road_truck_station; -static CommandCost AiDoBuildDefaultRoadBlock(TileIndex tile, const AiDefaultBlockData *p, uint32 flag); - -// Returns rule and cost -static int AiFindBestDefaultRoadBlock(TileIndex tile, byte direction, byte cargo, CommandCost *cost) -{ - int i; - const AiDefaultRoadBlock *p; - - _want_road_truck_station = (cargo & 0x7F) != CT_PASSENGERS; - - for (i = 0; (p = _road_default_block_data[i]) != NULL; i++) { - if (p->dir == direction) { - *cost = AiDoBuildDefaultRoadBlock(tile, p->data, 0); - if (CmdSucceeded(*cost) && AiCheckRoadResources(tile, p->data, cargo)) - return i; - } - } - - return -1; -} - -static CommandCost AiDoBuildDefaultRoadBlock(TileIndex tile, const AiDefaultBlockData *p, uint32 flag) -{ - CommandCost ret; - CommandCost total_cost(EXPENSES_CONSTRUCTION); - Town *t = NULL; - int rating = 0; - int roadflag = 0; - - for (;p->mode != 4;p++) { - TileIndex c = TILE_MASK(tile + ToTileIndexDiff(p->tileoffs)); - - _cleared_town = NULL; - - if (p->mode == 2) { - if (IsNormalRoadTile(c) && - (GetRoadBits(c, ROADTYPE_ROAD) & p->attr) != 0) { - roadflag |= 2; - - // all bits are already built? - if ((GetRoadBits(c, ROADTYPE_ROAD) & p->attr) == p->attr) continue; - } - - ret = DoCommand(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD); - if (CmdFailed(ret)) return CMD_ERROR; - total_cost.AddCost(ret); - - continue; - } - - if (p->mode == 0) { - // Depot - ret = DoCommand(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_DEPOT); - goto clear_town_stuff; - } else if (p->mode == 1) { - if (_want_road_truck_station) { - // Truck station - ret = DoCommand(c, p->attr, ROADTYPES_ROAD << 2 | ROADSTOP_TRUCK | INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP); - } else { - // Bus station - ret = DoCommand(c, p->attr, ROADTYPES_ROAD << 2 | ROADSTOP_BUS | INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP); - } -clear_town_stuff:; - - if (CmdFailed(ret)) return CMD_ERROR; - total_cost.AddCost(ret); - - if (_cleared_town != NULL) { - if (t != NULL && t != _cleared_town) return CMD_ERROR; - t = _cleared_town; - rating += _cleared_town_rating; - } - } else if (p->mode == 3) { - if (flag & DC_EXEC) continue; - - if (GetTileSlope(c, NULL) != SLOPE_FLAT) return CMD_ERROR; - - if (!IsNormalRoadTile(c)) { - ret = DoCommand(c, 0, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_LANDSCAPE_CLEAR); - if (CmdFailed(ret)) return CMD_ERROR; - } - - } - } - - if (!_want_road_truck_station && !(roadflag & 2)) return CMD_ERROR; - - if (!(flag & DC_EXEC)) { - if (t != NULL && rating > t->ratings[_current_company]) return CMD_ERROR; - } - return total_cost; -} - -// Make sure the blocks are not too close to each other -static bool AiCheckBlockDistances(Company *c, TileIndex tile) -{ - const AiBuildRec *aib = &_companies_ai[c->index].src; - uint num = _companies_ai[c->index].num_build_rec; - - do { - if (aib->cur_building_rule != 255) { - if (DistanceManhattan(aib->use_tile, tile) < 9) return false; - } - } while (++aib, --num); - - return true; -} - - -static void AiStateBuildDefaultRoadBlocks(Company *c) -{ - uint i; - int j; - AiBuildRec *aib; - int rule; - CommandCost cost; - - // time out? - if (++_companies_ai[c->index].timeout_counter == 1388) { - _companies_ai[c->index].state = AIS_DELETE_RAIL_BLOCKS; - return; - } - - // do the following 8 times - for (i = 0; i != 8; i++) { - // check if we can build the default track - aib = &_companies_ai[c->index].src; - j = _companies_ai[c->index].num_build_rec; - do { - // this item has already been built? - if (aib->cur_building_rule != 255) continue; - - // adjust the coordinate randomly, - // to make sure that we find a position. - aib->use_tile = AdjustTileCoordRandomly(aib->spec_tile, aib->rand_rng); - - // check if the road can be built there. - rule = AiFindBestDefaultRoadBlock( - aib->use_tile, aib->direction, aib->cargo, &cost - ); - - if (rule == -1) { - // cannot build, terraform after a while - if (_companies_ai[c->index].state_counter >= 600) { - AiDoTerraformLand(aib->use_tile, (DiagDirection)(Random() & 3), 3, (int8)_companies_ai[c->index].state_mode); - } - // also try the other terraform direction - if (++_companies_ai[c->index].state_counter >= 1000) { - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].state_mode = -_companies_ai[c->index].state_mode; - } - } else if (CheckCompanyHasMoney(cost) && AiCheckBlockDistances(c, aib->use_tile)) { - CommandCost r; - - // company has money, build it. - aib->cur_building_rule = rule; - - r = AiDoBuildDefaultRoadBlock( - aib->use_tile, - _road_default_block_data[rule]->data, - DC_EXEC | DC_NO_TOWN_RATING - ); - assert(CmdSucceeded(r)); - } - } while (++aib, --j); - } - - // check if we're done with all of them - aib = &_companies_ai[c->index].src; - j = _companies_ai[c->index].num_build_rec; - do { - if (aib->cur_building_rule == 255) return; - } while (++aib, --j); - - // yep, all are done. switch state to the rail building state. - _companies_ai[c->index].state = AIS_BUILD_ROAD; - _companies_ai[c->index].state_mode = 255; -} - -struct AiRoadFinder { - TileIndex final_tile; - DiagDirection final_dir; - byte depth; - byte recursive_mode; - DiagDirection cur_best_dir; - DiagDirection best_dir; - byte cur_best_depth; - byte best_depth; - uint cur_best_dist; - const byte *best_ptr; - uint best_dist; - TileIndex cur_best_tile, best_tile; - TileIndex bridge_end_tile; - Company *company; -}; - -struct AiRoadEnum { - TileIndex dest; - TileIndex best_tile; - Trackdir best_track; - uint best_dist; -}; - -static void AiBuildRoadRecursive(AiRoadFinder *arf, TileIndex tile, DiagDirection dir); - -static bool AiCheckRoadPathBetter(AiRoadFinder *arf, const byte *p) -{ - bool better = false; - - if (arf->recursive_mode < 1) { - // Mode is 0. This means destination has not been found yet. - // If the found path is shorter than the current one, remember it. - if (arf->cur_best_dist < arf->best_dist || - (arf->cur_best_dist == arf->best_dist && arf->cur_best_depth < arf->best_depth)) { - arf->best_depth = arf->cur_best_depth; - arf->best_dist = arf->cur_best_dist; - arf->best_dir = arf->cur_best_dir; - arf->best_ptr = p; - arf->best_tile = arf->cur_best_tile; - better = true; - } - } else if (arf->recursive_mode > 1) { - // Mode is 2. - if (arf->best_dist != 0 || arf->cur_best_depth < arf->best_depth) { - arf->best_depth = arf->cur_best_depth; - arf->best_dist = 0; - arf->best_ptr = p; - arf->best_tile = 0; - better = true; - } - } - arf->recursive_mode = 0; - arf->cur_best_dist = UINT_MAX; - arf->cur_best_depth = 0xff; - - return better; -} - - -static bool AiEnumFollowRoad(TileIndex tile, AiRoadEnum *a, Trackdir track, uint length) -{ - uint dist = DistanceManhattan(tile, a->dest); - - if (dist <= a->best_dist) { - TileIndex tile2 = TILE_MASK(tile + TileOffsByDiagDir(TrackdirToExitdir(track))); - - if (IsNormalRoadTile(tile2)) { - a->best_dist = dist; - a->best_tile = tile; - a->best_track = track; - } - } - - return false; -} - -static bool AiCheckRoadFinished(Company *c) -{ - AiRoadEnum are; - TileIndex tile; - DiagDirection dir = _companies_ai[c->index].cur_dir_a; - - are.dest = _companies_ai[c->index].cur_tile_b; - tile = TILE_MASK(_companies_ai[c->index].cur_tile_a + TileOffsByDiagDir(dir)); - - if (IsRoadStopTile(tile) || IsRoadDepotTile(tile)) return false; - TrackdirBits bits = TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, ROADTYPES_ROAD)) & DiagdirReachesTrackdirs(dir); - if (bits == TRACKDIR_BIT_NONE) return false; - - are.best_dist = UINT_MAX; - - while (bits != TRACKDIR_BIT_NONE) { - Trackdir trackdir = RemoveFirstTrackdir(&bits); - FollowTrack(tile, PATHFIND_FLAGS_DISABLE_TILE_HASH, TRANSPORT_ROAD, ROADTYPES_ROAD, TrackdirToExitdir(trackdir), (TPFEnumProc*)AiEnumFollowRoad, NULL, &are); - } - - if (DistanceManhattan(tile, are.dest) <= are.best_dist) return false; - - if (are.best_dist == 0) return true; - - _companies_ai[c->index].cur_tile_a = are.best_tile; - _companies_ai[c->index].cur_dir_a = TrackdirToExitdir(are.best_track); - return false; -} - - -static bool AiBuildRoadHelper(TileIndex tile, uint32 flags, int type) -{ - static const RoadBits _road_bits[] = { - ROAD_X, - ROAD_Y, - ROAD_NW | ROAD_NE, - ROAD_SW | ROAD_SE, - ROAD_NW | ROAD_SW, - ROAD_SE | ROAD_NE - }; - return CmdSucceeded(DoCommand(tile, _road_bits[type], 0, flags, CMD_BUILD_ROAD)); -} - -static inline void AiCheckBuildRoadBridgeHere(AiRoadFinder *arf, TileIndex tile, const byte *p) -{ - Slope tileh; - uint z; - bool flag; - - DiagDirection dir2 = (DiagDirection)(p[0] & 3); - - tileh = GetTileSlope(tile, &z); - if (tileh == InclinedSlope(ReverseDiagDir(dir2)) || (tileh == SLOPE_FLAT && z != 0)) { - TileIndex tile_new = tile; - - // Allow bridges directly over bottom tiles - flag = z == 0; - for (;;) { - TileType type; - - if ((TileIndexDiff)tile_new < -TileOffsByDiagDir(dir2)) return; // Wraping around map, no bridge possible! - tile_new = TILE_MASK(tile_new + TileOffsByDiagDir(dir2)); - type = GetTileType(tile_new); - - if (type == MP_CLEAR || type == MP_TREES || GetTileSlope(tile_new, NULL) != SLOPE_FLAT) { - // Allow a bridge if either we have a tile that's water, rail or street, - // or if we found an up tile. - if (!flag) return; - break; - } - if (type != MP_WATER && type != MP_RAILWAY && type != MP_ROAD) return; - flag = true; - } - - // Is building a (rail)bridge possible at this place (type doesn't matter)? - if (CmdFailed(DoCommand(tile_new, tile, ROADTYPES_ROAD << 8 | TRANSPORT_ROAD << 15, DC_AUTO, CMD_BUILD_BRIDGE))) - return; - AiBuildRoadRecursive(arf, tile_new, dir2); - - // At the bottom depth, check if the new path is better than the old one. - if (arf->depth == 1) { - if (AiCheckRoadPathBetter(arf, p)) arf->bridge_end_tile = tile_new; - } - } -} - -static inline void AiCheckBuildRoadTunnelHere(AiRoadFinder *arf, TileIndex tile, const byte *p) -{ - uint z; - - if (GetTileSlope(tile, &z) == InclinedSlope((DiagDirection)(p[0] & 3)) && z != 0) { - CommandCost cost = DoCommand(tile, 0x200, 0, DC_AUTO, CMD_BUILD_TUNNEL); - - if (CmdSucceeded(cost) && cost.GetCost() <= (arf->company->money >> 4)) { - AiBuildRoadRecursive(arf, _build_tunnel_endtile, (DiagDirection)(p[0] & 3)); - if (arf->depth == 1) AiCheckRoadPathBetter(arf, p); - } - } -} - - - -static void AiBuildRoadRecursive(AiRoadFinder *arf, TileIndex tile, DiagDirection dir) -{ - const byte *p; - - tile = TILE_MASK(tile + TileOffsByDiagDir(dir)); - - // Reached destination? - if (tile == arf->final_tile) { - if (ReverseDiagDir(arf->final_dir) == dir) { - arf->recursive_mode = 2; - arf->cur_best_depth = arf->depth; - } - return; - } - - // Depth too deep? - if (arf->depth >= 4) { - uint dist = DistanceMaxPlusManhattan(tile, arf->final_tile); - if (dist < arf->cur_best_dist) { - // Store the tile that is closest to the final position. - arf->cur_best_dist = dist; - arf->cur_best_tile = tile; - arf->cur_best_dir = dir; - arf->cur_best_depth = arf->depth; - } - return; - } - - // Increase recursion depth - arf->depth++; - - // Grab pointer to list of stuff that is possible to build - p = _ai_table_15[dir]; - - // Try to build a single rail in all directions. - if (GetTileZ(tile) == 0) { - p += 6; - } else { - do { - // Make sure that a road can be built here. - if (AiBuildRoadHelper(tile, DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, p[0])) { - AiBuildRoadRecursive(arf, tile, (DiagDirection)p[1]); - } - - // At the bottom depth? - if (arf->depth == 1) AiCheckRoadPathBetter(arf, p); - - p += 2; - } while (!(p[0] & 0x80)); - } - - AiCheckBuildRoadBridgeHere(arf, tile, p); - AiCheckBuildRoadTunnelHere(arf, tile, p + 1); - - arf->depth--; -} - - -static void AiBuildRoadConstruct(Company *c) -{ - AiRoadFinder arf; - int i; - TileIndex tile; - - // Reached destination? - if (AiCheckRoadFinished(c)) { - _companies_ai[c->index].state_mode = 255; - return; - } - - // Setup recursive finder and call it. - arf.company = c; - arf.final_tile = _companies_ai[c->index].cur_tile_b; - arf.final_dir = _companies_ai[c->index].cur_dir_b; - arf.depth = 0; - arf.recursive_mode = 0; - arf.best_ptr = NULL; - arf.cur_best_dist = UINT_MAX; - arf.cur_best_depth = 0xff; - arf.best_dist = UINT_MAX; - arf.best_depth = 0xff; - arf.cur_best_tile = 0; - arf.best_tile = 0; - AiBuildRoadRecursive(&arf, _companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_dir_a); - - // Reached destination? - if (arf.recursive_mode == 2 && arf.cur_best_depth == 0) { - _companies_ai[c->index].state_mode = 255; - return; - } - - // Didn't find anything to build? - if (arf.best_ptr == NULL) { - // Terraform some -do_some_terraform: - for (i = 0; i != 5; i++) - AiDoTerraformLand(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_dir_a, 3, 0); - - if (++_companies_ai[c->index].state_counter == 21) { - _companies_ai[c->index].state_mode = 1; - - _companies_ai[c->index].cur_tile_a = TILE_MASK(_companies_ai[c->index].cur_tile_a + TileOffsByDiagDir(_companies_ai[c->index].cur_dir_a)); - _companies_ai[c->index].cur_dir_a = ReverseDiagDir(_companies_ai[c->index].cur_dir_a); - _companies_ai[c->index].state_counter = 0; - } - return; - } - - tile = TILE_MASK(_companies_ai[c->index].cur_tile_a + TileOffsByDiagDir(_companies_ai[c->index].cur_dir_a)); - - if (arf.best_ptr[0] & 0x80) { - TileIndex t1 = tile; - TileIndex t2 = arf.bridge_end_tile; - - int32 bridge_len = GetTunnelBridgeLength(t1, t2); - - Axis axis = (TileX(t1) == TileX(t2) ? AXIS_Y : AXIS_X); - - /* try to build a long road instead of bridge - CMD_BUILD_LONG_ROAD has to fail if it couldn't build at least one piece! */ - CommandCost cost = DoCommand(t2, t1, (t2 < t1 ? 1 : 2) | (axis << 2) | (ROADTYPE_ROAD << 3), DC_AUTO | DC_NO_WATER, CMD_BUILD_LONG_ROAD); - - if (CmdSucceeded(cost) && cost.GetCost() <= c->money) { - DoCommand(t2, t1, (t2 < t1 ? 1 : 2) | (axis << 2) | (ROADTYPE_ROAD << 3), DC_AUTO | DC_EXEC | DC_NO_WATER, CMD_BUILD_LONG_ROAD); - } else { - int i; - - /* Figure out what (road)bridge type to build - * start with best bridge, then go down to worse and worse bridges - * unnecessary to check for worse bridge (i=0), since AI will always build that */ - for (i = MAX_BRIDGES - 1; i != 0; i--) { - if (CheckBridge_Stuff(i, bridge_len)) { - CommandCost cost = DoCommand(t1, t2, i | ROADTYPES_ROAD << 8 | TRANSPORT_ROAD << 15, DC_AUTO, CMD_BUILD_BRIDGE); - if (CmdSucceeded(cost) && cost.GetCost() < (c->money >> 1) && cost.GetCost() < ((c->money + _economy.max_loan - c->current_loan) >> 5)) break; - } - } - - /* Build it */ - DoCommand(t1, t2, i | ROADTYPES_ROAD << 8 | TRANSPORT_ROAD << 15, DC_AUTO | DC_EXEC, CMD_BUILD_BRIDGE); - } - - _companies_ai[c->index].cur_tile_a = t2; - _companies_ai[c->index].state_counter = 0; - } else if (arf.best_ptr[0] & 0x40) { - // tunnel - DoCommand(tile, 0x200, 0, DC_AUTO | DC_EXEC, CMD_BUILD_TUNNEL); - _companies_ai[c->index].cur_tile_a = _build_tunnel_endtile; - _companies_ai[c->index].state_counter = 0; - } else { - // road - if (!AiBuildRoadHelper(tile, DC_EXEC | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, arf.best_ptr[0])) - goto do_some_terraform; - - _companies_ai[c->index].cur_dir_a = (DiagDirection)(arf.best_ptr[1] & 3); - _companies_ai[c->index].cur_tile_a = tile; - _companies_ai[c->index].state_counter = 0; - } - - if (arf.best_tile != 0) { - for (i = 0; i != 2; i++) - AiDoTerraformLand(arf.best_tile, arf.best_dir, 3, 0); - } -} - - -static void AiBuildRoad(Company *c) -{ - if (_companies_ai[c->index].state_mode < 1) { - // Construct mode, build new road. - AiBuildRoadConstruct(c); - } else if (_companies_ai[c->index].state_mode == 1) { - // Destruct mode, not implemented for roads. - _companies_ai[c->index].state_mode = 2; - _companies_ai[c->index].state_counter = 0; - } else if (_companies_ai[c->index].state_mode == 2) { - uint i; - - // Terraform some and then try building again. - for (i = 0; i != 4; i++) { - AiDoTerraformLand(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_dir_a, 3, 0); - } - - if (++_companies_ai[c->index].state_counter == 4) { - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].state_mode = 0; - } - } -} - -static TileIndex AiGetRoadBlockEdge(byte rule, TileIndex tile, DiagDirection *dir) -{ - const AiDefaultBlockData *p = _road_default_block_data[rule]->data; - while (p->mode != 1) p++; - *dir = p->attr; - return TILE_ADD(tile, ToTileIndexDiff(p->tileoffs)); -} - - -static void AiStateBuildRoad(Company *c) -{ - int num; - AiBuildRec *aib; - byte cmd; - TileIndex tile; - DiagDirection dir; - - // time out? - if (++_companies_ai[c->index].timeout_counter == 1388) { - _companies_ai[c->index].state = AIS_DELETE_ROAD_BLOCKS; - return; - } - - // Currently building a road between two points? - if (_companies_ai[c->index].state_mode != 255) { - AiBuildRoad(c); - - // Alternate between edges - Swap(_companies_ai[c->index].start_tile_a, _companies_ai[c->index].start_tile_b); - Swap(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_tile_b); - Swap(_companies_ai[c->index].start_dir_a, _companies_ai[c->index].start_dir_b); - Swap(_companies_ai[c->index].cur_dir_a, _companies_ai[c->index].cur_dir_b); - - return; - } - - // Now, find two new points to build between - num = _companies_ai[c->index].num_build_rec; - aib = &_companies_ai[c->index].src; - - for (;;) { - cmd = aib->buildcmd_a; - aib->buildcmd_a = 255; - if (cmd != 255) break; - - aib++; - if (--num == 0) { - _companies_ai[c->index].state = AIS_BUILD_ROAD_VEHICLES; - return; - } - } - - // Find first edge to build from. - tile = AiGetRoadBlockEdge(aib->cur_building_rule, aib->use_tile, &dir); - _companies_ai[c->index].start_tile_a = tile; - _companies_ai[c->index].cur_tile_a = tile; - _companies_ai[c->index].start_dir_a = dir; - _companies_ai[c->index].cur_dir_a = dir; - - // Find second edge to build to - aib = (&_companies_ai[c->index].src) + (cmd & 0xF); - tile = AiGetRoadBlockEdge(aib->cur_building_rule, aib->use_tile, &dir); - _companies_ai[c->index].start_tile_b = tile; - _companies_ai[c->index].cur_tile_b = tile; - _companies_ai[c->index].start_dir_b = dir; - _companies_ai[c->index].cur_dir_b = dir; - - // And setup state. - _companies_ai[c->index].state_mode = 2; - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].banned_tile_count = 0; -} - -static StationID AiGetStationIdFromRoadBlock(TileIndex tile, int id) -{ - const AiDefaultBlockData *p = _road_default_block_data[id]->data; - while (p->mode != 1) p++; - return GetStationIndex(TILE_ADD(tile, ToTileIndexDiff(p->tileoffs))); -} - -static void AiStateBuildRoadVehicles(Company *c) -{ - const AiDefaultBlockData *ptr; - TileIndex tile; - VehicleID loco_id; - EngineID veh; - uint i; - - ptr = _road_default_block_data[_companies_ai[c->index].src.cur_building_rule]->data; - for (; ptr->mode != 0; ptr++) {} - tile = TILE_ADD(_companies_ai[c->index].src.use_tile, ToTileIndexDiff(ptr->tileoffs)); - - veh = AiChooseRoadVehToBuild(_companies_ai[c->index].cargo_type, c->money, tile); - if (veh == INVALID_ENGINE) { - _companies_ai[c->index].state = AIS_0; - return; - } - - if (CmdFailed(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_ROAD_VEH))) return; - - loco_id = _new_vehicle_id; - - if (GetVehicle(loco_id)->cargo_type != _companies_ai[c->index].cargo_type) { - /* Cargo type doesn't match, so refit it */ - if (CmdFailed(DoCommand(tile, loco_id, _companies_ai[c->index].cargo_type, DC_EXEC, CMD_REFIT_ROAD_VEH))) { - /* Refit failed... sell the vehicle */ - DoCommand(tile, loco_id, 0, DC_EXEC, CMD_SELL_ROAD_VEH); - return; - } - } - - for (i = 0; _companies_ai[c->index].order_list_blocks[i] != 0xFF; i++) { - const AiBuildRec *aib = &_companies_ai[c->index].src + _companies_ai[c->index].order_list_blocks[i]; - bool is_pass = ( - _companies_ai[c->index].cargo_type == CT_PASSENGERS || - _companies_ai[c->index].cargo_type == CT_MAIL || - (_settings_game.game_creation.landscape == LT_TEMPERATE && _companies_ai[c->index].cargo_type == CT_VALUABLES) - ); - Order order; - - order.MakeGoToStation(AiGetStationIdFromRoadBlock(aib->use_tile, aib->cur_building_rule)); - - if (!is_pass && i == 1) order.SetUnloadType(OUFB_UNLOAD); - if (_companies_ai[c->index].num_want_fullload != 0 && (is_pass || i == 0)) - order.SetLoadType(OLFB_FULL_LOAD); - - DoCommand(0, loco_id + (i << 16), order.Pack(), DC_EXEC, CMD_INSERT_ORDER); - } - - DoCommand(0, loco_id, 0, DC_EXEC, CMD_START_STOP_VEHICLE); - DoCommand(0, loco_id, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT); - - if (_companies_ai[c->index].num_want_fullload != 0) _companies_ai[c->index].num_want_fullload--; - if (--_companies_ai[c->index].num_loco_to_build == 0) _companies_ai[c->index].state = AIS_0; -} - -static void AiStateDeleteRoadBlocks(Company *c) -{ - const AiBuildRec *aib = &_companies_ai[c->index].src; - uint num = _companies_ai[c->index].num_build_rec; - - do { - const AiDefaultBlockData *b; - - if (aib->cur_building_rule == 255) continue; - for (b = _road_default_block_data[aib->cur_building_rule]->data; b->mode != 4; b++) { - if (b->mode > 1) continue; - DoCommand(TILE_ADD(aib->use_tile, ToTileIndexDiff(b->tileoffs)), 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); - } - } while (++aib, --num); - - _companies_ai[c->index].state = AIS_0; -} - - -static void AiStateAirportStuff(Company *c) -{ - const Station *st; - int i; - AiBuildRec *aib; - byte rule; - - // Here we look for an airport we could use instead of building a new - // one. If we find such an aiport for any waypoint, - // AiStateBuildDefaultAirportBlocks() will kindly skip that one when - // building the waypoints. - - i = 0; - do { - // We do this all twice - once for the source (town in the case - // of oilrig route) and then for the destination (oilrig in the - // case of oilrig route). - aib = &_companies_ai[c->index].src + i; - - FOR_ALL_STATIONS(st) { - // Is this an airport? - if (!(st->facilities & FACIL_AIRPORT)) continue; - - // Do we own the airport? (Oilrigs aren't owned, though.) - if (st->owner != OWNER_NONE && st->owner != _current_company) continue; - - AirportFTAClass::Flags flags = st->Airport()->flags; - - /* if airport doesn't accept our kind of plane, dismiss it */ - if (!(flags & (_companies_ai[c->index].build_kind == 1 ? AirportFTAClass::HELICOPTERS : AirportFTAClass::AIRPLANES))) { - continue; - } - - // Dismiss airports too far away. - if (DistanceMax(st->airport_tile, aib->spec_tile) > aib->rand_rng) - continue; - - // It's ideal airport, let's take it! - - /* XXX: This part is utterly broken - rule should - * contain number of the rule appropriate for the - * airport type (country, town, ...), see - * _airport_default_block_data (rule is just an index - * in this array). But the only difference between the - * currently existing two rules (rule 0 - town and rule - * 1 - country) is the attr field which is used only - * when building new airports - and that's irrelevant - * for us. So using just about any rule will suffice - * here for now (some of the new airport types would be - * broken because they will probably need different - * tileoff values etc), no matter that - * IsHangarTile() makes no sense. --pasky */ - if (!(flags & AirportFTAClass::AIRPLANES)) { - /* Heliports should have maybe own rulesets but - * OTOH we don't want AI to pick them up when - * looking for a suitable airport type to build. - * So any of rules 0 or 1 would do for now. The - * original rule number was 2 but that's a bug - * because we have no such rule. */ - rule = 1; - } else { - rule = IsHangarTile(st->airport_tile); - } - - aib->cur_building_rule = rule; - aib->use_tile = st->airport_tile; - break; - } - } while (++i != _companies_ai[c->index].num_build_rec); - - _companies_ai[c->index].state = AIS_BUILD_DEFAULT_AIRPORT_BLOCKS; - _companies_ai[c->index].state_mode = 255; - _companies_ai[c->index].state_counter = 0; -} - -static CommandCost AiDoBuildDefaultAirportBlock(TileIndex tile, const AiDefaultBlockData *p, uint32 flag) -{ - uint32 avail_airports = GetValidAirports(); - CommandCost ret, total_cost(EXPENSES_CONSTRUCTION); - - for (; p->mode == 0; p++) { - if (!HasBit(avail_airports, p->attr)) return CMD_ERROR; - ret = DoCommand(TILE_MASK(tile + ToTileIndexDiff(p->tileoffs)), p->attr, INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_AIRPORT); - if (CmdFailed(ret)) return CMD_ERROR; - total_cost.AddCost(ret); - } - - return total_cost; -} - -static bool AiCheckAirportResources(TileIndex tile, const AiDefaultBlockData *p, byte cargo) -{ - uint values[NUM_CARGO]; - - for (; p->mode == 0; p++) { - TileIndex tile2 = TILE_ADD(tile, ToTileIndexDiff(p->tileoffs)); - const AirportFTAClass *airport = GetAirport(p->attr); - uint w = airport->size_x; - uint h = airport->size_y; - uint rad = _settings_game.station.modified_catchment ? airport->catchment : (uint)CA_UNMODIFIED; - - if (cargo & 0x80) { - GetProductionAroundTiles(values, tile2, w, h, rad); - return values[cargo & 0x7F] != 0; - } else { - GetAcceptanceAroundTiles(values, tile2, w, h, rad); - return values[cargo] >= 8; - } - } - return true; -} - -static int AiFindBestDefaultAirportBlock(TileIndex tile, byte cargo, byte heli, CommandCost *cost) -{ - const AiDefaultBlockData *p; - - bool no_small = false; - - if (!heli) { - /* do not build small airport if we have large available and we are not building heli route */ - uint valid = GetValidAirports(); - for (uint i = 0; (p = _airport_default_block_data[i]) != NULL; i++) { - uint flags = GetAirport(p->attr)->flags; - if (HasBit(valid, p->attr) && (flags & AirportFTAClass::AIRPLANES) && !(flags & AirportFTAClass::SHORT_STRIP)) { - no_small = true; - break; - } - } - } - - for (uint i = 0; (p = _airport_default_block_data[i]) != NULL; i++) { - uint flags = GetAirport(p->attr)->flags; - /* If we are doing a helicopter service, avoid building airports where they can't land */ - if (heli && !(flags & AirportFTAClass::HELICOPTERS)) continue; - /* Similiar with aircraft ... */ - if (!heli && !(flags & AirportFTAClass::AIRPLANES)) continue; - /* Do not build small airport if we prefer large */ - if (no_small && (flags & AirportFTAClass::SHORT_STRIP)) continue; - - *cost = AiDoBuildDefaultAirportBlock(tile, p, 0); - if (CmdSucceeded(*cost) && AiCheckAirportResources(tile, p, cargo)) - return i; - } - return -1; -} - -static void AiStateBuildDefaultAirportBlocks(Company *c) -{ - int i, j; - AiBuildRec *aib; - int rule; - CommandCost cost; - - // time out? - if (++_companies_ai[c->index].timeout_counter == 1388) { - _companies_ai[c->index].state = AIS_0; - return; - } - - // do the following 8 times - i = 8; - do { - // check if we can build the default - aib = &_companies_ai[c->index].src; - j = _companies_ai[c->index].num_build_rec; - do { - // this item has already been built? - if (aib->cur_building_rule != 255) continue; - - // adjust the coordinate randomly, - // to make sure that we find a position. - aib->use_tile = AdjustTileCoordRandomly(aib->spec_tile, aib->rand_rng); - - // check if the aircraft stuff can be built there. - rule = AiFindBestDefaultAirportBlock(aib->use_tile, aib->cargo, _companies_ai[c->index].build_kind, &cost); - -// SetRedErrorSquare(aib->use_tile); - - if (rule == -1) { - // cannot build, terraform after a while - if (_companies_ai[c->index].state_counter >= 600) { - AiDoTerraformLand(aib->use_tile, (DiagDirection)(Random() & 3), 3, (int8)_companies_ai[c->index].state_mode); - } - // also try the other terraform direction - if (++_companies_ai[c->index].state_counter >= 1000) { - _companies_ai[c->index].state_counter = 0; - _companies_ai[c->index].state_mode = -_companies_ai[c->index].state_mode; - } - } else if (CheckCompanyHasMoney(cost) && AiCheckBlockDistances(c, aib->use_tile)) { - // company has money, build it. - CommandCost r; - - aib->cur_building_rule = rule; - - r = AiDoBuildDefaultAirportBlock( - aib->use_tile, - _airport_default_block_data[rule], - DC_EXEC | DC_NO_TOWN_RATING - ); - assert(CmdSucceeded(r)); - } - } while (++aib, --j); - } while (--i); - - // check if we're done with all of them - aib = &_companies_ai[c->index].src; - j = _companies_ai[c->index].num_build_rec; - do { - if (aib->cur_building_rule == 255) return; - } while (++aib, --j); - - // yep, all are done. switch state. - _companies_ai[c->index].state = AIS_BUILD_AIRCRAFT_VEHICLES; -} - -static StationID AiGetStationIdFromAircraftBlock(TileIndex tile, int id) -{ - const AiDefaultBlockData *p = _airport_default_block_data[id]; - while (p->mode != 1) p++; - return GetStationIndex(TILE_ADD(tile, ToTileIndexDiff(p->tileoffs))); -} - -static void AiStateBuildAircraftVehicles(Company *c) -{ - const AiDefaultBlockData *ptr; - TileIndex tile; - EngineID veh; - int i; - VehicleID loco_id; - - ptr = _airport_default_block_data[_companies_ai[c->index].src.cur_building_rule]; - for (; ptr->mode != 0; ptr++) {} - - tile = TILE_ADD(_companies_ai[c->index].src.use_tile, ToTileIndexDiff(ptr->tileoffs)); - - /* determine forbidden aircraft bits */ - byte forbidden = 0; - for (i = 0; _companies_ai[c->index].order_list_blocks[i] != 0xFF; i++) { - const AiBuildRec *aib = (&_companies_ai[c->index].src) + _companies_ai[c->index].order_list_blocks[i]; - const Station *st = GetStationByTile(aib->use_tile); - - if (st == NULL || !(st->facilities & FACIL_AIRPORT)) continue; - - AirportFTAClass::Flags flags = st->Airport()->flags; - if (!(flags & AirportFTAClass::AIRPLANES)) forbidden |= AIR_CTOL | AIR_FAST; // no planes for heliports / oil rigs - if (flags & AirportFTAClass::SHORT_STRIP) forbidden |= AIR_FAST; // no fast planes for small airports - } - - veh = AiChooseAircraftToBuild(c->money, forbidden); - if (veh == INVALID_ENGINE) return; - if (GetStationByTile(tile)->Airport()->nof_depots == 0) return; - - /* XXX - Have the AI pick the hangar terminal in an airport. Eg get airport-type - * and offset to the FIRST depot because the AI picks the st->xy tile */ - tile += ToTileIndexDiff(GetStationByTile(tile)->Airport()->airport_depots[0]); - if (CmdFailed(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_AIRCRAFT))) return; - loco_id = _new_vehicle_id; - - for (i = 0; _companies_ai[c->index].order_list_blocks[i] != 0xFF; i++) { - AiBuildRec *aib = (&_companies_ai[c->index].src) + _companies_ai[c->index].order_list_blocks[i]; - bool is_pass = (_companies_ai[c->index].cargo_type == CT_PASSENGERS || _companies_ai[c->index].cargo_type == CT_MAIL); - Order order; - - order.MakeGoToStation(AiGetStationIdFromAircraftBlock(aib->use_tile, aib->cur_building_rule)); - - if (!is_pass && i == 1) order.SetUnloadType(OUFB_UNLOAD); - if (_companies_ai[c->index].num_want_fullload != 0 && (is_pass || i == 0)) - order.SetLoadType(OLFB_FULL_LOAD); - - DoCommand(0, loco_id + (i << 16), order.Pack(), DC_EXEC, CMD_INSERT_ORDER); - } - - DoCommand(0, loco_id, 0, DC_EXEC, CMD_START_STOP_VEHICLE); - - DoCommand(0, loco_id, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT); - - if (_companies_ai[c->index].num_want_fullload != 0) _companies_ai[c->index].num_want_fullload--; - - if (--_companies_ai[c->index].num_loco_to_build == 0) _companies_ai[c->index].state = AIS_0; -} - -static void AiStateCheckShipStuff(Company *c) -{ - /* Ships are not implemented in this (broken) AI */ -} - -static void AiStateBuildDefaultShipBlocks(Company *c) -{ - /* Ships are not implemented in this (broken) AI */ -} - -static void AiStateDoShipStuff(Company *c) -{ - /* Ships are not implemented in this (broken) AI */ -} - -static void AiStateSellVeh(Company *c) -{ - Vehicle *v = _companies_ai[c->index].cur_veh; - - if (v->owner == _current_company) { - if (v->type == VEH_TRAIN) { - - if (!IsRailDepotTile(v->tile) || v->u.rail.track != TRACK_BIT_DEPOT || !(v->vehstatus & VS_STOPPED)) { - if (!v->current_order.IsType(OT_GOTO_DEPOT)) - DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_TRAIN_TO_DEPOT); - goto going_to_depot; - } - - // Sell whole train - DoCommand(v->tile, v->index, 1, DC_EXEC, CMD_SELL_RAIL_WAGON); - - } else if (v->type == VEH_ROAD) { - if (!v->IsStoppedInDepot()) { - if (!v->current_order.IsType(OT_GOTO_DEPOT)) - DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_ROADVEH_TO_DEPOT); - goto going_to_depot; - } - - DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH); - } else if (v->type == VEH_AIRCRAFT) { - if (!v->IsStoppedInDepot()) { - if (!v->current_order.IsType(OT_GOTO_DEPOT)) - DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR); - goto going_to_depot; - } - - DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_AIRCRAFT); - } else if (v->type == VEH_SHIP) { - /* Ships are not implemented in this (broken) AI */ - } - } - - goto return_to_loop; -going_to_depot:; - if (++_companies_ai[c->index].state_counter <= 832) return; - - if (v->current_order.IsType(OT_GOTO_DEPOT)) { - v->current_order.MakeDummy(); - InvalidateWindow(WC_VEHICLE_VIEW, v->index); - } -return_to_loop:; - _companies_ai[c->index].state = AIS_VEH_LOOP; -} - -static void AiStateRemoveStation(Company *c) -{ - // Remove stations that aren't in use by any vehicle - const Order *ord; - const Station *st; - TileIndex tile; - - // Go to this state when we're done. - _companies_ai[c->index].state = AIS_1; - - // Get a list of all stations that are in use by a vehicle - byte *in_use = MallocT<byte>(GetMaxStationIndex() + 1); - memset(in_use, 0, GetMaxStationIndex() + 1); - FOR_ALL_ORDERS(ord) { - if (ord->IsType(OT_GOTO_STATION)) in_use[ord->GetDestination()] = 1; - } - - // Go through all stations and delete those that aren't in use - FOR_ALL_STATIONS(st) { - if (st->owner == _current_company && !in_use[st->index] && - ( (st->bus_stops != NULL && (tile = st->bus_stops->xy) != INVALID_TILE) || - (st->truck_stops != NULL && (tile = st->truck_stops->xy) != INVALID_TILE) || - (tile = st->train_tile) != INVALID_TILE || - (tile = st->dock_tile) != INVALID_TILE || - (tile = st->airport_tile) != INVALID_TILE)) { - DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); - } - } - - free(in_use); -} - -static void AiRemoveCompanyRailOrRoad(Company *c, TileIndex tile) -{ - TrackBits rails; - - if (IsTileType(tile, MP_RAILWAY)) { - if (!IsTileOwner(tile, _current_company)) return; - - if (IsPlainRailTile(tile)) { -is_rail_crossing:; - rails = GetRailTrackStatus(tile); - - if (rails == TRACK_BIT_HORZ || rails == TRACK_BIT_VERT) return; - - if (rails & TRACK_BIT_3WAY_NE) { -pos_0: - if ((GetRailTrackStatus(TILE_MASK(tile - TileDiffXY(1, 0))) & TRACK_BIT_3WAY_SW) == 0) { - _companies_ai[c->index].cur_dir_a = DIAGDIR_NE; - _companies_ai[c->index].cur_tile_a = tile; - _companies_ai[c->index].state = AIS_REMOVE_SINGLE_RAIL_TILE; - return; - } - } - - if (rails & TRACK_BIT_3WAY_SE) { -pos_1: - if ((GetRailTrackStatus(TILE_MASK(tile + TileDiffXY(0, 1))) & TRACK_BIT_3WAY_NW) == 0) { - _companies_ai[c->index].cur_dir_a = DIAGDIR_SE; - _companies_ai[c->index].cur_tile_a = tile; - _companies_ai[c->index].state = AIS_REMOVE_SINGLE_RAIL_TILE; - return; - } - } - - if (rails & TRACK_BIT_3WAY_SW) { -pos_2: - if ((GetRailTrackStatus(TILE_MASK(tile + TileDiffXY(1, 0))) & TRACK_BIT_3WAY_NE) == 0) { - _companies_ai[c->index].cur_dir_a = DIAGDIR_SW; - _companies_ai[c->index].cur_tile_a = tile; - _companies_ai[c->index].state = AIS_REMOVE_SINGLE_RAIL_TILE; - return; - } - } - - if (rails & TRACK_BIT_3WAY_NW) { -pos_3: - if ((GetRailTrackStatus(TILE_MASK(tile - TileDiffXY(0, 1))) & TRACK_BIT_3WAY_SE) == 0) { - _companies_ai[c->index].cur_dir_a = DIAGDIR_NW; - _companies_ai[c->index].cur_tile_a = tile; - _companies_ai[c->index].state = AIS_REMOVE_SINGLE_RAIL_TILE; - return; - } - } - } else { - static const byte _depot_bits[] = {0x19, 0x16, 0x25, 0x2A}; - - DiagDirection dir = GetRailDepotDirection(tile); - - if (GetRailTrackStatus(tile + TileOffsByDiagDir(dir)) & _depot_bits[dir]) - return; - - DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); - } - } else if (IsTileType(tile, MP_ROAD)) { - if (IsLevelCrossing(tile)) goto is_rail_crossing; - - if (IsRoadDepot(tile)) { - if (!IsTileOwner(tile, _current_company)) return; - - DiagDirection dir; - TileIndex t; - - // Check if there are any stations around. - t = tile + TileDiffXY(-1, 0); - if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_company)) return; - - t = tile + TileDiffXY(1, 0); - if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_company)) return; - - t = tile + TileDiffXY(0, -1); - if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_company)) return; - - t = tile + TileDiffXY(0, 1); - if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_company)) return; - - dir = GetRoadDepotDirection(tile); - - DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); - DoCommand( - TILE_MASK(tile + TileOffsByDiagDir(dir)), - DiagDirToRoadBits(ReverseDiagDir(dir)), - 0, - DC_EXEC, - CMD_REMOVE_ROAD); - } - } else if (IsTileType(tile, MP_TUNNELBRIDGE)) { - if (!IsTileOwner(tile, _current_company) || - !IsBridge(tile) || - GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) { - return; - } - - rails = TRACK_BIT_NONE; - - switch (GetTunnelBridgeDirection(tile)) { - default: - case DIAGDIR_NE: goto pos_2; - case DIAGDIR_SE: goto pos_3; - case DIAGDIR_SW: goto pos_0; - case DIAGDIR_NW: goto pos_1; - } - } -} - -static void AiStateRemoveTrack(Company *c) -{ - /* Was 1000 for standard 8x8 maps. */ - int num = MapSizeX() * 4; - - do { - TileIndex tile = ++_companies_ai[c->index].state_counter; - - // Iterated all tiles? - if (tile >= MapSize()) { - _companies_ai[c->index].state = AIS_REMOVE_STATION; - return; - } - - // Remove company stuff in that tile - AiRemoveCompanyRailOrRoad(c, tile); - if (_companies_ai[c->index].state != AIS_REMOVE_TRACK) return; - } while (--num); -} - -static void AiStateRemoveSingleRailTile(Company *c) -{ - // Remove until we can't remove more. - if (!AiRemoveTileAndGoForward(c)) _companies_ai[c->index].state = AIS_REMOVE_TRACK; -} - -static AiStateAction * const _ai_actions[] = { - AiCase0, - AiCase1, - AiStateVehLoop, - AiStateCheckReplaceVehicle, - AiStateDoReplaceVehicle, - AiStateWantNewRoute, - - AiStateBuildDefaultRailBlocks, - AiStateBuildRail, - AiStateBuildRailVeh, - AiStateDeleteRailBlocks, - - AiStateBuildDefaultRoadBlocks, - AiStateBuildRoad, - AiStateBuildRoadVehicles, - AiStateDeleteRoadBlocks, - - AiStateAirportStuff, - AiStateBuildDefaultAirportBlocks, - AiStateBuildAircraftVehicles, - - AiStateCheckShipStuff, - AiStateBuildDefaultShipBlocks, - AiStateDoShipStuff, - - AiStateSellVeh, - AiStateRemoveStation, - AiStateRemoveTrack, - - AiStateRemoveSingleRailTile -}; - -extern void ShowBuyCompanyDialog(CompanyID company); - -static void AiHandleTakeover(Company *c) -{ - if (c->bankrupt_timeout != 0) { - c->bankrupt_timeout -= 8; - if (c->bankrupt_timeout > 0) return; - c->bankrupt_timeout = 0; - DeleteWindowById(WC_BUY_COMPANY, _current_company); - if (IsLocalCompany()) { - AskExitToGameMenu(); - return; - } - if (IsHumanCompany(_current_company)) return; - } - - if (c->bankrupt_asked == MAX_UVALUE(CompanyMask)) return; - - { - CompanyMask asked = c->bankrupt_asked; - Company *company, *best_company = NULL; - int32 best_val = -1; - - // Ask the guy with the highest performance hist. - FOR_ALL_COMPANIES(company) { - if (!(asked & 1) && - company->bankrupt_asked == 0 && - best_val < company->old_economy[1].performance_history) { - best_val = company->old_economy[1].performance_history; - best_company = company; - } - asked >>= 1; - } - - // Asked all companies? - if (best_val == -1) { - c->bankrupt_asked = MAX_UVALUE(CompanyMask); - return; - } - - SetBit(c->bankrupt_asked, best_company->index); - - if (best_company->index == _local_company) { - c->bankrupt_timeout = 4440; - ShowBuyCompanyDialog(_current_company); - return; - } - if (IsHumanCompany(best_company->index)) return; - - // Too little money for computer to buy it? - if (best_company->money >> 1 >= c->bankrupt_value) { - // Computer wants to buy it. - CompanyID old_company = _current_company; - _current_company = best_company->index; - DoCommand(0, old_company, 0, DC_EXEC, CMD_BUY_COMPANY); - _current_company = old_company; - } - } -} - -static void AiAdjustLoan(const Company *c) -{ - Money base = AiGetBasePrice(c); - - if (c->money > base * 1400) { - // Decrease loan - if (c->current_loan != 0) { - DoCommand(0, 0, 0, DC_EXEC, CMD_DECREASE_LOAN); - } - } else if (c->money < base * 500) { - // Increase loan - if (c->current_loan < _economy.max_loan && - c->num_valid_stat_ent >= 2 && - -(c->old_economy[0].expenses + c->old_economy[1].expenses) < base * 60) { - DoCommand(0, 0, 0, DC_EXEC, CMD_INCREASE_LOAN); - } - } -} - -static void AiBuildCompanyHQ(Company *c) -{ - TileIndex tile; - - if (c->location_of_HQ == INVALID_TILE && - c->last_build_coordinate != 0) { - tile = AdjustTileCoordRandomly(c->last_build_coordinate, 8); - DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ); - } -} - - -void AiDoGameLoop(Company *c) -{ - if (c->bankrupt_asked != 0) { - AiHandleTakeover(c); - return; - } - - // Ugly hack to make sure the service interval of the AI is good, not looking - // to the patch-setting - // Also, it takes into account the setting if the service-interval is in days - // or in % - _ai_service_interval = _settings_game.vehicle.servint_ispercent ? 80 : 180; - - if (IsHumanCompany(_current_company)) return; - - AiAdjustLoan(c); - AiBuildCompanyHQ(c); - -#if 0 - { - static byte old_state = 99; - static bool hasdots = false; - char *_ai_state_names[] = { - "AiCase0", - "AiCase1", - "AiStateVehLoop", - "AiStateCheckReplaceVehicle", - "AiStateDoReplaceVehicle", - "AiStateWantNewRoute", - "AiStateBuildDefaultRailBlocks", - "AiStateBuildRail", - "AiStateBuildRailVeh", - "AiStateDeleteRailBlocks", - "AiStateBuildDefaultRoadBlocks", - "AiStateBuildRoad", - "AiStateBuildRoadVehicles", - "AiStateDeleteRoadBlocks", - "AiStateAirportStuff", - "AiStateBuildDefaultAirportBlocks", - "AiStateBuildAircraftVehicles", - "AiStateCheckShipStuff", - "AiStateBuildDefaultShipBlocks", - "AiStateDoShipStuff", - "AiStateSellVeh", - "AiStateRemoveStation", - "AiStateRemoveTrack", - "AiStateRemoveSingleRailTile" - }; - - if (_companies_ai[c->index].state != old_state) { - if (hasdots) - printf("\n"); - hasdots = false; - printf("AiState: %s\n", _ai_state_names[old_state=_companies_ai[c->index].state]); - } else { - printf("."); - hasdots = true; - } - } -#endif - - _ai_actions[_companies_ai[c->index].state](c); -} diff --git a/src/ai/default/default.h b/src/ai/default/default.h deleted file mode 100644 index 843cf0f17..000000000 --- a/src/ai/default/default.h +++ /dev/null @@ -1,69 +0,0 @@ -/* $Id$ */ - -/** @file default.h The original AI. */ - -#ifndef DEFAULT_H -#define DEFAULT_H - -#include "../../direction_type.h" -#include "../../vehicle_type.h" -#include "../../rail_type.h" - -void AiDoGameLoop(Company *c); - -struct AiBuildRec { - TileIndex spec_tile; - TileIndex use_tile; - byte rand_rng; - byte cur_building_rule; - byte unk6; - byte unk7; - byte buildcmd_a; - byte buildcmd_b; - byte direction; - CargoID cargo; -}; - -struct CompanyAI { - byte state; - byte tick; ///< Used to determine how often to move - uint32 state_counter; ///< Can hold tile index! - uint16 timeout_counter; - - byte state_mode; - byte banned_tile_count; - RailTypeByte railtype_to_use; - - CargoID cargo_type; - byte num_wagons; - byte build_kind; - byte num_build_rec; - byte num_loco_to_build; - byte num_want_fullload; - - byte route_type_mask; - - TileIndex start_tile_a; - TileIndex cur_tile_a; - DiagDirectionByte cur_dir_a; - DiagDirectionByte start_dir_a; - - TileIndex start_tile_b; - TileIndex cur_tile_b; - DiagDirectionByte cur_dir_b; - DiagDirectionByte start_dir_b; - - Vehicle *cur_veh; ///< only used by some states - - AiBuildRec src, dst, mid1, mid2; - - VehicleID wagon_list[9]; - byte order_list_blocks[20]; - - TileIndex banned_tiles[16]; - byte banned_val[16]; -}; - -extern CompanyAI _companies_ai[MAX_COMPANIES]; - -#endif diff --git a/src/ai/trolly/build.cpp b/src/ai/trolly/build.cpp deleted file mode 100644 index 0723386f7..000000000 --- a/src/ai/trolly/build.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* $Id$ */ - -/** @file build.cpp Building support for the trolly AI. */ - -#include "../../stdafx.h" -#include "../../openttd.h" -#include "../../debug.h" -#include "../../road_map.h" -#include "../../command_func.h" -#include "trolly.h" -#include "../../engine_func.h" -#include "../../engine_base.h" -#include "../../variables.h" -#include "../../bridge.h" -#include "../../vehicle_func.h" -#include "../../vehicle_base.h" -#include "../../company_base.h" -#include "../../company_func.h" -#include "../ai.h" -#include "../../tunnelbridge.h" - - -// Build HQ -// Params: -// tile : tile where HQ is going to be build -bool AiNew_Build_CompanyHQ(Company *c, TileIndex tile) -{ - if (CmdFailed(AI_DoCommand(tile, 0, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ))) - return false; - AI_DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ); - return true; -} - - -// Build station -// Params: -// type : AI_TRAIN/AI_BUS/AI_TRUCK : indicates the type of station -// tile : tile where station is going to be build -// length : in case of AI_TRAIN: length of station -// numtracks : in case of AI_TRAIN: tracks of station -// direction : the direction of the station -// flag : flag passed to DoCommand (normally 0 to get the cost or DC_EXEC to build it) -CommandCost AiNew_Build_Station(Company *c, byte type, TileIndex tile, byte length, byte numtracks, byte direction, byte flag) -{ - if (type == AI_TRAIN) - return AI_DoCommand(tile, (direction << 4) + (numtracks << 8) + (length << 16), INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_RAILROAD_STATION); - - if (type == AI_BUS) - return AI_DoCommand(tile, direction, ROADTYPES_ROAD << 2 | ROADSTOP_BUS | INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP); - - return AI_DoCommand(tile, direction, ROADTYPES_ROAD << 2 | ROADSTOP_TRUCK | INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP); -} - - -// Builds a brdige. The second best out of the ones available for this company -// Params: -// tile_a : starting point -// tile_b : end point -// flag : flag passed to DoCommand -CommandCost AiNew_Build_Bridge(Company *c, TileIndex tile_a, TileIndex tile_b, byte flag) -{ - int bridge_type, bridge_len, type, type2; - - // Find a good bridgetype (the best money can buy) - bridge_len = GetTunnelBridgeLength(tile_a, tile_b); - type = type2 = 0; - for (bridge_type = MAX_BRIDGES - 1; bridge_type >= 0; bridge_type--) { - if (CheckBridge_Stuff(bridge_type, bridge_len)) { - type2 = type; - type = bridge_type; - // We found two bridges, exit - if (type2 != 0) break; - } - } - // There is only one bridge that can be built - if (type2 == 0 && type != 0) type2 = type; - - // Now, simply, build the bridge! - if (_companies_ainew[c->index].tbt == AI_TRAIN) { - return AI_DoCommand(tile_a, tile_b, type2 | RAILTYPE_RAIL << 8 | TRANSPORT_RAIL << 15, flag | DC_AUTO, CMD_BUILD_BRIDGE); - } else { - return AI_DoCommand(tile_a, tile_b, type2 | ROADTYPES_ROAD << 8 | TRANSPORT_ROAD << 15, flag | DC_AUTO, CMD_BUILD_BRIDGE); - } -} - - -// Build the route part by part -// Basicly what this function do, is build that amount of parts of the route -// that go in the same direction. It sets 'part' to the last part of the route builded. -// The return value is the cost for the builded parts -// -// Params: -// PathFinderInfo : Pointer to the PathFinderInfo used for AiPathFinder -// part : Which part we need to build -// -// TODO: skip already builded road-pieces (e.g.: cityroad) -CommandCost AiNew_Build_RoutePart(Company *c, Ai_PathFinderInfo *PathFinderInfo, byte flag) -{ - int part = PathFinderInfo->position; - byte *route_extra = PathFinderInfo->route_extra; - TileIndex *route = PathFinderInfo->route; - int dir; - int old_dir = -1; - CommandCost cost; - CommandCost res; - // We need to calculate the direction with the parent of the parent.. so we skip - // the first pieces and the last piece - if (part < 1) part = 1; - // When we are done, stop it - if (part >= PathFinderInfo->route_length - 1) { - PathFinderInfo->position = -2; - return CommandCost(); - } - - - if (PathFinderInfo->rail_or_road) { - // Tunnel code - if ((AI_PATHFINDER_FLAG_TUNNEL & route_extra[part]) != 0) { - cost.AddCost(AI_DoCommand(route[part], 0, 0, flag, CMD_BUILD_TUNNEL)); - PathFinderInfo->position++; - // TODO: problems! - if (CmdFailed(cost)) { - DEBUG(ai, 0, "[BuildPath] tunnel could not be built (0x%X)", route[part]); - return CommandCost(); - } - return cost; - } - // Bridge code - if ((AI_PATHFINDER_FLAG_BRIDGE & route_extra[part]) != 0) { - cost.AddCost(AiNew_Build_Bridge(c, route[part], route[part - 1], flag)); - PathFinderInfo->position++; - // TODO: problems! - if (CmdFailed(cost)) { - DEBUG(ai, 0, "[BuildPath] bridge could not be built (0x%X, 0x%X)", route[part], route[part - 1]); - return CommandCost(); - } - return cost; - } - - // Build normal rail - // Keep it doing till we go an other way - if (route_extra[part - 1] == 0 && route_extra[part] == 0) { - while (route_extra[part] == 0) { - // Get the current direction - dir = AiNew_GetRailDirection(route[part - 1], route[part], route[part + 1]); - // Is it the same as the last one? - if (old_dir != -1 && old_dir != dir) break; - old_dir = dir; - // Build the tile - res = AI_DoCommand(route[part], 0, dir, flag, CMD_BUILD_SINGLE_RAIL); - if (CmdFailed(res)) { - // Problem.. let's just abort it all! - _companies_ainew[c->index].state = AI_STATE_NOTHING; - return CommandCost(); - } - cost.AddCost(res); - // Go to the next tile - part++; - // Check if it is still in range.. - if (part >= PathFinderInfo->route_length - 1) break; - } - part--; - } - // We want to return the last position, so we go back one - PathFinderInfo->position = part; - } else { - // Tunnel code - if ((AI_PATHFINDER_FLAG_TUNNEL & route_extra[part]) != 0) { - cost.AddCost(AI_DoCommand(route[part], 0x200 | ROADTYPES_ROAD, 0, flag, CMD_BUILD_TUNNEL)); - PathFinderInfo->position++; - // TODO: problems! - if (CmdFailed(cost)) { - DEBUG(ai, 0, "[BuildPath] tunnel could not be built (0x%X)", route[part]); - return CommandCost(); - } - return cost; - } - // Bridge code - if ((AI_PATHFINDER_FLAG_BRIDGE & route_extra[part]) != 0) { - cost.AddCost(AiNew_Build_Bridge(c, route[part], route[part + 1], flag)); - PathFinderInfo->position++; - // TODO: problems! - if (CmdFailed(cost)) { - DEBUG(ai, 0, "[BuildPath] bridge could not be built (0x%X, 0x%X)", route[part], route[part + 1]); - return CommandCost(); - } - return cost; - } - - // Build normal road - // Keep it doing till we go an other way - // EnsureNoVehicleOnGround makes sure we don't build on a tile where a vehicle is. This way - // it will wait till the vehicle is gone.. - if (route_extra[part - 1] == 0 && route_extra[part] == 0 && (flag != DC_EXEC || EnsureNoVehicleOnGround(route[part]))) { - while (route_extra[part] == 0 && (flag != DC_EXEC || EnsureNoVehicleOnGround(route[part]))) { - // Get the current direction - dir = AiNew_GetRoadDirection(route[part - 1], route[part], route[part + 1]); - // Is it the same as the last one? - if (old_dir != -1 && old_dir != dir) break; - old_dir = dir; - // There is already some road, and it is a bridge.. don't build!!! - if (!IsTileType(route[part], MP_TUNNELBRIDGE)) { - // Build the tile - res = AI_DoCommand(route[part], dir, 0, flag | DC_NO_WATER, CMD_BUILD_ROAD); - // Currently, we ignore CMD_ERRORs! - if (CmdFailed(res) && flag == DC_EXEC && !IsTileType(route[part], MP_ROAD) && !EnsureNoVehicleOnGround(route[part])) { - // Problem.. let's just abort it all! - DEBUG(ai, 0, "[BuidPath] route building failed at tile 0x%X, aborting", route[part]); - _companies_ainew[c->index].state = AI_STATE_NOTHING; - return CommandCost(); - } - - if (CmdSucceeded(res)) cost.AddCost(res); - } - // Go to the next tile - part++; - // Check if it is still in range.. - if (part >= PathFinderInfo->route_length - 1) break; - } - part--; - // We want to return the last position, so we go back one - } - if (!EnsureNoVehicleOnGround(route[part]) && flag == DC_EXEC) part--; - PathFinderInfo->position = part; - } - - return cost; -} - - -// This functions tries to find the best vehicle for this type of cargo -// It returns INVALID_ENGINE if not suitable engine is found -EngineID AiNew_PickVehicle(Company *c) -{ - if (_companies_ainew[c->index].tbt == AI_TRAIN) { - // Not supported yet - return INVALID_ENGINE; - } else { - EngineID best_veh_index = INVALID_ENGINE; - int32 best_veh_rating = 0; - const Engine *e; - - /* Loop through all road vehicles */ - FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { - EngineID i = e->index; - const RoadVehicleInfo *rvi = &e->u.road; - - /* Skip vehicles which can't take our cargo type */ - if (rvi->cargo_type != _companies_ainew[c->index].cargo && !CanRefitTo(i, _companies_ainew[c->index].cargo)) continue; - - /* Skip trams */ - if (HasBit(EngInfo(i)->misc_flags, EF_ROAD_TRAM)) continue; - - // Is it availiable? - // Also, check if the reliability of the vehicle is above the AI_VEHICLE_MIN_RELIABILTY - if (!HasBit(e->company_avail, _current_company) || e->reliability * 100 < AI_VEHICLE_MIN_RELIABILTY << 16) continue; - - /* Rate and compare the engine by speed & capacity */ - int rating = rvi->max_speed * rvi->capacity; - if (rating <= best_veh_rating) continue; - - // Can we build it? - CommandCost ret = AI_DoCommand(0, i, 0, DC_QUERY_COST, CMD_BUILD_ROAD_VEH); - if (CmdFailed(ret)) continue; - - best_veh_rating = rating; - best_veh_index = i; - } - - return best_veh_index; - } -} - - -void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2) -{ - Company *c = GetCompany(_current_company); - - if (success) { - _companies_ainew[c->index].state = AI_STATE_GIVE_ORDERS; - _companies_ainew[c->index].veh_id = _new_vehicle_id; - - if (GetVehicle(_companies_ainew[c->index].veh_id)->cargo_type != _companies_ainew[c->index].cargo) { - /* Cargo type doesn't match, so refit it */ - if (CmdFailed(DoCommand(tile, _companies_ainew[c->index].veh_id, _companies_ainew[c->index].cargo, DC_EXEC, CMD_REFIT_ROAD_VEH))) { - /* Refit failed, so sell the vehicle */ - DoCommand(tile, _companies_ainew[c->index].veh_id, 0, DC_EXEC, CMD_SELL_ROAD_VEH); - _companies_ainew[c->index].state = AI_STATE_NOTHING; - } - } - } else { - /* XXX this should be handled more gracefully */ - _companies_ainew[c->index].state = AI_STATE_NOTHING; - } -} - - -// Builds the best vehicle possible -CommandCost AiNew_Build_Vehicle(Company *c, TileIndex tile, byte flag) -{ - EngineID i = AiNew_PickVehicle(c); - - if (i == INVALID_ENGINE) return CMD_ERROR; - if (_companies_ainew[c->index].tbt == AI_TRAIN) return CMD_ERROR; - - if (flag & DC_EXEC) { - return AI_DoCommandCc(tile, i, 0, flag, CMD_BUILD_ROAD_VEH, CcAI); - } else { - return AI_DoCommand(tile, i, 0, flag, CMD_BUILD_ROAD_VEH); - } -} - -CommandCost AiNew_Build_Depot(Company *c, TileIndex tile, DiagDirection direction, byte flag) -{ - CommandCost ret, ret2; - if (_companies_ainew[c->index].tbt == AI_TRAIN) { - return AI_DoCommand(tile, 0, direction, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_TRAIN_DEPOT); - } else { - ret = AI_DoCommand(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_DEPOT); - if (CmdFailed(ret2)) return ret; - // Try to build the road from the depot - ret2 = AI_DoCommand(tile + TileOffsByDiagDir(direction), DiagDirToRoadBits(ReverseDiagDir(direction)), 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD); - // If it fails, ignore it.. - if (CmdFailed(ret2)) return ret; - ret.AddCost(ret2); - return ret; - } -} diff --git a/src/ai/trolly/pathfinder.cpp b/src/ai/trolly/pathfinder.cpp deleted file mode 100644 index 0e08ddf77..000000000 --- a/src/ai/trolly/pathfinder.cpp +++ /dev/null @@ -1,482 +0,0 @@ -/* $Id$ */ - -/** @file pathfinder.cpp Pathfinder support for the trolly AI. */ - -#include "../../stdafx.h" -#include "../../openttd.h" -#include "../../bridge_map.h" -#include "../../debug.h" -#include "../../command_func.h" -#include "trolly.h" -#include "../../tunnel_map.h" -#include "../../bridge.h" -#include "../../tunnelbridge_map.h" -#include "../ai.h" -#include "../../variables.h" -#include "../../company_base.h" -#include "../../company_func.h" -#include "../../tunnelbridge.h" - - -#define TEST_STATION_NO_DIR 0xFF - -// Tests if a station can be build on the given spot -// TODO: make it train compatible -static bool TestCanBuildStationHere(TileIndex tile, byte dir) -{ - Company *c = GetCompany(_current_company); - - if (dir == TEST_STATION_NO_DIR) { - CommandCost ret; - // TODO: currently we only allow spots that can be access from al 4 directions... - // should be fixed!!! - for (dir = 0; dir < 4; dir++) { - ret = AiNew_Build_Station(c, _companies_ainew[c->index].tbt, tile, 1, 1, dir, DC_QUERY_COST); - if (CmdSucceeded(ret)) return true; - } - return false; - } - - // return true if command succeeded, so the inverse of CmdFailed() - return CmdSucceeded(AiNew_Build_Station(c, _companies_ainew[c->index].tbt, tile, 1, 1, dir, DC_QUERY_COST)); -} - - -static bool IsRoad(TileIndex tile) -{ - return - // MP_ROAD, but not a road depot? - (IsTileType(tile, MP_ROAD) && !IsRoadDepot(tile)) || - (IsTileType(tile, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(tile) == TRANSPORT_ROAD); -} - - -// Checks if a tile 'a' is between the tiles 'b' and 'c' -#define TILES_BETWEEN(a, b, c) (TileX(a) >= TileX(b) && TileX(a) <= TileX(c) && TileY(a) >= TileY(b) && TileY(a) <= TileY(c)) - - -// Check if the current tile is in our end-area -static int32 AyStar_AiPathFinder_EndNodeCheck(AyStar *aystar, OpenListNode *current) -{ - const Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target; - - // It is not allowed to have a station on the end of a bridge or tunnel ;) - if (current->path.node.user_data[0] != 0) return AYSTAR_DONE; - if (TILES_BETWEEN(current->path.node.tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) - if (IsTileType(current->path.node.tile, MP_CLEAR) || IsTileType(current->path.node.tile, MP_TREES)) - if (current->path.parent == NULL || TestCanBuildStationHere(current->path.node.tile, AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile))) - return AYSTAR_FOUND_END_NODE; - - return AYSTAR_DONE; -} - - -// Calculates the hash -// Currently it is a 10 bit hash, so the hash array has a max depth of 6 bits (so 64) -static uint AiPathFinder_Hash(uint key1, uint key2) -{ - return (TileX(key1) & 0x1F) + ((TileY(key1) & 0x1F) << 5); -} - - -// Clear the memory of all the things -static void AyStar_AiPathFinder_Free(AyStar *aystar) -{ - AyStarMain_Free(aystar); - delete aystar; -} - - -static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent); -static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent); -static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current); -static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current); - - -// This creates the AiPathFinder -AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFinderInfo) -{ - PathNode start_node; - uint x; - uint y; - // Create AyStar - AyStar *result = new AyStar(); - init_AyStar(result, AiPathFinder_Hash, 1 << 10); - // Set the function pointers - result->CalculateG = AyStar_AiPathFinder_CalculateG; - result->CalculateH = AyStar_AiPathFinder_CalculateH; - result->EndNodeCheck = AyStar_AiPathFinder_EndNodeCheck; - result->FoundEndNode = AyStar_AiPathFinder_FoundEndNode; - result->GetNeighbours = AyStar_AiPathFinder_GetNeighbours; - - result->free = AyStar_AiPathFinder_Free; - - // Set some information - result->loops_per_tick = AI_PATHFINDER_LOOPS_PER_TICK; - result->max_path_cost = 0; - result->max_search_nodes = AI_PATHFINDER_MAX_SEARCH_NODES; - - // Set the user_data to the PathFinderInfo - result->user_target = PathFinderInfo; - - // Set the start node - start_node.parent = NULL; - start_node.node.direction = INVALID_TRACKDIR; - start_node.node.user_data[0] = 0; - - // Now we add all the starting tiles - for (x = TileX(PathFinderInfo->start_tile_tl); x <= TileX(PathFinderInfo->start_tile_br); x++) { - for (y = TileY(PathFinderInfo->start_tile_tl); y <= TileY(PathFinderInfo->start_tile_br); y++) { - start_node.node.tile = TileXY(x, y); - result->addstart(result, &start_node.node, 0); - } - } - - return result; -} - - -// To reuse AyStar we sometimes have to clean all the memory -void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo) -{ - PathNode start_node; - uint x; - uint y; - - aystar->clear(aystar); - - // Set the user_data to the PathFinderInfo - aystar->user_target = PathFinderInfo; - - // Set the start node - start_node.parent = NULL; - start_node.node.direction = INVALID_TRACKDIR; - start_node.node.user_data[0] = 0; - start_node.node.tile = PathFinderInfo->start_tile_tl; - - // Now we add all the starting tiles - for (x = TileX(PathFinderInfo->start_tile_tl); x <= TileX(PathFinderInfo->start_tile_br); x++) { - for (y = TileY(PathFinderInfo->start_tile_tl); y <= TileY(PathFinderInfo->start_tile_br); y++) { - TileIndex tile = TileXY(x, y); - - if (!IsTileType(tile, MP_CLEAR) && !IsTileType(tile, MP_TREES)) continue; - if (!TestCanBuildStationHere(tile, TEST_STATION_NO_DIR)) continue; - start_node.node.tile = tile; - aystar->addstart(aystar, &start_node.node, 0); - } - } -} - - -// The h-value, simple calculation -static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent) -{ - const Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target; - int r, r2; - - if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION) { - // The station is pointing to a direction, add a tile towards that direction, so the H-value is more accurate - r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl + TileOffsByDiagDir(PathFinderInfo->end_direction)); - r2 = DistanceManhattan(current->tile, PathFinderInfo->end_tile_br + TileOffsByDiagDir(PathFinderInfo->end_direction)); - } else { - // No direction, so just get the fastest route to the station - r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl); - r2 = DistanceManhattan(current->tile, PathFinderInfo->end_tile_br); - } - // See if the bottomright is faster than the topleft.. - if (r2 < r) r = r2; - return r * AI_PATHFINDER_H_MULTIPLER; -} - - -// We found the end.. let's get the route back and put it in an array -static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current) -{ - Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target; - uint i = 0; - PathNode *parent = ¤t->path; - - do { - PathFinderInfo->route_extra[i] = parent->node.user_data[0]; - PathFinderInfo->route[i++] = parent->node.tile; - if (i > lengthof(PathFinderInfo->route)) { - // We ran out of space for the PathFinder - DEBUG(ai, 0, "No more space in pathfinder route[] array"); - PathFinderInfo->route_length = -1; // -1 indicates out of space - return; - } - parent = parent->parent; - } while (parent != NULL); - PathFinderInfo->route_length = i; - DEBUG(ai, 1, "Found route of %d nodes long in %d nodes of searching", i, Hash_Size(&aystar->ClosedListHash)); -} - - -// What tiles are around us. -static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current) -{ - CommandCost ret; - int dir; - - Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target; - - aystar->num_neighbours = 0; - - // Go through all surrounding tiles and check if they are within the limits - for (DiagDirection i = DIAGDIR_BEGIN; i < DIAGDIR_END; i++) { - TileIndex ctile = current->path.node.tile; // Current tile - TileIndex atile = ctile + TileOffsByDiagDir(i); // Adjacent tile - - if (TileX(atile) > 1 && TileX(atile) < MapMaxX() - 1 && - TileY(atile) > 1 && TileY(atile) < MapMaxY() - 1) { - // We also directly test if the current tile can connect to this tile.. - // We do this simply by just building the tile! - - // If the next step is a bridge, we have to enter it the right way - if (!PathFinderInfo->rail_or_road && IsRoad(atile)) { - if (IsTileType(atile, MP_TUNNELBRIDGE)) { - if (GetTunnelBridgeDirection(atile) != i) continue; - } - } - - if ((AI_PATHFINDER_FLAG_BRIDGE & current->path.node.user_data[0]) != 0 || - (AI_PATHFINDER_FLAG_TUNNEL & current->path.node.user_data[0]) != 0) { - // We are a bridge/tunnel, how cool!! - // This means we can only point forward.. get the direction from the user_data - if ((uint)i != (current->path.node.user_data[0] >> 8)) continue; - } - dir = 0; - - // First, check if we have a parent - if (current->path.parent == NULL && current->path.node.user_data[0] == 0) { - // If not, this means we are at the starting station - if (PathFinderInfo->start_direction != AI_PATHFINDER_NO_DIRECTION) { - // We do need a direction? - if (AiNew_GetDirection(ctile, atile) != PathFinderInfo->start_direction) { - // We are not pointing the right way, invalid tile - continue; - } - } - } else if (current->path.node.user_data[0] == 0) { - if (PathFinderInfo->rail_or_road) { - // Rail check - dir = AiNew_GetRailDirection(current->path.parent->node.tile, ctile, atile); - ret = AI_DoCommand(ctile, 0, dir, DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL); - if (CmdFailed(ret)) continue; -#ifdef AI_PATHFINDER_NO_90DEGREES_TURN - if (current->path.parent->parent != NULL) { - // Check if we don't make a 90degree curve - int dir1 = AiNew_GetRailDirection(current->path.parent->parent->node.tile, current->path.parent->node.tile, ctile); - if (_illegal_curves[dir1] == dir || _illegal_curves[dir] == dir1) { - continue; - } - } -#endif - } else { - // Road check - dir = AiNew_GetRoadDirection(current->path.parent->node.tile, ctile, atile); - if (IsRoad(ctile)) { - if (IsTileType(ctile, MP_TUNNELBRIDGE)) { - // We have a bridge, how nicely! We should mark it... - dir = 0; - } else { - // It already has road.. check if we miss any bits! - if ((GetAnyRoadBits(ctile, ROADTYPE_ROAD) & dir) != dir) { - // We do miss some pieces :( - dir &= ~GetAnyRoadBits(ctile, ROADTYPE_ROAD); - } else { - dir = 0; - } - } - } - // Only destruct things if it is MP_CLEAR of MP_TREES - if (dir != 0) { - ret = AI_DoCommand(ctile, dir, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD); - if (CmdFailed(ret)) continue; - } - } - } - - // The tile can be connected - aystar->neighbours[aystar->num_neighbours].tile = atile; - aystar->neighbours[aystar->num_neighbours].user_data[0] = 0; - aystar->neighbours[aystar->num_neighbours++].direction = INVALID_TRACKDIR; - } - } - - // Next step, check for bridges and tunnels - if (current->path.parent != NULL && current->path.node.user_data[0] == 0) { - // First we get the dir from this tile and his parent - DiagDirection dir = AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile); - // It means we can only walk with the track, so the bridge has to be in the same direction - TileIndex tile = current->path.node.tile; - TileIndex new_tile = tile; - Slope tileh = GetTileSlope(tile, NULL); - - // Bridges can only be build on land that is not flat - // And if there is a road or rail blocking - if (tileh != SLOPE_FLAT || - (PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDiagDir(dir), MP_ROAD)) || - (!PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDiagDir(dir), MP_RAILWAY))) { - for (;;) { - new_tile += TileOffsByDiagDir(dir); - - // Precheck, is the length allowed? - if (!CheckBridge_Stuff(0, GetTunnelBridgeLength(tile, new_tile))) break; - - // Check if we hit the station-tile.. we don't like that! - if (TILES_BETWEEN(new_tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) break; - - // Try building the bridge.. - ret = AI_DoCommand(tile, new_tile, (0 << 8) + (MAX_BRIDGES / 2), DC_AUTO, CMD_BUILD_BRIDGE); - if (CmdFailed(ret)) continue; - // We can build a bridge here.. add him to the neighbours - aystar->neighbours[aystar->num_neighbours].tile = new_tile; - aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_BRIDGE + (dir << 8); - aystar->neighbours[aystar->num_neighbours++].direction = INVALID_TRACKDIR; - // We can only have 12 neighbours, and we need 1 left for tunnels - if (aystar->num_neighbours == 11) break; - } - } - - // Next, check for tunnels! - // Tunnels can only be built on slopes corresponding to the direction - // For now, we check both sides for this tile.. terraforming gives fuzzy result - if (tileh == InclinedSlope(dir)) { - // Now simply check if a tunnel can be build - ret = AI_DoCommand(tile, (PathFinderInfo->rail_or_road ? 0 : 0x200), 0, DC_AUTO, CMD_BUILD_TUNNEL); - tileh = GetTileSlope(_build_tunnel_endtile, NULL); - if (CmdSucceeded(ret) && IsInclinedSlope(tileh)) { - aystar->neighbours[aystar->num_neighbours].tile = _build_tunnel_endtile; - aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_TUNNEL + (dir << 8); - aystar->neighbours[aystar->num_neighbours++].direction = INVALID_TRACKDIR; - } - } - } -} - - -extern Foundation GetRailFoundation(Slope tileh, TrackBits bits); // XXX function declaration in .c -extern Foundation GetRoadFoundation(Slope tileh, RoadBits bits); // XXX function declaration in .c - -// The most important function: it calculates the g-value -static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent) -{ - Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target; - int res = 0; - Slope tileh = GetTileSlope(current->tile, NULL); - Slope parent_tileh = GetTileSlope(parent->path.node.tile, NULL); - - // Check if we hit the end-tile - if (TILES_BETWEEN(current->tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) { - // We are at the end-tile, check if we had a direction or something... - if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION && AiNew_GetDirection(current->tile, parent->path.node.tile) != PathFinderInfo->end_direction) { - // We are not pointing the right way, invalid tile - return AYSTAR_INVALID_NODE; - } - // If it was valid, drop out.. we don't build on the endtile - return 0; - } - - // Give everything a small penalty - res += AI_PATHFINDER_PENALTY; - - if (!PathFinderInfo->rail_or_road) { - // Road has the lovely advantage it can use other road... check if - // the current tile is road, and if so, give a good bonus - if (IsRoad(current->tile)) { - res -= AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS; - } - } - - // We should give a penalty when the tile is going up or down.. this is one way to do so! - // Too bad we have to count it from the parent.. but that is not so bad. - // We also dislike long routes on slopes, since they do not look too realistic - // when there is a flat land all around, they are more expensive to build, and - // especially they essentially block the ability to connect or cross the road - // from one side. - if (parent_tileh != SLOPE_FLAT && parent->path.parent != NULL) { - // Skip if the tile was from a bridge or tunnel - if (parent->path.node.user_data[0] == 0 && current->user_data[0] == 0) { - if (PathFinderInfo->rail_or_road) { - Foundation f = GetRailFoundation(parent_tileh, (TrackBits)(1 << AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile))); - if (IsInclinedFoundation(f) || (!IsFoundation(f) && IsInclinedSlope(parent_tileh))) { - res += AI_PATHFINDER_TILE_GOES_UP_PENALTY; - } else { - res += AI_PATHFINDER_FOUNDATION_PENALTY; - } - } else { - if (!IsRoad(parent->path.node.tile) || !IsTileType(parent->path.node.tile, MP_TUNNELBRIDGE)) { - Foundation f = GetRoadFoundation(parent_tileh, (RoadBits)AiNew_GetRoadDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile)); - if (IsInclinedFoundation(f) || (!IsFoundation(f) && IsInclinedSlope(parent_tileh))) { - res += AI_PATHFINDER_TILE_GOES_UP_PENALTY; - } else { - res += AI_PATHFINDER_FOUNDATION_PENALTY; - } - } - } - } - } - - // Are we part of a tunnel? - if ((AI_PATHFINDER_FLAG_TUNNEL & current->user_data[0]) != 0) { - int r; - // Tunnels are very expensive when build on long routes.. - // Ironicly, we are using BridgeCode here ;) - r = AI_PATHFINDER_TUNNEL_PENALTY * GetTunnelBridgeLength(current->tile, parent->path.node.tile); - res += r + (r >> 8); - } - - // Are we part of a bridge? - if ((AI_PATHFINDER_FLAG_BRIDGE & current->user_data[0]) != 0) { - // That means for every length a penalty - res += AI_PATHFINDER_BRIDGE_PENALTY * GetTunnelBridgeLength(current->tile, parent->path.node.tile); - // Check if we are going up or down, first for the starting point - // In user_data[0] is at the 8th bit the direction - if (!HasBridgeFlatRamp(parent_tileh, (Axis)((current->user_data[0] >> 8) & 1))) res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY; - // Second for the end point - if (!HasBridgeFlatRamp(tileh, (Axis)((current->user_data[0] >> 8) & 1))) res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY; - } - - // To prevent the AI from taking the fastest way in tiles, but not the fastest way - // in speed, we have to give a good penalty to direction changing - // This way, we get almost the fastest way in tiles, and a very good speed on the track - if (!PathFinderInfo->rail_or_road) { - if (parent->path.parent != NULL && - AiNew_GetDirection(current->tile, parent->path.node.tile) != AiNew_GetDirection(parent->path.node.tile, parent->path.parent->node.tile)) { - // When road exists, we don't like turning, but its free, so don't be to piggy about it - if (IsRoad(parent->path.node.tile)) { - res += AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY; - } else { - res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY; - } - } - } else { - // For rail we have 1 exeption: diagonal rail.. - // So we fetch 2 raildirection. That of the current one, and of the one before that - if (parent->path.parent != NULL && parent->path.parent->parent != NULL) { - int dir1 = AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile); - int dir2 = AiNew_GetRailDirection(parent->path.parent->parent->node.tile, parent->path.parent->node.tile, parent->path.node.tile); - // First, see if we are on diagonal path, that is better than straight path - if (dir1 > 1) res -= AI_PATHFINDER_DIAGONAL_BONUS; - - // First see if they are different - if (dir1 != dir2) { - // dir 2 and 3 are 1 diagonal track, and 4 and 5. - if (!(((dir1 == 2 || dir1 == 3) && (dir2 == 2 || dir2 == 3)) || ((dir1 == 4 || dir1 == 5) && (dir2 == 4 || dir2 == 5)))) { - // It is not, so we changed of direction - res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY; - } - if (parent->path.parent->parent->parent != NULL) { - int dir3 = AiNew_GetRailDirection(parent->path.parent->parent->parent->node.tile, parent->path.parent->parent->node.tile, parent->path.parent->node.tile); - // Check if we changed 3 tiles of direction in 3 tiles.. bad!!! - if ((dir1 == 0 || dir1 == 1) && dir2 > 1 && (dir3 == 0 || dir3 == 1)) { - res += AI_PATHFINDER_CURVE_PENALTY; - } - } - } - } - } - - return (res < 0) ? 0 : res; -} diff --git a/src/ai/trolly/shared.cpp b/src/ai/trolly/shared.cpp deleted file mode 100644 index 1183f0371..000000000 --- a/src/ai/trolly/shared.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* $Id$ */ - -/** @file shared.cpp Shared functions for the trolly AI. */ - -#include "../../stdafx.h" -#include "../../openttd.h" -#include "../../debug.h" -#include "../../map_func.h" -#include "../../vehicle_base.h" -#include "../../company_base.h" -#include "trolly.h" - -int AiNew_GetRailDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c) -{ - // 0 = vert - // 1 = horz - // 2 = dig up-left - // 3 = dig down-right - // 4 = dig down-left - // 5 = dig up-right - - uint x1 = TileX(tile_a); - uint x2 = TileX(tile_b); - uint x3 = TileX(tile_c); - - uint y1 = TileY(tile_a); - uint y2 = TileY(tile_b); - uint y3 = TileY(tile_c); - - if (y1 == y2 && y2 == y3) return 0; - if (x1 == x2 && x2 == x3) return 1; - if (y2 > y1) return x2 > x3 ? 2 : 4; - if (x2 > x1) return y2 > y3 ? 2 : 5; - if (y1 > y2) return x2 > x3 ? 5 : 3; - if (x1 > x2) return y2 > y3 ? 4 : 3; - - return 0; -} - -int AiNew_GetRoadDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c) -{ - int x1, x2, x3; - int y1, y2, y3; - int r; - - x1 = TileX(tile_a); - x2 = TileX(tile_b); - x3 = TileX(tile_c); - - y1 = TileY(tile_a); - y2 = TileY(tile_b); - y3 = TileY(tile_c); - - r = 0; - - if (x1 < x2) r += 8; - if (y1 < y2) r += 1; - if (x1 > x2) r += 2; - if (y1 > y2) r += 4; - - if (x2 < x3) r += 2; - if (y2 < y3) r += 4; - if (x2 > x3) r += 8; - if (y2 > y3) r += 1; - - return r; -} - -// Get's the direction between 2 tiles seen from tile_a -DiagDirection AiNew_GetDirection(TileIndex tile_a, TileIndex tile_b) -{ - if (TileY(tile_a) < TileY(tile_b)) return DIAGDIR_SE; - if (TileY(tile_a) > TileY(tile_b)) return DIAGDIR_NW; - if (TileX(tile_a) < TileX(tile_b)) return DIAGDIR_SW; - return DIAGDIR_NE; -} - - -// This functions looks up if this vehicle is special for this AI -// and returns his flag -uint AiNew_GetSpecialVehicleFlag(Company *c, Vehicle *v) -{ - uint i; - - for (i = 0; i < AI_MAX_SPECIAL_VEHICLES; i++) { - if (_companies_ainew[c->index].special_vehicles[i].veh_id == v->index) { - return _companies_ainew[c->index].special_vehicles[i].flag; - } - } - - // Not found :( - return 0; -} - - -bool AiNew_SetSpecialVehicleFlag(Company *c, Vehicle *v, uint flag) -{ - int new_id = -1; - uint i; - - for (i = 0; i < AI_MAX_SPECIAL_VEHICLES; i++) { - if (_companies_ainew[c->index].special_vehicles[i].veh_id == v->index) { - _companies_ainew[c->index].special_vehicles[i].flag |= flag; - return true; - } - if (new_id == -1 && - _companies_ainew[c->index].special_vehicles[i].veh_id == 0 && - _companies_ainew[c->index].special_vehicles[i].flag == 0) { - new_id = i; - } - } - - // Out of special_vehicle spots :s - if (new_id == -1) { - DEBUG(ai, 1, "special_vehicles list is too small"); - return false; - } - _companies_ainew[c->index].special_vehicles[new_id].veh_id = v->index; - _companies_ainew[c->index].special_vehicles[new_id].flag = flag; - return true; -} diff --git a/src/ai/trolly/trolly.cpp b/src/ai/trolly/trolly.cpp deleted file mode 100644 index 6b874ec0d..000000000 --- a/src/ai/trolly/trolly.cpp +++ /dev/null @@ -1,1348 +0,0 @@ -/* $Id$ */ - -/** - * @file trolly.cpp Implementation of the trolly AI. - * - * This AI was created as a direct reaction to the big demand for some good AIs - * in OTTD. Too bad it never left alpha-stage, and it is considered dead in its - * current form. - * By the time of writing this, we, the creator of this AI and a good friend of - * mine, are designing a whole new AI-system that allows us to create AIs - * easier and without all the fuzz we encountered while I was working on this - * AI. By the time that system is finished, you can expect that this AI will - * dissapear, because it is pretty obselete and bad programmed. - * - * Meanwhile I wish you all much fun with this AI; if you are interested as - * AI-developer in this AI, I advise you not stare too long to some code, some - * things in here really are... strange ;) But in either way: enjoy :) - * - * -- TrueLight :: 2005-09-01 - */ - -#include "../../stdafx.h" -#include "../../openttd.h" -#include "../../debug.h" -#include "../../road_map.h" -#include "../../station_map.h" -#include "../../command_func.h" -#include "trolly.h" -#include "../../depot_base.h" -#include "../../town.h" -#include "../../industry.h" -#include "../../station_base.h" -#include "../../engine_func.h" -#include "../../engine_base.h" -#include "../../gui.h" -#include "../../vehicle_base.h" -#include "../../vehicle_func.h" -#include "../../date_func.h" -#include "../ai.h" -#include "../../company_base.h" -#include "../../company_func.h" - -#include "table/strings.h" - -CompanyAiNew _companies_ainew[MAX_COMPANIES]; - -// This function is called after StartUp. It is the init of an AI -static void AiNew_State_FirstTime(Company *c) -{ - // This assert is used to protect those function from misuse - // You have quickly a small mistake in the state-array - // With that, everything would go wrong. Finding that, is almost impossible - // With this assert, that problem can never happen. - assert(_companies_ainew[c->index].state == AI_STATE_FIRST_TIME); - // We first have to init some things - - if (_current_company == 1) ShowErrorMessage(INVALID_STRING_ID, TEMP_AI_IN_PROGRESS, 0, 0); - - // The PathFinder (AyStar) - // TODO: Maybe when an AI goes bankrupt, this is de-init - // or when coming from a savegame.. should be checked out! - _companies_ainew[c->index].path_info.start_tile_tl = 0; - _companies_ainew[c->index].path_info.start_tile_br = 0; - _companies_ainew[c->index].path_info.end_tile_tl = 0; - _companies_ainew[c->index].path_info.end_tile_br = 0; - _companies_ainew[c->index].pathfinder = new_AyStar_AiPathFinder(12, &_companies_ainew[c->index].path_info); - - _companies_ainew[c->index].idle = 0; - _companies_ainew[c->index].last_vehiclecheck_date = _date; - - // We ALWAYS start with a bus route.. just some basic money ;) - _companies_ainew[c->index].action = AI_ACTION_BUS_ROUTE; - - // Let's popup the news, and after that, start building.. - _companies_ainew[c->index].state = AI_STATE_WAKE_UP; -} - - -// This function just waste some time -// It keeps it more real. The AI can build on such tempo no normal user -// can ever keep up with that. The competitor_speed already delays a bit -// but after the AI finished a track it really needs to go to sleep. -// -// Let's say, we sleep between one and three days if the AI is put on Very Fast. -// This means that on Very Slow it will be between 16 and 48 days.. slow enough? -static void AiNew_State_Nothing(Company *c) -{ - assert(_companies_ainew[c->index].state == AI_STATE_NOTHING); - // If we are done idling, start over again - if (_companies_ainew[c->index].idle == 0) _companies_ainew[c->index].idle = AI_RandomRange(DAY_TICKS * 2) + DAY_TICKS; - if (--_companies_ainew[c->index].idle == 0) { - // We are done idling.. what you say? Let's do something! - // I mean.. the next tick ;) - _companies_ainew[c->index].state = AI_STATE_WAKE_UP; - } -} - - -// This function picks out a task we are going to do. -// Currently supported: -// - Make new route -// - Check route -// - Build HQ -static void AiNew_State_WakeUp(Company *c) -{ - assert(_companies_ainew[c->index].state == AI_STATE_WAKE_UP); - // First, check if we have a HQ - if (c->location_of_HQ == INVALID_TILE) { - // We have no HQ yet, build one on a random place - // Random till we found a place for it! - // TODO: this should not be on a random place.. - AiNew_Build_CompanyHQ(c, AI_Random() % MapSize()); - // Enough for now, but we want to come back here the next time - // so we do not change any status - return; - } - - Money money = c->money - AI_MINIMUM_MONEY; - - // Let's pick an action! - if (_companies_ainew[c->index].action == AI_ACTION_NONE) { - int r = AI_Random() & 0xFF; - if (c->current_loan > 0 && - c->old_economy[1].income > AI_MINIMUM_INCOME_FOR_LOAN && - r < 10) { - _companies_ainew[c->index].action = AI_ACTION_REPAY_LOAN; - } else if (_companies_ainew[c->index].last_vehiclecheck_date + AI_DAYS_BETWEEN_VEHICLE_CHECKS < _date) { - // Check all vehicles once in a while - _companies_ainew[c->index].action = AI_ACTION_CHECK_ALL_VEHICLES; - _companies_ainew[c->index].last_vehiclecheck_date = _date; - } else if (r < 100 && !_settings_game.ai.ai_disable_veh_roadveh) { - // Do we have any spots for road-vehicles left open? - if (GetFreeUnitNumber(VEH_ROAD) <= _settings_game.vehicle.max_roadveh) { - if (r < 85) { - _companies_ainew[c->index].action = AI_ACTION_TRUCK_ROUTE; - } else { - _companies_ainew[c->index].action = AI_ACTION_BUS_ROUTE; - } - } -#if 0 - } else if (r < 200 && !_settings_game.ai.ai_disable_veh_train) { - if (GetFreeUnitNumber(VEH_TRAIN) <= _settings_game.vehicle.max_trains) { - _companies_ainew[c->index].action = AI_ACTION_TRAIN_ROUTE; - } -#endif - } - - _companies_ainew[c->index].counter = 0; - } - - if (_companies_ainew[c->index].counter++ > AI_MAX_TRIES_FOR_SAME_ROUTE) { - _companies_ainew[c->index].action = AI_ACTION_NONE; - return; - } - - if (_settings_game.ai.ai_disable_veh_roadveh && ( - _companies_ainew[c->index].action == AI_ACTION_BUS_ROUTE || - _companies_ainew[c->index].action == AI_ACTION_TRUCK_ROUTE - )) { - _companies_ainew[c->index].action = AI_ACTION_NONE; - return; - } - - if (_companies_ainew[c->index].action == AI_ACTION_REPAY_LOAN && - money > AI_MINIMUM_LOAN_REPAY_MONEY) { - // We start repaying some money.. - _companies_ainew[c->index].state = AI_STATE_REPAY_MONEY; - return; - } - - if (_companies_ainew[c->index].action == AI_ACTION_CHECK_ALL_VEHICLES) { - _companies_ainew[c->index].state = AI_STATE_CHECK_ALL_VEHICLES; - return; - } - - // It is useless to start finding a route if we don't have enough money - // to build the route anyway.. - if (_companies_ainew[c->index].action == AI_ACTION_BUS_ROUTE && - money > AI_MINIMUM_BUS_ROUTE_MONEY) { - if (GetFreeUnitNumber(VEH_ROAD) > _settings_game.vehicle.max_roadveh) { - _companies_ainew[c->index].action = AI_ACTION_NONE; - return; - } - _companies_ainew[c->index].cargo = AI_NEED_CARGO; - _companies_ainew[c->index].state = AI_STATE_LOCATE_ROUTE; - _companies_ainew[c->index].tbt = AI_BUS; // Bus-route - return; - } - if (_companies_ainew[c->index].action == AI_ACTION_TRUCK_ROUTE && - money > AI_MINIMUM_TRUCK_ROUTE_MONEY) { - if (GetFreeUnitNumber(VEH_ROAD) > _settings_game.vehicle.max_roadveh) { - _companies_ainew[c->index].action = AI_ACTION_NONE; - return; - } - _companies_ainew[c->index].cargo = AI_NEED_CARGO; - _companies_ainew[c->index].last_id = 0; - _companies_ainew[c->index].state = AI_STATE_LOCATE_ROUTE; - _companies_ainew[c->index].tbt = AI_TRUCK; - return; - } - - _companies_ainew[c->index].state = AI_STATE_NOTHING; -} - - -static void AiNew_State_ActionDone(Company *c) -{ - _companies_ainew[c->index].action = AI_ACTION_NONE; - _companies_ainew[c->index].state = AI_STATE_NOTHING; -} - - -// Check if a city or industry is good enough to start a route there -static bool AiNew_Check_City_or_Industry(Company *c, int ic, byte type) -{ - if (type == AI_CITY) { - const Town *t = GetTown(ic); - const Station *st; - uint count = 0; - int j = 0; - - // We don't like roadconstructions, don't even true such a city - if (t->road_build_months != 0) return false; - - // Check if the rating in a city is high enough - // If not, take a chance if we want to continue - if (t->ratings[_current_company] < 0 && AI_CHANCE16(1, 4)) return false; - - if (t->max_pass - t->act_pass < AI_CHECKCITY_NEEDED_CARGO && !AI_CHANCE16(1, AI_CHECKCITY_CITY_CHANCE)) return false; - - // Check if we have build a station in this town the last 6 months - // else we don't do it. This is done, because stat updates can be slow - // and sometimes it takes up to 4 months before the stats are corectly. - // This way we don't get 12 busstations in one city of 100 population ;) - FOR_ALL_STATIONS(st) { - // Do we own it? - if (st->owner == _current_company) { - // Are we talking busses? - if (_companies_ainew[c->index].tbt == AI_BUS && (FACIL_BUS_STOP & st->facilities) != FACIL_BUS_STOP) continue; - // Is it the same city as we are in now? - if (st->town != t) continue; - // When was this station build? - if (_date - st->build_date < AI_CHECKCITY_DATE_BETWEEN) return false; - // Cound the amount of stations in this city that we own - count++; - } else { - // We do not own it, request some info about the station - // we want to know if this station gets the same good. If so, - // we want to know its rating. If it is too high, we are not going - // to build there - if (!st->goods[CT_PASSENGERS].last_speed) continue; - // Is it around our city - if (DistanceManhattan(st->xy, t->xy) > 10) continue; - // It does take this cargo.. what is his rating? - if (st->goods[CT_PASSENGERS].rating < AI_CHECKCITY_CARGO_RATING) continue; - j++; - // When this is the first station, we build a second with no problem ;) - if (j == 1) continue; - // The rating is high.. second station... - // a little chance that we still continue - // But if there are 3 stations of this size, we never go on... - if (j == 2 && AI_CHANCE16(1, AI_CHECKCITY_CARGO_RATING_CHANCE)) continue; - // We don't like this station :( - return false; - } - } - - // We are about to add one... - count++; - // Check if we the city can provide enough cargo for this amount of stations.. - if (count * AI_CHECKCITY_CARGO_PER_STATION > t->max_pass) return false; - - // All check are okay, so we can build here! - return true; - } - if (type == AI_INDUSTRY) { - const Industry *i = GetIndustry(ic); - const Station *st; - int count = 0; - int j = 0; - - if (i->town != NULL && i->town->ratings[_current_company] < 0 && AI_CHANCE16(1, 4)) return false; - - // No limits on delevering stations! - // Or for industry that does not give anything yet - if (i->produced_cargo[0] == CT_INVALID || i->last_month_production[0] == 0) return true; - - if (i->last_month_production[0] - i->last_month_transported[0] < AI_CHECKCITY_NEEDED_CARGO) return false; - - // Check if we have build a station in this town the last 6 months - // else we don't do it. This is done, because stat updates can be slow - // and sometimes it takes up to 4 months before the stats are corectly. - FOR_ALL_STATIONS(st) { - // Do we own it? - if (st->owner == _current_company) { - // Are we talking trucks? - if (_companies_ainew[c->index].tbt == AI_TRUCK && (FACIL_TRUCK_STOP & st->facilities) != FACIL_TRUCK_STOP) continue; - // Is it the same city as we are in now? - if (st->town != i->town) continue; - // When was this station build? - if (_date - st->build_date < AI_CHECKCITY_DATE_BETWEEN) return false; - // Cound the amount of stations in this city that we own - count++; - } else { - // We do not own it, request some info about the station - // we want to know if this station gets the same good. If so, - // we want to know its rating. If it is too high, we are not going - // to build there - if (i->produced_cargo[0] == CT_INVALID) continue; - // It does not take this cargo - if (!st->goods[i->produced_cargo[0]].last_speed) continue; - // Is it around our industry - if (DistanceManhattan(st->xy, i->xy) > 5) continue; - // It does take this cargo.. what is his rating? - if (st->goods[i->produced_cargo[0]].rating < AI_CHECKCITY_CARGO_RATING) continue; - j++; - // The rating is high.. a little chance that we still continue - // But if there are 2 stations of this size, we never go on... - if (j == 1 && AI_CHANCE16(1, AI_CHECKCITY_CARGO_RATING_CHANCE)) continue; - // We don't like this station :( - return false; - } - } - - // We are about to add one... - count++; - // Check if we the city can provide enough cargo for this amount of stations.. - if (count * AI_CHECKCITY_CARGO_PER_STATION > i->last_month_production[0]) return false; - - // All check are okay, so we can build here! - return true; - } - - return true; -} - - -// This functions tries to locate a good route -static void AiNew_State_LocateRoute(Company *c) -{ - assert(_companies_ainew[c->index].state == AI_STATE_LOCATE_ROUTE); - // For now, we only support PASSENGERS, CITY and BUSSES - - // We don't have a route yet - if (_companies_ainew[c->index].cargo == AI_NEED_CARGO) { - _companies_ainew[c->index].new_cost = 0; // No cost yet - _companies_ainew[c->index].temp = -1; - // Reset the counter - _companies_ainew[c->index].counter = 0; - - _companies_ainew[c->index].from_ic = -1; - _companies_ainew[c->index].to_ic = -1; - if (_companies_ainew[c->index].tbt == AI_BUS) { - // For now we only have a passenger route - _companies_ainew[c->index].cargo = CT_PASSENGERS; - - // Find a route to cities - _companies_ainew[c->index].from_type = AI_CITY; - _companies_ainew[c->index].to_type = AI_CITY; - } else if (_companies_ainew[c->index].tbt == AI_TRUCK) { - _companies_ainew[c->index].cargo = AI_NO_CARGO; - - _companies_ainew[c->index].from_type = AI_INDUSTRY; - _companies_ainew[c->index].to_type = AI_INDUSTRY; - } - - // Now we are doing initing, we wait one tick - return; - } - - // Increase the counter and abort if it is taking too long! - _companies_ainew[c->index].counter++; - if (_companies_ainew[c->index].counter > AI_LOCATE_ROUTE_MAX_COUNTER) { - // Switch back to doing nothing! - _companies_ainew[c->index].state = AI_STATE_NOTHING; - return; - } - - // We are going to locate a city from where we are going to connect - if (_companies_ainew[c->index].from_ic == -1) { - if (_companies_ainew[c->index].temp == -1) { - // First, we pick a random spot to search from - if (_companies_ainew[c->index].from_type == AI_CITY) { - _companies_ainew[c->index].temp = AI_RandomRange(GetMaxTownIndex() + 1); - } else { - _companies_ainew[c->index].temp = AI_RandomRange(GetMaxIndustryIndex() + 1); - } - } - - if (!AiNew_Check_City_or_Industry(c, _companies_ainew[c->index].temp, _companies_ainew[c->index].from_type)) { - // It was not a valid city - // increase the temp with one, and return. We will come back later here - // to try again - _companies_ainew[c->index].temp++; - if (_companies_ainew[c->index].from_type == AI_CITY) { - if (_companies_ainew[c->index].temp > GetMaxTownIndex()) _companies_ainew[c->index].temp = 0; - } else { - if (_companies_ainew[c->index].temp > GetMaxIndustryIndex()) _companies_ainew[c->index].temp = 0; - } - - // Don't do an attempt if we are trying the same id as the last time... - if (_companies_ainew[c->index].last_id == _companies_ainew[c->index].temp) return; - _companies_ainew[c->index].last_id = _companies_ainew[c->index].temp; - - return; - } - - // We found a good city/industry, save the data of it - _companies_ainew[c->index].from_ic = _companies_ainew[c->index].temp; - - // Start the next tick with finding a to-city - _companies_ainew[c->index].temp = -1; - return; - } - - // Find a to-city - if (_companies_ainew[c->index].temp == -1) { - // First, we pick a random spot to search to - if (_companies_ainew[c->index].to_type == AI_CITY) { - _companies_ainew[c->index].temp = AI_RandomRange(GetMaxTownIndex() + 1); - } else { - _companies_ainew[c->index].temp = AI_RandomRange(GetMaxIndustryIndex() + 1); - } - } - - // The same city is not allowed - // Also check if the city is valid - if (_companies_ainew[c->index].temp != _companies_ainew[c->index].from_ic && AiNew_Check_City_or_Industry(c, _companies_ainew[c->index].temp, _companies_ainew[c->index].to_type)) { - // Maybe it is valid.. - - /* We need to know if they are not to far apart from eachother.. - * We do that by checking how much cargo we have to move and how long the - * route is. - */ - - if (_companies_ainew[c->index].from_type == AI_CITY && _companies_ainew[c->index].tbt == AI_BUS) { - const Town *town_from = GetTown(_companies_ainew[c->index].from_ic); - const Town *town_temp = GetTown(_companies_ainew[c->index].temp); - uint distance = DistanceManhattan(town_from->xy, town_temp->xy); - int max_cargo; - - max_cargo = town_from->max_pass + town_temp->max_pass; - max_cargo -= town_from->act_pass + town_temp->act_pass; - - // max_cargo is now the amount of cargo we can move between the two cities - // If it is more than the distance, we allow it - if (distance <= max_cargo * AI_LOCATEROUTE_BUS_CARGO_DISTANCE) { - // We found a good city/industry, save the data of it - _companies_ainew[c->index].to_ic = _companies_ainew[c->index].temp; - _companies_ainew[c->index].state = AI_STATE_FIND_STATION; - - DEBUG(ai, 1, "[LocateRoute] found bus-route of %d tiles long (from %d to %d)", - distance, - _companies_ainew[c->index].from_ic, - _companies_ainew[c->index].temp - ); - - _companies_ainew[c->index].from_tile = 0; - _companies_ainew[c->index].to_tile = 0; - - return; - } - } else if (_companies_ainew[c->index].tbt == AI_TRUCK) { - const Industry *ind_from = GetIndustry(_companies_ainew[c->index].from_ic); - const Industry *ind_temp = GetIndustry(_companies_ainew[c->index].temp); - bool found = false; - int max_cargo = 0; - uint i; - - // TODO: in max_cargo, also check other cargo (beside [0]) - // First we check if the from_ic produces cargo that this ic accepts - if (ind_from->produced_cargo[0] != CT_INVALID && ind_from->last_month_production[0] != 0) { - for (i = 0; i < lengthof(ind_temp->accepts_cargo); i++) { - if (ind_temp->accepts_cargo[i] == CT_INVALID) break; - if (ind_from->produced_cargo[0] == ind_temp->accepts_cargo[i]) { - // Found a compatible industry - max_cargo = ind_from->last_month_production[0] - ind_from->last_month_transported[0]; - found = true; - _companies_ainew[c->index].from_deliver = true; - _companies_ainew[c->index].to_deliver = false; - break; - } - } - } - if (!found && ind_temp->produced_cargo[0] != CT_INVALID && ind_temp->last_month_production[0] != 0) { - // If not check if the current ic produces cargo that the from_ic accepts - for (i = 0; i < lengthof(ind_from->accepts_cargo); i++) { - if (ind_from->accepts_cargo[i] == CT_INVALID) break; - if (ind_from->produced_cargo[0] == ind_from->accepts_cargo[i]) { - // Found a compatbiel industry - found = true; - max_cargo = ind_temp->last_month_production[0] - ind_temp->last_month_transported[0]; - _companies_ainew[c->index].from_deliver = false; - _companies_ainew[c->index].to_deliver = true; - break; - } - } - } - if (found) { - // Yeah, they are compatible!!! - // Check the length against the amount of goods - uint distance = DistanceManhattan(ind_from->xy, ind_temp->xy); - - if (distance > AI_LOCATEROUTE_TRUCK_MIN_DISTANCE && - distance <= max_cargo * AI_LOCATEROUTE_TRUCK_CARGO_DISTANCE) { - _companies_ainew[c->index].to_ic = _companies_ainew[c->index].temp; - if (_companies_ainew[c->index].from_deliver) { - _companies_ainew[c->index].cargo = ind_from->produced_cargo[0]; - } else { - _companies_ainew[c->index].cargo = ind_temp->produced_cargo[0]; - } - _companies_ainew[c->index].state = AI_STATE_FIND_STATION; - - DEBUG(ai, 1, "[LocateRoute] found truck-route of %d tiles long (from %d to %d)", - distance, - _companies_ainew[c->index].from_ic, - _companies_ainew[c->index].temp - ); - - _companies_ainew[c->index].from_tile = 0; - _companies_ainew[c->index].to_tile = 0; - - return; - } - } - } - } - - // It was not a valid city - // increase the temp with one, and return. We will come back later here - // to try again - _companies_ainew[c->index].temp++; - if (_companies_ainew[c->index].to_type == AI_CITY) { - if (_companies_ainew[c->index].temp > GetMaxTownIndex()) _companies_ainew[c->index].temp = 0; - } else { - if (_companies_ainew[c->index].temp > GetMaxIndustryIndex()) _companies_ainew[c->index].temp = 0; - } - - // Don't do an attempt if we are trying the same id as the last time... - if (_companies_ainew[c->index].last_id == _companies_ainew[c->index].temp) return; - _companies_ainew[c->index].last_id = _companies_ainew[c->index].temp; -} - - -// Check if there are not more than a certain amount of vehicles pointed to a certain -// station. This to prevent 10 busses going to one station, which gives... problems ;) -static bool AiNew_CheckVehicleStation(Company *c, Station *st) -{ - int count = 0; - Vehicle *v; - - // Also check if we don't have already a lot of busses to this city... - FOR_ALL_VEHICLES(v) { - if (v->owner == _current_company) { - const Order *order; - - FOR_VEHICLE_ORDERS(v, order) { - if (order->IsType(OT_GOTO_STATION) && GetStation(order->GetDestination()) == st) { - // This vehicle has this city in its list - count++; - } - } - } - } - - if (count > AI_CHECK_MAX_VEHICLE_PER_STATION) return false; - return true; -} - -// This function finds a good spot for a station -static void AiNew_State_FindStation(Company *c) -{ - TileIndex tile; - Station *st; - int count = 0; - EngineID i; - TileIndex new_tile = 0; - DiagDirection direction = DIAGDIR_NE; - Town *town = NULL; - assert(_companies_ainew[c->index].state == AI_STATE_FIND_STATION); - - if (_companies_ainew[c->index].from_tile == 0) { - // First we scan for a station in the from-city - if (_companies_ainew[c->index].from_type == AI_CITY) { - town = GetTown(_companies_ainew[c->index].from_ic); - tile = town->xy; - } else { - tile = GetIndustry(_companies_ainew[c->index].from_ic)->xy; - } - } else if (_companies_ainew[c->index].to_tile == 0) { - // Second we scan for a station in the to-city - if (_companies_ainew[c->index].to_type == AI_CITY) { - town = GetTown(_companies_ainew[c->index].to_ic); - tile = town->xy; - } else { - tile = GetIndustry(_companies_ainew[c->index].to_ic)->xy; - } - } else { - // Unsupported request - // Go to FIND_PATH - _companies_ainew[c->index].temp = -1; - _companies_ainew[c->index].state = AI_STATE_FIND_PATH; - return; - } - - // First, we are going to look at the stations that already exist inside the city - // If there is enough cargo left in the station, we take that station - // If that is not possible, and there are more than 2 stations in the city, abort - i = AiNew_PickVehicle(c); - // Euhmz, this should not happen _EVER_ - // Quit finding a route... - if (i == INVALID_ENGINE) { - _companies_ainew[c->index].state = AI_STATE_NOTHING; - return; - } - - FOR_ALL_STATIONS(st) { - if (st->owner == _current_company) { - if (_companies_ainew[c->index].tbt == AI_BUS && (FACIL_BUS_STOP & st->facilities) == FACIL_BUS_STOP) { - if (st->town == town) { - // Check how much cargo there is left in the station - if ((int)st->goods[_companies_ainew[c->index].cargo].cargo.Count() > RoadVehInfo(i)->capacity * AI_STATION_REUSE_MULTIPLER) { - if (AiNew_CheckVehicleStation(c, st)) { - // We did found a station that was good enough! - new_tile = st->xy; - direction = GetRoadStopDir(st->xy); - break; - } - } - count++; - } - } - } - } - // We are going to add a new station... - if (new_tile == 0) count++; - // No more than 2 stations allowed in a city - // This is because only the best 2 stations of one cargo do get any cargo - if (count > 2) { - _companies_ainew[c->index].state = AI_STATE_NOTHING; - return; - } - - if (new_tile == 0 && _companies_ainew[c->index].tbt == AI_BUS) { - uint x, y, i = 0; - CommandCost r; - uint best; - uint accepts[NUM_CARGO]; - TileIndex found_spot[AI_FINDSTATION_TILE_RANGE*AI_FINDSTATION_TILE_RANGE * 4]; - uint found_best[AI_FINDSTATION_TILE_RANGE*AI_FINDSTATION_TILE_RANGE * 4]; - // To find a good spot we scan a range from the center, a get the point - // where we get the most cargo and where it is buildable. - // TODO: also check for station of myself and make sure we are not - // taking eachothers passengers away (bad result when it does not) - for (x = TileX(tile) - AI_FINDSTATION_TILE_RANGE; x <= TileX(tile) + AI_FINDSTATION_TILE_RANGE; x++) { - for (y = TileY(tile) - AI_FINDSTATION_TILE_RANGE; y <= TileY(tile) + AI_FINDSTATION_TILE_RANGE; y++) { - new_tile = TileXY(x, y); - if (IsTileType(new_tile, MP_CLEAR) || IsTileType(new_tile, MP_TREES)) { - // This tile we can build on! - // Check acceptance - // XXX - Get the catchment area - GetAcceptanceAroundTiles(accepts, new_tile, 1, 1, 4); - // >> 3 == 0 means no cargo - if (accepts[_companies_ainew[c->index].cargo] >> 3 == 0) continue; - // See if we can build the station - r = AiNew_Build_Station(c, _companies_ainew[c->index].tbt, new_tile, 0, 0, 0, DC_QUERY_COST); - if (CmdFailed(r)) continue; - // We can build it, so add it to found_spot - found_spot[i] = new_tile; - found_best[i++] = accepts[_companies_ainew[c->index].cargo]; - } - } - } - - // If i is still zero, we did not find anything - if (i == 0) { - _companies_ainew[c->index].state = AI_STATE_NOTHING; - return; - } - - // Go through all the found_best and check which has the highest value - best = 0; - new_tile = 0; - - for (x = 0; x < i; x++) { - if (found_best[x] > best || - (found_best[x] == best && DistanceManhattan(tile, new_tile) > DistanceManhattan(tile, found_spot[x]))) { - new_tile = found_spot[x]; - best = found_best[x]; - } - } - - // See how much it is going to cost us... - r = AiNew_Build_Station(c, _companies_ainew[c->index].tbt, new_tile, 0, 0, 0, DC_QUERY_COST); - _companies_ainew[c->index].new_cost += r.GetCost(); - - direction = (DiagDirection)AI_PATHFINDER_NO_DIRECTION; - } else if (new_tile == 0 && _companies_ainew[c->index].tbt == AI_TRUCK) { - // Truck station locater works differently.. a station can be on any place - // as long as it is in range. So we give back code AI_STATION_RANGE - // so the pathfinder routine can work it out! - new_tile = AI_STATION_RANGE; - direction = (DiagDirection)AI_PATHFINDER_NO_DIRECTION; - } - - if (_companies_ainew[c->index].from_tile == 0) { - _companies_ainew[c->index].from_tile = new_tile; - _companies_ainew[c->index].from_direction = direction; - // Now we found thisone, go in for to_tile - return; - } else if (_companies_ainew[c->index].to_tile == 0) { - _companies_ainew[c->index].to_tile = new_tile; - _companies_ainew[c->index].to_direction = direction; - // K, done placing stations! - _companies_ainew[c->index].temp = -1; - _companies_ainew[c->index].state = AI_STATE_FIND_PATH; - return; - } -} - - -// We try to find a path between 2 points -static void AiNew_State_FindPath(Company *c) -{ - int r; - assert(_companies_ainew[c->index].state == AI_STATE_FIND_PATH); - - // First time, init some data - if (_companies_ainew[c->index].temp == -1) { - // Init path_info - if (_companies_ainew[c->index].from_tile == AI_STATION_RANGE) { - const Industry *i = GetIndustry(_companies_ainew[c->index].from_ic); - - // For truck routes we take a range around the industry - _companies_ainew[c->index].path_info.start_tile_tl = i->xy - TileDiffXY(1, 1); - _companies_ainew[c->index].path_info.start_tile_br = i->xy + TileDiffXY(i->width + 1, i->height + 1); - _companies_ainew[c->index].path_info.start_direction = _companies_ainew[c->index].from_direction; - } else { - _companies_ainew[c->index].path_info.start_tile_tl = _companies_ainew[c->index].from_tile; - _companies_ainew[c->index].path_info.start_tile_br = _companies_ainew[c->index].from_tile; - _companies_ainew[c->index].path_info.start_direction = _companies_ainew[c->index].from_direction; - } - - if (_companies_ainew[c->index].to_tile == AI_STATION_RANGE) { - const Industry *i = GetIndustry(_companies_ainew[c->index].to_ic); - - _companies_ainew[c->index].path_info.end_tile_tl = i->xy - TileDiffXY(1, 1); - _companies_ainew[c->index].path_info.end_tile_br = i->xy + TileDiffXY(i->width + 1, i->height + 1); - _companies_ainew[c->index].path_info.end_direction = _companies_ainew[c->index].to_direction; - } else { - _companies_ainew[c->index].path_info.end_tile_tl = _companies_ainew[c->index].to_tile; - _companies_ainew[c->index].path_info.end_tile_br = _companies_ainew[c->index].to_tile; - _companies_ainew[c->index].path_info.end_direction = _companies_ainew[c->index].to_direction; - } - - _companies_ainew[c->index].path_info.rail_or_road = (_companies_ainew[c->index].tbt == AI_TRAIN); - - // First, clean the pathfinder with our new begin and endpoints - clean_AyStar_AiPathFinder(_companies_ainew[c->index].pathfinder, &_companies_ainew[c->index].path_info); - - _companies_ainew[c->index].temp = 0; - } - - // Start the pathfinder - r = _companies_ainew[c->index].pathfinder->main(_companies_ainew[c->index].pathfinder); - switch (r) { - case AYSTAR_NO_PATH: - DEBUG(ai, 1, "No route found by pathfinder"); - // Start all over again - _companies_ainew[c->index].state = AI_STATE_NOTHING; - break; - - case AYSTAR_FOUND_END_NODE: // We found the end-point - _companies_ainew[c->index].temp = -1; - _companies_ainew[c->index].state = AI_STATE_FIND_DEPOT; - break; - - // In any other case, we are still busy finding the route - default: break; - } -} - - -// This function tries to locate a good place for a depot! -static void AiNew_State_FindDepot(Company *c) -{ - // To place the depot, we walk through the route, and if we find a lovely spot (MP_CLEAR, MP_TREES), we place it there.. - // Simple, easy, works! - // To make the depot stand in the middle of the route, we start from the center.. - // But first we walk through the route see if we can find a depot that is ours - // this keeps things nice ;) - int g, i; - CommandCost r; - DiagDirection j; - TileIndex tile; - assert(_companies_ainew[c->index].state == AI_STATE_FIND_DEPOT); - - _companies_ainew[c->index].depot_tile = 0; - - for (i = 2; i < _companies_ainew[c->index].path_info.route_length - 2; i++) { - tile = _companies_ainew[c->index].path_info.route[i]; - for (j = DIAGDIR_BEGIN; j < DIAGDIR_END; j++) { - TileIndex t = tile + TileOffsByDiagDir(j); - - if (IsRoadDepotTile(t) && - IsTileOwner(t, _current_company) && - GetRoadDepotDirection(t) == ReverseDiagDir(j)) { - _companies_ainew[c->index].depot_tile = t; - _companies_ainew[c->index].depot_direction = ReverseDiagDir(j); - _companies_ainew[c->index].state = AI_STATE_VERIFY_ROUTE; - return; - } - } - } - - // This routine let depot finding start in the middle, and work his way to the stations - // It makes depot placing nicer :) - i = _companies_ainew[c->index].path_info.route_length / 2; - g = 1; - while (i > 1 && i < _companies_ainew[c->index].path_info.route_length - 2) { - i += g; - g *= -1; - (g < 0 ? g-- : g++); - - if (_companies_ainew[c->index].path_info.route_extra[i] != 0 || _companies_ainew[c->index].path_info.route_extra[i + 1] != 0) { - // Bridge or tunnel.. we can't place a depot there - continue; - } - - tile = _companies_ainew[c->index].path_info.route[i]; - - for (j = DIAGDIR_BEGIN; j < DIAGDIR_END; j++) { - TileIndex t = tile + TileOffsByDiagDir(j); - - // It may not be placed on the road/rail itself - // And because it is not build yet, we can't see it on the tile.. - // So check the surrounding tiles :) - if (t == _companies_ainew[c->index].path_info.route[i - 1] || - t == _companies_ainew[c->index].path_info.route[i + 1]) { - continue; - } - // Not around a bridge? - if (_companies_ainew[c->index].path_info.route_extra[i] != 0) continue; - if (IsTileType(tile, MP_TUNNELBRIDGE)) continue; - // Is the terrain clear? - if (IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)) { - // If the current tile is on a slope then we do not allow this - if (GetTileSlope(tile, NULL) != SLOPE_FLAT) continue; - // Check if everything went okay.. - r = AiNew_Build_Depot(c, t, ReverseDiagDir(j), 0); - if (CmdFailed(r)) continue; - // Found a spot! - _companies_ainew[c->index].new_cost += r.GetCost(); - _companies_ainew[c->index].depot_tile = t; - _companies_ainew[c->index].depot_direction = ReverseDiagDir(j); // Reverse direction - _companies_ainew[c->index].state = AI_STATE_VERIFY_ROUTE; - return; - } - } - } - - // Failed to find a depot? - _companies_ainew[c->index].state = AI_STATE_NOTHING; -} - - -// This function calculates how many vehicles there are needed on this -// traject. -// It works pretty simple: get the length, see how much we move around -// and hussle that, and you know how many vehicles there are needed. -// It returns the cost for the vehicles -static int AiNew_HowManyVehicles(Company *c) -{ - if (_companies_ainew[c->index].tbt == AI_BUS) { - // For bus-routes we look at the time before we are back in the station - EngineID i; - int length, tiles_a_day; - int amount; - i = AiNew_PickVehicle(c); - if (i == INVALID_ENGINE) return 0; - // Passenger run.. how long is the route? - length = _companies_ainew[c->index].path_info.route_length; - // Calculating tiles a day a vehicle moves is not easy.. this is how it must be done! - tiles_a_day = RoadVehInfo(i)->max_speed * DAY_TICKS / 256 / 16; - if (tiles_a_day == 0) tiles_a_day = 1; - // We want a vehicle in a station once a month at least, so, calculate it! - // (the * 2 is because we have 2 stations ;)) - amount = length * 2 * 2 / tiles_a_day / 30; - if (amount == 0) amount = 1; - return amount; - } else if (_companies_ainew[c->index].tbt == AI_TRUCK) { - // For truck-routes we look at the cargo - EngineID i; - int length, amount, tiles_a_day; - int max_cargo; - i = AiNew_PickVehicle(c); - if (i == INVALID_ENGINE) return 0; - // Passenger run.. how long is the route? - length = _companies_ainew[c->index].path_info.route_length; - // Calculating tiles a day a vehicle moves is not easy.. this is how it must be done! - tiles_a_day = RoadVehInfo(i)->max_speed * DAY_TICKS / 256 / 16; - if (tiles_a_day == 0) tiles_a_day = 1; - if (_companies_ainew[c->index].from_deliver) { - max_cargo = GetIndustry(_companies_ainew[c->index].from_ic)->last_month_production[0]; - } else { - max_cargo = GetIndustry(_companies_ainew[c->index].to_ic)->last_month_production[0]; - } - - // This is because moving 60% is more than we can dream of! - max_cargo *= 6; - max_cargo /= 10; - // We want all the cargo to be gone in a month.. so, we know the cargo it delivers - // we know what the vehicle takes with him, and we know the time it takes him - // to get back here.. now let's do some math! - amount = 2 * length * max_cargo / tiles_a_day / 30 / RoadVehInfo(i)->capacity; - amount += 1; - return amount; - } else { - // Currently not supported - return 0; - } -} - - -// This function checks: -// - If the route went okay -// - Calculates the amount of money needed to build the route -// - Calculates how much vehicles needed for the route -static void AiNew_State_VerifyRoute(Company *c) -{ - int res, i; - assert(_companies_ainew[c->index].state == AI_STATE_VERIFY_ROUTE); - - // Let's calculate the cost of the path.. - // new_cost already contains the cost of the stations - _companies_ainew[c->index].path_info.position = -1; - - do { - _companies_ainew[c->index].path_info.position++; - _companies_ainew[c->index].new_cost += AiNew_Build_RoutePart(c, &_companies_ainew[c->index].path_info, DC_QUERY_COST).GetCost(); - } while (_companies_ainew[c->index].path_info.position != -2); - - // Now we know the price of build station + path. Now check how many vehicles - // we need and what the price for that will be - res = AiNew_HowManyVehicles(c); - // If res == 0, no vehicle was found, or an other problem did occour - if (res == 0) { - _companies_ainew[c->index].state = AI_STATE_NOTHING; - return; - } - _companies_ainew[c->index].amount_veh = res; - _companies_ainew[c->index].cur_veh = 0; - - // Check how much it it going to cost us.. - for (i = 0; i < res; i++) { - _companies_ainew[c->index].new_cost += AiNew_Build_Vehicle(c, 0, DC_QUERY_COST).GetCost(); - } - - // Now we know how much the route is going to cost us - // Check if we have enough money for it! - if (_companies_ainew[c->index].new_cost > c->money - AI_MINIMUM_MONEY) { - // Too bad.. - DEBUG(ai, 1, "Insufficient funds to build route (%" OTTD_PRINTF64 "d)", (int64)_companies_ainew[c->index].new_cost); - _companies_ainew[c->index].state = AI_STATE_NOTHING; - return; - } - - // Now we can build the route, check the direction of the stations! - if (_companies_ainew[c->index].from_direction == AI_PATHFINDER_NO_DIRECTION) { - _companies_ainew[c->index].from_direction = AiNew_GetDirection(_companies_ainew[c->index].path_info.route[_companies_ainew[c->index].path_info.route_length - 1], _companies_ainew[c->index].path_info.route[_companies_ainew[c->index].path_info.route_length - 2]); - } - if (_companies_ainew[c->index].to_direction == AI_PATHFINDER_NO_DIRECTION) { - _companies_ainew[c->index].to_direction = AiNew_GetDirection(_companies_ainew[c->index].path_info.route[0], _companies_ainew[c->index].path_info.route[1]); - } - if (_companies_ainew[c->index].from_tile == AI_STATION_RANGE) - _companies_ainew[c->index].from_tile = _companies_ainew[c->index].path_info.route[_companies_ainew[c->index].path_info.route_length - 1]; - if (_companies_ainew[c->index].to_tile == AI_STATION_RANGE) - _companies_ainew[c->index].to_tile = _companies_ainew[c->index].path_info.route[0]; - - _companies_ainew[c->index].state = AI_STATE_BUILD_STATION; - _companies_ainew[c->index].temp = 0; - - DEBUG(ai, 1, "The route is set and buildable, building 0x%X to 0x%X...", _companies_ainew[c->index].from_tile, _companies_ainew[c->index].to_tile); -} - - -// Build the stations -static void AiNew_State_BuildStation(Company *c) -{ - CommandCost res; - assert(_companies_ainew[c->index].state == AI_STATE_BUILD_STATION); - if (_companies_ainew[c->index].temp == 0) { - if (!IsTileType(_companies_ainew[c->index].from_tile, MP_STATION)) - res = AiNew_Build_Station(c, _companies_ainew[c->index].tbt, _companies_ainew[c->index].from_tile, 0, 0, _companies_ainew[c->index].from_direction, DC_EXEC); - } else { - if (!IsTileType(_companies_ainew[c->index].to_tile, MP_STATION)) - res = AiNew_Build_Station(c, _companies_ainew[c->index].tbt, _companies_ainew[c->index].to_tile, 0, 0, _companies_ainew[c->index].to_direction, DC_EXEC); - _companies_ainew[c->index].state = AI_STATE_BUILD_PATH; - } - if (CmdFailed(res)) { - DEBUG(ai, 0, "[BuildStation] station could not be built (0x%X)", _companies_ainew[c->index].to_tile); - _companies_ainew[c->index].state = AI_STATE_NOTHING; - // If the first station _was_ build, destroy it - if (_companies_ainew[c->index].temp != 0) - AI_DoCommand(_companies_ainew[c->index].from_tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); - return; - } - _companies_ainew[c->index].temp++; -} - - -// Build the path -static void AiNew_State_BuildPath(Company *c) -{ - assert(_companies_ainew[c->index].state == AI_STATE_BUILD_PATH); - // _companies_ainew[c->index].temp is set to -1 when this function is called for the first time - if (_companies_ainew[c->index].temp == -1) { - DEBUG(ai, 1, "Starting to build new path"); - // Init the counter - _companies_ainew[c->index].counter = (4 - _settings_game.difficulty.competitor_speed) * AI_BUILDPATH_PAUSE + 1; - // Set the position to the startingplace (-1 because in a minute we do ++) - _companies_ainew[c->index].path_info.position = -1; - // And don't do this again - _companies_ainew[c->index].temp = 0; - } - // Building goes very fast on normal rate, so we are going to slow it down.. - // By let the counter count from AI_BUILDPATH_PAUSE to 0, we have a nice way :) - if (--_companies_ainew[c->index].counter != 0) return; - _companies_ainew[c->index].counter = (4 - _settings_game.difficulty.competitor_speed) * AI_BUILDPATH_PAUSE + 1; - - // Increase the building position - _companies_ainew[c->index].path_info.position++; - // Build route - AiNew_Build_RoutePart(c, &_companies_ainew[c->index].path_info, DC_EXEC); - if (_companies_ainew[c->index].path_info.position == -2) { - // This means we are done building! - - if (_companies_ainew[c->index].tbt == AI_TRUCK && !_settings_game.pf.roadveh_queue) { - // If they not queue, they have to go up and down to try again at a station... - // We don't want that, so try building some road left or right of the station - DiagDirection dir1, dir2, dir3; - TileIndex tile; - CommandCost ret; - for (int i = 0; i < 2; i++) { - if (i == 0) { - tile = _companies_ainew[c->index].from_tile + TileOffsByDiagDir(_companies_ainew[c->index].from_direction); - dir1 = ChangeDiagDir(_companies_ainew[c->index].from_direction, DIAGDIRDIFF_90LEFT); - dir2 = ChangeDiagDir(_companies_ainew[c->index].from_direction, DIAGDIRDIFF_90RIGHT); - dir3 = _companies_ainew[c->index].from_direction; - } else { - tile = _companies_ainew[c->index].to_tile + TileOffsByDiagDir(_companies_ainew[c->index].to_direction); - dir1 = ChangeDiagDir(_companies_ainew[c->index].to_direction, DIAGDIRDIFF_90LEFT); - dir2 = ChangeDiagDir(_companies_ainew[c->index].to_direction, DIAGDIRDIFF_90RIGHT); - dir3 = _companies_ainew[c->index].to_direction; - } - - ret = AI_DoCommand(tile, DiagDirToRoadBits(ReverseDiagDir(dir1)), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - if (CmdSucceeded(ret)) { - TileIndexDiff offset = TileOffsByDiagDir(dir1); - if (IsTileType(tile + offset, MP_CLEAR) || IsTileType(tile + offset, MP_TREES)) { - ret = AI_DoCommand(tile + offset, AiNew_GetRoadDirection(tile, tile + offset, tile + offset + offset), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - if (CmdSucceeded(ret)) { - if (IsTileType(tile + offset + offset, MP_CLEAR) || IsTileType(tile + offset + offset, MP_TREES)) - AI_DoCommand(tile + offset + offset, AiNew_GetRoadDirection(tile + offset, tile + offset + offset, tile + offset + offset + offset), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - } - } - } - - ret = AI_DoCommand(tile, DiagDirToRoadBits(ReverseDiagDir(dir2)), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - if (CmdSucceeded(ret)) { - TileIndexDiff offset = TileOffsByDiagDir(dir2); - if (IsTileType(tile + offset, MP_CLEAR) || IsTileType(tile + offset, MP_TREES)) { - ret = AI_DoCommand(tile + offset, AiNew_GetRoadDirection(tile, tile + offset, tile + offset + offset), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - if (CmdSucceeded(ret)) { - if (IsTileType(tile + offset + offset, MP_CLEAR) || IsTileType(tile + offset + offset, MP_TREES)) - AI_DoCommand(tile + offset + offset, AiNew_GetRoadDirection(tile + offset, tile + offset + offset, tile + offset + offset + offset), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - } - } - } - - ret = AI_DoCommand(tile, DiagDirToRoadBits(dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - if (CmdSucceeded(ret)) { - TileIndexDiff offset = TileOffsByDiagDir(dir3); - if (IsTileType(tile + offset, MP_CLEAR) || IsTileType(tile + offset, MP_TREES)) { - ret = AI_DoCommand(tile + offset, AiNew_GetRoadDirection(tile, tile + offset, tile + offset + offset), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - if (CmdSucceeded(ret)) { - if (IsTileType(tile + offset + offset, MP_CLEAR) || IsTileType(tile + offset + offset, MP_TREES)) - AI_DoCommand(tile + offset + offset, AiNew_GetRoadDirection(tile + offset, tile + offset + offset, tile + offset + offset + offset), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - } - } - } - } - } - - DEBUG(ai, 1, "Finished building path, cost: %" OTTD_PRINTF64 "d", (int64)_companies_ainew[c->index].new_cost); - _companies_ainew[c->index].state = AI_STATE_BUILD_DEPOT; - } -} - - -// Builds the depot -static void AiNew_State_BuildDepot(Company *c) -{ - CommandCost res; - assert(_companies_ainew[c->index].state == AI_STATE_BUILD_DEPOT); - - if (IsRoadDepotTile(_companies_ainew[c->index].depot_tile)) { - if (IsTileOwner(_companies_ainew[c->index].depot_tile, _current_company)) { - // The depot is already built - _companies_ainew[c->index].state = AI_STATE_BUILD_VEHICLE; - return; - } else { - // There is a depot, but not of our team! :( - _companies_ainew[c->index].state = AI_STATE_NOTHING; - return; - } - } - - // There is a bus on the tile we want to build road on... idle till he is gone! (BAD PERSON! :p) - if (!EnsureNoVehicleOnGround(_companies_ainew[c->index].depot_tile + TileOffsByDiagDir(_companies_ainew[c->index].depot_direction))) - return; - - res = AiNew_Build_Depot(c, _companies_ainew[c->index].depot_tile, _companies_ainew[c->index].depot_direction, DC_EXEC); - if (CmdFailed(res)) { - DEBUG(ai, 0, "[BuildDepot] depot could not be built (0x%X)", _companies_ainew[c->index].depot_tile); - _companies_ainew[c->index].state = AI_STATE_NOTHING; - return; - } - - _companies_ainew[c->index].state = AI_STATE_BUILD_VEHICLE; - _companies_ainew[c->index].idle = 10; - _companies_ainew[c->index].veh_main_id = INVALID_VEHICLE; -} - - -// Build vehicles -static void AiNew_State_BuildVehicle(Company *c) -{ - CommandCost res; - assert(_companies_ainew[c->index].state == AI_STATE_BUILD_VEHICLE); - - // Check if we need to build a vehicle - if (_companies_ainew[c->index].amount_veh == 0) { - // Nope, we are done! - // This means: we are all done! The route is open.. go back to NOTHING - // He will idle some time and it will all start over again.. :) - _companies_ainew[c->index].state = AI_STATE_ACTION_DONE; - return; - } - if (--_companies_ainew[c->index].idle != 0) return; - // It is realistic that the AI can only build 1 vehicle a day.. - // This makes sure of that! - _companies_ainew[c->index].idle = AI_BUILD_VEHICLE_TIME_BETWEEN; - - // Build the vehicle - res = AiNew_Build_Vehicle(c, _companies_ainew[c->index].depot_tile, DC_EXEC); - if (CmdFailed(res)) { - // This happens when the AI can't build any more vehicles! - _companies_ainew[c->index].state = AI_STATE_NOTHING; - return; - } - // Increase the current counter - _companies_ainew[c->index].cur_veh++; - // Decrease the total counter - _companies_ainew[c->index].amount_veh--; - // Go give some orders! - _companies_ainew[c->index].state = AI_STATE_WAIT_FOR_BUILD; -} - - -// Put the stations in the order list -static void AiNew_State_GiveOrders(Company *c) -{ - int idx; - Order order; - - assert(_companies_ainew[c->index].state == AI_STATE_GIVE_ORDERS); - - if (_companies_ainew[c->index].veh_main_id != INVALID_VEHICLE) { - AI_DoCommand(0, _companies_ainew[c->index].veh_id + (_companies_ainew[c->index].veh_main_id << 16), CO_SHARE, DC_EXEC, CMD_CLONE_ORDER); - - _companies_ainew[c->index].state = AI_STATE_START_VEHICLE; - return; - } else { - _companies_ainew[c->index].veh_main_id = _companies_ainew[c->index].veh_id; - } - - // Very handy for AI, goto depot.. but yeah, it needs to be activated ;) - if (_settings_game.order.gotodepot) { - idx = 0; - order.MakeGoToDepot(GetDepotByTile(_companies_ainew[c->index].depot_tile)->index, ODTFB_PART_OF_ORDERS); - AI_DoCommand(0, _companies_ainew[c->index].veh_id + (idx << 16), order.Pack(), DC_EXEC, CMD_INSERT_ORDER); - } - - idx = 0; - order.MakeGoToStation(GetStationIndex(_companies_ainew[c->index].to_tile)); - if (_companies_ainew[c->index].tbt == AI_TRUCK && _companies_ainew[c->index].to_deliver) order.SetLoadType(OLFB_FULL_LOAD); - AI_DoCommand(0, _companies_ainew[c->index].veh_id + (idx << 16), order.Pack(), DC_EXEC, CMD_INSERT_ORDER); - - idx = 0; - order.MakeGoToStation(GetStationIndex(_companies_ainew[c->index].from_tile)); - if (_companies_ainew[c->index].tbt == AI_TRUCK && _companies_ainew[c->index].from_deliver) order.SetLoadType(OLFB_FULL_LOAD); - AI_DoCommand(0, _companies_ainew[c->index].veh_id + (idx << 16), order.Pack(), DC_EXEC, CMD_INSERT_ORDER); - - // Start the engines! - _companies_ainew[c->index].state = AI_STATE_START_VEHICLE; -} - - -// Start the vehicle -static void AiNew_State_StartVehicle(Company *c) -{ - assert(_companies_ainew[c->index].state == AI_STATE_START_VEHICLE); - - // Skip the first order if it is a second vehicle - // This to make vehicles go different ways.. - if (_companies_ainew[c->index].cur_veh & 1) - AI_DoCommand(0, _companies_ainew[c->index].veh_id, 1, DC_EXEC, CMD_SKIP_TO_ORDER); - - // 3, 2, 1... go! (give START_STOP command ;)) - AI_DoCommand(0, _companies_ainew[c->index].veh_id, 0, DC_EXEC, CMD_START_STOP_VEHICLE); - // Try to build an other vehicle (that function will stop building when needed) - _companies_ainew[c->index].idle = 10; - _companies_ainew[c->index].state = AI_STATE_BUILD_VEHICLE; -} - - -// Repays money -static void AiNew_State_RepayMoney(Company *c) -{ - uint i; - - for (i = 0; i < AI_LOAN_REPAY; i++) { - AI_DoCommand(0, 0, 0, DC_EXEC, CMD_DECREASE_LOAN); - } - _companies_ainew[c->index].state = AI_STATE_ACTION_DONE; -} - - -static void AiNew_CheckVehicle(Company *c, Vehicle *v) -{ - // When a vehicle is under the 6 months, we don't check for anything - if (v->age < 180) return; - - // When a vehicle is older than 1 year, it should make money... - if (v->age > 360) { - // If both years together are not more than AI_MINIMUM_ROUTE_PROFIT, - // it is not worth the line I guess... - if (v->profit_last_year + v->profit_this_year < (Money)256 * AI_MINIMUM_ROUTE_PROFIT || - (v->reliability * 100 >> 16) < 40) { - // There is a possibility that the route is fucked up... - if (v->cargo.DaysInTransit() > AI_VEHICLE_LOST_DAYS) { - // The vehicle is lost.. check the route, or else, get the vehicle - // back to a depot - // TODO: make this piece of code - } - - - // We are already sending him back - if (AiNew_GetSpecialVehicleFlag(c, v) & AI_VEHICLEFLAG_SELL) { - if (v->type == VEH_ROAD && IsRoadDepotTile(v->tile) && - (v->vehstatus & VS_STOPPED)) { - // We are at the depot, sell the vehicle - AI_DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH); - } - return; - } - - if (!AiNew_SetSpecialVehicleFlag(c, v, AI_VEHICLEFLAG_SELL)) return; - { - CommandCost ret; - if (v->type == VEH_ROAD) - ret = AI_DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_ROADVEH_TO_DEPOT); - // This means we can not find a depot :s - // if (CmdFailed(ret)) - } - } - } -} - - -// Checks all vehicles if they are still valid and make money and stuff -static void AiNew_State_CheckAllVehicles(Company *c) -{ - Vehicle *v; - - FOR_ALL_VEHICLES(v) { - if (v->owner != c->index) continue; - // Currently, we only know how to handle road-vehicles - if (v->type != VEH_ROAD) continue; - - AiNew_CheckVehicle(c, v); - } - - _companies_ainew[c->index].state = AI_STATE_ACTION_DONE; -} - - -// Using the technique simular to the original AI -// Keeps things logical -// It really should be in the same order as the AI_STATE's are! -static AiNew_StateFunction * const _ainew_state[] = { - NULL, - AiNew_State_FirstTime, - AiNew_State_Nothing, - AiNew_State_WakeUp, - AiNew_State_LocateRoute, - AiNew_State_FindStation, - AiNew_State_FindPath, - AiNew_State_FindDepot, - AiNew_State_VerifyRoute, - AiNew_State_BuildStation, - AiNew_State_BuildPath, - AiNew_State_BuildDepot, - AiNew_State_BuildVehicle, - NULL, - AiNew_State_GiveOrders, - AiNew_State_StartVehicle, - AiNew_State_RepayMoney, - AiNew_State_CheckAllVehicles, - AiNew_State_ActionDone, - NULL, -}; - -static void AiNew_OnTick(Company *c) -{ - if (_ainew_state[_companies_ainew[c->index].state] != NULL) - _ainew_state[_companies_ainew[c->index].state](c); -} - - -void AiNewDoGameLoop(Company *c) -{ - if (_companies_ainew[c->index].state == AI_STATE_STARTUP) { - // The AI just got alive! - _companies_ainew[c->index].state = AI_STATE_FIRST_TIME; - _companies_ainew[c->index].tick = 0; - - // Only startup the AI - return; - } - - // We keep a ticker. We use it for competitor_speed - _companies_ainew[c->index].tick++; - - // If we come here, we can do a tick.. do so! - AiNew_OnTick(c); -} diff --git a/src/ai/trolly/trolly.h b/src/ai/trolly/trolly.h deleted file mode 100644 index 60180694c..000000000 --- a/src/ai/trolly/trolly.h +++ /dev/null @@ -1,344 +0,0 @@ -/* $Id$ */ - -/** @file trolly.h Functions/defines related to the trolly AI. */ - -#ifndef AI_TROLLY_H -#define AI_TROLLY_H - -#include "../../aystar.h" -#include "../../company_type.h" -#include "../../vehicle_type.h" -#include "../../date_type.h" -#include "../../engine_type.h" -#include "../../direction_type.h" - -/* - * These defines can be altered to change the behavoir of the AI - * - * WARNING: - * This can also alter the AI in a negative way. I will never claim these settings - * are perfect, but don't change them if you don't know what the effect is. - */ - -// How many times it the H multiplied. The higher, the more it will go straight to the -// end point. The lower, how more it will find the route with the lowest cost. -// also: the lower, the longer it takes before route is calculated.. -#define AI_PATHFINDER_H_MULTIPLER 100 - -// How many loops may AyStar do before it stops -// 0 = infinite -#define AI_PATHFINDER_LOOPS_PER_TICK 5 - -// How long may the AI search for one route? -// 0 = infinite -// This number is the number of tiles tested. -// It takes (AI_PATHFINDER_MAX_SEARCH_NODES / AI_PATHFINDER_LOOPS_PER_TICK) ticks -// to get here.. with 5000 / 10 = 500. 500 / 74 (one day) = 8 days till it aborts -// (that is: if the AI is on VERY FAST! :p -#define AI_PATHFINDER_MAX_SEARCH_NODES 5000 - -// If you enable this, the AI is not allowed to make 90degree turns -#define AI_PATHFINDER_NO_90DEGREES_TURN - -// Below are defines for the g-calculation - -// Standard penalty given to a tile -#define AI_PATHFINDER_PENALTY 150 -// The penalty given to a tile that is going up -#define AI_PATHFINDER_TILE_GOES_UP_PENALTY 450 -// The penalty given to a tile which would have to use fundation -#define AI_PATHFINDER_FOUNDATION_PENALTY 100 -// Changing direction is a penalty, to prevent curved ways (with that: slow ways) -#define AI_PATHFINDER_DIRECTION_CHANGE_PENALTY 200 -// Same penalty, only for when road already exists -#define AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY 50 -// A diagonal track cost the same as a straigh, but a diagonal is faster... so give -// a bonus for using diagonal track -#ifdef AI_PATHFINDER_NO_90DEGREES_TURN -#define AI_PATHFINDER_DIAGONAL_BONUS 95 -#else -#define AI_PATHFINDER_DIAGONAL_BONUS 75 -#endif -// If a roadblock already exists, it gets a bonus -#define AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS 140 -// To prevent 3 direction changes in 3 tiles, this penalty is given in such situation -#define AI_PATHFINDER_CURVE_PENALTY 200 - -// Penalty a bridge gets per length -#define AI_PATHFINDER_BRIDGE_PENALTY 180 -// The penalty for a bridge going up -#define AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY 1000 - -// Tunnels are expensive... -// Because of that, every tile the cost is increased with 1/8th of his value -// This is also true if you are building a tunnel yourself -#define AI_PATHFINDER_TUNNEL_PENALTY 350 - -/* - * Ai_New defines - */ - -// How long may we search cities and industry for a new route? -#define AI_LOCATE_ROUTE_MAX_COUNTER 200 - -// How many days must there be between building the first station and the second station -// within one city. This number is in days and should be more than 4 months. -#define AI_CHECKCITY_DATE_BETWEEN 180 - -// How many cargo is needed for one station in a city? -#define AI_CHECKCITY_CARGO_PER_STATION 60 -// How much cargo must there not be used in a city before we can build a new station? -#define AI_CHECKCITY_NEEDED_CARGO 50 -// When there is already a station which takes the same good and the rating of that -// city is higher than this numer, we are not going to attempt to build anything -// there -#define AI_CHECKCITY_CARGO_RATING 50 -// But, there is a chance of 1 out of this number, that we do ;) -#define AI_CHECKCITY_CARGO_RATING_CHANCE 5 -// If a city is too small to contain a station, there is a small chance -// that we still do so.. just to make the city bigger! -#define AI_CHECKCITY_CITY_CHANCE 5 - -// This number indicates for every unit of cargo, how many tiles two stations maybe be away -// from eachother. In other words: if we have 120 units of cargo in one station, and 120 units -// of the cargo in the other station, both stations can be 96 units away from eachother, if the -// next number is 0.4. -#define AI_LOCATEROUTE_BUS_CARGO_DISTANCE 0.4 -#define AI_LOCATEROUTE_TRUCK_CARGO_DISTANCE 0.7 -// In whole tiles, the minimum distance for a truck route -#define AI_LOCATEROUTE_TRUCK_MIN_DISTANCE 30 - -// The amount of tiles in a square from -X to +X that is scanned for a station spot -// (so if this number is 10, 20x20 = 400 tiles are scanned for _the_ perfect spot -// Safe values are between 15 and 5 -#define AI_FINDSTATION_TILE_RANGE 10 - -// Building on normal speed goes very fast. Idle this amount of ticks between every -// building part. It is calculated like this: (4 - competitor_speed) * num + 1 -// where competitor_speed is between 0 (very slow) to 4 (very fast) -#define AI_BUILDPATH_PAUSE 10 - -// Minimum % of reliabilty a vehicle has to have before the AI buys it -#define AI_VEHICLE_MIN_RELIABILTY 60 - -// The minimum amount of money a company should always have -#define AI_MINIMUM_MONEY 15000 - -// If the most cheap route is build, how much is it going to cost.. -// This is to prevent the AI from trying to build a route which can not be paid for -#define AI_MINIMUM_BUS_ROUTE_MONEY 25000 -#define AI_MINIMUM_TRUCK_ROUTE_MONEY 35000 - -// The minimum amount of money before we are going to repay any money -#define AI_MINIMUM_LOAN_REPAY_MONEY 40000 -// How many repays do we do if we have enough money to do so? -// Every repay is 10000 -#define AI_LOAN_REPAY 2 -// How much income must we have before paying back a loan? Month-based (and looked at the last month) -#define AI_MINIMUM_INCOME_FOR_LOAN 7000 - -// If there is <num> time as much cargo in the station then the vehicle can handle -// reuse the station instead of building a new one! -#define AI_STATION_REUSE_MULTIPLER 2 - -// No more than this amount of vehicles per station.. -#define AI_CHECK_MAX_VEHICLE_PER_STATION 10 - -// How many thick between building 2 vehicles -#define AI_BUILD_VEHICLE_TIME_BETWEEN DAY_TICKS - -// How many days must there between vehicle checks -// The more often, the less non-money-making lines there will be -// but the unfair it may seem to a human company -#define AI_DAYS_BETWEEN_VEHICLE_CHECKS 30 - -// How money profit does a vehicle needs to make to stay in order -// This is the profit of this year + profit of last year -// But also for vehicles that are just one year old. In other words: -// Vehicles of 2 years do easier meet this setting than vehicles -// of one year. This is a very good thing. New vehicles are filtered, -// while old vehicles stay longer, because we do get less in return. -#define AI_MINIMUM_ROUTE_PROFIT 1000 - -// A vehicle is considered lost when he his cargo is more than 180 days old -#define AI_VEHICLE_LOST_DAYS 180 - -// How many times may the AI try to find a route before it gives up -#define AI_MAX_TRIES_FOR_SAME_ROUTE 8 - -/* - * End of defines - */ - -// This stops 90degrees curves -static const byte _illegal_curves[6] = { - 255, 255, // Horz and vert, don't have the effect - 5, // upleft and upright are not valid - 4, // downright and downleft are not valid - 2, // downleft and upleft are not valid - 3, // upright and downright are not valid -}; - -enum { - AI_STATE_STARTUP = 0, - AI_STATE_FIRST_TIME, - AI_STATE_NOTHING, - AI_STATE_WAKE_UP, - AI_STATE_LOCATE_ROUTE, - AI_STATE_FIND_STATION, - AI_STATE_FIND_PATH, - AI_STATE_FIND_DEPOT, - AI_STATE_VERIFY_ROUTE, - AI_STATE_BUILD_STATION, - AI_STATE_BUILD_PATH, - AI_STATE_BUILD_DEPOT, - AI_STATE_BUILD_VEHICLE, - AI_STATE_WAIT_FOR_BUILD, - AI_STATE_GIVE_ORDERS, - AI_STATE_START_VEHICLE, - AI_STATE_REPAY_MONEY, - AI_STATE_CHECK_ALL_VEHICLES, - AI_STATE_ACTION_DONE, - AI_STATE_STOP, // Temporary function to stop the AI -}; - -// Used for tbt (train/bus/truck) -enum { - AI_TRAIN = 0, - AI_BUS, - AI_TRUCK, -}; - -enum { - AI_ACTION_NONE = 0, - AI_ACTION_BUS_ROUTE, - AI_ACTION_TRUCK_ROUTE, - AI_ACTION_REPAY_LOAN, - AI_ACTION_CHECK_ALL_VEHICLES, -}; - -// Used for from_type/to_type -enum { - AI_NO_TYPE = 0, - AI_CITY, - AI_INDUSTRY, -}; - -// Flags for in the vehicle -enum { - AI_VEHICLEFLAG_SELL = 1, - // Remember, flags must be in power of 2 -}; - -#define AI_NO_CARGO 0xFF // Means that there is no cargo defined yet (used for industry) -#define AI_NEED_CARGO 0xFE // Used when the AI needs to find out a cargo for the route -#define AI_STATION_RANGE TileXY(MapMaxX(), MapMaxY()) - -#define AI_PATHFINDER_NO_DIRECTION (byte)-1 - -// Flags used in user_data -#define AI_PATHFINDER_FLAG_BRIDGE 1 -#define AI_PATHFINDER_FLAG_TUNNEL 2 - -typedef void AiNew_StateFunction(Company *c); - -// ai_new.c -void AiNewDoGameLoop(Company *c); - -struct Ai_PathFinderInfo { - TileIndex start_tile_tl; ///< tl = top-left - TileIndex start_tile_br; ///< br = bottom-right - TileIndex end_tile_tl; ///< tl = top-left - TileIndex end_tile_br; ///< br = bottom-right - DiagDirection start_direction; ///< 0 to 3 or AI_PATHFINDER_NO_DIRECTION - DiagDirection end_direction; ///< 0 to 3 or AI_PATHFINDER_NO_DIRECTION - - TileIndex route[500]; - byte route_extra[500]; ///< Some extra information about the route like bridge/tunnel - int route_length; - int position; ///< Current position in the build-path, needed to build the path - - bool rail_or_road; ///< true = rail, false = road -}; - -// ai_pathfinder.c -AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFinderInfo); -void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo); - -// ai_shared.c -int AiNew_GetRailDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c); -int AiNew_GetRoadDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c); -DiagDirection AiNew_GetDirection(TileIndex tile_a, TileIndex tile_b); -bool AiNew_SetSpecialVehicleFlag(Company *c, Vehicle *v, uint flag); -uint AiNew_GetSpecialVehicleFlag(Company *c, Vehicle *v); - -// ai_build.c -bool AiNew_Build_CompanyHQ(Company *c, TileIndex tile); -CommandCost AiNew_Build_Station(Company *c, byte type, TileIndex tile, byte length, byte numtracks, byte direction, byte flag); -CommandCost AiNew_Build_Bridge(Company *c, TileIndex tile_a, TileIndex tile_b, byte flag); -CommandCost AiNew_Build_RoutePart(Company *c, Ai_PathFinderInfo *PathFinderInfo, byte flag); -EngineID AiNew_PickVehicle(Company *c); -CommandCost AiNew_Build_Vehicle(Company *c, TileIndex tile, byte flag); -CommandCost AiNew_Build_Depot(Company *c, TileIndex tile, DiagDirection direction, byte flag); - -/* The amount of memory reserved for the AI-special-vehicles */ -#define AI_MAX_SPECIAL_VEHICLES 100 - -struct Ai_SpecialVehicle { - VehicleID veh_id; - uint32 flag; -}; - -struct CompanyAiNew { - uint8 state; - uint tick; - uint idle; - - int temp; ///< A value used in more than one function, but it just temporary - ///< The use is pretty simple: with this we can 'think' about stuff - ///< in more than one tick, and more than one AI. A static will not - ///< do, because they are not saved. This way, the AI is almost human ;) - int counter; ///< For the same reason as temp, we have counter. It can count how - ///< long we are trying something, and just abort if it takes too long - - /* Pathfinder stuff */ - Ai_PathFinderInfo path_info; - AyStar *pathfinder; - - /* Route stuff */ - - CargoID cargo; - byte tbt; ///< train/bus/truck 0/1/2 AI_TRAIN/AI_BUS/AI_TRUCK - Money new_cost; - - byte action; - - int last_id; ///< here is stored the last id of the searched city/industry - Date last_vehiclecheck_date; // Used in CheckVehicle - Ai_SpecialVehicle special_vehicles[AI_MAX_SPECIAL_VEHICLES]; ///< Some vehicles have some special flags - - TileIndex from_tile; - TileIndex to_tile; - - DiagDirectionByte from_direction; - DiagDirectionByte to_direction; - - bool from_deliver; ///< True if this is the station that GIVES cargo - bool to_deliver; - - TileIndex depot_tile; - DiagDirectionByte depot_direction; - - byte amount_veh; ///< How many vehicles we are going to build in this route - byte cur_veh; ///< How many vehicles did we bought? - VehicleID veh_id; ///< Used when bought a vehicle - VehicleID veh_main_id; ///< The ID of the first vehicle, for shared copy - - int from_ic; ///< ic = industry/city. This is the ID of them - byte from_type; ///< AI_NO_TYPE/AI_CITY/AI_INDUSTRY - int to_ic; - byte to_type; -}; -extern CompanyAiNew _companies_ainew[MAX_COMPANIES]; - -#endif /* AI_TROLLY_H */ |