merge default qmlfrontend
authorunc0rr
Fri, 01 Jan 2016 19:15:32 +0300
branchqmlfrontend
changeset 11481 caa1e84c3ac2
parent 11480 b0c34402038c (current diff)
parent 11479 846a3b3a3d1c (diff)
child 11544 b69f5f22a3ba
merge default
hedgewars/hwengine.pas
hedgewars/uTypes.pas
hedgewars/uVariables.pas
--- a/QTfrontend/net/newnetclient.cpp	Fri Jan 01 19:14:59 2016 +0300
+++ b/QTfrontend/net/newnetclient.cpp	Fri Jan 01 19:15:32 2016 +0300
@@ -183,7 +183,7 @@
 
 void HWNewNet::RawSendNet(const QByteArray & buf)
 {
-    qDebug() << "Client: " << QString(buf).split("\n");
+    qDebug() << "Client: " << QString(QString::fromUtf8(buf)).split("\n");
     NetSocket.write(buf);
     NetSocket.write("\n\n", 2);
 }
@@ -552,7 +552,7 @@
                 }
 
                 netClientState = InLobby;
-                RawSendNet(QString("LIST"));
+                //RawSendNet(QString("LIST")); //deprecated
                 emit connected();
             }
 
@@ -945,7 +945,7 @@
         qWarning("Illegal try to get rooms list!");
         return;
     }
-    RawSendNet(QString("LIST"));
+    //RawSendNet(QString("LIST")); //deprecated
 }
 
 HWNewNet::ClientState HWNewNet::clientState()
--- a/QTfrontend/sdlkeys.h	Fri Jan 01 19:14:59 2016 +0300
+++ b/QTfrontend/sdlkeys.h	Fri Jan 01 19:15:32 2016 +0300
@@ -118,8 +118,8 @@
     {"insert", QT_TRANSLATE_NOOP("binds (keys)", "Insert")},
     {"home", QT_TRANSLATE_NOOP("binds (keys)", "Home")},
     {"end", QT_TRANSLATE_NOOP("binds (keys)", "End")},
-    {"page up", QT_TRANSLATE_NOOP("binds (keys)", "Page up")},
-    {"page down", QT_TRANSLATE_NOOP("binds (keys)", "Page down")},
+    {"page_up", QT_TRANSLATE_NOOP("binds (keys)", "Page up")},
+    {"page_down", QT_TRANSLATE_NOOP("binds (keys)", "Page down")},
     {"f1", "F1"},
     {"f2", "F2"},
     {"f3", "F3"},
--- a/gameServer/Actions.hs	Fri Jan 01 19:14:59 2016 +0300
+++ b/gameServer/Actions.hs	Fri Jan 01 19:15:32 2016 +0300
@@ -466,12 +466,15 @@
 
 
 processAction (ProcessAccountInfo info) = do
+    si <- gets serverInfo
     case info of
         HasAccount passwd isAdmin isContr -> do
             b <- isBanned
             c <- client's isChecker
             when (not b) $ (if c then checkerLogin else playerLogin) passwd isAdmin isContr
-        Guest -> do
+        Guest | isRegisteredUsersOnly si -> do
+            processAction $ ByeClient "Registered users only"
+            | otherwise -> do
             b <- isBanned
             c <- client's isChecker
             when (not b) $
@@ -508,6 +511,7 @@
     chan <- client's sendChan
     rnc <- gets roomsClients
     clientNick <- client's nick
+    clProto <- client's clientProto
     isAuthenticated <- liftM (not . B.null) $ client's webPassword
     isAdmin <- client's isAdministrator
     isContr <- client's isContributor
