hedgewars/uWorld.pas
branchui-scaling
changeset 15283 c4fd2813b127
parent 15209 fe79f62ef158
child 15436 617f4c092e3d
--- a/hedgewars/uWorld.pas	Wed May 16 18:22:28 2018 +0200
+++ b/hedgewars/uWorld.pas	Wed Jul 31 23:14:27 2019 +0200
@@ -31,8 +31,9 @@
 procedure DrawWorld(Lag: LongInt);
 procedure DrawWorldStereo(Lag: LongInt; RM: TRenderMode);
 procedure ShowMission(caption, subcaption, text: ansistring; icon, time : LongInt);
+procedure ShowMission(caption, subcaption, text: ansistring; icon, time : LongInt; forceDisplay : boolean);
 procedure HideMission;
-procedure SetAmmoTexts(ammoType: TAmmoType; name: ansistring; caption: ansistring; description: ansistring);
+procedure SetAmmoTexts(ammoType: TAmmoType; name: ansistring; caption: ansistring; description: ansistring; autoLabels: boolean);
 procedure ShakeCamera(amount: LongInt);
 procedure InitCameraBorders;
 procedure InitTouchInterface;
@@ -41,6 +42,7 @@
 procedure MoveCamera;
 procedure onFocusStateChanged;
 procedure updateCursorVisibility;
+procedure updateTouchWidgets(ammoType: TAmmoType);
 
 implementation
 uses
@@ -62,6 +64,7 @@
     , uCommands
     , uTeams
     , uDebug
+    , uInputHandler
 {$IFDEF USE_VIDEO_RECORDING}
     , uVideoRec
 {$ENDIF}
@@ -75,7 +78,7 @@
     timeTexture: PTexture;
     FPS: Longword;
     CountTicks: Longword;
-    prevPoint{, prevTargetPoint}: TPoint;
+    prevPoint: TPoint;
     amSel: TAmmoType = amNothing;
     missionTex: PTexture;
     missionTimer: LongInt;
@@ -104,7 +107,6 @@
       AMTypeMaskX     = $00000001;
       AMTypeMaskY     = $00000002;
       AMTypeMaskAlpha = $00000004;
-      //AMTypeMaskSlide = $00000008;
 
 {$IFDEF MOBILE}
       AMSlotSize = 48;
@@ -113,6 +115,22 @@
 {$ENDIF}
       AMSlotPadding = (AMSlotSize - 32) shr 1;
 
+{$IFDEF USE_LANDSCAPE_AMMOMENU}
+      amNumOffsetX = 0;
+      {$IFDEF USE_AM_NUMCOLUMN}
+      amNumOffsetY = AMSlotSize;
+      {$ELSE}
+      amNumOffsetY = 0;
+      {$ENDIF}
+{$ELSE}
+      amNumOffsetY = 0;
+      {$IFDEF USE_AM_NUMCOLUMN}
+      amNumOffsetX = AMSlotSize;
+      {$ELSE}
+      amNumOffsetX = 0;
+      {$ENDIF}
+{$ENDIF}
+
       cSendCursorPosTime = 50;
       cCursorEdgesDist   = 100;
 
@@ -120,22 +138,18 @@
 function AddGoal(s: ansistring; gf: longword; si: TGoalStrId; i: LongInt): ansistring;
 var t: ansistring;
 begin
-{$IFNDEF PAS2C}
     if (GameFlags and gf) <> 0 then
         begin
         t:= inttostr(i);
         s:= s + FormatA(trgoal[si], t) + '|'
         end;
-{$ENDIF}
     AddGoal:= s;
 end;
 
 function AddGoal(s: ansistring; gf: longword; si: TGoalStrId): ansistring;
 begin
-{$IFNDEF PAS2C}
     if (GameFlags and gf) <> 0 then
         s:= s + trgoal[si] + '|';
-{$ENDIF}
     AddGoal:= s;
 end;
 
@@ -158,15 +172,14 @@
             ClansArray[t]:= cp;
             ClansArray[t]^.ClanIndex:= t;
             ClansArray[0]^.ClanIndex:= 0;
-            if (LocalClan = t) then
-                LocalClan:= 0
-            else if (LocalClan = 0) then
-                LocalClan:= t
             end;
         end;
     CurrentTeam:= ClansArray[0]^.Teams[0];
     end;
 
+if (GameFlags and gfInvulnerable) <> 0 then
+    cTagsMask:= cTagsMask and (not htHealth);
+
 // if special game flags/settings are changed, add them to the game mode notice window and then show it
 g:= ''; // no text/things to note yet
 
@@ -175,10 +188,10 @@
     g:= LuaGoals + '|';
 
 // check different game flags
-g:= AddGoal(g, gfPlaceHog, gidPlaceHog); // placement?
 g:= AddGoal(g, gfKing, gidKing); // king?
 if ((GameFlags and gfKing) <> 0) and ((GameFlags and gfPlaceHog) = 0) then
     g:= AddGoal(g, gfAny, gidPlaceKing);
+g:= AddGoal(g, gfPlaceHog, gidPlaceHog); // placement?
 g:= AddGoal(g, gfTagTeam, gidTagTeam); // tag team mode?
 g:= AddGoal(g, gfSharedAmmo, gidSharedAmmo); // shared ammo?
 g:= AddGoal(g, gfPerHogAmmo, gidPerHogAmmo);
@@ -216,19 +229,21 @@
 
 // if the string has been set, show it for (default timeframe) seconds
 if length(g) > 0 then
-    ShowMission(trgoal[gidCaption], trgoal[gidSubCaption], g, 1, 0);
+    // choose icon
+    if ((GameFlags and gfKing) <> 0) then
+        // crown icon for King Mode
+        ShowMission(trgoal[gidCaption], trgoal[gidSubCaption], g, 0, 0)
+    else
+        // target icon for anything else
+        ShowMission(trgoal[gidCaption], trgoal[gidSubCaption], g, 1, 0);
 
-//cWaveWidth:= SpritesData[sprWater].Width;
-//cWaveHeight:= SpritesData[sprWater].Height;
 cWaveHeight:= 32;
 
 InitCameraBorders();
 uCursor.init();
 prevPoint.X:= 0;
 prevPoint.Y:= cScreenHeight div 2;
-//prevTargetPoint.X:= 0;
-//prevTargetPoint.Y:= 0;
-WorldDx:=  -(LongInt(leftX + (playWidth div 2))); // -(LAND_WIDTH div 2);// + cScreenWidth div 2;
+WorldDx:=  -(LongInt(leftX + (playWidth div 2)));
 WorldDy:=  -(LAND_HEIGHT - (playHeight div 2)) + (cScreenHeight div 2);
 
 //aligns it to the bottom of the screen, minus the border
@@ -398,6 +413,28 @@
         source.y:= frame.y;
         end;
     end;
+
+with utilityWidget2 do
+    begin
+    show:= false;
+    sprite:= sprBounceButton;
+    frame.w:= Round(spritesData[sprite].Texture^.w * buttonScale);
+    frame.h:= Round(spritesData[sprite].Texture^.h * buttonScale);
+    frame.x:= utilityWidget.frame.x + Round(frame.w * 1.25);
+    frame.y:= arrowLeft.frame.y - Round(frame.h * 1.25);
+    active.x:= frame.x;
+    active.y:= frame.y;
+    active.w:= frame.w;
+    active.h:= frame.h;
+    with moveAnim do
+        begin
+        target.x:= frame.x;
+        target.y:= frame.y;
+        source.x:= frame.x;
+        source.y:= frame.y;
+        end;
+    end;
+
 {$ENDIF}
 end;
 
@@ -418,7 +455,10 @@
     STurns: LongInt;
     amSurface: PSDL_Surface;
     AMRect: TSDL_Rect;
-{$IFDEF USE_AM_NUMCOLUMN}tmpsurf: PSDL_Surface;{$ENDIF}
+{$IFDEF USE_AM_NUMCOLUMN}
+    tmpsurf: PSDL_Surface;
+    usesDefaultSlotKeys: boolean;
+{$ENDIF}
 begin
     if cOnlyStats then exit(nil);
 
@@ -454,6 +494,9 @@
 
     x:= AMRect.x;
     y:= AMRect.y;
