diff -r 6155187bf599 -r e510d1245bd7 hedgewars/uGearsHedgehog.pas --- a/hedgewars/uGearsHedgehog.pas Tue Jan 17 09:01:31 2012 -0500 +++ b/hedgewars/uGearsHedgehog.pas Tue Jan 17 09:20:16 2012 -0500 @@ -1,1258 +1,1258 @@ -(* - * Hedgewars, a free turn based strategy game - * Copyright (c) 2004-2011 Andrey Korotaev - * - * 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 - *) - -{$INCLUDE "options.inc"} - -unit uGearsHedgehog; -interface -uses uTypes; - -procedure doStepHedgehog(Gear: PGear); -procedure AfterAttack; -procedure HedgehogStep(Gear: PGear); -procedure doStepHedgehogMoving(Gear: PGear); -procedure HedgehogChAngle(HHGear: PGear); -procedure PickUp(HH, Gear: PGear); - -implementation -uses uConsts, uVariables, uFloat, uAmmos, uSound, uCaptions, uMisc, - uCommands, uLocale, uUtils, uVisualGears, uStats, uIO, uScript, - uGearsList, uGears, uCollisions, uRandom, uStore, uTeams, - uGearsUtils; - -// Shouldn't more of this ammo switching stuff be moved to uAmmos ? -function ChangeAmmo(HHGear: PGear): boolean; -var slot, i: Longword; - ammoidx: LongInt; -begin -ChangeAmmo:= 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 ((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; - end -end; - -procedure HHSetWeapon(HHGear: PGear); -var t: LongInt; - weap: TAmmoType; - Hedgehog: PHedgehog; - s: boolean; -begin -s:= false; - -weap:= TAmmoType(HHGear^.MsgParam); -Hedgehog:= HHGear^.Hedgehog; - -if Hedgehog^.Team^.Clan^.TurnNumber <= Ammoz[weap].SkipTurns then - exit; // weapon is not activated yet - -HHGear^.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; -begin -Gear^.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; -begin -newGear:= 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 (Power > 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: 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, amPiano: CurAmmoGear:= newGear; - end; - - if ((CurAmmoType = amMine) or (CurAmmoType = amSMine)) and (GameFlags and gfInfAttack <> 0) then - newGear^.FlightTime:= GameTicks + 1000 - else if CurAmmoType = amDrill then - newGear^.FlightTime:= GameTicks + 250; - 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 := NoPointX; - ScriptCall('onHogAttack'); -end; - -procedure AfterAttack; -var s: shortstring; - a: TAmmoType; -begin -with 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 - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepHedgehogDead(Gear: PGear); -const frametime = 200; - timertime = frametime * 6; -begin -if 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 -else if 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 - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepHedgehogGone(Gear: PGear); -const frametime = 65; - timertime = frametime * 11; -begin -if 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 -else -if 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 - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure PickUp(HH, Gear: PGear); -var s: shortstring; - a: TAmmoType; - i: LongInt; - vga: PVisualGear; -begin -Gear^.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) -else -case 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(HH^.Hedgehog) - else - a:= GetAmmo(HH^.Hedgehog) - 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; - end -end; - -const StepTicks: LongWord = 0; - -procedure HedgehogStep(Gear: PGear); -var PrevdX: LongInt; - CurWeapon: PAmmo; -begin -CurWeapon:= 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 TestCollisionYwithGear(Gear, -1) = 0 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) <> 0)) 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) <> 0)) then - Gear^.Y:= Gear^.Y - _1; - if not (TestCollisionXwithXYShift(Gear, _0, -5, hwSign(Gear^.dX)) - or (TestCollisionYwithGear(Gear, -1) <> 0)) then - Gear^.Y:= Gear^.Y - _1; - if not (TestCollisionXwithXYShift(Gear, _0, -4, hwSign(Gear^.dX)) - or (TestCollisionYwithGear(Gear, -1) <> 0)) then - Gear^.Y:= Gear^.Y - _1; - if not (TestCollisionXwithXYShift(Gear, _0, -3, hwSign(Gear^.dX)) - or (TestCollisionYwithGear(Gear, -1) <> 0)) then - Gear^.Y:= Gear^.Y - _1; - if not (TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) - or (TestCollisionYwithGear(Gear, -1) <> 0)) then - Gear^.Y:= Gear^.Y - _1; - if not (TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) - or (TestCollisionYwithGear(Gear, -1) <> 0)) 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 TestCollisionYwithGear(Gear, 1) = 0 then - begin - Gear^.Y:= Gear^.Y + _1; - if TestCollisionYwithGear(Gear, 1) = 0 then - begin - Gear^.Y:= Gear^.Y + _1; - if TestCollisionYwithGear(Gear, 1) = 0 then - begin - Gear^.Y:= Gear^.Y + _1; - if TestCollisionYwithGear(Gear, 1) = 0 then - begin - Gear^.Y:= Gear^.Y + _1; - if TestCollisionYwithGear(Gear, 1) = 0 then - begin - Gear^.Y:= Gear^.Y + _1; - if TestCollisionYwithGear(Gear, 1) = 0 then - begin - Gear^.Y:= Gear^.Y + _1; - if TestCollisionYwithGear(Gear, 1) = 0 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) - end -end; - -procedure HedgehogChAngle(HHGear: PGear); -var da: LongWord; -begin -with 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 doStepHedgehogMoving(Gear: PGear); -var isFalling, isUnderwater: boolean; - land: Word; -begin -land:= 0; -isUnderwater:= 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 (CurrentHedgehog^.Gear = Gear) - and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > _0_003) then - begin - FollowGear:= Gear; - end; - 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 - land:= TestCollisionYwithGear(Gear, 1); - if ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_55.QWordValue) and ((land and lfIce) = 0) - 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 - begin - if land and lfIce <> 0 then - begin - Gear^.dX:= Gear^.dX * (_1 - (_1 - Gear^.Friction) / _2) - end - else - Gear^.dX:= Gear^.dX * Gear^.Friction; - end - 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); - while TestCollisionYWithGear(Gear,1) = 0 do - Gear^.Y:= Gear^.Y+_1; - SetLittle(Gear^.dX) - end - else - begin - Gear^.State:= Gear^.State and (not gstMoving); - while TestCollisionYWithGear(Gear,1) = 0 do - Gear^.Y:= Gear^.Y+_1; - 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); - while TestCollisionYWithGear(Gear,1) = 0 do - Gear^.Y:= Gear^.Y+_1; - 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); - // hide target cursor if current hog is drowning - if (Gear^.State and gstDrowning) <> 0 then - if (CurrentHedgehog^.Gear = Gear) then - isCursorVisible:= false - end; - -if (hwAbs(Gear^.dY) > _0) and (Gear^.FlightTime > 0) and ((GameFlags and gfLowGravity) = 0) then - begin - inc(Gear^.FlightTime); - if Gear^.FlightTime = 3000 then - begin - AddCaption(GetEventString(eidHomerun), cWhiteColor, capgrpMessage); - PlaySound(sndHomerun) - end; - end -else - begin - uStats.hedgehogFlight(Gear, Gear^.FlightTime); - Gear^.FlightTime:= 0; - end; - -end; - -procedure doStepHedgehogDriven(HHGear: PGear); -var t: PGear; - wasJumping: boolean; - Hedgehog: PHedgehog; -begin -Hedgehog:= HHGear^.Hedgehog; -if isInMultiShoot then - HHGear^.Message:= 0; - -if ((Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_Utility) <> 0) and isInMultiShoot then - AllInactive:= true -else if not isInMultiShoot then - AllInactive:= false; - -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 - else -else - 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) - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepHedgehogFree(Gear: PGear); -var prevState: Longword; -begin -prevState:= 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 (Gear^.State and gstHHGone) = 0 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) and (not gstAnimation); - Gear^.doStep:= @doStepHedgehogDead; - // Death message - AddCaption(Format(GetEventString(eidDied), Gear^.Hedgehog^.Name), cWhiteColor, capgrpMessage); - end; - end - else - begin - Gear^.State:= Gear^.State and (not gstAnimation); - 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:= Gear^.State or gstWait; - Gear^.Timer:= 150 - end -else - begin - if Gear^.Timer = 0 then - begin - Gear^.State:= Gear^.State and (not (gstWait or gstLoser or gstWinner or gstAttacked or gstNotKickable or gstHHChooseTarget)); - Gear^.Active:= false; - AddGearCI(Gear); - exit - end - else dec(Gear^.Timer) - end; - -AllInactive:= false -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepHedgehog(Gear: PGear); -(* -var x,y,tx,ty: LongInt; - tdX, tdY, slope: hwFloat; - land: Word; *) -var slope: hwFloat; -begin -if (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^) - else - doStepHedgehogDriven(Gear) - end; -if (Gear^.Message and (gmAllStoppable or gmLJump or gmHJump) = 0) -and (Gear^.State and (gstHHJumping or gstHHHJump or gstAttacking) = 0) -and (not Gear^.dY.isNegative) and (GameTicks mod (100*LongWOrd(hwRound(cMaxWindSpeed*2/cGravity))) = 0) -and (TestCollisionYwithGear(Gear, 1) and lfIce <> 0) then - begin - slope:= CalcSlopeBelowGear(Gear); - Gear^.dX:=Gear^.dX+slope*_0_07; - if slope.QWordValue <> 0 then - Gear^.State:= Gear^.State or gstMoving; -(* - x:= hwRound(Gear^.X); - y:= hwRound(Gear^.Y); - AddVisualGear(x, y, vgtSmokeTrace); - AddVisualGear(x - hwRound(_5*slope), y + hwRound(_5*slope), vgtSmokeTrace); - AddVisualGear(x + hwRound(_5*slope), y - hwRound(_5*slope), vgtSmokeTrace); - AddVisualGear(x - hwRound(_20 * slope), y + hwRound(_20 * slope), vgtSmokeTrace); - AddVisualGear(x + hwRound(_20 * slope), y - hwRound(_20 * slope), vgtSmokeTrace); - AddVisualGear(x - hwRound(_30 * slope), y + hwRound(_30 * slope), vgtSmokeTrace); - AddVisualGear(x + hwRound(_30 * slope), y - hwRound(_30 * slope), vgtSmokeTrace); - AddVisualGear(x - hwRound(_40 * slope), y + hwRound(_40 * slope), vgtSmokeTrace); - AddVisualGear(x + hwRound(_40 * slope), y - hwRound(_40 * slope), vgtSmokeTrace); - AddVisualGear(x - hwRound(_50 * slope), y + hwRound(_50 * slope), vgtSmokeTrace); - AddVisualGear(x + hwRound(_50 * slope), y - hwRound(_50 * slope), vgtSmokeTrace); *) - end -end; - -end. +(* + * Hedgewars, a free turn based strategy game + * Copyright (c) 2004-2011 Andrey Korotaev + * + * 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 + *) + +{$INCLUDE "options.inc"} + +unit uGearsHedgehog; +interface +uses uTypes; + +procedure doStepHedgehog(Gear: PGear); +procedure AfterAttack; +procedure HedgehogStep(Gear: PGear); +procedure doStepHedgehogMoving(Gear: PGear); +procedure HedgehogChAngle(HHGear: PGear); +procedure PickUp(HH, Gear: PGear); + +implementation +uses uConsts, uVariables, uFloat, uAmmos, uSound, uCaptions, uMisc, + uCommands, uLocale, uUtils, uVisualGears, uStats, uIO, uScript, + uGearsList, uGears, uCollisions, uRandom, uStore, uTeams, + uGearsUtils; + +// Shouldn't more of this ammo switching stuff be moved to uAmmos ? +function ChangeAmmo(HHGear: PGear): boolean; +var slot, i: Longword; + ammoidx: LongInt; +begin +ChangeAmmo:= 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 ((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; + end +end; + +procedure HHSetWeapon(HHGear: PGear); +var t: LongInt; + weap: TAmmoType; + Hedgehog: PHedgehog; + s: boolean; +begin +s:= false; + +weap:= TAmmoType(HHGear^.MsgParam); +Hedgehog:= HHGear^.Hedgehog; + +if Hedgehog^.Team^.Clan^.TurnNumber <= Ammoz[weap].SkipTurns then + exit; // weapon is not activated yet + +HHGear^.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; +begin +Gear^.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; +begin +newGear:= 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 (Power > 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: 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, amPiano: CurAmmoGear:= newGear; + end; + + if ((CurAmmoType = amMine) or (CurAmmoType = amSMine)) and (GameFlags and gfInfAttack <> 0) then + newGear^.FlightTime:= GameTicks + 1000 + else if CurAmmoType = amDrill then + newGear^.FlightTime:= GameTicks + 250; + 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 := NoPointX; + ScriptCall('onHogAttack'); +end; + +procedure AfterAttack; +var s: shortstring; + a: TAmmoType; +begin +with 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 + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepHedgehogDead(Gear: PGear); +const frametime = 200; + timertime = frametime * 6; +begin +if 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 +else if 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 + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepHedgehogGone(Gear: PGear); +const frametime = 65; + timertime = frametime * 11; +begin +if 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 +else +if 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 + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure PickUp(HH, Gear: PGear); +var s: shortstring; + a: TAmmoType; + i: LongInt; + vga: PVisualGear; +begin +Gear^.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) +else +case 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(HH^.Hedgehog) + else + a:= GetAmmo(HH^.Hedgehog) + 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; + end +end; + +const StepTicks: LongWord = 0; + +procedure HedgehogStep(Gear: PGear); +var PrevdX: LongInt; + CurWeapon: PAmmo; +begin +CurWeapon:= 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 TestCollisionYwithGear(Gear, -1) = 0 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) <> 0)) 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) <> 0)) then + Gear^.Y:= Gear^.Y - _1; + if not (TestCollisionXwithXYShift(Gear, _0, -5, hwSign(Gear^.dX)) + or (TestCollisionYwithGear(Gear, -1) <> 0)) then + Gear^.Y:= Gear^.Y - _1; + if not (TestCollisionXwithXYShift(Gear, _0, -4, hwSign(Gear^.dX)) + or (TestCollisionYwithGear(Gear, -1) <> 0)) then + Gear^.Y:= Gear^.Y - _1; + if not (TestCollisionXwithXYShift(Gear, _0, -3, hwSign(Gear^.dX)) + or (TestCollisionYwithGear(Gear, -1) <> 0)) then + Gear^.Y:= Gear^.Y - _1; + if not (TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) + or (TestCollisionYwithGear(Gear, -1) <> 0)) then + Gear^.Y:= Gear^.Y - _1; + if not (TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) + or (TestCollisionYwithGear(Gear, -1) <> 0)) 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 TestCollisionYwithGear(Gear, 1) = 0 then + begin + Gear^.Y:= Gear^.Y + _1; + if TestCollisionYwithGear(Gear, 1) = 0 then + begin + Gear^.Y:= Gear^.Y + _1; + if TestCollisionYwithGear(Gear, 1) = 0 then + begin + Gear^.Y:= Gear^.Y + _1; + if TestCollisionYwithGear(Gear, 1) = 0 then + begin + Gear^.Y:= Gear^.Y + _1; + if TestCollisionYwithGear(Gear, 1) = 0 then + begin + Gear^.Y:= Gear^.Y + _1; + if TestCollisionYwithGear(Gear, 1) = 0 then + begin + Gear^.Y:= Gear^.Y + _1; + if TestCollisionYwithGear(Gear, 1) = 0 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) + end +end; + +procedure HedgehogChAngle(HHGear: PGear); +var da: LongWord; +begin +with 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 doStepHedgehogMoving(Gear: PGear); +var isFalling, isUnderwater: boolean; + land: Word; +begin +land:= 0; +isUnderwater:= 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 (CurrentHedgehog^.Gear = Gear) + and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > _0_003) then + begin + FollowGear:= Gear; + end; + 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 + land:= TestCollisionYwithGear(Gear, 1); + if ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_55.QWordValue) and ((land and lfIce) = 0) + 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 + begin + if land and lfIce <> 0 then + begin + Gear^.dX:= Gear^.dX * (_1 - (_1 - Gear^.Friction) / _2) + end + else + Gear^.dX:= Gear^.dX * Gear^.Friction; + end + 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); + while TestCollisionYWithGear(Gear,1) = 0 do + Gear^.Y:= Gear^.Y+_1; + SetLittle(Gear^.dX) + end + else + begin + Gear^.State:= Gear^.State and (not gstMoving); + while TestCollisionYWithGear(Gear,1) = 0 do + Gear^.Y:= Gear^.Y+_1; + 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); + while TestCollisionYWithGear(Gear,1) = 0 do + Gear^.Y:= Gear^.Y+_1; + 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); + // hide target cursor if current hog is drowning + if (Gear^.State and gstDrowning) <> 0 then + if (CurrentHedgehog^.Gear = Gear) then + isCursorVisible:= false + end; + +if (hwAbs(Gear^.dY) > _0) and (Gear^.FlightTime > 0) and ((GameFlags and gfLowGravity) = 0) then + begin + inc(Gear^.FlightTime); + if Gear^.FlightTime = 3000 then + begin + AddCaption(GetEventString(eidHomerun), cWhiteColor, capgrpMessage); + PlaySound(sndHomerun) + end; + end +else + begin + uStats.hedgehogFlight(Gear, Gear^.FlightTime); + Gear^.FlightTime:= 0; + end; + +end; + +procedure doStepHedgehogDriven(HHGear: PGear); +var t: PGear; + wasJumping: boolean; + Hedgehog: PHedgehog; +begin +Hedgehog:= HHGear^.Hedgehog; +if isInMultiShoot then + HHGear^.Message:= 0; + +if ((Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_Utility) <> 0) and isInMultiShoot then + AllInactive:= true +else if not isInMultiShoot then + AllInactive:= false; + +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 + else +else + 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) + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepHedgehogFree(Gear: PGear); +var prevState: Longword; +begin +prevState:= 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 (Gear^.State and gstHHGone) = 0 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) and (not gstAnimation); + Gear^.doStep:= @doStepHedgehogDead; + // Death message + AddCaption(Format(GetEventString(eidDied), Gear^.Hedgehog^.Name), cWhiteColor, capgrpMessage); + end; + end + else + begin + Gear^.State:= Gear^.State and (not gstAnimation); + 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:= Gear^.State or gstWait; + Gear^.Timer:= 150 + end +else + begin + if Gear^.Timer = 0 then + begin + Gear^.State:= Gear^.State and (not (gstWait or gstLoser or gstWinner or gstAttacked or gstNotKickable or gstHHChooseTarget)); + Gear^.Active:= false; + AddGearCI(Gear); + exit + end + else dec(Gear^.Timer) + end; + +AllInactive:= false +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepHedgehog(Gear: PGear); +(* +var x,y,tx,ty: LongInt; + tdX, tdY, slope: hwFloat; + land: Word; *) +var slope: hwFloat; +begin +if (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^) + else + doStepHedgehogDriven(Gear) + end; +if (Gear^.Message and (gmAllStoppable or gmLJump or gmHJump) = 0) +and (Gear^.State and (gstHHJumping or gstHHHJump or gstAttacking) = 0) +and (not Gear^.dY.isNegative) and (GameTicks mod (100*LongWOrd(hwRound(cMaxWindSpeed*2/cGravity))) = 0) +and (TestCollisionYwithGear(Gear, 1) and lfIce <> 0) then + begin + slope:= CalcSlopeBelowGear(Gear); + Gear^.dX:=Gear^.dX+slope*_0_07; + if slope.QWordValue <> 0 then + Gear^.State:= Gear^.State or gstMoving; +(* + x:= hwRound(Gear^.X); + y:= hwRound(Gear^.Y); + AddVisualGear(x, y, vgtSmokeTrace); + AddVisualGear(x - hwRound(_5*slope), y + hwRound(_5*slope), vgtSmokeTrace); + AddVisualGear(x + hwRound(_5*slope), y - hwRound(_5*slope), vgtSmokeTrace); + AddVisualGear(x - hwRound(_20 * slope), y + hwRound(_20 * slope), vgtSmokeTrace); + AddVisualGear(x + hwRound(_20 * slope), y - hwRound(_20 * slope), vgtSmokeTrace); + AddVisualGear(x - hwRound(_30 * slope), y + hwRound(_30 * slope), vgtSmokeTrace); + AddVisualGear(x + hwRound(_30 * slope), y - hwRound(_30 * slope), vgtSmokeTrace); + AddVisualGear(x - hwRound(_40 * slope), y + hwRound(_40 * slope), vgtSmokeTrace); + AddVisualGear(x + hwRound(_40 * slope), y - hwRound(_40 * slope), vgtSmokeTrace); + AddVisualGear(x - hwRound(_50 * slope), y + hwRound(_50 * slope), vgtSmokeTrace); + AddVisualGear(x + hwRound(_50 * slope), y - hwRound(_50 * slope), vgtSmokeTrace); *) + end +end; + +end.