@@ -521,6 +525,13 @@
         >>= filterM (liftM ((/=) lobbyId) . clientRoomM rnc)
         >>= mapM (client'sM rnc nick)
     let clFlags = B.concat . L.concat $ [["u" | isAuthenticated], ["a" | isAdmin], ["c" | isContr]]
+
+    roomsInfoList <- io $ do
+        rooms <- roomsM rnc
+        mapM (\r -> (if isNothing $ masterID r then return "" else client'sM rnc nick (fromJust $ masterID r))
+            >>= \cn -> return $ roomInfo clProto cn r)
+            $ filter (\r -> (roomProto r == clProto)) rooms
+
     mapM_ processAction . concat $ [
         [AnswerClients clientsChans ["LOBBY:JOINED", clientNick]]
         , [AnswerClients [chan] ("LOBBY:JOINED" : clientNick : lobbyNicks)]
@@ -531,6 +542,7 @@
         , [AnswerClients (chan : clientsChans) ["CLIENT_FLAGS",  B.concat["+" , clFlags], clientNick] | not $ B.null clFlags]
         , [ModifyClient (\cl -> cl{logonPassed = True, isVisible = True})]
         , [SendServerMessage]
+        , [AnswerClients [chan] ("ROOMS" : concat roomsInfoList)]
         ]
 
 
--- a/gameServer/CoreTypes.hs	Fri Jan 01 19:14:59 2016 +0300
+++ b/gameServer/CoreTypes.hs	Fri Jan 01 19:15:32 2016 +0300
@@ -126,32 +126,35 @@
 data ClientInfo =
     ClientInfo
     {
-        clUID :: Unique,
-        sendChan :: ClientChan,
-        clientSocket :: Socket,
-        host :: B.ByteString,
-        connectTime :: UTCTime,
-        nick :: B.ByteString,
-        webPassword :: B.ByteString,
-        serverSalt :: B.ByteString,
-        logonPassed :: Bool,
-        isVisible :: Bool,
+        clUID :: !Unique,
+        sendChan :: !ClientChan,
+        clientSocket :: !Socket,
+        host :: !B.ByteString,
+        connectTime :: !UTCTime,
+        nick :: !B.ByteString,
+        webPassword :: !B.ByteString,
+        serverSalt :: !B.ByteString,
+        logonPassed :: !Bool,
+        isVisible :: !Bool,
         clientProto :: !Word16,
         pingsQueue :: !Word,
-        isMaster :: Bool,
+        isMaster :: !Bool,
         isReady :: !Bool,
-        isInGame :: Bool,
-        isAdministrator :: Bool,
-        isChecker :: Bool,
-        isContributor :: Bool,
-        isKickedFromServer :: Bool,
-        isJoinedMidGame :: Bool,
+        isInGame :: !Bool,
+        isAdministrator :: !Bool,
+        hasSuperPower :: !Bool,
+        isChecker :: !Bool,
+        isContributor :: !Bool,
+        isKickedFromServer :: !Bool,
+        isJoinedMidGame :: !Bool,
+        hasAskedList :: !Bool,
         clientClan :: !(Maybe B.ByteString),
-        checkInfo :: Maybe CheckInfo,
+        checkInfo :: !(Maybe CheckInfo),
         eiLobbyChat,
         eiEM,
-        eiJoin :: EventsInfo,
-        teamsInGame :: Word
+        eiJoin :: !EventsInfo,
+        teamsInGame :: !Word,
+        pendingActions :: ![Action]
     }
 
 instance Eq ClientInfo where
@@ -164,17 +167,17 @@
 data TeamInfo =
     TeamInfo
     {
-        teamowner :: B.ByteString,
-        teamname :: B.ByteString,
-        teamcolor :: B.ByteString,
-        teamgrave :: B.ByteString,
-        teamfort :: B.ByteString,
-        teamvoicepack :: B.ByteString,
-        teamflag :: B.ByteString,
-        isOwnerRegistered :: Bool,
-        difficulty :: Int,
-        hhnum :: Int,
-        hedgehogs :: [HedgehogInfo]
+        teamowner :: !B.ByteString,
+        teamname :: !B.ByteString,
+        teamcolor :: !B.ByteString,
+        teamgrave :: !B.ByteString,
+        teamfort :: !B.ByteString,
+        teamvoicepack :: !B.ByteString,
+        teamflag :: !B.ByteString,
+        isOwnerRegistered :: !Bool,
+        difficulty :: !Int,
+        hhnum :: !Int,
+        hedgehogs :: ![HedgehogInfo]
     }
     deriving (Show, Read)
 
@@ -214,26 +217,26 @@
 data RoomInfo =
     RoomInfo
     {
-        masterID :: Maybe ClientIndex,
-        name :: B.ByteString,
-        password :: B.ByteString,
-        roomProto :: Word16,
-        teams :: [TeamInfo],
-        gameInfo :: Maybe GameInfo,
+        masterID :: !(Maybe ClientIndex),
+        name :: !B.ByteString,
+        password :: !B.ByteString,
+        roomProto :: !Word16,
+        teams :: ![TeamInfo],
+        gameInfo :: !(Maybe GameInfo),
         playersIn :: !Int,
         readyPlayers :: !Int,
-        isRestrictedJoins :: Bool,
-        isRestrictedTeams :: Bool,
-        isRegisteredOnly :: Bool,
-        isSpecial :: Bool,
-        defaultHedgehogsNumber :: Int,
-        teamsNumberLimit :: Int,
-        greeting :: B.ByteString,
-        voting :: Maybe Voting,
+        isRestrictedJoins :: !Bool,
+        isRestrictedTeams :: !Bool,
+        isRegisteredOnly :: !Bool,
+        isSpecial :: !Bool,
+        defaultHedgehogsNumber :: !Int,
+        teamsNumberLimit :: !Int,
+        greeting :: !B.ByteString,
+        voting :: !(Maybe Voting),
         roomBansList :: ![B.ByteString],
-        mapParams :: Map.Map B.ByteString B.ByteString,
-        params :: Map.Map B.ByteString [B.ByteString],
-        roomSaves :: Map.Map B.ByteString (Map.Map B.ByteString B.ByteString, Map.Map B.ByteString [B.ByteString])
+        mapParams :: !(Map.Map B.ByteString B.ByteString),
+        params :: !(Map.Map B.ByteString [B.ByteString]),
+        roomSaves :: !(Map.Map B.ByteString (Map.Map B.ByteString B.ByteString, Map.Map B.ByteString [B.ByteString]))
     }
 
 newRoom :: RoomInfo
@@ -280,6 +283,7 @@
     ServerInfo
     {
         isDedicated :: Bool,
+        isRegisteredUsersOnly :: Bool,
         serverMessage :: B.ByteString,
         serverMessageForOldVersions :: B.ByteString,
         latestReleaseVersion :: Word16,
@@ -303,6 +307,7 @@
 newServerInfo =
     ServerInfo
         True
+        False
         "<h2><p align=center><a href=\"http://www.hedgewars.org/\">http://www.hedgewars.org/</a></p></h2>"
         "<font color=yellow><h3 align=center>Hedgewars 0.9.22 is out! Please update.</h3><p align=center><a href=http://hedgewars.org/download.html>Download page here</a></font>"
         51 -- latestReleaseVersion
--- a/gameServer/FloodDetection.hs	Fri Jan 01 19:14:59 2016 +0300
+++ b/gameServer/FloodDetection.hs	Fri Jan 01 19:15:32 2016 +0300
@@ -49,7 +49,7 @@
     chat1 = [Warning $ loc "Warning! Chat flood protection activated"]
     chat2 = [ByeClient $ loc "Excess flood"]
     em1 = [Warning $ loc "Game messages flood detected - 1"]
-    em2 = [Warning $ loc "Game messages flood detected - 2"]
+    em2 = [ByeClient $ loc "Excess flood"]
     join1 = [Warning $ loc "Warning! Joins flood protection activated"]
     join2 = [ByeClient $ loc "Excess flood"]
 
@@ -69,7 +69,9 @@
                     else
                     []
 
-        return $ (ModifyClient . transformField e . const $ (numPerEntry, curTime) : nei) : actions
+        return $ [ModifyClient . transformField e . const $ (numPerEntry, curTime) : nei
+                , ModifyClient (\c -> c{pendingActions = actions}) -- append? prepend? just replacing for now
+            ]
 
     updateInfo = return [
         ModifyClient $ transformField e
--- a/gameServer/HWProtoCore.hs	Fri Jan 01 19:14:59 2016 +0300
+++ b/gameServer/HWProtoCore.hs	Fri Jan 01 19:15:32 2016 +0300
@@ -75,11 +75,10 @@
         h "QUIT" m | not $ B.null m = handleCmd ["QUIT", m]
                    | otherwise = handleCmd ["QUIT"]
         h "RND" p = handleCmd ("RND" : B.words p)
-        h "GLOBAL" p = do
-            cl <- thisClient
+        h "GLOBAL" p = serverAdminOnly $ do
             rnc <- liftM snd ask
             let chans = map (sendChan . client rnc) $ allClients rnc
-            return [AnswerClients chans ["CHAT", "[global notice]", p] | isAdministrator cl]
+            return [AnswerClients chans ["CHAT", "[global notice]", p]]
         h "WATCH" f = return [QueryReplay f]
         h "FIX" _ = handleCmd ["FIX"]
         h "UNFIX" _ = handleCmd ["UNFIX"]
@@ -92,6 +91,13 @@
         h "MAXTEAMS" n | not $ B.null n = handleCmd ["MAXTEAMS", n]
         h "INFO" n | not $ B.null n = handleCmd ["INFO", n]
         h "RESTART_SERVER" "YES" = handleCmd ["RESTART_SERVER"]
+        h "REGISTERED_ONLY" _ = serverAdminOnly $ do
+            cl <- thisClient
+            return
+                [ModifyServerInfo(\s -> s{isRegisteredUsersOnly = not $ isRegisteredUsersOnly s})
+                , AnswerClients [sendChan cl] ["CHAT", "[server]", "'Registered only' state toggled"]
+                ]
+        h "SUPER_POWER" _ = serverAdminOnly $ return [ModifyClient (\c -> c{hasSuperPower = True})]
         h c p = return [Warning $ B.concat ["Unknown cmd: /", c, " ", p]]
 
         extractParameters p = let (a, b) = B.break (== ' ') p in (upperCase a, B.dropWhile (== ' ') b)
--- a/gameServer/HWProtoLobbyState.hs	Fri Jan 01 19:14:59 2016 +0300
+++ b/gameServer/HWProtoLobbyState.hs	Fri Jan 01 19:15:32 2016 +0300
@@ -39,7 +39,9 @@
     let cl = irnc `client` ci
     rooms <- allRoomInfos
     let roomsInfoList = concatMap (\r -> roomInfo (clientProto cl) (maybeNick . liftM (client irnc) $ masterID r) r) . filter (\r -> (roomProto r == clientProto cl))
-    return [AnswerClients [sendChan cl] ("ROOMS" : roomsInfoList rooms)]
+    return $ if hasAskedList cl then [] else
+        [ ModifyClient (\c -> c{hasAskedList = True})
+        , AnswerClients [sendChan cl] ("ROOMS" : roomsInfoList rooms)]
 
 handleCmd_lobby ["CHAT", msg] = do
     n <- clientNick
@@ -91,13 +93,13 @@
             [Warning $ loc "No such room"]
             else if (not sameProto) && (not $ isAdministrator cl) then
             [Warning $ loc "Room version incompatible to your hedgewars version"]
-            else if isRestrictedJoins jRoom then
+            else if isRestrictedJoins jRoom && not (hasSuperPower cl) then
             [Warning $ loc "Joining restricted"]
             else if isRegisteredOnly jRoom && (B.null . webPassword $ cl) && not (isAdministrator cl) then
             [Warning $ loc "Registered users only"]
             else if isBanned then
             [Warning $ loc "You are banned in this room"]
-            else if roomPassword /= password jRoom then
+            else if roomPassword /= password jRoom  && not (hasSuperPower cl) then
             [NoticeMessage WrongPassword]
             else
             (
--- a/gameServer/NetRoutines.hs	Fri Jan 01 19:14:59 2016 +0300
+++ b/gameServer/NetRoutines.hs	Fri Jan 01 19:15:32 2016 +0300
@@ -72,12 +72,15 @@
                     False
                     False
                     False
+                    False
+                    False
                     Nothing
                     Nothing
                     newEventsInfo
                     newEventsInfo
                     newEventsInfo
                     0
+                    []
                     )
 
         writeChan chan $ Accept newClient
--- a/gameServer/ServerCore.hs	Fri Jan 01 19:14:59 2016 +0300
+++ b/gameServer/ServerCore.hs	Fri Jan 01 19:15:32 2016 +0300
@@ -23,7 +23,7 @@
 import System.Log.Logger
 import Control.Monad.Reader
 import Control.Monad.State.Strict
-import Data.Set as Set
+import Data.Set as Set hiding (null)
 import Data.Unique
 import Data.Maybe
 --------------------------------------
@@ -55,6 +55,10 @@
             unless (ci `Set.member` removed) $ do
                 modify (\s -> s{clientIndex = Just ci})
                 processAction $ ReactCmd cmd
+                pa <- client's pendingActions
+                when (not $ null pa) $ do
+                    mapM_ processAction pa
+                    processAction $ ModifyClient $ \c -> c{pendingActions = []}
 
         Remove ci ->
             processAction (DeleteClient ci)
--- a/hedgewars/hwengine.pas	Fri Jan 01 19:14:59 2016 +0300
+++ b/hedgewars/hwengine.pas	Fri Jan 01 19:15:32 2016 +0300
@@ -70,9 +70,9 @@
                 AddClouds;
             AddFlakes;
             SetRandomSeed(cSeed, false);
+            StoreLoad(false);
             AssignHHCoords;
             AddMiscGears;
-            StoreLoad(false);
             InitWorld;
             ResetKbd;
             if GameType = gmtSave then
--- a/hedgewars/uAI.pas	Fri Jan 01 19:14:59 2016 +0300
+++ b/hedgewars/uAI.pas	Fri Jan 01 19:15:32 2016 +0300
@@ -166,6 +166,11 @@
 
                     AddAction(BestActions, aia_Weapon, Longword(a), 300 + random(400), 0, 0);
 
+                    if (Ammoz[a].Ammo.Propz and ammoprop_NeedTarget) <> 0 then
+                        begin
+                        AddAction(BestActions, aia_Put, 0, 8, ap.AttackPutX, ap.AttackPutY)
+                        end;
+
                     if (ap.Angle > 0) then
                         AddAction(BestActions, aia_LookRight, 0, 200, 0, 0)
                     else if (ap.Angle < 0) then
@@ -189,11 +194,6 @@
                             end
                         end;
 
-                    if (Ammoz[a].Ammo.Propz and ammoprop_NeedTarget) <> 0 then
-                        begin
-                        AddAction(BestActions, aia_Put, 0, 1, ap.AttackPutX, ap.AttackPutY)
-                        end;
-
                     if (Ammoz[a].Ammo.Propz and ammoprop_OscAim) <> 0 then
                         begin
                         AddAction(BestActions, aia_attack, aim_push, 350 + random(200), 0, 0);
--- a/hedgewars/uAIAmmoTests.pas	Fri Jan 01 19:14:59 2016 +0300
+++ b/hedgewars/uAIAmmoTests.pas	Fri Jan 01 19:15:32 2016 +0300
@@ -36,6 +36,7 @@
         end;
 
 function TestBazooka(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
+function TestBee(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
 function TestSnowball(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
 function TestGrenade(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
 function TestMolotov(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
@@ -68,7 +69,7 @@
             (proc: @TestGrenade;     flags: 0), // amGrenade
             (proc: @TestClusterBomb; flags: 0), // amClusterBomb
             (proc: @TestBazooka;     flags: 0), // amBazooka
-            (proc: nil;              flags: 0), // amBee
+            (proc: @TestBee;         flags: amtest_Rare), // amBee
             (proc: @TestShotgun;     flags: 0), // amShotgun
             (proc: nil;              flags: 0), // amPickHammer
             (proc: nil;              flags: 0), // amSkip
@@ -176,7 +177,9 @@
             value:= RateExplosion(Me, EX, EY, 101, afTrackFall or afErasesLand)
         else value:= RateExplosion(Me, EX, EY, 101);
         if (value = 0) and (Targ.Kind = gtHedgehog) and (Targ.Score > 0) then
-            value:= 1024 - Metric(Targ.Point.X, Targ.Point.Y, EX, EY) div 64;
+            if GameFlags and gfSolidLand = 0 then
+                 value := 1024 - Metric(Targ.Point.X, Targ.Point.Y, EX, EY) div 64
+            else value := BadTurn;
         if valueResult <= value then
             begin
             ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random((Level - 1) * 9));
@@ -192,6 +195,113 @@
 TestBazooka:= valueResult
 end;
 
+function calcBeeFlight(Me: PGear; x, y, dx, dy, tX, tY: real; var eX, eY: LongInt): LongInt;
+var t: Longword;
+    f: boolean;
+    speed, d: real;
+begin
+    // parabola flight before activation
+    t:= 500;
+    repeat
+        x:= x + dx;
+        y:= y + dy;
+        dy:= dy + cGravityf;
+        f:= ((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or
+           ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me^.Hedgehog^.Gear, trunc(x), trunc(y), 5));
+        dec(t)
+    until (t = 0) or (y >= cWaterLine) or f;
+
+    if f then
+    begin
+        eX:= trunc(x);
+        eY:= trunc(y);
+        exit(RateExplosion(Me, eX, eY, 101, afTrackFall or afErasesLand));
+    end;
+
+
+    // activated
+    t:= 5000;
+    speed:= sqrt(sqr(dx) + sqr(dy));
+
+    repeat
+        if (t and $F) = 0 then
+        begin
+            dx:= dx + 0.000064 * (tX - x);
+            dy:= dy + 0.000064 * (tY - y);
+            d := speed / sqrt(sqr(dx) + sqr(dy));
+            dx:= dx * d;
+            dy:= dy * d;
+        end;
+
+        x:= x + dx;
+        y:= y + dy;
+        f:= ((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or
+           ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me^.Hedgehog^.Gear, trunc(x), trunc(y), 5));
+        dec(t)
+    until (t = 0) or f;
+
+    if f then
+    begin
+        eX:= trunc(x);
+        eY:= trunc(y);
+        exit(RateExplosion(Me, eX, eY, 101, afTrackFall or afErasesLand));
+    end
+    else
+        calcBeeFlight:= BadTurn
+end;
+
+function TestBee(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
+var i, j: LongInt;
+    valueResult, v, a, p: LongInt;
+    mX, mY, dX: real;
+    eX, eY: LongInt;
+begin
+    if Level > 1 then
+        exit(BadTurn);
+
+    eX:= 0;
+    eY:= 0;
+    mX:= hwFloat2Float(Me^.X);
+    mY:= hwFloat2Float(Me^.Y);
+    valueResult:= BadTurn;
+    for i:= 0 to 8 do
+        for j:= 0 to 1 do
+            begin
+            a:= i * 120;
+            p:= random(cMaxPower - 200) + 180;
+
+            if j = 0 then
+                a:= -a;
+
+            v:= calcBeeFlight(Me
+                    , mX
+                    , mY
+                    , sin(a * pi / 2048) * p / cPowerDivisor
+                    , -cos(a * pi / 2048) * p / cPowerDivisor
+                    , Targ.Point.X
+                    , Targ.Point.Y
+                    , eX
+                    , eY);
+
+            if v > valueResult then
+                begin
+                ap.ExplR:= 100;
+                ap.ExplX:= eX;
+                ap.ExplY:= eY;
+                ap.Angle:= a;
+                ap.Power:= p;
+                valueResult:= v
+                end
+            end;
+
+    ap.AttackPutX:= Targ.Point.X;
+    ap.AttackPutY:= Targ.Point.Y;
+
+    if valueResult > 0 then
+        TestBee:= valueResult - 5000
+    else
+        TestBee:= BadTurn // no digging
+end;
 
 function TestDrillRocket(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams): LongInt;
 var Vx, Vy, r, mX, mY: real;
@@ -668,7 +778,11 @@
         valueResult:= RateShotgun(Me, vX, vY, rx, ry);
 
         if (valueResult = 0) and (Targ.Kind = gtHedgehog) and (Targ.Score > 0) then
-            valueResult:= 1024 - Metric(Targ.Point.X, Targ.Point.Y, rx, ry) div 64
+            begin
+            if GameFlags and gfSolidLand = 0 then
+                 valueResult:= 1024 - Metric(Targ.Point.X, Targ.Point.Y, rx, ry) div 64
+            else valueResult := BadTurn
+            end
         else
             dec(valueResult, Level * 4000);
         // 27/20 is reuse bonus
--- a/hedgewars/uChat.pas	Fri Jan 01 19:14:59 2016 +0300
+++ b/hedgewars/uChat.pas	Fri Jan 01 19:15:32 2016 +0300
@@ -196,7 +196,7 @@
 // create and blit text
 strSurface:= TTF_RenderUTF8_Blended(Fontz[font].Handle, Str2PChar(str), cl.color);
 //SDL_UpperBlit(strSurface, nil, resSurface, @dstrect);
-if strSurface <> nil then copyTOXY(strSurface, resSurface, Padding, Padding);
+if strSurface <> nil then copyToXY(strSurface, resSurface, Padding, Padding);
 SDL_FreeSurface(strSurface);
 
 cl.Tex:= Surface2Tex(resSurface, false);
--- a/hedgewars/uGears.pas	Fri Jan 01 19:14:59 2016 +0300
+++ b/hedgewars/uGears.pas	Fri Jan 01 19:15:32 2016 +0300
@@ -168,7 +168,9 @@
     i, AliveCount: LongInt;
     s: ansistring;
     prevtime: LongWord;
+    stirFallers: boolean;
 begin
+stirFallers:= false;
 prevtime:= TurnTimeLeft;
 ScriptCall('onGameTick');
 if GameTicks mod 20 = 0 then ScriptCall('onGameTick20');
@@ -199,6 +201,8 @@
     begin
     curHandledGear:= t;
     t:= curHandledGear^.NextGear;
+    if (GameTicks and $1FFF = 0) and (curHandledGear^.Kind = gtCase) and (curHandledGear^.Pos <> posCaseHealth) then
+        stirFallers := true; 
 
     if curHandledGear^.Message and gmDelete <> 0 then
         DeleteGear(curHandledGear)
@@ -224,6 +228,23 @@
             end
         end
     end;
+if stirFallers then
+    begin
+    t := GearsList;
+    while t <> nil do
+        begin
+        if t^.Kind = gtGenericFaller then
+            begin
+            t^.Active:= true;
+            t^.X:=  int2hwFloat(GetRandom(rightX-leftX)+leftX);
+            t^.Y:=  int2hwFloat(GetRandom(LAND_HEIGHT-topY)+topY);
+            t^.dX:= _90-(GetRandomf*_360);
+            t^.dY:= _90-(GetRandomf*_360)
+            end;
+        t := t^.NextGear
+        end
+    end;
+
 curHandledGear:= nil;
 
 if AllInactive then
@@ -749,7 +770,8 @@
                                 end;
         t:= LAND_WIDTH div 2
         end
-    end else // mix hedgehogs
+    end 
+else // mix hedgehogs
     begin
     Count:= 0;
     for p:= 0 to Pred(TeamsCount) do
@@ -778,7 +800,30 @@
         ar[i]:= ar[Count - 1];
         dec(Count)
         end
-    end
+    end;
+for p:= 0 to Pred(TeamsCount) do
+    with TeamsArray[p]^ do
+        for i:= 0 to cMaxHHIndex do
+            with Hedgehogs[i] do
+                if (Gear <> nil) and (Gear^.State and gsttmpFlag <> 0) then
+                    begin
+                    DrawExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50);
+                    AddFileLog('Carved a hole for hog at coordinates (' + inttostr(hwRound(Gear^.X)) + ',' + inttostr(hwRound(Gear^.Y)) + ')')
+                    end;
+// place flowers after in case holes overlap (we shrink search distance if we are failing to place)
+for p:= 0 to Pred(TeamsCount) do
+    with TeamsArray[p]^ do
+        for i:= 0 to cMaxHHIndex do
+            with Hedgehogs[i] do
+                if (Gear <> nil) and (Gear^.State and gsttmpFlag <> 0) then
+                    begin
+                    ForcePlaceOnLand(hwRound(Gear^.X) - SpritesData[sprTargetBee].Width div 2, 
+                                     hwRound(Gear^.Y) - SpritesData[sprTargetBee].Height div 2, 
+                                     sprTargetBee, 0, lfBasic, $FFFFFFFF, false, false, false);
+                    Gear^.Y:= int2hwFloat(hwRound(Gear^.Y) - 16 - Gear^.Radius);
+                    Gear^.State:= Gear^.State and not gsttmpFlag;
+                    AddFileLog('Carved a hole for hog at coordinates (' + inttostr(hwRound(Gear^.X)) + ',' + inttostr(hwRound(Gear^.Y)) + ')')
+                    end
 end;
 
 
--- a/hedgewars/uGearsHandlersMess.pas	Fri Jan 01 19:14:59 2016 +0300
+++ b/hedgewars/uGearsHandlersMess.pas	Fri Jan 01 19:15:32 2016 +0300
@@ -128,7 +128,7 @@
 procedure doStepResurrectorWork(Gear: PGear);
 procedure doStepResurrector(Gear: PGear);
 procedure doStepNapalmBomb(Gear: PGear);
-procedure doStepStructure(Gear: PGear);
+//procedure doStepStructure(Gear: PGear);
 procedure doStepTardisWarp(Gear: PGear);
 procedure doStepTardis(Gear: PGear);
 procedure updateFuel(Gear: PGear);
@@ -136,7 +136,7 @@
 procedure doStepIceGun(Gear: PGear);
 procedure doStepAddAmmo(Gear: PGear);
 procedure doStepGenericFaller(Gear: PGear);
-procedure doStepCreeper(Gear: PGear);
+//procedure doStepCreeper(Gear: PGear);
 procedure doStepKnife(Gear: PGear);
 
 var
@@ -513,10 +513,10 @@
     dec(Gear^.Timer);
     if Gear^.Timer = 1000 then // might need adjustments
         case Gear^.Kind of
-            gtGrenade: makeHogsWorry(Gear^.X, Gear^.Y, 50);
-            gtClusterBomb: makeHogsWorry(Gear^.X, Gear^.Y, 20);
-            gtWatermelon: makeHogsWorry(Gear^.X, Gear^.Y, 75);
-            gtHellishBomb: makeHogsWorry(Gear^.X, Gear^.Y, 90);
+            gtGrenade,
+            gtClusterBomb,
+            gtWatermelon,
+            gtHellishBomb: makeHogsWorry(Gear^.X, Gear^.Y, Gear^.Boom);
             gtGasBomb: makeHogsWorry(Gear^.X, Gear^.Y, 50);
         end;
 
@@ -524,7 +524,7 @@
         begin
         CheckCollision(Gear);
         if (Gear^.State and gstCollision) <> 0 then
-            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx);
+            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx);
         end;
 
     if (Gear^.Kind = gtGasBomb) and ((GameTicks mod 200) = 0) then
@@ -537,14 +537,14 @@
     if Gear^.Timer = 0 then
         begin
         case Gear^.Kind of
-            gtGrenade: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
-            gtBall: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 40, Gear^.Hedgehog, EXPLAutoSound);
+            gtGrenade: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
+            gtBall: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
             gtClusterBomb:
                 begin
                 x := hwRound(Gear^.X);
                 y := hwRound(Gear^.Y);
                 gdX:= Gear^.dX;
-                doMakeExplosion(x, y, 20, Gear^.Hedgehog, EXPLAutoSound);
+                doMakeExplosion(x, y, Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
                 for i:= 0 to 4 do
                     begin
                     dX := rndSign(GetRandomf * _0_1) + gdX / 5;
@@ -557,7 +557,7 @@
                 x := hwRound(Gear^.X);
                 y := hwRound(Gear^.Y);
                 gdX:= Gear^.dX;
-                doMakeExplosion(x, y, 75, Gear^.Hedgehog, EXPLAutoSound);
+                doMakeExplosion(x, y, Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
                 for i:= 0 to 5 do
                     begin
                     dX := rndSign(GetRandomf * _0_1) + gdX / 5;
@@ -570,7 +570,7 @@
                 begin
                 x := hwRound(Gear^.X);
                 y := hwRound(Gear^.Y);
-                doMakeExplosion(x, y, 90, Gear^.Hedgehog, EXPLAutoSound);
+                doMakeExplosion(x, y, Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
 
                 for i:= 0 to 127 do
                     begin
@@ -590,7 +590,7 @@
                 end;
             gtGasBomb:
                 begin
-                doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound);
+                doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
                 for i:= 0 to 2 do
                     begin
                     x:= GetRandom(60);
@@ -699,13 +699,12 @@
     doStepFallingGear(Gear);
     if (Gear^.State and gstCollision) <> 0 then
         begin
-        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Timer, Gear^.Hedgehog, EXPLAutoSound);
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
         DeleteGear(Gear);
         exit
     end;
 
-    if (Gear^.Kind = gtMelonPiece)
-    or (Gear^.Kind = gtBall) then
+    if (Gear^.Kind = gtMelonPiece) then
         CalcRotationDirAngle(Gear)
     else if (GameTicks and $1F) = 0 then
         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);
@@ -720,7 +719,7 @@
     doStepFallingGear(Gear);
     if (Gear^.State and gstCollision) <> 0 then
         begin
-        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
         DeleteGear(Gear);
         exit
         end;
@@ -743,7 +742,7 @@
     CalcRotationDirAngle(Gear);
     if (Gear^.State and gstCollision) <> 0 then
         begin
-        kick:= hwRound((hwAbs(gdX)+hwAbs(gdY)) * _20);
+        kick:= hwRound((hwAbs(gdX)+hwAbs(gdY)) * Gear^.Boom / 10000);
         Gear^.dX:= gdX;
         Gear^.dY:= gdY;
         AmmoShove(Gear, 0, kick);
@@ -1029,13 +1028,13 @@
         end
     else
         begin
-        if (GameTicks and $F) = 0 then
+        if (Gear^.Timer and $F) = 0 then
             begin
-            if (GameTicks and $30) = 0 then
+            if (Gear^.Timer and $3F) = 0 then
                 AddVisualGear(gX, gY, vgtBeeTrace);
 
-            Gear^.dX := Gear^.Elasticity * (Gear^.dX + _0_000064 * (Gear^.Target.X - gX));
-            Gear^.dY := Gear^.Elasticity * (Gear^.dY + _0_000064 * (Gear^.Target.Y - gY));
+            Gear^.dX := Gear^.dX + _0_000064 * (Gear^.Target.X - gX);
+            Gear^.dY := Gear^.dY + _0_000064 * (Gear^.Target.Y - gY);
             // make sure new speed isn't higher than original one (which we stored in Friction variable)
             t := Gear^.Friction / Distance(Gear^.dX, Gear^.dY);
             Gear^.dX := Gear^.dX * t;
@@ -1052,7 +1051,7 @@
     if ((Gear^.State and gstCollision) <> 0) then
         begin
         StopSoundChan(Gear^.SoundChannel);
-        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
         for i:= 0 to 31 do
             begin
             flower:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot);
@@ -1098,7 +1097,7 @@
     CheckCollision(Gear);
     if (Gear^.State and gstCollision) <> 0 then
         begin
-        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
         DeleteGear(Gear);
         exit
     end;
@@ -1280,9 +1279,9 @@
         if Gear^.Damage > 5 then
             begin
             if Gear^.AmmoType = amDEagle then
-                AmmoShove(Gear, 7, 20)
+                AmmoShove(Gear, Gear^.Boom, 20)
             else
-                AmmoShove(Gear, Gear^.Timer, 20);
+                AmmoShove(Gear, Gear^.Timer * Gear^.Boom div 100000, 20);
             end;
         CheckGearDrowning(Gear);
         dec(i)
@@ -1471,7 +1470,7 @@
     if (Gear^.Timer mod 33) = 0 then
         begin
         HHGear^.State := HHGear^.State or gstNoDamage;
-        doMakeExplosion(x, y + 7, 6, Gear^.Hedgehog, EXPLDontDraw);
+        doMakeExplosion(x, y + 7, Gear^.Boom, Gear^.Hedgehog, EXPLDontDraw);
         HHGear^.State := HHGear^.State and (not gstNoDamage)
         end;
 
@@ -1646,7 +1645,7 @@
                 Gear^.Y := HHGear^.Y + Gear^.dY * (cHHRadius + cBlowTorchC);
                 end;
             HHGear^.State := HHGear^.State or gstNoDamage;
-            AmmoShove(Gear, 2, 15);
+            AmmoShove(Gear, Gear^.Boom, 15);
             HHGear^.State := HHGear^.State and (not gstNoDamage)
             end;
         end;
@@ -1731,7 +1730,7 @@
 
         if (Gear^.Damage > 35) then
             begin
-            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
+            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
             DeleteGear(Gear);
             exit
             end
@@ -1755,7 +1754,7 @@
                 or (cMineDudPercent = 0)
                 or (getRandom(100) > cMineDudPercent) then
                     begin
-                    doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
+                    doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
                     DeleteGear(Gear)
                     end
                 else
@@ -1779,12 +1778,6 @@
             Gear^.State := Gear^.State or gsttmpFlag;
 end;
 
-(*
-Just keeping track for my own benefit.
-Every second, locate new target.  Clear if target radius has been set to 0 or no target in range.
-Every... 16 milliseconds? Update vector to target.
-*)
-
 procedure doStepAirMine(Gear: PGear);
 var i,t,targDist,tmpDist: LongWord;
     targ, tmpG: PGear;
@@ -1983,7 +1976,8 @@
         if ((Gear^.State and gstAttacking) = 0) then
             begin
             if ((GameTicks and $1F) = 0) then
-                if CheckGearNear(Gear, gtHedgehog, 46, 32) <> nil then
+// FIXME - values taken from mine.  use a gear val and set both to same
+               if CheckGearNear(Gear, gtHedgehog, 46, 32) <> nil then
                     Gear^.State := Gear^.State or gstAttacking
             end
         else // gstAttacking <> 0
@@ -1991,7 +1985,7 @@
             AllInactive := false;
             if Gear^.Timer = 0 then
                 begin
-                doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
+                doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
                 DeleteGear(Gear);
                 exit
                 end
@@ -2018,7 +2012,7 @@
         makeHogsWorry(Gear^.X, Gear^.Y, 75);
     if Gear^.Timer = 0 then
         begin
-        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 75, Gear^.Hedgehog, EXPLAutoSound);
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
         DeleteGear(Gear);
         exit
         end;
@@ -2137,13 +2131,13 @@
 
         if k = gtCase then
             begin
-            doMakeExplosion(x, y, 25, hog, EXPLAutoSound);
+            doMakeExplosion(x, y, Gear^.Boom, hog, EXPLAutoSound);
             for i:= 0 to 63 do
                 AddGear(x, y, gtFlame, 0, _0, _0, 0);
             end
         else if k = gtExplosives then
                 begin
-                doMakeExplosion(x, y, 75, hog, EXPLAutoSound);
+                doMakeExplosion(x, y, Gear^.Boom, hog, EXPLAutoSound);
                 for i:= 0 to 31 do
                     begin
                     dX := AngleCos(i * 64) * _0_5 * (getrandomf + _1);
@@ -2175,23 +2169,6 @@
         end
     else
         begin
-        if (Gear^.Pos <> posCaseHealth) and (GameTicks and $1FFF = 0) then // stir 'em up periodically
-            begin
-            gi := GearsList;
-            while gi <> nil do
-                begin
-                if gi^.Kind = gtGenericFaller then
-                    begin
-                    gi^.Active:= true;
-                    gi^.X:=  int2hwFloat(GetRandom(rightX-leftX)+leftX);
-                    gi^.Y:=  int2hwFloat(GetRandom(LAND_HEIGHT-topY)+topY);
-                    gi^.dX:= _90-(GetRandomf*_360);
-                    gi^.dY:= _90-(GetRandomf*_360)
-                    end;
-                gi := gi^.NextGear
-                end
-            end;
-
         if Gear^.Timer = 500 then
             begin
 (* Can't make sparkles team coloured without working out what the next team is going to be. This should be solved, really, since it also screws up
@@ -2312,7 +2289,7 @@
     HHGear^.State := HHGear^.State or gstNoDamage;
     DeleteCI(HHGear);
 
-    AmmoShove(Gear, 30, 115);
+    AmmoShove(Gear, Gear^.Boom, 115);
 
     HHGear^.State := (HHGear^.State and (not gstNoDamage)) or gstMoving;
     Gear^.Timer := 250;
@@ -2332,7 +2309,7 @@
     for i:= 0 to 3 do
         begin
         AddVisualGear(hwRound(Gear^.X) + hwSign(Gear^.dX) * (10 + 6 * i), hwRound(Gear^.Y) + 12 + Random(6), vgtDust);
-        AmmoShove(Gear, 30, 25);
+        AmmoShove(Gear, Gear^.Boom, 25);
         Gear^.X := Gear^.X + Gear^.dX * 5
         end;
 
@@ -2370,7 +2347,7 @@
             Gear^.dY.QWordValue:= 429496730;
             Gear^.dX.isNegative:= getrandom(2)<>1;
             Gear^.dY.isNegative:= true;
-            AmmoShove(Gear, 2, 125);
+            AmmoShove(Gear, Gear^.Boom, 125);
             Gear^.dX:= tdX;
             Gear^.dY:= tdY;
             Gear^.Radius := 1
@@ -2449,7 +2426,7 @@
             Gear^.dY.QWordValue:= 429496730;
             Gear^.dX.isNegative:= getrandom(2)<>1;
             Gear^.dY.isNegative:= true;
-            AmmoShove(Gear, 2, 125);
+            AmmoShove(Gear, Gear^.Boom, 125);
             Gear^.dX:= tdX;
             Gear^.dY:= tdY;
             Gear^.Radius := 1
@@ -2475,13 +2452,13 @@
                     Gear^.dY.QWordValue:= 429496730;
                     Gear^.dX.isNegative:= getrandom(2)<>1;
                     Gear^.dY.isNegative:= true;
-                    AmmoShove(Gear, 6, 100);
+                    AmmoShove(Gear, Gear^.Boom * 3, 100);
                     Gear^.dX:= tdX;
                     Gear^.dY:= tdY;
                     Gear^.Radius := 1;
                     end
                 else if ((GameTicks and $3) = 3) then
-                    doMakeExplosion(gX, gY, 8, Gear^.Hedgehog, 0);//, EXPLNoDamage);
+                    doMakeExplosion(gX, gY, Gear^.Boom * 4, Gear^.Hedgehog, 0);//, EXPLNoDamage);
                 //DrawExplosion(gX, gY, 4);
 
                 if ((GameTicks and $7) = 0) and (Random(2) = 0) then
@@ -2548,7 +2525,7 @@
         DrawTunnel(HHGear^.X - int2hwFloat(cHHRadius), HHGear^.Y - _1, _0_5, _0, cHHRadius * 4+2, 2);
         HHGear^.State := HHGear^.State or gstNoDamage;
         Gear^.Y := HHGear^.Y;
-        AmmoShove(Gear, 30, 40);
+        AmmoShove(Gear, Gear^.Boom, 40);
         HHGear^.State := HHGear^.State and (not gstNoDamage)
         end;
 
@@ -2734,7 +2711,7 @@
     doStepFallingGear(Gear);
     if (Gear^.State and gstCollision) <> 0 then
         begin
-        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
         DeleteGear(Gear);
         {$IFNDEF PAS2C}
         with mobileRecord do
@@ -3009,7 +2986,7 @@
     doStepFallingGear(Gear);
     if (Gear^.State and gstCollision) <> 0 then
         begin
-        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound);
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
         gdX.isNegative := not gdX.isNegative;
         gdY.isNegative := not gdY.isNegative;
         gdX:= gdX*_0_2;
@@ -3101,7 +3078,7 @@
                 Gear^.Pos := 2;
             end;
 
-        AmmoShove(Gear, 30, 40);
+        AmmoShove(Gear, Gear^.Boom, 40);
 
         DrawTunnel(HHGear^.X - HHGear^.dX * 10,
                     HHGear^.Y - _2 - HHGear^.dY * 10 + hwAbs(HHGear^.dY) * 2,
@@ -3115,7 +3092,7 @@
 
     if Gear^.Health < Gear^.Damage then
         begin
-        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
         if hasWishes then
             for i:= 0 to 31 do
                 begin
@@ -3186,7 +3163,7 @@
     if Gear^.Tag < 2250 then
         exit;
 
-    doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cakeDmg, Gear^.Hedgehog, EXPLAutoSound);
+    doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
     AfterAttack;
     DeleteGear(Gear)
 end;
@@ -3510,10 +3487,7 @@
         begin
         //out of time or exited ground
         StopSoundChan(Gear^.SoundChannel);
-        if (Gear^.State and gsttmpFlag) <> 0 then
-            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound)
-        else
-            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
         DeleteGear(Gear);
         exit
         end
@@ -3573,10 +3547,7 @@
         else if (t <> nil) then
             begin
             //explode right on contact with HH
-            if (Gear^.State and gsttmpFlag) <> 0 then
-                doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound)
-            else
-                doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound);
+            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
             DeleteGear(Gear);
             exit;
             end;
