merge default gettext
authorsheepluva
Mon, 02 Nov 2015 06:58:08 +0100
branchgettext
changeset 11268 b14de8b74183
parent 11172 db1d14179b6c (current diff)
parent 11267 4c4f22cc3fa4 (diff)
merge default
CMakeLists.txt
hedgewars/uScript.pas
--- a/.hgtags	Mon Nov 02 06:23:46 2015 +0100
+++ b/.hgtags	Mon Nov 02 06:58:08 2015 +0100
@@ -64,3 +64,6 @@
 7e55468ffe384a3065524c483eb5e3cdb1658fd5 0.9.21-release
 7e55468ffe384a3065524c483eb5e3cdb1658fd5 fab746a3597e
 0f5961910e2712582b162abd08ae3eed330cc978 Nice one
+d9622394ec9c2974a84b9b4d9e6c0ac26c4060ff 0.9.22-RC
+0f5961910e2712582b162abd08ae3eed330cc978 Nice one
+0000000000000000000000000000000000000000 Nice one
--- a/CMakeLists.txt	Mon Nov 02 06:23:46 2015 +0100
+++ b/CMakeLists.txt	Mon Nov 02 06:58:08 2015 +0100
@@ -55,7 +55,7 @@
 set(CPACK_PACKAGE_VERSION_MAJOR 0)
 set(CPACK_PACKAGE_VERSION_MINOR 9)
 set(CPACK_PACKAGE_VERSION_PATCH 22)
-set(HEDGEWARS_PROTO_VER 50)
+set(HEDGEWARS_PROTO_VER 51)
 set(HEDGEWARS_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
 include(${CMAKE_MODULE_PATH}/revinfo.cmake)
 
--- a/ChangeLog.txt	Mon Nov 02 06:23:46 2015 +0100
+++ b/ChangeLog.txt	Mon Nov 02 06:58:08 2015 +0100
@@ -2,10 +2,11 @@
 * bugfixes
 
 0.9.21 -> 0.9.22
+ + New Weapon / Map object: AirMine (floating mine that will follow nearby hedgehogs)
  + Extensive changes to TechRacer: Variable terrain types, enhanced parameters, hwmap interpreter, fuel limiter, etc. 
  + Map previews can now take script parameters into account and preview waypoints in TechRacer
  + Added a couple new flags
- + Small improvements to the interface and in-game chat
+ + Various tweaks to the interface and in-game chat
  + Divided teams options will now just be ignored when more/less than 2 teams, instead of displaying a fatal error
  + Added 6 TechRacer maps to TechMaps
  + Added 3 SpeedShoppa Challenges: Shoppa Love, Ropes and Crates, The Customer is King
@@ -13,10 +14,20 @@
  + Improved "Art" theme.
  * Generated bridges/girders are now connected better to the land mass
  * Fixed rubberband sprite
+ * Fixed Wind-Indicator being wrong in certain situations
+ * Melon Bomb Pieces now bounce on Rubberband
+ * Reduced menu music volume
  * The game will now fallback to default voicepack if a team's voicepack is not locally installed. (Instead of rendering team voiceless)
  * Hammer now does more damage when the Extra-Damage utility is used
  * Many other bug fixes
 
+Lua-API:
+ + New map parameter: MapFeatureSize -- numeric representation of detail slider below map preview; use within onGameInit()/onPreviewInit()
+ + New function: SetMaxBuildDistance([ distInPx ]) -- specify how many pixels away a hedgehog can still place girders/etc. set to 0 for no limit; call with no param to reset to default
+ + New hook: onSuddenDeath() -- called by engine when sudden death begins
+ * Previously missing gear states are now available (gstSubmersible, gstFrozen and gstNoGravity)
+ * Fixed OnHogAttack giving the incorrect AmmoType (amNothing) under certain conditions
+
 0.9.20 -> 0.9.21:
  + New type of randomly generated maps: Perlin Maps.
  + Old Random generated maps are more diverse now.
--- a/gameServer/Actions.hs	Mon Nov 02 06:23:46 2015 +0100
+++ b/gameServer/Actions.hs	Mon Nov 02 06:58:08 2015 +0100
@@ -757,15 +757,15 @@
 
 
 processAction (CheckFailed msg) = do
-    Just (CheckInfo fileName _) <- client's checkInfo
+    Just (CheckInfo fileName _ _) <- client's checkInfo
     io $ moveFailedRecord fileName
 
 
 processAction (CheckSuccess info) = do
-    Just (CheckInfo fileName teams) <- client's checkInfo
+    Just (CheckInfo fileName teams script) <- client's checkInfo
     p <- client's clientProto
     si <- gets serverInfo
-    io $ writeChan (dbQueries si) $ StoreAchievements p (B.pack fileName) (map toPair teams) info
+    io $ writeChan (dbQueries si) $ StoreAchievements p (B.pack fileName) (map toPair teams) script info
     io $ moveCheckedRecord fileName
     where
         toPair t = (teamname t, teamowner t)
--- a/gameServer/CoreTypes.hs	Mon Nov 02 06:23:46 2015 +0100
+++ b/gameServer/CoreTypes.hs	Mon Nov 02 06:58:08 2015 +0100
@@ -119,7 +119,8 @@
     CheckInfo
     {
         recordFileName :: String,
-        recordTeams :: [TeamInfo]
+        recordTeams :: [TeamInfo],
+        recordScript :: B.ByteString
     }
 
 data ClientInfo =
@@ -345,7 +346,7 @@
     CheckAccount ClientIndex Int B.ByteString B.ByteString
     | ClearCache
     | SendStats Int Int
-    | StoreAchievements Word16 B.ByteString [(B.ByteString, B.ByteString)] [B.ByteString]
+    | StoreAchievements Word16 B.ByteString [(B.ByteString, B.ByteString)] B.ByteString [B.ByteString]
     | GetReplayName ClientIndex Int B.ByteString
     deriving (Show, Read)
 
--- a/gameServer/EngineInteraction.hs	Mon Nov 02 06:23:46 2015 +0100
+++ b/gameServer/EngineInteraction.hs	Mon Nov 02 06:58:08 2015 +0100
@@ -100,8 +100,8 @@
         -> Map.Map B.ByteString B.ByteString
         -> Map.Map B.ByteString [B.ByteString]
         -> [B.ByteString]
-        -> [B.ByteString]
-replayToDemo ti mParams prms msgs = if not sane then [] else concat [
+        -> ([B.ByteString], [B.ByteString])
+replayToDemo ti mParams prms msgs = if not sane then ([], []) else ([scriptName], concat [
         [em "TD"]
         , maybeScript
         , maybeMap
@@ -117,7 +117,7 @@
         , concatMap teamSetup ti
         , msgs
         , [em "!"]
-        ]
+        ])
     where
         keys1, keys2 :: Set.Set B.ByteString
         keys1 = Set.fromList ["FEATURE_SIZE", "MAP", "MAPGEN", "MAZE_SIZE", "SEED", "TEMPLATE"]
@@ -127,7 +127,8 @@
             && (not . null . drop 41 $ scheme)
             && (not . null . tail $ prms Map.! "AMMO")
         mapGenTypes = ["+rnd+", "+maze+", "+drawn+", "+perlin+"]
-        maybeScript = let s = head . fromMaybe ["Normal"] $ Map.lookup "SCRIPT" prms in if s == "Normal" then [] else [eml ["escript Scripts/Multiplayer/", s, ".lua"]]
+        scriptName = head . fromMaybe ["Normal"] $ Map.lookup "SCRIPT" prms
+        maybeScript = let s = scriptName in if s == "Normal" then [] else [eml ["escript Scripts/Multiplayer/", s, ".lua"]]
         maybeMap = let m = mParams Map.! "MAP" in if m `elem` mapGenTypes then [] else [eml ["emap ", m]]
         scheme = tail $ prms Map.! "SCHEME"
         mapgen = mParams Map.! "MAPGEN"
--- a/gameServer/OfficialServer/GameReplayStore.hs	Mon Nov 02 06:23:46 2015 +0100
+++ b/gameServer/OfficialServer/GameReplayStore.hs	Mon Nov 02 06:58:08 2015 +0100
@@ -16,7 +16,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  \-}
 
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE ScopedTypeVariables, OverloadedStrings #-}
 module OfficialServer.GameReplayStore where
 
 import Data.Time
