DEADSOFTWARE

gl: implement texture filtering
[d2df-sdl.git] / src / game / renders / opengl / r_map.pas
index fa1d00656dda1775f0d3213e85953b82275ce18c..eba81403044209ce9ace6903bc3d83c22bb5850e 100644 (file)
@@ -42,6 +42,7 @@ interface
   procedure r_Map_Update;
 
   procedure r_Map_Draw (x, y, w, h, camx, camy: Integer; player: TPlayer; out acx, acy: Integer);
+  procedure r_Map_GetSpectatorLimits (out x0, y0, x1, y1: Integer);
 
 implementation
 
@@ -69,6 +70,11 @@ implementation
   ;
 
   const
+    MAXGIBW = 32;
+    MAXGIBH = 32;
+
+    MAXMONW = 256;
+    MAXMONH = 128;
     MTABLE: array [0..MONSTER_MAN] of record
       w, h: Integer;
     end = (
@@ -97,6 +103,8 @@ implementation
     VILEFIRE_DX = 32;
     VILEFIRE_DY = 128;
 
+    MAXITEMW = 64;
+    MAXITEMH = 64;
     ItemAnim: array [0..ITEM_LAST] of record
       name: AnsiString;
       w, h: Integer;
@@ -174,36 +182,37 @@ implementation
 
     ShotAnim: array [0..WEAPON_LAST] of record
       name: AnsiString;
+      x, y: Integer;
       w, h: Integer;
       anim: TAnimInfo;
     end = (
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 0  KASTET
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 1  SAW
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 2  PISTOL
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 3  SHOTGUN1
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 4  SHOTGUN2
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 5  CHAINGUN
-      (name: 'BROCKET';     w: 64; h: 32; anim: (loop: true; delay: 1; frames: 1; back: false)),  // 6  ROCKETLAUNCHER
-      (name: 'BPLASMA';     w: 16; h: 16; anim: (loop: true; delay: 5; frames: 2; back: false)),  // 7  PLASMA
-      (name: 'BBFG';        w: 64; h: 64; anim: (loop: true; delay: 6; frames: 2; back: false)),  // 8  BFG
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 9  SUPERPULEMET
-      (name: 'FLAME';       w: 32; h: 32; anim: (loop: true; delay: 6; frames: 0{11}; back: false)), // 10 FLAMETHROWER
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 11
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 12
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 13
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 14
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 15
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 16
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 17
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 18
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 19
-      (name: '';            w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 20 ZOMPY_PISTOL
-      (name: 'BIMPFIRE';    w: 16; h: 16; anim: (loop: true; delay: 4; frames: 2; back: false)),  // 21 IMP_FIRE
-      (name: 'BBSPFIRE';    w: 16; h: 16; anim: (loop: true; delay: 4; frames: 2; back: false)),  // 22 BSP_FIRE
-      (name: 'BCACOFIRE';   w: 16; h: 16; anim: (loop: true; delay: 4; frames: 2; back: false)),  // 23 CACO_FIRE
-      (name: 'BBARONFIRE';  w: 64; h: 16; anim: (loop: true; delay: 4; frames: 2; back: false)),  // 24 BARON_FIRE
-      (name: 'BMANCUBFIRE'; w: 64; h: 32; anim: (loop: true; delay: 4; frames: 2; back: false)),  // 25 MANCUB_FIRE
-      (name: 'BSKELFIRE';   w: 64; h: 64; anim: (loop: true; delay: 5; frames: 2; back: false))   // 26 SKEL_FIRE
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 0  KASTET
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 1  SAW
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 2  PISTOL
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 3  SHOTGUN1
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 4  SHOTGUN2
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 5  CHAINGUN
+      (name: 'BROCKET';     x: 0; y: 0; w: 64; h: 32; anim: (loop: true; delay: 1; frames: 1; back: false)),  // 6  ROCKETLAUNCHER
+      (name: 'BPLASMA';     x: 0; y: 0; w: 16; h: 16; anim: (loop: true; delay: 5; frames: 2; back: false)),  // 7  PLASMA
+      (name: 'BBFG';        x: 6; y: 7; w: 64; h: 64; anim: (loop: true; delay: 6; frames: 2; back: false)),  // 8  BFG
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 9  SUPERPULEMET
+      (name: 'FLAME';       x: 0; y: 0; w: 32; h: 32; anim: (loop: true; delay: 6; frames: 0{11}; back: false)), // 10 FLAMETHROWER
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 11
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 12
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 13
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 14
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 15
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 16
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 17
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 18
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 19
+      (name: '';            x: 0; y: 0; w: 0;  h: 0;  anim: (loop: true; delay: 0; frames: 0; back: false)),  // 20 ZOMPY_PISTOL
+      (name: 'BIMPFIRE';    x: 0; y: 0; w: 16; h: 16; anim: (loop: true; delay: 4; frames: 2; back: false)),  // 21 IMP_FIRE
+      (name: 'BBSPFIRE';    x: 0; y: 0; w: 16; h: 16; anim: (loop: true; delay: 4; frames: 2; back: false)),  // 22 BSP_FIRE
+      (name: 'BCACOFIRE';   x: 0; y: 0; w: 16; h: 16; anim: (loop: true; delay: 4; frames: 2; back: false)),  // 23 CACO_FIRE
+      (name: 'BBARONFIRE';  x: 0; y: 0; w: 64; h: 16; anim: (loop: true; delay: 4; frames: 2; back: false)),  // 24 BARON_FIRE
+      (name: 'BMANCUBFIRE'; x: 0; y: 0; w: 64; h: 32; anim: (loop: true; delay: 4; frames: 2; back: false)),  // 25 MANCUB_FIRE
+      (name: 'BSKELFIRE';   x: 0; y: 0; w: 64; h: 64; anim: (loop: true; delay: 5; frames: 2; back: false))   // 26 SKEL_FIRE
     );
 
 {$IFDEF ENABLE_SHELLS}
@@ -232,6 +241,7 @@ implementation
     TMonsterAnims = array [0..ANIM_LAST, TDirection] of TGLMultiTexture;
 
   var
+    UseAccel: Boolean;
     DebugFrames: Boolean;
     DebugHealth: Boolean;
     DebugCameraScale: Single;
@@ -287,9 +297,9 @@ implementation
 
   class function TBinHeapPanelDrawCmp.less (const a, b: TPanel): Boolean; inline;
   begin
-    if a.tag < b.tag then begin result := true; exit; end;
-    if a.tag > b.tag then begin result := false; exit; end;
-    result := a.arrIdx < b.arrIdx;
+    if a.tag < b.tag then result := true
+    else if a.tag > b.tag then result := false
+    else result := a.arrIdx < b.arrIdx;
   end;
 
   procedure r_Map_Initialize;
@@ -461,7 +471,7 @@ implementation
       end;
     {$ENDIF}
     // --------- shots --------- //
-    r_Common_SetLoading('Weapon splahses', WEAPON_LAST + 1);
+    r_Common_SetLoading('Weapon splashes', WEAPON_LAST + 1);
     for i := 0 to WEAPON_LAST do
     begin
       if ShotAnim[i].anim.frames > 0 then
@@ -583,20 +593,21 @@ implementation
   end;
 
   procedure r_Map_DrawPanel (p: TPanel);
-    var Texture: Integer; t: TGLMultiTexture; tex: TGLTexture; count, frame: LongInt; a: TAnimInfo;
+    var Texture, x, y, w, h: Integer; t: TGLMultiTexture; count, frame: LongInt; a: TAnimInfo;
   begin
     ASSERT(p <> nil);
+    ASSERT(p.FCurTexture >= -1); (* p.FCurTexture = -1 -> invisible texture *)
     if p.FCurTexture >= 0 then
     begin
+      r_Common_GetPanelPos(p, x, y, w, h);
       ASSERT(p.FCurTexture <= High(p.TextureIDs));
       Texture := p.TextureIDs[p.FCurTexture].Texture;
-      t := nil;
-      ASSERT(Texture >= -1);
-      ASSERT(Texture <= High(RenTextures));
+      ASSERT(Texture >= -1); (* Texture = -1 -> texture not found *)
       if Texture >= 0 then
       begin
+        ASSERT(Texture <= High(RenTextures));
         t := RenTextures[Texture].tex;
-        if (RenTextures[Texture].spec = 0) or (t <> nil) then
+        if t <> nil then
         begin
           count := 0; frame := 0;
           if p.AnimTime <= gTime then
@@ -605,40 +616,66 @@ implementation
             a.loop := p.AnimLoop;
             g_Anim_GetFrameByTime(a, (gTime - p.AnimTime) DIV GAME_TICK, count, frame);
           end;
-          if t <> nil then
-          begin
-            tex := t.GetTexture(frame);
-            r_Draw_TextureRepeat(tex, p.x, p.y, p.width, p.height, false, 255, 255, 255, 255 - p.alpha, p.blending);
-          end
-          else
-            r_Draw_TextureRepeat(nil, p.x, p.y, p.width, p.height, false, 255, 255, 255, 255, false);
+          r_Draw_TextureRepeat(t.GetTexture(frame), x, y, w, h, false, 255, 255, 255, 255 - p.alpha, p.blending);
+        end
+        else if RenTextures[Texture].spec = 0 then
+        begin
+          r_Draw_TextureRepeat(nil, x, y, w, h, false, 255, 255, 255, 255, false);
+        end;
+        case RenTextures[Texture].spec of
+          TEXTURE_SPECIAL_WATER: r_Draw_Filter(x, y, x + w, y + h, 0, 0, 255, 255);
+          TEXTURE_SPECIAL_ACID1: r_Draw_Filter(x, y, x + w, y + h, 0, 230, 0, 255);
+          TEXTURE_SPECIAL_ACID2: r_Draw_Filter(x, y, x + w, y + h, 230, 0, 0, 255);
         end;
+      end
+      else
+      begin
+        r_Draw_TextureRepeat(nil, x, y, w, h, false, 255, 255, 255, 255, false);
       end;
+    end;
+  end;
 
-      if t = nil then
+  procedure r_Map_DrawPanels (constref panels: TPanelArray; drawDoors: Boolean = false);
+    var i: Integer; p: TPanel;
+  begin
+    if panels <> nil then
+    begin
+      for i := 0 to High(panels) do
       begin
-        case RenTextures[Texture].spec of
-          TEXTURE_SPECIAL_WATER: r_Draw_Filter(p.x, p.y, p.x + p.width, p.y + p.height, 0, 0, 255, 255);
-          TEXTURE_SPECIAL_ACID1: r_Draw_Filter(p.x, p.y, p.x + p.width, p.y + p.height, 0, 230, 0, 255);
-          TEXTURE_SPECIAL_ACID2: r_Draw_Filter(p.x, p.y, p.x + p.width, p.y + p.height, 230, 0, 0, 255);
-        end
-      end
-    end
+        p := panels[i];
+        if p.enabled and not (drawDoors xor p.door) then
+          r_Map_DrawPanel(p);
+      end;
+    end;
   end;
 
   procedure r_Map_DrawPanelType (panelTyp: DWORD);
-    var tagMask, i: Integer; p: TPanel;
+    var tagMask: Integer; p: TPanel;
   begin
-    i := 0;
-    tagMask := PanelTypeToTag(panelTyp);
-    while plist.count > 0 do
+    if UseAccel then
+    begin
+      tagMask := PanelTypeToTag(panelTyp);
+      while plist.count > 0 do
+      begin
+        p := TPanel(plist.Front());
+        if (p.tag and tagMask) = 0 then
+          break;
+        r_Map_DrawPanel(p);
+        plist.PopFront;
+      end;
+    end
+    else
     begin
-      p := TPanel(plist.Front());
-      if (p.tag and tagMask) = 0 then
-        break;
-      r_Map_DrawPanel(p);
-      Inc(i);
-      plist.PopFront
+      case panelTyp of
+        PANEL_BACK:      if g_rlayer_back then r_Map_DrawPanels(gRenderBackgrounds);
+        PANEL_STEP:      if g_rlayer_step then r_Map_DrawPanels(gSteps);
+        PANEL_WALL:      if g_rlayer_wall then r_Map_DrawPanels(gWalls);
+        PANEL_CLOSEDOOR: if g_rlayer_door then r_Map_DrawPanels(gWalls, True);
+        PANEL_ACID1:     if g_rlayer_acid1 then r_Map_DrawPanels(gAcid1);
+        PANEL_ACID2:     if g_rlayer_acid2 then r_Map_DrawPanels(gAcid2);
+        PANEL_WATER:     if g_rlayer_water then r_Map_DrawPanels(gWater);
+        PANEL_FORE:      if g_rlayer_fore then r_Map_DrawPanels(gRenderForegrounds);
+      end;
     end;
   end;
 
@@ -647,15 +684,20 @@ implementation
   begin
     if ggItems <> nil then
     begin
+      (* hack: prevent visual disappearance *)
+      x := x - MAXITEMW;
+      y := y - MAXITEMH;
+      w := w + MAXITEMW*2;
+      h := h + MAXITEMH*2;
       for i := 0 to High(ggItems) do
       begin
         it := @ggItems[i];
         if it.used and it.alive and (it.dropped = drop) and (it.ItemType > ITEM_NONE) and (it.ItemType <= ITEM_LAST) then
         begin
-          t := Items[it.ItemType].tex;
-          if g_Collide(it.obj.x, it.obj.y, t.width, t.height, x, y, w, h) then
+          r_Common_GetObjectPos(it.obj, xx, yy);
+          if g_Collide(xx + it.obj.rect.x, yy + it.obj.rect.y, it.obj.rect.width, it.obj.rect.height, x, y, w, h) then
           begin
-            r_Common_GetObjectPos(it.obj, xx, yy);
+            t := Items[it.ItemType].tex;
             tex := t.GetTexture(Items[it.ItemType].frame);
             r_Draw_TextureRepeat(tex, xx, yy, tex.width, tex.height, false, 255, 255, 255, 255, false);
             if DebugFrames then
@@ -675,7 +717,7 @@ implementation
   end;
 
   function r_Map_GetMonsterTexture (m, a: Integer; d: TDirection; out t: TGLMultiTexture; out dx, dy: Integer; out flip: Boolean): Boolean;
-    // var c: Integer;
+    var w: Integer;
   begin
     t := nil; dx := 0; dy := 0; flip := false;
     result := MonTextures[m, a, d] <> nil;
@@ -700,9 +742,8 @@ implementation
       end;
       if flip then
       begin
-//        c := (MONSTERTABLE[MonsterType].Rect.X - dx) + MONSTERTABLE[MonsterType].Rect.Width;
-//        dx := MTABLE[m].width - c - MONSTERTABLE[MonsterType].Rect.X;
-        dx := -dx;
+        if (m = MONSTER_SOUL) and (a = ANIM_DIE) then w := 64 else w := 0;
+        dx := -dx - w;
       end;
     end;
   end;
@@ -725,16 +766,20 @@ implementation
   end;
 
   procedure r_Map_DrawMonster (constref mon: TMonster);
-    var m, a, xx, yy, dx, dy: Integer; d: TDirection; flip: Boolean; t: TGLMultiTexture;
+    var m, a, xx, yy, dx, dy: Integer; d, da: TDirection; flip: Boolean; t: TGLMultiTexture;
   begin
     m := mon.MonsterType;
     a := mon.MonsterAnim;
     d := mon.GameDirection;
 
+    (* hack: barrel tracks player, fix it in game logic *)
+    if m = MONSTER_BARREL then d := TDirection.D_LEFT;
+
     r_Common_GetObjectPos(mon.obj, xx, yy);
     if r_Map_GetMonsterTexture(m, a, d, t, dx, dy, flip) then
     begin
-      r_Draw_MultiTextureRepeat(t, mon.DirAnim[a, d], false, xx + dx, yy + dy, t.width, t.height, flip, 255, 255, 255, 255, false);
+      da := mon.GameDirection;
+      r_Draw_MultiTextureRepeat(t, mon.DirAnim[a, da], false, xx + dx, yy + dy, t.width, t.height, flip, 255, 255, 255, 255, false);
     end;
     if DebugFrames then
     begin
@@ -764,6 +809,11 @@ implementation
   begin
     if gMonsters <> nil then
     begin
+      (* hack: prevent visual disappearance *)
+      x := x - MAXMONW;
+      y := y - MAXMONH;
+      w := w + MAXMONW*2;
+      h := h + MAXMONH*2;
       for i := 0 to High(gMonsters) do
       begin
         m := gMonsters[i];
@@ -934,6 +984,46 @@ implementation
     end;
   end;
 
+  procedure r_Map_DrawAim (p: TPlayer);
+    var x0, y0, x1, y1, a, len: Integer;
+  begin
+    ASSERT(p <> nil);
+    r_Common_GetPlayerPos(p, x0, y0);
+    x0 := x0 + WEAPONPOINT[p.Direction].X + IfThen(p.Direction = TDirection.D_LEFT, 7, -7);
+    y0 := y0 + WEAPONPOINT[p.Direction].Y;
+    a := p.Angle_;
+    len := 1024;
+    case p.CurrWeap of
+      WEAPON_KASTET: len := 12;
+      WEAPON_SAW: len := 24;
+      WEAPON_PISTOL, WEAPON_SHOTGUN1, WEAPON_SHOTGUN2, WEAPON_CHAINGUN,
+      WEAPON_ROCKETLAUNCHER, WEAPON_SUPERPULEMET, WEAPON_FLAMETHROWER:
+      begin
+        if a = ANGLE_RIGHTUP then DEC(a, 2);
+        if a = ANGLE_RIGHTDOWN then INC(a, 4);
+        if a = ANGLE_LEFTUP then INC(a, 2);
+        if a = ANGLE_LEFTDOWN then DEC(a, 4);
+      end;
+      WEAPON_PLASMA:
+      begin
+        if a = ANGLE_RIGHTUP then DEC(a, 1);
+        if a = ANGLE_RIGHTDOWN then INC(a, 3);
+        if a = ANGLE_LEFTUP then INC(a, 1);
+        if a = ANGLE_LEFTDOWN then DEC(a, 3);
+      end;
+      WEAPON_BFG:
+      begin
+        if a = ANGLE_RIGHTUP then DEC(a, 1);
+        if a = ANGLE_RIGHTDOWN then INC(a, 2);
+        if a = ANGLE_LEFTUP then INC(a, 1);
+        if a = ANGLE_LEFTDOWN then DEC(a, 2);
+      end;
+    end;
+    x1 := x0 + Trunc(Cos(-DegToRad(a)) * len);
+    y1 := y0 + Trunc(Sin(-DegToRad(a)) * len);
+    r_Draw_Line(x0, y0, x1, y1, 255, 0, 0, 158);
+  end;
+
   procedure r_Map_DrawPlayer (p, drawed: TPlayer);
     var x, y, ax, ay, w, h: Integer; b, flip: Boolean; t: TGLMultiTexture; tex: TGLTexture; alpha: Byte; count, frame: LongInt;
   begin
@@ -1018,7 +1108,8 @@ implementation
       if (p.FMegaRulez[MR_INVIS] <= gTime) or ((drawed <> nil) and ((p = drawed) or (p.Team = drawed.Team) and (gGameSettings.GameMode <> GM_DM))) then
         r_Map_DrawTalkBubble(p);
 
-    // TODO draw aim
+    if gAimLine and p.alive and ((p = gPlayer1) or (p = gPlayer2)) then
+      r_Map_DrawAim(p);
   end;
 
   procedure r_Map_DrawPlayers (x, y, w, h: Integer; player: TPlayer);
@@ -1041,6 +1132,11 @@ implementation
   begin
     if gGibs <> nil then
     begin
+      (* hack: prevent visual disappearance *)
+      x := x - MAXGIBW;
+      y := y - MAXGIBH;
+      w := w + MAXGIBW*2;
+      h := h + MAXGIBH*2;
       for i := 0 to High(gGibs) do
       begin
         if gGibs[i].alive then
@@ -1247,7 +1343,7 @@ implementation
             pY := Shots[i].Obj.Rect.Height div 2;
             g_Anim_GetFrameByTime(ShotAnim[typ].anim, (gTime - Shots[i].time) DIV GAME_TICK, count, frame);
             tex := t.GetTexture(frame);
-            r_Draw_TextureRepeatRotate(tex, xx, yy, tex.width, tex.height, false, 255, 255, 255, 255, false, pX, pY, a);
+            r_Draw_TextureRepeatRotate(tex, xx - ShotAnim[typ].x, yy - ShotAnim[typ].y, tex.width, tex.height, false, 255, 255, 255, 255, false, pX, pY, a);
             if DebugFrames then
             begin
               r_Draw_Rect(
@@ -1482,16 +1578,34 @@ implementation
     end;
   end;
 
+  function r_Map_GetDrawableGridMask (): Integer;
+    var mask: Integer;
+  begin
+    mask := 0;
+    if g_rlayer_back then mask := mask or GridTagBack;
+    if g_rlayer_step then mask := mask or GridTagStep;
+    if g_rlayer_wall then mask := mask or GridTagWall;
+    if g_rlayer_door then mask := mask or GridTagDoor;
+    if g_rlayer_acid1 then mask := mask or GridTagAcid1;
+    if g_rlayer_acid2 then mask := mask or GridTagAcid2;
+    if g_rlayer_water then mask := mask or GridTagWater;
+    if g_rlayer_fore then mask := mask or GridTagFore;
+    result := mask;
+  end;
+
   procedure r_Map_DrawGame (xx, yy, ww, hh: Integer; player: TPlayer);
     (* xx/yy/ww/hh are in map units *)
     var iter: TPanelGrid.Iter; p: PPanel;
   begin
-    plist.Clear;
-    iter := mapGrid.ForEachInAABB(xx, yy, ww, hh, GridDrawableMask);
-    for p in iter do
-      if ((p^.tag and GridTagDoor) <> 0) = p^.door then
-        plist.Insert(p^);
-    iter.Release;
+    if UseAccel then
+    begin
+      plist.Clear;
+      iter := mapGrid.ForEachInAABB(xx, yy, ww, hh, r_Map_GetDrawableGridMask());
+      for p in iter do
+        if ((p^.tag and GridTagDoor) <> 0) = p^.door then
+          plist.Insert(p^);
+      iter.Release;
+    end;
 
     r_Map_DrawPanelType(PANEL_BACK);
     r_Map_DrawPanelType(PANEL_STEP);
@@ -1520,8 +1634,6 @@ implementation
     r_Map_DrawPanelType(PANEL_ACID2);
     r_Map_DrawPanelType(PANEL_WATER);
     r_Map_DrawPanelType(PANEL_FORE);
-    // TODO draw monsters health bar
-    // TODO draw players health bar
   end;
 
   procedure r_Map_DrawScaled (w, h, camx, camy: Integer; player: TPlayer; out acx, acy, acw, ach, axx, ayy, aww, ahh: Integer);
@@ -1596,9 +1708,10 @@ implementation
       glTranslatef(-w div 2, -h div 2, 0);
     end;
 
-    if SkyTexture <> nil then
+    if gDrawBackGround and (SkyTexture <> nil) then
     begin
       r_Map_CalcSkyParallax(cx, cy, w, h, SkyTexture.width, SkyTexture.height, gMapInfo.Width, gMapInfo.Height, sx, sy, sw, sh);
+      r_Draw_SetFilter(SkyTexture, gTextureFilter);
       r_Draw_Texture(SkyTexture, sx, sy, sw, sh, false, 255, 255, 255, 255, false);
     end;
 
@@ -1664,11 +1777,41 @@ implementation
     g_Anim_GetFrameByTime(FlagAnim, tick, count, FlagFrame);
   end;
 
+  procedure r_Map_GetSpectatorLimits (out x0, y0, x1, y1: Integer);
+    var w, h: Integer;
+  begin
+    w := Round(gScreenWidth / g_dbg_scale);
+    if gMapInfo.Width > w then
+    begin
+      x0 := w div 2;
+      x1 := gMapInfo.Width - w div 2 - 1;
+    end
+    else
+    begin
+      x0 := gMapInfo.Width div 2;
+      x1 := gMapInfo.Width div 2;
+    end;
+
+    h := Round(gScreenHeight / g_dbg_scale);
+    if gMapInfo.Height > h then
+    begin
+      y0 := h div 2;
+      y1 := gMapInfo.Height - h div 2 - 1;
+    end
+    else
+    begin
+      y0 := gMapInfo.Height div 2;
+      y1 := gMapInfo.Height div 2;
+    end;
+  end;
+
 initialization
+  conRegVar('r_sq_draw', @UseAccel, 'accelerated spatial queries in rendering', 'accelerated rendering');
   conRegVar('r_debug_camera_scale', @DebugCameraScale, 0.0001, 1000.0, '', '');
   conRegVar('r_gl_fill_outside', @FillOutsizeArea, '', '');
   conRegVar('d_frames', @DebugFrames, '', '');
   conRegVar('d_health', @DebugHealth, '', '');
+  UseAccel := true;
   DebugCameraScale := 1.0;
   FillOutsizeArea := true;
   DebugFrames := false;