+{$IFDEF USE_AM_NUMCOLUMN}
+    usesDefaultSlotKeys:= CheckDefaultSlotKeys;
+{$ENDIF USE_AM_NUMCOLUMN}
     for i:= 0 to cMaxSlotIndex do
         if (i <> cHiddenSlotIndex) and (Ammo^[i, 0].Count > 0) then
             begin
@@ -463,7 +506,13 @@
             x:= AMRect.x;
 {$ENDIF}
 {$IFDEF USE_AM_NUMCOLUMN}
-            tmpsurf:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar('F' + IntToStr(i+1)), cWhiteColorChannels);
+            // Ammo slot number column
+            if usesDefaultSlotKeys then
+                // F1, F2, F3, F4, ...
+                tmpsurf:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar('F'+IntToStr(i+1)), cWhiteColorChannels)
+            else
+                // 1, 2, 3, 4, ...
+                tmpsurf:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar(IntToStr(i+1)), cWhiteColorChannels);
             copyToXY(tmpsurf, amSurface,
                      x + AMSlotPadding + (AMSlotSize shr 1) - (tmpsurf^.w shr 1),
                      y + AMSlotPadding + (AMSlotSize shr 1) - (tmpsurf^.h shr 1));
@@ -603,12 +652,16 @@
 
 if AMState = AMShowingUp then // show ammo menu
     begin
-    if (cReducedQuality and rqSlowMenu) <> 0 then
+    // No "appear" animation in low quality or playing with very short turn time.
+    if ((cReducedQuality and rqSlowMenu) <> 0) or (cHedgehogTurnTime <= 10000) then
         begin
         AMShiftX:= 0;
         AMShiftY:= 0;
+        CursorPoint.X:= AmmoRect.x + AmmoRect.w - 3;
+        CursorPoint.Y:= cScreenHeight - AmmoRect.y - amNumOffsetY - 1;
         AMState:= AMShowing;
         end
+    // "Appear" animation
     else
         if AMAnimState < 1 then
             begin
@@ -621,19 +674,22 @@
             begin
             AMShiftX:= 0;
             AMShiftY:= 0;
-            CursorPoint.X:= AmmoRect.x + AmmoRect.w;
-            CursorPoint.Y:= AmmoRect.y;
+            CursorPoint.X:= AmmoRect.x + AmmoRect.w - 3;
+            CursorPoint.Y:= cScreenHeight - AmmoRect.y - amNumOffsetY - 1;
             AMState:= AMShowing;
             end;
     end;
 if AMState = AMHiding then // hide ammo menu
     begin
-    if (cReducedQuality and rqSlowMenu) <> 0 then
+    // No "disappear" animation (see above)
+    if ((cReducedQuality and rqSlowMenu) <> 0) or (cHedgehogTurnTime <= 10000) then
         begin
         AMShiftX:= AMShiftTargetX;
         AMShiftY:= AMShiftTargetY;
+        prevPoint:= CursorPoint;
         AMState:= AMHidden;
         end
+    // "Disappear" animation
     else
         if AMAnimState < 1 then
             begin
@@ -647,7 +703,6 @@
             AMShiftX:= AMShiftTargetX;
             AMShiftY:= AMShiftTargetY;
             prevPoint:= CursorPoint;
-            //prevTargetPoint:= TargetCursorPoint;
             AMState:= AMHidden;
             end;
     end;
@@ -736,7 +791,7 @@
                         Ammoz[Ammo^[Slot, Pos].AmmoType].NameTex);
             if Ammo^[Slot, Pos].Count < AMMO_INFINITE then
                 DrawTexture(AmmoRect.x + AmmoRect.w - 20 - (CountTexz[Ammo^[Slot, Pos].Count]^.w),
-                            AmmoRect.y + AmmoRect.h - BORDERSIZE - (AMslotSize shr 1) - (CountTexz[Ammo^[Slot, Pos].Count]^.w shr 1),
+                            AmmoRect.y + AmmoRect.h - BORDERSIZE - (AMslotSize shr 1) - (CountTexz[Ammo^[Slot, Pos].Count]^.h shr 1),
                             CountTexz[Ammo^[Slot, Pos].Count]);
 
             if bSelected and (Ammoz[Ammo^[Slot, Pos].AmmoType].SkipTurns - CurrentTeam^.Clan^.TurnNumber < 0) then
@@ -745,23 +800,7 @@
                 SetWeapon(Ammo^[Slot, Pos].AmmoType);
                 bSelected:= false;
                 FreeAndNilTexture(WeaponTooltipTex);
-{$IFDEF USE_TOUCH_INTERFACE}//show the aiming buttons + animation
-                if (Ammo^[Slot, Pos].Propz and ammoprop_NeedUpDown) <> 0 then
-                    begin
-                    if (not arrowUp.show) then
-                        begin
-                        animateWidget(@arrowUp, true, true);
-                        animateWidget(@arrowDown, true, true);
-                        end;
-                    end
-                else
-                    if arrowUp.show then
-                        begin
-                        animateWidget(@arrowUp, true, false);
-                        animateWidget(@arrowDown, true, false);
-                        end;
-                SetUtilityWidgetState(Ammo^[Slot, Pos].AmmoType);
-{$ENDIF}
+                updateTouchWidgets(Ammo^[Slot, Pos].AmmoType);
                 exit
                 end;
             end
@@ -883,10 +922,8 @@
 
 procedure RenderWorldEdge;
 var
-    //VertexBuffer: array [0..3] of TVertex2f;
     tmp, w: LongInt;
     rect: TSDL_Rect;
-    //c1, c2: LongWord; // couple of colours for edges
 begin
 if (WorldEdge <> weNone) and (WorldEdge <> weSea) then
     begin
@@ -894,7 +931,7 @@
 
     rect.y:= ViewTopY;
     rect.h:= ViewHeight;
-    tmp:= LongInt(leftX) + WorldDx;
+    tmp:= leftX + WorldDx;
     w:= tmp - ViewLeftX;
 
     if w > 0 then
@@ -906,7 +943,7 @@
             DrawLineOnScreen(tmp - 1, ViewTopY, tmp - 1, ViewBottomY, 2, $54, $54, $FF, $FF);
         end;
 
-    tmp:= LongInt(rightX) + WorldDx;
+    tmp:= rightX + WorldDx;
     w:= ViewRightX - tmp;
 
     if w > 0 then
@@ -918,110 +955,6 @@
             DrawLineOnScreen(tmp - 1, ViewTopY, tmp - 1, ViewBottomY, 2, $54, $54, $FF, $FF);
         end;
 