@@ -70,11 +70,12 @@
     where
         loadFile :: String -> IO (Maybe CheckInfo, [B.ByteString])
         loadFile fileName = E.handle (\(e :: SomeException) ->
-                    warningM "REPLAYS" ("Problems reading " ++ fileName ++ ": " ++ show e) >> return (Just $ CheckInfo fileName [], [])) $ do
+                    warningM "REPLAYS" ("Problems reading " ++ fileName ++ ": " ++ show e) >> return (Just $ CheckInfo fileName [] "", [])) $ do
             (teams, params1, params2, roundMsgs) <- liftM read $ readFile fileName
-            return $ (
-                Just (CheckInfo fileName teams)
-                , let d = replayToDemo teams (Map.fromList params1) (Map.fromList params2) (reverse roundMsgs) in d `deepseq` d
+            let d = replayToDemo teams (Map.fromList params1) (Map.fromList params2) (reverse roundMsgs)
+            d `deepseq` return $ (
+                Just (CheckInfo fileName teams (head $ fst d))
+                , snd d
                 )
 
 moveFailedRecord :: String -> IO ()
--- a/gameServer/OfficialServer/extdbinterface.hs	Mon Nov 02 06:23:46 2015 +0100
+++ b/gameServer/OfficialServer/extdbinterface.hs	Mon Nov 02 06:58:08 2015 +0100
@@ -50,6 +50,9 @@
     \ VALUES (?, (SELECT id FROM achievement_types WHERE name = ?), (SELECT uid FROM users WHERE name = ?), \
     \ ?, ?, ?, ?)"
 
+dbQueryGamesHistory =
+    "? ? ?"
+
 dbQueryReplayFilename = "SELECT filename FROM achievements WHERE id = ?"
 
 
@@ -83,8 +86,8 @@
 
         SendStats clients rooms ->
                 void $ execute dbConn dbQueryStats (clients, rooms)
-        StoreAchievements p fileName teams info ->
-            mapM_ (execute dbConn dbQueryAchievement) $ (parseStats p fileName teams) info
+        StoreAchievements p fileName teams script info ->
+            mapM_ (uncurry (execute dbConn)) $ parseStats p fileName teams script info
 
 
 --readTime = read . B.unpack . B.take 19 . B.drop 8
@@ -94,15 +97,16 @@
     Word16 
     -> B.ByteString 
     -> [(B.ByteString, B.ByteString)] 
-    -> [B.ByteString] 
-    -> [(B.ByteString, B.ByteString, B.ByteString, Int, B.ByteString, B.ByteString, Int)]
-parseStats p fileName teams = ps
+    -> B.ByteString
+    -> [B.ByteString]
+    -> [(Query, (B.ByteString, B.ByteString, B.ByteString, Int, B.ByteString, B.ByteString, Int))]
+parseStats p fileName teams script = ps
     where
     time = readTime fileName
     ps [] = []
     ps ("DRAW" : bs) = ps bs
     ps ("WINNERS" : n : bs) = ps $ drop (readInt_ n) bs
-    ps ("ACHIEVEMENT" : typ : teamname : location : value : bs) =
+    ps ("ACHIEVEMENT" : typ : teamname : location : value : bs) = (dbQueryAchievement, 
         ( time
         , typ
         , fromMaybe "" (lookup teamname teams)
@@ -110,7 +114,7 @@
         , fileName
         , location
         , fromIntegral p
-        ) : ps bs
+        )) : ps bs
     ps (b:bs) = ps bs
 
 
