src/ai/ai.cpp
changeset 5835 e0ff603ae0b7
parent 5726 8f399788f6c9
child 5650 aefc131bf5ce
equal deleted inserted replaced
5834:7bf92d5a5a0f 5835:e0ff603ae0b7
       
     1 /* $Id$ */
       
     2 
       
     3 #include "../stdafx.h"
       
     4 #include "../openttd.h"
       
     5 #include "../variables.h"
       
     6 #include "../command.h"
       
     7 #include "../network/network.h"
       
     8 #include "ai.h"
       
     9 #include "default/default.h"
       
    10 
       
    11 /**
       
    12  * Dequeues commands put in the queue via AI_PutCommandInQueue.
       
    13  */
       
    14 static void AI_DequeueCommands(PlayerID player)
       
    15 {
       
    16 	AICommand *com, *entry_com;
       
    17 
       
    18 	entry_com = _ai_player[player].queue;
       
    19 
       
    20 	/* It happens that DoCommandP issues a new DoCommandAI which adds a new command
       
    21 	 *  to this very same queue (don't argue about this, if it currently doesn't
       
    22 	 *  happen I can tell you it will happen with AIScript -- TrueLight). If we
       
    23 	 *  do not make the queue NULL, that commands will be dequeued immediatly.
       
    24 	 *  Therefor we safe the entry-point to entry_com, and make the queue NULL, so
       
    25 	 *  the new queue can be safely built up. */
       
    26 	_ai_player[player].queue = NULL;
       
    27 	_ai_player[player].queue_tail = NULL;
       
    28 
       
    29 	/* Dequeue all commands */
       
    30 	while ((com = entry_com) != NULL) {
       
    31 		_current_player = player;
       
    32 
       
    33 		_cmd_text = com->text;
       
    34 		DoCommandP(com->tile, com->p1, com->p2, com->callback, com->procc);
       
    35 
       
    36 		/* Free item */
       
    37 		entry_com = com->next;
       
    38 		free(com->text);
       
    39 		free(com);
       
    40 	}
       
    41 }
       
    42 
       
    43 /**
       
    44  * Needed for SP; we need to delay DoCommand with 1 tick, because else events
       
    45  *  will make infinite loops (AIScript).
       
    46  */
       
    47 static void AI_PutCommandInQueue(PlayerID player, TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCallback* callback)
       
    48 {
       
    49 	AICommand *com;
       
    50 
       
    51 	if (_ai_player[player].queue_tail == NULL) {
       
    52 		/* There is no item in the queue yet, create the queue */
       
    53 		_ai_player[player].queue = malloc(sizeof(AICommand));
       
    54 		_ai_player[player].queue_tail = _ai_player[player].queue;
       
    55 	} else {
       
    56 		/* Add an item at the end */
       
    57 		_ai_player[player].queue_tail->next = malloc(sizeof(AICommand));
       
    58 		_ai_player[player].queue_tail = _ai_player[player].queue_tail->next;
       
    59 	}
       
    60 
       
    61 	/* This is our new item */
       
    62 	com = _ai_player[player].queue_tail;
       
    63 
       
    64 	/* Assign the info */
       
    65 	com->tile  = tile;
       
    66 	com->p1    = p1;
       
    67 	com->p2    = p2;
       
    68 	com->procc = procc;
       
    69 	com->callback = callback;
       
    70 	com->next  = NULL;
       
    71 	com->text  = NULL;
       
    72 
       
    73 	/* Copy the cmd_text, if needed */
       
    74 	if (_cmd_text != NULL) {
       
    75 		com->text = strdup(_cmd_text);
       
    76 		_cmd_text = NULL;
       
    77 	}
       
    78 }
       
    79 
       
    80 /**
       
    81  * Executes a raw DoCommand for the AI.
       
    82  */
       
    83 int32 AI_DoCommandCc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc, CommandCallback* callback)
       
    84 {
       
    85 	PlayerID old_lp;
       
    86 	int32 res = 0;
       
    87 	const char* tmp_cmdtext;
       
    88 
       
    89 	/* If you enable DC_EXEC with DC_QUERY_COST you are a really strange
       
    90 	 *   person.. should we check for those funny jokes?
       
    91 	 */
       
    92 
       
    93 	/* The test already resets _cmd_text, so backup the pointer */
       
    94 	tmp_cmdtext = _cmd_text;
       
    95 
       
    96 	/* First, do a test-run to see if we can do this */
       
    97 	res = DoCommand(tile, p1, p2, flags & ~DC_EXEC, procc);
       
    98 	/* The command failed, or you didn't want to execute, or you are quering, return */
       
    99 	if (CmdFailed(res) || !(flags & DC_EXEC) || (flags & DC_QUERY_COST)) {
       
   100 		return res;
       
   101 	}
       
   102 
       
   103 	/* Restore _cmd_text */
       
   104 	_cmd_text = tmp_cmdtext;
       
   105 
       
   106 	/* If we did a DC_EXEC, and the command did not return an error, execute it
       
   107 	 * over the network */
       
   108 	if (flags & DC_AUTO)     procc |= CMD_AUTO;
       
   109 	if (flags & DC_NO_WATER) procc |= CMD_NO_WATER;
       
   110 
       
   111 	/* NetworkSend_Command needs _local_player to be set correctly, so
       
   112 	 * adjust it, and put it back right after the function */
       
   113 	old_lp = _local_player;
       
   114 	_local_player = _current_player;
       
   115 
       
   116 #ifdef ENABLE_NETWORK
       
   117 	/* Send the command */
       
   118 	if (_networking) {
       
   119 		/* Network is easy, send it to his handler */
       
   120 		NetworkSend_Command(tile, p1, p2, procc, callback);
       
   121 	} else {
       
   122 #else
       
   123 	{
       
   124 #endif
       
   125 		/* If we execute BuildCommands directly in SP, we have a big problem with events
       
   126 		 *  so we need to delay is for 1 tick */
       
   127 		AI_PutCommandInQueue(_current_player, tile, p1, p2, procc, callback);
       
   128 	}
       
   129 
       
   130 	/* Set _local_player back */
       
   131 	_local_player = old_lp;
       
   132 
       
   133 	return res;
       
   134 }
       
   135 
       
   136 
       
   137 int32 AI_DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
       
   138 {
       
   139 	return AI_DoCommandCc(tile, p1, p2, flags, procc, NULL);
       
   140 }
       
   141 
       
   142 
       
   143 /**
       
   144  * Run 1 tick of the AI. Don't overdo it, keep it realistic.
       
   145  */
       
   146 static void AI_RunTick(PlayerID player)
       
   147 {
       
   148 	extern void AiNewDoGameLoop(Player *p);
       
   149 
       
   150 	Player *p = GetPlayer(player);
       
   151 	_current_player = player;
       
   152 
       
   153 	if (_patches.ainew_active) {
       
   154 		AiNewDoGameLoop(p);
       
   155 	} else {
       
   156 		/* Enable all kind of cheats the old AI needs in order to operate correctly... */
       
   157 		_is_old_ai_player = true;
       
   158 		AiDoGameLoop(p);
       
   159 		_is_old_ai_player = false;
       
   160 	}
       
   161 }
       
   162 
       
   163 
       
   164 /**
       
   165  * The gameloop for AIs.
       
   166  *  Handles one tick for all the AIs.
       
   167  */
       
   168 void AI_RunGameLoop(void)
       
   169 {
       
   170 	/* Don't do anything if ai is disabled */
       
   171 	if (!_ai.enabled) return;
       
   172 
       
   173 	/* Don't do anything if we are a network-client, or the AI has been disabled */
       
   174 	if (_networking && (!_network_server || !_patches.ai_in_multiplayer)) return;
       
   175 
       
   176 	/* New tick */
       
   177 	_ai.tick++;
       
   178 
       
   179 	/* Make sure the AI follows the difficulty rule.. */
       
   180 	assert(_opt.diff.competitor_speed <= 4);
       
   181 	if ((_ai.tick & ((1 << (4 - _opt.diff.competitor_speed)) - 1)) != 0) return;
       
   182 
       
   183 	/* Check for AI-client (so joining a network with an AI) */
       
   184 	if (!_networking || _network_server) {
       
   185 		/* Check if we want to run AIs (server or SP only) */
       
   186 		const Player* p;
       
   187 
       
   188 		FOR_ALL_PLAYERS(p) {
       
   189 			if (p->is_active && p->is_ai) {
       
   190 				/* This should always be true, else something went wrong... */
       
   191 				assert(_ai_player[p->index].active);
       
   192 
       
   193 				/* Run the script */
       
   194 				AI_DequeueCommands(p->index);
       
   195 				AI_RunTick(p->index);
       
   196 			}
       
   197 		}
       
   198 	}
       
   199 
       
   200 	_current_player = OWNER_NONE;
       
   201 }
       
   202 
       
   203 /**
       
   204  * A new AI sees the day of light. You can do here what ever you think is needed.
       
   205  */
       
   206 void AI_StartNewAI(PlayerID player)
       
   207 {
       
   208 	assert(IsValidPlayer(player));
       
   209 
       
   210 	/* Called if a new AI is booted */
       
   211 	_ai_player[player].active = true;
       
   212 }
       
   213 
       
   214 /**
       
   215  * This AI player died. Give it some chance to make a final puf.
       
   216  */
       
   217 void AI_PlayerDied(PlayerID player)
       
   218 {
       
   219 	/* Called if this AI died */
       
   220 	_ai_player[player].active = false;
       
   221 }
       
   222 
       
   223 /**
       
   224  * Initialize some AI-related stuff.
       
   225  */
       
   226 void AI_Initialize(void)
       
   227 {
       
   228 	/* First, make sure all AIs are DEAD! */
       
   229 	AI_Uninitialize();
       
   230 
       
   231 	memset(&_ai, 0, sizeof(_ai));
       
   232 	memset(&_ai_player, 0, sizeof(_ai_player));
       
   233 
       
   234 	_ai.enabled = true;
       
   235 }
       
   236 
       
   237 /**
       
   238  * Deinitializer for AI-related stuff.
       
   239  */
       
   240 void AI_Uninitialize(void)
       
   241 {
       
   242 	const Player* p;
       
   243 
       
   244 	FOR_ALL_PLAYERS(p) {
       
   245 		if (p->is_active && p->is_ai) AI_PlayerDied(p->index);
       
   246 	}
       
   247 }