-    (*
-    WARNING: the following render code is outdated and does not work with
-             current Render.pas ! - don't just uncomment without fixing it first
-
-    glDisable(GL_TEXTURE_2D);
-    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-    if (WorldEdge = weWrap) or (worldEdge = weBounce) then
-        glColor4ub($00, $00, $00, $40)
-    else
-        begin
-        glEnableClientState(GL_COLOR_ARRAY);
-        glColorPointer(4, GL_UNSIGNED_BYTE, 0, @WorldFade[0]);
-        end;
-
-    glPushMatrix;
-    glTranslatef(WorldDx, WorldDy, 0);
-
-    VertexBuffer[0].X:= leftX-20;
-    VertexBuffer[0].Y:= -3500;
-    VertexBuffer[1].X:= leftX-20;
-    VertexBuffer[1].Y:= cWaterLine+cVisibleWater;
-    VertexBuffer[2].X:= leftX+30;
-    VertexBuffer[2].Y:= cWaterLine+cVisibleWater;
-    VertexBuffer[3].X:= leftX+30;
-    VertexBuffer[3].Y:= -3500;
-
-    glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
-    glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
-
-    VertexBuffer[0].X:= rightX+20;
-    VertexBuffer[1].X:= rightX+20;
-    VertexBuffer[2].X:= rightX-30;
-    VertexBuffer[3].X:= rightX-30;
-
-    glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
-    glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
-
-    glColorPointer(4, GL_UNSIGNED_BYTE, 0, @WorldEnd[0]);
-
-    VertexBuffer[0].X:= -5000;
-    VertexBuffer[1].X:= -5000;
-    VertexBuffer[2].X:= leftX-20;
-    VertexBuffer[3].X:= leftX-20;
-
-    glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
-    glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
-
-    VertexBuffer[0].X:= rightX+5000;
-    VertexBuffer[1].X:= rightX+5000;
-    VertexBuffer[2].X:= rightX+20;
-    VertexBuffer[3].X:= rightX+20;
-
-    glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
-    glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
-
-    glPopMatrix;
-    glDisableClientState(GL_COLOR_ARRAY);
-    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-
-    glColor4ub($FF, $FF, $FF, $FF); // must not be Tint() as color array seems to stay active and color reset is required
-    glEnable(GL_TEXTURE_2D);
-
-    // I'd still like to have things happen to the border when a wrap or bounce just occurred, based on a timer
-    if WorldEdge = weBounce then
-        begin
-        // could maybe alternate order of these on a bounce, or maybe drop the outer ones.
-        if LeftImpactTimer mod 2 = 0 then
-            begin
-            c1:= $5454FFFF; c2:= $FFFFFFFF;
-            end
-        else begin
-            c1:= $FFFFFFFF; c2:= $5454FFFF;
-            end;
-        DrawLine(leftX, -3000, leftX, cWaterLine+cVisibleWater, 7.0,   c1);
-        DrawLine(leftX, -3000, leftX, cWaterLine+cVisibleWater, 5.0,   c2);
-        DrawLine(leftX, -3000, leftX, cWaterLine+cVisibleWater, 3.0,   c1);
-        DrawLine(leftX, -3000, leftX, cWaterLine+cVisibleWater, 1.0,   c2);
-        if RightImpactTimer mod 2 = 0 then
-            begin
-            c1:= $5454FFFF; c2:= $FFFFFFFF;
-            end
-        else begin
-            c1:= $FFFFFFFF; c2:= $5454FFFF;
-            end;
-        DrawLine(rightX, -3000, rightX, cWaterLine+cVisibleWater, 7.0, c1);
-        DrawLine(rightX, -3000, rightX, cWaterLine+cVisibleWater, 5.0, c2);
-        DrawLine(rightX, -3000, rightX, cWaterLine+cVisibleWater, 3.0, c1);
-        DrawLine(rightX, -3000, rightX, cWaterLine+cVisibleWater, 1.0, c2)
-        end
-    else if WorldEdge = weWrap then
-        begin
-        DrawLine(leftX, -3000, leftX, cWaterLine+cVisibleWater, 5.0, $A0, $30, $60, max(50,255-LeftImpactTimer));
-        DrawLine(leftX, -3000, leftX, cWaterLine+cVisibleWater, 2.0, $FF0000FF);
-        DrawLine(rightX, -3000, rightX, cWaterLine+cVisibleWater, 5.0, $A0, $30, $60, max(50,255-RightImpactTimer));
-        DrawLine(rightX, -3000, rightX, cWaterLine+cVisibleWater, 2.0, $FF0000FF);
-        end
-    else
-        begin
-        DrawLine(leftX, -3000, leftX, cWaterLine+cVisibleWater, 5.0, $2E8B5780);
-        DrawLine(rightX, -3000, rightX, cWaterLine+cVisibleWater, 5.0, $2E8B5780)
-        end;
-    if LeftImpactTimer > Lag then dec(LeftImpactTimer,Lag) else LeftImpactTimer:= 0;
-    if RightImpactTimer > Lag then dec(RightImpactTimer,Lag) else RightImpactTimer:= 0
-    *)
     end;
 end;
 
@@ -1075,25 +1008,29 @@
 
         TeamHealthBarWidth:= cTeamHealthWidth * TeamHealthBarHealth div MaxTeamHealth;
 
-        // draw health bar
+        // draw team health bar
         r.x:= 0;
         r.y:= 0;
         r.w:= 2 + TeamHealthBarWidth;
         r.h:= htex^.h;
         DrawTextureFromRect(14, cScreenHeight + DrawHealthY + smallScreenOffset, @r, htex);
 
-        // draw health bars right border
+        // draw health bar's right border
         inc(r.x, cTeamHealthWidth + 2);
         r.w:= 3;
         DrawTextureFromRect(TeamHealthBarWidth + 15, cScreenHeight + DrawHealthY + smallScreenOffset, @r, htex);
 
+        // draw hedgehog health separators in team health bar
         h:= 0;
         if not hasGone then
             for i:= 0 to cMaxHHIndex do
                 begin
                 inc(h, Hedgehogs[i].HealthBarHealth);
                 if (h < TeamHealthBarHealth) and (Hedgehogs[i].HealthBarHealth > 0) then
-                    DrawTexture(15 + h * TeamHealthBarWidth div TeamHealthBarHealth, cScreenHeight + DrawHealthY + smallScreenOffset + 1, SpritesData[sprSlider].Texture);
+                    if (IsTooDarkToRead(Clan^.Color)) then
+                        DrawTexture(15 + h * TeamHealthBarWidth div TeamHealthBarHealth, cScreenHeight + DrawHealthY + smallScreenOffset + 1, SpritesData[sprSlider].Texture)
+                    else
+                        DrawTexture(15 + h * TeamHealthBarWidth div TeamHealthBarHealth, cScreenHeight + DrawHealthY + smallScreenOffset + 1, SpritesData[sprSliderInverted].Texture);
                 end;
 
         // draw Lua value, if set
@@ -1149,7 +1086,13 @@
             h:= -NameTagTex^.w - 24;
             if OwnerTex <> nil then
                 h:= h - OwnerTex^.w - 4;
+            if (IsTooDarkToRead(TeamsArray[t]^.Clan^.Color)) then
+                DrawSpriteRotatedF(sprFingerBackInv, h, cScreenHeight + DrawHealthY + smallScreenOffset + 2 + SpritesData[sprFingerBackInv].Width div 4, 0, 1, -90)
+            else
+                DrawSpriteRotatedF(sprFingerBack, h, cScreenHeight + DrawHealthY + smallScreenOffset + 2 + SpritesData[sprFingerBack].Width div 4, 0, 1, -90);
+            Tint(TeamsArray[t]^.Clan^.Color shl 8 or $FF);
             DrawSpriteRotatedF(sprFinger, h, cScreenHeight + DrawHealthY + smallScreenOffset + 2 + SpritesData[sprFinger].Width div 4, 0, 1, -90);
+            untint;
             end;
         end;
       end;
@@ -1162,6 +1105,24 @@
 VisibleTeamsCount:= v;
 end;
 
+procedure RenderAttackBar();
+var i: LongInt;
+    tdx, tdy: Double;
+begin
+    if CurrentTeam <> nil then
+        case AttackBar of
+        2: with CurrentHedgehog^ do
+                begin
+                tdx:= hwSign(Gear^.dX) * Sin(Gear^.Angle * Pi / cMaxAngle);
+                tdy:= - Cos(Gear^.Angle * Pi / cMaxAngle);
+                for i:= (Gear^.Power * 24) div cPowerDivisor downto 0 do
+                    DrawSprite(sprPower,
+                            hwRound(Gear^.X) + GetLaunchX(CurAmmoType, hwSign(Gear^.dX), Gear^.Angle) + LongInt(round(WorldDx + tdx * (24 + i * 2))) - 16,
+                            hwRound(Gear^.Y) + GetLaunchY(CurAmmoType, Gear^.Angle) + LongInt(round(WorldDy + tdy * (24 + i * 2))) - 16,
+                            i)
+                end;
+        end;
+end;
 
 var preShiftWorldDx: LongInt;
 
@@ -1181,10 +1142,9 @@
 var i, t: LongInt;
     spr: TSprite;
     r: TSDL_Rect;
-    tdx, tdy: Double;
     s: shortstring;
     offsetX, offsetY, screenBottom: LongInt;
-    replicateToLeft, replicateToRight, tmp: boolean;
+    replicateToLeft, replicateToRight, isNotHiddenByCinematic: boolean;
 {$IFDEF USE_VIDEO_RECORDING}
     a: Byte;
 {$ENDIF}
@@ -1196,8 +1156,8 @@
     end
 else
     begin
-    replicateToLeft := (LongInt(leftX)  + WorldDx > ViewLeftX);
-    replicateToRight:= (LongInt(rightX) + WorldDx < ViewRightX);
+    replicateToLeft := (leftX  + WorldDx > ViewLeftX);
+    replicateToRight:= (rightX + WorldDx < ViewRightX);
     end;
 
 ScreenBottom:= (WorldDy - trunc(cScreenHeight/cScaleFactor) - (cScreenHeight div 2) + cWaterLine);
