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