Engine:
authorsmxx
Fri, 28 May 2010 16:21:54 +0000
changeset 3483 54ff8cbabaa6
parent 3482 106d1f983b48
child 3484 3c65b1d979cd
Engine: * New weapon attributes: ejectX/ejectY: Offset to the hedgehog (center of hand graphic) where the projectile(s) will be spawned * Changed Laser Sight to origin from the weapon instead of the hedgehog (needs some adjustments while walking/jumping) * Changed most weapons to no longer shoot from the hedgehog's center and use the weapon's visible position instead (might require some testing and adjustments) * Silenced the small Piano explosions
hedgewars/GSHandlers.inc
hedgewars/GearDrawing.inc
hedgewars/HHHandlers.inc
hedgewars/uConsts.pas
hedgewars/uGears.pas
hedgewars/uWorld.pas
--- a/hedgewars/GSHandlers.inc	Fri May 28 13:04:11 2010 +0000
+++ b/hedgewars/GSHandlers.inc	Fri May 28 16:21:54 2010 +0000
@@ -3479,11 +3479,9 @@
         begin
             r0 := GetRandom(21);
             r1 := GetRandom(21);
+            doMakeExplosion(hwRound(Gear^.X) - 30 - r0, hwRound(Gear^.Y) + 40, 40 + r1, 0);
+            doMakeExplosion(hwRound(Gear^.X) + 30 + r1, hwRound(Gear^.Y) + 40, 40 + r0, 0);
             doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 80 + r0, EXPLAutoSound);
-            doMakeExplosion(hwRound(Gear^.X) - 30 - r0, hwRound(Gear^.Y) + 40, 40 + r1,
-            EXPLAutoSound);
-            doMakeExplosion(hwRound(Gear^.X) + 30 + r1, hwRound(Gear^.Y) + 40, 40 + r0,
-            EXPLAutoSound);
             Gear^.dY := -_1;
             Gear^.Pos := Gear^.Pos + 1;
         end
--- a/hedgewars/GearDrawing.inc	Fri May 28 13:04:11 2010 +0000
+++ b/hedgewars/GearDrawing.inc	Fri May 28 16:21:54 2010 +0000
@@ -2,7 +2,7 @@
 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, hAngle: real;  // laser, change
+    dx, dy, ax, ay, aAngle, dAngle, hAngle, lx, ly: real;  // laser, change
     defaultPos, HatVisible: boolean;
     VertexBuffer: array [0..1] of TVertex2f;
     HH: PHedgehog;
@@ -77,13 +77,11 @@
    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);
+        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;
+            lx:= hwRound(Gear^.X) + GetLaunchX(HH^.Ammo^[HH^.CurSlot, HH^.CurAmmo].AmmoType, hwSign(Gear^.dX), Gear^.Angle);
+            ly:= hwRound(Gear^.Y) + GetLaunchY(HH^.Ammo^[HH^.CurSlot, HH^.CurAmmo].AmmoType, Gear^.Angle);
 
             ax:= dx * 4;
             ay:= dy * 4;
@@ -130,13 +128,13 @@
                 end;
             end;
         // draw crosshair
-        cx:= Round(hwRound(Gear^.X) + dx * 80);
-        cy:= Round(hwRound(Gear^.Y) + dy * 80);
+        cx:= Round(hwRound(Gear^.X) + dx * 80 + GetLaunchX(HH^.Ammo^[HH^.CurSlot, HH^.CurAmmo].AmmoType, hwSign(Gear^.dX), Gear^.Angle));
+        cy:= Round(hwRound(Gear^.Y) + dy * 80 + GetLaunchY(HH^.Ammo^[HH^.CurSlot, HH^.CurAmmo].AmmoType, Gear^.Angle));
         DrawRotatedTex(HH^.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;
+    hx:= hwRound(Gear^.X) + 8 * hwSign(Gear^.dX) + WorldDx;
     hy:= hwRound(Gear^.Y) - 2 + WorldDy;
     aangle:= Gear^.Angle * 180 / cMaxAngle - 90;
 
--- a/hedgewars/HHHandlers.inc	Fri May 28 13:04:11 2010 +0000
+++ b/hedgewars/HHHandlers.inc	Fri May 28 16:21:54 2010 +0000
@@ -102,7 +102,7 @@
 
 
 procedure Attack(Gear: PGear);
-var xx, yy: hwFloat;
+var xx, yy, lx, ly: hwFloat;
     tmpGear: PVisualGear;
 begin
 bShowFinger:= false;
@@ -139,69 +139,72 @@
         xx:= SignAs(AngleSin(Angle), dX);
         yy:= -AngleCos(Angle);
 
+        lx:= X + int2hwfloat(round(GetLaunchX(Ammo^[CurSlot, CurAmmo].AmmoType, hwSign(dX), Angle)));
+        ly:= Y + int2hwfloat(round(GetLaunchY(Ammo^[CurSlot, CurAmmo].AmmoType, Angle)));
+
         if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) then xx:= - xx;
         if Ammo^[CurSlot, CurAmmo].AttackVoice <> sndNone then
            PlaySound(Ammo^[CurSlot, CurAmmo].AttackVoice, CurrentTeam^.voicepack);
              case Ammo^[CurSlot, CurAmmo].AmmoType of
