truelight@2395: /* $Id$ */ truelight@2395: truelight@2395: #include "../stdafx.h" truelight@2395: #include "../openttd.h" truelight@2395: #include "../variables.h" truelight@2395: #include "../command.h" truelight@2395: #include "../network.h" truelight@2395: #include "ai.h" tron@2447: #include "default/default.h" truelight@2395: truelight@2395: /** truelight@2395: * Dequeues commands put in the queue via AI_PutCommandInQueue. truelight@2395: */ truelight@2395: void AI_DequeueCommands(byte player) truelight@2395: { truelight@2395: AICommand *com, *entry_com; truelight@2395: truelight@2395: entry_com = _ai_player[player].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. */ truelight@2395: _ai_player[player].queue = NULL; truelight@2395: _ai_player[player].queue_tail = NULL; truelight@2395: truelight@2395: /* Dequeue all commands */ truelight@2395: while ((com = entry_com) != NULL) { truelight@2395: _current_player = player; truelight@2395: truelight@2395: /* Copy the DP back in place */ truelight@2395: memcpy(_decode_parameters, com->dp, sizeof(com->dp)); truelight@2395: DoCommandP(com->tile, com->p1, com->p2, NULL, com->procc); truelight@2395: truelight@2395: /* Free item */ truelight@2395: entry_com = com->next; 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: */ truelight@2395: void AI_PutCommandInQueue(byte player, uint tile, uint32 p1, uint32 p2, uint procc) truelight@2395: { truelight@2395: AICommand *com; truelight@2395: truelight@2395: if (_ai_player[player].queue_tail == NULL) { truelight@2395: /* There is no item in the queue yet, create the queue */ truelight@2395: _ai_player[player].queue = malloc(sizeof(AICommand)); truelight@2395: _ai_player[player].queue_tail = _ai_player[player].queue; truelight@2395: } else { truelight@2395: /* Add an item at the end */ truelight@2395: _ai_player[player].queue_tail->next = malloc(sizeof(AICommand)); truelight@2395: _ai_player[player].queue_tail = _ai_player[player].queue_tail->next; truelight@2395: } truelight@2395: truelight@2395: /* This is our new item */ truelight@2395: com = _ai_player[player].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; truelight@2395: com->next = NULL; truelight@2395: truelight@2395: /* Copy the decode_parameters */ truelight@2395: memcpy(com->dp, _decode_parameters, sizeof(com->dp)); truelight@2395: } truelight@2395: truelight@2395: /** truelight@2395: * Executes a raw DoCommand for the AI. truelight@2395: */ truelight@2395: int32 AI_DoCommand(uint tile, uint32 p1, uint32 p2, uint32 flags, uint procc) truelight@2395: { tron@2551: PlayerID old_lp; truelight@2395: int32 res = 0; 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: truelight@2395: /* First, do a test-run to see if we can do this */ truelight@2395: res = DoCommandByTile(tile, p1, p2, flags & ~DC_EXEC, procc); truelight@2395: /* The command failed, or you didn't want to execute, or you are quering, return */ truelight@2395: if ((res & CMD_ERROR) || !(flags & DC_EXEC) || (flags & DC_QUERY_COST)) truelight@2395: return res; truelight@2395: truelight@2395: /* If we did a DC_EXEC, and the command did not return an error, execute it truelight@2395: over the network */ truelight@2395: if (flags & DC_AUTO) procc |= CMD_AUTO; truelight@2395: if (flags & DC_NO_WATER) procc |= CMD_NO_WATER; truelight@2395: truelight@2395: /* NetworkSend_Command needs _local_player to be set correctly, so truelight@2395: adjust it, and put it back right after the function */ truelight@2395: old_lp = _local_player; truelight@2395: _local_player = _current_player; truelight@2395: truelight@2395: /* Send the command */ truelight@2395: if (_networking) truelight@2395: /* Network is easy, send it to his handler */ truelight@2395: NetworkSend_Command(tile, p1, p2, procc, NULL); truelight@2395: else 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 */ truelight@2395: AI_PutCommandInQueue(_current_player, tile, p1, p2, procc); truelight@2395: truelight@2395: /* Set _local_player back */ truelight@2395: _local_player = old_lp; truelight@2395: truelight@2395: return res; truelight@2395: } truelight@2395: truelight@2395: /** truelight@2395: * Run 1 tick of the AI. Don't overdo it, keep it realistic. truelight@2395: */ tron@2551: static void AI_RunTick(PlayerID player) truelight@2395: { truelight@2395: extern void AiNewDoGameLoop(Player *p); truelight@2395: truelight@2395: Player *p = GetPlayer(player); truelight@2395: _current_player = player; truelight@2395: tron@2639: if (_patches.ainew_active) { truelight@2395: AiNewDoGameLoop(p); tron@2639: } else { truelight@2422: /* Enable all kind of cheats the old AI needs in order to operate correctly... */ truelight@2422: _is_old_ai_player = true; truelight@2395: AiDoGameLoop(p); truelight@2422: _is_old_ai_player = false; truelight@2422: } 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: */ truelight@2395: void AI_RunGameLoop(void) truelight@2395: { truelight@2395: /* Don't do anything if ai is disabled */ tron@2639: if (!_ai.enabled) return; truelight@2395: truelight@2395: /* New tick */ truelight@2395: _ai.tick++; truelight@2395: truelight@2395: /* Make sure the AI follows the difficulty rule.. */ tron@2446: assert(_opt.diff.competitor_speed <= 4); tron@2446: if ((_ai.tick & ((1 << (4 - _opt.diff.competitor_speed)) - 1)) != 0) tron@2446: return; truelight@2395: truelight@2395: /* Check for AI-client (so joining a network with an AI) */ truelight@2395: if (_ai.network_client) { truelight@2395: /* Run the script */ truelight@2395: AI_DequeueCommands(_ai.network_player); truelight@2395: AI_RunTick(_ai.network_player); truelight@2395: } else if (!_networking || _network_server) { truelight@2395: /* Check if we want to run AIs (server or SP only) */ truelight@2395: Player *p; truelight@2395: truelight@2395: FOR_ALL_PLAYERS(p) { truelight@2395: if (p->is_active && p->is_ai) { truelight@2395: /* This should always be true, else something went wrong... */ truelight@2395: assert(_ai_player[p->index].active); truelight@2395: truelight@2395: /* Run the script */ truelight@2395: AI_DequeueCommands(p->index); truelight@2395: AI_RunTick(p->index); truelight@2395: } truelight@2395: } truelight@2395: } truelight@2395: truelight@2395: _current_player = 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: */ tron@2551: void AI_StartNewAI(PlayerID player) truelight@2395: { truelight@2395: /* Called if a new AI is booted */ truelight@2395: _ai_player[player].active = true; truelight@2395: } truelight@2395: truelight@2395: /** truelight@2395: * This AI player died. Give it some chance to make a final puf. truelight@2395: */ tron@2551: void AI_PlayerDied(PlayerID player) truelight@2395: { truelight@2395: /* Called if this AI died */ truelight@2395: _ai_player[player].active = false; truelight@2395: } truelight@2395: truelight@2395: /** truelight@2395: * Initialize some AI-related stuff. truelight@2395: */ truelight@2395: void AI_Initialize(void) truelight@2395: { truelight@2395: memset(&_ai, 0, sizeof(_ai)); truelight@2395: memset(&_ai_player, 0, sizeof(_ai_player)); truelight@2395: truelight@2395: _ai.enabled = true; truelight@2395: } truelight@2395: truelight@2395: /** truelight@2395: * Deinitializer for AI-related stuff. truelight@2395: */ truelight@2395: void AI_Uninitialize(void) truelight@2395: { tron@2639: Player* p; truelight@2395: truelight@2395: FOR_ALL_PLAYERS(p) { tron@2639: if (p->is_active && p->is_ai) AI_PlayerDied(p->index); truelight@2395: } truelight@2395: }