@@ -3596,7 +3567,7 @@
             dec(Gear^.Timer)
         else
             begin
-            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound);
+            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
             DeleteGear(Gear);
             end
         end;
@@ -3764,7 +3735,7 @@
 
         if ((Gear^.State and gstCollision) <> 0) then
             begin
-            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, Gear^.Hedgehog, EXPLAutoSound);
+            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLAutoSound);
             for i:= 0 to 15 do
                 begin
                 dX := AngleCos(i * 64) * _0_5 * (GetRandomf + _1);
@@ -4185,7 +4156,7 @@
 
     if (Gear^.State and gstCollision) <> 0 then
         begin
-        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLPoisoned, $C0E0FFE0);
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLPoisoned, $C0E0FFE0);
         PlaySound(sndEggBreak);
         AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEgg);
         vg := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEgg);
@@ -4795,11 +4766,11 @@
         end
     else if (Gear^.State and gstCollision) <> 0 then
         begin
-        r0 := GetRandom(21);
-        r1 := GetRandom(21);
-        doMakeExplosion(hwRound(Gear^.X) - 30 - r0, hwRound(Gear^.Y) + 40, 40 + r1, Gear^.Hedgehog, 0);
-        doMakeExplosion(hwRound(Gear^.X) + 30 + r1, hwRound(Gear^.Y) + 40, 40 + r0, Gear^.Hedgehog, 0);
-        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 80 + r0, Gear^.Hedgehog, EXPLAutoSound);
+        r0 := GetRandom(Gear^.Boom div 4 + 1);
+        r1 := GetRandom(Gear^.Boom div 4 + 1);
+        doMakeExplosion(hwRound(Gear^.X) - 30 - r0, hwRound(Gear^.Y) + 40, Gear^.Boom div 2 + r1, Gear^.Hedgehog, 0);
+        doMakeExplosion(hwRound(Gear^.X) + 30 + r1, hwRound(Gear^.Y) + 40, Gear^.Boom div 2 + r0, Gear^.Hedgehog, 0);
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom + r0, Gear^.Hedgehog, EXPLAutoSound);
         for r0:= 0 to 4 do
             AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote);
         Gear^.dY := cGravity * 2 - odY;