-                      amGrenade: FollowGear:= AddGear(hwRound(X), hwRound(Y), gtAmmo_Bomb,    0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, Ammo^[CurSlot, CurAmmo].Timer);
-                      amMolotov: FollowGear:= AddGear(hwRound(X), hwRound(Y), gtMolotov,      0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
-                  amClusterBomb: FollowGear:= AddGear(hwRound(X), hwRound(Y), gtClusterBomb,  0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, Ammo^[CurSlot, CurAmmo].Timer);
-                      amGasBomb: FollowGear:= AddGear(hwRound(X), hwRound(Y), gtGasBomb,    0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, Ammo^[CurSlot, CurAmmo].Timer);
-                      amBazooka: FollowGear:= AddGear(hwRound(X), hwRound(Y), gtAmmo_Grenade, 0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
-                          amBee: FollowGear:= AddGear(hwRound(X), hwRound(Y), gtBee,          0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
+                      amGrenade: FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtAmmo_Bomb,    0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, Ammo^[CurSlot, CurAmmo].Timer);
+                      amMolotov: FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtMolotov,      0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
+                  amClusterBomb: FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtClusterBomb,  0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, Ammo^[CurSlot, CurAmmo].Timer);
+                      amGasBomb: FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtGasBomb,    0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, Ammo^[CurSlot, CurAmmo].Timer);
+                      amBazooka: FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtAmmo_Grenade, 0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
+                          amBee: FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtBee,          0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
                       amShotgun: begin
                                  PlaySound(sndShotgunReload);
-                                 CurAmmoGear:= AddGear(hwRound(X), hwRound(Y), gtShotgunShot,  0, xx * _0_5, yy * _0_5, 0);
+                                 CurAmmoGear:= AddGear(hwRound(lx), hwRound(ly), gtShotgunShot,  0, xx * _0_5, yy * _0_5, 0);
                                  end;
-                   amPickHammer: CurAmmoGear:= AddGear(hwRound(Gear^.X), hwRound(Gear^.Y) + cHHRadius, gtPickHammer, 0, _0, _0, 0);
+                   amPickHammer: CurAmmoGear:= AddGear(hwRound(lx), hwRound(ly) + cHHRadius, gtPickHammer, 0, _0, _0, 0);
                          amSkip: ParseCommand('/skip', true);
-                         amRope: CurAmmoGear:= AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtRope, 0, xx, yy, 0);
-                         amMine: AddGear(hwRound(X) + hwSign(dX) * 7, hwRound(Y), gtMine, gstWait, SignAs(_0_02, dX), _0, 3000);
-                       amDEagle: CurAmmoGear:= AddGear(hwRound(X + xx * cHHRadius), hwRound(Y + yy * cHHRadius), gtDEagleShot, 0, xx * _0_5, yy * _0_5, 0);
-                      amSineGun: CurAmmoGear:= AddGear(hwRound(X + xx * cHHRadius), hwRound(Y + yy * cHHRadius), gtSineGunShot, 0, xx * _0_5, yy * _0_5, 0);
-                    amPortalGun: AddGear(hwRound(X + xx * cHHRadius), hwRound(Y + yy * cHHRadius), gtPortal, 0, xx * _0_6, yy * _0_6, 0);
+                         amRope: CurAmmoGear:= AddGear(hwRound(lx), hwRound(ly), gtRope, 0, xx, yy, 0);
+                         amMine: AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtMine, gstWait, SignAs(_0_02, dX), _0, 3000);
+                       amDEagle: CurAmmoGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtDEagleShot, 0, xx * _0_5, yy * _0_5, 0);
+                      amSineGun: CurAmmoGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtSineGunShot, 0, xx * _0_5, yy * _0_5, 0);
+                    amPortalGun: AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtPortal, 0, xx * _0_6, yy * _0_6, 0);
                   amSniperRifle: begin
                                  PlaySound(sndSniperReload);
