FREE AT LAST!!! SDL came around a (mostly) sane way for implementing rotation events, so we can scrap all the workaround code that has been added to workaround it!! Also this allows us to use proper (internal) multitasking handling and can simplify optional settings and other yet unexplored features. Yay!
(* * Hedgewars, a free turn based strategy game * Copyright (c) 2004-2011 Andrey Korotaev <unC0Rr@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA *)////////////////////////////////////////////////////////////////////////////////procedure HHHurt(Hedgehog: PHedgehog; Source: TDamageSource);beginif (Source = dsFall) or (Source = dsExplosion) then case random(3) of 0: PlaySound(sndOoff1, Hedgehog^.Team^.voicepack); 1: PlaySound(sndOoff2, Hedgehog^.Team^.voicepack); 2: PlaySound(sndOoff3, Hedgehog^.Team^.voicepack); endelse if (Source = dsPoison) then case random(2) of 0: PlaySound(sndPoisonCough, Hedgehog^.Team^.voicepack); 1: PlaySound(sndPoisonMoan, Hedgehog^.Team^.voicepack); endelse case random(4) of 0: PlaySound(sndOw1, Hedgehog^.Team^.voicepack); 1: PlaySound(sndOw2, Hedgehog^.Team^.voicepack); 2: PlaySound(sndOw3, Hedgehog^.Team^.voicepack); 3: PlaySound(sndOw4, Hedgehog^.Team^.voicepack); endend;// Shouldn't more of this ammo switching stuff be moved to uAmmos ?function ChangeAmmo(HHGear: PGear): boolean;var slot, i: Longword; ammoidx: LongInt;beginChangeAmmo:= false;slot:= HHGear^.MsgParam;with HHGear^.Hedgehog^ do begin HHGear^.Message:= HHGear^.Message and not gmSlot; ammoidx:= 0; if ((HHGear^.State and (gstAttacking or gstAttacked)) <> 0) or (TargetPoint.X <> NoPointX) or ((MultiShootAttacks > 0) and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NoRoundEnd) = 0)) or ((HHGear^.State and gstHHDriven) = 0) then exit; ChangeAmmo:= true; while (ammoidx < cMaxSlotAmmoIndex) and (Ammo^[slot, ammoidx].AmmoType <> CurAmmoType) do inc(ammoidx); if ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NoRoundEnd) <> 0) and (MultiShootAttacks > 0) then OnUsedAmmo(HHGear^.Hedgehog^); MultiShootAttacks:= 0; HHGear^.Message:= HHGear^.Message and not (gmLJump or gmHJump); if Ammoz[CurAmmoType].Slot = slot then begin i:= 0; repeat inc(ammoidx); if (ammoidx > cMaxSlotAmmoIndex) then begin inc(i); CurAmmoType:= amNothing; ammoidx:= -1; //TryDo(i < 2, 'Engine bug: no ammo in current slot', true) end; until (i = 1) or ((Ammo^[slot, ammoidx].Count > 0) and (Team^.Clan^.TurnNumber > Ammoz[Ammo^[slot, ammoidx].AmmoType].SkipTurns)) end else begin i:= 0; // check whether there is ammo in slot while (i <= cMaxSlotAmmoIndex) and ((Ammo^[slot, i].Count = 0) or (Team^.Clan^.TurnNumber <= Ammoz[Ammo^[slot, i].AmmoType].SkipTurns)) do inc(i); if i <= cMaxSlotAmmoIndex then ammoidx:= i else ammoidx:= -1 end; if ammoidx >= 0 then CurAmmoType:= Ammo^[slot, ammoidx].AmmoType; endend;procedure HHSetWeapon(HHGear: PGear);var t: LongInt; weap: TAmmoType; Hedgehog: PHedgehog; s: boolean;begins:= false;weap:= TAmmoType(HHGear^.MsgParam);Hedgehog:= HHGear^.Hedgehog;if Hedgehog^.Team^.Clan^.TurnNumber <= Ammoz[weap].SkipTurns then exit; // weapon is not activated yetHHGear^.MsgParam:= Ammoz[weap].Slot;t:= cMaxSlotAmmoIndex;HHGear^.Message:= HHGear^.Message and not gmWeapon;with Hedgehog^ do while (CurAmmoType <> weap) and (t >= 0) do begin s:= ChangeAmmo(HHGear); dec(t) end;if s then ApplyAmmoChanges(HHGear^.Hedgehog^)end;procedure HHSetTimer(Gear: PGear);var CurWeapon: PAmmo; color: LongWord;beginGear^.Message:= Gear^.Message and not gmTimer;CurWeapon:= GetAmmoEntry(Gear^.Hedgehog^);with Gear^.Hedgehog^ do if ((Gear^.Message and gmPrecise) <> 0) and ((CurWeapon^.Propz and ammoprop_SetBounce) <> 0) then begin color:= Gear^.Hedgehog^.Team^.Clan^.Color; case Gear^.MsgParam of 1: begin AddCaption(format(trmsg[sidBounce], trmsg[sidBounce1]), color, capgrpAmmostate); CurWeapon^.Bounciness:= 350; end; 2: begin AddCaption(format(trmsg[sidBounce], trmsg[sidBounce2]), color, capgrpAmmostate); CurWeapon^.Bounciness:= 700; end; 3: begin AddCaption(format(trmsg[sidBounce], trmsg[sidBounce3]), color, capgrpAmmostate); CurWeapon^.Bounciness:= 1000; end; 4: begin AddCaption(format(trmsg[sidBounce], trmsg[sidBounce4]), color, capgrpAmmostate); CurWeapon^.Bounciness:= 2000; end; 5: begin AddCaption(format(trmsg[sidBounce], trmsg[sidBounce5]), color, capgrpAmmostate); CurWeapon^.Bounciness:= 4000; end end end else if (CurWeapon^.Propz and ammoprop_Timerable) <> 0 then begin CurWeapon^.Timer:= 1000 * Gear^.MsgParam; with CurrentTeam^ do ApplyAmmoChanges(Hedgehogs[CurrHedgehog]); end;end;procedure Attack(Gear: PGear);var xx, yy, newDx, newDy, lx, ly: hwFloat; speech: PVisualGear; newGear: PGear; CurWeapon: PAmmo; altUse: boolean; elastic: hwFloat;beginnewGear:= nil;bShowFinger:= false;CurWeapon:= GetAmmoEntry(Gear^.Hedgehog^);with Gear^, Gear^.Hedgehog^ do begin if ((State and gstHHDriven) <> 0)and ((State and (gstAttacked or gstHHChooseTarget)) = 0) and (((State and gstMoving) = 0) or (CurAmmoType = amTeleport) or // Allow attacks while moving on ammo with AltAttack ((CurAmmoGear <> nil) and ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_AltAttack) <> 0)) or ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AttackInMove) <> 0)) and ((TargetPoint.X <> NoPointX) or ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NeedTarget) = 0)) then begin State:= State or gstAttacking; if Power = cMaxPower then Message:= Message and not gmAttack else if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) = 0 then Message:= Message and not gmAttack else begin if Power = 0 then begin AttackBar:= CurrentTeam^.AttackBar; PlaySound(sndThrowPowerUp) end; inc(Power) end; if ((Message and gmAttack) <> 0) then exit; if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) <> 0 then begin StopSound(sndThrowPowerUp); PlaySound(sndThrowRelease); end; xx:= SignAs(AngleSin(Angle), dX); yy:= -AngleCos(Angle); lx:= X + int2hwfloat(round(GetLaunchX(CurAmmoType, hwSign(dX), Angle))); ly:= Y + int2hwfloat(round(GetLaunchY(CurAmmoType, Angle))); if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) then xx:= - xx; if Ammoz[CurAmmoType].Ammo.AttackVoice <> sndNone then AddVoice(Ammoz[CurAmmoType].Ammo.AttackVoice, CurrentTeam^.voicepack);// Initiating alt attack if (CurAmmoGear <> nil) and ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_AltAttack) <> 0) and ((Gear^.Message and gmLJump) <> 0) and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) <> 0) then begin newDx:= dX / _2; newDy:= dY / _2; altUse:= true; end else begin newDx:= xx*Power/cPowerDivisor; newDy:= yy*Power/cPowerDivisor; altUse:= false end; case CurAmmoType of amGrenade: newGear:= AddGear(hwRound(lx), hwRound(ly), gtGrenade, 0, newDx, newDy, CurWeapon^.Timer); amMolotov: newGear:= AddGear(hwRound(lx), hwRound(ly), gtMolotov, 0, newDx, newDy, 0); amClusterBomb: newGear:= AddGear(hwRound(lx), hwRound(ly), gtClusterBomb, 0, newDx, newDy, CurWeapon^.Timer); amGasBomb: newGear:= AddGear(hwRound(lx), hwRound(ly), gtGasBomb, 0, newDx, newDy, CurWeapon^.Timer); amBazooka: newGear:= AddGear(hwRound(lx), hwRound(ly), gtShell, 0, newDx, newDy, 0); amSnowball: newGear:= AddGear(hwRound(lx), hwRound(ly), gtSnowball, 0, newDx, newDy, 0); amBee: newGear:= AddGear(hwRound(lx), hwRound(ly), gtBee, 0, newDx, newDy, 0); amShotgun: begin PlaySound(sndShotgunReload); newGear:= AddGear(hwRound(lx), hwRound(ly), gtShotgunShot, 0, xx * _0_5, yy * _0_5, 0); end; amPickHammer: newGear:= AddGear(hwRound(lx), hwRound(ly) + cHHRadius, gtPickHammer, 0, _0, _0, 0); amSkip: ParseCommand('/skip', true); amRope: newGear:= AddGear(hwRound(lx), hwRound(ly), gtRope, 0, xx, yy, 0); amMine: if altUse then newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtMine, gstWait, newDx, newDy, 3000) else newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtMine, gstWait, SignAs(_0_02, dX), _0, 3000); amSMine: newGear:= AddGear(hwRound(lx), hwRound(ly), gtSMine, 0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0); amDEagle: newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtDEagleShot, 0, xx * _0_5, yy * _0_5, 0); amSineGun: newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtSineGunShot, 0, xx * _0_5, yy * _0_5, 0); amPortalGun: begin newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtPortal, 0, xx * _0_6, yy * _0_6, // set selected color CurWeapon^.Pos); end; amSniperRifle: begin PlaySound(sndSniperReload); newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtSniperRifleShot, 0, xx * _0_5, yy * _0_5, 0); end; amDynamite: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtDynamite, 0, SignAs(_0_03, dX), _0, 5000); amFirePunch: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 10, hwRound(ly), gtFirePunch, 0, xx, _0, 0); amWhip: begin newGear:= AddGear(hwRound(lx) + hwSign(dX) * 10, hwRound(ly), gtWhip, 0, SignAs(_1, dX), - _0_8, 0); PlaySound(sndWhipCrack) end; amHammer: begin newGear:= AddGear(hwRound(lx) + hwSign(dX) * 10, hwRound(ly), gtHammer, 0, SignAs(_1, dX), - _0_8, 0); PlaySound(sndWhack) end; amBaseballBat: begin newGear:= AddGear(hwRound(lx) + hwSign(dX) * 10, hwRound(ly), gtShover, gsttmpFlag, xx * _0_5, yy * _0_5, 0); PlaySound(sndBaseballBat) // TODO: Only play if something is hit? end; amParachute: begin newGear:= AddGear(hwRound(lx), hwRound(ly), gtParachute, 0, _0, _0, 0); PlaySound(sndParachute) end; // we save CurWeapon^.Pos (in this case: cursor direction) by using it as (otherwise irrelevant) X value of the new gear. amAirAttack: newGear:= AddGear(CurWeapon^.Pos, 0, gtAirAttack, 0, _0, _0, 0); amMineStrike: newGear:= AddGear(CurWeapon^.Pos, 0, gtAirAttack, 1, _0, _0, 0); amDrillStrike: newGear:= AddGear(CurWeapon^.Pos, 0, gtAirAttack, 3, _0, _0, CurWeapon^.Timer); amNapalm: newGear:= AddGear(CurWeapon^.Pos, 0, gtAirAttack, 2, _0, _0, 0); amBlowTorch: newGear:= AddGear(hwRound(lx), hwRound(ly), gtBlowTorch, 0, SignAs(_0_5, dX), _0, 0); amGirder: newGear:= AddGear(0, 0, gtGirder, CurWeapon^.Pos, _0, _0, 0); amTeleport: newGear:= AddGear(CurWeapon^.Pos, 0, gtTeleport, 0, _0, _0, 0); amSwitch: newGear:= AddGear(hwRound(lx), hwRound(ly), gtSwitcher, 0, _0, _0, 0); amMortar: begin playSound(sndMortar); newGear:= AddGear(hwRound(lx), hwRound(ly), gtMortar, 0, xx*cMaxPower/cPowerDivisor, yy*cMaxPower/cPowerDivisor, 0); end; amRCPlane: begin newGear:= AddGear(hwRound(lx), hwRound(ly), gtRCPlane, 0, xx * cMaxPower / cPowerDivisor / 4, yy * cMaxPower / cPowerDivisor / 4, 0); newGear^.SoundChannel:= LoopSound(sndRCPlane, nil) end; amKamikaze: newGear:= AddGear(hwRound(lx), hwRound(ly), gtKamikaze, 0, xx * _0_5, yy * _0_5, 0); amCake: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 3, hwRound(ly), gtCake, 0, xx, _0, 0); amSeduction: newGear:= AddGear(hwRound(lx), hwRound(ly), gtSeduction, 0, _0, _0, 0); amWatermelon: newGear:= AddGear(hwRound(lx), hwRound(ly), gtWatermelon, 0, newDx, newDy, CurWeapon^.Timer); amHellishBomb: newGear:= AddGear(hwRound(lx), hwRound(ly), gtHellishBomb, 0, newDx, newDy, 0); amDrill: newGear:= AddGear(hwRound(lx), hwRound(ly), gtDrill, 0, newDx, newDy, 0); amBallgun: newGear:= AddGear(hwRound(X), hwRound(Y), gtBallgun, 0, xx * _0_5, yy * _0_5, 0); amJetpack: newGear:= AddGear(hwRound(lx), hwRound(ly), gtJetpack, 0, _0, _0, 0); amBirdy: begin PlaySound(sndWhistle); newGear:= AddGear(hwRound(lx), hwRound(ly) - 32, gtBirdy, 0, _0, _0, 0); end; amLowGravity: begin PlaySound(sndLowGravity); cGravity:= cMaxWindSpeed; cGravityf:= 0.00025 end; amExtraDamage:begin PlaySound(sndHellishImpact4); cDamageModifier:= _1_5 end; amInvulnerable: Invulnerable:= true; amExtraTime: begin PlaySound(sndSwitchHog); TurnTimeLeft:= TurnTimeLeft + 30000 end; amLaserSight: cLaserSighting:= true; amVampiric: begin PlaySound(sndOw1, Team^.voicepack); cVampiric:= true; end; amPiano: begin // Tuck the hedgehog away until the piano attack is completed Unplaced:= true; X:= _0; Y:= _0; newGear:= AddGear(TargetPoint.X, 0, gtPiano, 0, _0, _0, 0); PauseMusic end; amFlamethrower: newGear:= AddGear(hwRound(X), hwRound(Y), gtFlamethrower, 0, xx * _0_5, yy * _0_5, 0); amLandGun: newGear:= AddGear(hwRound(X), hwRound(Y), gtLandGun, 0, xx * _0_5, yy * _0_5, 0); amResurrector: begin newGear:= AddGear(hwRound(lx), hwRound(ly), gtResurrector, 0, _0, _0, 0); newGear^.SoundChannel := LoopSound(sndResurrector); end; //amMelonStrike: AddGear(CurWeapon^.Pos, 0, gtAirAttack, 4, _0, _0, 0); amStructure: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtStructure, gstWait, SignAs(_0_02, dX), _0, 3000); amTardis: newGear:= AddGear(hwRound(X), hwRound(Y), gtTardis, 0, _0, _0, 5000); end; case CurAmmoType of amGrenade, amMolotov, amClusterBomb, amGasBomb, amBazooka, amSnowball, amBee, amSMine, amMortar, amWatermelon, amHellishBomb, amDrill, amPiano: FollowGear:= newGear; amShotgun, amPickHammer, amRope, amDEagle, amSineGun, amSniperRifle, amFirePunch, amWhip, amHammer, amBaseballBat, amParachute, amBlowTorch, amGirder, amTeleport, amSwitch, amRCPlane, amKamikaze, amCake, amSeduction, amBallgun, amJetpack, amBirdy, amFlamethrower, amLandGun, amResurrector, amStructure, amTardis: CurAmmoGear:= newGear; end; if Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0 then begin newGear^.Target.X:= TargetPoint.X; newGear^.Target.Y:= TargetPoint.Y end; // Clear FollowGear if using on a rope/parachute/saucer etc so focus stays with the hog's movement if altUse then FollowGear:= nil; if (newGear <> nil) and ((Ammoz[newGear^.AmmoType].Ammo.Propz and ammoprop_SetBounce) <> 0) then begin elastic:= int2hwfloat(CurWeapon^.Bounciness) / _1000; if elastic < _1 then newGear^.Elasticity:= newGear^.Elasticity * elastic else if elastic > _1 then newGear^.Elasticity:= _1 - ((_1-newGear^.Elasticity) / elastic);(* Experimented with friction modifier. Didn't seem helpful fric:= int2hwfloat(CurWeapon^.Bounciness) / _250; if fric < _1 then newGear^.Friction:= newGear^.Friction * fric else if fric > _1 then newGear^.Friction:= _1 - ((_1-newGear^.Friction) / fric)*) end; uStats.AmmoUsed(CurAmmoType); if not (SpeechText = '') then begin speech:= AddVisualGear(0, 0, vgtSpeechBubble); if speech <> nil then begin speech^.Text:= SpeechText; speech^.Hedgehog:= Gear^.Hedgehog; speech^.FrameTicks:= SpeechType; end; SpeechText:= '' end; Power:= 0; if (CurAmmoGear <> nil) and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) = 0){check for dropping ammo from rope} then begin Message:= Message or gmAttack; CurAmmoGear^.Message:= Message end else begin if not CurrentTeam^.ExtDriven and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) <> 0) then SendIPC('a'); AfterAttack; end end else Message:= Message and not gmAttack; end; TargetPoint.X := NoPointXend;procedure AfterAttack;var s: shortstring; a: TAmmoType;beginwith CurrentHedgehog^.Gear^, CurrentHedgehog^ do begin a:= CurAmmoType; State:= State and not gstAttacking; if (Ammoz[a].Ammo.Propz and ammoprop_Effect) = 0 then begin Inc(MultiShootAttacks); if (Ammoz[a].Ammo.NumPerTurn >= MultiShootAttacks) then begin s:= inttostr(Ammoz[a].Ammo.NumPerTurn - MultiShootAttacks + 1); AddCaption(format(trmsg[sidRemaining], s), cWhiteColor, capgrpAmmostate); end; if (Ammoz[a].Ammo.NumPerTurn >= MultiShootAttacks) or ((GameFlags and gfMultiWeapon) <> 0) then begin isInMultiShoot:= true end else begin OnUsedAmmo(CurrentHedgehog^); if ((Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) = 0) and (((GameFlags and gfInfAttack) = 0) or PlacingHogs) then begin if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft; TurnTimeLeft:=(Ammoz[a].TimeAfterTurn * cGetAwayTime) div 100; end; if ((Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) = 0) then State:= State or gstAttacked; if (Ammoz[a].Ammo.Propz and ammoprop_NoRoundEnd) <> 0 then ApplyAmmoChanges(CurrentHedgehog^) end; end else begin OnUsedAmmo(CurrentHedgehog^); ApplyAmmoChanges(CurrentHedgehog^); end; AttackBar:= 0 endend;////////////////////////////////////////////////////////////////////////////////procedure doStepHedgehogDead(Gear: PGear);const frametime = 200; timertime = frametime * 6;beginif Gear^.Hedgehog^.Unplaced then exit;if Gear^.Timer > 1 then begin AllInactive:= false; dec(Gear^.Timer); if (Gear^.Timer mod frametime) = 0 then inc(Gear^.Pos) end elseif Gear^.Timer = 1 then begin Gear^.State:= Gear^.State or gstNoDamage; doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, CurrentHedgehog, EXPLAutoSound); AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtGrave, 0, _0, _0, 0)^.Hedgehog:= Gear^.Hedgehog; DeleteGear(Gear); SetAllToActive end else // Gear^.Timer = 0 begin AllInactive:= false; Gear^.Z:= cCurrHHZ; RemoveGearFromList(Gear); InsertGearToList(Gear); PlaySound(sndByeBye, Gear^.Hedgehog^.Team^.voicepack); Gear^.Pos:= 0; Gear^.Timer:= timertime endend;////////////////////////////////////////////////////////////////////////////////procedure doStepHedgehogGone(Gear: PGear);const frametime = 65; timertime = frametime * 11;beginif Gear^.Hedgehog^.Unplaced then exit;if Gear^.Timer > 1 then begin AllInactive:= false; dec(Gear^.Timer); if (Gear^.Timer mod frametime) = 0 then inc(Gear^.Pos) end elseif Gear^.Timer = 1 then begin DeleteGear(Gear); SetAllToActive end else // Gear^.Timer = 0 begin AllInactive:= false; Gear^.Z:= cCurrHHZ; RemoveGearFromList(Gear); InsertGearToList(Gear); PlaySound(sndByeBye, Gear^.Hedgehog^.Team^.voicepack); PlaySound(sndWarp); Gear^.Pos:= 0; Gear^.Timer:= timertime endend;////////////////////////////////////////////////////////////////////////////////procedure PickUp(HH, Gear: PGear);var s: shortstring; a: TAmmoType; i: LongInt; vga: PVisualGear;beginGear^.Message:= gmDestroy;PlaySound(sndShotgunReload);if (Gear^.Pos and posCaseExplode) <> 0 then if (Gear^.Pos and posCasePoison) <> 0 then doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, HH^.Hedgehog, EXPLAutoSound + EXPLPoisoned) else doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, HH^.Hedgehog, EXPLAutoSound)else if (Gear^.Pos and posCasePoison) <> 0 then doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, HH^.Hedgehog, EXPLAutoSound + EXPLPoisoned + EXPLNoDamage)elsecase Gear^.Pos of posCaseUtility, posCaseAmmo: begin if Gear^.AmmoType <> amNothing then a:= Gear^.AmmoType else begin for i:= 0 to GameTicks and $7F do GetRandom(2); // Burn some random numbers if Gear^.Pos = posCaseUtility then a:= GetUtility else a:= GetAmmo end; AddAmmo(HH^.Hedgehog^, a);// Possibly needs to check shared clan ammo game flag once added.// On the other hand, no obvious reason that clan members shouldn't know what ammo another clan member picked up if (not (HH^.Hedgehog^.Team^.ExtDriven or (HH^.Hedgehog^.BotLevel > 0))) or (HH^.Hedgehog^.Team^.Clan^.ClanIndex = LocalClan) or (GameType = gmtDemo) then begin s:= trammo[Ammoz[a].NameId] + ' (+' + IntToStr(Ammoz[a].NumberInCase) + ')'; AddCaption(s, HH^.Hedgehog^.Team^.Clan^.Color, capgrpAmmoinfo); // show ammo icon vga:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtAmmo); if vga <> nil then vga^.Frame:= Longword(a); end; end; posCaseHealth: begin inc(HH^.Health, Gear^.Health); HH^.Hedgehog^.Effects[hePoisoned] := false; str(Gear^.Health, s); s:= '+' + s; AddCaption(s, HH^.Hedgehog^.Team^.Clan^.Color, capgrpAmmoinfo); RenderHealth(HH^.Hedgehog^); RecountTeamHealth(HH^.Hedgehog^.Team); i:= 0; while i < Gear^.Health do begin vga:= AddVisualGear(hwRound(HH^.X), hwRound(HH^.Y), vgtStraightShot); if vga <> nil then with vga^ do begin Tint:= $00FF00FF; State:= ord(sprHealth) end; inc(i, 5); end; end; endend;const StepTicks: LongWord = 0;procedure HedgehogStep(Gear: PGear);var PrevdX: LongInt; CurWeapon: PAmmo;beginCurWeapon:= GetAmmoEntry(Gear^.Hedgehog^);if ((Gear^.State and (gstAttacking or gstMoving)) = 0) then begin if isCursorVisible then with Gear^.Hedgehog^ do with CurWeapon^ do begin if (Gear^.Message and gmLeft ) <> 0 then Pos:= (Pos - 1 + Ammoz[AmmoType].PosCount) mod Ammoz[AmmoType].PosCount else if (Gear^.Message and gmRight ) <> 0 then Pos:= (Pos + 1) mod Ammoz[AmmoType].PosCount else exit; StepTicks:= 200; exit end; if ((Gear^.Message and gmAnimate) <> 0) then begin Gear^.Message:= 0; Gear^.State:= Gear^.State or gstAnimation; Gear^.Tag:= Gear^.MsgParam; Gear^.Timer:= 0; Gear^.Pos:= 0 end; if ((Gear^.Message and gmLJump ) <> 0) then begin Gear^.Message:= Gear^.Message and not gmLJump; DeleteCI(Gear); if not TestCollisionYwithGear(Gear, -1) then if not TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) then Gear^.Y:= Gear^.Y - _2 else if not TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) then Gear^.Y:= Gear^.Y - _1; if not (TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) or TestCollisionYwithGear(Gear, -1)) then begin Gear^.dY:= -_0_15; if not cArtillery then Gear^.dX:= SignAs(_0_15, Gear^.dX); Gear^.State:= Gear^.State or gstMoving or gstHHJumping; PlaySound(sndJump1, Gear^.Hedgehog^.Team^.voicepack); exit end; end; if ((Gear^.Message and gmHJump ) <> 0) then begin DeleteCI(Gear); Gear^.Message:= Gear^.Message and not gmHJump; Gear^.dY:= -_0_2; SetLittle(Gear^.dX); Gear^.State:= Gear^.State or gstMoving or gstHHJumping; PlaySound(sndJump3, Gear^.Hedgehog^.Team^.voicepack); exit end; PrevdX:= hwSign(Gear^.dX); if (Gear^.Message and gmLeft )<>0 then Gear^.dX:= -cLittle else if (Gear^.Message and gmRight )<>0 then Gear^.dX:= cLittle else exit; if (Gear^.Message and (gmLeft or gmRight)) <> 0 then begin StepSoundTimer:= cHHStepTicks; end; StepTicks:= cHHStepTicks; if PrevdX <> hwSign(Gear^.dX) then begin FollowGear:= Gear; exit end; DeleteCI(Gear); // must be after exit!! (see previous line) Gear^.Hedgehog^.visStepPos:= (Gear^.Hedgehog^.visStepPos + 1) and 7; if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then begin if not (TestCollisionXwithXYShift(Gear, _0, -6, hwSign(Gear^.dX)) or TestCollisionYwithGear(Gear, -1)) then Gear^.Y:= Gear^.Y - _1; if not (TestCollisionXwithXYShift(Gear, _0, -5, hwSign(Gear^.dX)) or TestCollisionYwithGear(Gear, -1)) then Gear^.Y:= Gear^.Y - _1; if not (TestCollisionXwithXYShift(Gear, _0, -4, hwSign(Gear^.dX)) or TestCollisionYwithGear(Gear, -1)) then Gear^.Y:= Gear^.Y - _1; if not (TestCollisionXwithXYShift(Gear, _0, -3, hwSign(Gear^.dX)) or TestCollisionYwithGear(Gear, -1)) then Gear^.Y:= Gear^.Y - _1; if not (TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) or TestCollisionYwithGear(Gear, -1)) then Gear^.Y:= Gear^.Y - _1; if not (TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) or TestCollisionYwithGear(Gear, -1)) then Gear^.Y:= Gear^.Y - _1; end; if (not cArtillery) and ((Gear^.Message and gmPrecise) = 0) and (not TestCollisionXwithGear(Gear, hwSign(Gear^.dX))) then Gear^.X:= Gear^.X + SignAs(_1, Gear^.dX); SetAllHHToActive; if not TestCollisionYwithGear(Gear, 1) then begin Gear^.Y:= Gear^.Y + _1; if not TestCollisionYwithGear(Gear, 1) then begin Gear^.Y:= Gear^.Y + _1; if not TestCollisionYwithGear(Gear, 1) then begin Gear^.Y:= Gear^.Y + _1; if not TestCollisionYwithGear(Gear, 1) then begin Gear^.Y:= Gear^.Y + _1; if not TestCollisionYwithGear(Gear, 1) then begin Gear^.Y:= Gear^.Y + _1; if not TestCollisionYwithGear(Gear, 1) then begin Gear^.Y:= Gear^.Y + _1; if not TestCollisionYwithGear(Gear, 1) then begin Gear^.Y:= Gear^.Y - _6; Gear^.dY:= _0; Gear^.State:= Gear^.State or gstMoving; exit end; end end end end end end; AddGearCI(Gear) endend;procedure HedgehogChAngle(HHGear: PGear);var da: LongWord;beginwith HHGear^.Hedgehog^ do if ((CurAmmoType = amRope) and ((HHGear^.State and (gstMoving or gstHHJumping)) = gstMoving)) or ((CurAmmoType = amPortalGun) and ((HHGear^.State and gstMoving) <> 0)) then da:= 2 else da:= 1;if (((HHGear^.Message and gmPrecise) = 0) or ((GameTicks mod 5) = 1)) then if ((HHGear^.Message and gmUp) <> 0) and (HHGear^.Angle >= CurMinAngle + da) then dec(HHGear^.Angle, da) else if ((HHGear^.Message and gmDown) <> 0) and (HHGear^.Angle + da <= CurMaxAngle) then inc(HHGear^.Angle, da)end;procedure doStepHedgehog(Gear: PGear); forward;////////////////////////////////////////////////////////////////////////////////procedure doStepHedgehogMoving(Gear: PGear);var isFalling, isUnderwater: boolean;beginisUnderwater:= cWaterLine < hwRound(Gear^.Y) + Gear^.Radius;if Gear^.dX.QWordValue > 8160437862 then Gear^.dX.QWordValue:= 8160437862;if Gear^.dY.QWordValue > 8160437862 then Gear^.dY.QWordValue:= 8160437862;if Gear^.Hedgehog^.Unplaced then begin Gear^.dY:= _0; Gear^.dX:= _0; Gear^.State:= Gear^.State and not gstMoving; exit end;isFalling:= (Gear^.dY.isNegative) or not TestCollisionYKick(Gear, 1);if isFalling then begin if (Gear^.dY.isNegative) and TestCollisionYKick(Gear, -1) then Gear^.dY:= _0; Gear^.State:= Gear^.State or gstMoving; if isUnderwater then Gear^.dY:= Gear^.dY + cGravity / _2 else begin Gear^.dY:= Gear^.dY + cGravity;// this set of circumstances could be less complex if jumping was more clearly identified if ((GameFlags and gfMoreWind) <> 0) and (((Gear^.Damage <> 0) or ((CurAmmoGear <> nil) and ((CurAmmoGear^.AmmoType = amJetpack) or (CurAmmoGear^.AmmoType = amBirdy))) or ((Gear^.dY.QWordValue + Gear^.dX.QWordValue) > _0_55.QWordValue))) then Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density end end else begin if ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_55.QWordValue) and ((Gear^.State and gstHHJumping) <> 0) then SetLittle(Gear^.dX); if not Gear^.dY.isNegative then begin CheckHHDamage(Gear); if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) and (Gear^.dX.QWordValue < _0_02.QWordValue) then Gear^.dX.isNegative:= not Gear^.dX.isNegative; // landing after high jump Gear^.State:= Gear^.State and not (gstHHJumping or gstHHHJump); Gear^.dY:= _0; end else Gear^.dY:= Gear^.dY + cGravity; if ((Gear^.State and gstMoving) <> 0) then Gear^.dX:= Gear^.dX * Gear^.Friction end;if (Gear^.State <> 0) then DeleteCI(Gear);if isUnderwater then begin Gear^.dY:= Gear^.dY * _0_999; Gear^.dX:= Gear^.dX * _0_999; end;if (Gear^.State and gstMoving) <> 0 then if TestCollisionXKick(Gear, hwSign(Gear^.dX)) then if not isFalling then if hwAbs(Gear^.dX) > _0_01 then if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -1, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_96; Gear^.Y:= Gear^.Y - _1 end else if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -2, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_93; Gear^.Y:= Gear^.Y - _2 end else if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -3, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_9 ; Gear^.Y:= Gear^.Y - _3 end else if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -4, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_87; Gear^.Y:= Gear^.Y - _4 end else if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -5, hwSign(Gear^.dX)) then begin Gear^.X:= Gear^.X + Gear^.dX; Gear^.dX:= Gear^.dX * _0_84; Gear^.Y:= Gear^.Y - _5 end else if hwAbs(Gear^.dX) > _0_02 then Gear^.dX:= -Gear^.Elasticity * Gear^.dX else begin Gear^.State:= Gear^.State and not gstMoving; SetLittle(Gear^.dX) end else begin Gear^.State:= Gear^.State and not gstMoving; SetLittle(Gear^.dX) end else if (hwAbs(Gear^.dX) > cLittle) and ((Gear^.State and gstHHJumping) = 0) then Gear^.dX:= -Gear^.Elasticity * Gear^.dX else SetLittle(Gear^.dX);if (not isFalling) and (hwAbs(Gear^.dX) + hwAbs(Gear^.dY) < _0_03) then begin Gear^.State:= Gear^.State and not gstWinner; Gear^.State:= Gear^.State and not gstMoving; SetLittle(Gear^.dX); Gear^.dY:= _0 end else Gear^.State:= Gear^.State or gstMoving;if (Gear^.State and gstMoving) <> 0 then begin Gear^.State:= Gear^.State and not gstAnimation;// ARTILLERY but not being moved by explosions Gear^.X:= Gear^.X + Gear^.dX; Gear^.Y:= Gear^.Y + Gear^.dY; if (not Gear^.dY.isNegative) and (not TestCollisionYKick(Gear, 1)) and TestCollisionYwithXYShift(Gear, 0, 1, 1) then begin CheckHHDamage(Gear); Gear^.dY:= _0; Gear^.Y:= Gear^.Y + _1 end; CheckGearDrowning(Gear); if (Gear^.State and gstDrowning) <> 0 then isCursorVisible:= false end;if (hwAbs(Gear^.dY) > _0) and (Gear^.FlightTime > 0) and ((GameFlags and gfLowGravity) = 0) then begin inc(Gear^.FlightTime, 1); if Gear^.FlightTime = 3000 then begin AddCaption(GetEventString(eidHomerun), cWhiteColor, capgrpMessage); PlaySound(sndHomerun) end; endelse begin Gear^.FlightTime:= 0; end;end;procedure doStepHedgehogDriven(HHGear: PGear);var t: PGear; wasJumping: boolean; Hedgehog: PHedgehog;beginHedgehog:= HHGear^.Hedgehog;if not isInMultiShoot then AllInactive:= falseelse HHGear^.Message:= 0;if (TurnTimeLeft = 0) or (HHGear^.Damage > 0) then begin if TagTurnTimeLeft = 0 then TagTurnTimeLeft:= TurnTimeLeft; TurnTimeLeft:= 0; isCursorVisible:= false; HHGear^.State:= HHGear^.State and not (gstHHDriven or gstAnimation or gstAttacking); AttackBar:= 0; if HHGear^.Damage > 0 then HHGear^.State:= HHGear^.State and not (gstHHJumping or gstHHHJump); exit end;if (HHGear^.State and gstAnimation) <> 0 then begin HHGear^.Message:= 0; if (HHGear^.Pos = Wavez[TWave(HHGear^.Tag)].VoiceDelay) and (HHGear^.Timer = 0) then PlaySound(Wavez[TWave(HHGear^.Tag)].Voice, Hedgehog^.Team^.voicepack); inc(HHGear^.Timer); if HHGear^.Timer = Wavez[TWave(HHGear^.Tag)].Interval then begin HHGear^.Timer:= 0; inc(HHGear^.Pos); if HHGear^.Pos = Wavez[TWave(HHGear^.Tag)].FramesCount then HHGear^.State:= HHGear^.State and not gstAnimation end; exit end;if ((HHGear^.State and gstMoving) <> 0) or (StepTicks = cHHStepTicks) or (CurAmmoGear <> nil) then // we are moving begin with Hedgehog^ do if (CurAmmoGear = nil) and (HHGear^.dY > _0_39) and (CurAmmoType = amParachute) then HHGear^.Message:= HHGear^.Message or gmAttack; // check for case with ammo t:= CheckGearNear(HHGear, gtCase, 36, 36); if t <> nil then PickUp(HHGear, t) end;if (CurAmmoGear = nil) then if (((HHGear^.Message and gmAttack) <> 0) or ((HHGear^.State and gstAttacking) <> 0)) then Attack(HHGear) // should be before others to avoid desync with '/put' msg and changing weapon msgs elseelse with Hedgehog^ do if ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_AltAttack) <> 0) and ((HHGear^.Message and gmLJump) <> 0) and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) <> 0) then begin Attack(HHGear); HHGear^.Message:= HHGear^.Message and not gmLJump end;if (CurAmmoGear = nil) or ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_AltAttack) <> 0) or ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_NoRoundEnd) <> 0) then begin if ((HHGear^.Message and gmSlot) <> 0) then if ChangeAmmo(HHGear) then ApplyAmmoChanges(Hedgehog^); if ((HHGear^.Message and gmWeapon) <> 0) then HHSetWeapon(HHGear); if ((HHGear^.Message and gmTimer) <> 0) then HHSetTimer(HHGear); end;if CurAmmoGear <> nil then begin CurAmmoGear^.Message:= HHGear^.Message; exit end;if not isInMultiShoot then HedgehogChAngle(HHGear);if (HHGear^.State and gstMoving) <> 0 then begin wasJumping:= ((HHGear^.State and gstHHJumping) <> 0); if ((HHGear^.Message and gmHJump) <> 0) and wasJumping and ((HHGear^.State and gstHHHJump) = 0) then if (not (hwAbs(HHGear^.dX) > cLittle)) and (HHGear^.dY < -_0_02) then begin HHGear^.State:= HHGear^.State or gstHHHJump; HHGear^.dY:= -_0_25; if not cArtillery then HHGear^.dX:= -SignAs(_0_02, HHGear^.dX); PlaySound(sndJump2, Hedgehog^.Team^.voicepack) end; HHGear^.Message:= HHGear^.Message and not (gmLJump or gmHJump); if (not cArtillery) and wasJumping and TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then SetLittle(HHGear^.dX); if Hedgehog^.Gear <> nil then doStepHedgehogMoving(HHGear); if ((HHGear^.State and (gstMoving or gstDrowning)) = 0) then begin AddGearCI(HHGear); if wasJumping then StepTicks:= 410 else StepTicks:= 95 end; exit end; if not isInMultiShoot and (Hedgehog^.Gear <> nil) then begin if StepTicks > 0 then dec(StepTicks); if (StepTicks = 0) then HedgehogStep(HHGear) endend;////////////////////////////////////////////////////////////////////////////////procedure doStepHedgehogFree(Gear: PGear);var prevState: Longword;beginprevState:= Gear^.State;doStepHedgehogMoving(Gear);if (Gear^.State and (gstMoving or gstDrowning)) <> 0 then begin if Gear^.Damage > 0 then CalcRotationDirAngle(Gear); AllInactive:= false; exit end;if (Gear^.Health = 0) then begin if PrvInactive or ((GameFlags and gfInfAttack) <> 0) then begin Gear^.Timer:= 0; FollowGear:= Gear; PrvInactive:= false; AllInactive:= false; if not Gear^.Hedgehog^.Team^.hasGone then begin Gear^.Hedgehog^.Effects[hePoisoned] := false; if Gear^.Hedgehog^.Effects[heResurrectable] then begin ResurrectHedgehog(Gear); end else begin Gear^.State:= Gear^.State or gstHHDeath; Gear^.doStep:= @doStepHedgehogDead; // Death message AddCaption(Format(GetEventString(eidDied), Gear^.Hedgehog^.Name), cWhiteColor, capgrpMessage); end; end else begin Gear^.State:= Gear^.State or gstHHGone; Gear^.doStep:= @doStepHedgehogGone; // Gone message AddCaption(Format(GetEventString(eidGone), Gear^.Hedgehog^.Name), cWhiteColor, capgrpMessage); end end; exit end;if ((Gear^.State and gstWait) = 0) and (prevState <> Gear^.State) then begin Gear^.State:= gstWait; Gear^.Timer:= 150 end else begin if Gear^.Timer = 0 then begin Gear^.State:= 0; Gear^.Active:= false; AddGearCI(Gear); exit end else dec(Gear^.Timer) end;AllInactive:= falseend;////////////////////////////////////////////////////////////////////////////////procedure doStepHedgehog(Gear: PGear);beginif (Gear^.Message and gmDestroy) <> 0 then begin DeleteGear(Gear); exit end;if (Gear^.State and gstHHDriven) = 0 then doStepHedgehogFree(Gear)else begin with Gear^.Hedgehog^ do if Team^.hasGone then TeamGoneEffect(Team^); doStepHedgehogDriven(Gear) end;end;procedure doStepHedgehogReturn(Gear: PGear);beginif (Gear^.Timer > 0) and ((GameTicks mod 20) = 0) then dec(Gear^.Timer)else if (Gear^.Timer = 0) then begin Gear^.doStep:= @doStepHedgehog; Gear^.State:= Gear^.State and not gstTmpFlag; endend;