@@ -4933,7 +4904,7 @@
                         end;
 
                     // kick nearby hogs
-                    AmmoShove(Gear, 35, 50);
+                    AmmoShove(Gear, Gear^.Boom, 50);
 
                     dec(Gear^.Health, Gear^.Damage);
 
@@ -5214,7 +5185,7 @@
     Gear^.dX := Gear^.dX + cWindSpeed / 4;
     Gear^.dY := Gear^.dY + cGravity / 100;
     if (GameTicks and $FF) = 0 then
-        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx or EXPLNoDamage or EXPLDoNotTouchAny or EXPLPoisoned);
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx or EXPLNoDamage or EXPLDoNotTouchAny or EXPLPoisoned);
     if Gear^.State and gstTmpFlag = 0 then
         AllInactive:= false;
 end;
@@ -5253,18 +5224,13 @@
                 dmg:= (tmp^.Health - tmp^.Damage);
                 if dmg > 0 then
                     begin
-                    // do 1/2 current hp worth of damage if extra damage is enabled (1/3 damage if not)
-                    if cDamageModifier > _1 then
-                        d:= 2
-                    else
-                        d:= 3;
-
                     // always rounding down
-                    dmg:= dmg div d;
+                    dmg:= dmg div Gear^.Boom;
 
                     if dmg > 0 then
                         ApplyDamage(tmp, CurrentHedgehog, dmg, dsUnknown);
                     end;