-                                 CurAmmoGear:= AddGear(hwRound(X + xx * cHHRadius), hwRound(Y + yy * cHHRadius), gtSniperRifleShot, 0, xx * _0_5, yy * _0_5, 0);
+                                 CurAmmoGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtSniperRifleShot, 0, xx * _0_5, yy * _0_5, 0);
                                  end;
-                     amDynamite: AddGear(hwRound(X) + hwSign(dX) * 7, hwRound(Y), gtDynamite, 0, SignAs(_0_03, dX), _0, 5000);
-                    amFirePunch: CurAmmoGear:= AddGear(hwRound(X) + hwSign(dX) * 10, hwRound(Y), gtFirePunch, 0, xx, _0, 0);
+                     amDynamite: AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtDynamite, 0, SignAs(_0_03, dX), _0, 5000);
+                    amFirePunch: CurAmmoGear:= AddGear(hwRound(lx) + hwSign(dX) * 10, hwRound(ly), gtFirePunch, 0, xx, _0, 0);
                          amWhip: begin
-                                 CurAmmoGear:= AddGear(hwRound(X) + hwSign(dX) * 10, hwRound(Y), gtWhip, 0, SignAs(_1, dX), - _0_8, 0);
+                                 CurAmmoGear:= AddGear(hwRound(lx) + hwSign(dX) * 10, hwRound(ly), gtWhip, 0, SignAs(_1, dX), - _0_8, 0);
                                  PlaySound(sndWhipCrack)
                                  end;
                   amBaseballBat: begin
-                                 CurAmmoGear:= AddGear(hwRound(X) + hwSign(dX) * 10, hwRound(Y), gtShover, gsttmpFlag, xx * _0_5, yy * _0_5, 0);
+                                 CurAmmoGear:= 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: CurAmmoGear:= AddGear(hwRound(X), hwRound(Y), gtParachute, 0, _0, _0, 0);
+                    amParachute: CurAmmoGear:= AddGear(hwRound(lx), hwRound(ly), gtParachute, 0, _0, _0, 0);
                     // we save Ammo^[CurSlot, CurAmmo].Pos (in this case: cursor direction) by using it as (otherwise irrelevant) X value of the new gear.
                     amAirAttack: AddGear(Ammo^[CurSlot, CurAmmo].Pos, 0, gtAirAttack, 0, _0, _0, 0);
                    amMineStrike: AddGear(Ammo^[CurSlot, CurAmmo].Pos, 0, gtAirAttack, 1, _0, _0, 0);
-                    amBlowTorch: CurAmmoGear:= AddGear(hwRound(X), hwRound(Y), gtBlowTorch, 0, SignAs(_0_5, dX), _0, 0);
+                    amBlowTorch: CurAmmoGear:= AddGear(hwRound(lx), hwRound(ly), gtBlowTorch, 0, SignAs(_0_5, dX), _0, 0);
                        amGirder: CurAmmoGear:= AddGear(0, 0, gtGirder, Ammo^[CurSlot, CurAmmo].Pos, _0, _0, 0);
                      amTeleport: CurAmmoGear:= AddGear(Ammo^[CurSlot, CurAmmo].Pos, 0, gtTeleport, 0, _0, _0, 0);
-                       amSwitch: CurAmmoGear:= AddGear(hwRound(X), hwRound(Y), gtSwitcher, 0, _0, _0, 0);
+                       amSwitch: CurAmmoGear:= AddGear(hwRound(lx), hwRound(ly), gtSwitcher, 0, _0, _0, 0);
                        amMortar: begin
-                playSound(sndMortar);
-                FollowGear:= AddGear(hwRound(X), hwRound(Y), gtMortar,  0, xx*cMaxPower/cPowerDivisor, yy*cMaxPower/cPowerDivisor, 0);
-                 end;
+                                 playSound(sndMortar);
+                                 FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtMortar,  0, xx*cMaxPower/cPowerDivisor, yy*cMaxPower/cPowerDivisor, 0);
+                                 end;
                       amRCPlane: begin
