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 }

Generated on Wed Oct 1 17:03:19 2008 for openttd by  doxygen 1.5.6