+		tmp^.dY:= _0_03 * Gear^.Boom
                 end;
 
             if (tmp^.Kind <> gtHedgehog) or (dmg > 0) or (tmp^.Health > tmp^.Damage) then
@@ -5534,6 +5500,7 @@
     dec(Gear^.Timer)
 end;
 
+(*
 ////////////////////////////////////////////////////////////////////////////////
 procedure doStepStructure(Gear: PGear);
 var
@@ -5625,6 +5592,7 @@
         doMakeExplosion(x, y, 50, CurrentHedgehog, EXPLAutoSound);
         end;
 end;
+*)
 
 ////////////////////////////////////////////////////////////////////////////////
 (*
@@ -6114,7 +6082,7 @@
         end;
     end
 end;
-
+(*
 procedure doStepCreeper(Gear: PGear);
 var hogs: PGearArrayS;
     HHGear: PGear;
@@ -6195,7 +6163,7 @@
         end;
     end;
 end;
-
+*)
 ////////////////////////////////////////////////////////////////////////////////
 procedure doStepKnife(Gear: PGear);
 //var ox, oy: LongInt;
@@ -6214,7 +6182,7 @@
         DeleteCI(Gear);
         Gear^.Radius:= 7;
         // used for damage and impact calc. needs balancing I think
-        Gear^.Health:= hwRound(hwSqr((hwAbs(Gear^.dY)+hwAbs(Gear^.dX))*_4));
+        Gear^.Health:= hwRound(hwSqr((hwAbs(Gear^.dY)+hwAbs(Gear^.dX))*Gear^.Boom/10000));
         doStepFallingGear(Gear);
         AllInactive := false;
         a:= Gear^.DirAngle;
--- a/hedgewars/uGearsHedgehog.pas	Fri Jan 01 19:14:59 2016 +0300
+++ b/hedgewars/uGearsHedgehog.pas	Fri Jan 01 19:15:32 2016 +0300
@@ -587,7 +587,7 @@
     begin
     Gear^.Hedgehog^.Effects[heFrozen]:= 0;
     Gear^.State:= Gear^.State or gstNoDamage;
-    doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, CurrentHedgehog, EXPLAutoSound);
+    doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Boom, CurrentHedgehog, EXPLAutoSound);
     grave:= AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtGrave, 0, _0, _0, 0);
     grave^.Hedgehog:= Gear^.Hedgehog;
     grave^.Pos:= Gear^.uid;
