--- 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 <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
- *)
-
-{$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 <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
+ *)
+
+{$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.
--- a/hedgewars/uGearsUtils.pas Tue Jan 17 09:01:31 2012 -0500
+++ b/hedgewars/uGearsUtils.pas Tue Jan 17 09:20:16 2012 -0500
@@ -1,573 +1,573 @@
-(*
- * 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
- *)
-
-{$INCLUDE "options.inc"}
-
-unit uGearsUtils;
-interface
-uses uTypes;
-
-procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord = $FFFFFFFF);
-function ModifyDamage(dmg: Longword; Gear: PGear): Longword;
-procedure ApplyDamage(Gear: PGear; AttackerHog: PHedgehog; Damage: Longword; Source: TDamageSource);
-procedure spawnHealthTagForHH(HHGear: PGear; dmg: Longword);
-procedure HHHurt(Hedgehog: PHedgehog; Source: TDamageSource);
-procedure CheckHHDamage(Gear: PGear);
-procedure CalcRotationDirAngle(Gear: PGear);
-procedure ResurrectHedgehog(gear: PGear);
-procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity: boolean = false);
-function CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear;
-function CheckGearsNear(mX, mY: LongInt; Kind: TGearsType; rX, rY: LongInt): PGear;
-function CheckGearDrowning(Gear: PGear): boolean;
-
-var doStepHandlers: array[TGearType] of TGearStepProcedure;
-
-
-implementation
-uses uFloat, uSound, uCollisions, uUtils, uConsts, uVisualGears, uAIMisc,
- uVariables, uLandGraphics, uScript, uStats, uCaptions, uTeams, uStore,
- uLocale, uTextures, uRenderUtils, uRandom, SDLh, uDebug, uGears,
- uGearsList;
-
-procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord);
-var Gear: PGear;
- dmg, dmgRadius, dmgBase: LongInt;
- fX, fY: hwFloat;
- vg: PVisualGear;
- i, cnt: LongInt;
-begin
-if Radius > 4 then AddFileLog('Explosion: at (' + inttostr(x) + ',' + inttostr(y) + ')');
-if Radius > 25 then KickFlakes(Radius, X, Y);
-
-if ((Mask and EXPLNoGfx) = 0) then
- begin
- vg:= nil;
- if Radius > 50 then vg:= AddVisualGear(X, Y, vgtBigExplosion)
- else if Radius > 10 then vg:= AddVisualGear(X, Y, vgtExplosion);
- if vg <> nil then
- vg^.Tint:= Tint;
- end;
-if (Mask and EXPLAutoSound) <> 0 then PlaySound(sndExplosion);
-
-if (Mask and EXPLAllDamageInRadius) = 0 then
- dmgRadius:= Radius shl 1
-else
- dmgRadius:= Radius;
-dmgBase:= dmgRadius + cHHRadius div 2;
-fX:= int2hwFloat(X);
-fY:= int2hwFloat(Y);
-Gear:= GearsList;
-while Gear <> nil do
- begin
- dmg:= 0;
- //dmg:= dmgRadius + cHHRadius div 2 - hwRound(Distance(Gear^.X - int2hwFloat(X), Gear^.Y - int2hwFloat(Y)));
- //if (dmg > 1) and
- if (Gear^.State and gstNoDamage) = 0 then
- begin
- case Gear^.Kind of
- gtHedgehog,
- gtMine,
- gtBall,
- gtMelonPiece,
- gtGrenade,
- gtClusterBomb,
- // gtCluster, too game breaking I think
- gtSMine,
- gtCase,
- gtTarget,
- gtFlame,
- gtExplosives,
- gtStructure: begin
-// Run the calcs only once we know we have a type that will need damage
- if hwRound(hwAbs(Gear^.X-fX)+hwAbs(Gear^.Y-fY)) < dmgBase then
- dmg:= dmgBase - max(hwRound(Distance(Gear^.X - fX, Gear^.Y - fY)),Gear^.Radius);
- if dmg > 1 then
- begin
- dmg:= ModifyDamage(min(dmg div 2, Radius), Gear);
- //AddFileLog('Damage: ' + inttostr(dmg));
- if (Mask and EXPLNoDamage) = 0 then
- begin
- if not Gear^.Invulnerable then
- ApplyDamage(Gear, AttackingHog, dmg, dsExplosion)
- else
- Gear^.State:= Gear^.State or gstWinner;
- end;
- if ((Mask and EXPLDoNotTouchAny) = 0) and (((Mask and EXPLDoNotTouchHH) = 0) or (Gear^.Kind <> gtHedgehog)) then
- begin
- DeleteCI(Gear);
- Gear^.dX:= Gear^.dX + SignAs(_0_005 * dmg + cHHKick, Gear^.X - fX)/(Gear^.Density/_3);
- Gear^.dY:= Gear^.dY + SignAs(_0_005 * dmg + cHHKick, Gear^.Y - fY)/(Gear^.Density/_3);
-
- Gear^.State:= (Gear^.State or gstMoving) and (not gstLoser);
- if not Gear^.Invulnerable then
- Gear^.State:= (Gear^.State or gstMoving) and (not gstWinner);
- Gear^.Active:= true;
- if Gear^.Kind <> gtFlame then FollowGear:= Gear
- end;
- if ((Mask and EXPLPoisoned) <> 0) and (Gear^.Kind = gtHedgehog) and (not Gear^.Invulnerable) then
- Gear^.Hedgehog^.Effects[hePoisoned] := true;
- end;
-
- end;
- gtGrave: begin
-// Run the calcs only once we know we have a type that will need damage
- if hwRound(hwAbs(Gear^.X-fX)+hwAbs(Gear^.Y-fY)) < dmgBase then
- dmg:= dmgBase - hwRound(Distance(Gear^.X - fX, Gear^.Y - fY));
- if dmg > 1 then
- begin
- dmg:= ModifyDamage(min(dmg div 2, Radius), Gear);
- Gear^.dY:= - _0_004 * dmg;
- Gear^.Active:= true
- end
- end;
- end;
- end;
- Gear:= Gear^.NextGear
- end;
-
-if (Mask and EXPLDontDraw) = 0 then
- if (GameFlags and gfSolidLand) = 0 then
- begin
- cnt:= DrawExplosion(X, Y, Radius) div 1608; // approx 2 16x16 circles to erase per chunk
- if (cnt > 0) and (SpritesData[sprChunk].Texture <> nil) then
- for i:= 0 to cnt do
- AddVisualGear(X, Y, vgtChunk)
- end;
-
-uAIMisc.AwareOfExplosion(0, 0, 0)
-end;
-
-function ModifyDamage(dmg: Longword; Gear: PGear): Longword;
-var i: hwFloat;
-begin
-(* Invulnerability cannot be placed in here due to still needing kicks
- Not without a new damage machine.
- King check should be in here instead of ApplyDamage since Tiy wants them kicked less
-*)
-i:= _1;
-if (CurrentHedgehog <> nil) and CurrentHedgehog^.King then
- i:= _1_5;
-if (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.King) then
- ModifyDamage:= hwRound(_0_01 * cDamageModifier * dmg * i * cDamagePercent * _0_5)
-else
- ModifyDamage:= hwRound(_0_01 * cDamageModifier * dmg * i * cDamagePercent)
-end;
-
-procedure ApplyDamage(Gear: PGear; AttackerHog: PHedgehog; Damage: Longword; Source: TDamageSource);
-var s: shortstring;
- vampDmg, tmpDmg, i: Longword;
- vg: PVisualGear;
-begin
- if Damage = 0 then
- exit; // nothing to apply
-
- if (Gear^.Kind = gtHedgehog) then
- begin
- Gear^.LastDamage := AttackerHog;
-
- Gear^.Hedgehog^.Team^.Clan^.Flawless:= false;
- HHHurt(Gear^.Hedgehog, Source);
- AddDamageTag(hwRound(Gear^.X), hwRound(Gear^.Y), Damage, Gear^.Hedgehog^.Team^.Clan^.Color);
- tmpDmg:= min(Damage, max(0,Gear^.Health-Gear^.Damage));
- if (Gear <> CurrentHedgehog^.Gear) and (CurrentHedgehog^.Gear <> nil) and (tmpDmg >= 1) then
- begin
- if cVampiric then
- begin
- vampDmg:= hwRound(int2hwFloat(tmpDmg)*_0_8);
- if vampDmg >= 1 then
- begin
- // was considering pulsing on attack, Tiy thinks it should be permanent while in play
- //CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State or gstVampiric;
- inc(CurrentHedgehog^.Gear^.Health,vampDmg);
- str(vampDmg, s);
- s:= '+' + s;
- AddCaption(s, CurrentHedgehog^.Team^.Clan^.Color, capgrpAmmoinfo);
- RenderHealth(CurrentHedgehog^);
- RecountTeamHealth(CurrentHedgehog^.Team);
- i:= 0;
- while i < vampDmg do
- begin
- vg:= AddVisualGear(hwRound(CurrentHedgehog^.Gear^.X), hwRound(CurrentHedgehog^.Gear^.Y), vgtStraightShot);
- if vg <> nil then
- with vg^ do
- begin
- Tint:= $FF0000FF;
- State:= ord(sprHealth)
- end;
- inc(i, 5);
- end;
- end
- end;
- if ((GameFlags and gfKarma) <> 0) and
- ((GameFlags and gfInvulnerable) = 0)
- and (not CurrentHedgehog^.Gear^.Invulnerable) then
- begin // this cannot just use Damage or it interrupts shotgun and gets you called stupid
- inc(CurrentHedgehog^.Gear^.Karma, tmpDmg);
- CurrentHedgehog^.Gear^.LastDamage := CurrentHedgehog;
- spawnHealthTagForHH(CurrentHedgehog^.Gear, tmpDmg);
- end;
- uStats.HedgehogDamaged(Gear, AttackerHog, Damage, false);
- end;
- end
- else if Gear^.Kind <> gtStructure then // not gtHedgehog nor gtStructure
- begin
- Gear^.Hedgehog:= AttackerHog;
- end;
- inc(Gear^.Damage, Damage);
-
- ScriptCall('onGearDamage', Gear^.UID, Damage);
-end;
-
-procedure spawnHealthTagForHH(HHGear: PGear; dmg: Longword);
-var tag: PVisualGear;
-begin
-tag:= AddVisualGear(hwRound(HHGear^.X), hwRound(HHGear^.Y), vgtHealthTag, dmg);
-if (tag <> nil) then
- tag^.Hedgehog:= HHGear^.Hedgehog; // the tag needs the tag to determine the text color
-AllInactive:= false;
-HHGear^.Active:= true;
-end;
-
-procedure HHHurt(Hedgehog: PHedgehog; Source: TDamageSource);
-begin
-if (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);
- end
-else if (Source = dsPoison) then
- case random(2) of
- 0: PlaySound(sndPoisonCough, Hedgehog^.Team^.voicepack);
- 1: PlaySound(sndPoisonMoan, Hedgehog^.Team^.voicepack);
- end
-else
- 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);
- end
-end;
-
-procedure CheckHHDamage(Gear: PGear);
-var
- dmg: Longword;
- i: LongInt;
- particle: PVisualGear;
-begin
- if _0_4 < Gear^.dY then
- begin
- dmg := ModifyDamage(1 + hwRound((hwAbs(Gear^.dY) - _0_4) * 70), Gear);
- PlaySound(sndBump);
- if dmg < 1 then
- exit;
-
- for i:= min(12, (3 + dmg div 10)) downto 0 do
- begin
- particle := AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust);
- if particle <> nil then
- particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480);
- end;
-
- if (Gear^.Invulnerable) then
- exit;
-
- //if _0_6 < Gear^.dY then
- // PlaySound(sndOw4, Gear^.Hedgehog^.Team^.voicepack)
- //else
- // PlaySound(sndOw1, Gear^.Hedgehog^.Team^.voicepack);
-
- if Gear^.LastDamage <> nil then
- ApplyDamage(Gear, Gear^.LastDamage, dmg, dsFall)
- else
- ApplyDamage(Gear, CurrentHedgehog, dmg, dsFall);
- end
-end;
-
-
-procedure CalcRotationDirAngle(Gear: PGear);
-var
- dAngle: real;
-begin
- dAngle := (Gear^.dX.QWordValue + Gear^.dY.QWordValue) / $80000000;
- if not Gear^.dX.isNegative then
- Gear^.DirAngle := Gear^.DirAngle + dAngle
- else
- Gear^.DirAngle := Gear^.DirAngle - dAngle;
-
- if Gear^.DirAngle < 0 then
- Gear^.DirAngle := Gear^.DirAngle + 360
- else if 360 < Gear^.DirAngle then
- Gear^.DirAngle := Gear^.DirAngle - 360
-end;
-
-function CheckGearDrowning(Gear: PGear): boolean;
-var
- skipSpeed, skipAngle, skipDecay: hwFloat;
- i, maxDrops, X, Y: LongInt;
- vdX, vdY: real;
- particle: PVisualGear;
- isSubmersible: boolean;
-begin
- // probably needs tweaking. might need to be in a case statement based upon gear type
- Y:= hwRound(Gear^.Y);
- if cWaterLine < Y + Gear^.Radius then
- begin
- isSubmersible:= (Gear = CurrentHedgehog^.Gear) and (CurAmmoGear <> nil) and (CurAmmoGear^.AmmoType = amJetpack);
- skipSpeed := _0_25;
- skipAngle := _1_9;
- skipDecay := _0_87;
- X:= hwRound(Gear^.X);
- vdX:= hwFloat2Float(Gear^.dX);
- vdY:= hwFloat2Float(Gear^.dY);
- // this could perhaps be a tiny bit higher.
- if (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > skipSpeed)
- and (hwAbs(Gear^.dX) > skipAngle * hwAbs(Gear^.dY)) then
- begin
- Gear^.dY.isNegative := true;
- Gear^.dY := Gear^.dY * skipDecay;
- Gear^.dX := Gear^.dX * skipDecay;
- CheckGearDrowning := false;
- PlaySound(sndSkip)
- end
- else
- begin
- if not isSubmersible then
- begin
- CheckGearDrowning := true;
- Gear^.State := gstDrowning;
- Gear^.RenderTimer := false;
- if (Gear^.Kind <> gtSniperRifleShot) and (Gear^.Kind <> gtShotgunShot)
- and (Gear^.Kind <> gtDEagleShot) and (Gear^.Kind <> gtSineGunShot) then
- if Gear^.Kind = gtHedgehog then
- begin
- if Gear^.Hedgehog^.Effects[heResurrectable] then
- ResurrectHedgehog(Gear)
- else
- begin
- Gear^.doStep := @doStepDrowningGear;
- Gear^.State := Gear^.State and (not gstHHDriven);
- AddCaption(Format(GetEventString(eidDrowned), Gear^.Hedgehog^.Name), cWhiteColor, capgrpMessage);
- end
- end
- else
- Gear^.doStep := @doStepDrowningGear;
- if Gear^.Kind = gtFlake then
- exit // skip splashes
- end;
- if ((not isSubmersible) and (Y < cWaterLine + 64 + Gear^.Radius))
- or (isSubmersible and (Y < cWaterLine + 2 + Gear^.Radius) and ((CurAmmoGear^.Pos = 0)
- and (CurAmmoGear^.dY < _0_01))) then
- // don't play splash if they are already way past the surface
- PlaySound(sndSplash)
- end;
-
- if ((cReducedQuality and rqPlainSplash) = 0)
- and (((not isSubmersible) and (Y < cWaterLine + 64 + Gear^.Radius))
- or (isSubmersible and (Y < cWaterLine + 2 + Gear^.Radius) and ((CurAmmoGear^.Pos = 0)
- and (CurAmmoGear^.dY < _0_01)))) then
- begin
- AddVisualGear(X, cWaterLine, vgtSplash);
-
- maxDrops := (Gear^.Radius div 2) + round(vdX * Gear^.Radius * 2) + round(vdY * Gear^.Radius * 2);
- for i:= max(maxDrops div 3, min(32, Random(maxDrops))) downto 0 do
- begin
- particle := AddVisualGear(X - 3 + Random(6), cWaterLine, vgtDroplet);
- if particle <> nil then
- begin
- particle^.dX := particle^.dX - vdX / 10;
- particle^.dY := particle^.dY - vdY / 5;
- end
- end
- end;
- if isSubmersible and (CurAmmoGear^.Pos = 0) then
- CurAmmoGear^.Pos := 1000
- end
- else
- CheckGearDrowning := false;
-end;
-
-
-procedure ResurrectHedgehog(gear: PGear);
-var tempTeam : PTeam;
-begin
- AttackBar:= 0;
- gear^.dX := _0;
- gear^.dY := _0;
- gear^.Damage := 0;
- gear^.Health := gear^.Hedgehog^.InitialHealth;
- gear^.Hedgehog^.Effects[hePoisoned] := false;
- if not CurrentHedgehog^.Effects[heResurrectable] then
- with CurrentHedgehog^ do
- begin
- inc(Team^.stats.AIKills);
- FreeTexture(Team^.AIKillsTex);
- Team^.AIKillsTex := RenderStringTex(inttostr(Team^.stats.AIKills), Team^.Clan^.Color, fnt16);
- end;
- tempTeam := gear^.Hedgehog^.Team;
- DeleteCI(gear);
- FindPlace(gear, false, 0, LAND_WIDTH, true);
- if gear <> nil then
- begin
- RenderHealth(gear^.Hedgehog^);
- ScriptCall('onGearResurrect', gear^.uid);
- gear^.State := gstWait;
- end;
- RecountTeamHealth(tempTeam);
-end;
-
-function CountNonZeroz(x, y, r, c: LongInt): LongInt;
-var i: LongInt;
- count: LongInt = 0;
-begin
-if (y and LAND_HEIGHT_MASK) = 0 then
- for i:= max(x - r, 0) to min(x + r, LAND_WIDTH - 4) do
- if Land[y, i] <> 0 then
- begin
- inc(count);
- if count = c then
- exit(count)
- end;
-CountNonZeroz:= count;
-end;
-
-procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity: boolean);
-var x: LongInt;
- y, sy: LongInt;
- ar: array[0..511] of TPoint;
- ar2: array[0..1023] of TPoint;
- cnt, cnt2: Longword;
- delta: LongInt;
- reallySkip, tryAgain: boolean;
-begin
-reallySkip:= false; // try not skipping proximity at first
-tryAgain:= true;
-while tryAgain do
- begin
- delta:= 250;
- cnt2:= 0;
- repeat
- x:= Left + LongInt(GetRandom(Delta));
- repeat
- inc(x, Delta);
- cnt:= 0;
- y:= min(1024, topY) - 2 * Gear^.Radius;
- while y < cWaterLine do
- begin
- repeat
- inc(y, 2);
- until (y >= cWaterLine) or (CountNonZeroz(x, y, Gear^.Radius - 1, 1) = 0);
-
- sy:= y;
-
- repeat
- inc(y);
- until (y >= cWaterLine) or (CountNonZeroz(x, y, Gear^.Radius - 1, 1) <> 0);
-
- if (y - sy > Gear^.Radius * 2)
- and (((Gear^.Kind = gtExplosives)
- and (y < cWaterLine)
- and (reallySkip or (CheckGearsNear(x, y - Gear^.Radius, [gtFlame, gtHedgehog, gtMine, gtCase, gtExplosives], 60, 60) = nil))
- and (CountNonZeroz(x, y+1, Gear^.Radius - 1, Gear^.Radius+1) > Gear^.Radius))
- or
- ((Gear^.Kind <> gtExplosives)
- and (y < cWaterLine)
- and (reallySkip or (CheckGearsNear(x, y - Gear^.Radius, [gtFlame, gtHedgehog, gtMine, gtCase, gtExplosives], 110, 110) = nil)))) then
-
- begin
- ar[cnt].X:= x;
- if withFall then
- ar[cnt].Y:= sy + Gear^.Radius
- else
- ar[cnt].Y:= y - Gear^.Radius;
- inc(cnt)
- end;
-
- inc(y, 45)
- end;
-
- if cnt > 0 then
- with ar[GetRandom(cnt)] do
- begin
- ar2[cnt2].x:= x;
- ar2[cnt2].y:= y;
- inc(cnt2)
- end
- until (x + Delta > Right);
-
- dec(Delta, 60)
- until (cnt2 > 0) or (Delta < 70);
- if (cnt2 = 0) and skipProximity and (not reallySkip) then
- tryAgain:= true
- else tryAgain:= false;
- reallySkip:= true;
- end;
-
-if cnt2 > 0 then
- with ar2[GetRandom(cnt2)] do
- begin
- Gear^.X:= int2hwFloat(x);
- Gear^.Y:= int2hwFloat(y);
- AddFileLog('Assigned Gear coordinates (' + inttostr(x) + ',' + inttostr(y) + ')');
- end
- else
- begin
- OutError('Can''t find place for Gear', false);
- if Gear^.Kind = gtHedgehog then
- Gear^.Hedgehog^.Effects[heResurrectable] := false;
- DeleteGear(Gear);
- Gear:= nil
- end
-end;
-
-function CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear;
-var t: PGear;
-begin
-t:= GearsList;
-rX:= sqr(rX);
-rY:= sqr(rY);
-
-while t <> nil do
- begin
- if (t <> Gear) and (t^.Kind = Kind) then
- if not((hwSqr(Gear^.X - t^.X) / rX + hwSqr(Gear^.Y - t^.Y) / rY) > _1) then
- exit(t);
- t:= t^.NextGear
- end;
-
-CheckGearNear:= nil
-end;
-
-
-function CheckGearsNear(mX, mY: LongInt; Kind: TGearsType; rX, rY: LongInt): PGear;
-var t: PGear;
-begin
-t:= GearsList;
-rX:= sqr(rX);
-rY:= sqr(rY);
-while t <> nil do
- begin
- if t^.Kind in Kind then
- if not (hwSqr(int2hwFloat(mX) - t^.X) / rX + hwSqr(int2hwFloat(mY) - t^.Y) / rY > _1) then
- exit(t);
- t:= t^.NextGear
- end;
-CheckGearsNear:= nil
-end;
-end.
+(*
+ * 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
+ *)
+
+{$INCLUDE "options.inc"}
+
+unit uGearsUtils;
+interface
+uses uTypes;
+
+procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord = $FFFFFFFF);
+function ModifyDamage(dmg: Longword; Gear: PGear): Longword;
+procedure ApplyDamage(Gear: PGear; AttackerHog: PHedgehog; Damage: Longword; Source: TDamageSource);
+procedure spawnHealthTagForHH(HHGear: PGear; dmg: Longword);
+procedure HHHurt(Hedgehog: PHedgehog; Source: TDamageSource);
+procedure CheckHHDamage(Gear: PGear);
+procedure CalcRotationDirAngle(Gear: PGear);
+procedure ResurrectHedgehog(gear: PGear);
+procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity: boolean = false);
+function CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear;
+function CheckGearsNear(mX, mY: LongInt; Kind: TGearsType; rX, rY: LongInt): PGear;
+function CheckGearDrowning(Gear: PGear): boolean;
+
+var doStepHandlers: array[TGearType] of TGearStepProcedure;
+
+
+implementation
+uses uFloat, uSound, uCollisions, uUtils, uConsts, uVisualGears, uAIMisc,
+ uVariables, uLandGraphics, uScript, uStats, uCaptions, uTeams, uStore,
+ uLocale, uTextures, uRenderUtils, uRandom, SDLh, uDebug, uGears,
+ uGearsList;
+
+procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord);
+var Gear: PGear;
+ dmg, dmgRadius, dmgBase: LongInt;
+ fX, fY: hwFloat;
+ vg: PVisualGear;
+ i, cnt: LongInt;
+begin
+if Radius > 4 then AddFileLog('Explosion: at (' + inttostr(x) + ',' + inttostr(y) + ')');
+if Radius > 25 then KickFlakes(Radius, X, Y);
+
+if ((Mask and EXPLNoGfx) = 0) then
+ begin
+ vg:= nil;
+ if Radius > 50 then vg:= AddVisualGear(X, Y, vgtBigExplosion)
+ else if Radius > 10 then vg:= AddVisualGear(X, Y, vgtExplosion);
+ if vg <> nil then
+ vg^.Tint:= Tint;
+ end;
+if (Mask and EXPLAutoSound) <> 0 then PlaySound(sndExplosion);
+
+if (Mask and EXPLAllDamageInRadius) = 0 then
+ dmgRadius:= Radius shl 1
+else
+ dmgRadius:= Radius;
+dmgBase:= dmgRadius + cHHRadius div 2;
+fX:= int2hwFloat(X);
+fY:= int2hwFloat(Y);
+Gear:= GearsList;
+while Gear <> nil do
+ begin
+ dmg:= 0;
+ //dmg:= dmgRadius + cHHRadius div 2 - hwRound(Distance(Gear^.X - int2hwFloat(X), Gear^.Y - int2hwFloat(Y)));
+ //if (dmg > 1) and
+ if (Gear^.State and gstNoDamage) = 0 then
+ begin
+ case Gear^.Kind of
+ gtHedgehog,
+ gtMine,
+ gtBall,
+ gtMelonPiece,
+ gtGrenade,
+ gtClusterBomb,
+ // gtCluster, too game breaking I think
+ gtSMine,
+ gtCase,
+ gtTarget,
+ gtFlame,
+ gtExplosives,
+ gtStructure: begin
+// Run the calcs only once we know we have a type that will need damage
+ if hwRound(hwAbs(Gear^.X-fX)+hwAbs(Gear^.Y-fY)) < dmgBase then
+ dmg:= dmgBase - max(hwRound(Distance(Gear^.X - fX, Gear^.Y - fY)),Gear^.Radius);
+ if dmg > 1 then
+ begin
+ dmg:= ModifyDamage(min(dmg div 2, Radius), Gear);
+ //AddFileLog('Damage: ' + inttostr(dmg));
+ if (Mask and EXPLNoDamage) = 0 then
+ begin
+ if not Gear^.Invulnerable then
+ ApplyDamage(Gear, AttackingHog, dmg, dsExplosion)
+ else
+ Gear^.State:= Gear^.State or gstWinner;
+ end;
+ if ((Mask and EXPLDoNotTouchAny) = 0) and (((Mask and EXPLDoNotTouchHH) = 0) or (Gear^.Kind <> gtHedgehog)) then
+ begin
+ DeleteCI(Gear);
+ Gear^.dX:= Gear^.dX + SignAs(_0_005 * dmg + cHHKick, Gear^.X - fX)/(Gear^.Density/_3);
+ Gear^.dY:= Gear^.dY + SignAs(_0_005 * dmg + cHHKick, Gear^.Y - fY)/(Gear^.Density/_3);
+
+ Gear^.State:= (Gear^.State or gstMoving) and (not gstLoser);
+ if not Gear^.Invulnerable then
+ Gear^.State:= (Gear^.State or gstMoving) and (not gstWinner);
+ Gear^.Active:= true;
+ if Gear^.Kind <> gtFlame then FollowGear:= Gear
+ end;
+ if ((Mask and EXPLPoisoned) <> 0) and (Gear^.Kind = gtHedgehog) and (not Gear^.Invulnerable) then
+ Gear^.Hedgehog^.Effects[hePoisoned] := true;
+ end;
+
+ end;
+ gtGrave: begin
+// Run the calcs only once we know we have a type that will need damage
+ if hwRound(hwAbs(Gear^.X-fX)+hwAbs(Gear^.Y-fY)) < dmgBase then
+ dmg:= dmgBase - hwRound(Distance(Gear^.X - fX, Gear^.Y - fY));
+ if dmg > 1 then
+ begin
+ dmg:= ModifyDamage(min(dmg div 2, Radius), Gear);
+ Gear^.dY:= - _0_004 * dmg;
+ Gear^.Active:= true
+ end
+ end;
+ end;
+ end;
+ Gear:= Gear^.NextGear
+ end;
+
+if (Mask and EXPLDontDraw) = 0 then
+ if (GameFlags and gfSolidLand) = 0 then
+ begin
+ cnt:= DrawExplosion(X, Y, Radius) div 1608; // approx 2 16x16 circles to erase per chunk
+ if (cnt > 0) and (SpritesData[sprChunk].Texture <> nil) then
+ for i:= 0 to cnt do
+ AddVisualGear(X, Y, vgtChunk)
+ end;
+
+uAIMisc.AwareOfExplosion(0, 0, 0)
+end;
+
+function ModifyDamage(dmg: Longword; Gear: PGear): Longword;
+var i: hwFloat;
+begin
+(* Invulnerability cannot be placed in here due to still needing kicks
+ Not without a new damage machine.
+ King check should be in here instead of ApplyDamage since Tiy wants them kicked less
+*)
+i:= _1;
+if (CurrentHedgehog <> nil) and CurrentHedgehog^.King then
+ i:= _1_5;
+if (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.King) then
+ ModifyDamage:= hwRound(_0_01 * cDamageModifier * dmg * i * cDamagePercent * _0_5)
+else
+ ModifyDamage:= hwRound(_0_01 * cDamageModifier * dmg * i * cDamagePercent)
+end;
+
+procedure ApplyDamage(Gear: PGear; AttackerHog: PHedgehog; Damage: Longword; Source: TDamageSource);
+var s: shortstring;
+ vampDmg, tmpDmg, i: Longword;
+ vg: PVisualGear;
+begin
+ if Damage = 0 then
+ exit; // nothing to apply
+
+ if (Gear^.Kind = gtHedgehog) then
+ begin
+ Gear^.LastDamage := AttackerHog;
+
+ Gear^.Hedgehog^.Team^.Clan^.Flawless:= false;
+ HHHurt(Gear^.Hedgehog, Source);
+ AddDamageTag(hwRound(Gear^.X), hwRound(Gear^.Y), Damage, Gear^.Hedgehog^.Team^.Clan^.Color);
+ tmpDmg:= min(Damage, max(0,Gear^.Health-Gear^.Damage));
+ if (Gear <> CurrentHedgehog^.Gear) and (CurrentHedgehog^.Gear <> nil) and (tmpDmg >= 1) then
+ begin
+ if cVampiric then
+ begin
+ vampDmg:= hwRound(int2hwFloat(tmpDmg)*_0_8);
+ if vampDmg >= 1 then
+ begin
+ // was considering pulsing on attack, Tiy thinks it should be permanent while in play
+ //CurrentHedgehog^.Gear^.State:= CurrentHedgehog^.Gear^.State or gstVampiric;
+ inc(CurrentHedgehog^.Gear^.Health,vampDmg);
+ str(vampDmg, s);
+ s:= '+' + s;
+ AddCaption(s, CurrentHedgehog^.Team^.Clan^.Color, capgrpAmmoinfo);
+ RenderHealth(CurrentHedgehog^);
+ RecountTeamHealth(CurrentHedgehog^.Team);
+ i:= 0;
+ while i < vampDmg do
+ begin
+ vg:= AddVisualGear(hwRound(CurrentHedgehog^.Gear^.X), hwRound(CurrentHedgehog^.Gear^.Y), vgtStraightShot);
+ if vg <> nil then
+ with vg^ do
+ begin
+ Tint:= $FF0000FF;
+ State:= ord(sprHealth)
+ end;
+ inc(i, 5);
+ end;
+ end
+ end;
+ if ((GameFlags and gfKarma) <> 0) and
+ ((GameFlags and gfInvulnerable) = 0)
+ and (not CurrentHedgehog^.Gear^.Invulnerable) then
+ begin // this cannot just use Damage or it interrupts shotgun and gets you called stupid
+ inc(CurrentHedgehog^.Gear^.Karma, tmpDmg);
+ CurrentHedgehog^.Gear^.LastDamage := CurrentHedgehog;
+ spawnHealthTagForHH(CurrentHedgehog^.Gear, tmpDmg);
+ end;
+ uStats.HedgehogDamaged(Gear, AttackerHog, Damage, false);
+ end;
+ end
+ else if Gear^.Kind <> gtStructure then // not gtHedgehog nor gtStructure
+ begin
+ Gear^.Hedgehog:= AttackerHog;
+ end;
+ inc(Gear^.Damage, Damage);
+
+ ScriptCall('onGearDamage', Gear^.UID, Damage);
+end;
+
+procedure spawnHealthTagForHH(HHGear: PGear; dmg: Longword);
+var tag: PVisualGear;
+begin
+tag:= AddVisualGear(hwRound(HHGear^.X), hwRound(HHGear^.Y), vgtHealthTag, dmg);
+if (tag <> nil) then
+ tag^.Hedgehog:= HHGear^.Hedgehog; // the tag needs the tag to determine the text color
+AllInactive:= false;
+HHGear^.Active:= true;
+end;
+
+procedure HHHurt(Hedgehog: PHedgehog; Source: TDamageSource);
+begin
+if (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);
+ end
+else if (Source = dsPoison) then
+ case random(2) of
+ 0: PlaySound(sndPoisonCough, Hedgehog^.Team^.voicepack);
+ 1: PlaySound(sndPoisonMoan, Hedgehog^.Team^.voicepack);
+ end
+else
+ 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);
+ end
+end;
+
+procedure CheckHHDamage(Gear: PGear);
+var
+ dmg: Longword;
+ i: LongInt;
+ particle: PVisualGear;
+begin
+ if _0_4 < Gear^.dY then
+ begin
+ dmg := ModifyDamage(1 + hwRound((hwAbs(Gear^.dY) - _0_4) * 70), Gear);
+ PlaySound(sndBump);
+ if dmg < 1 then
+ exit;
+
+ for i:= min(12, (3 + dmg div 10)) downto 0 do
+ begin
+ particle := AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust);
+ if particle <> nil then
+ particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480);
+ end;
+
+ if (Gear^.Invulnerable) then
+ exit;
+
+ //if _0_6 < Gear^.dY then
+ // PlaySound(sndOw4, Gear^.Hedgehog^.Team^.voicepack)
+ //else
+ // PlaySound(sndOw1, Gear^.Hedgehog^.Team^.voicepack);
+
+ if Gear^.LastDamage <> nil then
+ ApplyDamage(Gear, Gear^.LastDamage, dmg, dsFall)
+ else
+ ApplyDamage(Gear, CurrentHedgehog, dmg, dsFall);
+ end
+end;
+
+
+procedure CalcRotationDirAngle(Gear: PGear);
+var
+ dAngle: real;
+begin
+ dAngle := (Gear^.dX.QWordValue + Gear^.dY.QWordValue) / $80000000;
+ if not Gear^.dX.isNegative then
+ Gear^.DirAngle := Gear^.DirAngle + dAngle
+ else
+ Gear^.DirAngle := Gear^.DirAngle - dAngle;
+
+ if Gear^.DirAngle < 0 then
+ Gear^.DirAngle := Gear^.DirAngle + 360
+ else if 360 < Gear^.DirAngle then
+ Gear^.DirAngle := Gear^.DirAngle - 360
+end;
+
+function CheckGearDrowning(Gear: PGear): boolean;
+var
+ skipSpeed, skipAngle, skipDecay: hwFloat;
+ i, maxDrops, X, Y: LongInt;
+ vdX, vdY: real;
+ particle: PVisualGear;
+ isSubmersible: boolean;
+begin
+ // probably needs tweaking. might need to be in a case statement based upon gear type
+ Y:= hwRound(Gear^.Y);
+ if cWaterLine < Y + Gear^.Radius then
+ begin
+ isSubmersible:= (Gear = CurrentHedgehog^.Gear) and (CurAmmoGear <> nil) and (CurAmmoGear^.AmmoType = amJetpack);
+ skipSpeed := _0_25;
+ skipAngle := _1_9;
+ skipDecay := _0_87;
+ X:= hwRound(Gear^.X);
+ vdX:= hwFloat2Float(Gear^.dX);
+ vdY:= hwFloat2Float(Gear^.dY);
+ // this could perhaps be a tiny bit higher.
+ if (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > skipSpeed)
+ and (hwAbs(Gear^.dX) > skipAngle * hwAbs(Gear^.dY)) then
+ begin
+ Gear^.dY.isNegative := true;
+ Gear^.dY := Gear^.dY * skipDecay;
+ Gear^.dX := Gear^.dX * skipDecay;
+ CheckGearDrowning := false;
+ PlaySound(sndSkip)
+ end
+ else
+ begin
+ if not isSubmersible then
+ begin
+ CheckGearDrowning := true;
+ Gear^.State := gstDrowning;
+ Gear^.RenderTimer := false;
+ if (Gear^.Kind <> gtSniperRifleShot) and (Gear^.Kind <> gtShotgunShot)
+ and (Gear^.Kind <> gtDEagleShot) and (Gear^.Kind <> gtSineGunShot) then
+ if Gear^.Kind = gtHedgehog then
+ begin
+ if Gear^.Hedgehog^.Effects[heResurrectable] then
+ ResurrectHedgehog(Gear)
+ else
+ begin
+ Gear^.doStep := @doStepDrowningGear;
+ Gear^.State := Gear^.State and (not gstHHDriven);
+ AddCaption(Format(GetEventString(eidDrowned), Gear^.Hedgehog^.Name), cWhiteColor, capgrpMessage);
+ end
+ end
+ else
+ Gear^.doStep := @doStepDrowningGear;
+ if Gear^.Kind = gtFlake then
+ exit // skip splashes
+ end;
+ if ((not isSubmersible) and (Y < cWaterLine + 64 + Gear^.Radius))
+ or (isSubmersible and (Y < cWaterLine + 2 + Gear^.Radius) and ((CurAmmoGear^.Pos = 0)
+ and (CurAmmoGear^.dY < _0_01))) then
+ // don't play splash if they are already way past the surface
+ PlaySound(sndSplash)
+ end;
+
+ if ((cReducedQuality and rqPlainSplash) = 0)
+ and (((not isSubmersible) and (Y < cWaterLine + 64 + Gear^.Radius))
+ or (isSubmersible and (Y < cWaterLine + 2 + Gear^.Radius) and ((CurAmmoGear^.Pos = 0)
+ and (CurAmmoGear^.dY < _0_01)))) then
+ begin
+ AddVisualGear(X, cWaterLine, vgtSplash);
+
+ maxDrops := (Gear^.Radius div 2) + round(vdX * Gear^.Radius * 2) + round(vdY * Gear^.Radius * 2);
+ for i:= max(maxDrops div 3, min(32, Random(maxDrops))) downto 0 do
+ begin
+ particle := AddVisualGear(X - 3 + Random(6), cWaterLine, vgtDroplet);
+ if particle <> nil then
+ begin
+ particle^.dX := particle^.dX - vdX / 10;
+ particle^.dY := particle^.dY - vdY / 5;
+ end
+ end
+ end;
+ if isSubmersible and (CurAmmoGear^.Pos = 0) then
+ CurAmmoGear^.Pos := 1000
+ end
+ else
+ CheckGearDrowning := false;
+end;
+
+
+procedure ResurrectHedgehog(gear: PGear);
+var tempTeam : PTeam;
+begin
+ AttackBar:= 0;
+ gear^.dX := _0;
+ gear^.dY := _0;
+ gear^.Damage := 0;
+ gear^.Health := gear^.Hedgehog^.InitialHealth;
+ gear^.Hedgehog^.Effects[hePoisoned] := false;
+ if not CurrentHedgehog^.Effects[heResurrectable] then
+ with CurrentHedgehog^ do
+ begin
+ inc(Team^.stats.AIKills);
+ FreeTexture(Team^.AIKillsTex);
+ Team^.AIKillsTex := RenderStringTex(inttostr(Team^.stats.AIKills), Team^.Clan^.Color, fnt16);
+ end;
+ tempTeam := gear^.Hedgehog^.Team;
+ DeleteCI(gear);
+ FindPlace(gear, false, 0, LAND_WIDTH, true);
+ if gear <> nil then
+ begin
+ RenderHealth(gear^.Hedgehog^);
+ ScriptCall('onGearResurrect', gear^.uid);
+ gear^.State := gstWait;
+ end;
+ RecountTeamHealth(tempTeam);
+end;
+
+function CountNonZeroz(x, y, r, c: LongInt): LongInt;
+var i: LongInt;
+ count: LongInt = 0;
+begin
+if (y and LAND_HEIGHT_MASK) = 0 then
+ for i:= max(x - r, 0) to min(x + r, LAND_WIDTH - 4) do
+ if Land[y, i] <> 0 then
+ begin
+ inc(count);
+ if count = c then
+ exit(count)
+ end;
+CountNonZeroz:= count;
+end;
+
+procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity: boolean);
+var x: LongInt;
+ y, sy: LongInt;
+ ar: array[0..511] of TPoint;
+ ar2: array[0..1023] of TPoint;
+ cnt, cnt2: Longword;
+ delta: LongInt;
+ reallySkip, tryAgain: boolean;
+begin
+reallySkip:= false; // try not skipping proximity at first
+tryAgain:= true;
+while tryAgain do
+ begin
+ delta:= 250;
+ cnt2:= 0;
+ repeat
+ x:= Left + LongInt(GetRandom(Delta));
+ repeat
+ inc(x, Delta);
+ cnt:= 0;
+ y:= min(1024, topY) - 2 * Gear^.Radius;
+ while y < cWaterLine do
+ begin
+ repeat
+ inc(y, 2);
+ until (y >= cWaterLine) or (CountNonZeroz(x, y, Gear^.Radius - 1, 1) = 0);
+
+ sy:= y;
+
+ repeat
+ inc(y);
+ until (y >= cWaterLine) or (CountNonZeroz(x, y, Gear^.Radius - 1, 1) <> 0);
+
+ if (y - sy > Gear^.Radius * 2)
+ and (((Gear^.Kind = gtExplosives)
+ and (y < cWaterLine)
+ and (reallySkip or (CheckGearsNear(x, y - Gear^.Radius, [gtFlame, gtHedgehog, gtMine, gtCase, gtExplosives], 60, 60) = nil))
+ and (CountNonZeroz(x, y+1, Gear^.Radius - 1, Gear^.Radius+1) > Gear^.Radius))
+ or
+ ((Gear^.Kind <> gtExplosives)
+ and (y < cWaterLine)
+ and (reallySkip or (CheckGearsNear(x, y - Gear^.Radius, [gtFlame, gtHedgehog, gtMine, gtCase, gtExplosives], 110, 110) = nil)))) then
+
+ begin
+ ar[cnt].X:= x;
+ if withFall then
+ ar[cnt].Y:= sy + Gear^.Radius
+ else
+ ar[cnt].Y:= y - Gear^.Radius;
+ inc(cnt)
+ end;
+
+ inc(y, 45)
+ end;
+
+ if cnt > 0 then
+ with ar[GetRandom(cnt)] do
+ begin
+ ar2[cnt2].x:= x;
+ ar2[cnt2].y:= y;
+ inc(cnt2)
+ end
+ until (x + Delta > Right);
+
+ dec(Delta, 60)
+ until (cnt2 > 0) or (Delta < 70);
+ if (cnt2 = 0) and skipProximity and (not reallySkip) then
+ tryAgain:= true
+ else tryAgain:= false;
+ reallySkip:= true;
+ end;
+
+if cnt2 > 0 then
+ with ar2[GetRandom(cnt2)] do
+ begin
+ Gear^.X:= int2hwFloat(x);
+ Gear^.Y:= int2hwFloat(y);
+ AddFileLog('Assigned Gear coordinates (' + inttostr(x) + ',' + inttostr(y) + ')');
+ end
+ else
+ begin
+ OutError('Can''t find place for Gear', false);
+ if Gear^.Kind = gtHedgehog then
+ Gear^.Hedgehog^.Effects[heResurrectable] := false;
+ DeleteGear(Gear);
+ Gear:= nil
+ end
+end;
+
+function CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear;
+var t: PGear;
+begin
+t:= GearsList;
+rX:= sqr(rX);
+rY:= sqr(rY);
+
+while t <> nil do
+ begin
+ if (t <> Gear) and (t^.Kind = Kind) then
+ if not((hwSqr(Gear^.X - t^.X) / rX + hwSqr(Gear^.Y - t^.Y) / rY) > _1) then
+ exit(t);
+ t:= t^.NextGear
+ end;
+
+CheckGearNear:= nil
+end;
+
+
+function CheckGearsNear(mX, mY: LongInt; Kind: TGearsType; rX, rY: LongInt): PGear;
+var t: PGear;
+begin
+t:= GearsList;
+rX:= sqr(rX);
+rY:= sqr(rY);
+while t <> nil do
+ begin
+ if t^.Kind in Kind then
+ if not (hwSqr(int2hwFloat(mX) - t^.X) / rX + hwSqr(int2hwFloat(mY) - t^.Y) / rY > _1) then
+ exit(t);
+ t:= t^.NextGear
+ end;
+CheckGearsNear:= nil
+end;
+end.