--- a/hedgewars/uGears.pas	Mon Nov 02 06:23:46 2015 +0100
+++ b/hedgewars/uGears.pas	Mon Nov 02 06:58:08 2015 +0100
@@ -320,6 +320,7 @@
                     Ammoz[amTardis].Probability:= 0;
                     end;
                 AddCaption(trmsg[sidSuddenDeath], cWhiteColor, capgrpGameState);
+                ScriptCall('onSuddenDeath');
                 playSound(sndSuddenDeath);
                 StopMusic;
                 if SDMusicFN <> '' then PlayMusic
--- a/hedgewars/uGearsHandlersMess.pas	Mon Nov 02 06:23:46 2015 +0100
+++ b/hedgewars/uGearsHandlersMess.pas	Mon Nov 02 06:58:08 2015 +0100
@@ -2675,7 +2675,7 @@
             //4: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtWaterMelon, 0, cBombsSpeed *
             //                 Gear^.Tag, _0, 5000);
             end;
-        Gear^.dX := Gear^.dX + int2hwFloat(30 * Gear^.Tag);
+        Gear^.dX := Gear^.dX + int2hwFloat(Gear^.Damage * Gear^.Tag);
         if CheckCoordInWater(hwRound(Gear^.X), hwRound(Gear^.Y)) then
             FollowGear^.State:= FollowGear^.State or gstSubmersible;
         StopSoundChan(Gear^.SoundChannel, 4000);
@@ -2708,7 +2708,7 @@
         end;
 
     Gear^.Y := int2hwFloat(topY-300);
-    Gear^.dX := int2hwFloat(Gear^.Target.X - 5 * Gear^.Tag * 15);
+    Gear^.dX := int2hwFloat(Gear^.Target.X) - int2hwFloat(Gear^.Tag * Gear^.Health * Gear^.Damage) / 2;
 
     // calcs for Napalm Strike, so that it will hit the target (without wind at least :P)
     if (Gear^.State = 2) then
@@ -2718,7 +2718,6 @@
             Gear^.dX := Gear^.dX - cBombsSpeed * hwSqrt((int2hwFloat(Gear^.Target.Y) - Gear^.Y) * 2 /
                 cGravity) * Gear^.Tag;
 
-    Gear^.Health := 6;
     Gear^.doStep := @doStepAirAttackWork;
     Gear^.SoundChannel := LoopSound(sndPlane, 4000);
 
@@ -5198,7 +5197,9 @@
 ////////////////////////////////////////////////////////////////////////////////
 procedure doStepPoisonCloud(Gear: PGear);
 begin
-    WorldWrap(Gear);
+    // don't bounce
+    if WorldEdge <> weBounce then
+        WorldWrap(Gear);
     if Gear^.Timer = 0 then
         begin
         DeleteGear(Gear);
@@ -5430,7 +5431,7 @@
         for i:= 0 to graves.size - 1 do
             if graves.ar^[i]^.Health > 0 then
                 begin