--- a/hedgewars/uGearsList.pas	Fri Jan 01 19:14:59 2016 +0300
+++ b/hedgewars/uGearsList.pas	Fri Jan 01 19:15:32 2016 +0300
@@ -215,6 +215,54 @@
     gear^.Z:= cHHZ+1
 else gear^.Z:= cUsualZ;
 
+case Kind of
+          gtFlame: Gear^.Boom := 2;  // some additional expl in there are x3, x4 this
+       gtHedgehog: Gear^.Boom := 30;
+           gtMine: Gear^.Boom := 50;
+           gtCase: Gear^.Boom := 25;
+        gtAirMine: Gear^.Boom := 25;
+     gtExplosives: Gear^.Boom := 75;
+        gtGrenade: Gear^.Boom := 50;
+          gtShell: Gear^.Boom := 50;
+            gtBee: Gear^.Boom := 50;
+    gtShotgunShot: Gear^.Boom := 25;
+     gtPickHammer: Gear^.Boom := 6;
+//           gtRope: Gear^.Boom := 2; could be funny to have rope attaching to hog deal small amount of dmg?
+     gtDEagleShot: Gear^.Boom := 7;
+       gtDynamite: Gear^.Boom := 75;
+    gtClusterBomb: Gear^.Boom := 20;
+     gtMelonPiece,
+        gtCluster: Gear^.Boom := Timer;
+         gtShover: Gear^.Boom := 30;
+      gtFirePunch: Gear^.Boom := 30;
+        gtAirBomb: Gear^.Boom := 30;
+      gtBlowTorch: Gear^.Boom := 2;
+         gtMortar: Gear^.Boom := 20;
+           gtWhip: Gear^.Boom := 30;
+       gtKamikaze: Gear^.Boom := 30; // both shove and explosion
+           gtCake: Gear^.Boom := cakeDmg; // why is cake damage a global constant
+     gtWatermelon: Gear^.Boom := 75;
+    gtHellishBomb: Gear^.Boom := 90;
+          gtDrill: if Gear^.State and gsttmpFlag = 0 then
+                        Gear^.Boom := 50
+                   else Gear^.Boom := 30;
+           gtBall: Gear^.Boom := 40;
+        gtRCPlane: Gear^.Boom := 25;
+// sniper rifle is distance linked, this Boom is just an arbitrary scaling factor applied to timer-based-damage
+// because, eh, why not..
+gtSniperRifleShot: Gear^.Boom := 100000;
+            gtEgg: Gear^.Boom := 10;
+          gtPiano: Gear^.Boom := 80;
+        gtGasBomb: Gear^.Boom := 20;
+    gtSineGunShot: Gear^.Boom := 35;
+          gtSMine: Gear^.Boom := 30;
+    gtSnowball: Gear^.Boom := 200000; // arbitrary scaling for the shove
+         gtHammer: if cDamageModifier > _1 then // scale it based on cDamageModifier?
+                         Gear^.Boom := 2
+                    else Gear^.Boom := 3;
+    gtPoisonCloud: Gear^.Boom := 20;
+          gtKnife: Gear^.Boom := 40000; // arbitrary scaling factor since impact-based
+    end;
 
 case Kind of
      gtGrenade,
