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