-                resgear := AddGear(hwRound(graves.ar^[i]^.X), hwRound(graves.ar^[i]^.Y), gtHedgehog, gstWait, _0, _0, 0);
+                resgear := AddGear(hwRound(graves.ar^[i]^.X), hwRound(graves.ar^[i]^.Y), gtHedgehog, gstWait, _0, _0, 0,graves.ar^[i]^.Pos);
                 resgear^.Hedgehog := graves.ar^[i]^.Hedgehog;
                 resgear^.Health := graves.ar^[i]^.Health;
                 PHedgehog(graves.ar^[i]^.Hedgehog)^.Gear := resgear;
@@ -5792,9 +5793,16 @@
 All these effects assume the ray's angle is not changed and that the target type was unchanged over a number of ticks.  This is a simplifying assumption for "gun was applying freezing effect to the same target".
   * When fired at water a layer of ice textured land is added above the water.
   * When fired at non-ice land (land and lfLandMask and not lfIce) the land is overlaid with a thin layer of ice textured land around that point (say, 1 or 2px into land, 1px above). For attractiveness, a slope would probably be needed.
-  * When fired at a hog (land and $00FF <> 0), while the hog is targetted, the hog's state is set to frozen.  As long as the gun is on the hog, a frozen hog sprite creeps up from the feet to the head.  If the effect is interrupted before reaching the top, the freezing state is cleared.
-A frozen hog will animate differently.  To be decided, but possibly in a similar fashion to a grave when it comes to explosions.  The hog might (possibly) not be damaged by explosions.  This might make freezing potentially useful for friendlies in a bad position.  It might be better to allow damage though.
-A frozen hog stays frozen for a certain number of turns. Each turn the frozen overlay becomes fainter, until it fades and the hog animates normally again.
+  * When fired at a hog (land and $00FF <> 0), while the hog is targetted, the hog's state is set to frozen.
+    As long as the gun is on the hog, a frozen hog sprite creeps up from the feet to the head.
+    If the effect is interrupted before reaching the top, the freezing state is cleared.
+A frozen hog will animate differently.
+    To be decided, but possibly in a similar fashion to a grave when it comes to explosions.
+    The hog might (possibly) not be damaged by explosions.
+    This might make freezing potentially useful for friendlies in a bad position.
+    It might be better to allow damage though.
+A frozen hog stays frozen for a certain number of turns.
+    Each turn the frozen overlay becomes fainter, until it fades and the hog animates normally again.
 *)
 
 
--- a/hedgewars/uGearsHandlersRope.pas	Mon Nov 02 06:23:46 2015 +0100
+++ b/hedgewars/uGearsHandlersRope.pas	Mon Nov 02 06:58:08 2015 +0100
@@ -42,7 +42,8 @@
         OutError('ERROR: doStepRopeAfterAttack called while HHGear = nil', IsNilHHFatal);
         DeleteGear(Gear);
         exit()
-        end;
+        end
+    else if not CurrentTeam^.ExtDriven then FollowGear := HHGear;
 
     tX:= HHGear^.X;
     if WorldWrap(HHGear) and (WorldEdge = weWrap) and
@@ -137,7 +138,8 @@
         OutError('ERROR: doStepRopeWork called while HHGear = nil', IsNilHHFatal);
         DeleteGear(Gear);
         exit()
-        end;
+        end
+    else if not CurrentTeam^.ExtDriven then FollowGear := HHGear;
 
     if ((HHGear^.State and gstHHDriven) = 0) or
         (CheckGearDrowning(HHGear)) or (Gear^.PortalCounter <> 0) then
@@ -425,6 +427,7 @@
     HHGear: PGear;
     tx, ty, tt: hwFloat;
 begin
+    
     Gear^.X := Gear^.X - Gear^.dX;
     Gear^.Y := Gear^.Y - Gear^.dY;
     Gear^.Elasticity := Gear^.Elasticity + _1;
@@ -435,7 +438,8 @@
         OutError('ERROR: doStepRopeAttach called while HHGear = nil', IsNilHHFatal);
         DeleteGear(Gear);
         exit()
-        end;
+        end
+    else if not CurrentTeam^.ExtDriven then FollowGear := HHGear;
 
     DeleteCI(HHGear);
 
--- a/hedgewars/uGearsHedgehog.pas	Mon Nov 02 06:23:46 2015 +0100
+++ b/hedgewars/uGearsHedgehog.pas	Mon Nov 02 06:58:08 2015 +0100
@@ -451,7 +451,7 @@
             if CurAmmoType = amAirMine then newGear^.Hedgehog:= nil;
 
             if ((CurAmmoType = amMine) or (CurAmmoType = amSMine) or (CurAmmoType = amAirMine)) and (GameFlags and gfInfAttack <> 0) then
-                newGear^.FlightTime:= GameTicks + 1000
+                newGear^.FlightTime:= GameTicks + min(TurnTimeLeft,1000)
             else if CurAmmoType = amDrill then
                 newGear^.FlightTime:= GameTicks + 250;
             if Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0 then