--- a/hedgewars/uGearsUtils.pas	Fri Jan 01 19:14:59 2016 +0300
+++ b/hedgewars/uGearsUtils.pas	Fri Jan 01 19:15:32 2016 +0300
@@ -927,13 +927,42 @@
         AddFileLog('Assigned Gear coordinates (' + inttostr(x) + ',' + inttostr(y) + ')');
         end
     end
-    else
+else
     begin
     OutError('Can''t find place for Gear', false);
     if Gear^.Kind = gtHedgehog then
-        Gear^.Hedgehog^.Effects[heResurrectable] := 0;
-    DeleteGear(Gear);
-    Gear:= nil
+        begin
+        cnt:= 0;
+        if GameTicks = 0 then
+            begin
+            //AddFileLog('Trying to make a hole');
+            while (cnt < 1000) do
+                begin
+                inc(cnt);
+                x:= leftX+GetRandom(rightX-leftX-32)+16;
+                y:= topY+GetRandom(LAND_HEIGHT-topY-64)+48;
+                if NoGearsToAvoid(x, y, 100 div max(1,cnt div 100), 100 div max(1,cnt div 100)) then
+                    begin
+                    Gear^.State:= Gear^.State or gsttmpFlag;
+                    Gear^.X:= int2hwFloat(x);
+                    Gear^.Y:= int2hwFloat(y);
+                    AddFileLog('Picked a spot for hog at coordinates (' + inttostr(hwRound(Gear^.X)) + ',' + inttostr(hwRound(Gear^.Y)) + ')');
+                    cnt:= 2000
+                    end
+                end;
+            end;
+        if cnt < 2000 then
+            begin
+            Gear^.Hedgehog^.Effects[heResurrectable] := 0;
+            DeleteGear(Gear);
+            Gear:= nil
+            end
+        end
+    else
+        begin
+        DeleteGear(Gear);
+        Gear:= nil
+        end
     end
 end;
 