-                                 CurAmmoGear:= AddGear(hwRound(X), hwRound(Y), gtRCPlane,  0, xx * cMaxPower / cPowerDivisor / 4, yy * cMaxPower / cPowerDivisor / 4, 0);
+                                 CurAmmoGear:= AddGear(hwRound(lx), hwRound(ly), gtRCPlane,  0, xx * cMaxPower / cPowerDivisor / 4, yy * cMaxPower / cPowerDivisor / 4, 0);
                                  CurAmmoGear^.SoundChannel:= LoopSound(sndRCPlane, nil)
                                  end;
-                       amKamikaze: CurAmmoGear:= AddGear(hwRound(X), hwRound(Y), gtKamikaze, 0, xx * _0_5, yy * _0_5, 0);
-                         amCake: CurAmmoGear:= AddGear(hwRound(X) + hwSign(dX) * 3, hwRound(Y), gtCake, 0, xx, _0, 0);
-                    amSeduction: CurAmmoGear:= AddGear(hwRound(X + xx * cHHRadius * 2), hwRound(Y + yy * cHHRadius * 2), gtSeduction, 0, xx * _0_4, yy * _0_4, 0);
-                   amWatermelon: FollowGear:= AddGear(hwRound(X), hwRound(Y), gtWatermelon,  0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, Ammo^[CurSlot, CurAmmo].Timer);
-                  amHellishBomb: FollowGear:= AddGear(hwRound(X), hwRound(Y), gtHellishBomb,    0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
+                       amKamikaze: CurAmmoGear:= AddGear(hwRound(lx), hwRound(ly), gtKamikaze, 0, xx * _0_5, yy * _0_5, 0);
+                         amCake: CurAmmoGear:= AddGear(hwRound(lx) + hwSign(dX) * 3, hwRound(ly), gtCake, 0, xx, _0, 0);
+                    amSeduction: CurAmmoGear:= AddGear(hwRound(lx + xx * cHHRadius * 2), hwRound(ly + yy * cHHRadius * 2), gtSeduction, 0, xx * _0_4, yy * _0_4, 0);
+                   amWatermelon: FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtWatermelon,  0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, Ammo^[CurSlot, CurAmmo].Timer);
+                  amHellishBomb: FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtHellishBomb,    0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
                        amNapalm: AddGear(Ammo^[CurSlot, CurAmmo].Pos, 0, gtAirAttack, 2, _0, _0, 0);