@@ -572,6 +572,7 @@
 procedure doStepHedgehogDead(Gear: PGear);
 const frametime = 200;
       timertime = frametime * 6;
+var grave:  PGear;
 begin
 if Gear^.Hedgehog^.Unplaced then
     exit;
@@ -587,7 +588,10 @@
     Gear^.Hedgehog^.Effects[heFrozen]:= 0;
     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;
+    grave:= AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtGrave, 0, _0, _0, 0);
+    grave^.Hedgehog:= Gear^.Hedgehog;
+    grave^.Pos:= Gear^.uid;
+    
     DeleteGear(Gear);
     SetAllToActive
     end
--- a/hedgewars/uGearsList.pas	Mon Nov 02 06:23:46 2015 +0100
+++ b/hedgewars/uGearsList.pas	Mon Nov 02 06:58:08 2015 +0100
@@ -23,6 +23,7 @@
 uses uFloat, uTypes, SDLh;
 
 function  AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer: LongWord): PGear;
+function  AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer, newUid: LongWord): PGear;
 procedure DeleteGear(Gear: PGear);
 procedure InsertGearToList(Gear: PGear);
 procedure RemoveGearFromList(Gear: PGear);
@@ -164,11 +165,16 @@
 
 
 function AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer: LongWord): PGear;
+begin
+AddGear:= AddGear(X, Y, Kind, State, dX, dY, Timer, 0);
+end;
+function AddGear(X, Y: LongInt; Kind: TGearType; State: Longword; dX, dY: hwFloat; Timer, newUid: LongWord): PGear;
 var gear: PGear;
     //c: byte;
     cakeData: PCakeData;
 begin
-inc(GCounter);
+if newUid = 0 then
+    inc(GCounter);
 
 AddFileLog('AddGear: #' + inttostr(GCounter) + ' (' + inttostr(x) + ',' + inttostr(y) + '), d(' + floattostr(dX) + ',' + floattostr(dY) + ') type = ' + EnumToStr(Kind));
 
@@ -186,7 +192,9 @@
 gear^.doStep:= doStepHandlers[Kind];
 gear^.CollisionIndex:= -1;
 gear^.Timer:= Timer;
-gear^.uid:= GCounter;
+if newUid = 0 then
+     gear^.uid:= GCounter
+else gear^.uid:= newUid;
 gear^.SoundChannel:= -1;
 gear^.ImpactSound:= sndNone;
 gear^.Density:= _1;
@@ -466,6 +474,8 @@
                 gear^.Tag:= Y
                 end;
    gtAirAttack: begin
+                gear^.Health:= 6;
+                gear^.Damage:= 30;
                 gear^.Z:= cHHZ+2;
                 gear^.Tint:= gear^.Hedgehog^.Team^.Clan^.Color shl 8 or $FF
                 end;
--- a/hedgewars/uGearsRender.pas	Mon Nov 02 06:23:46 2015 +0100
+++ b/hedgewars/uGearsRender.pas	Mon Nov 02 06:58:08 2015 +0100
@@ -728,7 +728,7 @@
                 amHellishBomb: DrawSpriteRotated(sprHandHellish, hx, hy, sign, aangle);
                 amGasBomb: DrawSpriteRotated(sprHandCheese, hx, hy, sign, aangle);
                 amMine: DrawSpriteRotated(sprHandMine, hx, hy, sign, aangle);
-                amAirMine: DrawSpriteRotated(sprHandMine, hx, hy, sign, aangle);
+                amAirMine: DrawSpriteRotated(sprHandAirMine, hx, hy, sign, aangle);
                 amSMine: DrawSpriteRotated(sprHandSMine, hx, hy, sign, aangle);
                 amKnife: DrawSpriteRotatedF(sprHandKnife, hx, hy, 0, sign, aangle);
                 amSeduction: begin
--- a/hedgewars/uGearsUtils.pas	Mon Nov 02 06:23:46 2015 +0100
+++ b/hedgewars/uGearsUtils.pas	Mon Nov 02 06:58:08 2015 +0100
@@ -829,7 +829,7 @@
 
 procedure FindPlace(var Gear: PGear; withFall: boolean; Left, Right: LongInt; skipProximity: boolean);
 var x: LongInt;
-    y, sy: LongInt;
+    y, sy, dir: LongInt;
     ar: array[0..1023] of TPoint;
     ar2: array[0..2047] of TPoint;
     temp: TPoint;
@@ -850,9 +850,10 @@
     delta:= LAND_WIDTH div 16;
     cnt2:= 0;
     repeat
-        x:= Left + max(LAND_WIDTH div 2048, LongInt(GetRandom(Delta)));
+        if GetRandom(2) = 0 then dir:= -1 else dir:= 1;
+        x:= max(LAND_WIDTH div 2048, LongInt(GetRandom(Delta)));
+        if dir = 1 then x:= Left + x else x:= Right - x; 
         repeat
-            inc(x, Delta);
             cnt:= 0;
             y:= min(1024, topY) - Gear^.Radius shl 1;
             while y < cWaterLine do
