truelight@2395: /* $Id$ */ truelight@2395: rubidium@9111: /** @file ai.cpp Base for all AIs. */ rubidium@9111: truelight@2395: #include "../stdafx.h" truelight@2395: #include "../openttd.h" truelight@2395: #include "../variables.h" rubidium@8116: #include "../command_func.h" rubidium@5469: #include "../network/network.h" rubidium@8130: #include "../core/alloc_func.hpp" rubidium@10208: #include "../company_func.h" rubidium@10208: #include "../company_base.h" truelight@2395: #include "ai.h" tron@2447: #include "default/default.h" rubidium@9701: #include "trolly/trolly.h" smatz@8306: #include "../signal_func.h" truelight@2715: rubidium@8268: AIStruct _ai; rubidium@10207: AICompany _ai_company[MAX_COMPANIES]; rubidium@8268: truelight@2395: /** truelight@2395: * Dequeues commands put in the queue via AI_PutCommandInQueue. truelight@2395: */ rubidium@10207: static void AI_DequeueCommands(CompanyID company) truelight@2395: { truelight@2395: AICommand *com, *entry_com; truelight@2395: rubidium@10207: entry_com = _ai_company[company].queue; truelight@2395: truelight@2395: /* It happens that DoCommandP issues a new DoCommandAI which adds a new command truelight@2395: * to this very same queue (don't argue about this, if it currently doesn't truelight@2395: * happen I can tell you it will happen with AIScript -- TrueLight). If we truelight@2395: * do not make the queue NULL, that commands will be dequeued immediatly. truelight@2395: * Therefor we safe the entry-point to entry_com, and make the queue NULL, so truelight@2395: * the new queue can be safely built up. */ rubidium@10207: _ai_company[company].queue = NULL; rubidium@10207: _ai_company[company].queue_tail = NULL; truelight@2395: truelight@2395: /* Dequeue all commands */ truelight@2395: while ((com = entry_com) != NULL) { rubidium@10207: _current_company = company; truelight@2395: truelight@2738: _cmd_text = com->text; tron@3946: DoCommandP(com->tile, com->p1, com->p2, com->callback, com->procc); truelight@2395: truelight@2395: /* Free item */ truelight@2395: entry_com = com->next; tron@3886: free(com->text); truelight@2395: free(com); truelight@2395: } truelight@2395: } truelight@2395: truelight@2395: /** truelight@2395: * Needed for SP; we need to delay DoCommand with 1 tick, because else events truelight@2395: * will make infinite loops (AIScript). truelight@2395: */ rubidium@10207: static void AI_PutCommandInQueue(CompanyID company, TileIndex tile, uint32 p1, uint32 p2, uint32 procc, CommandCallback* callback) truelight@2395: { truelight@2395: AICommand *com; truelight@2395: rubidium@10207: if (_ai_company[company].queue_tail == NULL) { truelight@2395: /* There is no item in the queue yet, create the queue */ rubidium@10207: _ai_company[company].queue = MallocT(1); rubidium@10207: _ai_company[company].queue_tail = _ai_company[company].queue; truelight@2395: } else { truelight@2395: /* Add an item at the end */ rubidium@10207: _ai_company[company].queue_tail->next = MallocT(1); rubidium@10207: _ai_company[company].queue_tail = _ai_company[company].queue_tail->next; truelight@2395: } truelight@2395: truelight@2395: /* This is our new item */ rubidium@10207: com = _ai_company[company].queue_tail; truelight@2395: truelight@2395: /* Assign the info */ truelight@2395: com->tile = tile; truelight@2395: com->p1 = p1; truelight@2395: com->p2 = p2; truelight@2395: com->procc = procc; tron@3946: com->callback = callback; truelight@2395: com->next = NULL; truelight@2738: com->text = NULL; truelight@2395: truelight@2738: /* Copy the cmd_text, if needed */ truelight@2738: if (_cmd_text != NULL) { truelight@2738: com->text = strdup(_cmd_text); truelight@2738: _cmd_text = NULL; truelight@2738: } truelight@2395: } truelight@2395: truelight@2395: /** truelight@2395: * Executes a raw DoCommand for the AI. truelight@2395: */ rubidium@8116: CommandCost AI_DoCommandCc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint32 procc, CommandCallback* callback) truelight@2395: { rubidium@10207: CompanyID old_local_company; rubidium@6950: CommandCost res; tron@3950: const char* tmp_cmdtext; truelight@2395: truelight@2395: /* If you enable DC_EXEC with DC_QUERY_COST you are a really strange truelight@2395: * person.. should we check for those funny jokes? truelight@2395: */ truelight@2395: tron@3950: /* The test already resets _cmd_text, so backup the pointer */ tron@3950: tmp_cmdtext = _cmd_text; truelight@2738: truelight@2395: /* First, do a test-run to see if we can do this */ tron@3491: res = DoCommand(tile, p1, p2, flags & ~DC_EXEC, procc); truelight@2395: /* The command failed, or you didn't want to execute, or you are quering, return */ tron@4000: if (CmdFailed(res) || !(flags & DC_EXEC) || (flags & DC_QUERY_COST)) { truelight@2395: return res; truelight@2738: } truelight@2738: tron@3950: /* Restore _cmd_text */ tron@3950: _cmd_text = tmp_cmdtext; truelight@2395: rubidium@10207: /* NetworkSend_Command needs _local_company to be set correctly, so tron@4000: * adjust it, and put it back right after the function */ rubidium@10207: old_local_company = _local_company; rubidium@10207: _local_company = _current_company; truelight@2395: bjarni@2727: #ifdef ENABLE_NETWORK truelight@2395: /* Send the command */ tron@4000: if (_networking) { truelight@2395: /* Network is easy, send it to his handler */ tron@3946: NetworkSend_Command(tile, p1, p2, procc, callback); tron@4000: } else { tron@4000: #else tron@4000: { bjarni@2727: #endif truelight@2395: /* If we execute BuildCommands directly in SP, we have a big problem with events truelight@2395: * so we need to delay is for 1 tick */ rubidium@10207: AI_PutCommandInQueue(_current_company, tile, p1, p2, procc, callback); tron@4000: } truelight@2395: rubidium@10207: /* Set _local_company back */ rubidium@10207: _local_company = old_local_company; truelight@2395: truelight@2395: return res; truelight@2395: } truelight@2395: tron@3946: rubidium@8116: CommandCost AI_DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint32 procc) tron@3946: { tron@3946: return AI_DoCommandCc(tile, p1, p2, flags, procc, NULL); tron@3946: } tron@3946: tron@3946: truelight@2759: /** truelight@2395: * Run 1 tick of the AI. Don't overdo it, keep it realistic. truelight@2395: */ rubidium@10207: static void AI_RunTick(CompanyID company) truelight@2395: { rubidium@10207: extern void AiNewDoGameLoop(Company *c); tron@2767: rubidium@10207: Company *c = GetCompany(company); rubidium@10207: _current_company = company; truelight@2395: rubidium@9413: if (_settings_game.ai.ainew_active) { rubidium@10207: AiNewDoGameLoop(c); tron@2767: } else { tron@2767: /* Enable all kind of cheats the old AI needs in order to operate correctly... */ rubidium@10207: _is_old_ai_company = true; rubidium@10207: AiDoGameLoop(c); rubidium@10207: _is_old_ai_company = false; truelight@2422: } smatz@8306: smatz@8306: /* AI could change some track, so update signals */ smatz@8306: UpdateSignalsInBuffer(); truelight@2395: } truelight@2395: truelight@2395: truelight@2395: /** truelight@2395: * The gameloop for AIs. truelight@2395: * Handles one tick for all the AIs. truelight@2395: */ rubidium@6247: void AI_RunGameLoop() truelight@2395: { truelight@2395: /* Don't do anything if ai is disabled */ tron@2639: if (!_ai.enabled) return; truelight@2395: Darkvater@5332: /* Don't do anything if we are a network-client, or the AI has been disabled */ rubidium@9413: if (_networking && (!_network_server || !_settings_game.ai.ai_in_multiplayer)) return; truelight@2682: truelight@2395: /* New tick */ truelight@2395: _ai.tick++; truelight@2395: truelight@2395: /* Make sure the AI follows the difficulty rule.. */ rubidium@9413: assert(_settings_game.difficulty.competitor_speed <= 4); rubidium@9413: if ((_ai.tick & ((1 << (4 - _settings_game.difficulty.competitor_speed)) - 1)) != 0) return; truelight@2395: truelight@2395: /* Check for AI-client (so joining a network with an AI) */ Darkvater@4854: if (!_networking || _network_server) { truelight@2395: /* Check if we want to run AIs (server or SP only) */ rubidium@10207: const Company *c; truelight@2395: rubidium@10207: FOR_ALL_COMPANIES(c) { rubidium@10207: if (c->is_ai) { tron@2767: /* This should always be true, else something went wrong... */ rubidium@10207: assert(_ai_company[c->index].active); tron@2767: truelight@2395: /* Run the script */ rubidium@10207: AI_DequeueCommands(c->index); rubidium@10207: AI_RunTick(c->index); truelight@2395: } truelight@2395: } truelight@2395: } truelight@2395: rubidium@10207: _current_company = OWNER_NONE; truelight@2395: } truelight@2395: truelight@2395: /** truelight@2395: * A new AI sees the day of light. You can do here what ever you think is needed. truelight@2395: */ rubidium@10207: void AI_StartNewAI(CompanyID company) truelight@2395: { rubidium@10207: assert(IsValidCompanyID(company)); truelight@2702: truelight@2395: /* Called if a new AI is booted */ rubidium@10207: _ai_company[company].active = true; truelight@2395: } truelight@2395: truelight@2395: /** rubidium@10207: * This AI company died. Give it some chance to make a final puf. truelight@2395: */ rubidium@10207: void AI_CompanyDied(CompanyID company) truelight@2395: { truelight@2395: /* Called if this AI died */ rubidium@10207: _ai_company[company].active = false; rubidium@9701: rubidium@10207: if (_companies_ainew[company].pathfinder == NULL) return; rubidium@9701: rubidium@10207: AyStarMain_Free(_companies_ainew[company].pathfinder); rubidium@10207: delete _companies_ainew[company].pathfinder; rubidium@10207: _companies_ainew[company].pathfinder = NULL; rubidium@9701: truelight@2395: } truelight@2395: truelight@2395: /** truelight@2395: * Initialize some AI-related stuff. truelight@2395: */ rubidium@6247: void AI_Initialize() truelight@2395: { truelight@2706: /* First, make sure all AIs are DEAD! */ truelight@2706: AI_Uninitialize(); truelight@2706: truelight@2395: memset(&_ai, 0, sizeof(_ai)); rubidium@10207: memset(&_ai_company, 0, sizeof(_ai_company)); truelight@2395: truelight@2395: _ai.enabled = true; truelight@2395: } truelight@2395: truelight@2395: /** truelight@2395: * Deinitializer for AI-related stuff. truelight@2395: */ rubidium@6247: void AI_Uninitialize() truelight@2395: { rubidium@10207: for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) AI_CompanyDied(c); truelight@2395: }