50 procedure DrawGears; |
50 procedure DrawGears; |
51 procedure FreeGearsList; |
51 procedure FreeGearsList; |
52 procedure AddMiscGears; |
52 procedure AddMiscGears; |
53 procedure AssignHHCoords; |
53 procedure AssignHHCoords; |
54 function GearByUID(uid : Longword) : PGear; |
54 function GearByUID(uid : Longword) : PGear; |
55 procedure InsertGearToList(Gear: PGear); |
55 procedure doStepDrowningGear(Gear: PGear); |
56 procedure RemoveGearFromList(Gear: PGear); |
|
57 procedure DeleteGear(Gear: PGear); |
|
58 |
56 |
59 |
57 |
60 implementation |
58 implementation |
61 uses uStore, uSound, uTeams, uRandom, uCollisions, uIO, uLandGraphics, |
59 uses uStore, uSound, uTeams, uRandom, uCollisions, uIO, uLandGraphics, |
62 uAIMisc, uLocale, uAI, uAmmos, uStats, uVisualGears, uScript, GLunit, uMobile, uVariables, |
60 uAIMisc, uLocale, uAI, uAmmos, uStats, uVisualGears, uScript, GLunit, uMobile, uVariables, |
63 uCommands, uUtils, uTextures, uRenderUtils, uGearsRender, uCaptions, uDebug, uLandTexture, |
61 uCommands, uUtils, uTextures, uRenderUtils, uGearsRender, uCaptions, uDebug, uLandTexture, |
64 uGearsHedgehog; |
62 uGearsHedgehog, uGearsUtils, uGearsList; |
65 |
63 |
66 |
64 |
67 procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); forward; |
65 procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); forward; |
68 //procedure AmmoFlameWork(Ammo: PGear); forward; |
66 //procedure AmmoFlameWork(Ammo: PGear); forward; |
69 function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): TPGearArray; forward; |
67 function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): TPGearArray; forward; |
70 procedure SpawnBoxOfSmth; forward; |
68 procedure SpawnBoxOfSmth; forward; |
71 procedure ShotgunShot(Gear: PGear); forward; |
69 procedure ShotgunShot(Gear: PGear); forward; |
72 procedure PickUp(HH, Gear: PGear); forward; |
|
73 procedure HHSetWeapon(HHGear: PGear); forward; |
|
74 procedure doStepCase(Gear: PGear); forward; |
70 procedure doStepCase(Gear: PGear); forward; |
75 |
71 |
76 // For better maintainability the step handlers of gears are stored in |
72 // For better maintainability the step handlers of gears are stored in |
77 // separate files. |
73 // separate files. |
78 // Note: step handlers of gears that are hedgehogs are in a different file |
74 // Note: step handlers of gears that are hedgehogs are in a different file |
79 // than the handlers for all other gears. |
75 // than the handlers for all other gears. |
80 {$INCLUDE "GSHandlers.inc"} |
76 {$INCLUDE "GSHandlers.inc"} |
81 |
77 |
82 const doStepHandlers: array[TGearType] of TGearStepProcedure = ( |
78 function CheckNoDamage: boolean; // returns TRUE in case of no damaged hhs |
|
79 var Gear: PGear; |
|
80 dmg: LongInt; |
|
81 begin |
|
82 CheckNoDamage:= true; |
|
83 Gear:= GearsList; |
|
84 while Gear <> nil do |
|
85 begin |
|
86 if (Gear^.Kind = gtHedgehog) and (((GameFlags and gfInfAttack) = 0) or ((Gear^.dX.QWordValue < _0_000004.QWordValue) and (Gear^.dY.QWordValue < _0_000004.QWordValue))) then |
|
87 begin |
|
88 if (not isInMultiShoot) then inc(Gear^.Damage, Gear^.Karma); |
|
89 if (Gear^.Damage <> 0) and |
|
90 (not Gear^.Invulnerable) then |
|
91 begin |
|
92 CheckNoDamage:= false; |
|
93 |
|
94 dmg:= Gear^.Damage; |
|
95 if Gear^.Health < dmg then |
|
96 begin |
|
97 Gear^.Active:= true; |
|
98 Gear^.Health:= 0 |
|
99 end |
|
100 else |
|
101 dec(Gear^.Health, dmg); |
|
102 |
|
103 if (Gear^.Hedgehog^.Team = CurrentTeam) and |
|
104 (Gear^.Damage <> Gear^.Karma) and |
|
105 (not Gear^.Hedgehog^.King) and |
|
106 (not Gear^.Hedgehog^.Effects[hePoisoned]) and |
|
107 (not SuddenDeathDmg) then |
|
108 Gear^.State:= Gear^.State or gstLoser; |
|
109 |
|
110 spawnHealthTagForHH(Gear, dmg); |
|
111 |
|
112 RenderHealth(Gear^.Hedgehog^); |
|
113 RecountTeamHealth(Gear^.Hedgehog^.Team); |
|
114 |
|
115 end; |
|
116 if (not isInMultiShoot) then Gear^.Karma:= 0; |
|
117 Gear^.Damage:= 0 |
|
118 end; |
|
119 Gear:= Gear^.NextGear |
|
120 end; |
|
121 end; |
|
122 |
|
123 procedure HealthMachine; |
|
124 var Gear: PGear; |
|
125 team: PTeam; |
|
126 i: LongWord; |
|
127 flag: Boolean; |
|
128 tmp: LongWord; |
|
129 begin |
|
130 Gear:= GearsList; |
|
131 |
|
132 while Gear <> nil do |
|
133 begin |
|
134 if Gear^.Kind = gtHedgehog then |
|
135 begin |
|
136 tmp:= 0; |
|
137 if Gear^.Hedgehog^.Effects[hePoisoned] then |
|
138 begin |
|
139 inc(tmp, ModifyDamage(5, Gear)); |
|
140 if (GameFlags and gfResetHealth) <> 0 then dec(Gear^.Hedgehog^.InitialHealth) // does not need a minimum check since <= 1 basically disables it |
|
141 end; |
|
142 if (TotalRounds > cSuddenDTurns - 1) then |
|
143 begin |
|
144 inc(tmp, cHealthDecrease); |
|
145 if (GameFlags and gfResetHealth) <> 0 then dec(Gear^.Hedgehog^.InitialHealth, cHealthDecrease) |
|
146 end; |
|
147 if Gear^.Hedgehog^.King then |
|
148 begin |
|
149 flag:= false; |
|
150 team:= Gear^.Hedgehog^.Team; |
|
151 for i:= 0 to Pred(team^.HedgehogsNumber) do |
|
152 if (team^.Hedgehogs[i].Gear <> nil) and |
|
153 (not team^.Hedgehogs[i].King) and |
|
154 (team^.Hedgehogs[i].Gear^.Health > team^.Hedgehogs[i].Gear^.Damage) |
|
155 then flag:= true; |
|
156 if not flag then |
|
157 begin |
|
158 inc(tmp, 5); |
|
159 if (GameFlags and gfResetHealth) <> 0 then dec(Gear^.Hedgehog^.InitialHealth, 5) |
|
160 end |
|
161 end; |
|
162 if tmp > 0 then |
|
163 begin |
|
164 inc(Gear^.Damage, min(tmp, max(0,Gear^.Health - 1 - Gear^.Damage))); |
|
165 HHHurt(Gear^.Hedgehog, dsPoison); |
|
166 end |
|
167 end; |
|
168 |
|
169 Gear:= Gear^.NextGear |
|
170 end; |
|
171 end; |
|
172 |
|
173 procedure ProcessGears; |
|
174 const delay: LongWord = 0; |
|
175 delay2: LongWord = 0; |
|
176 step: (stDelay, stChDmg, stSweep, stTurnReact, |
|
177 stAfterDelay, stChWin, stWater, stChWin2, stHealth, |
|
178 stSpawn, stNTurn) = stDelay; |
|
179 var Gear, t: PGear; |
|
180 i, AliveCount: LongInt; |
|
181 s: shortstring; |
|
182 begin |
|
183 PrvInactive:= AllInactive; |
|
184 AllInactive:= true; |
|
185 |
|
186 if (StepSoundTimer > 0) and (StepSoundChannel < 0) then |
|
187 StepSoundChannel:= LoopSound(sndSteps) |
|
188 else if (StepSoundTimer = 0) and (StepSoundChannel > -1) then |
|
189 begin |
|
190 StopSound(StepSoundChannel); |
|
191 StepSoundChannel:= -1 |
|
192 end; |
|
193 |
|
194 if StepSoundTimer > 0 then |
|
195 dec(StepSoundTimer, 1); |
|
196 |
|
197 t:= GearsList; |
|
198 while t <> nil do |
|
199 begin |
|
200 Gear:= t; |
|
201 t:= Gear^.NextGear; |
|
202 |
|
203 if Gear^.Active then |
|
204 begin |
|
205 if Gear^.RenderTimer and (Gear^.Timer > 500) and ((Gear^.Timer mod 1000) = 0) then |
|
206 begin |
|
207 FreeTexture(Gear^.Tex); |
|
208 Gear^.Tex:= RenderStringTex(inttostr(Gear^.Timer div 1000), cWhiteColor, fntSmall); |
|
209 end; |
|
210 Gear^.doStep(Gear); |
|
211 // might be useful later |
|
212 //ScriptCall('onGearStep', Gear^.uid); |
|
213 end |
|
214 end; |
|
215 |
|
216 if AllInactive then |
|
217 case step of |
|
218 stDelay: begin |
|
219 if delay = 0 then |
|
220 delay:= cInactDelay |
|
221 else |
|
222 dec(delay); |
|
223 |
|
224 if delay = 0 then |
|
225 inc(step) |
|
226 end; |
|
227 stChDmg: if CheckNoDamage then inc(step) else step:= stDelay; |
|
228 stSweep: if SweepDirty then |
|
229 begin |
|
230 SetAllToActive; |
|
231 step:= stChDmg |
|
232 end else inc(step); |
|
233 stTurnReact: begin |
|
234 if (not bBetweenTurns) and (not isInMultiShoot) then |
|
235 begin |
|
236 uStats.TurnReaction; |
|
237 inc(step) |
|
238 end else |
|
239 inc(step, 2); |
|
240 end; |
|
241 stAfterDelay: begin |
|
242 if delay = 0 then |
|
243 delay:= cInactDelay |
|
244 else |
|
245 dec(delay); |
|
246 |
|
247 if delay = 0 then |
|
248 inc(step) |
|
249 end; |
|
250 stChWin: begin |
|
251 CheckForWin; |
|
252 inc(step) |
|
253 end; |
|
254 stWater: if (not bBetweenTurns) and (not isInMultiShoot) then |
|
255 begin |
|
256 if TotalRounds = cSuddenDTurns + 1 then bWaterRising:= true; |
|
257 |
|
258 if bWaterRising and (cWaterRise > 0) then |
|
259 AddGear(0, 0, gtWaterUp, 0, _0, _0, 0)^.Tag:= cWaterRise; |
|
260 |
|
261 inc(step) |
|
262 end else inc(step); |
|
263 stChWin2: begin |
|
264 CheckForWin; |
|
265 inc(step) |
|
266 end; |
|
267 stHealth: begin |
|
268 if (cWaterRise <> 0) or (cHealthDecrease <> 0) then |
|
269 begin |
|
270 if (TotalRounds = cSuddenDTurns) and (not SuddenDeath) and (not isInMultiShoot) then |
|
271 begin |
|
272 SuddenDeath:= true; |
|
273 if cHealthDecrease <> 0 then |
|
274 begin |
|
275 SuddenDeathDmg:= true; |
|
276 |
|
277 // flash |
|
278 ScreenFade:= sfFromWhite; |
|
279 ScreenFadeValue:= sfMax; |
|
280 ScreenFadeSpeed:= 1; |
|
281 |
|
282 ChangeToSDClouds; |
|
283 ChangeToSDFlakes; |
|
284 glClearColor(SDSkyColor.r * (SDTint/255) / 255, SDSkyColor.g * (SDTint/255) / 255, SDSkyColor.b * (SDTint/255) / 255, 0.99); |
|
285 Ammoz[amTardis].SkipTurns:= 9999; |
|
286 Ammoz[amTardis].Probability:= 0; |
|
287 end; |
|
288 AddCaption(trmsg[sidSuddenDeath], cWhiteColor, capgrpGameState); |
|
289 playSound(sndSuddenDeath); |
|
290 StopMusic //No SDMusic for now |
|
291 //MusicFN:= SDMusic; |
|
292 //ChangeMusic |
|
293 end |
|
294 else if (TotalRounds < cSuddenDTurns) and (not isInMultiShoot) then |
|
295 begin |
|
296 i:= cSuddenDTurns - TotalRounds; |
|
297 s:= inttostr(i); |
|
298 if i = 1 then |
|
299 AddCaption(trmsg[sidRoundSD], cWhiteColor, capgrpGameState) |
|
300 else if (i = 2) or ((i > 0) and ((i mod 50 = 0) or ((i <= 25) and (i mod 5 = 0)))) then |
|
301 AddCaption(Format(trmsg[sidRoundsSD], s), cWhiteColor, capgrpGameState); |
|
302 end; |
|
303 end; |
|
304 if bBetweenTurns |
|
305 or isInMultiShoot |
|
306 or (TotalRounds = -1) then inc(step) |
|
307 else begin |
|
308 bBetweenTurns:= true; |
|
309 HealthMachine; |
|
310 step:= stChDmg |
|
311 end |
|
312 end; |
|
313 stSpawn: begin |
|
314 if not isInMultiShoot then SpawnBoxOfSmth; |
|
315 inc(step) |
|
316 end; |
|
317 stNTurn: begin |
|
318 if isInMultiShoot then |
|
319 isInMultiShoot:= false |
|
320 else begin |
|
321 // delayed till after 0.9.12 |
|
322 // reset to default zoom |
|
323 //ZoomValue:= ZoomDefault; |
|
324 with CurrentHedgehog^ do |
|
325 if (Gear <> nil) |
|
326 and ((Gear^.State and gstAttacked) = 0) |
|
327 and (MultiShootAttacks > 0) then OnUsedAmmo(CurrentHedgehog^); |
|
328 |
|
329 EndTurnCleanup; |
|
330 |
|
331 FreeActionsList; // could send -left, -right and similar commands, so should be called before /nextturn |
|
332 |
|
333 ParseCommand('/nextturn', true); |
|
334 SwitchHedgehog; |
|
335 |
|
336 AfterSwitchHedgehog; |
|
337 bBetweenTurns:= false |
|
338 end; |
|
339 step:= Low(step) |
|
340 end; |
|
341 end |
|
342 else if ((GameFlags and gfInfAttack) <> 0) then |
|
343 begin |
|
344 if delay2 = 0 then |
|
345 delay2:= cInactDelay * 50 |
|
346 else |
|
347 begin |
|
348 dec(delay2); |
|
349 |
|
350 if ((delay2 mod cInactDelay) = 0) and (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and (not CurrentHedgehog^.Unplaced) then |
|
351 begin |
|
352 if (CurrentHedgehog^.Gear^.State and gstAttacked <> 0) and (Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0) then |
|
353 begin |
|
354 CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State or gstHHChooseTarget; |
|
355 isCursorVisible := true |
|
356 end; |
|
357 CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State and (not gstAttacked); |
|
358 end; |
|
359 if delay2 = 0 then |
|
360 begin |
|
361 if (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.State and gstAttacked = 0) and (CurAmmoGear = nil) then SweepDirty; |
|
362 CheckNoDamage; |
|
363 AliveCount:= 0; // shorter version of check for win to allow typical step activity to proceed |
|
364 for i:= 0 to Pred(ClansCount) do |
|
365 if ClansArray[i]^.ClanHealth > 0 then inc(AliveCount); |
|
366 if (AliveCount <= 1) and ((GameFlags and gfOneClanMode) = 0) then |
|
367 begin |
|
368 step:= stChDmg; |
|
369 if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft; |
|
370 TurnTimeLeft:= 0 |
|
371 end |
|
372 end |
|
373 end |
|
374 end; |
|
375 |
|
376 if TurnTimeLeft > 0 then |
|
377 if CurrentHedgehog^.Gear <> nil then |
|
378 if ((CurrentHedgehog^.Gear^.State and gstAttacking) = 0) |
|
379 and (not isInMultiShoot) then |
|
380 begin |
|
381 if (TurnTimeLeft = 5000) |
|
382 and (cHedgehogTurnTime >= 10000) |
|
383 and (not PlacingHogs) |
|
384 and (CurrentHedgehog^.Gear <> nil) |
|
385 and ((CurrentHedgehog^.Gear^.State and gstAttacked) = 0) then |
|
386 AddVoice(sndHurry, CurrentTeam^.voicepack); |
|
387 if ReadyTimeLeft > 0 then |
|
388 begin |
|
389 if ReadyTimeLeft = 2000 then |
|
390 AddVoice(sndComeonthen, CurrentTeam^.voicepack); |
|
391 dec(ReadyTimeLeft) |
|
392 end |
|
393 else |
|
394 dec(TurnTimeLeft) |
|
395 end; |
|
396 |
|
397 if skipFlag then |
|
398 begin |
|
399 if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft; |
|
400 TurnTimeLeft:= 0; |
|
401 skipFlag:= false; |
|
402 inc(CurrentHedgehog^.Team^.stats.TurnSkips); |
|
403 end; |
|
404 |
|
405 if ((GameTicks and $FFFF) = $FFFF) then |
|
406 begin |
|
407 if (not CurrentTeam^.ExtDriven) then |
|
408 begin |
|
409 SendIPC('#'); |
|
410 AddFileLog('hiTicks increment message sent') |
|
411 end; |
|
412 |
|
413 if (not CurrentTeam^.ExtDriven) or CurrentTeam^.hasGone then |
|
414 inc(hiTicks) // we do not recieve a message for this |
|
415 end; |
|
416 |
|
417 ScriptCall('onGameTick'); |
|
418 inc(GameTicks) |
|
419 end; |
|
420 |
|
421 //Purpose, to reset all transient attributes toggled by a utility and clean up various gears and effects at end of turn |
|
422 //If any of these are set as permanent toggles in the frontend, that needs to be checked and skipped here. |
|
423 procedure EndTurnCleanup; |
|
424 var i: LongInt; |
|
425 t: PGear; |
|
426 begin |
|
427 SpeechText:= ''; // in case it has not been consumed |
|
428 |
|
429 if (GameFlags and gfLowGravity) = 0 then |
|
430 begin |
|
431 cGravity:= cMaxWindSpeed * 2; |
|
432 cGravityf:= 0.00025 * 2 |
|
433 end; |
|
434 |
|
435 if (GameFlags and gfVampiric) = 0 then |
|
436 cVampiric:= false; |
|
437 |
|
438 cDamageModifier:= _1; |
|
439 |
|
440 if (GameFlags and gfLaserSight) = 0 then |
|
441 cLaserSighting:= false; |
|
442 |
|
443 if (GameFlags and gfArtillery) = 0 then |
|
444 cArtillery:= false; |
|
445 // have to sweep *all* current team hedgehogs since it is theoretically possible if you have enough invulnerabilities and switch turns to make your entire team invulnerable |
|
446 if (CurrentTeam <> nil) then |
|
447 with CurrentTeam^ do |
|
448 for i:= 0 to cMaxHHIndex do |
|
449 with Hedgehogs[i] do |
|
450 begin |
|
451 (* |
|
452 if (SpeechGear <> nil) then |
|
453 begin |
|
454 DeleteVisualGear(SpeechGear); // remove to restore persisting beyond end of turn. Tiy says was too much of a gameplay issue |
|
455 SpeechGear:= nil |
|
456 end; |
|
457 *) |
|
458 |
|
459 if (Gear <> nil) then |
|
460 begin |
|
461 if (GameFlags and gfInvulnerable) = 0 then |
|
462 Gear^.Invulnerable:= false; |
|
463 end; |
|
464 end; |
|
465 t:= GearsList; |
|
466 while t <> nil do |
|
467 begin |
|
468 t^.PortalCounter:= 0; |
|
469 if ((GameFlags and gfResetHealth) <> 0) and (t^.Kind = gtHedgehog) and (t^.Health < t^.Hedgehog^.InitialHealth) then |
|
470 begin |
|
471 t^.Health:= t^.Hedgehog^.InitialHealth; |
|
472 RenderHealth(t^.Hedgehog^); |
|
473 end; |
|
474 t:= t^.NextGear |
|
475 end; |
|
476 |
|
477 if ((GameFlags and gfResetWeps) <> 0) and (not PlacingHogs) then |
|
478 ResetWeapons; |
|
479 |
|
480 if (GameFlags and gfResetHealth) <> 0 then |
|
481 for i:= 0 to Pred(TeamsCount) do |
|
482 RecountTeamHealth(TeamsArray[i]) |
|
483 end; |
|
484 |
|
485 procedure SetAllToActive; |
|
486 var t: PGear; |
|
487 begin |
|
488 AllInactive:= false; |
|
489 t:= GearsList; |
|
490 while t <> nil do |
|
491 begin |
|
492 t^.Active:= true; |
|
493 t:= t^.NextGear |
|
494 end |
|
495 end; |
|
496 |
|
497 procedure SetAllHHToActive; |
|
498 var t: PGear; |
|
499 begin |
|
500 AllInactive:= false; |
|
501 t:= GearsList; |
|
502 while t <> nil do |
|
503 begin |
|
504 if (t^.Kind = gtHedgehog) or (t^.Kind = gtExplosives) then t^.Active:= true; |
|
505 t:= t^.NextGear |
|
506 end |
|
507 end; |
|
508 |
|
509 |
|
510 procedure DrawGears; |
|
511 var Gear: PGear; |
|
512 x, y: LongInt; |
|
513 begin |
|
514 Gear:= GearsList; |
|
515 while Gear <> nil do |
|
516 begin |
|
517 if Gear^.State and gstInvisible = 0 then |
|
518 begin |
|
519 x:= hwRound(Gear^.X) + WorldDx; |
|
520 y:= hwRound(Gear^.Y) + WorldDy; |
|
521 RenderGear(Gear, x, y); |
|
522 end; |
|
523 Gear:= Gear^.NextGear |
|
524 end; |
|
525 end; |
|
526 |
|
527 procedure FreeGearsList; |
|
528 var t, tt: PGear; |
|
529 begin |
|
530 tt:= GearsList; |
|
531 GearsList:= nil; |
|
532 while tt <> nil do |
|
533 begin |
|
534 t:= tt; |
|
535 tt:= tt^.NextGear; |
|
536 Dispose(t) |
|
537 end; |
|
538 end; |
|
539 |
|
540 procedure AddMiscGears; |
|
541 var i: Longword; |
|
542 Gear: PGear; |
|
543 begin |
|
544 AddGear(0, 0, gtATStartGame, 0, _0, _0, 2000); |
|
545 |
|
546 i:= 0; |
|
547 Gear:= PGear(1); |
|
548 while (i < cLandMines) {and (Gear <> nil)} do // disable this check until better solution found |
|
549 begin |
|
550 Gear:= AddGear(0, 0, gtMine, 0, _0, _0, 0); |
|
551 FindPlace(Gear, false, 0, LAND_WIDTH); |
|
552 inc(i) |
|
553 end; |
|
554 |
|
555 i:= 0; |
|
556 Gear:= PGear(1); |
|
557 while (i < cExplosives){ and (Gear <> nil)} do |
|
558 begin |
|
559 Gear:= AddGear(0, 0, gtExplosives, 0, _0, _0, 0); |
|
560 FindPlace(Gear, false, 0, LAND_WIDTH); |
|
561 inc(i) |
|
562 end; |
|
563 |
|
564 if (GameFlags and gfLowGravity) <> 0 then |
|
565 begin |
|
566 cGravity:= cMaxWindSpeed; |
|
567 cGravityf:= 0.00025 |
|
568 end; |
|
569 |
|
570 if (GameFlags and gfVampiric) <> 0 then |
|
571 cVampiric:= true; |
|
572 |
|
573 Gear:= GearsList; |
|
574 if (GameFlags and gfInvulnerable) <> 0 then |
|
575 while Gear <> nil do |
|
576 begin |
|
577 Gear^.Invulnerable:= true; // this is only checked on hogs right now, so no need for gear type check |
|
578 Gear:= Gear^.NextGear |
|
579 end; |
|
580 |
|
581 if (GameFlags and gfLaserSight) <> 0 then |
|
582 cLaserSighting:= true; |
|
583 |
|
584 if (GameFlags and gfArtillery) <> 0 then |
|
585 cArtillery:= true; |
|
586 |
|
587 if not hasBorder and ((Theme = 'Snow') or (Theme = 'Christmas')) then |
|
588 for i:= 0 to Pred(vobCount*2) do |
|
589 AddGear(GetRandom(LAND_WIDTH+1024)-512, LAND_HEIGHT - GetRandom(LAND_HEIGHT div 2), gtFlake, 0, _0, _0, 0); |
|
590 end; |
|
591 |
|
592 |
|
593 procedure ShotgunShot(Gear: PGear); |
|
594 var t: PGear; |
|
595 dmg, r, dist: LongInt; |
|
596 dx, dy: hwFloat; |
|
597 begin |
|
598 Gear^.Radius:= cShotgunRadius; |
|
599 t:= GearsList; |
|
600 while t <> nil do |
|
601 begin |
|
602 case t^.Kind of |
|
603 gtHedgehog, |
|
604 gtMine, |
|
605 gtSMine, |
|
606 gtCase, |
|
607 gtTarget, |
|
608 gtExplosives, |
|
609 gtStructure: begin |
|
610 //addFileLog('ShotgunShot radius: ' + inttostr(Gear^.Radius) + ', t^.Radius = ' + inttostr(t^.Radius) + ', distance = ' + inttostr(dist) + ', dmg = ' + inttostr(dmg)); |
|
611 dmg:= 0; |
|
612 r:= Gear^.Radius + t^.Radius; |
|
613 dx:= Gear^.X-t^.X; |
|
614 dx.isNegative:= false; |
|
615 dy:= Gear^.Y-t^.Y; |
|
616 dy.isNegative:= false; |
|
617 if r-hwRound(dx+dy) > 0 then |
|
618 begin |
|
619 dist:= hwRound(Distance(dx, dy)); |
|
620 dmg:= ModifyDamage(min(r - dist, 25), t); |
|
621 end; |
|
622 if dmg > 0 then |
|
623 begin |
|
624 if (not t^.Invulnerable) then |
|
625 ApplyDamage(t, Gear^.Hedgehog, dmg, dsBullet) |
|
626 else |
|
627 Gear^.State:= Gear^.State or gstWinner; |
|
628 |
|
629 DeleteCI(t); |
|
630 t^.dX:= t^.dX + Gear^.dX * dmg * _0_01 + SignAs(cHHKick, Gear^.dX); |
|
631 t^.dY:= t^.dY + Gear^.dY * dmg * _0_01; |
|
632 t^.State:= t^.State or gstMoving; |
|
633 t^.Active:= true; |
|
634 FollowGear:= t |
|
635 end |
|
636 end; |
|
637 gtGrave: begin |
|
638 dmg:= 0; |
|
639 r:= Gear^.Radius + t^.Radius; |
|
640 dx:= Gear^.X-t^.X; |
|
641 dx.isNegative:= false; |
|
642 dy:= Gear^.Y-t^.Y; |
|
643 dy.isNegative:= false; |
|
644 if r-hwRound(dx+dy) > 0 then |
|
645 begin |
|
646 dist:= hwRound(Distance(dx, dy)); |
|
647 dmg:= ModifyDamage(min(r - dist, 25), t); |
|
648 end; |
|
649 if dmg > 0 then |
|
650 begin |
|
651 t^.dY:= - _0_1; |
|
652 t^.Active:= true |
|
653 end |
|
654 end; |
|
655 end; |
|
656 t:= t^.NextGear |
|
657 end; |
|
658 if (GameFlags and gfSolidLand) = 0 then DrawExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cShotgunRadius) |
|
659 end; |
|
660 |
|
661 procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); |
|
662 var t: PGearArray; |
|
663 Gear: PGear; |
|
664 i, tmpDmg: LongInt; |
|
665 VGear: PVisualGear; |
|
666 begin |
|
667 t:= CheckGearsCollision(Ammo); |
|
668 // Just to avoid hogs on rope dodging fire. |
|
669 if (CurAmmoGear <> nil) and ((CurAmmoGear^.Kind = gtRope) or (CurAmmoGear^.Kind = gtJetpack) or (CurAmmoGear^.Kind = gtBirdy)) and |
|
670 (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.CollisionIndex = -1) and |
|
671 (sqr(hwRound(Ammo^.X) - hwRound(CurrentHedgehog^.Gear^.X)) + sqr(hwRound(Ammo^.Y) - hwRound(CurrentHedgehog^.Gear^.Y)) <= sqr(cHHRadius + Ammo^.Radius)) then |
|
672 begin |
|
673 t^.ar[t^.Count]:= CurrentHedgehog^.Gear; |
|
674 inc(t^.Count) |
|
675 end; |
|
676 |
|
677 i:= t^.Count; |
|
678 |
|
679 if (Ammo^.Kind = gtFlame) and (i > 0) then Ammo^.Health:= 0; |
|
680 while i > 0 do |
|
681 begin |
|
682 dec(i); |
|
683 Gear:= t^.ar[i]; |
|
684 tmpDmg:= ModifyDamage(Damage, Gear); |
|
685 if (Gear^.State and gstNoDamage) = 0 then |
|
686 begin |
|
687 |
|
688 if (Ammo^.Kind = gtDEagleShot) or (Ammo^.Kind = gtSniperRifleShot) then |
|
689 begin |
|
690 VGear := AddVisualGear(hwround(Ammo^.X), hwround(Ammo^.Y), vgtBulletHit); |
|
691 if VGear <> nil then VGear^.Angle := DxDy2Angle(-Ammo^.dX, Ammo^.dY); |
|
692 end; |
|
693 |
|
694 if (Gear^.Kind = gtHedgehog) and (Ammo^.State and gsttmpFlag <> 0) and (Ammo^.Kind = gtShover) then Gear^.FlightTime:= 1; |
|
695 |
|
696 case Gear^.Kind of |
|
697 gtHedgehog, |
|
698 gtMine, |
|
699 gtSMine, |
|
700 gtTarget, |
|
701 gtCase, |
|
702 gtExplosives, |
|
703 gtStructure: begin |
|
704 if (Ammo^.Kind = gtDrill) then begin Ammo^.Timer:= 0; exit; end; |
|
705 if (not Gear^.Invulnerable) then |
|
706 ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg, dsShove) |
|
707 else |
|
708 Gear^.State:= Gear^.State or gstWinner; |
|
709 if (Gear^.Kind = gtExplosives) and (Ammo^.Kind = gtBlowtorch) then |
|
710 begin |
|
711 if (Ammo^.Hedgehog^.Gear <> nil) then Ammo^.Hedgehog^.Gear^.State:= Ammo^.Hedgehog^.Gear^.State and (not gstNotKickable); |
|
712 ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg * 100, dsUnknown); // crank up damage for explosives + blowtorch |
|
713 end; |
|
714 |
|
715 DeleteCI(Gear); |
|
716 if (Gear^.Kind = gtHedgehog) and Gear^.Hedgehog^.King then |
|
717 begin |
|
718 Gear^.dX:= Ammo^.dX * Power * _0_005; |
|
719 Gear^.dY:= Ammo^.dY * Power * _0_005 |
|
720 end |
|
721 else |
|
722 begin |
|
723 Gear^.dX:= Ammo^.dX * Power * _0_01; |
|
724 Gear^.dY:= Ammo^.dY * Power * _0_01 |
|
725 end; |
|
726 |
|
727 Gear^.Active:= true; |
|
728 Gear^.State:= Gear^.State or gstMoving; |
|
729 |
|
730 if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then |
|
731 begin |
|
732 if not (TestCollisionXwithXYShift(Gear, _0, -3, hwSign(Gear^.dX)) |
|
733 or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1; |
|
734 if not (TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) |
|
735 or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1; |
|
736 if not (TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) |
|
737 or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1; |
|
738 end; |
|
739 |
|
740 if (Ammo^.Kind <> gtFlame) or ((Ammo^.State and gsttmpFlag) = 0) then FollowGear:= Gear |
|
741 end; |
|
742 end |
|
743 end; |
|
744 end; |
|
745 if i <> 0 then SetAllToActive |
|
746 end; |
|
747 |
|
748 procedure AssignHHCoords; |
|
749 var i, t, p, j: LongInt; |
|
750 ar: array[0..Pred(cMaxHHs)] of PHedgehog; |
|
751 Count: Longword; |
|
752 begin |
|
753 if (GameFlags and gfPlaceHog) <> 0 then PlacingHogs:= true; |
|
754 if (GameFlags and gfDivideTeams) <> 0 then |
|
755 begin |
|
756 t:= 0; |
|
757 TryDo(ClansCount = 2, 'More or less than 2 clans on map in divided teams mode!', true); |
|
758 for p:= 0 to 1 do |
|
759 begin |
|
760 with ClansArray[p]^ do |
|
761 for j:= 0 to Pred(TeamsNumber) do |
|
762 with Teams[j]^ do |
|
763 for i:= 0 to cMaxHHIndex do |
|
764 with Hedgehogs[i] do |
|
765 if (Gear <> nil) and (Gear^.X.QWordValue = 0) then |
|
766 begin |
|
767 if PlacingHogs then Unplaced:= true |
|
768 else FindPlace(Gear, false, t, t + LAND_WIDTH div 2);// could make Gear == nil; |
|
769 if Gear <> nil then |
|
770 begin |
|
771 Gear^.Pos:= GetRandom(49); |
|
772 Gear^.dX.isNegative:= p = 1; |
|
773 end |
|
774 end; |
|
775 t:= LAND_WIDTH div 2 |
|
776 end |
|
777 end else // mix hedgehogs |
|
778 begin |
|
779 Count:= 0; |
|
780 for p:= 0 to Pred(TeamsCount) do |
|
781 with TeamsArray[p]^ do |
|
782 begin |
|
783 for i:= 0 to cMaxHHIndex do |
|
784 with Hedgehogs[i] do |
|
785 if (Gear <> nil) and (Gear^.X.QWordValue = 0) then |
|
786 begin |
|
787 ar[Count]:= @Hedgehogs[i]; |
|
788 inc(Count) |
|
789 end; |
|
790 end; |
|
791 // unC0Rr, while it is true user can watch value on map screen, IMO this (and check above) should be enforced in UI |
|
792 // - is there a good place to put values for the different widgets to check? Right now they are kind of disconnected. |
|
793 //it would be nice if divide teams, forts mode and hh per map could all be checked by the team widget, or maybe disable start button |
|
794 TryDo(Count <= MaxHedgehogs, 'Too many hedgehogs for this map! (max # is ' + inttostr(MaxHedgehogs) + ')', true); |
|
795 while (Count > 0) do |
|
796 begin |
|
797 i:= GetRandom(Count); |
|
798 if PlacingHogs then ar[i]^.Unplaced:= true |
|
799 else FindPlace(ar[i]^.Gear, false, 0, LAND_WIDTH); |
|
800 if ar[i]^.Gear <> nil then |
|
801 begin |
|
802 ar[i]^.Gear^.dX.isNegative:= hwRound(ar[i]^.Gear^.X) > LAND_WIDTH div 2; |
|
803 ar[i]^.Gear^.Pos:= GetRandom(19) |
|
804 end; |
|
805 ar[i]:= ar[Count - 1]; |
|
806 dec(Count) |
|
807 end |
|
808 end |
|
809 end; |
|
810 |
|
811 function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): TPGearArray; |
|
812 var |
|
813 t: PGear; |
|
814 l: Longword; |
|
815 begin |
|
816 r:= r*r; |
|
817 GearsNear := nil; |
|
818 t := GearsList; |
|
819 while t <> nil do |
|
820 begin |
|
821 if (t^.Kind = Kind) |
|
822 and ((X - t^.X)*(X - t^.X) + (Y - t^.Y)*(Y-t^.Y) < int2hwFloat(r)) then |
|
823 begin |
|
824 l:= Length(GearsNear); |
|
825 SetLength(GearsNear, l + 1); |
|
826 GearsNear[l] := t; |
|
827 end; |
|
828 t := t^.NextGear; |
|
829 end; |
|
830 end; |
|
831 |
|
832 {procedure AmmoFlameWork(Ammo: PGear); |
|
833 var t: PGear; |
|
834 begin |
|
835 t:= GearsList; |
|
836 while t <> nil do |
|
837 begin |
|
838 if (t^.Kind = gtHedgehog) and (t^.Y < Ammo^.Y) then |
|
839 if not (hwSqr(Ammo^.X - t^.X) + hwSqr(Ammo^.Y - t^.Y - int2hwFloat(cHHRadius)) * 2 > _2) then |
|
840 begin |
|
841 ApplyDamage(t, 5); |
|
842 t^.dX:= t^.dX + (t^.X - Ammo^.X) * _0_02; |
|
843 t^.dY:= - _0_25; |
|
844 t^.Active:= true; |
|
845 DeleteCI(t); |
|
846 FollowGear:= t |
|
847 end; |
|
848 t:= t^.NextGear |
|
849 end; |
|
850 end;} |
|
851 |
|
852 |
|
853 function CountGears(Kind: TGearType): Longword; |
|
854 var t: PGear; |
|
855 count: Longword = 0; |
|
856 begin |
|
857 |
|
858 t:= GearsList; |
|
859 while t <> nil do |
|
860 begin |
|
861 if t^.Kind = Kind then inc(count); |
|
862 t:= t^.NextGear |
|
863 end; |
|
864 CountGears:= count; |
|
865 end; |
|
866 |
|
867 function SpawnCustomCrateAt(x, y: LongInt; crate: TCrateType; content: Longword): PGear; |
|
868 begin |
|
869 FollowGear := AddGear(x, y, gtCase, 0, _0, _0, 0); |
|
870 cCaseFactor := 0; |
|
871 |
|
872 if (crate <> HealthCrate) and (content > ord(High(TAmmoType))) then content := ord(High(TAmmoType)); |
|
873 |
|
874 case crate of |
|
875 HealthCrate: begin |
|
876 FollowGear^.Pos := posCaseHealth; |
|
877 FollowGear^.Health := content; |
|
878 AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo); |
|
879 end; |
|
880 AmmoCrate: begin |
|
881 FollowGear^.Pos := posCaseAmmo; |
|
882 FollowGear^.AmmoType := TAmmoType(content); |
|
883 AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo); |
|
884 end; |
|
885 UtilityCrate: begin |
|
886 FollowGear^.Pos := posCaseUtility; |
|
887 FollowGear^.AmmoType := TAmmoType(content); |
|
888 AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo); |
|
889 end; |
|
890 end; |
|
891 |
|
892 if ( (x = 0) and (y = 0) ) then FindPlace(FollowGear, true, 0, LAND_WIDTH); |
|
893 |
|
894 SpawnCustomCrateAt := FollowGear; |
|
895 end; |
|
896 |
|
897 function SpawnFakeCrateAt(x, y: LongInt; crate: TCrateType; explode: boolean; poison: boolean): PGear; |
|
898 begin |
|
899 FollowGear := AddGear(x, y, gtCase, 0, _0, _0, 0); |
|
900 cCaseFactor := 0; |
|
901 FollowGear^.Pos := posCaseDummy; |
|
902 |
|
903 if explode then FollowGear^.Pos := FollowGear^.Pos + posCaseExplode; |
|
904 if poison then FollowGear^.Pos := FollowGear^.Pos + posCasePoison; |
|
905 |
|
906 case crate of |
|
907 HealthCrate: begin |
|
908 FollowGear^.Pos := FollowGear^.Pos + posCaseHealth; |
|
909 AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo); |
|
910 end; |
|
911 AmmoCrate: begin |
|
912 FollowGear^.Pos := FollowGear^.Pos + posCaseAmmo; |
|
913 AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo); |
|
914 end; |
|
915 UtilityCrate: begin |
|
916 FollowGear^.Pos := FollowGear^.Pos + posCaseUtility; |
|
917 AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo); |
|
918 end; |
|
919 end; |
|
920 |
|
921 if ( (x = 0) and (y = 0) ) then FindPlace(FollowGear, true, 0, LAND_WIDTH); |
|
922 |
|
923 SpawnFakeCrateAt := FollowGear; |
|
924 end; |
|
925 |
|
926 function GetAmmo(Hedgehog: PHedgehog): TAmmoType; |
|
927 var t, aTot: LongInt; |
|
928 i: TAmmoType; |
|
929 begin |
|
930 |
|
931 aTot:= 0; |
|
932 for i:= Low(TAmmoType) to High(TAmmoType) do |
|
933 if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then |
|
934 inc(aTot, Ammoz[i].Probability); |
|
935 |
|
936 t:= aTot; |
|
937 i:= Low(TAmmoType); |
|
938 if (t > 0) then |
|
939 begin |
|
940 t:= GetRandom(t); |
|
941 while t >= 0 do |
|
942 begin |
|
943 inc(i); |
|
944 if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then |
|
945 dec(t, Ammoz[i].Probability) |
|
946 end |
|
947 end; |
|
948 GetAmmo:= i |
|
949 end; |
|
950 |
|
951 function GetUtility(Hedgehog: PHedgehog): TAmmoType; |
|
952 var t, uTot: LongInt; |
|
953 i: TAmmoType; |
|
954 begin |
|
955 |
|
956 uTot:= 0; |
|
957 for i:= Low(TAmmoType) to High(TAmmoType) do |
|
958 if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) and ((Hedgehog^.Team^.HedgehogsNumber > 1) or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then |
|
959 inc(uTot, Ammoz[i].Probability); |
|
960 |
|
961 t:= uTot; |
|
962 i:= Low(TAmmoType); |
|
963 if (t > 0) then |
|
964 begin |
|
965 t:= GetRandom(t); |
|
966 while t >= 0 do |
|
967 begin |
|
968 inc(i); |
|
969 if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) and ((Hedgehog^.Team^.HedgehogsNumber > 1) or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then |
|
970 dec(t, Ammoz[i].Probability) |
|
971 end |
|
972 end; |
|
973 GetUtility:= i |
|
974 end; |
|
975 |
|
976 |
|
977 |
|
978 procedure SpawnBoxOfSmth; |
|
979 var t, aTot, uTot, a, h: LongInt; |
|
980 i: TAmmoType; |
|
981 begin |
|
982 if (PlacingHogs) or |
|
983 (cCaseFactor = 0) or |
|
984 (CountGears(gtCase) >= 5) or |
|
985 (GetRandom(cCaseFactor) <> 0) then exit; |
|
986 |
|
987 FollowGear:= nil; |
|
988 aTot:= 0; |
|
989 uTot:= 0; |
|
990 for i:= Low(TAmmoType) to High(TAmmoType) do |
|
991 if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then |
|
992 inc(aTot, Ammoz[i].Probability) |
|
993 else |
|
994 inc(uTot, Ammoz[i].Probability); |
|
995 |
|
996 t:=0; |
|
997 a:=aTot; |
|
998 h:= 1; |
|
999 |
|
1000 if (aTot+uTot) <> 0 then |
|
1001 if ((GameFlags and gfInvulnerable) = 0) then |
|
1002 begin |
|
1003 h:= cHealthCaseProb * 100; |
|
1004 t:= GetRandom(10000); |
|
1005 a:= (10000-h)*aTot div (aTot+uTot) |
|
1006 end |
|
1007 else |
|
1008 begin |
|
1009 t:= GetRandom(aTot+uTot); |
|
1010 h:= 0 |
|
1011 end; |
|
1012 |
|
1013 |
|
1014 if t<h then |
|
1015 begin |
|
1016 FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0); |
|
1017 FollowGear^.Health:= cHealthCaseAmount; |
|
1018 FollowGear^.Pos:= posCaseHealth; |
|
1019 AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo); |
|
1020 end |
|
1021 else if (t<a+h) then |
|
1022 begin |
|
1023 t:= aTot; |
|
1024 if (t > 0) then |
|
1025 begin |
|
1026 FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0); |
|
1027 t:= GetRandom(t); |
|
1028 i:= Low(TAmmoType); |
|
1029 FollowGear^.Pos:= posCaseAmmo; |
|
1030 FollowGear^.AmmoType:= i; |
|
1031 AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo); |
|
1032 end |
|
1033 end |
|
1034 else |
|
1035 begin |
|
1036 t:= uTot; |
|
1037 if (t > 0) then |
|
1038 begin |
|
1039 FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0); |
|
1040 t:= GetRandom(t); |
|
1041 i:= Low(TAmmoType); |
|
1042 FollowGear^.Pos:= posCaseUtility; |
|
1043 FollowGear^.AmmoType:= i; |
|
1044 AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo); |
|
1045 end |
|
1046 end; |
|
1047 |
|
1048 // handles case of no ammo or utility crates - considered also placing booleans in uAmmos and altering probabilities |
|
1049 if (FollowGear <> nil) then |
|
1050 begin |
|
1051 FindPlace(FollowGear, true, 0, LAND_WIDTH); |
|
1052 |
|
1053 if (FollowGear <> nil) then |
|
1054 AddVoice(sndReinforce, CurrentTeam^.voicepack) |
|
1055 end |
|
1056 end; |
|
1057 |
|
1058 |
|
1059 function GearByUID(uid : Longword) : PGear; |
|
1060 var gear: PGear; |
|
1061 begin |
|
1062 GearByUID:= nil; |
|
1063 if uid = 0 then exit; |
|
1064 if (lastGearByUID <> nil) and (lastGearByUID^.uid = uid) then |
|
1065 begin |
|
1066 GearByUID:= lastGearByUID; |
|
1067 exit |
|
1068 end; |
|
1069 gear:= GearsList; |
|
1070 while gear <> nil do |
|
1071 begin |
|
1072 if gear^.uid = uid then |
|
1073 begin |
|
1074 lastGearByUID:= gear; |
|
1075 GearByUID:= gear; |
|
1076 exit |
|
1077 end; |
|
1078 gear:= gear^.NextGear |
|
1079 end |
|
1080 end; |
|
1081 |
|
1082 |
|
1083 procedure chSkip(var s: shortstring); |
|
1084 begin |
|
1085 s:= s; // avoid compiler hint |
|
1086 if not CurrentTeam^.ExtDriven then SendIPC(','); |
|
1087 uStats.Skipped; |
|
1088 skipFlag:= true |
|
1089 end; |
|
1090 |
|
1091 procedure chHogSay(var s: shortstring); |
|
1092 var Gear: PVisualGear; |
|
1093 text: shortstring; |
|
1094 hh: PHedgehog; |
|
1095 i, x, t, h: byte; |
|
1096 c, j: LongInt; |
|
1097 begin |
|
1098 hh:= nil; |
|
1099 i:= 0; |
|
1100 t:= 0; |
|
1101 x:= byte(s[1]); // speech type |
|
1102 if x < 4 then |
|
1103 begin |
|
1104 t:= byte(s[2]); // team |
|
1105 if Length(s) > 2 then h:= byte(s[3]) // target hog |
|
1106 end; |
|
1107 // allow targetting a hog by specifying a number as the first portion of the text |
|
1108 if (x < 4) and (h > byte('0')) and (h < byte('9')) then i:= h - 48; |
|
1109 if i <> 0 then text:= copy(s, 4, Length(s) - 1) |
|
1110 else if x < 4 then text:= copy(s, 3, Length(s) - 1) |
|
1111 else text:= copy(s, 2, Length(s) - 1); |
|
1112 |
|
1113 (* |
|
1114 if CheckNoTeamOrHH then |
|
1115 begin |
|
1116 ParseCommand('say ' + text, true); |
|
1117 exit |
|
1118 end; |
|
1119 *) |
|
1120 |
|
1121 if (x < 4) and (TeamsArray[t] <> nil) then |
|
1122 begin |
|
1123 // if team matches current hedgehog team, default to current hedgehog |
|
1124 if (i = 0) and (CurrentHedgehog <> nil) and (CurrentHedgehog^.Team = TeamsArray[t]) then hh:= CurrentHedgehog |
|
1125 else |
|
1126 begin |
|
1127 // otherwise use the first living hog or the hog amongs the remaining ones indicated by i |
|
1128 j:= 0; |
|
1129 c:= 0; |
|
1130 while (j <= cMaxHHIndex) and (hh = nil) do |
|
1131 begin |
|
1132 if (TeamsArray[t]^.Hedgehogs[j].Gear <> nil) then |
|
1133 begin |
|
1134 inc(c); |
|
1135 if (i=0) or (i=c) then |
|
1136 hh:= @TeamsArray[t]^.Hedgehogs[j] |
|
1137 end; |
|
1138 inc(j) |
|
1139 end |
|
1140 end; |
|
1141 if hh <> nil then |
|
1142 begin |
|
1143 Gear:= AddVisualGear(0, 0, vgtSpeechBubble); |
|
1144 if Gear <> nil then |
|
1145 begin |
|
1146 Gear^.Hedgehog:= hh; |
|
1147 Gear^.Text:= text; |
|
1148 Gear^.FrameTicks:= x |
|
1149 end |
|
1150 end |
|
1151 //else ParseCommand('say ' + text, true) |
|
1152 end |
|
1153 else if (x >= 4) then |
|
1154 begin |
|
1155 SpeechType:= x-3; |
|
1156 SpeechText:= text |
|
1157 end; |
|
1158 end; |
|
1159 |
|
1160 procedure initModule; |
|
1161 const handlers: array[TGearType] of TGearStepProcedure = ( |
83 @doStepBomb, |
1162 @doStepBomb, |
84 @doStepHedgehog, |
1163 @doStepHedgehog, |
85 @doStepShell, |
1164 @doStepShell, |
86 @doStepGrave, |
1165 @doStepGrave, |
87 @doStepBee, |
1166 @doStepBee, |
140 @doStepSnowball, |
1219 @doStepSnowball, |
141 @doStepSnowflake, |
1220 @doStepSnowflake, |
142 @doStepStructure, |
1221 @doStepStructure, |
143 @doStepLandGun, |
1222 @doStepLandGun, |
144 @doStepTardis); |
1223 @doStepTardis); |
145 |
1224 begin |
146 function CheckNoDamage: boolean; // returns TRUE in case of no damaged hhs |
1225 doStepHandlers:= handlers; |
147 var Gear: PGear; |
1226 |
148 dmg: LongInt; |
|
149 begin |
|
150 CheckNoDamage:= true; |
|
151 Gear:= GearsList; |
|
152 while Gear <> nil do |
|
153 begin |
|
154 if (Gear^.Kind = gtHedgehog) and (((GameFlags and gfInfAttack) = 0) or ((Gear^.dX.QWordValue < _0_000004.QWordValue) and (Gear^.dY.QWordValue < _0_000004.QWordValue))) then |
|
155 begin |
|
156 if (not isInMultiShoot) then inc(Gear^.Damage, Gear^.Karma); |
|
157 if (Gear^.Damage <> 0) and |
|
158 (not Gear^.Invulnerable) then |
|
159 begin |
|
160 CheckNoDamage:= false; |
|
161 |
|
162 dmg:= Gear^.Damage; |
|
163 if Gear^.Health < dmg then |
|
164 begin |
|
165 Gear^.Active:= true; |
|
166 Gear^.Health:= 0 |
|
167 end |
|
168 else |
|
169 dec(Gear^.Health, dmg); |
|
170 |
|
171 if (Gear^.Hedgehog^.Team = CurrentTeam) and |
|
172 (Gear^.Damage <> Gear^.Karma) and |
|
173 (not Gear^.Hedgehog^.King) and |
|
174 (not Gear^.Hedgehog^.Effects[hePoisoned]) and |
|
175 (not SuddenDeathDmg) then |
|
176 Gear^.State:= Gear^.State or gstLoser; |
|
177 |
|
178 spawnHealthTagForHH(Gear, dmg); |
|
179 |
|
180 RenderHealth(Gear^.Hedgehog^); |
|
181 RecountTeamHealth(Gear^.Hedgehog^.Team); |
|
182 |
|
183 end; |
|
184 if (not isInMultiShoot) then Gear^.Karma:= 0; |
|
185 Gear^.Damage:= 0 |
|
186 end; |
|
187 Gear:= Gear^.NextGear |
|
188 end; |
|
189 end; |
|
190 |
|
191 procedure HealthMachine; |
|
192 var Gear: PGear; |
|
193 team: PTeam; |
|
194 i: LongWord; |
|
195 flag: Boolean; |
|
196 tmp: LongWord; |
|
197 begin |
|
198 Gear:= GearsList; |
|
199 |
|
200 while Gear <> nil do |
|
201 begin |
|
202 if Gear^.Kind = gtHedgehog then |
|
203 begin |
|
204 tmp:= 0; |
|
205 if Gear^.Hedgehog^.Effects[hePoisoned] then |
|
206 begin |
|
207 inc(tmp, ModifyDamage(5, Gear)); |
|
208 if (GameFlags and gfResetHealth) <> 0 then dec(Gear^.Hedgehog^.InitialHealth) // does not need a minimum check since <= 1 basically disables it |
|
209 end; |
|
210 if (TotalRounds > cSuddenDTurns - 1) then |
|
211 begin |
|
212 inc(tmp, cHealthDecrease); |
|
213 if (GameFlags and gfResetHealth) <> 0 then dec(Gear^.Hedgehog^.InitialHealth, cHealthDecrease) |
|
214 end; |
|
215 if Gear^.Hedgehog^.King then |
|
216 begin |
|
217 flag:= false; |
|
218 team:= Gear^.Hedgehog^.Team; |
|
219 for i:= 0 to Pred(team^.HedgehogsNumber) do |
|
220 if (team^.Hedgehogs[i].Gear <> nil) and |
|
221 (not team^.Hedgehogs[i].King) and |
|
222 (team^.Hedgehogs[i].Gear^.Health > team^.Hedgehogs[i].Gear^.Damage) |
|
223 then flag:= true; |
|
224 if not flag then |
|
225 begin |
|
226 inc(tmp, 5); |
|
227 if (GameFlags and gfResetHealth) <> 0 then dec(Gear^.Hedgehog^.InitialHealth, 5) |
|
228 end |
|
229 end; |
|
230 if tmp > 0 then |
|
231 begin |
|
232 inc(Gear^.Damage, min(tmp, max(0,Gear^.Health - 1 - Gear^.Damage))); |
|
233 HHHurt(Gear^.Hedgehog, dsPoison); |
|
234 end |
|
235 end; |
|
236 |
|
237 Gear:= Gear^.NextGear |
|
238 end; |
|
239 end; |
|
240 |
|
241 procedure ProcessGears; |
|
242 const delay: LongWord = 0; |
|
243 delay2: LongWord = 0; |
|
244 step: (stDelay, stChDmg, stSweep, stTurnReact, |
|
245 stAfterDelay, stChWin, stWater, stChWin2, stHealth, |
|
246 stSpawn, stNTurn) = stDelay; |
|
247 var Gear, t: PGear; |
|
248 i, AliveCount: LongInt; |
|
249 s: shortstring; |
|
250 begin |
|
251 PrvInactive:= AllInactive; |
|
252 AllInactive:= true; |
|
253 |
|
254 if (StepSoundTimer > 0) and (StepSoundChannel < 0) then |
|
255 StepSoundChannel:= LoopSound(sndSteps) |
|
256 else if (StepSoundTimer = 0) and (StepSoundChannel > -1) then |
|
257 begin |
|
258 StopSound(StepSoundChannel); |
|
259 StepSoundChannel:= -1 |
|
260 end; |
|
261 |
|
262 if StepSoundTimer > 0 then |
|
263 dec(StepSoundTimer, 1); |
|
264 |
|
265 t:= GearsList; |
|
266 while t <> nil do |
|
267 begin |
|
268 Gear:= t; |
|
269 t:= Gear^.NextGear; |
|
270 |
|
271 if Gear^.Active then |
|
272 begin |
|
273 if Gear^.RenderTimer and (Gear^.Timer > 500) and ((Gear^.Timer mod 1000) = 0) then |
|
274 begin |
|
275 FreeTexture(Gear^.Tex); |
|
276 Gear^.Tex:= RenderStringTex(inttostr(Gear^.Timer div 1000), cWhiteColor, fntSmall); |
|
277 end; |
|
278 Gear^.doStep(Gear); |
|
279 // might be useful later |
|
280 //ScriptCall('onGearStep', Gear^.uid); |
|
281 end |
|
282 end; |
|
283 |
|
284 if AllInactive then |
|
285 case step of |
|
286 stDelay: begin |
|
287 if delay = 0 then |
|
288 delay:= cInactDelay |
|
289 else |
|
290 dec(delay); |
|
291 |
|
292 if delay = 0 then |
|
293 inc(step) |
|
294 end; |
|
295 stChDmg: if CheckNoDamage then inc(step) else step:= stDelay; |
|
296 stSweep: if SweepDirty then |
|
297 begin |
|
298 SetAllToActive; |
|
299 step:= stChDmg |
|
300 end else inc(step); |
|
301 stTurnReact: begin |
|
302 if (not bBetweenTurns) and (not isInMultiShoot) then |
|
303 begin |
|
304 uStats.TurnReaction; |
|
305 inc(step) |
|
306 end else |
|
307 inc(step, 2); |
|
308 end; |
|
309 stAfterDelay: begin |
|
310 if delay = 0 then |
|
311 delay:= cInactDelay |
|
312 else |
|
313 dec(delay); |
|
314 |
|
315 if delay = 0 then |
|
316 inc(step) |
|
317 end; |
|
318 stChWin: begin |
|
319 CheckForWin; |
|
320 inc(step) |
|
321 end; |
|
322 stWater: if (not bBetweenTurns) and (not isInMultiShoot) then |
|
323 begin |
|
324 if TotalRounds = cSuddenDTurns + 1 then bWaterRising:= true; |
|
325 |
|
326 if bWaterRising and (cWaterRise > 0) then |
|
327 AddGear(0, 0, gtWaterUp, 0, _0, _0, 0)^.Tag:= cWaterRise; |
|
328 |
|
329 inc(step) |
|
330 end else inc(step); |
|
331 stChWin2: begin |
|
332 CheckForWin; |
|
333 inc(step) |
|
334 end; |
|
335 stHealth: begin |
|
336 if (cWaterRise <> 0) or (cHealthDecrease <> 0) then |
|
337 begin |
|
338 if (TotalRounds = cSuddenDTurns) and (not SuddenDeath) and (not isInMultiShoot) then |
|
339 begin |
|
340 SuddenDeath:= true; |
|
341 if cHealthDecrease <> 0 then |
|
342 begin |
|
343 SuddenDeathDmg:= true; |
|
344 |
|
345 // flash |
|
346 ScreenFade:= sfFromWhite; |
|
347 ScreenFadeValue:= sfMax; |
|
348 ScreenFadeSpeed:= 1; |
|
349 |
|
350 ChangeToSDClouds; |
|
351 ChangeToSDFlakes; |
|
352 glClearColor(SDSkyColor.r * (SDTint/255) / 255, SDSkyColor.g * (SDTint/255) / 255, SDSkyColor.b * (SDTint/255) / 255, 0.99); |
|
353 Ammoz[amTardis].SkipTurns:= 9999; |
|
354 Ammoz[amTardis].Probability:= 0; |
|
355 end; |
|
356 AddCaption(trmsg[sidSuddenDeath], cWhiteColor, capgrpGameState); |
|
357 playSound(sndSuddenDeath); |
|
358 StopMusic //No SDMusic for now |
|
359 //MusicFN:= SDMusic; |
|
360 //ChangeMusic |
|
361 end |
|
362 else if (TotalRounds < cSuddenDTurns) and (not isInMultiShoot) then |
|
363 begin |
|
364 i:= cSuddenDTurns - TotalRounds; |
|
365 s:= inttostr(i); |
|
366 if i = 1 then |
|
367 AddCaption(trmsg[sidRoundSD], cWhiteColor, capgrpGameState) |
|
368 else if (i = 2) or ((i > 0) and ((i mod 50 = 0) or ((i <= 25) and (i mod 5 = 0)))) then |
|
369 AddCaption(Format(trmsg[sidRoundsSD], s), cWhiteColor, capgrpGameState); |
|
370 end; |
|
371 end; |
|
372 if bBetweenTurns |
|
373 or isInMultiShoot |
|
374 or (TotalRounds = -1) then inc(step) |
|
375 else begin |
|
376 bBetweenTurns:= true; |
|
377 HealthMachine; |
|
378 step:= stChDmg |
|
379 end |
|
380 end; |
|
381 stSpawn: begin |
|
382 if not isInMultiShoot then SpawnBoxOfSmth; |
|
383 inc(step) |
|
384 end; |
|
385 stNTurn: begin |
|
386 if isInMultiShoot then |
|
387 isInMultiShoot:= false |
|
388 else begin |
|
389 // delayed till after 0.9.12 |
|
390 // reset to default zoom |
|
391 //ZoomValue:= ZoomDefault; |
|
392 with CurrentHedgehog^ do |
|
393 if (Gear <> nil) |
|
394 and ((Gear^.State and gstAttacked) = 0) |
|
395 and (MultiShootAttacks > 0) then OnUsedAmmo(CurrentHedgehog^); |
|
396 |
|
397 EndTurnCleanup; |
|
398 |
|
399 FreeActionsList; // could send -left, -right and similar commands, so should be called before /nextturn |
|
400 |
|
401 ParseCommand('/nextturn', true); |
|
402 SwitchHedgehog; |
|
403 |
|
404 AfterSwitchHedgehog; |
|
405 bBetweenTurns:= false |
|
406 end; |
|
407 step:= Low(step) |
|
408 end; |
|
409 end |
|
410 else if ((GameFlags and gfInfAttack) <> 0) then |
|
411 begin |
|
412 if delay2 = 0 then |
|
413 delay2:= cInactDelay * 50 |
|
414 else |
|
415 begin |
|
416 dec(delay2); |
|
417 |
|
418 if ((delay2 mod cInactDelay) = 0) and (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and (not CurrentHedgehog^.Unplaced) then |
|
419 begin |
|
420 if (CurrentHedgehog^.Gear^.State and gstAttacked <> 0) and (Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0) then |
|
421 begin |
|
422 CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State or gstHHChooseTarget; |
|
423 isCursorVisible := true |
|
424 end; |
|
425 CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State and (not gstAttacked); |
|
426 end; |
|
427 if delay2 = 0 then |
|
428 begin |
|
429 if (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.State and gstAttacked = 0) and (CurAmmoGear = nil) then SweepDirty; |
|
430 CheckNoDamage; |
|
431 AliveCount:= 0; // shorter version of check for win to allow typical step activity to proceed |
|
432 for i:= 0 to Pred(ClansCount) do |
|
433 if ClansArray[i]^.ClanHealth > 0 then inc(AliveCount); |
|
434 if (AliveCount <= 1) and ((GameFlags and gfOneClanMode) = 0) then |
|
435 begin |
|
436 step:= stChDmg; |
|
437 if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft; |
|
438 TurnTimeLeft:= 0 |
|
439 end |
|
440 end |
|
441 end |
|
442 end; |
|
443 |
|
444 if TurnTimeLeft > 0 then |
|
445 if CurrentHedgehog^.Gear <> nil then |
|
446 if ((CurrentHedgehog^.Gear^.State and gstAttacking) = 0) |
|
447 and (not isInMultiShoot) then |
|
448 begin |
|
449 if (TurnTimeLeft = 5000) |
|
450 and (cHedgehogTurnTime >= 10000) |
|
451 and (not PlacingHogs) |
|
452 and (CurrentHedgehog^.Gear <> nil) |
|
453 and ((CurrentHedgehog^.Gear^.State and gstAttacked) = 0) then |
|
454 AddVoice(sndHurry, CurrentTeam^.voicepack); |
|
455 if ReadyTimeLeft > 0 then |
|
456 begin |
|
457 if ReadyTimeLeft = 2000 then |
|
458 AddVoice(sndComeonthen, CurrentTeam^.voicepack); |
|
459 dec(ReadyTimeLeft) |
|
460 end |
|
461 else |
|
462 dec(TurnTimeLeft) |
|
463 end; |
|
464 |
|
465 if skipFlag then |
|
466 begin |
|
467 if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft; |
|
468 TurnTimeLeft:= 0; |
|
469 skipFlag:= false; |
|
470 inc(CurrentHedgehog^.Team^.stats.TurnSkips); |
|
471 end; |
|
472 |
|
473 if ((GameTicks and $FFFF) = $FFFF) then |
|
474 begin |
|
475 if (not CurrentTeam^.ExtDriven) then |
|
476 begin |
|
477 SendIPC('#'); |
|
478 AddFileLog('hiTicks increment message sent') |
|
479 end; |
|
480 |
|
481 if (not CurrentTeam^.ExtDriven) or CurrentTeam^.hasGone then |
|
482 inc(hiTicks) // we do not recieve a message for this |
|
483 end; |
|
484 |
|
485 ScriptCall('onGameTick'); |
|
486 inc(GameTicks) |
|
487 end; |
|
488 |
|
489 //Purpose, to reset all transient attributes toggled by a utility and clean up various gears and effects at end of turn |
|
490 //If any of these are set as permanent toggles in the frontend, that needs to be checked and skipped here. |
|
491 procedure EndTurnCleanup; |
|
492 var i: LongInt; |
|
493 t: PGear; |
|
494 begin |
|
495 SpeechText:= ''; // in case it has not been consumed |
|
496 |
|
497 if (GameFlags and gfLowGravity) = 0 then |
|
498 begin |
|
499 cGravity:= cMaxWindSpeed * 2; |
|
500 cGravityf:= 0.00025 * 2 |
|
501 end; |
|
502 |
|
503 if (GameFlags and gfVampiric) = 0 then |
|
504 cVampiric:= false; |
|
505 |
|
506 cDamageModifier:= _1; |
|
507 |
|
508 if (GameFlags and gfLaserSight) = 0 then |
|
509 cLaserSighting:= false; |
|
510 |
|
511 if (GameFlags and gfArtillery) = 0 then |
|
512 cArtillery:= false; |
|
513 // have to sweep *all* current team hedgehogs since it is theoretically possible if you have enough invulnerabilities and switch turns to make your entire team invulnerable |
|
514 if (CurrentTeam <> nil) then |
|
515 with CurrentTeam^ do |
|
516 for i:= 0 to cMaxHHIndex do |
|
517 with Hedgehogs[i] do |
|
518 begin |
|
519 (* |
|
520 if (SpeechGear <> nil) then |
|
521 begin |
|
522 DeleteVisualGear(SpeechGear); // remove to restore persisting beyond end of turn. Tiy says was too much of a gameplay issue |
|
523 SpeechGear:= nil |
|
524 end; |
|
525 *) |
|
526 |
|
527 if (Gear <> nil) then |
|
528 begin |
|
529 if (GameFlags and gfInvulnerable) = 0 then |
|
530 Gear^.Invulnerable:= false; |
|
531 end; |
|
532 end; |
|
533 t:= GearsList; |
|
534 while t <> nil do |
|
535 begin |
|
536 t^.PortalCounter:= 0; |
|
537 if ((GameFlags and gfResetHealth) <> 0) and (t^.Kind = gtHedgehog) and (t^.Health < t^.Hedgehog^.InitialHealth) then |
|
538 begin |
|
539 t^.Health:= t^.Hedgehog^.InitialHealth; |
|
540 RenderHealth(t^.Hedgehog^); |
|
541 end; |
|
542 t:= t^.NextGear |
|
543 end; |
|
544 |
|
545 if ((GameFlags and gfResetWeps) <> 0) and (not PlacingHogs) then |
|
546 ResetWeapons; |
|
547 |
|
548 if (GameFlags and gfResetHealth) <> 0 then |
|
549 for i:= 0 to Pred(TeamsCount) do |
|
550 RecountTeamHealth(TeamsArray[i]) |
|
551 end; |
|
552 |
|
553 procedure SetAllToActive; |
|
554 var t: PGear; |
|
555 begin |
|
556 AllInactive:= false; |
|
557 t:= GearsList; |
|
558 while t <> nil do |
|
559 begin |
|
560 t^.Active:= true; |
|
561 t:= t^.NextGear |
|
562 end |
|
563 end; |
|
564 |
|
565 procedure SetAllHHToActive; |
|
566 var t: PGear; |
|
567 begin |
|
568 AllInactive:= false; |
|
569 t:= GearsList; |
|
570 while t <> nil do |
|
571 begin |
|
572 if (t^.Kind = gtHedgehog) or (t^.Kind = gtExplosives) then t^.Active:= true; |
|
573 t:= t^.NextGear |
|
574 end |
|
575 end; |
|
576 |
|
577 |
|
578 procedure DrawGears; |
|
579 var Gear: PGear; |
|
580 x, y: LongInt; |
|
581 begin |
|
582 Gear:= GearsList; |
|
583 while Gear <> nil do |
|
584 begin |
|
585 if Gear^.State and gstInvisible = 0 then |
|
586 begin |
|
587 x:= hwRound(Gear^.X) + WorldDx; |
|
588 y:= hwRound(Gear^.Y) + WorldDy; |
|
589 RenderGear(Gear, x, y); |
|
590 end; |
|
591 Gear:= Gear^.NextGear |
|
592 end; |
|
593 end; |
|
594 |
|
595 procedure FreeGearsList; |
|
596 var t, tt: PGear; |
|
597 begin |
|
598 tt:= GearsList; |
|
599 GearsList:= nil; |
|
600 while tt <> nil do |
|
601 begin |
|
602 t:= tt; |
|
603 tt:= tt^.NextGear; |
|
604 Dispose(t) |
|
605 end; |
|
606 end; |
|
607 |
|
608 procedure AddMiscGears; |
|
609 var i: Longword; |
|
610 Gear: PGear; |
|
611 begin |
|
612 AddGear(0, 0, gtATStartGame, 0, _0, _0, 2000); |
|
613 |
|
614 i:= 0; |
|
615 Gear:= PGear(1); |
|
616 while (i < cLandMines) {and (Gear <> nil)} do // disable this check until better solution found |
|
617 begin |
|
618 Gear:= AddGear(0, 0, gtMine, 0, _0, _0, 0); |
|
619 FindPlace(Gear, false, 0, LAND_WIDTH); |
|
620 inc(i) |
|
621 end; |
|
622 |
|
623 i:= 0; |
|
624 Gear:= PGear(1); |
|
625 while (i < cExplosives){ and (Gear <> nil)} do |
|
626 begin |
|
627 Gear:= AddGear(0, 0, gtExplosives, 0, _0, _0, 0); |
|
628 FindPlace(Gear, false, 0, LAND_WIDTH); |
|
629 inc(i) |
|
630 end; |
|
631 |
|
632 if (GameFlags and gfLowGravity) <> 0 then |
|
633 begin |
|
634 cGravity:= cMaxWindSpeed; |
|
635 cGravityf:= 0.00025 |
|
636 end; |
|
637 |
|
638 if (GameFlags and gfVampiric) <> 0 then |
|
639 cVampiric:= true; |
|
640 |
|
641 Gear:= GearsList; |
|
642 if (GameFlags and gfInvulnerable) <> 0 then |
|
643 while Gear <> nil do |
|
644 begin |
|
645 Gear^.Invulnerable:= true; // this is only checked on hogs right now, so no need for gear type check |
|
646 Gear:= Gear^.NextGear |
|
647 end; |
|
648 |
|
649 if (GameFlags and gfLaserSight) <> 0 then |
|
650 cLaserSighting:= true; |
|
651 |
|
652 if (GameFlags and gfArtillery) <> 0 then |
|
653 cArtillery:= true; |
|
654 |
|
655 if not hasBorder and ((Theme = 'Snow') or (Theme = 'Christmas')) then |
|
656 for i:= 0 to Pred(vobCount*2) do |
|
657 AddGear(GetRandom(LAND_WIDTH+1024)-512, LAND_HEIGHT - GetRandom(LAND_HEIGHT div 2), gtFlake, 0, _0, _0, 0); |
|
658 end; |
|
659 |
|
660 |
|
661 procedure ShotgunShot(Gear: PGear); |
|
662 var t: PGear; |
|
663 dmg, r, dist: LongInt; |
|
664 dx, dy: hwFloat; |
|
665 begin |
|
666 Gear^.Radius:= cShotgunRadius; |
|
667 t:= GearsList; |
|
668 while t <> nil do |
|
669 begin |
|
670 case t^.Kind of |
|
671 gtHedgehog, |
|
672 gtMine, |
|
673 gtSMine, |
|
674 gtCase, |
|
675 gtTarget, |
|
676 gtExplosives, |
|
677 gtStructure: begin |
|
678 //addFileLog('ShotgunShot radius: ' + inttostr(Gear^.Radius) + ', t^.Radius = ' + inttostr(t^.Radius) + ', distance = ' + inttostr(dist) + ', dmg = ' + inttostr(dmg)); |
|
679 dmg:= 0; |
|
680 r:= Gear^.Radius + t^.Radius; |
|
681 dx:= Gear^.X-t^.X; |
|
682 dx.isNegative:= false; |
|
683 dy:= Gear^.Y-t^.Y; |
|
684 dy.isNegative:= false; |
|
685 if r-hwRound(dx+dy) > 0 then |
|
686 begin |
|
687 dist:= hwRound(Distance(dx, dy)); |
|
688 dmg:= ModifyDamage(min(r - dist, 25), t); |
|
689 end; |
|
690 if dmg > 0 then |
|
691 begin |
|
692 if (not t^.Invulnerable) then |
|
693 ApplyDamage(t, Gear^.Hedgehog, dmg, dsBullet) |
|
694 else |
|
695 Gear^.State:= Gear^.State or gstWinner; |
|
696 |
|
697 DeleteCI(t); |
|
698 t^.dX:= t^.dX + Gear^.dX * dmg * _0_01 + SignAs(cHHKick, Gear^.dX); |
|
699 t^.dY:= t^.dY + Gear^.dY * dmg * _0_01; |
|
700 t^.State:= t^.State or gstMoving; |
|
701 t^.Active:= true; |
|
702 FollowGear:= t |
|
703 end |
|
704 end; |
|
705 gtGrave: begin |
|
706 dmg:= 0; |
|
707 r:= Gear^.Radius + t^.Radius; |
|
708 dx:= Gear^.X-t^.X; |
|
709 dx.isNegative:= false; |
|
710 dy:= Gear^.Y-t^.Y; |
|
711 dy.isNegative:= false; |
|
712 if r-hwRound(dx+dy) > 0 then |
|
713 begin |
|
714 dist:= hwRound(Distance(dx, dy)); |
|
715 dmg:= ModifyDamage(min(r - dist, 25), t); |
|
716 end; |
|
717 if dmg > 0 then |
|
718 begin |
|
719 t^.dY:= - _0_1; |
|
720 t^.Active:= true |
|
721 end |
|
722 end; |
|
723 end; |
|
724 t:= t^.NextGear |
|
725 end; |
|
726 if (GameFlags and gfSolidLand) = 0 then DrawExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cShotgunRadius) |
|
727 end; |
|
728 |
|
729 procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); |
|
730 var t: PGearArray; |
|
731 Gear: PGear; |
|
732 i, tmpDmg: LongInt; |
|
733 VGear: PVisualGear; |
|
734 begin |
|
735 t:= CheckGearsCollision(Ammo); |
|
736 // Just to avoid hogs on rope dodging fire. |
|
737 if (CurAmmoGear <> nil) and ((CurAmmoGear^.Kind = gtRope) or (CurAmmoGear^.Kind = gtJetpack) or (CurAmmoGear^.Kind = gtBirdy)) and |
|
738 (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.CollisionIndex = -1) and |
|
739 (sqr(hwRound(Ammo^.X) - hwRound(CurrentHedgehog^.Gear^.X)) + sqr(hwRound(Ammo^.Y) - hwRound(CurrentHedgehog^.Gear^.Y)) <= sqr(cHHRadius + Ammo^.Radius)) then |
|
740 begin |
|
741 t^.ar[t^.Count]:= CurrentHedgehog^.Gear; |
|
742 inc(t^.Count) |
|
743 end; |
|
744 |
|
745 i:= t^.Count; |
|
746 |
|
747 if (Ammo^.Kind = gtFlame) and (i > 0) then Ammo^.Health:= 0; |
|
748 while i > 0 do |
|
749 begin |
|
750 dec(i); |
|
751 Gear:= t^.ar[i]; |
|
752 tmpDmg:= ModifyDamage(Damage, Gear); |
|
753 if (Gear^.State and gstNoDamage) = 0 then |
|
754 begin |
|
755 |
|
756 if (Ammo^.Kind = gtDEagleShot) or (Ammo^.Kind = gtSniperRifleShot) then |
|
757 begin |
|
758 VGear := AddVisualGear(hwround(Ammo^.X), hwround(Ammo^.Y), vgtBulletHit); |
|
759 if VGear <> nil then VGear^.Angle := DxDy2Angle(-Ammo^.dX, Ammo^.dY); |
|
760 end; |
|
761 |
|
762 if (Gear^.Kind = gtHedgehog) and (Ammo^.State and gsttmpFlag <> 0) and (Ammo^.Kind = gtShover) then Gear^.FlightTime:= 1; |
|
763 |
|
764 case Gear^.Kind of |
|
765 gtHedgehog, |
|
766 gtMine, |
|
767 gtSMine, |
|
768 gtTarget, |
|
769 gtCase, |
|
770 gtExplosives, |
|
771 gtStructure: begin |
|
772 if (Ammo^.Kind = gtDrill) then begin Ammo^.Timer:= 0; exit; end; |
|
773 if (not Gear^.Invulnerable) then |
|
774 ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg, dsShove) |
|
775 else |
|
776 Gear^.State:= Gear^.State or gstWinner; |
|
777 if (Gear^.Kind = gtExplosives) and (Ammo^.Kind = gtBlowtorch) then |
|
778 begin |
|
779 if (Ammo^.Hedgehog^.Gear <> nil) then Ammo^.Hedgehog^.Gear^.State:= Ammo^.Hedgehog^.Gear^.State and (not gstNotKickable); |
|
780 ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg * 100, dsUnknown); // crank up damage for explosives + blowtorch |
|
781 end; |
|
782 |
|
783 DeleteCI(Gear); |
|
784 if (Gear^.Kind = gtHedgehog) and Gear^.Hedgehog^.King then |
|
785 begin |
|
786 Gear^.dX:= Ammo^.dX * Power * _0_005; |
|
787 Gear^.dY:= Ammo^.dY * Power * _0_005 |
|
788 end |
|
789 else |
|
790 begin |
|
791 Gear^.dX:= Ammo^.dX * Power * _0_01; |
|
792 Gear^.dY:= Ammo^.dY * Power * _0_01 |
|
793 end; |
|
794 |
|
795 Gear^.Active:= true; |
|
796 Gear^.State:= Gear^.State or gstMoving; |
|
797 |
|
798 if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then |
|
799 begin |
|
800 if not (TestCollisionXwithXYShift(Gear, _0, -3, hwSign(Gear^.dX)) |
|
801 or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1; |
|
802 if not (TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) |
|
803 or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1; |
|
804 if not (TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) |
|
805 or (TestCollisionYwithGear(Gear, -1) <> 0)) then Gear^.Y:= Gear^.Y - _1; |
|
806 end; |
|
807 |
|
808 if (Ammo^.Kind <> gtFlame) or ((Ammo^.State and gsttmpFlag) = 0) then FollowGear:= Gear |
|
809 end; |
|
810 end |
|
811 end; |
|
812 end; |
|
813 if i <> 0 then SetAllToActive |
|
814 end; |
|
815 |
|
816 procedure AssignHHCoords; |
|
817 var i, t, p, j: LongInt; |
|
818 ar: array[0..Pred(cMaxHHs)] of PHedgehog; |
|
819 Count: Longword; |
|
820 begin |
|
821 if (GameFlags and gfPlaceHog) <> 0 then PlacingHogs:= true; |
|
822 if (GameFlags and gfDivideTeams) <> 0 then |
|
823 begin |
|
824 t:= 0; |
|
825 TryDo(ClansCount = 2, 'More or less than 2 clans on map in divided teams mode!', true); |
|
826 for p:= 0 to 1 do |
|
827 begin |
|
828 with ClansArray[p]^ do |
|
829 for j:= 0 to Pred(TeamsNumber) do |
|
830 with Teams[j]^ do |
|
831 for i:= 0 to cMaxHHIndex do |
|
832 with Hedgehogs[i] do |
|
833 if (Gear <> nil) and (Gear^.X.QWordValue = 0) then |
|
834 begin |
|
835 if PlacingHogs then Unplaced:= true |
|
836 else FindPlace(Gear, false, t, t + LAND_WIDTH div 2);// could make Gear == nil; |
|
837 if Gear <> nil then |
|
838 begin |
|
839 Gear^.Pos:= GetRandom(49); |
|
840 Gear^.dX.isNegative:= p = 1; |
|
841 end |
|
842 end; |
|
843 t:= LAND_WIDTH div 2 |
|
844 end |
|
845 end else // mix hedgehogs |
|
846 begin |
|
847 Count:= 0; |
|
848 for p:= 0 to Pred(TeamsCount) do |
|
849 with TeamsArray[p]^ do |
|
850 begin |
|
851 for i:= 0 to cMaxHHIndex do |
|
852 with Hedgehogs[i] do |
|
853 if (Gear <> nil) and (Gear^.X.QWordValue = 0) then |
|
854 begin |
|
855 ar[Count]:= @Hedgehogs[i]; |
|
856 inc(Count) |
|
857 end; |
|
858 end; |
|
859 // unC0Rr, while it is true user can watch value on map screen, IMO this (and check above) should be enforced in UI |
|
860 // - is there a good place to put values for the different widgets to check? Right now they are kind of disconnected. |
|
861 //it would be nice if divide teams, forts mode and hh per map could all be checked by the team widget, or maybe disable start button |
|
862 TryDo(Count <= MaxHedgehogs, 'Too many hedgehogs for this map! (max # is ' + inttostr(MaxHedgehogs) + ')', true); |
|
863 while (Count > 0) do |
|
864 begin |
|
865 i:= GetRandom(Count); |
|
866 if PlacingHogs then ar[i]^.Unplaced:= true |
|
867 else FindPlace(ar[i]^.Gear, false, 0, LAND_WIDTH); |
|
868 if ar[i]^.Gear <> nil then |
|
869 begin |
|
870 ar[i]^.Gear^.dX.isNegative:= hwRound(ar[i]^.Gear^.X) > LAND_WIDTH div 2; |
|
871 ar[i]^.Gear^.Pos:= GetRandom(19) |
|
872 end; |
|
873 ar[i]:= ar[Count - 1]; |
|
874 dec(Count) |
|
875 end |
|
876 end |
|
877 end; |
|
878 |
|
879 function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): TPGearArray; |
|
880 var |
|
881 t: PGear; |
|
882 l: Longword; |
|
883 begin |
|
884 r:= r*r; |
|
885 GearsNear := nil; |
|
886 t := GearsList; |
|
887 while t <> nil do |
|
888 begin |
|
889 if (t^.Kind = Kind) |
|
890 and ((X - t^.X)*(X - t^.X) + (Y - t^.Y)*(Y-t^.Y) < int2hwFloat(r)) then |
|
891 begin |
|
892 l:= Length(GearsNear); |
|
893 SetLength(GearsNear, l + 1); |
|
894 GearsNear[l] := t; |
|
895 end; |
|
896 t := t^.NextGear; |
|
897 end; |
|
898 end; |
|
899 |
|
900 {procedure AmmoFlameWork(Ammo: PGear); |
|
901 var t: PGear; |
|
902 begin |
|
903 t:= GearsList; |
|
904 while t <> nil do |
|
905 begin |
|
906 if (t^.Kind = gtHedgehog) and (t^.Y < Ammo^.Y) then |
|
907 if not (hwSqr(Ammo^.X - t^.X) + hwSqr(Ammo^.Y - t^.Y - int2hwFloat(cHHRadius)) * 2 > _2) then |
|
908 begin |
|
909 ApplyDamage(t, 5); |
|
910 t^.dX:= t^.dX + (t^.X - Ammo^.X) * _0_02; |
|
911 t^.dY:= - _0_25; |
|
912 t^.Active:= true; |
|
913 DeleteCI(t); |
|
914 FollowGear:= t |
|
915 end; |
|
916 t:= t^.NextGear |
|
917 end; |
|
918 end;} |
|
919 |
|
920 |
|
921 function CountGears(Kind: TGearType): Longword; |
|
922 var t: PGear; |
|
923 count: Longword = 0; |
|
924 begin |
|
925 |
|
926 t:= GearsList; |
|
927 while t <> nil do |
|
928 begin |
|
929 if t^.Kind = Kind then inc(count); |
|
930 t:= t^.NextGear |
|
931 end; |
|
932 CountGears:= count; |
|
933 end; |
|
934 |
|
935 function SpawnCustomCrateAt(x, y: LongInt; crate: TCrateType; content: Longword): PGear; |
|
936 begin |
|
937 FollowGear := AddGear(x, y, gtCase, 0, _0, _0, 0); |
|
938 cCaseFactor := 0; |
|
939 |
|
940 if (crate <> HealthCrate) and (content > ord(High(TAmmoType))) then content := ord(High(TAmmoType)); |
|
941 |
|
942 case crate of |
|
943 HealthCrate: begin |
|
944 FollowGear^.Pos := posCaseHealth; |
|
945 FollowGear^.Health := content; |
|
946 AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo); |
|
947 end; |
|
948 AmmoCrate: begin |
|
949 FollowGear^.Pos := posCaseAmmo; |
|
950 FollowGear^.AmmoType := TAmmoType(content); |
|
951 AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo); |
|
952 end; |
|
953 UtilityCrate: begin |
|
954 FollowGear^.Pos := posCaseUtility; |
|
955 FollowGear^.AmmoType := TAmmoType(content); |
|
956 AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo); |
|
957 end; |
|
958 end; |
|
959 |
|
960 if ( (x = 0) and (y = 0) ) then FindPlace(FollowGear, true, 0, LAND_WIDTH); |
|
961 |
|
962 SpawnCustomCrateAt := FollowGear; |
|
963 end; |
|
964 |
|
965 function SpawnFakeCrateAt(x, y: LongInt; crate: TCrateType; explode: boolean; poison: boolean): PGear; |
|
966 begin |
|
967 FollowGear := AddGear(x, y, gtCase, 0, _0, _0, 0); |
|
968 cCaseFactor := 0; |
|
969 FollowGear^.Pos := posCaseDummy; |
|
970 |
|
971 if explode then FollowGear^.Pos := FollowGear^.Pos + posCaseExplode; |
|
972 if poison then FollowGear^.Pos := FollowGear^.Pos + posCasePoison; |
|
973 |
|
974 case crate of |
|
975 HealthCrate: begin |
|
976 FollowGear^.Pos := FollowGear^.Pos + posCaseHealth; |
|
977 AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo); |
|
978 end; |
|
979 AmmoCrate: begin |
|
980 FollowGear^.Pos := FollowGear^.Pos + posCaseAmmo; |
|
981 AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo); |
|
982 end; |
|
983 UtilityCrate: begin |
|
984 FollowGear^.Pos := FollowGear^.Pos + posCaseUtility; |
|
985 AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo); |
|
986 end; |
|
987 end; |
|
988 |
|
989 if ( (x = 0) and (y = 0) ) then FindPlace(FollowGear, true, 0, LAND_WIDTH); |
|
990 |
|
991 SpawnFakeCrateAt := FollowGear; |
|
992 end; |
|
993 |
|
994 function GetAmmo(Hedgehog: PHedgehog): TAmmoType; |
|
995 var t, aTot: LongInt; |
|
996 i: TAmmoType; |
|
997 begin |
|
998 |
|
999 aTot:= 0; |
|
1000 for i:= Low(TAmmoType) to High(TAmmoType) do |
|
1001 if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then |
|
1002 inc(aTot, Ammoz[i].Probability); |
|
1003 |
|
1004 t:= aTot; |
|
1005 i:= Low(TAmmoType); |
|
1006 if (t > 0) then |
|
1007 begin |
|
1008 t:= GetRandom(t); |
|
1009 while t >= 0 do |
|
1010 begin |
|
1011 inc(i); |
|
1012 if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then |
|
1013 dec(t, Ammoz[i].Probability) |
|
1014 end |
|
1015 end; |
|
1016 GetAmmo:= i |
|
1017 end; |
|
1018 |
|
1019 function GetUtility(Hedgehog: PHedgehog): TAmmoType; |
|
1020 var t, uTot: LongInt; |
|
1021 i: TAmmoType; |
|
1022 begin |
|
1023 |
|
1024 uTot:= 0; |
|
1025 for i:= Low(TAmmoType) to High(TAmmoType) do |
|
1026 if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) and ((Hedgehog^.Team^.HedgehogsNumber > 1) or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then |
|
1027 inc(uTot, Ammoz[i].Probability); |
|
1028 |
|
1029 t:= uTot; |
|
1030 i:= Low(TAmmoType); |
|
1031 if (t > 0) then |
|
1032 begin |
|
1033 t:= GetRandom(t); |
|
1034 while t >= 0 do |
|
1035 begin |
|
1036 inc(i); |
|
1037 if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) and ((Hedgehog^.Team^.HedgehogsNumber > 1) or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then |
|
1038 dec(t, Ammoz[i].Probability) |
|
1039 end |
|
1040 end; |
|
1041 GetUtility:= i |
|
1042 end; |
|
1043 |
|
1044 |
|
1045 |
|
1046 procedure SpawnBoxOfSmth; |
|
1047 var t, aTot, uTot, a, h: LongInt; |
|
1048 i: TAmmoType; |
|
1049 begin |
|
1050 if (PlacingHogs) or |
|
1051 (cCaseFactor = 0) or |
|
1052 (CountGears(gtCase) >= 5) or |
|
1053 (GetRandom(cCaseFactor) <> 0) then exit; |
|
1054 |
|
1055 FollowGear:= nil; |
|
1056 aTot:= 0; |
|
1057 uTot:= 0; |
|
1058 for i:= Low(TAmmoType) to High(TAmmoType) do |
|
1059 if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then |
|
1060 inc(aTot, Ammoz[i].Probability) |
|
1061 else |
|
1062 inc(uTot, Ammoz[i].Probability); |
|
1063 |
|
1064 t:=0; |
|
1065 a:=aTot; |
|
1066 h:= 1; |
|
1067 |
|
1068 if (aTot+uTot) <> 0 then |
|
1069 if ((GameFlags and gfInvulnerable) = 0) then |
|
1070 begin |
|
1071 h:= cHealthCaseProb * 100; |
|
1072 t:= GetRandom(10000); |
|
1073 a:= (10000-h)*aTot div (aTot+uTot) |
|
1074 end |
|
1075 else |
|
1076 begin |
|
1077 t:= GetRandom(aTot+uTot); |
|
1078 h:= 0 |
|
1079 end; |
|
1080 |
|
1081 |
|
1082 if t<h then |
|
1083 begin |
|
1084 FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0); |
|
1085 FollowGear^.Health:= cHealthCaseAmount; |
|
1086 FollowGear^.Pos:= posCaseHealth; |
|
1087 AddCaption(GetEventString(eidNewHealthPack), cWhiteColor, capgrpAmmoInfo); |
|
1088 end |
|
1089 else if (t<a+h) then |
|
1090 begin |
|
1091 t:= aTot; |
|
1092 if (t > 0) then |
|
1093 begin |
|
1094 FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0); |
|
1095 t:= GetRandom(t); |
|
1096 i:= Low(TAmmoType); |
|
1097 FollowGear^.Pos:= posCaseAmmo; |
|
1098 FollowGear^.AmmoType:= i; |
|
1099 AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo); |
|
1100 end |
|
1101 end |
|
1102 else |
|
1103 begin |
|
1104 t:= uTot; |
|
1105 if (t > 0) then |
|
1106 begin |
|
1107 FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0); |
|
1108 t:= GetRandom(t); |
|
1109 i:= Low(TAmmoType); |
|
1110 FollowGear^.Pos:= posCaseUtility; |
|
1111 FollowGear^.AmmoType:= i; |
|
1112 AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo); |
|
1113 end |
|
1114 end; |
|
1115 |
|
1116 // handles case of no ammo or utility crates - considered also placing booleans in uAmmos and altering probabilities |
|
1117 if (FollowGear <> nil) then |
|
1118 begin |
|
1119 FindPlace(FollowGear, true, 0, LAND_WIDTH); |
|
1120 |
|
1121 if (FollowGear <> nil) then |
|
1122 AddVoice(sndReinforce, CurrentTeam^.voicepack) |
|
1123 end |
|
1124 end; |
|
1125 |
|
1126 |
|
1127 function GearByUID(uid : Longword) : PGear; |
|
1128 var gear: PGear; |
|
1129 begin |
|
1130 GearByUID:= nil; |
|
1131 if uid = 0 then exit; |
|
1132 if (lastGearByUID <> nil) and (lastGearByUID^.uid = uid) then |
|
1133 begin |
|
1134 GearByUID:= lastGearByUID; |
|
1135 exit |
|
1136 end; |
|
1137 gear:= GearsList; |
|
1138 while gear <> nil do |
|
1139 begin |
|
1140 if gear^.uid = uid then |
|
1141 begin |
|
1142 lastGearByUID:= gear; |
|
1143 GearByUID:= gear; |
|
1144 exit |
|
1145 end; |
|
1146 gear:= gear^.NextGear |
|
1147 end |
|
1148 end; |
|
1149 |
|
1150 |
|
1151 procedure chSkip(var s: shortstring); |
|
1152 begin |
|
1153 s:= s; // avoid compiler hint |
|
1154 if not CurrentTeam^.ExtDriven then SendIPC(','); |
|
1155 uStats.Skipped; |
|
1156 skipFlag:= true |
|
1157 end; |
|
1158 |
|
1159 procedure chHogSay(var s: shortstring); |
|
1160 var Gear: PVisualGear; |
|
1161 text: shortstring; |
|
1162 hh: PHedgehog; |
|
1163 i, x, t, h: byte; |
|
1164 c, j: LongInt; |
|
1165 begin |
|
1166 hh:= nil; |
|
1167 i:= 0; |
|
1168 t:= 0; |
|
1169 x:= byte(s[1]); // speech type |
|
1170 if x < 4 then |
|
1171 begin |
|
1172 t:= byte(s[2]); // team |
|
1173 if Length(s) > 2 then h:= byte(s[3]) // target hog |
|
1174 end; |
|
1175 // allow targetting a hog by specifying a number as the first portion of the text |
|
1176 if (x < 4) and (h > byte('0')) and (h < byte('9')) then i:= h - 48; |
|
1177 if i <> 0 then text:= copy(s, 4, Length(s) - 1) |
|
1178 else if x < 4 then text:= copy(s, 3, Length(s) - 1) |
|
1179 else text:= copy(s, 2, Length(s) - 1); |
|
1180 |
|
1181 (* |
|
1182 if CheckNoTeamOrHH then |
|
1183 begin |
|
1184 ParseCommand('say ' + text, true); |
|
1185 exit |
|
1186 end; |
|
1187 *) |
|
1188 |
|
1189 if (x < 4) and (TeamsArray[t] <> nil) then |
|
1190 begin |
|
1191 // if team matches current hedgehog team, default to current hedgehog |
|
1192 if (i = 0) and (CurrentHedgehog <> nil) and (CurrentHedgehog^.Team = TeamsArray[t]) then hh:= CurrentHedgehog |
|
1193 else |
|
1194 begin |
|
1195 // otherwise use the first living hog or the hog amongs the remaining ones indicated by i |
|
1196 j:= 0; |
|
1197 c:= 0; |
|
1198 while (j <= cMaxHHIndex) and (hh = nil) do |
|
1199 begin |
|
1200 if (TeamsArray[t]^.Hedgehogs[j].Gear <> nil) then |
|
1201 begin |
|
1202 inc(c); |
|
1203 if (i=0) or (i=c) then |
|
1204 hh:= @TeamsArray[t]^.Hedgehogs[j] |
|
1205 end; |
|
1206 inc(j) |
|
1207 end |
|
1208 end; |
|
1209 if hh <> nil then |
|
1210 begin |
|
1211 Gear:= AddVisualGear(0, 0, vgtSpeechBubble); |
|
1212 if Gear <> nil then |
|
1213 begin |
|
1214 Gear^.Hedgehog:= hh; |
|
1215 Gear^.Text:= text; |
|
1216 Gear^.FrameTicks:= x |
|
1217 end |
|
1218 end |
|
1219 //else ParseCommand('say ' + text, true) |
|
1220 end |
|
1221 else if (x >= 4) then |
|
1222 begin |
|
1223 SpeechType:= x-3; |
|
1224 SpeechText:= text |
|
1225 end; |
|
1226 end; |
|
1227 |
|
1228 procedure initModule; |
|
1229 begin |
|
1230 RegisterVariable('skip', vtCommand, @chSkip, false); |
1227 RegisterVariable('skip', vtCommand, @chSkip, false); |
1231 RegisterVariable('hogsay', vtCommand, @chHogSay, true ); |
1228 RegisterVariable('hogsay', vtCommand, @chHogSay, true ); |
1232 |
1229 |
1233 CurAmmoGear:= nil; |
1230 CurAmmoGear:= nil; |
1234 GearsList:= nil; |
1231 GearsList:= nil; |