@@ -1205,6 +1165,7 @@
 // note: offsetY is negative!
 offsetY:= 10 *  Min(0, -145 - ScreenBottom); // TODO limit this in the other direction too
 
+// Sky and horizont
 if (cReducedQuality and rqNoBackground) = 0 then
     begin
         // Offsets relative to camera - spare them to wimpier cpus, no bg or flakes for them anyway
@@ -1224,9 +1185,9 @@
             untint;
     end;
 
-DrawVisualGears(0);
+DrawVisualGears(0, false);
 ChangeDepth(RM, -cStereo_MidDistance);
-DrawVisualGears(4);
+DrawVisualGears(4, false);
 
 if (cReducedQuality and rq2DWater) = 0 then
     begin
@@ -1245,7 +1206,7 @@
     DrawWaves(-1, 100, - cWaveHeight div 2, - cWaveHeight div 2, 0);
 
 ChangeDepth(RM, cStereo_Land);
-DrawVisualGears(5);
+DrawVisualGears(5, false);
 DrawLand(WorldDx, WorldDy);
 
 if replicateToLeft then
@@ -1264,56 +1225,27 @@
 
 DrawWater(255, 0, 0);
 
-(*
-// Attack bar
-    if CurrentTeam <> nil then
-        case AttackBar of
-        //1: begin
-        //r:= StuffPoz[sPowerBar];
-        //{$WARNINGS OFF}
-        //r.w:= (CurrentHedgehog^.Gear^.Power * 256) div cPowerDivisor;
-        //{$WARNINGS ON}
-        //DrawSpriteFromRect(r, cScreenWidth - 272, cScreenHeight - 48, 16, 0, Surface);
-        //end;
-        2: with CurrentHedgehog^ do
-                begin
-                tdx:= hwSign(Gear^.dX) * Sin(Gear^.Angle * Pi / cMaxAngle);
-                tdy:= - Cos(Gear^.Angle * Pi / cMaxAngle);
-                for i:= (Gear^.Power * 24) div cPowerDivisor downto 0 do
-                    DrawSprite(sprPower,
-                            hwRound(Gear^.X) + GetLaunchX(CurAmmoType, hwSign(Gear^.dX), Gear^.Angle) + LongInt(round(WorldDx + tdx * (24 + i * 2))) - 16,
-                            hwRound(Gear^.Y) + GetLaunchY(CurAmmoType, Gear^.Angle) + LongInt(round(WorldDy + tdy * (24 + i * 2))) - 16,
-                            i)
-                end
-        end;
-*)
-
-tmp:= bShowFinger;
-bShowFinger:= false;
-
 if replicateToLeft then
     begin
     ShiftWorld(-1);
-    DrawVisualGears(1);
+    DrawVisualGears(1, true);
     DrawGears();
-    DrawVisualGears(6);
+    DrawVisualGears(6, true);
     UnshiftWorld();
     end;
 
 if replicateToRight then
     begin
     ShiftWorld(1);
-    DrawVisualGears(1);
+    DrawVisualGears(1, true);
     DrawGears();
-    DrawVisualGears(6);
+    DrawVisualGears(6, true);
     UnshiftWorld();
     end;
 
-bShowFinger:= tmp;
-
-DrawVisualGears(1);
+DrawVisualGears(1, false);
 DrawGears;
-DrawVisualGears(6);
+DrawVisualGears(6, false);
 
 
 if SuddenDeathDmg then
@@ -1321,13 +1253,12 @@
 else
     DrawWater(WaterOpacity, 0, 0);
 
-    // Waves
+// Waves
 ChangeDepth(RM, cStereo_Water_near);
 DrawWaves( 1, 25 - WorldDx div 9, 0, 0, 12);
 
 if (cReducedQuality and rq2DWater) = 0 then
     begin
-    //DrawWater(WaterOpacity, - offsetY div 40);
     ChangeDepth(RM, cStereo_Water_near);
     DrawWaves(-1, 50 + WorldDx div 6, - offsetY div 40, 23, 8);
     if SuddenDeathDmg then
@@ -1346,7 +1277,27 @@
     else
         DrawWaves(-1, 50, cWaveHeight div 2, cWaveHeight div 2, 0);
 
-DrawGearsTimers;
+// line at airplane height for certain airstrike types (when spawning height is important)
+with CurrentHedgehog^ do
+    if (isCursorVisible) and ((CurAmmoType = amNapalm) or (CurAmmoType = amMineStrike) or (((GameFlags and gfMoreWind) <> 0) and ((CurAmmoType = amDrillStrike) or (CurAmmoType = amAirAttack)))) then
+        DrawLine(-3000, topY-300, 7000, topY-300, 3.0, (Team^.Clan^.Color shr 16), (Team^.Clan^.Color shr 8) and $FF, Team^.Clan^.Color and $FF, $FF);
+
+// gear HUD extras (fuel indicator, secondary ammo, etc.)
+if replicateToLeft then
+    begin
+    ShiftWorld(-1);
+    DrawGearsGui();
+    UnshiftWorld();
+    end;
+
+if replicateToRight then
+    begin
+    ShiftWorld(1);
+    DrawGearsGui();
+    UnshiftWorld();
+    end;
+
+DrawGearsGui();
 
 // everything after this ChangeDepth will be drawn outside the screen
 // note: negative parallax gears should last very little for a smooth stereo effect
@@ -1355,18 +1306,18 @@
     if replicateToLeft then
         begin
         ShiftWorld(-1);
-        DrawVisualGears(2);
+        DrawVisualGears(2, true);
         UnshiftWorld();
         end;
 
     if replicateToRight then
         begin
         ShiftWorld(1);
-        DrawVisualGears(2);
+        DrawVisualGears(2, true);
         UnshiftWorld();
         end;
 
-    DrawVisualGears(2);
+    DrawVisualGears(2, false);
 
 // everything after this ResetDepth will be drawn at screen level (depth = 0)
 // note: everything that needs to be readable should be on this level
@@ -1375,39 +1326,101 @@
     if replicateToLeft then
         begin
         ShiftWorld(-1);
-        DrawVisualGears(3);
+        DrawVisualGears(3, true);
         UnshiftWorld();
         end;
 
     if replicateToRight then
         begin
         ShiftWorld(1);
-        DrawVisualGears(3);
+        DrawVisualGears(3, true);
         UnshiftWorld();
         end;
 
-    DrawVisualGears(3);
+    DrawVisualGears(3, false);
 
-{$WARNINGS OFF}
-// Target
+// Target (e.g. air attack, bee, ...)
 if (TargetPoint.X <> NoPointX) and (CurrentTeam <> nil) and (CurrentHedgehog <> nil) then
     begin
     with PHedgehog(CurrentHedgehog)^ do
         begin
         if CurAmmoType = amBee then
-            DrawSpriteRotatedF(sprTargetBee, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360)
+            spr:= sprTargetBee
         else
-            DrawSpriteRotatedF(sprTargetP, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360)
-        end
+            spr:= sprTargetP;
+        if replicateToLeft then
+            begin
+            ShiftWorld(-1);
+            if spr = sprTargetP then
+                begin
+                if IsTooDarkToRead(Team^.Clan^.Color) then
+                    DrawSpriteRotatedF(sprTargetPBackInv, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360)
+                else
+                    DrawSpriteRotatedF(sprTargetPBack, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360);
+                Tint(Team^.Clan^.Color shl 8 or $FF);
+                end;
+            DrawSpriteRotatedF(spr, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360);
+            if spr = sprTargetP then
+                untint;
+            UnshiftWorld();
+            end;
+
+        if replicateToRight then
+            begin
+            ShiftWorld(1);
+            if spr = sprTargetP then
+                begin
+                if IsTooDarkToRead(Team^.Clan^.Color) then
+                    DrawSpriteRotatedF(sprTargetPBackInv, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360)
+                else
+                    DrawSpriteRotatedF(sprTargetPBack, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360);
+                Tint(Team^.Clan^.Color shl 8 or $FF);
+                end;
+            DrawSpriteRotatedF(spr, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360);
+            if spr = sprTargetP then
+                untint;
+            UnshiftWorld();
+            end;
+
+        if spr = sprTargetP then
+            begin
+            if IsTooDarkToRead(Team^.Clan^.Color) then
+                DrawSpriteRotatedF(sprTargetPBackInv, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360)
+            else
+                DrawSpriteRotatedF(sprTargetPBack, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360);
+            Tint(Team^.Clan^.Color shl 8 or $FF);
+            end;
+        DrawSpriteRotatedF(spr, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360);
+        if spr = sprTargetP then
+            untint;
+        end;
     end;