-                        amDrill: FollowGear:= AddGear(hwRound(X), hwRound(Y), gtDrill, 0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
-                      amBallgun: CurAmmoGear:= AddGear(hwRound(X), hwRound(Y), gtBallgun,  0, xx * _0_5, yy * _0_5, 0);
-                    amJetpack: CurAmmoGear:= AddGear(hwRound(X), hwRound(Y), gtJetpack, 0, _0, _0, 0);
+                        amDrill: FollowGear:= AddGear(hwRound(lx), hwRound(ly), gtDrill, 0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
+                      amBallgun: CurAmmoGear:= AddGear(hwRound(lx), hwRound(ly), gtBallgun,  0, xx * _0_5, yy * _0_5, 0);
+                    amJetpack: CurAmmoGear:= AddGear(hwRound(lx), hwRound(ly), gtJetpack, 0, _0, _0, 0);
                     amBirdy: begin
                              PlaySound(sndWhistle);
-                             CurAmmoGear:= AddGear(hwRound(X), hwRound(Y) - 32, gtBirdy, 0, _0, _0, 0);
+                             CurAmmoGear:= AddGear(hwRound(lx), hwRound(ly) - 32, gtBirdy, 0, _0, _0, 0);
                              end;
                       amLowGravity: begin
                                     PlaySound(sndLowGravity);
@@ -211,7 +214,10 @@
                       amInvulnerable: Invulnerable:= true;
                       amExtraTime: TurnTimeLeft:= TurnTimeLeft + 30000;
                       amLaserSight: cLaserSighting:= true;
-                      amVampiric: cVampiric:= true;
+                      amVampiric: begin
+                                  PlaySound(sndOw1, Team^.voicepack);
+                                  cVampiric:= true;
+                                  end;
                       amPiano: begin
                                // Tuck the hedgehog away until the piano attack is completed
                                Unplaced:= true;
@@ -220,7 +226,7 @@
                                FollowGear:= AddGear(TargetPoint.X, 0, gtPiano, 0, _0, _0, 0);
                                PauseMusic
                                end;
-                      amFlamethrower: CurAmmoGear:= AddGear(hwRound(X), hwRound(Y), gtFlamethrower,  0, xx * _0_5, yy * _0_5, 0);
+                      amFlamethrower: CurAmmoGear:= AddGear(hwRound(lx), hwRound(ly), gtFlamethrower,  0, xx * _0_5, yy * _0_5, 0);
                   end;
 
         uStats.AmmoUsed(Ammo^[CurSlot, CurAmmo].AmmoType);
--- a/hedgewars/uConsts.pas	Fri May 28 13:04:11 2010 +0000
+++ b/hedgewars/uConsts.pas	Fri May 28 16:21:54 2010 +0000
@@ -892,6 +892,7 @@
             SkipTurns: Longword;
             PosCount: Longword;
             PosSprite: TSprite;
+            ejectX, ejectY: Longint;
             end = (
             (NameId: sidNothing;
             NameTex: nil;
@@ -912,7 +913,9 @@
             isDamaging: false;
             SkipTurns: 9999;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Grenade
             (NameId: sidGrenade;
@@ -934,7 +937,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // ClusterBomb
             (NameId: sidClusterBomb;
@@ -956,7 +961,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Bazooka
             (NameId: sidBazooka;
@@ -978,7 +985,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 20;
+            ejectY: -6),
 
 // Bee
             (NameId: sidBee;
@@ -1000,7 +1009,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 16;
+            ejectY: 0),
 
 // Shotgun
             (NameId: sidShotgun;
@@ -1022,7 +1033,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 26;
+            ejectY: -6),
 
 // PickHammer
             (NameId: sidPickHammer;
@@ -1044,7 +1057,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Skip
             (NameId: sidSkip;
@@ -1066,7 +1081,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Rope
             (NameId: sidRope;
@@ -1092,7 +1109,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 20;
+            ejectY: -6),
 
 // Mine
             (NameId: sidMine;
@@ -1114,7 +1133,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // DEagle
             (NameId: sidDEagle;
@@ -1136,7 +1157,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 23;
+            ejectY: -6),
 
 // Dynamite
             (NameId: sidDynamite;
@@ -1158,7 +1181,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // FirePunch
             (NameId: sidFirePunch;
@@ -1180,7 +1205,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Whip
             (NameId: sidWhip;
@@ -1202,7 +1229,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // BaseballBat
             (NameId: sidBaseballBat;
@@ -1224,7 +1253,9 @@
             isDamaging: true;
             SkipTurns: 2;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Parachute
             (NameId: sidParachute;
@@ -1252,7 +1283,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // AirAttack
             (NameId: sidAirAttack;
@@ -1278,7 +1311,9 @@
             isDamaging: true;
             SkipTurns: 5;
             PosCount: 2;
-            PosSprite: sprAmAirplane),
+            PosSprite: sprAmAirplane;
+            ejectX: 0;
+            ejectY: 0),
 
 // MineStrike
             (NameId: sidMineStrike;
@@ -1304,7 +1339,9 @@
             isDamaging: true;
             SkipTurns: 5;
             PosCount: 2;
-            PosSprite: sprAmAirplane),
+            PosSprite: sprAmAirplane;
+            ejectX: 0;
+            ejectY: 0),
 
 // BlowTorch
             (NameId: sidBlowTorch;
@@ -1326,7 +1363,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Girder
             (NameId: sidGirder;
@@ -1352,7 +1391,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 8;
-            PosSprite: sprAmGirder),
+            PosSprite: sprAmGirder;
+            ejectX: 0;
+            ejectY: 0),
 
 // Teleport
             (NameId: sidTeleport;
@@ -1379,7 +1420,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 2;
-            PosSprite: sprAmTeleport),
+            PosSprite: sprAmTeleport;
+            ejectX: 0;
+            ejectY: 0),
 
 // Switch
             (NameId: sidSwitch;
@@ -1405,7 +1448,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Mortar
             (NameId: sidMortar;
@@ -1427,7 +1472,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 20;
+            ejectY: -6),
 
 // Kamikaze
             (NameId: sidKamikaze;
@@ -1449,7 +1496,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Cake
             (NameId: sidCake;
@@ -1471,7 +1520,9 @@
             isDamaging: true;
             SkipTurns: 4;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Seduction
             (NameId: sidSeduction;
@@ -1493,7 +1544,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Watermelon
             (NameId: sidWatermelon;
@@ -1515,7 +1568,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // HellishBomb ("Hellish Hand-Grenade")
             (NameId: sidHellishBomb;
@@ -1537,7 +1592,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Napalm
             (NameId: sidNapalm;
@@ -1563,7 +1620,9 @@
             isDamaging: true;
             SkipTurns: 7;
             PosCount: 2;
-            PosSprite: sprAmAirplane),
+            PosSprite: sprAmAirplane;
+            ejectX: 0;
+            ejectY: 0),
 
 // Drill ("Drill Rocket")
             (NameId: sidDrill;
@@ -1585,7 +1644,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprDrill),
+            PosSprite: sprDrill;
+            ejectX: 20;
+            ejectY: -6),
 
 // Ballgun
             (NameId: sidBallgun;
@@ -1607,7 +1668,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 20;
+            ejectY: -3),
 
 // RC-Plane
             (NameId: sidRCPlane;
@@ -1631,7 +1694,9 @@
             isDamaging: true;
             SkipTurns: 4;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // LowGravity
             (NameId: sidLowGravity;
@@ -1658,7 +1723,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // ExtraDamage
             (NameId: sidExtraDamage;
@@ -1685,7 +1752,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Invulnerable
             (NameId: sidInvulnerable;
@@ -1712,7 +1781,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // ExtraTime
             (NameId: sidExtraTime;
@@ -1739,7 +1810,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // LaserSight
             (NameId: sidLaserSight;
@@ -1766,7 +1839,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Vampiric
             (NameId: sidVampiric;
@@ -1793,7 +1868,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // SniperRifle
             (NameId: sidSniperRifle;
@@ -1815,7 +1892,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 40;
+            ejectY: -5),
 
 // Jetpack ("Flying Saucer")
             (NameId: sidJetpack;
@@ -1843,7 +1922,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Molotov
             (NameId: sidMolotov;
@@ -1865,7 +1946,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Birdy
             (NameId: sidBirdy;
@@ -1889,7 +1972,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // PortalGun
             (NameId: sidPortalGun;
@@ -1914,7 +1999,9 @@
             isDamaging: false;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 29;
+            ejectY: -15),
 
 // Piano
             (NameId: sidPiano;
@@ -1940,7 +2027,9 @@
             isDamaging: true;
             SkipTurns: 7;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // GasBomb
             (NameId: sidGasBomb;
@@ -1962,7 +2051,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
             
 // SineGun
             (NameId: sidSineGun;
@@ -1984,7 +2075,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater),
+            PosSprite: sprWater;
+            ejectX: 0;
+            ejectY: 0),
 
 // Flamethrower
             (NameId: sidFlamethrower;
@@ -2006,7 +2099,9 @@
             isDamaging: true;
             SkipTurns: 0;
             PosCount: 1;
-            PosSprite: sprWater)
+            PosSprite: sprWater;
+            ejectX: 20;
+            ejectY: -3)
             );
 
 
--- a/hedgewars/uGears.pas	Fri May 28 13:04:11 2010 +0000
+++ b/hedgewars/uGears.pas	Fri May 28 16:21:54 2010 +0000
@@ -128,6 +128,15 @@
 procedure HHSetWeapon(Gear: PGear); forward;
 procedure doStepCase(Gear: PGear); forward;
 
+function GetLaunchX(at: TAmmoType; dir: LongInt; angle: LongInt): LongInt;
+begin
+    GetLaunchX:= dir * (8 + hwRound(AngleSin(angle) * Ammoz[at].ejectX) + hwRound(AngleCos(angle) * Ammoz[at].ejectY))
+end;
+
+function GetLaunchY(at: TAmmoType; angle: LongInt): LongInt;
+begin
+    GetLaunchY:= hwRound(AngleSin(angle) * Ammoz[at].ejectY) - hwRound(AngleCos(angle) * Ammoz[at].ejectX) - 2;
+end;
 
 {$INCLUDE "GSHandlers.inc"}
 {$INCLUDE "HHHandlers.inc"}
--- a/hedgewars/uWorld.pas	Fri May 28 13:04:11 2010 +0000
+++ b/hedgewars/uWorld.pas	Fri May 28 16:21:54 2010 +0000
@@ -588,6 +588,7 @@
 DrawWater(255, 0);
 
 // Attack bar
+// TODO: Add weapon offset
 if CurrentTeam <> nil then
     case AttackBar of
 (*        1: begin