@@ -1074,7 +1103,7 @@
                     if r-hwRound(dx+dy) > 0 then
                         begin
                         dist:= hwRound(Distance(dx, dy));
-                        dmg:= ModifyDamage(min(r - dist, 25), t);
+                        dmg:= ModifyDamage(min(r - dist, Gear^.Boom), t);
                         end;
                     if dmg > 0 then
                         begin
@@ -1102,7 +1131,7 @@
                     if r-hwRound(dx+dy) > 0 then
                         begin
                         dist:= hwRound(Distance(dx, dy));
-                        dmg:= ModifyDamage(min(r - dist, 25), t);
+                        dmg:= ModifyDamage(min(r - dist, Gear^.Boom), t);
                         end;
                     if dmg > 0 then
                         begin
--- a/hedgewars/uLand.pas	Fri Jan 01 19:14:59 2016 +0300
+++ b/hedgewars/uLand.pas	Fri Jan 01 19:15:32 2016 +0300
@@ -271,9 +271,9 @@
     for x:= 0 to LAND_WIDTH - 1 do
     if Land[y, x] <> 0 then
         if (cReducedQuality and rqBlurryLand) = 0 then
-            LandPixels[y, x]:= p^[x] or AMask
+            LandPixels[y, x]:= p^[x]// or AMask
         else
-            LandPixels[y div 2, x div 2]:= p^[x] or AMask;
+            LandPixels[y div 2, x div 2]:= p^[x];// or AMask;
 
     p:= PLongwordArray(@(p^[Surface^.pitch div 4]));
     end;
@@ -289,7 +289,7 @@
 begin
     AddProgress();
 
-    tmpsurf:= SDL_CreateRGBSurface(SDL_SWSURFACE, LAND_WIDTH, LAND_HEIGHT, 32, RMask, GMask, BMask, 0);
+    tmpsurf:= SDL_CreateRGBSurface(SDL_SWSURFACE, LAND_WIDTH, LAND_HEIGHT, 32, RMask, GMask, BMask, AMask);
 
     TryDo(tmpsurf <> nil, 'Error creating pre-land surface', true);
     ColorizeLand(tmpsurf);
--- a/hedgewars/uScript.pas	Fri Jan 01 19:14:59 2016 +0300
+++ b/hedgewars/uScript.pas	Fri Jan 01 19:15:32 2016 +0300
@@ -921,29 +921,30 @@
             lua_pushinteger(L, Integer(gear^.ImpactSound));
             lua_pushinteger(L, gear^.nImpactSounds);
             lua_pushinteger(L, gear^.Tint);
-            lua_pushinteger(L, gear^.Damage)
+            lua_pushinteger(L, gear^.Damage);
+            lua_pushinteger(L, gear^.Boom)
             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); 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); lua_pushnil(L); lua_pushnil(L)
         end;
-    lc_getgearvalues:= 12
+    lc_getgearvalues:= 13
 end;
 
 function lc_setgearvalues(L : Plua_State) : LongInt; Cdecl;
 var gear : PGear;
 begin
-// Currently allows 1-13 params
-//    if CheckLuaParamCount(L, 13, 'SetGearValues', 'gearUid, Angle, Power, WDTimer, Radius, Density, Karma, DirAngle, AdvBounce, ImpactSound, # ImpactSounds, Tint, Damage') then
+// Currently allows 1-14 params
+//    if CheckLuaParamCount(L, 14, 'SetGearValues', 'gearUid, Angle, Power, WDTimer, Radius, Density, Karma, DirAngle, AdvBounce, ImpactSound, # ImpactSounds, Tint, Damage, Boom') then
 //        begin
         gear:= GearByUID(lua_tointeger(L, 1));
         if gear <> nil then
@@ -972,6 +973,8 @@
                 gear^.Tint := lua_tointeger(L, 12);
             if not lua_isnoneornil(L, 13) then
                 gear^.Damage := lua_tointeger(L, 13);
+            if not lua_isnoneornil(L, 14) then
+                gear^.Boom := lua_tointeger(L, 14);
             end;
 //        end
 //    else
--- a/hedgewars/uTeams.pas	Fri Jan 01 19:14:59 2016 +0300
+++ b/hedgewars/uTeams.pas	Fri Jan 01 19:15:32 2016 +0300
@@ -127,7 +127,7 @@
         if Gear <> nil then
            begin
            DeleteCI(Gear);
-           FindPlace(Gear, false, 0, LAND_WIDTH);
+           FindPlace(Gear, false, 0, LAND_WIDTH, true);
            if Gear <> nil then
                AddCI(Gear)
            end
--- a/hedgewars/uTypes.pas	Fri Jan 01 19:14:59 2016 +0300
+++ b/hedgewars/uTypes.pas	Fri Jan 01 19:15:32 2016 +0300
@@ -273,6 +273,7 @@
 // DirAngle is a 'real' - if you do not need it for rotation of sprite in uGearsRender, you can use it for any visual-only value
             DirAngle: real;
 // These are frequently overridden to serve some other purpose
+	    Boom: Longword;          // amount of damage caused by the gear
             Pos: Longword;           // Commonly overridden.  Example use is posCase values in uConsts.
             Angle, Power : Longword; // Used for hog aiming/firing.  Angle is rarely used as an Angle otherwise.
             Timer, WDTimer : LongWord;        // Typically used for some sort of gear timer. Time to explosion, remaining fuel...
--- a/hedgewars/uVariables.pas	Fri Jan 01 19:14:59 2016 +0300
+++ b/hedgewars/uVariables.pas	Fri Jan 01 19:15:32 2016 +0300
@@ -621,7 +621,7 @@
             (FileName:  'Egg'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
             Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprEgg
             (FileName:  'TargetBee'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprTargetBee
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprTargetBee
             (FileName:  'amBee'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
             Width:  128; Height: 128; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandBee
             (FileName:  'Feather'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;