-{$WARNINGS ON}
+
+// Attack bar
+if replicateToLeft then
+    begin
+    ShiftWorld(-1);
+    RenderAttackBar();
+    UnshiftWorld();
+    end;
 
+if replicateToRight then
+    begin
+    ShiftWorld(1);
+    RenderAttackBar();
+    UnshiftWorld();
+    end;
+
+RenderAttackBar();
+
+// World edge
 RenderWorldEdge();
 
-// this scale is used to keep the various widgets at the same dimension at all zoom levels
+// This scale is used to keep the various widgets at the same dimension at all zoom levels
 SetScale(cDefaultZoomLevel);
 
-// cinematic effects
+isNotHiddenByCinematic:= true;
+// Cinematic Mode: Determine effects and state
 if CinematicScript or (InCinematicMode and autoCameraOn
     and ((CurrentHedgehog = nil) or CurrentHedgehog^.Team^.ExtDriven
     or (CurrentHedgehog^.BotLevel <> 0) or (GameType = gmtDemo))) then
@@ -1416,7 +1429,10 @@
         begin
         inc(CinematicSteps, Lag);
         if CinematicSteps > 300 then
-        CinematicSteps:= 300;
+            begin
+            CinematicSteps:= 300;
+            isNotHiddenByCinematic:= false;
+            end;
         end;
     end
 else if CinematicSteps > 0 then
@@ -1426,22 +1442,8 @@
         CinematicSteps:= 0;
     end;
 
-// render black bars
-if CinematicSteps > 0 then
-    begin
-    r.x:= ViewLeftX;
-    r.w:= ViewWidth;
-    r.y:= ViewTopY;
-    CinematicBarH:= (ViewHeight * CinematicSteps) div 2048;
-    r.h:= CinematicBarH;
-    DrawRect(r, 0, 0, 0, $FF, true);
-    r.y:= ViewBottomY - r.h;
-    DrawRect(r, 0, 0, 0, $FF, true);
-    end;
-
-
 // Turn time
-if UIDisplay <> uiNone then
+if (UIDisplay <> uiNone) and (isNotHiddenByCinematic) then
     begin
 {$IFDEF USE_TOUCH_INTERFACE}
     offsetX:= cScreenHeight - 13;
@@ -1480,33 +1482,79 @@
         DrawSprite(sprFrame, -(cScreenWidth shr 1) + t - 4 + offsetY, cScreenHeight - offsetX, 0);
         end;
 
-// Captions
-    DrawCaptions
     end;
 
+// Team bars
+if (UIDisplay = uiAll) and (isNotHiddenByCinematic) then
+    RenderTeamsHealth;
+
+// Current hedgehog health in top left corner
+if ((UIDisplay = uiAll) or (UIDisplay = uiNoTeams)) and (isNotHiddenByCinematic) and
+        (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and
+        (CurrentHedgehog^.HealthTagTex <> nil) and
+        ((CurrentHedgehog^.Gear^.State and gstHHDriven) <> 0) then
+    begin
+    t:= 11;
+    i:= t;
 {$IFDEF USE_TOUCH_INTERFACE}
-// Draw buttons Related to the Touch interface
-DrawScreenWidget(@arrowLeft);
-DrawScreenWidget(@arrowRight);
-DrawScreenWidget(@arrowUp);
-DrawScreenWidget(@arrowDown);
-
-DrawScreenWidget(@fireButton);
-DrawScreenWidget(@jumpWidget);
-DrawScreenWidget(@AMWidget);
-DrawScreenWidget(@pauseButton);
-DrawScreenWidget(@utilityWidget);
+    i:= t + pauseButton.frame.y + pauseButton.frame.h;
 {$ENDIF}
 
-if UIDisplay = uiAll then
-    RenderTeamsHealth;
+    // Hide health and healh icons in gfInvulnerable mode (except heResurrectable)
+    if ((GameFlags and gfInvulnerable) = 0) then
+    begin
+    // Health tag
+    DrawTexture(cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - 16, i, CurrentHedgehog^.HealthTagTex);
+    inc(t, CurrentHedgehog^.HealthTagTex^.h);
+    cDemoClockFPSOffsetY:= t;
+
+    t:= SpritesData[sprHealthHud].Width + 18;
+    // Main health icon. Appearance depends on game mode and poisoning state
+    if ((GameFlags and gfResetHealth) = 0) then
+        if (CurrentHedgehog^.Effects[hePoisoned] <> 0) then
+            DrawSprite(sprHealthPoisonHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t), i, 0)
+        else
+            DrawSprite(sprHealthHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t), i, 0)
+    else
+        if (CurrentHedgehog^.Effects[hePoisoned] <> 0) then
+            DrawSprite(sprMedicPoisonHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t), i, 0)
+        else
+            DrawSprite(sprMedicHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t), i, 0);
+    // Put halo above health icon for resurrectable hog
+    if (CurrentHedgehog^.Effects[heResurrectable] <> 0) then
+        DrawSprite(sprHaloHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t - 2), i - SpritesData[sprHaloHud].Height + 1, 0);
 
-// Lag alert
-if isInLag then
-    DrawSprite(sprLag, 32 - (cScreenWidth shr 1), 32, (RealTicks shr 7) mod 12);
+    // Additional health-related states
+    inc(t, 2);
+    // Invulnerable
+    if (CurrentHedgehog^.Effects[heInvulnerable] <> 0) then
+        begin
+        inc(t, SpritesData[sprInvulnHud].Width + 2);
+        DrawSprite(sprInvulnHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t), i, 0)
+        end
+    // Karma
+    else if ((GameFlags and gfKarma) <> 0) then
+        begin
+        inc(t, SpritesData[sprKarmaHud].Width + 2);
+        DrawSprite(sprKarmaHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t), i, 0)
+        end;
+    // Vampirism
+    if cVampiric then
+        begin
+        inc(t, SpritesData[sprVampHud].Width + 2);
+        DrawSprite(sprVampHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t), i, 0);
+        end;
+    end
+    // in gfInvulnerable mode ...
+    else if (CurrentHedgehog^.Effects[heResurrectable] <> 0) then
+        // show halo for resurrectable hog
+        DrawSprite(sprHaloHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t - 2), i, 0);
+    end
+else
+    cDemoClockFPSOffsetY:= 0;
 
 // Wind bar
-if UIDisplay <> uiNone then
+if (UIDisplay <> uiNone) and (isNotHiddenByCinematic) then
     begin
 {$IFDEF USE_TOUCH_INTERFACE}
     offsetX:= cScreenHeight - 13;
@@ -1519,7 +1567,7 @@
     if WindBarWidth > 0 then
         begin
         {$WARNINGS OFF}
-        r.x:= 8 - (RealTicks shr 6) mod 8;
+        r.x:= 8 - (RealTicks shr 6) mod 9;
         {$WARNINGS ON}
         r.y:= 0;
         r.w:= WindBarWidth;
@@ -1530,7 +1578,7 @@
         if WindBarWidth < 0 then
         begin
         {$WARNINGS OFF}
-        r.x:= (Longword(WindBarWidth) + RealTicks shr 6) mod 8;
+        r.x:= (Longword(WindBarWidth) + RealTicks shr 6) mod 9;
         {$WARNINGS ON}
         r.y:= 0;
         r.w:= - WindBarWidth;
@@ -1539,28 +1587,97 @@
         end
     end;
 
-{$IFNDEF USE_TOUCH_INTERFACE}
 // Indicators for global effects (extra damage, low gravity)
-// TODO: Add support for touch interface (need to find out correct offset)
-if UIDisplay <> uiNone then
+if (UIDisplay <> uiNone) and (isNotHiddenByCinematic) then
     begin
