28 procedure ProcessBot; |
28 procedure ProcessBot; |
29 procedure FreeActionsList; |
29 procedure FreeActionsList; |
30 |
30 |
31 implementation |
31 implementation |
32 uses uConsts, SDLh, uAIMisc, uAIAmmoTests, uAIActions, |
32 uses uConsts, SDLh, uAIMisc, uAIAmmoTests, uAIActions, |
33 uAmmos, SysUtils{$IFNDEF USE_SDLTHREADS} {$IFDEF UNIX}, cthreads{$ENDIF} {$ENDIF}, uTypes, |
33 uAmmos, uTypes, |
34 uVariables, uCommands, uUtils, uDebug, uAILandMarks; |
34 uVariables, uCommands, uUtils, uDebug, uAILandMarks, |
|
35 uGearsUtils; |
35 |
36 |
36 var BestActions: TActions; |
37 var BestActions: TActions; |
37 CanUseAmmo: array [TAmmoType] of boolean; |
38 CanUseAmmo: array [TAmmoType] of boolean; |
38 StopThinking: boolean; |
39 StopThinking: boolean; |
39 {$IFDEF USE_SDLTHREADS} |
|
40 ThinkThread: PSDL_Thread = nil; |
|
41 {$ELSE} |
|
42 ThinkThread: TThreadID; |
|
43 {$ENDIF} |
|
44 hasThread: LongInt; |
|
45 StartTicks: Longword; |
40 StartTicks: Longword; |
|
41 ThreadSem: PSDL_Sem; |
46 |
42 |
47 procedure FreeActionsList; |
43 procedure FreeActionsList; |
48 begin |
44 begin |
49 AddFileLog('FreeActionsList called'); |
45 AddFileLog('FreeActionsList called'); |
50 if hasThread <> 0 then |
46 |
51 begin |
47 StopThinking:= true; |
52 AddFileLog('Waiting AI thread to finish'); |
48 SDL_SemWait(ThreadSem); |
53 StopThinking:= true; |
49 SDL_SemPost(ThreadSem); |
54 repeat |
50 |
55 SDL_Delay(10) |
51 if CurrentHedgehog <> nil then |
56 until hasThread = 0 |
52 with CurrentHedgehog^ do |
57 end; |
53 if Gear <> nil then |
58 |
54 if BotLevel <> 0 then |
59 with CurrentHedgehog^ do |
55 StopMessages(Gear^.Message); |
60 if Gear <> nil then |
|
61 if BotLevel <> 0 then |
|
62 StopMessages(Gear^.Message); |
|
63 |
56 |
64 BestActions.Count:= 0; |
57 BestActions.Count:= 0; |
65 BestActions.Pos:= 0 |
58 BestActions.Pos:= 0 |
66 end; |
59 end; |
67 |
60 |
68 |
61 |
69 |
|
70 const cBranchStackSize = 12; |
62 const cBranchStackSize = 12; |
71 type TStackEntry = record |
63 type TStackEntry = record |
72 WastedTicks: Longword; |
|
73 MadeActions: TActions; |
64 MadeActions: TActions; |
74 Hedgehog: TGear; |
65 Hedgehog: TGear; |
75 end; |
66 end; |
76 |
67 |
77 var Stack: record |
68 var Stack: record |
78 Count: Longword; |
69 Count: Longword; |
79 States: array[0..Pred(cBranchStackSize)] of TStackEntry; |
70 States: array[0..Pred(cBranchStackSize)] of TStackEntry; |
80 end; |
71 end; |
81 |
72 |
82 function Push(Ticks: Longword; const Actions: TActions; const Me: TGear; Dir: integer): boolean; |
73 function Push(const Actions: TActions; const Me: TGear; Dir: integer): boolean; |
83 var bRes: boolean; |
74 var bRes: boolean; |
84 begin |
75 begin |
85 bRes:= (Stack.Count < cBranchStackSize) and (Actions.Count < MAXACTIONS - 5); |
76 bRes:= (Stack.Count < cBranchStackSize) and (Actions.Count < MAXACTIONS - 5); |
86 if bRes then |
77 if bRes then |
87 with Stack.States[Stack.Count] do |
78 with Stack.States[Stack.Count] do |
88 begin |
79 begin |
89 WastedTicks:= Ticks; |
|
90 MadeActions:= Actions; |
80 MadeActions:= Actions; |
91 Hedgehog:= Me; |
81 Hedgehog:= Me; |
92 Hedgehog.Message:= Dir; |
82 Hedgehog.Message:= Dir; |
93 inc(Stack.Count) |
83 inc(Stack.Count) |
94 end; |
84 end; |
95 Push:= bRes |
85 Push:= bRes |
96 end; |
86 end; |
97 |
87 |
98 procedure Pop(var Ticks: Longword; var Actions: TActions; var Me: TGear); |
88 procedure Pop(var Actions: TActions; var Me: TGear); |
99 begin |
89 begin |
100 dec(Stack.Count); |
90 dec(Stack.Count); |
101 with Stack.States[Stack.Count] do |
91 with Stack.States[Stack.Count] do |
102 begin |
92 begin |
103 Ticks:= WastedTicks; |
|
104 Actions:= MadeActions; |
93 Actions:= MadeActions; |
105 Me:= Hedgehog |
94 Me:= Hedgehog |
106 end |
95 end |
107 end; |
96 end; |
108 |
97 |
109 |
98 |
110 |
99 |
111 procedure TestAmmos(var Actions: TActions; Me: PGear; rareChecks: boolean); |
100 procedure TestAmmos(var Actions: TActions; Me: PGear; rareChecks: boolean); |
112 var BotLevel: Byte; |
101 var BotLevel: Byte; |
113 ap: TAttackParams; |
102 ap: TAttackParams; |
114 Score, i, dAngle: LongInt; |
103 Score, i, t, n, dAngle: LongInt; |
115 a, aa: TAmmoType; |
104 a, aa: TAmmoType; |
|
105 useThisActions: boolean; |
116 begin |
106 begin |
117 BotLevel:= Me^.Hedgehog^.BotLevel; |
107 BotLevel:= Me^.Hedgehog^.BotLevel; |
118 windSpeed:= hwFloat2Float(cWindSpeed); |
108 windSpeed:= hwFloat2Float(cWindSpeed); |
|
109 useThisActions:= false; |
|
110 Me^.AIHints:= Me^.AIHints and (not aihAmmosChanged); |
119 |
111 |
120 for i:= 0 to Pred(Targets.Count) do |
112 for i:= 0 to Pred(Targets.Count) do |
121 if (Targets.ar[i].Score >= 0) and (not StopThinking) then |
113 if (Targets.ar[i].Score >= 0) and (not StopThinking) then |
122 begin |
114 begin |
123 with Me^.Hedgehog^ do |
115 with Me^.Hedgehog^ do |
124 a:= CurAmmoType; |
116 a:= CurAmmoType; |
125 aa:= a; |
117 aa:= a; |
126 {$IFDEF USE_SDLTHREADS} |
118 SDL_delay(0); // hint to let the context switch run |
127 SDL_delay(0); //ThreadSwitch was only a hint |
|
128 {$ELSE} |
|
129 ThreadSwitch(); |
|
130 {$ENDIF} |
|
131 repeat |
119 repeat |
132 if (CanUseAmmo[a]) |
120 if (CanUseAmmo[a]) |
133 and ((not rareChecks) or ((AmmoTests[a].flags and amtest_Rare) = 0)) |
121 and ((not rareChecks) or ((AmmoTests[a].flags and amtest_Rare) = 0)) |
134 and ((i = 0) or ((AmmoTests[a].flags and amtest_NoTarget) = 0)) |
122 and ((i = 0) or ((AmmoTests[a].flags and amtest_NoTarget) = 0)) |
135 then |
123 then |
136 begin |
124 begin |
137 {$HINTS OFF} |
125 {$HINTS OFF} |
138 Score:= AmmoTests[a].proc(Me, Targets.ar[i].Point, BotLevel, ap); |
126 Score:= AmmoTests[a].proc(Me, Targets.ar[i], BotLevel, ap); |
139 {$HINTS ON} |
127 {$HINTS ON} |
140 if Actions.Score + Score > BestActions.Score then |
128 if (Score > BadTurn) and (Actions.Score + Score > BestActions.Score) then |
141 if (BestActions.Score < 0) or (Actions.Score + Score > BestActions.Score + Byte(BotLevel) * 2048) then |
129 if (BestActions.Score < 0) or (Actions.Score + Score > BestActions.Score + Byte(BotLevel - 1) * 2048) then |
142 begin |
130 begin |
143 BestActions:= Actions; |
131 if useThisActions then |
144 inc(BestActions.Score, Score); |
132 begin |
145 BestActions.isWalkingToABetterPlace:= false; |
133 BestActions.Count:= Actions.Count |
|
134 end |
|
135 else |
|
136 begin |
|
137 BestActions:= Actions; |
|
138 BestActions.isWalkingToABetterPlace:= false; |
|
139 useThisActions:= true |
|
140 end; |
|
141 |
|
142 BestActions.Score:= Actions.Score + Score; |
|
143 |
|
144 // if not between shots, activate invulnerability/vampirism if available |
|
145 if CurrentHedgehog^.MultiShootAttacks = 0 then |
|
146 begin |
|
147 if (HHHasAmmo(Me^.Hedgehog^, amInvulnerable) > 0) and (Me^.Hedgehog^.Effects[heInvulnerable] = 0) then |
|
148 begin |
|
149 AddAction(BestActions, aia_Weapon, Longword(amInvulnerable), 80, 0, 0); |
|
150 AddAction(BestActions, aia_attack, aim_push, 10, 0, 0); |
|
151 AddAction(BestActions, aia_attack, aim_release, 10, 0, 0); |
|
152 end; |
|
153 |
|
154 if (HHHasAmmo(Me^.Hedgehog^, amExtraDamage) > 0) and (cDamageModifier <> _1_5) then |
|
155 begin |
|
156 AddAction(BestActions, aia_Weapon, Longword(amExtraDamage), 80, 0, 0); |
|
157 AddAction(BestActions, aia_attack, aim_push, 10, 0, 0); |
|
158 AddAction(BestActions, aia_attack, aim_release, 10, 0, 0); |
|
159 end; |
|
160 if (HHHasAmmo(Me^.Hedgehog^, amVampiric) > 0) and (not cVampiric) then |
|
161 begin |
|
162 AddAction(BestActions, aia_Weapon, Longword(amVampiric), 80, 0, 0); |
|
163 AddAction(BestActions, aia_attack, aim_push, 10, 0, 0); |
|
164 AddAction(BestActions, aia_attack, aim_release, 10, 0, 0); |
|
165 end; |
|
166 end; |
146 |
167 |
147 AddAction(BestActions, aia_Weapon, Longword(a), 300 + random(400), 0, 0); |
168 AddAction(BestActions, aia_Weapon, Longword(a), 300 + random(400), 0, 0); |
|
169 |
|
170 if (Ammoz[a].Ammo.Propz and ammoprop_NeedTarget) <> 0 then |
|
171 begin |
|
172 AddAction(BestActions, aia_Put, 0, 8, ap.AttackPutX, ap.AttackPutY) |
|
173 end; |
148 |
174 |
149 if (ap.Angle > 0) then |
175 if (ap.Angle > 0) then |
150 AddAction(BestActions, aia_LookRight, 0, 200, 0, 0) |
176 AddAction(BestActions, aia_LookRight, 0, 200, 0, 0) |
151 else if (ap.Angle < 0) then |
177 else if (ap.Angle < 0) then |
152 AddAction(BestActions, aia_LookLeft, 0, 200, 0, 0); |
178 AddAction(BestActions, aia_LookLeft, 0, 200, 0, 0); |
153 |
179 |
154 if (Ammoz[a].Ammo.Propz and ammoprop_Timerable) <> 0 then |
180 if (Ammoz[a].Ammo.Propz and ammoprop_Timerable) <> 0 then |
155 AddAction(BestActions, aia_Timer, ap.Time div 1000, 400, 0, 0); |
181 AddAction(BestActions, aia_Timer, ap.Time div 1000, 400, 0, 0); |
156 |
182 |
157 if (Ammoz[a].Ammo.Propz and ammoprop_NoCrosshair) = 0 then |
183 if (Ammoz[a].Ammo.Propz and ammoprop_NoCrosshair) = 0 then |
158 begin |
184 begin |
159 dAngle:= LongInt(Me^.Angle) - Abs(ap.Angle); |
185 dAngle:= LongInt(Me^.Angle) - Abs(ap.Angle); |
160 if dAngle > 0 then |
186 if dAngle > 0 then |
161 begin |
187 begin |
166 begin |
192 begin |
167 AddAction(BestActions, aia_Down, aim_push, 300 + random(250), 0, 0); |
193 AddAction(BestActions, aia_Down, aim_push, 300 + random(250), 0, 0); |
168 AddAction(BestActions, aia_Down, aim_release, -dAngle, 0, 0) |
194 AddAction(BestActions, aia_Down, aim_release, -dAngle, 0, 0) |
169 end |
195 end |
170 end; |
196 end; |
171 |
197 |
172 if (Ammoz[a].Ammo.Propz and ammoprop_NeedTarget) <> 0 then |
|
173 begin |
|
174 AddAction(BestActions, aia_Put, 0, 1, ap.AttackPutX, ap.AttackPutY) |
|
175 end; |
|
176 |
|
177 if (Ammoz[a].Ammo.Propz and ammoprop_OscAim) <> 0 then |
198 if (Ammoz[a].Ammo.Propz and ammoprop_OscAim) <> 0 then |
178 begin |
199 begin |
179 AddAction(BestActions, aia_attack, aim_push, 350 + random(200), 0, 0); |
200 AddAction(BestActions, aia_attack, aim_push, 350 + random(200), 0, 0); |
180 AddAction(BestActions, aia_attack, aim_release, 1, 0, 0); |
201 AddAction(BestActions, aia_attack, aim_release, 1, 0, 0); |
181 |
202 |
182 if abs(ap.Angle) > 32 then |
203 if abs(ap.Angle) > 32 then |
183 begin |
204 begin |
184 AddAction(BestActions, aia_Down, aim_push, 100 + random(150), 0, 0); |
205 AddAction(BestActions, aia_Down, aim_push, 100 + random(150), 0, 0); |
185 AddAction(BestActions, aia_Down, aim_release, 32, 0, 0); |
206 AddAction(BestActions, aia_Down, aim_release, 32, 0, 0); |
186 end; |
207 end; |
187 |
208 |
188 AddAction(BestActions, aia_waitAngle, ap.Angle, 250, 0, 0); |
209 AddAction(BestActions, aia_waitAngle, ap.Angle, 250, 0, 0); |
189 AddAction(BestActions, aia_attack, aim_push, 1, 0, 0); |
210 AddAction(BestActions, aia_attack, aim_push, 1, 0, 0); |
190 AddAction(BestActions, aia_attack, aim_release, 1, 0, 0); |
211 AddAction(BestActions, aia_attack, aim_release, 1, 0, 0); |
191 end else |
212 end else |
192 if (Ammoz[a].Ammo.Propz and ammoprop_AttackingPut) = 0 then |
213 if (Ammoz[a].Ammo.Propz and ammoprop_AttackingPut) = 0 then |
193 begin |
214 begin |
|
215 if (AmmoTests[a].flags and amtest_MultipleAttacks) = 0 then |
|
216 n:= 1 else n:= ap.AttacksNum; |
|
217 |
194 AddAction(BestActions, aia_attack, aim_push, 650 + random(300), 0, 0); |
218 AddAction(BestActions, aia_attack, aim_push, 650 + random(300), 0, 0); |
|
219 for t:= 2 to n do |
|
220 begin |
|
221 AddAction(BestActions, aia_attack, aim_push, 150, 0, 0); |
|
222 AddAction(BestActions, aia_attack, aim_release, ap.Power, 0, 0); |
|
223 end; |
195 AddAction(BestActions, aia_attack, aim_release, ap.Power, 0, 0); |
224 AddAction(BestActions, aia_attack, aim_release, ap.Power, 0, 0); |
196 end; |
225 end; |
197 |
226 |
198 if (Ammoz[a].Ammo.Propz and ammoprop_Track) <> 0 then |
227 if (Ammoz[a].Ammo.Propz and ammoprop_Track) <> 0 then |
199 begin |
228 begin |
215 end; |
244 end; |
216 |
245 |
217 procedure Walk(Me: PGear; var Actions: TActions); |
246 procedure Walk(Me: PGear; var Actions: TActions); |
218 const FallPixForBranching = cHHRadius; |
247 const FallPixForBranching = cHHRadius; |
219 var |
248 var |
220 ticks, maxticks, oldticks, steps, tmp: Longword; |
249 maxticks, oldticks, steps, tmp: Longword; |
221 BaseRate, BestRate, Rate: integer; |
250 BaseRate, BestRate, Rate: LongInt; |
222 GoInfo: TGoInfo; |
251 GoInfo: TGoInfo; |
223 CanGo: boolean; |
252 CanGo: boolean; |
224 AltMe: TGear; |
253 AltMe: TGear; |
225 BotLevel: Byte; |
254 BotLevel: Byte; |
226 a: TAmmoType; |
255 a: TAmmoType; |
227 begin |
256 isAfterAttack: boolean; |
228 ticks:= 0; |
257 begin |
|
258 Actions.ticks:= 0; |
229 oldticks:= 0; // avoid compiler hint |
259 oldticks:= 0; // avoid compiler hint |
230 Stack.Count:= 0; |
260 Stack.Count:= 0; |
231 |
261 |
232 clearAllMarks; |
262 clearAllMarks; |
233 |
263 |
234 for a:= Low(TAmmoType) to High(TAmmoType) do |
264 for a:= Low(TAmmoType) to High(TAmmoType) do |
235 CanUseAmmo[a]:= Assigned(AmmoTests[a].proc) and (HHHasAmmo(Me^.Hedgehog^, a) > 0); |
265 CanUseAmmo[a]:= Assigned(AmmoTests[a].proc) and (HHHasAmmo(Me^.Hedgehog^, a) > 0); |
236 |
266 |
237 BotLevel:= Me^.Hedgehog^.BotLevel; |
267 BotLevel:= Me^.Hedgehog^.BotLevel; |
238 |
268 |
239 if (Me^.State and gstAttacked) = 0 then |
269 isAfterAttack:= ((Me^.State and gstAttacked) <> 0) and ((GameFlags and gfInfAttack) = 0); |
240 maxticks:= Max(0, TurnTimeLeft - 5000 - LongWord(4000 * BotLevel)) |
270 if isAfterAttack then |
|
271 maxticks:= Max(0, TurnTimeLeft - 500) |
241 else |
272 else |
242 maxticks:= TurnTimeLeft; |
273 maxticks:= Max(0, TurnTimeLeft - 5000 - LongWord(4000 * BotLevel)); |
243 |
274 |
244 if (Me^.State and gstAttacked) = 0 then |
275 if not isAfterAttack then |
245 TestAmmos(Actions, Me, false); |
276 TestAmmos(Actions, Me, false); |
246 |
277 |
247 BestRate:= RatePlace(Me); |
278 BestRate:= RatePlace(Me); |
248 BaseRate:= Max(BestRate, 0); |
279 BaseRate:= Max(BestRate, 0); |
249 |
280 |
250 // switch to 'skip' if we can't move because of mouse cursor being shown |
281 // switch to 'skip' if we cannot move because of mouse cursor being shown |
251 if (Ammoz[Me^.Hedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NeedTarget) <> 0 then |
282 if (Ammoz[Me^.Hedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NeedTarget) <> 0 then |
252 AddAction(Actions, aia_Weapon, Longword(amSkip), 100 + random(200), 0, 0); |
283 AddAction(Actions, aia_Weapon, Longword(amSkip), 100 + random(200), 0, 0); |
253 |
284 |
254 if ((CurrentHedgehog^.MultiShootAttacks = 0) or ((Ammoz[Me^.Hedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NoMoveAfter) = 0)) |
285 if ((CurrentHedgehog^.MultiShootAttacks = 0) or ((Ammoz[Me^.Hedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NoMoveAfter) = 0)) |
255 and (GameFlags and gfArtillery = 0) then |
286 and (CurrentHedgehog^.Effects[heArtillery] = 0) and (cGravityf <> 0) then |
256 begin |
287 begin |
257 tmp:= random(2) + 1; |
288 tmp:= random(2) + 1; |
258 Push(0, Actions, Me^, tmp); |
289 Push(Actions, Me^, tmp); |
259 Push(0, Actions, Me^, tmp xor 3); |
290 Push(Actions, Me^, tmp xor 3); |
260 |
291 |
261 while (Stack.Count > 0) and (not StopThinking) do |
292 while (Stack.Count > 0) and (not StopThinking) do |
262 begin |
293 begin |
263 Pop(ticks, Actions, Me^); |
294 Pop(Actions, Me^); |
264 |
295 |
265 AddAction(Actions, Me^.Message, aim_push, 250, 0, 0); |
296 AddAction(Actions, Me^.Message, aim_push, 250, 0, 0); |
266 if (Me^.Message and gmLeft) <> 0 then |
297 if (Me^.Message and gmLeft) <> 0 then |
267 AddAction(Actions, aia_WaitXL, hwRound(Me^.X), 0, 0, 0) |
298 AddAction(Actions, aia_WaitXL, hwRound(Me^.X), 0, 0, 0) |
268 else |
299 else |
269 AddAction(Actions, aia_WaitXR, hwRound(Me^.X), 0, 0, 0); |
300 AddAction(Actions, aia_WaitXR, hwRound(Me^.X), 0, 0, 0); |
270 |
301 |
271 steps:= 0; |
302 steps:= 0; |
272 |
303 |
273 while (not StopThinking) do |
304 while (not StopThinking) do |
274 begin |
305 begin |
275 {$HINTS OFF} |
306 {$HINTS OFF} |
276 CanGo:= HHGo(Me, @AltMe, GoInfo); |
307 CanGo:= HHGo(Me, @AltMe, GoInfo); |
277 {$HINTS ON} |
308 {$HINTS ON} |
278 oldticks:= ticks; |
309 oldticks:= Actions.ticks; |
279 inc(ticks, GoInfo.Ticks); |
310 inc(Actions.ticks, GoInfo.Ticks); |
280 if ticks > maxticks then |
311 if (Actions.ticks > maxticks) or (TurnTimeLeft < BestActions.ticks + 5000) then |
|
312 begin |
|
313 if (BotLevel < 5) |
|
314 and (not isAfterAttack) |
|
315 and (BestActions.Score > 0) // we have a good move |
|
316 and (TurnTimeLeft < BestActions.ticks + 5000) // we won't have a lot of time after attack |
|
317 and (HHHasAmmo(Me^.Hedgehog^, amExtraTime) > 0) // but can use extra time |
|
318 then |
|
319 begin |
|
320 BestActions.Count:= 0; |
|
321 AddAction(BestActions, aia_Weapon, Longword(amExtraTime), 80, 0, 0); |
|
322 AddAction(BestActions, aia_attack, aim_push, 10, 0, 0); |
|
323 AddAction(BestActions, aia_attack, aim_release, 10, 0, 0); |
|
324 end; |
|
325 |
281 break; |
326 break; |
282 |
327 end; |
283 if (BotLevel < 5) |
328 |
284 and (GoInfo.JumpType = jmpHJump) |
329 if (BotLevel < 5) |
|
330 and (GoInfo.JumpType = jmpHJump) |
285 and (not checkMark(hwRound(Me^.X), hwRound(Me^.Y), markHJumped)) |
331 and (not checkMark(hwRound(Me^.X), hwRound(Me^.Y), markHJumped)) |
286 then // hjump support |
332 then // hjump support |
287 begin |
333 begin |
288 // check if we could go backwards and maybe ljump over a gap after this hjump |
334 // check if we could go backwards and maybe ljump over a gap after this hjump |
289 addMark(hwRound(Me^.X), hwRound(Me^.Y), markHJumped); |
335 addMark(hwRound(Me^.X), hwRound(Me^.Y), markHJumped); |
290 if Push(ticks, Actions, AltMe, Me^.Message xor 3) then |
336 if Push(Actions, AltMe, Me^.Message xor 3) then |
291 begin |
337 begin |
292 with Stack.States[Pred(Stack.Count)] do |
338 with Stack.States[Pred(Stack.Count)] do |
293 begin |
339 begin |
294 if Me^.dX.isNegative then |
340 if (Me^.Message and gmLeft) <> 0 then |
295 AddAction(MadeActions, aia_LookRight, 0, 200, 0, 0) |
341 AddAction(MadeActions, aia_LookRight, 0, 200, 0, 0) |
296 else |
342 else |
297 AddAction(MadeActions, aia_LookLeft, 0, 200, 0, 0); |
343 AddAction(MadeActions, aia_LookLeft, 0, 200, 0, 0); |
298 |
344 |
299 AddAction(MadeActions, aia_HJump, 0, 305 + random(50), 0, 0); |
345 AddAction(MadeActions, aia_HJump, 0, 305 + random(50), 0, 0); |
300 AddAction(MadeActions, aia_HJump, 0, 350, 0, 0); |
346 AddAction(MadeActions, aia_HJump, 0, 350, 0, 0); |
301 |
347 end; |
302 if Me^.dX.isNegative then |
348 // but first check walking forward |
|
349 Push(Stack.States[Pred(Stack.Count)].MadeActions, AltMe, Me^.Message) |
|
350 end; |
|
351 end; |
|
352 if (BotLevel < 3) |
|
353 and (GoInfo.JumpType = jmpLJump) |
|
354 and (not checkMark(hwRound(Me^.X), hwRound(Me^.Y), markLJumped)) |
|
355 then // ljump support |
|
356 begin |
|
357 addMark(hwRound(Me^.X), hwRound(Me^.Y), markLJumped); |
|
358 // at final check where we go after jump walking backward |
|
359 if Push(Actions, AltMe, Me^.Message xor 3) then |
|
360 with Stack.States[Pred(Stack.Count)] do |
|
361 begin |
|
362 if (Me^.Message and gmLeft) <> 0 then |
303 AddAction(MadeActions, aia_LookLeft, 0, 200, 0, 0) |
363 AddAction(MadeActions, aia_LookLeft, 0, 200, 0, 0) |
304 else |
364 else |
305 AddAction(MadeActions, aia_LookRight, 0, 200, 0, 0); |
365 AddAction(MadeActions, aia_LookRight, 0, 200, 0, 0); |
|
366 |
|
367 AddAction(MadeActions, aia_LJump, 0, 305 + random(50), 0, 0); |
306 end; |
368 end; |
307 // but first check walking forward |
369 |
308 Push(ticks, Stack.States[Pred(Stack.Count)].MadeActions, AltMe, Me^.Message) |
370 // push current position so we proceed from it after checking jump+forward walk opportunities |
309 end; |
371 if CanGo then Push(Actions, Me^, Me^.Message); |
310 end; |
372 |
311 if (BotLevel < 3) |
373 // first check where we go after jump walking forward |
312 and (GoInfo.JumpType = jmpLJump) |
374 if Push(Actions, AltMe, Me^.Message) then |
313 and (not checkMark(hwRound(Me^.X), hwRound(Me^.Y), markLJumped)) |
|
314 then // ljump support |
|
315 begin |
|
316 addMark(hwRound(Me^.X), hwRound(Me^.Y), markLJumped); |
|
317 // at final check where we go after jump walking backward |
|
318 if Push(ticks, Actions, AltMe, Me^.Message xor 3) then |
|
319 with Stack.States[Pred(Stack.Count)] do |
375 with Stack.States[Pred(Stack.Count)] do |
320 AddAction(MadeActions, aia_LJump, 0, 305 + random(50), 0, 0); |
376 AddAction(MadeActions, aia_LJump, 0, 305 + random(50), 0, 0); |
321 |
377 |
322 // push current position so we proceed from it after checking jump+forward walk opportunities |
|
323 if CanGo then Push(ticks, Actions, Me^, Me^.Message); |
|
324 |
|
325 // first check where we go after jump walking forward |
|
326 if Push(ticks, Actions, AltMe, Me^.Message) then |
|
327 with Stack.States[Pred(Stack.Count)] do |
|
328 AddAction(MadeActions, aia_LJump, 0, 305 + random(50), 0, 0); |
|
329 break |
378 break |
330 end; |
379 end; |
331 |
380 |
332 // 'not CanGO' means we can't go straight, possible jumps are checked above |
381 // 'not CanGO' means we cannot go straight, possible jumps are checked above |
333 if not CanGo then |
382 if not CanGo then |
334 break; |
383 break; |
335 |
384 |
336 inc(steps); |
385 inc(steps); |
337 Actions.actions[Pred(Actions.Count)].Param:= hwRound(Me^.X); |
386 Actions.actions[Pred(Actions.Count)].Param:= hwRound(Me^.X); |
338 Rate:= RatePlace(Me); |
387 Rate:= RatePlace(Me); |
339 if Rate > BestRate then |
388 if Rate > BestRate then |
340 begin |
389 begin |
341 BestActions:= Actions; |
390 BestActions:= Actions; |
342 BestActions.isWalkingToABetterPlace:= true; |
391 BestActions.isWalkingToABetterPlace:= true; |
343 BestRate:= Rate; |
392 BestRate:= Rate; |
344 Me^.State:= Me^.State or gstAttacked // we have better place, go there and do not use ammo |
393 isAfterAttack:= true // we have better place, go there and do not use ammo |
345 end |
394 end |
346 else if Rate < BestRate then |
395 else if Rate < BestRate then |
347 break; |
396 break; |
348 |
397 |
349 if ((Me^.State and gstAttacked) = 0) and ((steps mod 4) = 0) then |
398 if (not isAfterAttack) and ((steps mod 4) = 0) then |
350 begin |
399 begin |
351 if (steps > 4) and checkMark(hwRound(Me^.X), hwRound(Me^.Y), markWalkedHere) then |
400 if (steps > 4) and checkMark(hwRound(Me^.X), hwRound(Me^.Y), markWalkedHere) then |
352 break; |
401 break; |
353 addMark(hwRound(Me^.X), hwRound(Me^.Y), markWalkedHere); |
402 addMark(hwRound(Me^.X), hwRound(Me^.Y), markWalkedHere); |
354 |
403 |
355 TestAmmos(Actions, Me, ticks shr 12 = oldticks shr 12); |
404 TestAmmos(Actions, Me, Actions.ticks shr 12 = oldticks shr 12); |
356 end; |
405 end; |
357 |
406 |
358 if GoInfo.FallPix >= FallPixForBranching then |
407 if GoInfo.FallPix >= FallPixForBranching then |
359 Push(ticks, Actions, Me^, Me^.Message xor 3); // aia_Left xor 3 = aia_Right |
408 Push(Actions, Me^, Me^.Message xor 3); // aia_Left xor 3 = aia_Right |
360 end {while}; |
409 end {while}; |
361 |
410 |
362 if BestRate > BaseRate then |
411 if BestRate > BaseRate then |
363 exit |
412 exit |
364 end {while} |
413 end {while} |
365 end {if} |
414 end {if} |
366 end; |
415 end; |
367 |
416 |
368 function Think(Me: Pointer): ptrint; |
417 function Think(Me: PGear): LongInt; cdecl; export; |
369 var BackMe, WalkMe: TGear; |
418 var BackMe, WalkMe: TGear; |
370 switchCount: LongInt; |
419 switchCount: LongInt; |
371 StartTicks, currHedgehogIndex, itHedgehog, switchesNum, i: Longword; |
420 currHedgehogIndex, itHedgehog, switchesNum, i: Longword; |
372 switchImmediatelyAvailable: boolean; |
421 switchImmediatelyAvailable: boolean; |
373 Actions: TActions; |
422 Actions: TActions; |
374 begin |
423 begin |
375 InterlockedIncrement(hasThread); |
424 dmgMod:= 0.01 * hwFloat2Float(cDamageModifier) * cDamagePercent; |
376 StartTicks:= GameTicks; |
425 StartTicks:= GameTicks; |
|
426 |
377 currHedgehogIndex:= CurrentTeam^.CurrHedgehog; |
427 currHedgehogIndex:= CurrentTeam^.CurrHedgehog; |
378 itHedgehog:= currHedgehogIndex; |
428 itHedgehog:= currHedgehogIndex; |
379 switchesNum:= 0; |
429 switchesNum:= 0; |
380 |
430 |
381 switchImmediatelyAvailable:= (CurAmmoGear <> nil) and (CurAmmoGear^.Kind = gtSwitcher); |
431 switchImmediatelyAvailable:= (CurAmmoGear <> nil) and (CurAmmoGear^.Kind = gtSwitcher); |
382 if PGear(Me)^.Hedgehog^.BotLevel <> 5 then |
432 if Me^.Hedgehog^.BotLevel <> 5 then |
383 switchCount:= HHHasAmmo(PGear(Me)^.Hedgehog^, amSwitch) |
433 switchCount:= HHHasAmmo(PGear(Me)^.Hedgehog^, amSwitch) |
384 else switchCount:= 0; |
434 else switchCount:= 0; |
385 |
435 |
386 if (PGear(Me)^.State and gstAttacked) = 0 then |
436 if ((Me^.State and gstAttacked) = 0) or isInMultiShoot or bonuses.activity or ((Me^.AIHints and aihAmmosChanged) <> 0) then |
387 if Targets.Count > 0 then |
437 if Targets.Count > 0 then |
388 begin |
438 begin |
389 // iterate over current team hedgehogs |
439 // iterate over current team hedgehogs |
390 repeat |
440 repeat |
391 WalkMe:= CurrentTeam^.Hedgehogs[itHedgehog].Gear^; |
441 WalkMe:= CurrentTeam^.Hedgehogs[itHedgehog].Gear^; |
409 Walk(@WalkMe, Actions); |
459 Walk(@WalkMe, Actions); |
410 |
460 |
411 // find another hog in team |
461 // find another hog in team |
412 repeat |
462 repeat |
413 itHedgehog:= Succ(itHedgehog) mod CurrentTeam^.HedgehogsNumber; |
463 itHedgehog:= Succ(itHedgehog) mod CurrentTeam^.HedgehogsNumber; |
414 until (itHedgehog = currHedgehogIndex) or (CurrentTeam^.Hedgehogs[itHedgehog].Gear <> nil); |
464 until (itHedgehog = currHedgehogIndex) or ((CurrentTeam^.Hedgehogs[itHedgehog].Gear <> nil) and (CurrentTeam^.Hedgehogs[itHedgehog].Effects[heFrozen]=0)); |
415 |
|
416 |
465 |
417 inc(switchesNum); |
466 inc(switchesNum); |
418 until (not (switchImmediatelyAvailable or (switchCount > 0))) |
467 until (not (switchImmediatelyAvailable or (switchCount > 0))) |
419 or StopThinking |
468 or StopThinking |
420 or (itHedgehog = currHedgehogIndex) |
469 or (itHedgehog = currHedgehogIndex) |
421 or BestActions.isWalkingToABetterPlace; |
470 or BestActions.isWalkingToABetterPlace; |
422 |
471 |
423 if (StartTicks > GameTicks - 1500) and (not StopThinking) then |
472 if (StartTicks > GameTicks - 1500) and (not StopThinking) then |
424 SDL_Delay(1000); |
473 SDL_Delay(700); |
425 |
474 |
426 if (BestActions.Score < -1023) and (not BestActions.isWalkingToABetterPlace) then |
475 if (BestActions.Score < -1023) and (not BestActions.isWalkingToABetterPlace) then |
427 begin |
476 begin |
428 BestActions.Count:= 0; |
477 BestActions.Count:= 0; |
429 AddAction(BestActions, aia_Skip, 0, 250, 0, 0); |
478 |
|
479 FillBonuses(false); |
|
480 |
|
481 // Hog has no idea what to do. Use tardis or skip |
|
482 if (not bonuses.activity) and ((Me^.AIHints and aihAmmosChanged) = 0) then |
|
483 if (((GameFlags and gfInfAttack) <> 0) or (CurrentHedgehog^.MultiShootAttacks = 0)) and (HHHasAmmo(Me^.Hedgehog^, amTardis) > 0) and (CanUseTardis(Me^.Hedgehog^.Gear)) and (random(4) < 3) then |
|
484 // Tardis brings hog to a random place. Perfect for clueless AI |
|
485 begin |
|
486 AddAction(BestActions, aia_Weapon, Longword(amTardis), 80, 0, 0); |
|
487 AddAction(BestActions, aia_attack, aim_push, 10, 0, 0); |
|
488 AddAction(BestActions, aia_attack, aim_release, 10, 0, 0); |
|
489 end |
|
490 else |
|
491 AddAction(BestActions, aia_Skip, 0, 250, 0, 0); |
|
492 Me^.AIHints := ME^.AIHints and (not aihAmmosChanged); |
430 end; |
493 end; |
431 |
494 |
432 end else |
495 end else SDL_Delay(100) |
433 else |
496 else |
434 begin |
497 begin |
435 BackMe:= PGear(Me)^; |
498 BackMe:= Me^; |
436 while (not StopThinking) and (BestActions.Count = 0) do |
499 i:= 4; |
|
500 while (not StopThinking) and (BestActions.Count = 0) and (i > 0) do |
437 begin |
501 begin |
|
502 |
438 (* |
503 (* |
439 // Maybe this would get a bit of movement out of them? Hopefully not *toward* water. Need to check how often he'd choose that strategy |
504 // Maybe this would get a bit of movement out of them? Hopefully not *toward* water. Need to check how often he'd choose that strategy |
440 if SuddenDeathDmg and ((hwRound(BackMe.Y)+cWaterRise*2) > cWaterLine) then |
505 if SuddenDeathDmg and ((hwRound(BackMe.Y)+cWaterRise*2) > cWaterLine) then |
441 AddBonus(hwRound(BackMe.X), hwRound(BackMe.Y), 250, -40); |
506 AddBonus(hwRound(BackMe.X), hwRound(BackMe.Y), 250, -40); |
442 *) |
507 *) |
|
508 |
443 FillBonuses(true); |
509 FillBonuses(true); |
444 WalkMe:= BackMe; |
510 WalkMe:= BackMe; |
445 Actions.Count:= 0; |
511 Actions.Count:= 0; |
446 Actions.Pos:= 0; |
512 Actions.Pos:= 0; |
447 Actions.Score:= 0; |
513 Actions.Score:= 0; |
448 Walk(@WalkMe, Actions); |
514 Walk(@WalkMe, Actions); |
|
515 if not bonuses.activity then dec(i); |
449 if not StopThinking then |
516 if not StopThinking then |
450 SDL_Delay(100) |
517 SDL_Delay(100) |
451 end |
518 end |
452 end; |
519 end; |
453 |
520 |
454 PGear(Me)^.State:= PGear(Me)^.State and (not gstHHThinking); |
521 Me^.State:= Me^.State and (not gstHHThinking); |
455 Think:= 0; |
522 Think:= 0; |
456 InterlockedDecrement(hasThread) |
523 SDL_SemPost(ThreadSem); |
457 end; |
524 end; |
458 |
525 |
459 procedure StartThink(Me: PGear); |
526 procedure StartThink(Me: PGear); |
|
527 var ThinkThread: PSDL_Thread; |
460 begin |
528 begin |
461 if ((Me^.State and (gstAttacking or gstHHJumping or gstMoving)) <> 0) |
529 if ((Me^.State and (gstAttacking or gstHHJumping or gstMoving)) <> 0) |
462 or isInMultiShoot then |
530 or isInMultiShoot then |
463 exit; |
531 exit; |
464 |
532 |
|
533 SDL_SemWait(ThreadSem); |
465 //DeleteCI(Me); // this will break demo/netplay |
534 //DeleteCI(Me); // this will break demo/netplay |
466 |
535 |
467 Me^.State:= Me^.State or gstHHThinking; |
536 Me^.State:= Me^.State or gstHHThinking; |
468 Me^.Message:= 0; |
537 Me^.Message:= 0; |
469 |
538 |
480 begin |
549 begin |
481 OutError('AI: no targets!?', false); |
550 OutError('AI: no targets!?', false); |
482 exit |
551 exit |
483 end; |
552 end; |
484 |
553 |
485 FillBonuses((Me^.State and gstAttacked) <> 0); |
554 FillBonuses(((Me^.State and gstAttacked) <> 0) and (not isInMultiShoot) and ((GameFlags and gfInfAttack) = 0)); |
486 AddFileLog('Enter Think Thread'); |
555 |
487 {$IFDEF USE_SDLTHREADS} |
556 ThinkThread:= SDL_CreateThread(@Think, PChar('think'), Me); |
488 ThinkThread := SDL_CreateThread(@Think{$IFDEF SDL13}, nil{$ENDIF}, Me); |
557 SDL_DetachThread(ThinkThread); |
489 {$ELSE} |
558 end; |
490 BeginThread(@Think, Me, ThinkThread); |
559 |
|
560 {$IFDEF DEBUGAI} |
|
561 var scoreShown: boolean = false; |
491 {$ENDIF} |
562 {$ENDIF} |
492 AddFileLog('Thread started'); |
|
493 end; |
|
494 |
|
495 //var scoreShown: boolean = false; |
|
496 |
563 |
497 procedure ProcessBot; |
564 procedure ProcessBot; |
498 const cStopThinkTime = 40; |
565 const cStopThinkTime = 40; |
499 begin |
566 begin |
500 with CurrentHedgehog^ do |
567 with CurrentHedgehog^ do |
501 if (Gear <> nil) |
568 if (Gear <> nil) |
502 and ((Gear^.State and gstHHDriven) <> 0) |
569 and ((Gear^.State and gstHHDriven) <> 0) |
503 and (TurnTimeLeft < cHedgehogTurnTime - 50) then |
570 and ((TurnTimeLeft < cHedgehogTurnTime - 50) or (TurnTimeLeft > cHedgehogTurnTime)) then |
504 if ((Gear^.State and gstHHThinking) = 0) then |
571 if ((Gear^.State and gstHHThinking) = 0) then |
505 if (BestActions.Pos >= BestActions.Count) |
572 if (BestActions.Pos >= BestActions.Count) |
506 and (TurnTimeLeft > cStopThinkTime) then |
573 and (TurnTimeLeft > cStopThinkTime) then |
507 begin |
574 begin |
508 if Gear^.Message <> 0 then |
575 if Gear^.Message <> 0 then |
509 begin |
576 begin |
510 StopMessages(Gear^.Message); |
577 StopMessages(Gear^.Message); |
511 TryDo((Gear^.Message and gmAllStoppable) = 0, 'Engine bug: AI may break demos playing', true); |
578 if checkFails((Gear^.Message and gmAllStoppable) = 0, 'Engine bug: AI may break demos playing', true) then exit; |
512 end; |
579 end; |
513 |
580 |
514 if Gear^.Message <> 0 then |
581 if Gear^.Message <> 0 then |
515 exit; |
582 exit; |
516 |
583 |
517 //scoreShown:= false; |
584 {$IFDEF DEBUGAI} |
|
585 scoreShown:= false; |
|
586 {$ENDIF} |
518 StartThink(Gear); |
587 StartThink(Gear); |
519 StartTicks:= GameTicks |
588 StartTicks:= GameTicks |
520 |
589 |
521 end else |
590 end else |
522 begin |
591 begin |
523 {if not scoreShown then |
592 {$IFDEF DEBUGAI} |
|
593 if not scoreShown then |
524 begin |
594 begin |
525 if BestActions.Score > 0 then ParseCommand('/say Expected score = ' + inttostr(BestActions.Score div 1024), true); |
595 if BestActions.Score > 0 then ParseCommand('/say Expected score = ' + inttostr(BestActions.Score div 1024), true); |
526 scoreShown:= true |
596 scoreShown:= true |
527 end;} |
597 end; |
|
598 {$ENDIF} |
528 ProcessAction(BestActions, Gear) |
599 ProcessAction(BestActions, Gear) |
529 end |
600 end |
530 else if ((GameTicks - StartTicks) > cMaxAIThinkTime) |
601 else if ((GameTicks - StartTicks) > cMaxAIThinkTime) |
531 or (TurnTimeLeft <= cStopThinkTime) then |
602 or (TurnTimeLeft <= cStopThinkTime) then |
532 StopThinking:= true |
603 StopThinking:= true |
533 end; |
604 end; |
534 |
605 |
535 procedure initModule; |
606 procedure initModule; |
536 begin |
607 begin |
537 hasThread:= 0; |
|
538 StartTicks:= 0; |
608 StartTicks:= 0; |
539 ThinkThread:= ThinkThread; |
609 ThreadSem:= SDL_CreateSemaphore(1); |
540 end; |
610 end; |
541 |
611 |
542 procedure freeModule; |
612 procedure freeModule; |
543 begin |
613 begin |
544 FreeActionsList(); |
614 FreeActionsList(); |
|
615 SDL_DestroySemaphore(ThreadSem); |
545 end; |
616 end; |
546 |
617 |
547 end. |
618 end. |