Add an empty weapon to avoid selection of weapons which aren't yet ready. Might all be useful to switch to amNothing in certain situations, like after using up all ropes, instead of bazooka.
(*
* Hedgewars, a free turn based strategy game
* Copyright (c) 2004-2009 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
*)
unit uGears;
interface
uses SDLh, uConsts, uFloat;
{$INCLUDE options.inc}
const AllInactive: boolean = false;
PrvInactive: boolean = false;
type PGear = ^TGear;
TGearStepProcedure = procedure (Gear: PGear);
TGear = record
NextGear, PrevGear: PGear;
Active: Boolean;
Invulnerable: Boolean;
RenderTimer: Boolean;
Ammo : PAmmo;
State : Longword;
X : hwFloat;
Y : hwFloat;
dX: hwFloat;
dY: hwFloat;
Kind: TGearType;
Pos: Longword;
doStep: TGearStepProcedure;
Radius: LongInt;
Angle, Power : Longword;
DirAngle: real;
Timer : LongWord;
Elasticity: hwFloat;
Friction : hwFloat;
Message, MsgParam : Longword;
Hedgehog: pointer;
Health, Damage, Karma: LongInt;
CollisionIndex: LongInt;
Tag: LongInt;
Tex: PTexture;
Z: Longword;
IntersectGear: PGear;
TriggerId: Longword;
uid: Longword
end;
function AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer: LongWord): PGear;
procedure ProcessGears;
procedure ResetUtilities;
procedure ApplyDamage(Gear: PGear; Damage: Longword);
procedure SetAllToActive;
procedure SetAllHHToActive;
procedure DrawGears;
procedure FreeGearsList;
procedure AddMiscGears;
procedure AssignHHCoords;
procedure InsertGearToList(Gear: PGear);
procedure RemoveGearFromList(Gear: PGear);
var CurAmmoGear: PGear = nil;
GearsList: PGear = nil;
KilledHHs: Longword = 0;
SuddenDeathDmg: Boolean = false;
SpeechType: Longword = 1;
SpeechText: shortstring;
implementation
uses uWorld, uMisc, uStore, uConsole, uSound, uTeams, uRandom, uCollisions,
uLand, uIO, uLandGraphics, uAIMisc, uLocale, uAI, uAmmos, uTriggers,
{$IFDEF GLES11}
gles11,
{$ELSE}
GL,
{$ENDIF}
uStats, uVisualGears;
const MAXROPEPOINTS = 384;
var RopePoints: record
Count: Longword;
HookAngle: GLfloat;
ar: array[0..MAXROPEPOINTS] of record
X, Y: hwFloat;
dLen: hwFloat;
b: boolean;
end;
end;
procedure DeleteGear(Gear: PGear); forward;
procedure doMakeExplosion(X, Y, Radius: LongInt; Mask: LongWord); forward;
procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); forward;
//procedure AmmoFlameWork(Ammo: PGear); forward;
function CheckGearNear(Gear: PGear; Kind: TGearType; rX, rY: LongInt): PGear; forward;
procedure SpawnBoxOfSmth; forward;
procedure AfterAttack; forward;
procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt); forward;
procedure HedgehogStep(Gear: PGear); forward;
procedure doStepHedgehogMoving(Gear: PGear); forward;
procedure HedgehogChAngle(Gear: PGear); forward;
procedure ShotgunShot(Gear: PGear); forward;
procedure PickUp(HH, Gear: PGear); forward;
procedure HHSetWeapon(Gear: PGear); forward;
{$INCLUDE GSHandlers.inc}
{$INCLUDE HHHandlers.inc}
const doStepHandlers: array[TGearType] of TGearStepProcedure = (
@doStepBomb,
@doStepHedgehog,
@doStepGrenade,
@doStepHealthTag,
@doStepGrave,
@doStepUFO,
@doStepShotgunShot,
@doStepPickHammer,
@doStepRope,
@doStepSmokeTrace,
@doStepExplosion,
@doStepMine,
@doStepCase,
@doStepDEagleShot,
@doStepDynamite,
@doStepBomb,
@doStepCluster,
@doStepShover,
@doStepFlame,
@doStepFirePunch,
@doStepActionTimer,
@doStepActionTimer,
@doStepActionTimer,
@doStepParachute,
@doStepAirAttack,
@doStepAirBomb,
@doStepBlowTorch,
@doStepGirder,
@doStepTeleport,
@doStepSwitcher,
@doStepCase,
@doStepMortar,
@doStepWhip,
@doStepKamikaze,
@doStepCake,
@doStepSeduction,
@doStepWatermelon,
@doStepCluster,
@doStepBomb,
@doStepSmokeTrace,
@doStepWaterUp,
@doStepDrill,
@doStepBallgun,
@doStepBomb,
@doStepRCPlane,
@doStepSniperRifleShot,
@doStepJetpack
);
procedure InsertGearToList(Gear: PGear);
var tmp, ptmp: PGear;
begin
if GearsList = nil then
GearsList:= Gear
else begin
tmp:= GearsList;
ptmp:= GearsList;
while (tmp <> nil) and (tmp^.Z <= Gear^.Z) do
begin
ptmp:= tmp;
tmp:= tmp^.NextGear
end;
if ptmp <> nil then
begin
Gear^.NextGear:= ptmp^.NextGear;
Gear^.PrevGear:= ptmp;
if ptmp^.NextGear <> nil then ptmp^.NextGear^.PrevGear:= Gear;
ptmp^.NextGear:= Gear
end
else GearsList:= Gear
end
end;
procedure RemoveGearFromList(Gear: PGear);
begin
if Gear^.NextGear <> nil then Gear^.NextGear^.PrevGear:= Gear^.PrevGear;
if Gear^.PrevGear <> nil then
Gear^.PrevGear^.NextGear:= Gear^.NextGear
else
GearsList:= Gear^.NextGear
end;
function AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer: LongWord): PGear;
const Counter: Longword = 0;
var Result: PGear;
begin
inc(Counter);
{$IFDEF DEBUGFILE}
AddFileLog('AddGear: #' + inttostr(Counter) + ' (' + inttostr(x) + ',' + inttostr(y) + '), d(' + floattostr(dX) + ',' + floattostr(dY) + ') type = ' + inttostr(ord(Kind)));
{$ENDIF}
New(Result);
FillChar(Result^, sizeof(TGear), 0);
Result^.X:= int2hwFloat(X);
Result^.Y:= int2hwFloat(Y);
Result^.Kind := Kind;
Result^.State:= State;
Result^.Active:= true;
Result^.dX:= dX;
Result^.dY:= dY;
Result^.doStep:= doStepHandlers[Kind];
Result^.CollisionIndex:= -1;
Result^.Timer:= Timer;
Result^.Z:= cUsualZ;
Result^.uid:= Counter;
if CurrentTeam <> nil then
begin
Result^.Hedgehog:= CurrentHedgehog;
Result^.IntersectGear:= CurrentHedgehog^.Gear
end;
case Kind of
gtAmmo_Bomb,
gtClusterBomb: begin
Result^.Radius:= 4;
Result^.Elasticity:= _0_6;
Result^.Friction:= _0_96;
Result^.RenderTimer:= true;
if Result^.Timer = 0 then Result^.Timer:= 3000
end;
gtWatermelon: begin
Result^.Radius:= 4;
Result^.Elasticity:= _0_8;
Result^.Friction:= _0_995;
Result^.RenderTimer:= true;
if Result^.Timer = 0 then Result^.Timer:= 3000
end;
gtHedgehog: begin
Result^.Radius:= cHHRadius;
Result^.Elasticity:= _0_35;
Result^.Friction:= _0_999;
Result^.Angle:= cMaxAngle div 2;
Result^.Z:= cHHZ;
end;
gtAmmo_Grenade: begin // bazooka
Result^.Radius:= 4;
end;
gtHealthTag: begin
Result^.Timer:= 1500;
Result^.Z:= 2002;
end;
gtGrave: begin
Result^.Radius:= 10;
Result^.Elasticity:= _0_6;
end;
gtUFO: begin
Result^.Radius:= 5;
Result^.Timer:= 500;
Result^.RenderTimer:= true;
Result^.Elasticity:= _0_9
end;
gtShotgunShot: begin
Result^.Timer:= 900;
Result^.Radius:= 2
end;
gtPickHammer: begin
Result^.Radius:= 10;
Result^.Timer:= 4000
end;
gtSmokeTrace,
gtEvilTrace: begin
Result^.X:= Result^.X - _16;
Result^.Y:= Result^.Y - _16;
Result^.State:= 8;
Result^.Z:= cSmokeZ
end;
gtRope: begin
Result^.Radius:= 3;
Result^.Friction:= _450;
RopePoints.Count:= 0;
end;
gtExplosion: begin
Result^.X:= Result^.X;
Result^.Y:= Result^.Y;
end;
gtMine: begin
Result^.State:= Result^.State or gstMoving;
Result^.Radius:= 2;
Result^.Elasticity:= _0_55;
Result^.Friction:= _0_995;
if cMinesTime < 0 then
Result^.Timer:= getrandom(4)*1000
else
Result^.Timer:= cMinesTime*1;
end;
gtCase: begin
Result^.Radius:= 16;
Result^.Elasticity:= _0_3
end;
gtDEagleShot: begin
Result^.Radius:= 1;
Result^.Health:= 50
end;
gtSniperRifleShot: begin
Result^.Radius:= 1;
Result^.Health:= 50
end;
gtDynamite: begin
Result^.Radius:= 3;
Result^.Elasticity:= _0_55;
Result^.Friction:= _0_03;
Result^.Timer:= 5000;
end;
gtCluster: begin
Result^.Radius:= 2;
Result^.RenderTimer:= true
end;
gtShover: Result^.Radius:= 20;
gtFlame: begin
Result^.Tag:= Counter mod 32;
Result^.Radius:= 1;
Result^.Health:= 5;
if (Result^.dY.QWordValue = 0) and (Result^.dX.QWordValue = 0) then
begin
Result^.dY:= (getrandom - _0_8) * _0_03;
Result^.dX:= (getrandom - _0_5) * _0_4
end
end;
gtFirePunch: begin
Result^.Radius:= 15;
Result^.Tag:= Y
end;
gtAirBomb: begin
Result^.Radius:= 5;
end;
gtBlowTorch: begin
Result^.Radius:= cHHRadius + cBlowTorchC;
Result^.Timer:= 7500
end;
gtSwitcher: begin
Result^.Z:= cCurrHHZ
end;
gtTarget: begin
Result^.Radius:= 16;
Result^.Elasticity:= _0_3
end;
gtMortar: begin
Result^.Radius:= 4;
Result^.Elasticity:= _0_2;
Result^.Friction:= _0_08
end;
gtWhip: Result^.Radius:= 20;
gtKamikaze: begin
Result^.Health:= 2048;
Result^.Radius:= 20
end;
gtCake: begin
Result^.Health:= 2048;
Result^.Radius:= 7;
Result^.Z:= cOnHHZ;
Result^.RenderTimer:= true;
if hwSign(dX) > 0 then Result^.Angle:= 1 else Result^.Angle:= 3
end;
gtHellishBomb: begin
Result^.Radius:= 4;
Result^.Elasticity:= _0_5;
Result^.Friction:= _0_96;
Result^.RenderTimer:= true;
Result^.Timer:= 5000
end;
gtDrill: begin
Result^.Timer:= 5000;
Result^.Radius:= 4
end;
gtBall: begin
Result^.Radius:= 5;
Result^.Tag:= random(8);
Result^.Timer:= 5000;
Result^.Elasticity:= _0_7;
Result^.Friction:= _0_995;
end;
gtBallgun: begin
Result^.Timer:= 5001;
end;
gtRCPlane: begin
Result^.Timer:= 15000;
Result^.Health:= 3;
Result^.Radius:= 8
end;
gtJetpack: begin
Result^.Health:= 2000;
end;
end;
InsertGearToList(Result);
AddGear:= Result
end;
procedure DeleteGear(Gear: PGear);
var team: PTeam;
t: Longword;
begin
DeleteCI(Gear);
if Gear^.Tex <> nil then
begin
FreeTexture(Gear^.Tex);
Gear^.Tex:= nil
end;
if Gear^.Kind = gtHedgehog then
if (CurAmmoGear <> nil) and (CurrentHedgehog^.Gear = Gear) then
begin
Gear^.Message:= gm_Destroy;
CurAmmoGear^.Message:= gm_Destroy;
exit
end
else
begin
if (hwRound(Gear^.Y) >= cWaterLine) then
begin
t:= max(Gear^.Damage, Gear^.Health);
Gear^.Damage:= t;
AddGear(hwRound(Gear^.X), min(hwRound(Gear^.Y),cWaterLine+cVisibleWater+32), gtHealthTag, t, _0, _0, 0)^.Hedgehog:= Gear^.Hedgehog;
uStats.HedgehogDamaged(Gear)
end;
team:= PHedgehog(Gear^.Hedgehog)^.Team;
if CurrentHedgehog^.Gear = Gear then
FreeActionsList; // to avoid ThinkThread on drawned gear
PHedgehog(Gear^.Hedgehog)^.Gear:= nil;
inc(KilledHHs);
RecountTeamHealth(team)
end;
{$IFDEF DEBUGFILE}
with Gear^ do AddFileLog('Delete: #' + inttostr(uid) + ' (' + inttostr(hwRound(x)) + ',' + inttostr(hwRound(y)) + '), d(' + floattostr(dX) + ',' + floattostr(dY) + ') type = ' + inttostr(ord(Kind)));
{$ENDIF}
if Gear^.TriggerId <> 0 then TickTrigger(Gear^.TriggerId);
if CurAmmoGear = Gear then CurAmmoGear:= nil;
if FollowGear = Gear then FollowGear:= nil;
RemoveGearFromList(Gear);
Dispose(Gear)
end;
function CheckNoDamage: boolean; // returns TRUE in case of no damaged hhs
var Gear: PGear;
dmg: LongInt;
begin
CheckNoDamage:= true;
Gear:= GearsList;
while Gear <> nil do
begin
if Gear^.Kind = gtHedgehog then
begin
if (not isInMultiShoot) then inc(Gear^.Damage, Gear^.Karma);
if (Gear^.Damage <> 0) and
(not Gear^.Invulnerable) then
begin
CheckNoDamage:= false;
uStats.HedgehogDamaged(Gear);
dmg:= Gear^.Damage;
if Gear^.Health < dmg then
Gear^.Health:= 0
else
dec(Gear^.Health, dmg);
if (PHedgehog(Gear^.Hedgehog)^.Team = CurrentTeam) and
(Gear^.Damage <> Gear^.Karma) and
not SuddenDeathDmg then
Gear^.State:= Gear^.State or gstLoser;
AddGear(hwRound(Gear^.X), hwRound(Gear^.Y) - cHHRadius - 12,
gtHealthTag, dmg, _0, _0, 0)^.Hedgehog:= Gear^.Hedgehog;
RenderHealth(PHedgehog(Gear^.Hedgehog)^);
RecountTeamHealth(PHedgehog(Gear^.Hedgehog)^.Team);
end;
if (not isInMultiShoot) then Gear^.Karma:= 0;
Gear^.Damage:= 0
end;
Gear:= Gear^.NextGear
end;
SuddenDeathDmg:= false;
end;
procedure HealthMachine;
var Gear: PGear;
begin
Gear:= GearsList;
while Gear <> nil do
begin
if Gear^.Kind = gtHedgehog then
Gear^.Damage:= min(cHealthDecrease, Gear^.Health - 1);
Gear:= Gear^.NextGear
end;
end;
procedure ProcessGears;
const delay: LongWord = 0;
step: (stDelay, stChDmg, stSweep, stTurnReact,
stAfterDelay, stChWin, stWater, stChWin2, stHealth,
stSpawn, stNTurn) = stDelay;
var Gear, t: PGear;
begin
PrvInactive:= AllInactive;
AllInactive:= true;
t:= GearsList;
while t <> nil do
begin
Gear:= t;
t:= Gear^.NextGear;
if Gear^.Active then
begin
if Gear^.RenderTimer and (Gear^.Timer > 500) and ((Gear^.Timer mod 1000) = 0) then
begin
if Gear^.Tex <> nil then FreeTexture(Gear^.Tex);
Gear^.Tex:= RenderStringTex(inttostr(Gear^.Timer div 1000), $FFFFFFFF, fntSmall);
end;
Gear^.doStep(Gear);
end
end;
if AllInactive then
case step of
stDelay: begin
if delay = 0 then
delay:= cInactDelay
else
dec(delay);
if delay = 0 then
inc(step)
end;
stChDmg: if CheckNoDamage then inc(step) else step:= stDelay;
stSweep: if SweepDirty then
begin
SetAllToActive;
step:= stChDmg
end else inc(step);
stTurnReact: begin
if (not bBetweenTurns) and (not isInMultiShoot) then
begin
uStats.TurnReaction;
inc(step)
end else
inc(step, 2);
end;
stAfterDelay: begin
if delay = 0 then
delay:= cInactDelay
else
dec(delay);
if delay = 0 then
inc(step)
end;
stChWin: begin
CheckForWin;
inc(step)
end;
stWater: if (not bBetweenTurns) and (not isInMultiShoot) then
begin
if TotalRounds = cSuddenDTurns + 2 then bWaterRising:= true;
if bWaterRising then
AddGear(0, 0, gtWaterUp, 0, _0, _0, 0);
inc(step)
end else inc(step);
stChWin2: begin
CheckForWin;
inc(step)
end;
stHealth: begin
if (TotalRounds = cSuddenDTurns) and (cHealthDecrease = 0) then
begin
cHealthDecrease:= 5;
AddCaption(trmsg[sidSuddenDeath], $FFFFFF, capgrpGameState);
playSound(sndSuddenDeath, false, nil);
end;
if (cHealthDecrease = 0)
or bBetweenTurns
or isInMultiShoot
or (TotalRounds = 0) then inc(step)
else begin
bBetweenTurns:= true;
HealthMachine;
SuddenDeathDmg:= true;
step:= stChDmg
end
end;
stSpawn: begin
if not isInMultiShoot then SpawnBoxOfSmth;
inc(step)
end;
stNTurn: begin
if isInMultiShoot then isInMultiShoot:= false
else begin
ResetUtilities;
FreeActionsList; // could send -left, -right and similar commands, so should be called before /nextturn
ParseCommand('/nextturn', true);
SwitchHedgehog;
inc(step); // FIXME wtf is that, it overflows step, and does nothing
AfterSwitchHedgehog;
bBetweenTurns:= false
end;
step:= Low(step)
end;
end;
if TurnTimeLeft > 0 then
if CurrentHedgehog^.Gear <> nil then
if ((CurrentHedgehog^.Gear^.State and gstAttacking) = 0)
and not isInMultiShoot then
begin
if (TurnTimeLeft = 5000)
and (CurrentHedgehog^.Gear <> nil)
and ((CurrentHedgehog^.Gear^.State and gstAttacked) = 0) then
PlaySound(sndHurry, false, CurrentTeam^.voicepack);
dec(TurnTimeLeft)
end;
if ((GameTicks and $FFFF) = $FFFF) then
begin
if (not CurrentTeam^.ExtDriven) then
SendIPCTimeInc;
if (not CurrentTeam^.ExtDriven) or CurrentTeam^.hasGone then
inc(hiTicks) // we do not recieve a message for this
end;
inc(GameTicks)
end;
//Purpose, to reset all transient attributes toggled by a utility.
//If any of these are set as permanent toggles in the frontend, that needs to be checked and skipped here.
procedure ResetUtilities;
var i: LongInt;
begin
SpeechText:= ''; // in case it has not been consumed
if (GameFlags and gfLowGravity) = 0 then
cGravity:= cMaxWindSpeed;
if (GameFlags and gfVampiric) = 0 then
cVampiric:= false;
cDamageModifier:= _1;
if (GameFlags and gfLaserSight) = 0 then
cLaserSighting:= false;
if (GameFlags and gfArtillery) = 0 then
cArtillery:= false;
// have to sweep *all* current team hedgehogs since it is theoretically possible if you have enough invulnerabilities and switch turns to make your entire team invulnerable
if (CurrentTeam <> nil) then
with CurrentTeam^ do
for i:= 0 to cMaxHHIndex do
with Hedgehogs[i] do
begin
if (SpeechGear <> nil) then
begin
DeleteVisualGear(SpeechGear); // remove to restore persisting beyond end of turn. Tiy says was too much of a gameplay issue
SpeechGear:= nil
end;
if (Gear <> nil) then
if (GameFlags and gfInvulnerable) = 0 then
Gear^.Invulnerable:= false;
end;
end;
procedure ApplyDamage(Gear: PGear; Damage: Longword);
var s: shortstring;
vampDmg, tmpDmg: Longword;
begin
if (Gear^.Kind = gtHedgehog) and (Damage>=1) then
begin
AddDamageTag(hwRound(Gear^.X), hwRound(Gear^.Y), Damage, PHedgehog(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);
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);
AddGear(hwRound(CurrentHedgehog^.Gear^.X),
hwRound(CurrentHedgehog^.Gear^.Y),
gtHealthTag, tmpDmg, _0, _0, 0)^.Hedgehog:= CurrentHedgehog;
end;
end;
end;
inc(Gear^.Damage, Damage);
end;
procedure SetAllToActive;
var t: PGear;
begin
AllInactive:= false;
t:= GearsList;
while t <> nil do
begin
t^.Active:= true;
t:= t^.NextGear
end
end;
procedure SetAllHHToActive;
var t: PGear;
begin
AllInactive:= false;
t:= GearsList;
while t <> nil do
begin
if t^.Kind = gtHedgehog then t^.Active:= true;
t:= t^.NextGear
end
end;
procedure DrawHH(Gear: PGear);
var i, t: LongInt;
amt: TAmmoType;
hx, hy, cx, cy, tx, ty, sx, sy, m: LongInt; // hedgehog, crosshair, temp, sprite, direction
lx, ly, dx, dy, ax, ay, aAngle, dAngle: real; // laser, change
defaultPos, HatVisible: boolean;
VertexBuffer: array [0..1] of TVertex2f;
begin
m:= 1;
if ((Gear^.State and gstHHHJump) <> 0) and not cArtillery then m:= -1;
if (Gear^.State and gstHHDeath) <> 0 then
begin
DrawSprite(sprHHDeath, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 26 + WorldDy, Gear^.Pos);
exit
end;
defaultPos:= true;
HatVisible:= false;
sx:= hwRound(Gear^.X) + 1 + WorldDx;
sy:= hwRound(Gear^.Y) - 3 + WorldDy;
if (Gear^.State and gstDrowning) <> 0 then
begin
DrawHedgehog(sx, sy,
hwSign(Gear^.dX),
1,
7,
0);
defaultPos:= false
end else
if ((Gear^.State and gstWinner) <> 0) and
((CurAmmoGear = nil) or (CurAmmoGear^.Kind <> gtPickHammer)) then
begin
DrawHedgehog(sx, sy,
hwSign(Gear^.dX),
2,
0,
0);
defaultPos:= false
end else
if (Gear^.State and gstLoser) <> 0 then // for now using the jackhammer for its kind of bemused "oops" look
begin
DrawHedgehog(sx, sy,
hwSign(Gear^.dX),
2,
3,
0);
defaultPos:= false
end else
if (Gear^.State and gstHHDriven) <> 0 then
begin
if ((Gear^.State and gstHHThinking) = 0) and
ShowCrosshair and
((Gear^.State and (gstAttacked or gstAnimation)) = 0) then
begin
(* These calculations are a little complex for a few reasons:
1: I need to draw the laser from weapon origin to nearest land
2: I need to start the beam outside the hedgie for attractiveness.
3: I need to extend the beam beyond land.
This routine perhaps should be pushed into uStore or somesuch instead of continuuing the increase in size of this function.
*)
dx:= hwSign(Gear^.dX) * m * Sin(Gear^.Angle * pi / cMaxAngle);
dy:= - Cos(Gear^.Angle * pi / cMaxAngle);
if cLaserSighting then
begin
lx:= hwRound(Gear^.X);
ly:= hwRound(Gear^.Y);
lx:= lx + dx * 16;
ly:= ly + dy * 16;
ax:= dx * 4;
ay:= dy * 4;
tx:= round(lx);
ty:= round(ly);
hx:= tx;
hy:= ty;
while ((ty and LAND_HEIGHT_MASK) = 0) and
((tx and LAND_WIDTH_MASK) = 0) and
(Land[ty, tx] = 0) do
begin
lx:= lx + ax;
ly:= ly + ay;
tx:= round(lx);
ty:= round(ly)
end;
// reached edge of land. assume infinite beam. Extend it way out past camera
if ((ty and LAND_HEIGHT_MASK) <> 0) or ((tx and LAND_WIDTH_MASK) <> 0) then
begin
tx:= round(lx + ax * (LAND_WIDTH div 4));
ty:= round(ly + ay * (LAND_WIDTH div 4));
end;
//if (abs(lx-tx)>8) or (abs(ly-ty)>8) then
begin
glDisable(GL_TEXTURE_2D);
glEnable(GL_LINE_SMOOTH);
glColor4ub($FF, $00, $00, $C0);
VertexBuffer[0].X:= hx + WorldDx;
VertexBuffer[0].Y:= hy + WorldDy;
VertexBuffer[1].X:= tx + WorldDx;
VertexBuffer[1].Y:= ty + WorldDy;
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
glDrawArrays(GL_LINES, 0, Length(VertexBuffer));
glColor4f(1, 1, 1, 1);
glEnable(GL_TEXTURE_2D);
glDisable(GL_LINE_SMOOTH);
end;
end;
// draw crosshair
cx:= Round(hwRound(Gear^.X) + dx * 80);
cy:= Round(hwRound(Gear^.Y) + dy * 80);
DrawRotatedTex(PHedgehog(Gear^.Hedgehog)^.Team^.CrosshairTex,
12, 12, cx + WorldDx, cy + WorldDy, 0,
hwSign(Gear^.dX) * (Gear^.Angle * 180.0) / cMaxAngle);
end;
hx:= hwRound(Gear^.X) + 1 + 8 * hwSign(Gear^.dX) + WorldDx;
hy:= hwRound(Gear^.Y) - 2 + WorldDy;
aangle:= Gear^.Angle * 180 / cMaxAngle - 90;
if CurAmmoGear <> nil then
begin
case CurAmmoGear^.Kind of
gtShotgunShot: begin
if (CurAmmoGear^.State and gstAnimation <> 0) then
DrawRotated(sprShotgun, hx, hy, hwSign(Gear^.dX), aangle)
else
DrawRotated(sprHandShotgun, hx, hy, hwSign(Gear^.dX), aangle);
end;
gtDEagleShot: DrawRotated(sprDEagle, hx, hy, hwSign(Gear^.dX), aangle);
gtSniperRifleShot: begin
if (CurAmmoGear^.State and gstAnimation <> 0) then
DrawRotatedF(sprSniperRifle, hx, hy, 1, hwSign(Gear^.dX), aangle)
else
DrawRotatedF(sprSniperRifle, hx, hy, 0, hwSign(Gear^.dX), aangle)
end;
gtBallgun: DrawRotated(sprHandBallgun, hx, hy, hwSign(Gear^.dX), aangle);
gtRCPlane: begin
DrawRotated(sprHandPlane, hx, hy, hwSign(Gear^.dX), 0);
defaultPos:= false
end;
gtRope: begin
if Gear^.X < CurAmmoGear^.X then
begin
dAngle:= 0;
i:= 1
end else
begin
dAngle:= 180;
i:= -1
end;
sx:= hwRound(Gear^.X) + WorldDx;
sy:= hwRound(Gear^.Y) + WorldDy;
DrawHedgehog(sx, sy,
i,
1,
0,
DxDy2Angle(CurAmmoGear^.dY, CurAmmoGear^.dX) + dAngle);
defaultPos:= false
end;
gtBlowTorch: begin
DrawRotated(sprBlowTorch, hx, hy, hwSign(Gear^.dX), aangle);
DrawHedgehog(sx, sy,
hwSign(Gear^.dX),
3,
PHedgehog(Gear^.Hedgehog)^.visStepPos div 2,
0);
defaultPos:= false
end;
gtShover: DrawRotated(sprHandBaseball, hx, hy, hwSign(Gear^.dX), aangle + 180);
gtFirePunch: begin
DrawHedgehog(sx, sy,
hwSign(Gear^.dX),
1,
4,
0);
defaultPos:= false
end;
gtPickHammer: begin
defaultPos:= false;
dec(sy,20);
end;
gtTeleport: defaultPos:= false;
gtWhip: begin
DrawRotatedF(sprWhip,
sx,
sy,
1,
hwSign(Gear^.dX),
0);
defaultPos:= false
end;
gtKamikaze: begin
if CurAmmoGear^.Pos = 0 then
DrawHedgehog(sx, sy,
hwSign(Gear^.dX),
1,
6,
0)
else
DrawRotatedF(sprKamikaze,
hwRound(Gear^.X) + WorldDx,
hwRound(Gear^.Y) + WorldDy,
CurAmmoGear^.Pos - 1,
hwSign(Gear^.dX),
aangle);
defaultPos:= false
end;
gtSeduction: begin
if CurAmmoGear^.Pos >= 6 then
DrawHedgehog(sx, sy,
hwSign(Gear^.dX),
2,
2,
0)
else
begin
DrawRotatedF(sprDress,
hwRound(Gear^.X) + WorldDx,
hwRound(Gear^.Y) + WorldDy,
CurAmmoGear^.Pos,
hwSign(Gear^.dX),
0);
DrawSprite(sprCensored, hwRound(Gear^.X) - 32 + WorldDx, hwRound(Gear^.Y) - 20 + WorldDy, 0)
end;
defaultPos:= false
end;
end;
case CurAmmoGear^.Kind of
gtShotgunShot,
gtDEagleShot,
gtSniperRifleShot,
gtShover: begin
DrawHedgehog(sx, sy,
hwSign(Gear^.dX),
0,
4,
0);
defaultPos:= false;
HatVisible:= true
end
end
end else
if ((Gear^.State and gstHHJumping) <> 0) then
begin
DrawHedgehog(sx, sy,
hwSign(Gear^.dX)*m,
1,
1,
0);
HatVisible:= true;
HatVisible:= true;
defaultPos:= false
end else
if (Gear^.Message and (gm_Left or gm_Right) <> 0) and (not isCursorVisible) then
begin
DrawHedgehog(sx, sy,
hwSign(Gear^.dX),
0,
PHedgehog(Gear^.Hedgehog)^.visStepPos div 2,
0);
defaultPos:= false;
HatVisible:= true
end
else
if ((Gear^.State and gstAnimation) <> 0) then
begin
DrawRotatedF(Wavez[TWave(Gear^.Tag)].Sprite,
sx,
sy,
Gear^.Pos,
hwSign(Gear^.dX),
0.0);
defaultPos:= false
end
else
if ((Gear^.State and gstAttacked) = 0) then
begin
amt:= CurrentHedgehog^.Ammo^[CurrentHedgehog^.CurSlot, CurrentHedgehog^.CurAmmo].AmmoType;
case amt of
amBazooka,
amMortar: DrawRotated(sprHandBazooka, hx, hy, hwSign(Gear^.dX), aangle);
amBallgun: DrawRotated(sprHandBallgun, hx, hy, hwSign(Gear^.dX), aangle);
amDrill: DrawRotated(sprHandDrill, hx, hy, hwSign(Gear^.dX), aangle);
amRope: DrawRotated(sprHandRope, hx, hy, hwSign(Gear^.dX), aangle);
amShotgun: DrawRotated(sprHandShotgun, hx, hy, hwSign(Gear^.dX), aangle);
amDEagle: DrawRotated(sprHandDEagle, hx, hy, hwSign(Gear^.dX), aangle);
amSniperRifle: DrawRotatedF(sprSniperRifle, hx, hy, 0, hwSign(Gear^.dX), aangle);
amBlowTorch: DrawRotated(sprHandBlowTorch, hx, hy, hwSign(Gear^.dX), aangle);
amRCPlane: begin
DrawRotated(sprHandPlane, hx, hy, hwSign(Gear^.dX), 0);
defaultPos:= false
end;
amGirder: begin
DrawSpriteClipped(sprGirder,
sx-256,
sy-256,
LongInt(topY)+WorldDy,
LongInt(rightX)+WorldDx,
cWaterLine+WorldDy,
LongInt(leftX)+WorldDx);
end;
end;
case amt of
amAirAttack,
amMineStrike: DrawRotated(sprHandAirAttack, sx, hwRound(Gear^.Y) + WorldDy, hwSign(Gear^.dX), 0);
amPickHammer: DrawHedgehog(sx, sy,
hwSign(Gear^.dX),
1,
2,
0);
amBlowTorch: DrawHedgehog(sx, sy,
hwSign(Gear^.dX),
1,
3,
0);
amTeleport: DrawRotatedF(sprTeleport, sx, sy, 0, hwSign(Gear^.dX), 0);
amKamikaze: DrawHedgehog(sx, sy,
hwSign(Gear^.dX),
1,
5,
0);
amWhip: DrawRotatedF(sprWhip,
sx,
sy,
0,
hwSign(Gear^.dX),
0);
else
DrawHedgehog(sx, sy,
hwSign(Gear^.dX),
0,
4,
0);
HatVisible:= true;
with PHedgehog(Gear^.Hedgehog)^ do
if (HatTex <> nil)
and (HatVisibility > 0) then
DrawTextureF(HatTex,
HatVisibility,
sx,
hwRound(Gear^.Y) - 8 + WorldDy,
0,
hwSign(Gear^.dX),
32);
end;
case amt of
amBaseballBat: DrawRotated(sprHandBaseball,
hwRound(Gear^.X) + 1 - 4 * hwSign(Gear^.dX) + WorldDx,
hwRound(Gear^.Y) + 6 + WorldDy, hwSign(Gear^.dX), aangle);
end;
defaultPos:= false
end;
end else // not gstHHDriven
begin
if (Gear^.Damage > 0)
and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > _0_003) then
begin
DrawHedgehog(sx, sy,
hwSign(Gear^.dX),
2,
1,
Gear^.DirAngle);
defaultPos:= false
end else
if ((Gear^.State and gstHHJumping) <> 0) then
begin
DrawHedgehog(sx, sy,
hwSign(Gear^.dX)*m,
1,
1,
0);
defaultPos:= false
end;
end;
with PHedgehog(Gear^.Hedgehog)^ do
begin
if defaultPos then
begin
DrawRotatedF(sprHHIdle,
sx,
sy,
(RealTicks div 128 + Gear^.Pos) mod 19,
hwSign(Gear^.dX),
0);
HatVisible:= true;
end;
if HatVisible then
if HatVisibility < 1.0 then
HatVisibility:= HatVisibility + 0.2
else
else
if HatVisibility > 0.0 then
HatVisibility:= HatVisibility - 0.2;
if (HatTex <> nil)
and (HatVisibility > 0) then
if DefaultPos then
DrawTextureF(HatTex,
HatVisibility,
sx,
hwRound(Gear^.Y) - 8 + WorldDy,
(RealTicks div 128 + Gear^.Pos) mod 19,
hwSign(Gear^.dX),
32)
else
DrawTextureF(HatTex,
HatVisibility,
sx,
hwRound(Gear^.Y) - 8 + WorldDy,
0,
hwSign(Gear^.dX)*m,
32);
end;
if (Gear^.State and gstHHDriven) <> 0 then
begin
(* if (CurAmmoGear = nil) then
begin
amt:= CurrentHedgehog^.Ammo^[CurrentHedgehog^.CurSlot, CurrentHedgehog^.CurAmmo].AmmoType;
case amt of
amJetpack: DrawSprite(sprJetpack, sx-32, sy-32, 0);
end
end; *)
if CurAmmoGear <> nil then
begin
case CurAmmoGear^.Kind of
gtJetpack: begin
DrawSprite(sprJetpack, sx-32, sy-32, 0);
if (CurAmmoGear^.MsgParam and gm_Up) <> 0 then DrawSprite(sprJetpack, sx-32, sy-32, 1);
if (CurAmmoGear^.MsgParam and gm_Left) <> 0 then DrawSprite(sprJetpack, sx-32, sy-32, 2);
if (CurAmmoGear^.MsgParam and gm_Right) <> 0 then DrawSprite(sprJetpack, sx-32, sy-32, 3);
if CurAmmoGear^.Tex <> nil then DrawCentered(sx, sy - 40, CurAmmoGear^.Tex)
end;
end;
end
end;
with PHedgehog(Gear^.Hedgehog)^ do
begin
if ((Gear^.State and not gstWinner) = 0)
or (bShowFinger and ((Gear^.State and gstHHDriven) <> 0)) then
begin
t:= hwRound(Gear^.Y) - cHHRadius - 12 + WorldDy;
if (cTagsMask and 1) <> 0 then
begin
dec(t, HealthTagTex^.h + 2);
DrawCentered(hwRound(Gear^.X) + WorldDx, t, HealthTagTex)
end;
if (cTagsMask and 2) <> 0 then
begin
dec(t, NameTagTex^.h + 2);
DrawCentered(hwRound(Gear^.X) + WorldDx, t, NameTagTex)
end;
if (cTagsMask and 4) <> 0 then
begin
dec(t, Team^.NameTagTex^.h + 2);
DrawCentered(hwRound(Gear^.X) + WorldDx, t, Team^.NameTagTex)
end
end;
if (Gear^.State and gstHHDriven) <> 0 then // Current hedgehog
begin
if bShowFinger and ((Gear^.State and gstHHDriven) <> 0) then
DrawSprite(sprFinger, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 64 + WorldDy,
GameTicks div 32 mod 16);
if (Gear^.State and gstDrowning) = 0 then
if (Gear^.State and gstHHThinking) <> 0 then
DrawSprite(sprQuestion, hwRound(Gear^.X) - 10 + WorldDx, hwRound(Gear^.Y) - cHHRadius - 34 + WorldDy, 0)
end
end;
if Gear^.Invulnerable then
begin
DrawSprite(sprInvulnerable, sx - 24, sy - 24, 0);
end;
if cVampiric and
(CurrentHedgehog^.Gear <> nil) and
(CurrentHedgehog^.Gear = Gear) then
begin
DrawSprite(sprVampiric, sx - 24, sy - 24, 0);
end;
end;
procedure DrawGears;
var Gear, HHGear: PGear;
i: Longword;
roplen: LongInt;
procedure DrawRopeLine(X1, Y1, X2, Y2: LongInt);
var eX, eY, dX, dY: LongInt;
i, sX, sY, x, y, d: LongInt;
b: boolean;
begin
if (X1 = X2) and (Y1 = Y2) then
begin
//OutError('WARNING: zero length rope line!', false);
exit
end;
eX:= 0;
eY:= 0;
dX:= X2 - X1;
dY:= Y2 - Y1;
if (dX > 0) then sX:= 1
else
if (dX < 0) then
begin
sX:= -1;
dX:= -dX
end else sX:= dX;
if (dY > 0) then sY:= 1
else
if (dY < 0) then
begin
sY:= -1;
dY:= -dY
end else sY:= dY;
if (dX > dY) then d:= dX
else d:= dY;
x:= X1;
y:= Y1;
for i:= 0 to d do
begin
inc(eX, dX);
inc(eY, dY);
b:= false;
if (eX > d) then
begin
dec(eX, d);
inc(x, sX);
b:= true
end;
if (eY > d) then
begin
dec(eY, d);
inc(y, sY);
b:= true
end;
if b then
begin
inc(roplen);
if (roplen mod 4) = 0 then DrawSprite(sprRopeNode, x - 2, y - 2, 0)
end
end
end;
begin
Gear:= GearsList;
while Gear<>nil do
begin
case Gear^.Kind of
gtAmmo_Bomb: DrawRotated(sprBomb, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, Gear^.DirAngle);
gtRCPlane: if (Gear^.Tag = -1) then
DrawRotated(sprPlane, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, -1, DxDy2Angle(Gear^.dX, Gear^.dY) + 90)
else
DrawRotated(sprPlane, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy,0,DxDy2Angle(Gear^.dY, Gear^.dX));
gtBall: DrawRotatedf(sprBalls, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, Gear^.Tag,0, DxDy2Angle(Gear^.dY, Gear^.dX));
gtDrill: DrawRotated(sprDrill, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
gtHedgehog: DrawHH(Gear);
gtAmmo_Grenade: DrawRotated(sprGrenade, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
gtHealthTag: if Gear^.Tex <> nil then DrawCentered(hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, Gear^.Tex);
gtGrave: DrawSurfSprite(hwRound(Gear^.X) + WorldDx - 16, hwRound(Gear^.Y) + WorldDy - 16, 32, (GameTicks shr 7) and 7, PHedgehog(Gear^.Hedgehog)^.Team^.GraveTex);
gtUFO: DrawSprite(sprUFO, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 16 + WorldDy, (GameTicks shr 7) mod 4);
gtPickHammer: DrawSprite(sprPHammer, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 50 + LongInt(((GameTicks shr 5) and 1) * 2) + WorldDy, 0);
gtRope: begin
roplen:= 0;
if RopePoints.Count > 0 then
begin
i:= 0;
while i < Pred(RopePoints.Count) do
begin
DrawRopeLine(hwRound(RopePoints.ar[i].X) + WorldDx, hwRound(RopePoints.ar[i].Y) + WorldDy,
hwRound(RopePoints.ar[Succ(i)].X) + WorldDx, hwRound(RopePoints.ar[Succ(i)].Y) + WorldDy);
inc(i)
end;
DrawRopeLine(hwRound(RopePoints.ar[i].X) + WorldDx, hwRound(RopePoints.ar[i].Y) + WorldDy,
hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy);
DrawRopeLine(hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy,
hwRound(PHedgehog(Gear^.Hedgehog)^.Gear^.X) + WorldDx, hwRound(PHedgehog(Gear^.Hedgehog)^.Gear^.Y) + WorldDy);
DrawRotated(sprRopeHook, hwRound(RopePoints.ar[0].X) + WorldDx, hwRound(RopePoints.ar[0].Y) + WorldDy, 1, RopePoints.HookAngle)
end else
if Gear^.Elasticity.QWordValue > 0 then
begin
DrawRopeLine(hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy,
hwRound(PHedgehog(Gear^.Hedgehog)^.Gear^.X) + WorldDx, hwRound(PHedgehog(Gear^.Hedgehog)^.Gear^.Y) + WorldDy);
DrawRotated(sprRopeHook, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
end;
end;
gtSmokeTrace: if Gear^.State < 8 then DrawSprite(sprSmokeTrace, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, Gear^.State);
gtExplosion: DrawSprite(sprExplosion50, hwRound(Gear^.X) - 32 + WorldDx, hwRound(Gear^.Y) - 32 + WorldDy, Gear^.State);
gtMine: if ((Gear^.State and gstAttacking) = 0)or((Gear^.Timer and $3FF) < 420)
then DrawRotated(sprMineOff, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, Gear^.DirAngle)
else DrawRotated(sprMineOn, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, Gear^.DirAngle);
gtCase: case Gear^.Pos of
posCaseAmmo : begin
i:= (GameTicks shr 6) mod 64;
if i > 18 then i:= 0;
DrawSprite(sprCase, hwRound(Gear^.X) - 24 + WorldDx, hwRound(Gear^.Y) - 24 + WorldDy, i);
end;
posCaseHealth: begin
i:= ((GameTicks shr 6) + 38) mod 64;
if i > 13 then i:= 0;
DrawSprite(sprFAid, hwRound(Gear^.X) - 24 + WorldDx, hwRound(Gear^.Y) - 24 + WorldDy, i);
end;
posCaseUtility: begin
i:= (GameTicks shr 6) mod 70;
if i > 23 then i:= 0;
i:= i mod 12;
DrawSprite(sprUtility, hwRound(Gear^.X) - 24 + WorldDx, hwRound(Gear^.Y) - 24 + WorldDy, i);
end;
end;
gtDynamite: DrawSprite2(sprDynamite, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 25 + WorldDy, Gear^.Tag and 1, Gear^.Tag shr 1);
gtClusterBomb: DrawRotated(sprClusterBomb, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, Gear^.DirAngle);
gtCluster: DrawSprite(sprClusterParticle, hwRound(Gear^.X) - 8 + WorldDx, hwRound(Gear^.Y) - 8 + WorldDy, 0);
gtFlame: DrawSprite(sprFlame, hwRound(Gear^.X) - 8 + WorldDx, hwRound(Gear^.Y) - 8 + WorldDy, (GameTicks div 128 + LongWord(Gear^.Tag)) mod 8);
gtParachute: DrawSprite(sprParachute, hwRound(Gear^.X) - 24 + WorldDx, hwRound(Gear^.Y) - 48 + WorldDy, 0);
gtAirAttack: if Gear^.Tag > 0 then DrawSprite(sprAirplane, hwRound(Gear^.X) - SpritesData[sprAirplane].Width div 2 + WorldDx, hwRound(Gear^.Y) - SpritesData[sprAirplane].Height div 2 + WorldDy, 0)
else DrawSprite(sprAirplane, hwRound(Gear^.X) - SpritesData[sprAirplane].Width div 2 + WorldDx, hwRound(Gear^.Y) - SpritesData[sprAirplane].Height div 2 + WorldDy, 1);
gtAirBomb: DrawRotated(sprAirBomb, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
gtTeleport: begin
HHGear:= PHedgehog(Gear^.Hedgehog)^.Gear;
DrawRotatedF(sprTeleport, hwRound(Gear^.X) + 1 + WorldDx, hwRound(Gear^.Y) - 3 + WorldDy, Gear^.Pos, hwSign(HHGear^.dX), 0);
DrawRotatedF(sprTeleport, hwRound(HHGear^.X) + 1 + WorldDx, hwRound(HHGear^.Y) - 3 + WorldDy, 11 - Gear^.Pos, hwSign(HHGear^.dX), 0);
end;
gtSwitcher: DrawSprite(sprSwitch, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 56 + WorldDy, (GameTicks shr 6) mod 12);
gtTarget: DrawSprite(sprTarget, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 16 + WorldDy, 0);
gtMortar: DrawRotated(sprMortar, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
gtCake: if Gear^.Pos = 6 then
DrawRotatedf(sprCakeWalk, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, (GameTicks div 40) mod 6, hwSign(Gear^.dX), Gear^.DirAngle * hwSign(Gear^.dX) + 90)
else
DrawRotatedf(sprCakeDown, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 5 - Gear^.Pos, hwSign(Gear^.dX), 0);
gtSeduction: if Gear^.Pos >= 14 then DrawSprite(sprSeduction, hwRound(Gear^.X) - 16 + WorldDx, hwRound(Gear^.Y) - 16 + WorldDy, 0);
gtWatermelon: DrawRotatedf(sprWatermelon, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, 0, Gear^.DirAngle);
gtMelonPiece: DrawRotatedf(sprWatermelon, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 1, 0, Gear^.DirAngle);
gtHellishBomb: DrawRotated(sprHellishBomb, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, 0, Gear^.DirAngle);
gtEvilTrace: if Gear^.State < 8 then DrawSprite(sprEvilTrace, hwRound(Gear^.X) + WorldDx, hwRound(Gear^.Y) + WorldDy, Gear^.State);
end;
if Gear^.RenderTimer and (Gear^.Tex <> nil) then DrawCentered(hwRound(Gear^.X) + 8 + WorldDx, hwRound(Gear^.Y) + 8 + WorldDy, Gear^.Tex);
Gear:= Gear^.NextGear
end;
end;
procedure FreeGearsList;
var t, tt: PGear;
begin
tt:= GearsList;
GearsList:= nil;
while tt <> nil do
begin
t:= tt;
tt:= tt^.NextGear;
Dispose(t)
end;
end;
procedure AddMiscGears;
var i: LongInt;
Gear: PGear;
begin
AddGear(0, 0, gtATStartGame, 0, _0, _0, 2000);
if ((GameFlags and gfForts) = 0) and ((GameFlags and gfMines) <> 0) then
for i:= 0 to Pred(cLandAdditions) do
begin
Gear:= AddGear(0, 0, gtMine, 0, _0, _0, 0);
Gear^.TriggerId:= i + 1;
FindPlace(Gear, false, 0, LAND_WIDTH);
{ if(Gear <> nil) then
ParseCommand('addtrig s' + inttostr(Gear^.TriggerId) + ' 1 5 11 ' +
inttostr(hwRound(Gear^.X)) + ' ' + inttostr(hwRound(Gear^.Y)) +
' ' + inttostr(Gear^.TriggerId), true);
} end;
if (GameFlags and gfLowGravity) <> 0 then
cGravity:= cMaxWindSpeed / 2;
if (GameFlags and gfVampiric) <> 0 then
cVampiric:= true;
Gear:= GearsList;
if (GameFlags and gfInvulnerable) <> 0 then
while Gear <> nil do
begin
Gear^.Invulnerable:= true; // this is only checked on hogs right now, so no need for gear type check
Gear:= Gear^.NextGear
end;
if (GameFlags and gfLaserSight) <> 0 then
cLaserSighting:= true;
if (GameFlags and gfArtillery) <> 0 then
cArtillery:= true
end;
procedure doMakeExplosion(X, Y, Radius: LongInt; Mask: LongWord);
var Gear: PGear;
dmg, dmgRadius: LongInt;
begin
TargetPoint.X:= NoPointX;
{$IFDEF DEBUGFILE}if Radius > 4 then AddFileLog('Explosion: at (' + inttostr(x) + ',' + inttostr(y) + ')');{$ENDIF}
if (Radius > 10) then AddGear(X, Y, gtExplosion, 0, _0, _0, 0);
if (Mask and EXPLAutoSound) <> 0 then PlaySound(sndExplosion, false, nil);
if (Mask and EXPLAllDamageInRadius) = 0 then
dmgRadius:= Radius shl 1
else
dmgRadius:= Radius;
Gear:= GearsList;
while Gear <> nil do
begin
dmg:= dmgRadius + cHHRadius div 2 - hwRound(Distance(Gear^.X - int2hwFloat(X), Gear^.Y - int2hwFloat(Y)));
if (dmg > 1) and
((Gear^.State and gstNoDamage) = 0) then
begin
dmg:= modifyDamage(min(dmg div 2, Radius));
case Gear^.Kind of
gtHedgehog,
gtMine,
gtCase,
gtTarget,
gtFlame: begin
//{$IFDEF DEBUGFILE}AddFileLog('Damage: ' + inttostr(dmg));{$ENDIF}
if (Mask and EXPLNoDamage) = 0 then
begin
if not Gear^.Invulnerable then
ApplyDamage(Gear, dmg)
else
Gear^.State:= Gear^.State or gstWinner;
end;
if ((Mask and EXPLDoNotTouchHH) = 0) or (Gear^.Kind <> gtHedgehog) then
begin
DeleteCI(Gear);
Gear^.dX:= Gear^.dX + SignAs(_0_005 * dmg + cHHKick, Gear^.X - int2hwFloat(X));
Gear^.dY:= Gear^.dY + SignAs(_0_005 * dmg + cHHKick, Gear^.Y - int2hwFloat(Y));
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;
FollowGear:= Gear
end;
end;
gtGrave: begin
Gear^.dY:= - _0_004 * dmg;
Gear^.Active:= true;
end;
end;
end;
Gear:= Gear^.NextGear
end;
if (Mask and EXPLDontDraw) = 0 then
if (GameFlags and gfSolidLand) = 0 then DrawExplosion(X, Y, Radius);
uAIMisc.AwareOfExplosion(0, 0, 0)
end;
procedure ShotgunShot(Gear: PGear);
var t: PGear;
dmg: LongInt;
begin
Gear^.Radius:= cShotgunRadius;
t:= GearsList;
while t <> nil do
begin
dmg:= modifyDamage(min(Gear^.Radius + t^.Radius - hwRound(Distance(Gear^.X - t^.X, Gear^.Y - t^.Y)), 25));
if dmg > 0 then
case t^.Kind of
gtHedgehog,
gtMine,
gtCase,
gtTarget: begin
if (not t^.Invulnerable) then
ApplyDamage(t, dmg)
else
Gear^.State:= Gear^.State or gstWinner;
DeleteCI(t);
t^.dX:= t^.dX + Gear^.dX * dmg * _0_01 + SignAs(cHHKick, Gear^.dX);
t^.dY:= t^.dY + Gear^.dY * dmg * _0_01;
t^.State:= t^.State or gstMoving;
t^.Active:= true;
FollowGear:= t
end;
gtGrave: begin
t^.dY:= - _0_1;
t^.Active:= true
end;
end;
t:= t^.NextGear
end;
if (GameFlags and gfSolidLand) = 0 then DrawExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cShotgunRadius)
end;
procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt);
var t: PGearArray;
i: LongInt;
begin
t:= CheckGearsCollision(Ammo);
i:= t^.Count;
Damage:= modifyDamage(Damage);
while i > 0 do
begin
dec(i);
if (t^.ar[i]^.State and gstNoDamage) = 0 then
case t^.ar[i]^.Kind of
gtHedgehog,
gtMine,
gtTarget,
gtCase: begin
if (Ammo^.Kind = gtDrill) then begin Ammo^.Timer:= 0; exit; end;
if (not t^.ar[i]^.Invulnerable) then
ApplyDamage(t^.ar[i], Damage)
else
t^.ar[i]^.State:= t^.ar[i]^.State or gstWinner;
DeleteCI(t^.ar[i]);
t^.ar[i]^.dX:= Ammo^.dX * Power * _0_01;
t^.ar[i]^.dY:= Ammo^.dY * Power * _0_01;
t^.ar[i]^.Active:= true;
t^.ar[i]^.State:= t^.ar[i]^.State or gstMoving;
if TestCollisionXwithGear(t^.ar[i], hwSign(t^.ar[i]^.dX)) then
begin
if not (TestCollisionXwithXYShift(t^.ar[i], _0, -3, hwSign(t^.ar[i]^.dX))
or TestCollisionYwithGear(t^.ar[i], -1)) then t^.ar[i]^.Y:= t^.ar[i]^.Y - _1;
if not (TestCollisionXwithXYShift(t^.ar[i], _0, -2, hwSign(t^.ar[i]^.dX))
or TestCollisionYwithGear(t^.ar[i], -1)) then t^.ar[i]^.Y:= t^.ar[i]^.Y - _1;
if not (TestCollisionXwithXYShift(t^.ar[i], _0, -1, hwSign(t^.ar[i]^.dX))
or TestCollisionYwithGear(t^.ar[i], -1)) then t^.ar[i]^.Y:= t^.ar[i]^.Y - _1;
end;
FollowGear:= t^.ar[i]
end;
end
end;
SetAllToActive
end;
procedure AssignHHCoords;
var i, t, p, j: LongInt;
ar: array[0..Pred(cMaxHHs)] of PHedgehog;
Count: Longword;
begin
if (GameFlags and (gfForts or gfDivideTeams)) <> 0 then
begin
t:= 0;
TryDo(ClansCount = 2, 'More or less than 2 clans on map in divided teams mode!', true);
for p:= 0 to 1 do
begin
with ClansArray[p]^ do
for j:= 0 to Pred(TeamsNumber) do
with Teams[j]^ do
for i:= 0 to cMaxHHIndex do
with Hedgehogs[i] do
if (Gear <> nil) and (Gear^.X.QWordValue = 0) then
begin
FindPlace(Gear, false, t, t + LAND_WIDTH div 2);// could make Gear == nil
if Gear <> nil then
begin
Gear^.Pos:= GetRandom(49);
Gear^.dX.isNegative:= p = 1;
end
end;
t:= LAND_WIDTH div 2
end
end else // mix hedgehogs
begin
Count:= 0;
for p:= 0 to Pred(TeamsCount) do
with TeamsArray[p]^ do
begin
for i:= 0 to cMaxHHIndex do
with Hedgehogs[i] do
if (Gear <> nil) and (Gear^.X.QWordValue = 0) then
begin
ar[Count]:= @Hedgehogs[i];
inc(Count)
end;
end;
// unC0Rr, while it is true user can watch value on map screen, IMO this (and check above) should be enforced in UI
// - is there a good place to put values for the different widgets to check? Right now they are kind of disconnected.
//it would be nice if divide teams, forts mode and hh per map could all be checked by the team widget, or maybe disable start button
TryDo(Count <= MaxHedgehogs, 'Too many hedgehogs for this map! (max # is ' + inttostr(MaxHedgehogs) + ')', true);
while (Count > 0) do
begin
i:= GetRandom(Count);
FindPlace(ar[i]^.Gear, false, 0, LAND_WIDTH);
if ar[i]^.Gear <> nil then
begin
ar[i]^.Gear^.dX.isNegative:= hwRound(ar[i]^.Gear^.X) > LAND_WIDTH div 2;
ar[i]^.Gear^.Pos:= GetRandom(19)
end;
ar[i]:= ar[Count - 1];
dec(Count)
end
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;
{procedure AmmoFlameWork(Ammo: PGear);
var t: PGear;
begin
t:= GearsList;
while t <> nil do
begin
if (t^.Kind = gtHedgehog) and (t^.Y < Ammo^.Y) then
if not (hwSqr(Ammo^.X - t^.X) + hwSqr(Ammo^.Y - t^.Y - int2hwFloat(cHHRadius)) * 2 > _2) then
begin
ApplyDamage(t, 5);
t^.dX:= t^.dX + (t^.X - Ammo^.X) * _0_02;
t^.dY:= - _0_25;
t^.Active:= true;
DeleteCI(t);
FollowGear:= t
end;
t:= t^.NextGear
end;
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;
function CountGears(Kind: TGearType): Longword;
var t: PGear;
Result: Longword;
begin
Result:= 0;
t:= GearsList;
while t <> nil do
begin
if t^.Kind = Kind then inc(Result);
t:= t^.NextGear
end;
CountGears:= Result
end;
procedure SpawnBoxOfSmth;
var t: LongInt;
i: TAmmoType;
begin
if (cCaseFactor = 0) or
(CountGears(gtCase) >= 5) or
(getrandom(cCaseFactor) <> 0) then exit;
FollowGear:= nil;
if shoppa then // FIXME - TEMPORARY REMOVE WHEN CRATE PROBABILITY IS ADDED
t:= 7
else
t:= getrandom(20);
//case getrandom(20) of
case t of
0..6: begin
FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
FollowGear^.Health:= 25;
FollowGear^.Pos:= posCaseHealth;
AddCaption(GetEventString(eidNewHealthPack), $FFFFFF, capgrpGameState);
end;
7..13: begin
t:= 0;
for i:= Low(TAmmoType) to High(TAmmoType) do
if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then
inc(t, Ammoz[i].Probability);
if (t > 0) then
begin
FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
t:= GetRandom(t);
i:= Low(TAmmoType);
if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then
dec(t, Ammoz[i].Probability);
while t >= 0 do
begin
inc(i);
if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then
dec(t, Ammoz[i].Probability)
end;
FollowGear^.Pos:= posCaseAmmo;
FollowGear^.State:= Longword(i);
AddCaption(GetEventString(eidNewAmmoPack), $FFFFFF, capgrpGameState);
end
end;
14..19: begin
t:= 0;
for i:= Low(TAmmoType) to High(TAmmoType) do
if (Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0 then
inc(t, Ammoz[i].Probability);
if (t > 0) then
begin
FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0);
t:= GetRandom(t);
i:= Low(TAmmoType);
if (Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0 then
dec(t, Ammoz[i].Probability);
while t >= 0 do
begin
inc(i);
if (Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0 then
dec(t, Ammoz[i].Probability)
end;
FollowGear^.Pos:= posCaseUtility;
FollowGear^.State:= Longword(i);
AddCaption(GetEventString(eidNewUtilityPack), $FFFFFF, capgrpGameState);
end
end;
end;
// handles case of no ammo or utility crates - considered also placing booleans in uAmmos and altering probabilities
if (FollowGear <> nil) then
begin
FindPlace(FollowGear, true, 0, LAND_WIDTH);
if (FollowGear <> nil) then
PlaySound(sndReinforce, false, CurrentTeam^.voicepack)
end
end;
procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt);
function CountNonZeroz(x, y, r: LongInt): LongInt;
var i: LongInt;
Result: LongInt;
begin
Result:= 0;
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 inc(Result);
CountNonZeroz:= Result
end;
var x: LongInt;
y, sy: LongInt;
ar: array[0..511] of TPoint;
ar2: array[0..1023] of TPoint;
cnt, cnt2: Longword;
delta: LongInt;
begin
delta:= 250;
cnt2:= 0;
repeat
x:= Left + LongInt(GetRandom(Delta));
repeat
inc(x, Delta);
cnt:= 0;
if topY > 1024 then
y:= 1024-Gear^.Radius * 2
else
y:= topY-Gear^.Radius * 2;
while y < LAND_HEIGHT do
begin
repeat
inc(y, 2);
until (y >= LAND_HEIGHT) or (CountNonZeroz(x, y, Gear^.Radius - 1) = 0);
sy:= y;
repeat
inc(y);
until (y >= LAND_HEIGHT) or (CountNonZeroz(x, y, Gear^.Radius - 1) <> 0);
if (y - sy > Gear^.Radius * 2)
and (y < LAND_HEIGHT)
and (CheckGearsNear(x, y - Gear^.Radius, [gtHedgehog, gtMine, gtCase], 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 then
with ar2[GetRandom(cnt2)] do
begin
Gear^.X:= int2hwFloat(x);
Gear^.Y:= int2hwFloat(y);
{$IFDEF DEBUGFILE}
AddFileLog('Assigned Gear coordinates (' + inttostr(x) + ',' + inttostr(y) + ')');
{$ENDIF}
end
else
begin
OutError('Can''t find place for Gear', false);
DeleteGear(Gear);
Gear:= nil
end
end;
initialization
finalization
FreeGearsList;
end.