# ai.cpp

    00001 /* $Id: ai.cpp 13811 2008-07-23 21:51:25Z rubidium $ */
    00002
    00003 #include "../stdafx.h"
    00004 #include "../openttd.h"
    00005 #include "../variables.h"
    00006 #include "../command_func.h"
    00007 #include "../network/network.h"
    00008 #include "../core/alloc_func.hpp"
    00009 #include "../player_func.h"
    00010 #include "../player_base.h"
    00011 #include "ai.h"
    00012 #include "default/default.h"
    00013 #include "trolly/trolly.h"
    00014 #include "../signal_func.h"
    00015
    00016 AIStruct _ai;
    00017 AIPlayer _ai_player[MAX_PLAYERS];
    00018
    00022 static void AI_DequeueCommands(PlayerID player)
    00023 {
    00024   AICommand *com, *entry_com;
    00025
    00026   entry_com = _ai_player[player].queue;
    00027
    00028   /* It happens that DoCommandP issues a new DoCommandAI which adds a new command
    00029    *  to this very same queue (don't argue about this, if it currently doesn't
    00030    *  happen I can tell you it will happen with AIScript -- TrueLight). If we
    00031    *  do not make the queue NULL, that commands will be dequeued immediatly.
    00032    *  Therefor we safe the entry-point to entry_com, and make the queue NULL, so
    00033    *  the new queue can be safely built up. */
    00034   _ai_player[player].queue = NULL;
    00035   _ai_player[player].queue_tail = NULL;
    00036
    00037   /* Dequeue all commands */
    00038   while ((com = entry_com) != NULL) {
    00039     _current_player = player;
    00040
    00041     _cmd_text = com->text;
    00042     DoCommandP(com->tile, com->p1, com->p2, com->callback, com->procc);
    00043
    00044     /* Free item */
    00045     entry_com = com->next;
    00046     free(com->text);
    00047     free(com);
    00048   }
    00049 }
    00050
    00055 static void AI_PutCommandInQueue(PlayerID player, TileIndex tile, uint32 p1, uint32 p2, uint32 procc, CommandCallback* callback)
    00056 {
    00057   AICommand *com;
    00058
    00059   if (_ai_player[player].queue_tail == NULL) {
    00060     /* There is no item in the queue yet, create the queue */
    00061     _ai_player[player].queue = MallocT<AICommand>(1);
    00062     _ai_player[player].queue_tail = _ai_player[player].queue;
    00063   } else {
    00064     /* Add an item at the end */
    00065     _ai_player[player].queue_tail->next = MallocT<AICommand>(1);
    00066     _ai_player[player].queue_tail = _ai_player[player].queue_tail->next;
    00067   }
    00068
    00069   /* This is our new item */
    00070   com = _ai_player[player].queue_tail;
    00071
    00072   /* Assign the info */
    00073   com->tile  = tile;
    00074   com->p1    = p1;
    00075   com->p2    = p2;
    00076   com->procc = procc;
    00077   com->callback = callback;
    00078   com->next  = NULL;
    00079   com->text  = NULL;
    00080
    00081   /* Copy the cmd_text, if needed */
    00082   if (_cmd_text != NULL) {
    00083     com->text = strdup(_cmd_text);
    00084     _cmd_text = NULL;
    00085   }
    00086 }
    00087
    00091 CommandCost AI_DoCommandCc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint32 procc, CommandCallback* callback)
    00092 {
    00093   PlayerID old_lp;
    00094   CommandCost res;
    00095   const char* tmp_cmdtext;
    00096
    00097   /* If you enable DC_EXEC with DC_QUERY_COST you are a really strange
    00098    *   person.. should we check for those funny jokes?
    00099    */
    00100
    00101   /* The test already resets _cmd_text, so backup the pointer */
    00102   tmp_cmdtext = _cmd_text;
    00103
    00104   /* First, do a test-run to see if we can do this */
    00105   res = DoCommand(tile, p1, p2, flags & ~DC_EXEC, procc);
    00106   /* The command failed, or you didn't want to execute, or you are quering, return */
    00107   if (CmdFailed(res) || !(flags & DC_EXEC) || (flags & DC_QUERY_COST)) {
    00108     return res;
    00109   }
    00110
    00111   /* Restore _cmd_text */
    00112   _cmd_text = tmp_cmdtext;
    00113
    00114   /* NetworkSend_Command needs _local_player to be set correctly, so
    00115    * adjust it, and put it back right after the function */
    00116   old_lp = _local_player;
    00117   _local_player = _current_player;
    00118
    00119 #ifdef ENABLE_NETWORK
    00120   /* Send the command */
    00121   if (_networking) {
    00122     /* Network is easy, send it to his handler */
    00123     NetworkSend_Command(tile, p1, p2, procc, callback);
    00124   } else {
    00125 #else
    00126   {
    00127 #endif
    00128     /* If we execute BuildCommands directly in SP, we have a big problem with events
    00129      *  so we need to delay is for 1 tick */
    00130     AI_PutCommandInQueue(_current_player, tile, p1, p2, procc, callback);
    00131   }
    00132
    00133   /* Set _local_player back */
    00134   _local_player = old_lp;
    00135
    00136   return res;
    00137 }
    00138
    00139
    00140 CommandCost AI_DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint32 procc)
    00141 {
    00142   return AI_DoCommandCc(tile, p1, p2, flags, procc, NULL);
    00143 }
    00144
    00145
    00149 static void AI_RunTick(PlayerID player)
    00150 {
    00151   extern void AiNewDoGameLoop(Player *p);
    00152
    00153   Player *p = GetPlayer(player);
    00154   _current_player = player;
    00155
    00156   if (_patches.ainew_active) {
    00157     AiNewDoGameLoop(p);
    00158   } else {
    00159     /* Enable all kind of cheats the old AI needs in order to operate correctly... */
    00160     _is_old_ai_player = true;
    00161     AiDoGameLoop(p);
    00162     _is_old_ai_player = false;
    00163   }
    00164
    00165   /* AI could change some track, so update signals */
    00166   UpdateSignalsInBuffer();
    00167 }
    00168
    00169
    00174 void AI_RunGameLoop()
    00175 {
    00176   /* Don't do anything if ai is disabled */
    00177   if (!_ai.enabled) return;
    00178
    00179   /* Don't do anything if we are a network-client, or the AI has been disabled */
    00180   if (_networking && (!_network_server || !_patches.ai_in_multiplayer)) return;
    00181
    00182   /* New tick */
    00183   _ai.tick++;
    00184
    00185   /* Make sure the AI follows the difficulty rule.. */
    00186   assert(_opt.diff.competitor_speed <= 4);
    00187   if ((_ai.tick & ((1 << (4 - _opt.diff.competitor_speed)) - 1)) != 0) return;
    00188
    00189   /* Check for AI-client (so joining a network with an AI) */
    00190   if (!_networking || _network_server) {
    00191     /* Check if we want to run AIs (server or SP only) */
    00192     const Player* p;
    00193
    00194     FOR_ALL_PLAYERS(p) {
    00195       if (p->is_active && p->is_ai) {
    00196         /* This should always be true, else something went wrong... */
    00197         assert(_ai_player[p->index].active);
    00198
    00199         /* Run the script */
    00200         AI_DequeueCommands(p->index);
    00201         AI_RunTick(p->index);
    00202       }
    00203     }
    00204   }
    00205
    00206   _current_player = OWNER_NONE;
    00207 }
    00208
    00212 void AI_StartNewAI(PlayerID player)
    00213 {
    00214   assert(IsValidPlayer(player));
    00215
    00216   /* Called if a new AI is booted */
    00217   _ai_player[player].active = true;
    00218 }
    00219
    00223 void AI_PlayerDied(PlayerID player)
    00224 {
    00225   /* Called if this AI died */
    00226   _ai_player[player].active = false;
    00227
    00228   if (_players_ainew[player].pathfinder == NULL) return;
    00229
    00230   AyStarMain_Free(_players_ainew[player].pathfinder);
    00231   delete _players_ainew[player].pathfinder;
    00232   _players_ainew[player].pathfinder = NULL;
    00233
    00234 }
    00235
    00239 void AI_Initialize()
    00240 {
    00241   /* First, make sure all AIs are DEAD! */
    00242   AI_Uninitialize();
    00243
    00244   memset(&_ai, 0, sizeof(_ai));
    00245   memset(&_ai_player, 0, sizeof(_ai_player));
    00246
    00247   _ai.enabled = true;
    00248 }
    00249
    00253 void AI_Uninitialize()
    00254 {
    00255   for (PlayerID p = PLAYER_FIRST; p < MAX_PLAYERS; p++) AI_PlayerDied(p);
    00256 }