@@ -901,9 +902,10 @@
                     ar2[cnt2].x:= x;
                     ar2[cnt2].y:= y;
                     inc(cnt2)
-                    end
-                end
-        until (x + Delta > Right);
+                    end;
+                end;
+            inc(x, Delta*dir)
+        until ((dir = 1) and (x > Right)) or ((dir = -1) and (x < Left));
 
         dec(Delta, 60)
     until (cnt2 > 0) or (Delta < 70);
--- a/hedgewars/uScript.pas	Mon Nov 02 06:23:46 2015 +0100
+++ b/hedgewars/uScript.pas	Mon Nov 02 06:58:08 2015 +0100
@@ -629,7 +629,9 @@
         begin
         gear := SpawnFakeCrateAt(lua_tointeger(L, 1), lua_tointeger(L, 2),
         HealthCrate, lua_toboolean(L, 3), lua_toboolean(L, 4));
-        lua_pushinteger(L, gear^.uid);
+        if gear <> nil then
+             lua_pushinteger(L, gear^.uid)
+        else lua_pushnil(L)
         end
     else
         lua_pushnil(L);
@@ -643,7 +645,9 @@
         begin
         gear := SpawnFakeCrateAt(lua_tointeger(L, 1), lua_tointeger(L, 2),
         AmmoCrate, lua_toboolean(L, 3), lua_toboolean(L, 4));
-        lua_pushinteger(L, gear^.uid);
+        if gear <> nil then
+             lua_pushinteger(L, gear^.uid)
+        else lua_pushnil(L)
         end
     else
         lua_pushnil(L);
@@ -657,7 +661,9 @@
         begin
         gear := SpawnFakeCrateAt(lua_tointeger(L, 1), lua_tointeger(L, 2),
         UtilityCrate, lua_toboolean(L, 3), lua_toboolean(L, 4));
-        lua_pushinteger(L, gear^.uid);
+        if gear <> nil then
+             lua_pushinteger(L, gear^.uid)
+        else lua_pushnil(L)
         end
     else
         lua_pushnil(L);
@@ -676,9 +682,8 @@
             health:= cHealthCaseAmount;
         gear := SpawnCustomCrateAt(lua_tointeger(L, 1), lua_tointeger(L, 2), HealthCrate, health, 0);
         if gear <> nil then
-            lua_pushinteger(L, gear^.uid)
-        else
-            lua_pushnil(L);
+             lua_pushinteger(L, gear^.uid)
+        else lua_pushnil(L);
         end
     else
         lua_pushnil(L);
@@ -695,9 +700,8 @@
              gear := SpawnCustomCrateAt(lua_tointeger(L, 1), lua_tointeger(L, 2), AmmoCrate, lua_tointeger(L, 3), 0)
         else gear := SpawnCustomCrateAt(lua_tointeger(L, 1), lua_tointeger(L, 2), AmmoCrate, lua_tointeger(L, 3), lua_tointeger(L, 4));
         if gear <> nil then
-            lua_pushinteger(L, gear^.uid)
-        else
-            lua_pushnil(L);
+             lua_pushinteger(L, gear^.uid)
+        else lua_pushnil(L);
         end
     else
         lua_pushnil(L);
@@ -714,9 +718,8 @@
              gear := SpawnCustomCrateAt(lua_tointeger(L, 1), lua_tointeger(L, 2), UtilityCrate, lua_tointeger(L, 3), 0)
         else gear := SpawnCustomCrateAt(lua_tointeger(L, 1), lua_tointeger(L, 2), UtilityCrate, lua_tointeger(L, 3), lua_tointeger(L, 4));
         if gear <> nil then
-            lua_pushinteger(L, gear^.uid)
-        else
-            lua_pushnil(L);
+             lua_pushinteger(L, gear^.uid)
+        else lua_pushnil(L);
        end
     else
         lua_pushnil(L);
@@ -918,29 +921,30 @@
             lua_pushinteger(L, gear^.AdvBounce);
             lua_pushinteger(L, Integer(gear^.ImpactSound));
             lua_pushinteger(L, gear^.nImpactSounds);
-            lua_pushinteger(L, gear^.Tint)
+            lua_pushinteger(L, gear^.Tint);
+            lua_pushinteger(L, gear^.Damage)
             end
         else
             begin
             lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L);
             lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L);
-            lua_pushnil(L)
+            lua_pushnil(L); lua_pushnil(L)
             end
         end
     else
         begin
         lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L);
         lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L);
-        lua_pushnil(L)
+        lua_pushnil(L); lua_pushnil(L)
         end;
-    lc_getgearvalues:= 11
+    lc_getgearvalues:= 12
 end;
 
 function lc_setgearvalues(L : Plua_State) : LongInt; Cdecl;
 var gear : PGear;
 begin