+{$IFDEF USE_TOUCH_INTERFACE}
+    offsetX:= (cScreenWidth shr 1) - 95;
+    offsetY:= cScreenHeight - 21;
+{$ELSE}
     offsetX:= 45;
     offsetY:= 51;
+{$ENDIF}
 
     if cDamageModifier = _1_5 then
         begin
             DrawTextureF(ropeIconTex, 1, (cScreenWidth shr 1) - offsetX, cScreenHeight - offsetY, 0, 1, 32, 32);
             DrawTextureF(SpritesData[sprAMAmmos].Texture, 0.90, (cScreenWidth shr 1) - offsetX, cScreenHeight - offsetY, ord(amExtraDamage) - 1, 1, 32, 32);
+{$IFDEF USE_TOUCH_INTERFACE}
+            offsetX := offsetX - 33
+{$ELSE}
             offsetX := offsetX + 33
+{$ENDIF}
         end;
     if (cLowGravity) or ((GameFlags and gfLowGravity) <> 0) then
         begin
             DrawTextureF(ropeIconTex, 1, (cScreenWidth shr 1) - offsetX, cScreenHeight - offsetY, 0, 1, 32, 32);
             DrawTextureF(SpritesData[sprAMAmmos].Texture, 0.90, (cScreenWidth shr 1) - offsetX, cScreenHeight - offsetY, ord(amLowGravity) - 1, 1, 32, 32);
+{$IFDEF USE_TOUCH_INTERFACE}
+            offsetX := offsetX - 33
+{$ELSE}
+            offsetX := offsetX + 33
+{$ENDIF}
+        end;
+    if cLaserSighting then
+        begin
+            DrawTextureF(ropeIconTex, 1, (cScreenWidth shr 1) - offsetX, cScreenHeight - offsetY, 0, 1, 32, 32);
+            DrawTextureF(SpritesData[sprAMAmmos].Texture, 0.90, (cScreenWidth shr 1) - offsetX, cScreenHeight - offsetY, ord(amLaserSight) - 1, 1, 32, 32);
         end;
     end;
+
+// Cinematic Mode: Render black bars
+if CinematicSteps > 0 then
+    begin
+    r.x:= ViewLeftX;
+    r.w:= ViewWidth;
+    r.y:= ViewTopY;
+    CinematicBarH:= (ViewHeight * CinematicSteps) div 2048;
+    r.h:= CinematicBarH;
+    DrawRect(r, 0, 0, 0, $FF, true);
+    r.y:= ViewBottomY - r.h;
+    DrawRect(r, 0, 0, 0, $FF, true);
+    end;
+
+// Touchscreen interface widgets
+{$IFDEF USE_TOUCH_INTERFACE}
+DrawScreenWidget(@arrowLeft);
+DrawScreenWidget(@arrowRight);
+DrawScreenWidget(@arrowUp);
+DrawScreenWidget(@arrowDown);
+
+DrawScreenWidget(@fireButton);
+DrawScreenWidget(@jumpWidget);
+DrawScreenWidget(@AMWidget);
+DrawScreenWidget(@utilityWidget);
+DrawScreenWidget(@utilityWidget2);
+DrawScreenWidget(@pauseButton);
 {$ENDIF}
 
+// Captions
+if UIDisplay <> uiNone then
+    DrawCaptions;
+
+// Lag alert
+if isInLag then
+    DrawSprite(sprLag, 32 - (cScreenWidth shr 1), 32, (RealTicks shr 7) mod 12);
+
+// Chat
+DrawChat;
+
+
+// Mission panel
+if not isFirstFrame and (missionTimer <> 0) or isShowMission or isPaused or fastUntilLag or (GameState = gsConfirm) then
+    begin
+    if (ReadyTimeLeft = 0) and (missionTimer > 0) then
+        dec(missionTimer, Lag);
+    if missionTimer < 0 then
+        missionTimer:= 0; // avoid subtracting below 0
+    if missionTex <> nil then
+        DrawTextureCentered(0, Min((cScreenHeight shr 1) + 100, cScreenHeight - 48 - missionTex^.h), missionTex);
+    end;
+if missionTimer = 0 then
+    isForceMission := false;
+
 // AmmoMenu
 if bShowAmmoMenu and ((AMState = AMHidden) or (AMState = AMHiding)) then
     begin
@@ -1582,44 +1699,33 @@
 if bShowAmmoMenu or (AMState = AMHiding) then
     ShowAmmoMenu;
 
+// Centered status/menu messages (synchronizing, auto skip, pause, etc.)
+if fastUntilLag then
+    DrawTextureCentered(0, (cScreenHeight shr 1), SyncTexture)
+else if isAFK then
+    DrawTextureCentered(0, (cScreenHeight shr 1), AFKTexture)
+else if isPaused then
+    DrawTextureCentered(0, (cScreenHeight shr 1), PauseTexture);
+
 // Cursor
 if isCursorVisible and bShowAmmoMenu then
     DrawSprite(sprArrow, CursorPoint.X, cScreenHeight - CursorPoint.Y, (RealTicks shr 6) mod 8);
 
-// Chat
-DrawChat;
-
-
-// various captions
-if fastUntilLag then
-    DrawTextureCentered(0, (cScreenHeight shr 1), SyncTexture);
-if isPaused then
-    DrawTextureCentered(0, (cScreenHeight shr 1), PauseTexture);
-if isAFK then
-    DrawTextureCentered(0, (cScreenHeight shr 1), AFKTexture);
-if not isFirstFrame and (missionTimer <> 0) or isPaused or fastUntilLag or (GameState = gsConfirm) then
-    begin
-    if (ReadyTimeLeft = 0) and (missionTimer > 0) then
-        dec(missionTimer, Lag);
-    if missionTimer < 0 then
-        missionTimer:= 0; // avoid subtracting below 0
-    if missionTex <> nil then
-        DrawTextureCentered(0, Min((cScreenHeight shr 1) + 100, cScreenHeight - 48 - missionTex^.h), missionTex);
-    end;
-
-// fps
+// FPS and demo replay time
 {$IFDEF USE_TOUCH_INTERFACE}
-offsetX:= pauseButton.frame.y + pauseButton.frame.h + 12;
+offsetY:= cDemoClockFPSOffsetY + 10 + pauseButton.frame.y + pauseButton.frame.h;
 {$ELSE}
-offsetX:= 10;
+offsetY:= cDemoClockFPSOffsetY + 10;
 {$ENDIF}
-offsetY:= cOffsetY;
+offsetX:= cOffsetY;
 if (RM = rmDefault) or (RM = rmRightEye) then
     begin
     inc(Frames);
 
     if cShowFPS or (GameType = gmtDemo) then
         inc(CountTicks, Lag);
+
+    // Demo replay time
     if (GameType = gmtDemo) and (CountTicks >= 1000) then
         begin
         i:= GameTicks div 1000;
@@ -1642,29 +1748,36 @@
         SDL_FreeSurface(tmpSurface)
         end;
 
-    if timeTexture <> nil then
-        DrawTexture((cScreenWidth shr 1) - 20 - timeTexture^.w - offsetY, offsetX + timeTexture^.h+5, timeTexture);
+    if (timeTexture <> nil) and (UIDisplay <> uiNone) then
+        DrawTexture((cScreenWidth shr 1) - 20 - timeTexture^.w - offsetX, offsetY, timeTexture);
 
-    if cShowFPS then
+    // FPS counter
+    if cShowFPS and (UIDisplay <> uiNone) then
         begin
         if CountTicks >= 1000 then
             begin
             FPS:= Frames;
             Frames:= 0;
             CountTicks:= 0;
-            s:= Format(trmsg[sidFPS], inttostr(FPS));
-            tmpSurface:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar(s), cWhiteColorChannels);
+            s:= Format(shortstring(trmsg[sidFPS]), inttostr(FPS));
+            tmpSurface:= TTF_RenderUTF8_Blended(Fontz[CheckCJKFont(trmsg[sidFPS],fnt16)].Handle, Str2PChar(s), cWhiteColorChannels);
             tmpSurface:= doSurfaceConversion(tmpSurface);
             FreeAndNilTexture(fpsTexture);
             fpsTexture:= Surface2Tex(tmpSurface, false);
             SDL_FreeSurface(tmpSurface)
             end;
         if fpsTexture <> nil then
