|
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 } |