-// Currently allows 1-12 params
-//    if CheckLuaParamCount(L, 12, 'SetGearValues', 'gearUid, Angle, Power, WDTimer, Radius, Density, Karma, DirAngle, AdvBounce, ImpactSound, # ImpactSounds, Tint') then
+// Currently allows 1-13 params
+//    if CheckLuaParamCount(L, 13, 'SetGearValues', 'gearUid, Angle, Power, WDTimer, Radius, Density, Karma, DirAngle, AdvBounce, ImpactSound, # ImpactSounds, Tint, Damage') then
 //        begin
         gear:= GearByUID(lua_tointeger(L, 1));
         if gear <> nil then
@@ -966,7 +970,9 @@
             if not lua_isnoneornil(L, 11) then
                 gear^.nImpactSounds := lua_tointeger(L, 11);
             if not lua_isnoneornil(L, 12) then
-                gear^.Tint := lua_tointeger(L, 12)
+                gear^.Tint := lua_tointeger(L, 12);
+            if not lua_isnoneornil(L, 13) then
+                gear^.Damage := lua_tointeger(L, 13);
             end;
 //        end
 //    else
@@ -2907,6 +2913,12 @@
 
 procedure GetGlobals;
 begin
+// TODO
+// Use setters instead, because globals should be read-only!
+// Otherwise globals might be changed by Lua, but then unexpectatly overwritten by engine when a ScriptCall is triggered by whatever Lua is doing!
+// Sure, one could work around that in engine (e.g. by setting writable globals in SetGlobals only when their engine-side value has actually changed since SetGlobals was called the last time...), but things just get messier and messier then.
+// It is inconsistent anyway to have some globals be read-only and others not with no indication whatsoever.
+// -- sheepluva
 TurnTimeLeft:= ScriptGetInteger('TurnTimeLeft');
 end;
 
--- a/hedgewars/uTeams.pas	Mon Nov 02 06:23:46 2015 +0100
+++ b/hedgewars/uTeams.pas	Mon Nov 02 06:58:08 2015 +0100
@@ -604,7 +604,6 @@
     SplitBySpace(s, cs);
     SplitBySpace(cs, ts);
     Color:= StrToInt(cs);
-    TryDo(Color <> 0, 'Error: black team color', true);
 
     // color is always little endian so the mask must be constant also in big endian archs
     Color:= Color or $FF000000;
--- a/hedgewars/uTypes.pas	Mon Nov 02 06:23:46 2015 +0100
+++ b/hedgewars/uTypes.pas	Mon Nov 02 06:58:08 2015 +0100
@@ -88,7 +88,7 @@
             sprBulletHit, sprSnowball, sprHandSnowball, sprSnow,
             sprSDFlake, sprSDWater, sprSDCloud, sprSDSplash, sprSDDroplet, sprTardis,
             sprSlider, sprBotlevels, sprHandKnife, sprKnife, sprStar, sprIceTexture, sprIceGun,
-            sprFrozenHog, sprAmRubber, sprBoing, sprCustom1, sprCustom2, sprAirMine
+            sprFrozenHog, sprAmRubber, sprBoing, sprCustom1, sprCustom2, sprAirMine, sprHandAirMine
             );
 
     // Gears that interact with other Gears and/or Land
--- a/hedgewars/uVariables.pas	Mon Nov 02 06:23:46 2015 +0100
+++ b/hedgewars/uVariables.pas	Mon Nov 02 06:58:08 2015 +0100
@@ -722,7 +722,9 @@
             (FileName:       'custom2'; Path: ptCurrTheme;AltPath: ptGraphics; Texture: nil; Surface: nil;
             Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpLow; getDimensions: true; getImageDimensions: true), // sprCustom2
             (FileName:      'AirMine'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true)// sprAirMine
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true), // sprAirMine
+            (FileName:  'amAirMine'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true) // sprHandAirMine
             );
 
 const
Binary file share/hedgewars/Data/Graphics/Hedgehog/amAirMine.png has changed
--- a/share/hedgewars/Data/Locale/tips_en.xml	Mon Nov 02 06:23:46 2015 +0100
+++ b/share/hedgewars/Data/Locale/tips_en.xml	Mon Nov 02 06:58:08 2015 +0100
@@ -11,7 +11,7 @@
     <tip>You're bored of default gameplay? Try one of the missions — they'll offer different gameplay depending on the one you picked.</tip>
     <tip>By default the game will always record the last game played as a demo. Select “Local Game” and pick the “Demos” button on the lower right corner to play or manage them.</tip>
     <tip>Hedgewars is free software (Open Source) we create in our spare time. If you’ve got problems, ask on our forums or visit our IRC room!</tip>
-    <tip>Hedgewars is free software (Open Source) we create in our spare time. If you like it, help us with a small donation or contribute your own work!</tip>
+    <tip>Hedgewars is free software (Open Source) we create in our spare time. If you like it, feel free to help us with a small donation or contribute your own work!</tip>
     <tip>Hedgewars is free software (Open Source) we create in our spare time. Share it with your family and friends as you like!</tip>
     <tip>Hedgewars is free software (Open Source) we create in our spare time, just for fun! Meet the devs in <a href="irc://irc.freenode.net/hedgewars">#hedgewars</a>!</tip>
     <tip>From time to time there will be official tournaments. Upcoming events will be announced at <a href="http://www.hedgewars.org/">http://www.hedgewars.org/</a> some days in advance.</tip>