-            DrawTexture((cScreenWidth shr 1) - 60 - offsetY, offsetX, fpsTexture);
+            begin
+            if timeTexture <> nil then
+                i:= fpsTexture^.h + 5
+            else
+                i:= 0;
+            DrawTexture((cScreenWidth shr 1) - 20 - fpsTexture^.w - offsetX, offsetY + i, fpsTexture);
+            end;
         end;
 end;
 
-
+// Quit Y/N question
 if GameState = gsConfirm then
     DrawTextureCentered(0, (cScreenHeight shr 1)-40, ConfirmTexture);
 
@@ -1699,7 +1812,7 @@
     end;
 
 {$IFDEF USE_VIDEO_RECORDING}
-// during video prerecording draw red blinking circle and text 'rec'
+// During video prerecording draw red blinking circle and text 'rec'
 if flagPrerecording then
     begin
     if recTexture = nil then
@@ -1713,8 +1826,8 @@
         end;
     DrawTexture( -(cScreenWidth shr 1) + 50, 20, recTexture);
 
-    //a:= Byte(Round(127*(1 + sin(RealTicks*0.007))));
-    a:= Byte(min(255, abs(-255 + ((RealTicks div 2) and 511))));
+    t:= -255 + ((RealTicks div 2) and 511);
+    a:= Byte(min(255, abs(t)));
 
     // draw red circle
     DrawCircleFilled(-(cScreenWidth shr 1) + 30, 35, 10, $FF, $00, $00, a);
@@ -1723,29 +1836,6 @@
 
 SetScale(zoom);
 
-// Attack bar
-    if CurrentTeam <> nil then
-        case AttackBar of
-(*        1: begin
-        r:= StuffPoz[sPowerBar];
-        {$WARNINGS OFF}
-        r.w:= (CurrentHedgehog^.Gear^.Power * 256) div cPowerDivisor;
-        {$WARNINGS ON}
-        DrawSpriteFromRect(r, cScreenWidth - 272, cScreenHeight - 48, 16, 0, Surface);
-        end;*)
-        2: with CurrentHedgehog^ do
-                begin
-                tdx:= hwSign(Gear^.dX) * Sin(Gear^.Angle * Pi / cMaxAngle);
-                tdy:= - Cos(Gear^.Angle * Pi / cMaxAngle);
-                for i:= (Gear^.Power * 24) div cPowerDivisor downto 0 do
-                    DrawSprite(sprPower,
-                            hwRound(Gear^.X) + GetLaunchX(CurAmmoType, hwSign(Gear^.dX), Gear^.Angle) + LongInt(round(WorldDx + tdx * (24 + i * 2))) - 16,
-                            hwRound(Gear^.Y) + GetLaunchY(CurAmmoType, Gear^.Angle) + LongInt(round(WorldDy + tdy * (24 + i * 2))) - 16,
-                            i)
-                end
-        end;
-
-
 // Cursor
 if isCursorVisible and (not bShowAmmoMenu) then
     begin
@@ -1753,11 +1843,9 @@
     with CurrentHedgehog^ do
         if (Gear <> nil) and ((Gear^.State and gstChooseTarget) <> 0) then
             begin
-        if (CurAmmoType = amNapalm) or (CurAmmoType = amMineStrike) or (((GameFlags and gfMoreWind) <> 0) and ((CurAmmoType = amDrillStrike) or (CurAmmoType = amAirAttack))) then
-            DrawLine(-3000, topY-300, 7000, topY-300, 3.0, (Team^.Clan^.Color shr 16), (Team^.Clan^.Color shr 8) and $FF, Team^.Clan^.Color and $FF, $FF);
         i:= GetCurAmmoEntry(CurrentHedgehog^)^.Pos;
         with Ammoz[CurAmmoType] do
-            if PosCount > 1 then
+            if PosCount > 0 then
                 begin
                 if (CurAmmoType = amGirder) or (CurAmmoType = amTeleport) then
                     begin
@@ -1769,9 +1857,15 @@
                     end;
                 DrawSprite(PosSprite, TargetCursorPoint.X - (SpritesData[PosSprite].Width shr 1), cScreenHeight - TargetCursorPoint.Y - (SpritesData[PosSprite].Height shr 1),i);
                 Untint();
+                if (WorldEdge = weWrap) and (CurAmmoType = amBee) then
+                    begin
+                    if (TargetCursorPoint.X - WorldDx > rightX) then
+                        DrawSprite(sprThroughWrap, TargetCursorPoint.X - (SpritesData[sprThroughWrap].Width shr 1), cScreenHeight - TargetCursorPoint.Y - (SpritesData[PosSprite].Height shr 1) - SpritesData[sprThroughWrap].Height - 2, 0)
+                    else if (TargetCursorPoint.X - WorldDx < leftX) then
+                        DrawSprite(sprThroughWrap, TargetCursorPoint.X - (SpritesData[sprThroughWrap].Width shr 1), cScreenHeight - TargetCursorPoint.Y - (SpritesData[PosSprite].Height shr 1) - SpritesData[sprThroughWrap].Height - 2, 1);
+                    end;
                 end;
             end;
-    //DrawSprite(sprArrow, TargetCursorPoint.X, cScreenHeight - TargetCursorPoint.Y, (RealTicks shr 6) mod 8)
     DrawTextureF(SpritesData[sprArrow].Texture, cDefaultZoomLevel / cScaleFactor, TargetCursorPoint.X + round(SpritesData[sprArrow].Width / cScaleFactor), cScreenHeight + round(SpritesData[sprArrow].Height / cScaleFactor) - TargetCursorPoint.Y, (RealTicks shr 6) mod 8, 1, SpritesData[sprArrow].Width, SpritesData[sprArrow].Height);
     end;
 
@@ -1791,7 +1885,7 @@
 var PrevSentPointTime: LongWord = 0;
 
 procedure MoveCamera;
-var EdgesDist, wdy, shs,z, amNumOffsetX, amNumOffsetY, dstX: LongInt;
+var EdgesDist, wdy, shs,z, dstX: LongInt;
     inbtwnTrgtAttks: Boolean;
 begin
 {$IFNDEF MOBILE}
@@ -1813,9 +1907,9 @@
 
             if (WorldEdge = weWrap) then
                 begin
-                    if dstX - prevPoint.X < (LongInt(leftX) - rightX) div 2 then
+                    if dstX - prevPoint.X < (leftX - rightX) div 2 then
                         CursorPoint.X:= (prevPoint.X * 7 + dstX - (leftX - rightX)) div 8
-                    else if dstX - prevPoint.X > (LongInt(rightX) - leftX) div 2 then
+                    else if dstX - prevPoint.X > (rightX - leftX) div 2 then
                         CursorPoint.X:= (prevPoint.X * 7 + dstX - (rightX - leftX)) div 8
                     else
                         CursorPoint.X:= (prevPoint.X * 7 + dstX) div 8;
@@ -1834,9 +1928,9 @@
 if (WorldEdge = weWrap) then
     begin
         if -WorldDx < leftX then
-            WorldDx:= WorldDx - LongInt(rightX) + leftX
+            WorldDx:= WorldDx - rightX + leftX
         else if -WorldDx > rightX then
-            WorldDx:= WorldDx + LongInt(rightX) - leftX;
+            WorldDx:= WorldDx + rightX - leftX;
     end;
 
 wdy:= trunc(cScreenHeight / cScaleFactor) + cScreenHeight div 2 - cWaterLine - (cVisibleWater + trunc(CinematicBarH / (cScaleFactor / 2.0)));
@@ -1848,22 +1942,6 @@
 
 if (AMState = AMShowingUp) or (AMState = AMShowing) then
 begin
-{$IFDEF USE_LANDSCAPE_AMMOMENU}
-    amNumOffsetX:= 0;
-    {$IFDEF USE_AM_NUMCOLUMN}
-    amNumOffsetY:= AMSlotSize;
-    {$ELSE}
-    amNumOffsetY:= 0;
-    {$ENDIF}
-{$ELSE}
-    amNumOffsetY:= 0;
-    {$IFDEF USE_AM_NUMCOLUMN}
-    amNumOffsetX:= AMSlotSize;
-    {$ELSE}
-    amNumOffsetX:= 0;
-    {$ENDIF}
-
-{$ENDIF}
     if CursorPoint.X < AmmoRect.x + amNumOffsetX + 3 then//check left
         CursorPoint.X:= AmmoRect.x + amNumOffsetX + 3;
     if CursorPoint.X > AmmoRect.x + AmmoRect.w - 3 then//check right