@@ -19,14 +19,13 @@
     <tip>Hedgewars can be run on lots of different operating systems including Microsoft Windows, Mac OS X and GNU/Linux.</tip>
     <tip>Always remember you’re able to set up your own games in local and network/online play. You’re not restricted to the “Simple Game” option.</tip>
     <tip>Connect one or more gamepads before starting the game to be able to assign their controls to your teams.</tip>
-    <tip>Create an account on <a href="http://www.hedgewars.org/">http://www.hedgewars.org/</a> to keep others from using your most favourite nickname while playing on the official server.</tip>
-    <tip>While playing you should give yourself a short break at least once an hour.</tip>
+    <tip>Consider giving yourself a short break at least once an hour to guard against strain from playing.</tip>
     <tip>If your graphics card isn’t able to provide hardware accelerated OpenGL, try to enable the low quality mode to improve performance.</tip>
     <tip>If your graphics card isn’t able to provide hardware accelerated OpenGL, try to update the associated drivers.</tip>
     <tip>We’re open to suggestions and constructive feedback. If you don’t like something or got a great idea, let us know!</tip>
-    <tip>Especially while playing online be polite and always remember there might be some minors playing with or against you as well!</tip>
+    <tip>For your own benefit we'd like you to be polite and friendly while playing on our server. Also please keep in mind that some players are minors!</tip>
     <tip>Special game modes such as “Vampirism” or “Karma” allow you to develop completely new tactics. Try them in a custom game!</tip>
-    <tip>You should never install Hedgewars on computers you don’t own (school, university, work, etc.). Please ask the responsible person instead!</tip>
+    <tip>Please don't install Hedgewars on computers you don’t own (school, university, work, etc.) unless you got permission. We don't want you to get into any trouble.</tip>
     <tip>Hedgewars can be perfect for short games during breaks. Just ensure you don’t add too many hedgehogs or use an huge map. Reducing time and health might help as well.</tip>
     <tip>No hedgehogs were harmed in making this game.</tip>
     <tip>There are three different jumps available. Tap [high jump] twice to do a very high/backwards jump.</tip>
--- a/share/hedgewars/Data/Maps/Basketball/map.lua	Mon Nov 02 06:23:46 2015 +0100
+++ b/share/hedgewars/Data/Maps/Basketball/map.lua	Mon Nov 02 06:58:08 2015 +0100
@@ -54,3 +54,7 @@
 		end
 	end
 end
+
+function onNewTurn()
+    SetWeapon(amBaseballBat)
+end
--- a/share/hedgewars/Data/Maps/Knockball/map.lua	Mon Nov 02 06:23:46 2015 +0100
+++ b/share/hedgewars/Data/Maps/Knockball/map.lua	Mon Nov 02 06:58:08 2015 +0100
@@ -68,3 +68,7 @@
 		end
 	end
 end
+
+function onNewTurn()
+    SetWeapon(amBaseballBat)
+end
--- a/share/hedgewars/Data/Maps/TrophyRace/map.lua	Mon Nov 02 06:23:46 2015 +0100
+++ b/share/hedgewars/Data/Maps/TrophyRace/map.lua	Mon Nov 02 06:58:08 2015 +0100
@@ -33,6 +33,7 @@
 local worsthog = nil
 
 local besthog = nil
+local besthogname = ''
 
 -- best time
 local besttime = maxtime + 1
@@ -78,7 +79,8 @@
         SetHealth(CurrentHedgehog, 0)
         SetEffect(CurrentHedgehog, heInvulnerable, 0)
         x, y = GetGearPosition(CurrentHedgehog)
-        AddGear(x, y, gtShell, 0, 0, 0, 0)
+        AddGear(x, y-2, gtGrenade, 0, 0, 0, 2)
+        SetGearVelocity(CurrentHedgehog, 0, 0)
         worsttime = 99999
         worsthog = nil
         lasthog = nil
@@ -141,6 +143,7 @@
             if ttime < besttime then
                 besttime = ttime
                 besthog = CurrentHedgehog
+                besthogname = GetHogName(besthog)
                 hscore = hscore .. loc("NEW fastest lap: ")
             else
                 hscore = hscore .. loc("Fastest lap: ")
@@ -149,7 +152,7 @@
                 worsttime = ttime
                 worsthog = CurrentHedgehog
             end
-            hscore = hscore .. GetHogName(besthog) .. " - " .. (besttime / 1000) .. " s | |" .. loc("Best laps per team: ")
+            hscore = hscore .. besthogname .. " - " .. (besttime / 1000) .. " s | |" .. loc("Best laps per team: ")
             
             if clan == ClansCount -1 then
                 -- Time for elimination - worst hog is out and the worst hog vars are reset.