@@ -1873,7 +1951,6 @@
     if CursorPoint.Y < cScreenHeight - (AmmoRect.y + AmmoRect.h - AMSlotSize - 5) then//check bottom
         CursorPoint.Y:= cScreenHeight - (AmmoRect.y + AmmoRect.h - AMSlotSize - 5);
     prevPoint:= CursorPoint;
-    //if cHasFocus then SDL_WarpMouse(CursorPoint.X + cScreenWidth div 2, cScreenHeight - CursorPoint.Y);
     exit
 end;
 
@@ -1929,7 +2006,6 @@
 
 // this moves the camera according to CursorPoint X and Y
 prevPoint:= CursorPoint;
-//if cHasFocus then SDL_WarpMouse(CursorPoint.X + (cScreenWidth shr 1), cScreenHeight - CursorPoint.Y);
 if WorldDy > LAND_HEIGHT + 1024 then
     WorldDy:= LAND_HEIGHT + 1024;
 if WorldDy < wdy then
@@ -1941,6 +2017,11 @@
 end;
 
 procedure ShowMission(caption, subcaption, text: ansistring; icon, time : LongInt);
+begin
+    ShowMission(caption, subcaption, text, icon, time, false);
+end;
+
+procedure ShowMission(caption, subcaption, text: ansistring; icon, time : LongInt; forceDisplay : boolean);
 var r: TSDL_Rect;
 begin
 if cOnlyStats then exit;
@@ -1948,6 +2029,10 @@
 r.w:= 32;
 r.h:= 32;
 
+// If true, then mission panel cannot be hidden by releasing the mission panel key.
+// Is in effect until timer runs out, is hidden with HideMission or ShowMission is called with forceDisplay=false.
+isForceMission := forceDisplay;
+
 if time = 0 then
     time:= 5000;
 missionTimer:= time;
@@ -1970,14 +2055,17 @@
 procedure HideMission;
 begin
     missionTimer:= 0;
+    isForceMission:= false;
 end;
 
-procedure SetAmmoTexts(ammoType: TAmmoType; name: ansistring; caption: ansistring; description: ansistring);
+procedure SetAmmoTexts(ammoType: TAmmoType; name: ansistring; caption: ansistring; description: ansistring; autoLabels: boolean);
 var
     ammoStrId: TAmmoStrId;
     ammoStr: ansistring;
     tmpsurf: PSDL_Surface;
 begin
+    if cOnlyStats then exit;
+    
     ammoStrId := Ammoz[ammoType].NameId;
 
     trluaammo[ammoStrId] := name;
@@ -1987,7 +2075,7 @@
         ammoStr:= trammo[ammoStrId];
 
     if checkFails(length(ammoStr) > 0,'No default text/translation found for ammo type #' + intToStr(ord(ammoType)) + '!',true) then exit;
-        
+
     tmpsurf:= TTF_RenderUTF8_Blended(Fontz[CheckCJKFont(ammoStr,fnt16)].Handle, PChar(ammoStr), cWhiteColorChannels);
     if checkFails(tmpsurf <> nil,'Name-texture creation for ammo type #' + intToStr(ord(ammoType)) + ' failed!',true) then exit;
     tmpsurf:= doSurfaceConversion(tmpsurf);
@@ -1997,6 +2085,7 @@
 
     trluaammoc[ammoStrId] := caption;
     trluaammod[ammoStrId] := description;
+    trluaammoe[ammoStrId] := autoLabels;
 end;
 
 procedure ShakeCamera(amount: LongInt);
@@ -2006,19 +2095,19 @@
 amount:= Max(1, round(amount*zoom/2));
 WorldDx:= WorldDx - amount + LongInt(random(1 + amount * 2));
 WorldDy:= WorldDy - amount + LongInt(random(1 + amount * 2));
-//CursorPoint.X:= CursorPoint.X - amount + LongInt(random(1 + amount * 2));
-//CursorPoint.Y:= CursorPoint.Y - amount + LongInt(random(1 + amount * 2))
 end;
 
 
 procedure onFocusStateChanged;
 begin
-if (not cHasFocus) and (GameState <> gsConfirm) then
-    ParseCommand('quit', true);
 {$IFDEF MOBILE}
+if (not cHasFocus) and (not isPaused) then
+    ParseCommand('pause', true);
 // when created SDL receives an exposure event that calls UndampenAudio at full power, muting audio
 exit;
 {$ENDIF}
+if (not cHasFocus) and (GameState <> gsConfirm) then
+    ParseCommand('quit', true);
 
 {$IFDEF USE_VIDEO_RECORDING}
 // do not change volume during prerecording as it will affect sound in video file
@@ -2032,10 +2121,51 @@
 
 procedure updateCursorVisibility;
 begin
-    if isPaused or isAFK then
-        SDL_ShowCursor(1)
+    if isPaused or isAFK or (GameState = gsConfirm) then
+        begin
+{$IFNDEF USE_TOUCH_INTERFACE}
+        SDL_SetRelativeMouseMode(SDL_FALSE);
+{$ENDIF}
+        if SDL_ShowCursor(SDL_QUERY) = SDL_DISABLE then
+            begin
+            uCursor.resetPosition;
+{$IFNDEF USE_TOUCH_INTERFACE}
+            SDL_ShowCursor(SDL_ENABLE);
+{$ENDIF}
+            end;
+        end
     else
-        SDL_ShowCursor(ord(GameState = gsConfirm))
+        begin
+        uCursor.resetPositionDelta;
+{$IFNDEF USE_TOUCH_INTERFACE}
+        SDL_ShowCursor(SDL_DISABLE);
+        SDL_SetRelativeMouseMode(SDL_TRUE);
+{$ENDIF}
+        end;
+end;
+
+procedure updateTouchWidgets(ammoType: TAmmoType);
+begin
+{$IFDEF USE_TOUCH_INTERFACE}
+//show the aiming buttons + animation
+if (Ammoz[ammoType].Ammo.Propz and ammoprop_NeedUpDown) <> 0 then
+    begin
+    if (not arrowUp.show) then
+        begin
+        animateWidget(@arrowUp, true, true);
+        animateWidget(@arrowDown, true, true);
+        end;
+    end
+else
+    if arrowUp.show then
+        begin
+        animateWidget(@arrowUp, true, false);
+        animateWidget(@arrowDown, true, false);
+        end;
+SetUtilityWidgetState(ammoType);
+{$ELSE}
+ammoType:= ammoType; // avoid hint
+{$ENDIF}
 end;
 
 procedure SetUtilityWidgetState(ammoType: TAmmoType);
@@ -2048,20 +2178,32 @@
     if ((Ammoz[ammoType].Ammo.Propz and ammoprop_Timerable) <> 0) and (ammoType <> amDrillStrike) then
         begin
         utilityWidget.sprite:= sprTimerButton;
-        animateWidget(@utilityWidget, true, true);
+        if (not utilityWidget.show) then
+            animateWidget(@utilityWidget, true, true);
         end
     else if (Ammoz[ammoType].Ammo.Propz and ammoprop_NeedTarget) <> 0 then
         begin
         utilityWidget.sprite:= sprTargetButton;
-        animateWidget(@utilityWidget, true, true);
+        if (not utilityWidget.show) then
+            animateWidget(@utilityWidget, true, true);
         end
     else if ammoType = amSwitch then
         begin
         utilityWidget.sprite:= sprSwitchButton;
-        animateWidget(@utilityWidget, true, true);
+        if (not utilityWidget.show) then
+            animateWidget(@utilityWidget, true, true);
         end
     else if utilityWidget.show then
         animateWidget(@utilityWidget, true, false);
+
+    if ((Ammoz[ammoType].Ammo.Propz and ammoprop_SetBounce) <> 0) then
+        begin
+        utilityWidget2.sprite:= sprBounceButton;
+        if (not utilityWidget2.show) then
+            animateWidget(@utilityWidget2, true, true);
+        end
+    else if utilityWidget2.show then
+        animateWidget(@utilityWidget2, true, false);
 {$ELSE}
 ammoType:= ammoType; // avoid hint
 {$ENDIF}