DEADSOFTWARE

grid now works for any map bounding box; collisions with lifts and blockmons are...
[d2df-sdl.git] / src / game / g_map.pas
index 90cb224e401f03ba139123567a9177b77e6c16c8..6b343b325fa8913fa7f3cae31a27e42418be4659 100644 (file)
@@ -1,10 +1,27 @@
+(* Copyright (C)  DooM 2D:Forever Developers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *)
+{$MODE DELPHI}
+{$modeswitch nestedprocvars}
 unit g_map;
 
 interface
 
 uses
   e_graphics, g_basic, MAPSTRUCT, g_textures, Classes,
-  g_phys, WADEDITOR, BinEditor, g_panel, md5;
+  g_phys, wadreader, BinEditor, g_panel, g_grid, md5;
 
 type
   TMapInfo = record
@@ -39,14 +56,15 @@ type
     Direction:   TDirection;
   end;
 
-
 function  g_Map_Load(Res: String): Boolean;
 function  g_Map_GetMapInfo(Res: String): TMapInfo;
 function  g_Map_GetMapsList(WADName: String): SArray;
 function  g_Map_Exist(Res: String): Boolean;
 procedure g_Map_Free();
 procedure g_Map_Update();
-procedure g_Map_DrawPanels(PanelType: Word);
+
+procedure g_Map_DrawPanels(x0, y0, wdt, hgt: Integer; PanelType: Word);
+
 procedure g_Map_DrawBack(dx, dy: Integer);
 function  g_Map_CollidePanel(X, Y: Integer; Width, Height: Word;
                              PanelType: Word; b1x3: Boolean): Boolean;
@@ -66,9 +84,13 @@ function  g_Map_HaveFlagPoints(): Boolean;
 procedure g_Map_ResetFlag(Flag: Byte);
 procedure g_Map_DrawFlags();
 
+function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
+
 procedure g_Map_SaveState(Var Mem: TBinMemoryWriter);
 procedure g_Map_LoadState(Var Mem: TBinMemoryReader);
 
+procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
+
 const
   RESPAWNPOINT_PLAYER1 = 1;
   RESPAWNPOINT_PLAYER2 = 2;
@@ -116,18 +138,47 @@ uses
   g_main, e_log, SysUtils, g_items, g_gfx, g_console,
   GL, GLExt, g_weapons, g_game, g_sound, e_sound, CONFIG,
   g_options, MAPREADER, g_triggers, g_player, MAPDEF,
-  Math, g_monsters, g_saveload, g_language, g_netmsg;
+  Math, g_monsters, g_saveload, g_language, g_netmsg,
+  utils, sfs,
+  ImagingTypes, Imaging, ImagingUtility,
+  ImagingGif, ImagingNetworkGraphics;
 
 const
   FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
   MUSIC_SIGNATURE = $4953554D; // 'MUSI'
   FLAG_SIGNATURE = $47414C46; // 'FLAG'
 
+
+function panelTypeToTag (panelType: Word): Integer;
+begin
+  case panelType of
+    PANEL_WALL, PANEL_OPENDOOR, PANEL_CLOSEDOOR: result := 0; // gWalls
+    PANEL_BACK: result := 1; // gRenderBackgrounds
+    PANEL_FORE: result := 2; // gRenderForegrounds
+    PANEL_WATER: result := 3; // gWater
+    PANEL_ACID1: result := 4; // gAcid1
+    PANEL_ACID2: result := 5; // gAcid2
+    PANEL_STEP: result := 6; // gSteps
+    PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: result := 7; // gLifts -- this is for all lifts
+    PANEL_BLOCKMON: result := 8; // gBlockMon -- this is for all blockmons
+    else result := -1;
+  end;
+end;
+
+
+type
+  TPanelID = record
+    PWhere: ^TPanelArray;
+    PArrID: Integer;
+  end;
+
 var
+  PanelById:     array of TPanelID;
   Textures:      TLevelTextureArray;
   RespawnPoints: Array of TRespawnPoint;
   FlagPoints:    Array [FLAG_RED..FLAG_BLUE] of PFlagPoint;
   //DOMFlagPoints: Array of TFlagPoint;
+  gMapGrid: TBodyGrid = nil;
 
 
 function g_Map_IsSpecialTexture(Texture: String): Boolean;
@@ -337,17 +388,23 @@ begin
   len := Length(panels^);
   SetLength(panels^, len + 1);
 
-  panels^[len] := TPanel.Create(PanelRec, AddTextures,
-                                CurTex, Textures);
+  panels^[len] := TPanel.Create(PanelRec, AddTextures, CurTex, Textures);
+  panels^[len].ArrIdx := len;
   if sav then
     panels^[len].SaveIt := True;
 
   Result := len;
+
+  len := Length(PanelByID);
+  SetLength(PanelByID, len + 1);
+  PanelByID[len].PWhere := panels;
+  PanelByID[len].PArrID := Result;
 end;
 
-procedure CreateNullTexture(RecName: String);
+function CreateNullTexture(RecName: String): Integer;
 begin
   SetLength(Textures, Length(Textures)+1);
+  result := High(Textures);
 
   with Textures[High(Textures)] do
   begin
@@ -359,22 +416,20 @@ begin
   end;
 end;
 
-function CreateTexture(RecName: String; Map: string; log: Boolean): Boolean;
+function CreateTexture(RecName: String; Map: string; log: Boolean): Integer;
 var
-  WAD: TWADEditor_1;
+  WAD: TWADFile;
   TextureData: Pointer;
-  WADName: String;
-  SectionName: String;
-  TextureName: String;
+  WADName, txname: String;
   a, ResLength: Integer;
 begin
-  Result := False;
+  Result := -1;
 
   if Textures <> nil then
     for a := 0 to High(Textures) do
       if Textures[a].TextureName = RecName then
       begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
-        Result := True;
+        Result := a;
         Exit;
       end;
 
@@ -401,14 +456,14 @@ begin
       Anim := False;
     end;
 
-    Result := True;
+    result := High(Textures);
     Exit;
   end;
 
 // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
-  g_ProcessResourceStr(RecName, WADName, SectionName, TextureName);
+  WADName := g_ExtractWadName(RecName);
 
-  WAD := TWADEditor_1.Create();
+  WAD := TWADFile.Create();
 
   if WADName <> '' then
     WADName := GameDir+'/wads/'+WADName
@@ -417,142 +472,257 @@ begin
 
   WAD.ReadFile(WADName);
 
-  if WAD.GetResource(SectionName, TextureName, TextureData, ResLength) then
+  txname := RecName;
+  {
+  if (WADName = Map) and WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
+  begin
+    FreeMem(TextureData);
+    RecName := 'COMMON\ALIEN';
+  end;
+  }
+
+  if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
     begin
       SetLength(Textures, Length(Textures)+1);
-      if not e_CreateTextureMem(TextureData, Textures[High(Textures)].TextureID) then
+      if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
         Exit;
       e_GetTextureSize(Textures[High(Textures)].TextureID,
                        @Textures[High(Textures)].Width,
                        @Textures[High(Textures)].Height);
       FreeMem(TextureData);
-      Textures[High(Textures)].TextureName := RecName;
+      Textures[High(Textures)].TextureName := {RecName}txname;
       Textures[High(Textures)].Anim := False;
 
-      Result := True;
+      result := High(Textures);
     end
   else // Íåò òàêîãî ðåóñðñà â WAD'å
+  begin
+    //e_WriteLog(Format('SHIT! Error loading texture %s : %s : %s', [RecName, txname, g_ExtractFilePathName(RecName)]), MSG_WARNING);
     if log then
       begin
         e_WriteLog(Format('Error loading texture %s', [RecName]), MSG_WARNING);
-        e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
+        //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
       end;
+  end;
 
   WAD.Free();
 end;
 
-function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Boolean;
+function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
 var
-  WAD: TWADEditor_1;
-  TextureWAD: Pointer;
-  TextData: Pointer;
-  TextureData: Pointer;
-  cfg: TConfig;
+  WAD: TWADFile;
+  TextureWAD: PChar = nil;
+  TextData: Pointer = nil;
+  TextureData: Pointer = nil;
+  cfg: TConfig = nil;
   WADName: String;
-  SectionName: String;
-  TextureName: String;
   ResLength: Integer;
   TextureResource: String;
   _width, _height, _framecount, _speed: Integer;
   _backanimation: Boolean;
+  //imgfmt: string;
+  ia: TDynImageDataArray = nil;
+  f, c, frdelay, frloop: Integer;
 begin
-  Result := False;
+  result := -1;
 
-// ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
-  g_ProcessResourceStr(RecName, WADName, SectionName, TextureName);
+  //e_WriteLog(Format('*** Loading animated texture "%s"', [RecName]), MSG_NOTIFY);
 
-  WAD := TWADEditor_1.Create();
+  // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
+  WADName := g_ExtractWadName(RecName);
 
-  if WADName <> '' then
-    WADName := GameDir+'/wads/'+WADName
-  else
-    WADName := Map;
+  WAD := TWADFile.Create();
+  try
+    if WADName <> '' then
+      WADName := GameDir+'/wads/'+WADName
+    else
+      WADName := Map;
 
-  WAD.ReadFile(WADName);
+    WAD.ReadFile(WADName);
 
-  if not WAD.GetResource(SectionName, TextureName, TextureWAD, ResLength) then
-  begin
-    if log then
+    if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength) then
     begin
-      e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
-      e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
+      if log then
+      begin
+        e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
+        //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
+      end;
+      exit;
     end;
-    WAD.Free();
-    Exit;
-  end;
 
-  WAD.FreeWAD();
+    {TEST
+    if WADName = Map then
+    begin
+      //FreeMem(TextureWAD);
+      if not WAD.GetResource('COMMON/animation', TextureWAD, ResLength) then Halt(1);
+    end;
+    }
 
-  if not WAD.ReadMemory(TextureWAD, ResLength) then
-  begin
-    FreeMem(TextureWAD);
-    WAD.Free();
-    Exit;
-  end;
+    WAD.FreeWAD();
 
-// ×èòàåì INI-ðåñóðñ àíèì. òåêñòóðû è çàïîìèíàåì åãî óñòàíîâêè:
-  if not WAD.GetResource('TEXT', 'ANIM', TextData, ResLength) then
-  begin
-    FreeMem(TextureWAD);
-    WAD.Free();
-    Exit;
-  end;
+    if ResLength < 6 then
+    begin
+      e_WriteLog(Format('Animated texture file "%s" too short', [RecName]), MSG_WARNING);
+      exit;
+    end;
 
-  cfg := TConfig.CreateMem(TextData, ResLength);
+    // ýòî ïòèöà? ýòî ñàìîë¸ò?
+    if (TextureWAD[0] = 'D') and (TextureWAD[1] = 'F') and
+       (TextureWAD[2] = 'W') and (TextureWAD[3] = 'A') and (TextureWAD[4] = 'D') then
+    begin
+      // íåò, ýòî ñóïåðìåí!
+      if not WAD.ReadMemory(TextureWAD, ResLength) then
+      begin
+        e_WriteLog(Format('Animated texture WAD file "%s" is invalid', [RecName]), MSG_WARNING);
+        exit;
+      end;
 
-  TextureResource := cfg.ReadStr('', 'resource', '');
+      // ×èòàåì INI-ðåñóðñ àíèì. òåêñòóðû è çàïîìèíàåì åãî óñòàíîâêè:
+      if not WAD.GetResource('TEXT/ANIM', TextData, ResLength) then
+      begin
+        e_WriteLog(Format('Animated texture file "%s" has invalid INI', [RecName]), MSG_WARNING);
+        exit;
+      end;
 
-  if TextureResource = '' then
-  begin
-    FreeMem(TextureWAD);
-    FreeMem(TextData);
-    WAD.Free();
-    cfg.Free();
-    Exit;
-  end;
+      cfg := TConfig.CreateMem(TextData, ResLength);
 
-  _width := cfg.ReadInt('', 'framewidth', 0);
-  _height := cfg.ReadInt('', 'frameheight', 0);
-  _framecount := cfg.ReadInt('', 'framecount', 0);
-  _speed := cfg.ReadInt('', 'waitcount', 0);
-  _backanimation := cfg.ReadBool('', 'backanimation', False);
+      TextureResource := cfg.ReadStr('', 'resource', '');
+      if TextureResource = '' then
+      begin
+        e_WriteLog(Format('Animated texture WAD file "%s" has no "resource"', [RecName]), MSG_WARNING);
+        exit;
+      end;
 
-  cfg.Free();
+      _width := cfg.ReadInt('', 'framewidth', 0);
+      _height := cfg.ReadInt('', 'frameheight', 0);
+      _framecount := cfg.ReadInt('', 'framecount', 0);
+      _speed := cfg.ReadInt('', 'waitcount', 0);
+      _backanimation := cfg.ReadBool('', 'backanimation', False);
 
-// ×èòàåì ðåñóðñ òåêñòóð (êàäðîâ) àíèì. òåêñòóðû â ïàìÿòü:
-  if not WAD.GetResource('TEXTURES', TextureResource, TextureData, ResLength) then
-  begin
-    FreeMem(TextureWAD);
-    FreeMem(TextData);
-    WAD.Free();
-    Exit;
-  end;
+      cfg.Free();
+      cfg := nil;
 
-  WAD.Free();
+      // ×èòàåì ðåñóðñ òåêñòóð (êàäðîâ) àíèì. òåêñòóðû â ïàìÿòü:
+      if not WAD.GetResource('TEXTURES/'+TextureResource, TextureData, ResLength) then
+      begin
+        e_WriteLog(Format('Animated texture WAD file "%s" has no texture "%s"', [RecName, 'TEXTURES/'+TextureResource]), MSG_WARNING);
+        exit;
+      end;
 
-  SetLength(Textures, Length(Textures)+1);
-  with Textures[High(Textures)] do
-  begin
-  // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
-    if g_Frames_CreateMemory(@FramesID, '', TextureData,
-         _width, _height, _framecount, _backanimation) then
+      WAD.Free();
+      WAD := nil;
+
+      SetLength(Textures, Length(Textures)+1);
+      with Textures[High(Textures)] do
+      begin
+        // Ñîçäàåì êàäðû àíèì. òåêñòóðû èç ïàìÿòè:
+        if g_Frames_CreateMemory(@FramesID, '', TextureData, ResLength, _width, _height, _framecount, _backanimation) then
+        begin
+          TextureName := RecName;
+          Width := _width;
+          Height := _height;
+          Anim := True;
+          FramesCount := _framecount;
+          Speed := _speed;
+          result := High(Textures);
+        end
+        else
+        begin
+          if log then e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
+        end;
+      end;
+    end
+    else
+    begin
+      // try animated image
+      {
+      imgfmt := DetermineMemoryFormat(TextureWAD, ResLength);
+      if length(imgfmt) = 0 then
+      begin
+        e_WriteLog(Format('Animated texture file "%s" has unknown format', [RecName]), MSG_WARNING);
+        exit;
+      end;
+      }
+      GlobalMetadata.ClearMetaItems();
+      GlobalMetadata.ClearMetaItemsForSaving();
+      if not LoadMultiImageFromMemory(TextureWAD, ResLength, ia) then
+      begin
+        e_WriteLog(Format('Animated texture file "%s" cannot be loaded', [RecName]), MSG_WARNING);
+        exit;
+      end;
+      if length(ia) = 0 then
       begin
-        TextureName := RecName;
-        Width := _width;
-        Height := _height;
-        Anim := True;
-        FramesCount := _framecount;
-        Speed := _speed;
+        e_WriteLog(Format('Animated texture file "%s" has no frames', [RecName]), MSG_WARNING);
+        exit;
+      end;
 
-        Result := True;
+      WAD.Free();
+      WAD := nil;
+
+      _width := ia[0].width;
+      _height := ia[0].height;
+      _framecount := length(ia);
+      _speed := 1;
+      _backanimation := false;
+      frdelay := -1;
+      frloop := -666;
+      if GlobalMetadata.HasMetaItem(SMetaFrameDelay) then
+      begin
+        //writeln(' frame delay: ', GlobalMetadata.MetaItems[SMetaFrameDelay]);
+        try
+          f := GlobalMetadata.MetaItems[SMetaFrameDelay];
+          frdelay := f;
+          if f < 0 then f := 0;
+          // rounding ;-)
+          c := f mod 28;
+          if c < 13 then c := 0 else c := 1;
+          f := (f div 28)+c;
+          if f < 1 then f := 1 else if f > 255 then f := 255;
+          _speed := f;
+        except
+        end;
+      end;
+      if GlobalMetadata.HasMetaItem(SMetaAnimationLoops) then
+      begin
+        //writeln(' frame loop : ', GlobalMetadata.MetaItems[SMetaAnimationLoops]);
+        try
+          f := GlobalMetadata.MetaItems[SMetaAnimationLoops];
+          frloop := f;
+          if f <> 0 then _backanimation := true; // non-infinite looping == forth-and-back
+        except
+        end;
+      end;
+      //writeln(' creating animated texture with ', length(ia), ' frames (delay:', _speed, '; backloop:', _backanimation, ') from "', RecName, '"...');
+      //for f := 0 to high(ia) do writeln('  frame #', f, ': ', ia[f].width, 'x', ia[f].height);
+      f := ord(_backanimation);
+      e_WriteLog(Format('Animated texture file "%s": %d frames (delay:%d; back:%d; frdelay:%d; frloop:%d), %dx%d', [RecName, length(ia), _speed, f, frdelay, frloop, _width, _height]), MSG_NOTIFY);
+
+      SetLength(Textures, Length(Textures)+1);
+      // cîçäàåì êàäðû àíèì. òåêñòóðû èç êàðòèíîê
+      if g_CreateFramesImg(ia, @Textures[High(Textures)].FramesID, '', _backanimation) then
+      begin
+        Textures[High(Textures)].TextureName := RecName;
+        Textures[High(Textures)].Width := _width;
+        Textures[High(Textures)].Height := _height;
+        Textures[High(Textures)].Anim := True;
+        Textures[High(Textures)].FramesCount := length(ia);
+        Textures[High(Textures)].Speed := _speed;
+        result := High(Textures);
+        //writeln(' CREATED!');
       end
-    else
-      if log then
-        e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
+      else
+      begin
+        if log then e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), MSG_WARNING);
+      end;
+    end;
+  finally
+    for f := 0 to High(ia) do FreeImage(ia[f]);
+    WAD.Free();
+    cfg.Free();
+    if TextureWAD <> nil then FreeMem(TextureWAD);
+    if TextData <> nil then FreeMem(TextData);
+    if TextureData <> nil then FreeMem(TextureData);
   end;
-
-  FreeMem(TextureWAD);
-  FreeMem(TextData);
 end;
 
 procedure CreateItem(Item: TItemRec_1);
@@ -748,15 +918,83 @@ begin
   addResToExternalResList(mapHeader.SkyName);
 end;
 
+procedure mapCreateGrid ();
+var
+  mapX0: Integer = $3fffffff;
+  mapY0: Integer = $3fffffff;
+  mapX1: Integer = -$3fffffff;
+  mapY1: Integer = -$3fffffff;
+
+  procedure fixMinMax (var panels: TPanelArray);
+  var
+    idx: Integer;
+  begin
+    for idx := 0 to High(panels) do
+    begin
+      if (panels[idx].Width < 1) or (panels[idx].Height < 1) then continue;
+      if mapX0 > panels[idx].X then mapX0 := panels[idx].X;
+      if mapY0 > panels[idx].Y then mapY0 := panels[idx].Y;
+      if mapX1 < panels[idx].X+panels[idx].Width-1 then mapX1 := panels[idx].X+panels[idx].Width-1;
+      if mapY1 < panels[idx].Y+panels[idx].Height-1 then mapY1 := panels[idx].Y+panels[idx].Height-1;
+    end;
+  end;
+
+  procedure addPanelsToGrid (var panels: TPanelArray; tag: Integer);
+  var
+    idx: Integer;
+  begin
+    tag := panelTypeToTag(tag);
+    for idx := High(panels) downto 0 do
+    begin
+      gMapGrid.insertBody(panels[idx], panels[idx].X, panels[idx].Y, panels[idx].Width, panels[idx].Height, tag);
+    end;
+  end;
+
+begin
+  gMapGrid.Free();
+  gMapGrid := nil;
+
+  fixMinMax(gWalls);
+  fixMinMax(gRenderBackgrounds);
+  fixMinMax(gRenderForegrounds);
+  fixMinMax(gWater);
+  fixMinMax(gAcid1);
+  fixMinMax(gAcid2);
+  fixMinMax(gSteps);
+  fixMinMax(gLifts);
+  fixMinMax(gBlockMon);
+
+  if (mapX0 < 0) or (mapY0 < 0) then
+  begin
+    e_WriteLog(Format('funny map dimensions: (%d,%d)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1]), MSG_WARNING);
+    //raise Exception.Create('we are fucked');
+  end;
+
+  gMapGrid := TBodyGrid.Create(mapX0, mapY0, mapX1-mapX0+1, mapY1-mapY0+1);
+
+  addPanelsToGrid(gWalls, PANEL_WALL); // and PANEL_CLOSEDOOR
+  addPanelsToGrid(gRenderBackgrounds, PANEL_BACK);
+  addPanelsToGrid(gRenderForegrounds, PANEL_FORE);
+  addPanelsToGrid(gWater, PANEL_WATER);
+  addPanelsToGrid(gAcid1, PANEL_ACID1);
+  addPanelsToGrid(gAcid2, PANEL_ACID2);
+  addPanelsToGrid(gSteps, PANEL_STEP);
+  addPanelsToGrid(gLifts, PANEL_LIFTUP); // it doesn't matter which LIFT type is used here
+  addPanelsToGrid(gBlockMon, PANEL_BLOCKMON);
+
+  gMapGrid.dumpStats();
+end;
+
 function g_Map_Load(Res: String): Boolean;
 const
   DefaultMusRes = 'Standart.wad:STDMUS\MUS1';
   DefaultSkyRes = 'Standart.wad:STDSKY\SKY0';
 var
-  WAD: TWADEditor_1;
+  WAD: TWADFile;
   MapReader: TMapReader_1;
   Header: TMapHeaderRec_1;
   _textures: TTexturesRec1Array;
+  _texnummap: array of Integer; // `_textures` -> `Textures`
   panels: TPanelsRec1Array;
   items: TItemsRec1Array;
   monsters: TMonsterRec1Array;
@@ -772,452 +1010,484 @@ var
                            DoorPanel: Integer;
                            ShotPanel: Integer;
                           end;
-  FileName, SectionName, ResName,
-  FileName2, s, TexName: String;
+  FileName, mapResName, s, TexName: String;
   Data: Pointer;
   Len: Integer;
   ok, isAnim, trigRef: Boolean;
-  CurTex: Integer;
+  CurTex, ntn: Integer;
+
 begin
+  gMapGrid.Free();
+  gMapGrid := nil;
+
   Result := False;
   gMapInfo.Map := Res;
   TriggersTable := nil;
   FillChar(texture, SizeOf(texture), 0);
 
-// Çàãðóçêà WAD:
-  g_ProcessResourceStr(Res, FileName, SectionName, ResName);
-  e_WriteLog('Loading map WAD: ' + FileName, MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
+  sfsGCDisable(); // temporary disable removing of temporary volumes
+  try
+  // Çàãðóçêà WAD:
+    FileName := g_ExtractWadName(Res);
+    e_WriteLog('Loading map WAD: '+FileName, MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
 
-  WAD := TWADEditor_1.Create();
-  if not WAD.ReadFile(FileName) then
-  begin
-    g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
-    WAD.Free();
-    Exit;
-  end;
-  if not WAD.GetResource('', ResName, Data, Len) then
-  begin
-    g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [ResName]));
-    WAD.Free();
-    Exit;
-  end;
-  WAD.Free();
-
-// Çàãðóçêà êàðòû:
-  e_WriteLog('Loading map: ' + ResName, MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
-  MapReader := TMapReader_1.Create();
+    WAD := TWADFile.Create();
+    if not WAD.ReadFile(FileName) then
+    begin
+      g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName]));
+      WAD.Free();
+      Exit;
+    end;
+    //k8: why loader ignores path here?
+    mapResName := g_ExtractFileName(Res);
+    if not WAD.GetMapResource(mapResName, Data, Len) then
+    begin
+      g_FatalError(Format(_lc[I_GAME_ERROR_MAP_RES], [mapResName]));
+      WAD.Free();
+      Exit;
+    end;
 
-  if not MapReader.LoadMap(Data) then
-  begin
-    g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
-    FreeMem(Data);
-    MapReader.Free();
-    Exit;
-  end;
+    WAD.Free();
 
-  FreeMem(Data);
-  generateExternalResourcesList(MapReader);
-// Çàãðóçêà òåêñòóð:
-  g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
-  _textures := MapReader.GetTextures();
+  // Çàãðóçêà êàðòû:
+    e_WriteLog('Loading map: '+mapResName, MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False);
+    MapReader := TMapReader_1.Create();
 
-// Äîáàâëåíèå òåêñòóð â Textures[]:
-  if _textures <> nil then
-  begin
-    e_WriteLog('  Loading textures:', MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], High(_textures), False);
+    if not MapReader.LoadMap(Data) then
+    begin
+      g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]));
+      FreeMem(Data);
+      MapReader.Free();
+      Exit;
+    end;
 
-    for a := 0 to High(_textures) do
+    FreeMem(Data);
+    generateExternalResourcesList(MapReader);
+  // Çàãðóçêà òåêñòóð:
+    g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False);
+    _textures := MapReader.GetTextures();
+    _texnummap := nil;
+
+  // Äîáàâëåíèå òåêñòóð â Textures[]:
+    if _textures <> nil then
     begin
-      SetLength(s, 64);
-      CopyMemory(@s[1], @_textures[a].Resource[0], 64);
-      for b := 1 to Length(s) do
-        if s[b] = #0 then
-        begin
-          SetLength(s, b-1);
-          Break;
-        end;
-      e_WriteLog('    Loading texture: ' + s, MSG_NOTIFY);
-    // Àíèìèðîâàííàÿ òåêñòóðà:
-      if ByteBool(_textures[a].Anim) then
-        begin
-          if not CreateAnimTexture(_textures[a].Resource, FileName, True) then
+      e_WriteLog('  Loading textures:', MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], High(_textures), False);
+      SetLength(_texnummap, length(_textures));
+
+      for a := 0 to High(_textures) do
+      begin
+        SetLength(s, 64);
+        CopyMemory(@s[1], @_textures[a].Resource[0], 64);
+        for b := 1 to Length(s) do
+          if s[b] = #0 then
           begin
-            g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
-            CreateNullTexture(_textures[a].Resource);
+            SetLength(s, b-1);
+            Break;
+          end;
+        e_WriteLog(Format('    Loading texture #%d: %s', [a, s]), MSG_NOTIFY);
+        //if g_Map_IsSpecialTexture(s) then e_WriteLog('      SPECIAL!', MSG_NOTIFY);
+      // Àíèìèðîâàííàÿ òåêñòóðà:
+        if ByteBool(_textures[a].Anim) then
+          begin
+            ntn := CreateAnimTexture(_textures[a].Resource, FileName, True);
+            if ntn < 0 then
+            begin
+              g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s]));
+              ntn := CreateNullTexture(_textures[a].Resource);
+            end;
+          end
+        else // Îáû÷íàÿ òåêñòóðà:
+          ntn := CreateTexture(_textures[a].Resource, FileName, True);
+          if ntn < 0 then
+          begin
+            g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
+            ntn := CreateNullTexture(_textures[a].Resource);
           end;
-        end
-      else // Îáû÷íàÿ òåêñòóðà:
-        if not CreateTexture(_textures[a].Resource, FileName, True) then
-        begin
-          g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s]));
-          CreateNullTexture(_textures[a].Resource);
-        end;
 
-      g_Game_StepLoading();
+        _texnummap[a] := ntn; // fix texture number
+        g_Game_StepLoading();
+      end;
     end;
-  end;
 
-// Çàãðóçêà òðèããåðîâ:
-  gTriggerClientID := 0;
-  e_WriteLog('  Loading triggers...', MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
-  triggers := MapReader.GetTriggers();
+  // Çàãðóçêà òðèããåðîâ:
+    gTriggerClientID := 0;
+    e_WriteLog('  Loading triggers...', MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS], 0, False);
+    triggers := MapReader.GetTriggers();
 
-// Çàãðóçêà ïàíåëåé:
-  e_WriteLog('  Loading panels...', MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
-  panels := MapReader.GetPanels();
+  // Çàãðóçêà ïàíåëåé:
+    e_WriteLog('  Loading panels...', MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_PANELS], 0, False);
+    panels := MapReader.GetPanels();
 
-// Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì):
-  if triggers <> nil then
-  begin
-    e_WriteLog('  Setting up trigger table...', MSG_NOTIFY);
-    SetLength(TriggersTable, Length(triggers));
-    g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], High(TriggersTable), False);
-
-    for a := 0 to High(TriggersTable) do
+    // check texture numbers for panels
+    for a := 0 to High(panels) do
     begin
-    // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè):
-      TriggersTable[a].TexturePanel := triggers[a].TexturePanel;
-    // Ëèôòû:
-      if triggers[a].TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
-        TriggersTable[a].LiftPanel := TTriggerData(triggers[a].DATA).PanelID
-      else
-        TriggersTable[a].LiftPanel := -1;
-    // Äâåðè:
-      if triggers[a].TriggerType in [TRIGGER_OPENDOOR,
-          TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5,
-          TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
-        TriggersTable[a].DoorPanel := TTriggerData(triggers[a].DATA).PanelID
-      else
-        TriggersTable[a].DoorPanel := -1;
-    // Òóðåëü:
-      if triggers[a].TriggerType = TRIGGER_SHOT then
-        TriggersTable[a].ShotPanel := TTriggerData(triggers[a].DATA).ShotPanelID
-      else
-        TriggersTable[a].ShotPanel := -1;
-
-      g_Game_StepLoading();
+      if panels[a].TextureNum > High(_textures) then
+      begin
+        e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR);
+        result := false;
+        exit;
+      end;
+      panels[a].TextureNum := _texnummap[panels[a].TextureNum];
     end;
-  end;
-
-// Ñîçäàåì ïàíåëè:
-  if panels <> nil then
-  begin
-    e_WriteLog('  Setting up trigger links...', MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], High(panels), False);
 
-    for a := 0 to High(panels) do
+  // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì):
+    if triggers <> nil then
     begin
-      SetLength(AddTextures, 0);
-      trigRef := False;
-      CurTex := -1;
-      if _textures <> nil then
-        begin
-          texture := _textures[panels[a].TextureNum];
-          ok := True;
-        end
-      else
-        ok := False;
+      e_WriteLog('  Setting up trigger table...', MSG_NOTIFY);
+      SetLength(TriggersTable, Length(triggers));
+      g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], High(TriggersTable), False);
 
-      if ok then
+      for a := 0 to High(TriggersTable) do
       begin
-      // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
-      // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð:
-        ok := False;
-        if (TriggersTable <> nil) and (_textures <> nil) then
-          for b := 0 to High(TriggersTable) do
-            if (TriggersTable[b].TexturePanel = a)
-            or (TriggersTable[b].ShotPanel = a) then
-            begin
-              trigRef := True;
-              ok := True;
-              Break;
-            end;
+      // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè):
+        TriggersTable[a].TexturePanel := triggers[a].TexturePanel;
+      // Ëèôòû:
+        if triggers[a].TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then
+          TriggersTable[a].LiftPanel := TTriggerData(triggers[a].DATA).PanelID
+        else
+          TriggersTable[a].LiftPanel := -1;
+      // Äâåðè:
+        if triggers[a].TriggerType in [TRIGGER_OPENDOOR,
+            TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5,
+            TRIGGER_CLOSETRAP, TRIGGER_TRAP] then
+          TriggersTable[a].DoorPanel := TTriggerData(triggers[a].DATA).PanelID
+        else
+          TriggersTable[a].DoorPanel := -1;
+      // Òóðåëü:
+        if triggers[a].TriggerType = TRIGGER_SHOT then
+          TriggersTable[a].ShotPanel := TTriggerData(triggers[a].DATA).ShotPanelID
+        else
+          TriggersTable[a].ShotPanel := -1;
+
+        g_Game_StepLoading();
       end;
+    end;
 
-      if ok then
-      begin // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
-        SetLength(s, 64);
-        CopyMemory(@s[1], @texture.Resource[0], 64);
-      // Èçìåðÿåì äëèíó:
-        Len := Length(s);
-        for c := Len downto 1 do
-          if s[c] <> #0 then
-          begin
-            Len := c;
-            Break;
-          end;
-        SetLength(s, Len);
+  // Ñîçäàåì ïàíåëè:
+    if panels <> nil then
+    begin
+      e_WriteLog('  Setting up trigger links...', MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], High(panels), False);
 
-      // Ñïåö-òåêñòóðû çàïðåùåíû:
-        if g_Map_IsSpecialTexture(s) then
-          ok := False
+      for a := 0 to High(panels) do
+      begin
+        SetLength(AddTextures, 0);
+        trigRef := False;
+        CurTex := -1;
+        if _textures <> nil then
+          begin
+            texture := _textures[panels[a].TextureNum];
+            ok := True;
+          end
         else
-      // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè:
-          ok := g_Texture_NumNameFindStart(s);
+          ok := False;
 
-      // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
-      // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #:
         if ok then
         begin
-          k := NNF_NAME_BEFORE;
-        // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû:
-          while ok or (k = NNF_NAME_BEFORE) or
-                (k = NNF_NAME_EQUALS) do
+        // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû.
+        // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð:
+          ok := False;
+          if (TriggersTable <> nil) and (_textures <> nil) then
+            for b := 0 to High(TriggersTable) do
+              if (TriggersTable[b].TexturePanel = a)
+              or (TriggersTable[b].ShotPanel = a) then
+              begin
+                trigRef := True;
+                ok := True;
+                Break;
+              end;
+        end;
+
+        if ok then
+        begin // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü
+          SetLength(s, 64);
+          CopyMemory(@s[1], @texture.Resource[0], 64);
+        // Èçìåðÿåì äëèíó:
+          Len := Length(s);
+          for c := Len downto 1 do
+            if s[c] <> #0 then
+            begin
+              Len := c;
+              Break;
+            end;
+          SetLength(s, Len);
+
+        // Ñïåö-òåêñòóðû çàïðåùåíû:
+          if g_Map_IsSpecialTexture(s) then
+            ok := False
+          else
+        // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè:
+            ok := g_Texture_NumNameFindStart(s);
+
+        // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå.
+        // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #:
+          if ok then
           begin
-            k := g_Texture_NumNameFindNext(TexName);
+            k := NNF_NAME_BEFORE;
+          // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû:
+            while ok or (k = NNF_NAME_BEFORE) or
+                  (k = NNF_NAME_EQUALS) do
+            begin
+              k := g_Texture_NumNameFindNext(TexName);
 
-            if (k = NNF_NAME_BEFORE) or
-               (k = NNF_NAME_AFTER) then
-              begin
-              // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó:
-                if ByteBool(texture.Anim) then
-                  begin // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
-                    isAnim := True;
-                    ok := CreateAnimTexture(TexName, FileName, False);
-                    if not ok then
-                    begin // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
-                      isAnim := False;
-                      ok := CreateTexture(TexName, FileName, False);
-                    end;
-                  end
-                else
-                  begin // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
-                    isAnim := False;
-                    ok := CreateTexture(TexName, FileName, False);
-                    if not ok then
-                    begin // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
+              if (k = NNF_NAME_BEFORE) or
+                 (k = NNF_NAME_AFTER) then
+                begin
+                // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó:
+                  if ByteBool(texture.Anim) then
+                    begin // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
                       isAnim := True;
-                      ok := CreateAnimTexture(TexName, FileName, False);
+                      ok := CreateAnimTexture(TexName, FileName, False) >= 0;
+                      if not ok then
+                      begin // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
+                        isAnim := False;
+                        ok := CreateTexture(TexName, FileName, False) >= 0;
+                      end;
+                    end
+                  else
+                    begin // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
+                      isAnim := False;
+                      ok := CreateTexture(TexName, FileName, False) >= 0;
+                      if not ok then
+                      begin // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
+                        isAnim := True;
+                        ok := CreateAnimTexture(TexName, FileName, False) >= 0;
+                      end;
                     end;
-                  end;
 
-              // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè:
-                if ok then
-                begin
-                  for c := 0 to High(Textures) do
-                    if Textures[c].TextureName = TexName then
-                    begin
-                      SetLength(AddTextures, Length(AddTextures)+1);
-                      AddTextures[High(AddTextures)].Texture := c;
-                      AddTextures[High(AddTextures)].Anim := isAnim;
-                      Break;
-                    end;
-                end;
-              end
-            else
-              if k = NNF_NAME_EQUALS then
-                begin
-                // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî:
-                  SetLength(AddTextures, Length(AddTextures)+1);
-                  AddTextures[High(AddTextures)].Texture := panels[a].TextureNum;
-                  AddTextures[High(AddTextures)].Anim := ByteBool(texture.Anim);
-                  CurTex := High(AddTextures);
-                  ok := True;
+                // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè:
+                  if ok then
+                  begin
+                    for c := 0 to High(Textures) do
+                      if Textures[c].TextureName = TexName then
+                      begin
+                        SetLength(AddTextures, Length(AddTextures)+1);
+                        AddTextures[High(AddTextures)].Texture := c;
+                        AddTextures[High(AddTextures)].Anim := isAnim;
+                        Break;
+                      end;
+                  end;
                 end
-              else // NNF_NO_NAME
-                ok := False;
-          end; // while ok...
-
-          ok := True;
-        end; // if ok - åñòü ñìåæíûå òåêñòóðû
-      end; // if ok - ññûëàþòñÿ òðèããåðû
-
-      if not ok then
-      begin
-      // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó:
-        SetLength(AddTextures, 1);
-        AddTextures[0].Texture := panels[a].TextureNum;
-        AddTextures[0].Anim := ByteBool(texture.Anim);
-        CurTex := 0;
-      end;
+              else
+                if k = NNF_NAME_EQUALS then
+                  begin
+                  // Çàíîñèì òåêóùóþ òåêñòóðó íà ñâîå ìåñòî:
+                    SetLength(AddTextures, Length(AddTextures)+1);
+                    AddTextures[High(AddTextures)].Texture := panels[a].TextureNum;
+                    AddTextures[High(AddTextures)].Anim := ByteBool(texture.Anim);
+                    CurTex := High(AddTextures);
+                    ok := True;
+                  end
+                else // NNF_NO_NAME
+                  ok := False;
+            end; // while ok...
 
-    // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð:
-      PanelID := CreatePanel(panels[a], AddTextures, CurTex, trigRef);
+            ok := True;
+          end; // if ok - åñòü ñìåæíûå òåêñòóðû
+        end; // if ok - ññûëàþòñÿ òðèããåðû
 
-    // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID:
-      if TriggersTable <> nil then
-        for b := 0 to High(TriggersTable) do
+        if not ok then
         begin
-        // Òðèããåð äâåðè/ëèôòà:
-          if (TriggersTable[b].LiftPanel = a) or
-             (TriggersTable[b].DoorPanel = a) then
-            TTriggerData(triggers[b].DATA).PanelID := PanelID;
-        // Òðèããåð ñìåíû òåêñòóðû:
-          if TriggersTable[b].TexturePanel = a then
-            triggers[b].TexturePanel := PanelID;
-        // Òðèããåð "Òóðåëü":
-          if TriggersTable[b].ShotPanel = a then
-            TTriggerData(triggers[b].DATA).ShotPanelID := PanelID;
+        // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó:
+          SetLength(AddTextures, 1);
+          AddTextures[0].Texture := panels[a].TextureNum;
+          AddTextures[0].Anim := ByteBool(texture.Anim);
+          CurTex := 0;
         end;
 
-      g_Game_StepLoading();
+        //e_WriteLog(Format('panel #%d: TextureNum=%d; ht=%d; ht1=%d; atl=%d', [a, panels[a].TextureNum, High(_textures), High(Textures), High(AddTextures)]), MSG_NOTIFY);
+
+      // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð:
+        PanelID := CreatePanel(panels[a], AddTextures, CurTex, trigRef);
+
+      // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID:
+        if TriggersTable <> nil then
+          for b := 0 to High(TriggersTable) do
+          begin
+          // Òðèããåð äâåðè/ëèôòà:
+            if (TriggersTable[b].LiftPanel = a) or
+               (TriggersTable[b].DoorPanel = a) then
+              TTriggerData(triggers[b].DATA).PanelID := PanelID;
+          // Òðèããåð ñìåíû òåêñòóðû:
+            if TriggersTable[b].TexturePanel = a then
+              triggers[b].TexturePanel := PanelID;
+          // Òðèããåð "Òóðåëü":
+            if TriggersTable[b].ShotPanel = a then
+              TTriggerData(triggers[b].DATA).ShotPanelID := PanelID;
+          end;
+
+        g_Game_StepLoading();
+      end;
     end;
-  end;
 
-// Åñëè íå LoadState, òî ñîçäàåì òðèããåðû:
-  if (triggers <> nil) and not gLoadGameMode then
-  begin
-    e_WriteLog('  Creating triggers...', MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
-  // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü:
-    for a := 0 to High(triggers) do
+  // Åñëè íå LoadState, òî ñîçäàåì òðèããåðû:
+    if (triggers <> nil) and not gLoadGameMode then
     begin
-      if triggers[a].TexturePanel <> -1 then
-        b := panels[TriggersTable[a].TexturePanel].PanelType
-      else
-        b := 0;
-      if (triggers[a].TriggerType = TRIGGER_SHOT) and
-         (TTriggerData(triggers[a].DATA).ShotPanelID <> -1) then
-        c := panels[TriggersTable[a].ShotPanel].PanelType
-      else
-        c := 0;
-      CreateTrigger(triggers[a], b, c);
+      e_WriteLog('  Creating triggers...', MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False);
+    // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü:
+      for a := 0 to High(triggers) do
+      begin
+        if triggers[a].TexturePanel <> -1 then
+          b := panels[TriggersTable[a].TexturePanel].PanelType
+        else
+          b := 0;
+        if (triggers[a].TriggerType = TRIGGER_SHOT) and
+           (TTriggerData(triggers[a].DATA).ShotPanelID <> -1) then
+          c := panels[TriggersTable[a].ShotPanel].PanelType
+        else
+          c := 0;
+        CreateTrigger(triggers[a], b, c);
+      end;
     end;
-  end;
 
-// Çàãðóçêà ïðåäìåòîâ:
-  e_WriteLog('  Loading triggers...', MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
-  items := MapReader.GetItems();
+  // Çàãðóçêà ïðåäìåòîâ:
+    e_WriteLog('  Loading triggers...', MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False);
+    items := MapReader.GetItems();
 
-// Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû:
-  if (items <> nil) and not gLoadGameMode then
-  begin
-    e_WriteLog('  Spawning items...', MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
-    for a := 0 to High(items) do
-      CreateItem(Items[a]);
-  end;
+  // Åñëè íå LoadState, òî ñîçäàåì ïðåäìåòû:
+    if (items <> nil) and not gLoadGameMode then
+    begin
+      e_WriteLog('  Spawning items...', MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False);
+      for a := 0 to High(items) do
+        CreateItem(Items[a]);
+    end;
 
-// Çàãðóçêà îáëàñòåé:
-  e_WriteLog('  Loading areas...', MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
-  areas := MapReader.GetAreas();
+  // Çàãðóçêà îáëàñòåé:
+    e_WriteLog('  Loading areas...', MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False);
+    areas := MapReader.GetAreas();
 
-// Åñëè íå LoadState, òî ñîçäàåì îáëàñòè:
-  if areas <> nil then
-  begin
-    e_WriteLog('  Creating areas...', MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
-    for a := 0 to High(areas) do
-      CreateArea(areas[a]);
-  end;
+  // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè:
+    if areas <> nil then
+    begin
+      e_WriteLog('  Creating areas...', MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False);
+      for a := 0 to High(areas) do
+        CreateArea(areas[a]);
+    end;
 
-// Çàãðóçêà ìîíñòðîâ:
-  e_WriteLog('  Loading monsters...', MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
-  monsters := MapReader.GetMonsters();
+  // Çàãðóçêà ìîíñòðîâ:
+    e_WriteLog('  Loading monsters...', MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False);
+    monsters := MapReader.GetMonsters();
 
-  gTotalMonsters := 0;
+    gTotalMonsters := 0;
 
-// Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ:
-  if (monsters <> nil) and not gLoadGameMode then
-  begin
-    e_WriteLog('  Spawning monsters...', MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
-    for a := 0 to High(monsters) do
-      CreateMonster(monsters[a]);
-  end;
+  // Åñëè íå LoadState, òî ñîçäàåì ìîíñòðîâ:
+    if (monsters <> nil) and not gLoadGameMode then
+    begin
+      e_WriteLog('  Spawning monsters...', MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_CREATE_MONSTERS], 0, False);
+      for a := 0 to High(monsters) do
+        CreateMonster(monsters[a]);
+    end;
 
-// Çàãðóçêà îïèñàíèÿ êàðòû:
-  e_WriteLog('  Reading map info...', MSG_NOTIFY);
-  g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
-  Header := MapReader.GetMapHeader();
+  // Çàãðóçêà îïèñàíèÿ êàðòû:
+    e_WriteLog('  Reading map info...', MSG_NOTIFY);
+    g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False);
+    Header := MapReader.GetMapHeader();
 
-  MapReader.Free();
+    MapReader.Free();
 
-  with gMapInfo do
-  begin
-    Name := Header.MapName;
-    Description := Header.MapDescription;
-    Author := Header.MapAuthor;
-    MusicName := Header.MusicName;
-    SkyName := Header.SkyName;
-    Height := Header.Height;
-    Width := Header.Width;
-  end;
+    with gMapInfo do
+    begin
+      Name := Header.MapName;
+      Description := Header.MapDescription;
+      Author := Header.MapAuthor;
+      MusicName := Header.MusicName;
+      SkyName := Header.SkyName;
+      Height := Header.Height;
+      Width := Header.Width;
+    end;
 
-// Çàãðóçêà íåáà:
-  if gMapInfo.SkyName <> '' then
-  begin
-    e_WriteLog('  Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
-    g_ProcessResourceStr(gMapInfo.SkyName, FileName, SectionName, ResName);
+  // Çàãðóçêà íåáà:
+    if gMapInfo.SkyName <> '' then
+    begin
+      e_WriteLog('  Loading sky: ' + gMapInfo.SkyName, MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
+      FileName := g_ExtractWadName(gMapInfo.SkyName);
 
-    if FileName <> '' then
-      FileName := GameDir+'/wads/'+FileName
-    else
-      begin
-        g_ProcessResourceStr(Res, @FileName2, nil, nil);
-        FileName := FileName2;
-      end;
+      if FileName <> '' then
+        FileName := GameDir+'/wads/'+FileName
+      else
+        begin
+          FileName := g_ExtractWadName(Res);
+        end;
 
-    s := FileName+':'+SectionName+'/'+ResName;
-    if g_Texture_CreateWAD(BackID, s) then
-      begin
-        g_Game_SetupScreenSize();
-      end
-    else
-      g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
-  end;
+      s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
+      if g_Texture_CreateWAD(BackID, s) then
+        begin
+          g_Game_SetupScreenSize();
+        end
+      else
+        g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
+    end;
 
-// Çàãðóçêà ìóçûêè:
-  ok := False;
-  if gMapInfo.MusicName <> '' then
-  begin
-    e_WriteLog('  Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
-    g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
-    g_ProcessResourceStr(gMapInfo.MusicName, FileName, SectionName, ResName);
+  // Çàãðóçêà ìóçûêè:
+    ok := False;
+    if gMapInfo.MusicName <> '' then
+    begin
+      e_WriteLog('  Loading music: ' + gMapInfo.MusicName, MSG_NOTIFY);
+      g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
+      FileName := g_ExtractWadName(gMapInfo.MusicName);
 
-    if FileName <> '' then
-      FileName := GameDir+'/wads/'+FileName
-    else
-      begin
-        g_ProcessResourceStr(Res, @FileName2, nil, nil);
-        FileName := FileName2;
-      end;
+      if FileName <> '' then
+        FileName := GameDir+'/wads/'+FileName
+      else
+        begin
+          FileName := g_ExtractWadName(Res);
+        end;
 
-    s := FileName+':'+SectionName+'/'+ResName;
-    if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
-      ok := True
+      s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
+      if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
+        ok := True
+      else
+        g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
+    end;
+
+  // Îñòàëüíûå óñòàíâêè:
+    CreateDoorMap();
+    CreateLiftMap();
+
+    g_Items_Init();
+    g_Weapon_Init();
+    g_Monsters_Init();
+
+  // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
+    if not gLoadGameMode then
+      g_GFX_Init();
+
+  // Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
+    _textures := nil;
+    panels := nil;
+    items := nil;
+    areas := nil;
+    triggers := nil;
+    TriggersTable := nil;
+    AddTextures := nil;
+
+  // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
+    if ok and (not gLoadGameMode) then
+      begin
+        gMusic.SetByName(gMapInfo.MusicName);
+        gMusic.Play();
+      end
     else
-      g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s]));
+      gMusic.SetByName('');
+  finally
+    sfsGCEnable(); // enable releasing unused volumes
   end;
 
-// Îñòàëüíûå óñòàíâêè:
-  CreateDoorMap();
-  CreateLiftMap();
-
-  g_Items_Init();
-  g_Weapon_Init();
-  g_Monsters_Init();
-
-// Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé:
-  if not gLoadGameMode then
-    g_GFX_Init();
-
-// Ñáðîñ ëîêàëüíûõ ìàññèâîâ:
-  _textures := nil;
-  panels := nil;
-  items := nil;
-  areas := nil;
-  triggers := nil;
-  TriggersTable := nil;
-  AddTextures := nil;
-
-// Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà:
-  if ok and (not gLoadGameMode) then
-    begin
-      gMusic.SetByName(gMapInfo.MusicName);
-      gMusic.Play();
-    end
-  else
-    gMusic.SetByName('');
+  e_WriteLog('Creating map grid', MSG_NOTIFY);
+  mapCreateGrid();
 
   e_WriteLog('Done loading map.', MSG_NOTIFY);
   Result := True;
@@ -1225,24 +1495,25 @@ end;
 
 function g_Map_GetMapInfo(Res: String): TMapInfo;
 var
-  WAD: TWADEditor_1;
+  WAD: TWADFile;
   MapReader: TMapReader_1;
   Header: TMapHeaderRec_1;
-  FileName, SectionName, ResName: String;
+  FileName: String;
   Data: Pointer;
   Len: Integer;
 begin
   FillChar(Result, SizeOf(Result), 0);
-  g_ProcessResourceStr(Res, FileName, SectionName, ResName);
+  FileName := g_ExtractWadName(Res);
 
-  WAD := TWADEditor_1.Create();
+  WAD := TWADFile.Create();
   if not WAD.ReadFile(FileName) then
   begin
     WAD.Free();
     Exit;
   end;
 
-  if not WAD.GetResource('', ResName, Data, Len) then
+  //k8: it ignores path again
+  if not WAD.GetMapResource(g_ExtractFileName(Res), Data, Len) then
   begin
     WAD.Free();
     Exit;
@@ -1277,68 +1548,53 @@ end;
 
 function g_Map_GetMapsList(WADName: string): SArray;
 var
-  WAD: TWADEditor_1;
+  WAD: TWADFile;
   a: Integer;
   ResList: SArray;
-  Data: Pointer;
-  Len: Integer;
-  Sign: Array [0..2] of Char;
 begin
   Result := nil;
-
-  WAD := TWADEditor_1.Create();
+  WAD := TWADFile.Create();
   if not WAD.ReadFile(WADName) then
   begin
     WAD.Free();
     Exit;
   end;
-
-  ResList := WAD.GetResourcesList('');
-
+  ResList := WAD.GetMapResources();
   if ResList <> nil then
+  begin
     for a := 0 to High(ResList) do
     begin
-      if not WAD.GetResource('', ResList[a], Data, Len) then Continue;
-      CopyMemory(@Sign[0], Data, 3);
-      FreeMem(Data);
-
-      if Sign = MAP_SIGNATURE then
-      begin
-        SetLength(Result, Length(Result)+1);
-        Result[High(Result)] := ResList[a];
-      end;
-
-      Sign := '';
+      SetLength(Result, Length(Result)+1);
+      Result[High(Result)] := ResList[a];
     end;
-
+  end;
   WAD.Free();
 end;
 
 function g_Map_Exist(Res: string): Boolean;
 var
-  WAD: TWADEditor_1;
-  FileName, SectionName, ResName: string;
+  WAD: TWADFile;
+  FileName, mnn: string;
   ResList: SArray;
   a: Integer;
 begin
   Result := False;
 
-  g_ProcessResourceStr(Res, FileName, SectionName, ResName);
-
-  if Pos('.wad', LowerCase(FileName)) = 0 then FileName := FileName+'.wad';
+  FileName := addWadExtension(g_ExtractWadName(Res));
 
-  WAD := TWADEditor_1.Create;
+  WAD := TWADFile.Create;
   if not WAD.ReadFile(FileName) then
   begin
     WAD.Free();
     Exit;
   end;
 
-  ResList := WAD.GetResourcesList('');
+  ResList := WAD.GetMapResources();
   WAD.Free();
 
+  mnn := g_ExtractFileName(Res);
   if ResList <> nil then
-    for a := 0 to High(ResList) do if ResList[a] = ResName then
+    for a := 0 to High(ResList) do if StrEquCI1251(ResList[a], mnn) then
     begin
       Result := True;
       Exit;
@@ -1427,6 +1683,8 @@ begin
 
   gDoorMap := nil;
   gLiftMap := nil;
+
+  PanelByID := nil;
 end;
 
 procedure g_Map_Update();
@@ -1514,33 +1772,151 @@ begin
   end;
 end;
 
-procedure g_Map_DrawPanels(PanelType: Word);
 
-  procedure DrawPanels(var panels: TPanelArray;
-                       drawDoors: Boolean = False);
+procedure g_Map_DrawPanelsOld(PanelType: Word);
+
+  procedure DrawPanels (stp: Integer; var panels: TPanelArray; drawDoors: Boolean=False);
   var
-    a: Integer;
+    idx: Integer;
+  begin
+    if (panels <> nil) and (stp >= 0) and (stp <= 6) then
+    begin
+      // alas, no visible set
+      for idx := 0 to High(panels) do
+      begin
+        if not (drawDoors xor panels[idx].Door) then panels[idx].Draw();
+      end;
+    end;
+  end;
+
+begin
+  case PanelType of
+    PANEL_WALL:       DrawPanels(0, gWalls);
+    PANEL_CLOSEDOOR:  DrawPanels(0, gWalls, True);
+    PANEL_BACK:       DrawPanels(1, gRenderBackgrounds);
+    PANEL_FORE:       DrawPanels(2, gRenderForegrounds);
+    PANEL_WATER:      DrawPanels(3, gWater);
+    PANEL_ACID1:      DrawPanels(4, gAcid1);
+    PANEL_ACID2:      DrawPanels(5, gAcid2);
+    PANEL_STEP:       DrawPanels(6, gSteps);
+  end;
+end;
+
+
+var
+  gDrawPanelList: TBinaryHeapObj = nil;
+
+function dplLess (a, b: TObject): Boolean;
+begin
+  result := ((a as TPanel).ArrIdx < (b as TPanel).ArrIdx);
+end;
+
+procedure dplClear ();
+begin
+  if gDrawPanelList = nil then gDrawPanelList := TBinaryHeapObj.Create(@dplLess) else gDrawPanelList.clear();
+end;
+
+procedure dplAddPanel (pan: TPanel);
+begin
+  if pan = nil then exit;
+  gDrawPanelList.insert(pan);
+end;
+
 
+procedure g_Map_DrawPanels(x0, y0, wdt, hgt: Integer; PanelType: Word);
+var
+  ptag: Integer;
+
+  function qq (obj: TObject; tag: Integer): Boolean;
+  var
+    pan: TPanel;
   begin
-    if panels <> nil then
-      for a := 0 to High(panels) do
-        if not (drawDoors xor panels[a].Door) then
-          panels[a].Draw();
+    result := false; // don't stop, ever
+    if (tag <> ptag) then exit;
+    //e_WriteLog(Format('  *body: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
+
+    if obj = nil then begin e_WriteLog(Format('  !bodyFUUUUU0: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY); exit; end;
+    if not (obj is TPanel) then begin e_WriteLog(Format('  !bodyFUUUUU1: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY); exit; end;
+    //pan := (obj as TPanel);
+    //e_WriteLog(Format('  !body: (%d,%d)-(%dx%d) tag:%d; qtag:%d', [pan.X, pan.Y, pan.Width, pan.Height, tag, PanelType]), MSG_NOTIFY);
+
+    pan := (obj as TPanel);
+    if (PanelType = PANEL_CLOSEDOOR) then begin if not pan.Door then exit; end else begin if pan.Door then exit; end;
+    //e_WriteLog(Format('  body hit: (%d,%d)-(%dx%d) tag: %d; qtag:%d', [pan.X, pan.Y, pan.Width, pan.Height, tag, PanelType]), MSG_NOTIFY);
+    dplAddPanel(pan);
+  end;
+
+  procedure DrawPanels (stp: Integer; var panels: TPanelArray; drawDoors: Boolean=False);
+  var
+    idx: Integer;
+    pan: TPanel;
+  begin
+    if (panels <> nil) and (stp >= 0) and (stp <= 6) then
+    begin
+      // alas, no visible set
+      for idx := 0 to High(panels) do
+      begin
+        if not (drawDoors xor panels[idx].Door) then
+        begin
+          pan := panels[idx];
+          if (pan.Width < 1) or (pan.Height < 1) then continue;
+          if (pan.X+pan.Width <= x0) or (pan.Y+pan.Height <= y0) then continue;
+          if (pan.X >= x0+wdt) or (pan.Y >= y0+hgt) then continue;
+          e_WriteLog(Format(' *body hit: (%d,%d)-(%dx%d) tag: %d; qtag:%d', [pan.X, pan.Y, pan.Width, pan.Height, PanelType, PanelType]), MSG_NOTIFY);
+        end;
+      end;
+    end;
   end;
 
 begin
+  //g_Map_DrawPanelsOld(PanelType); exit;
+  //e_WriteLog('==================', MSG_NOTIFY);
+  //e_WriteLog(Format('***QQQ: qtag:%d', [PanelType]), MSG_NOTIFY);
+  dplClear();
+  ptag := panelTypeToTag(PanelType);
+  gMapGrid.forEachInAABB(x0, y0, wdt, hgt, qq);
+
+  // debug
+  {
+  e_WriteLog(Format('+++QQQ: qtag:%d', [PanelType]), MSG_NOTIFY);
   case PanelType of
-    PANEL_WALL:       DrawPanels(gWalls);
-    PANEL_CLOSEDOOR:  DrawPanels(gWalls, True);
-    PANEL_BACK:       DrawPanels(gRenderBackgrounds);
-    PANEL_FORE:       DrawPanels(gRenderForegrounds);
-    PANEL_WATER:      DrawPanels(gWater);
-    PANEL_ACID1:      DrawPanels(gAcid1);
-    PANEL_ACID2:      DrawPanels(gAcid2);
-    PANEL_STEP:       DrawPanels(gSteps);
+    PANEL_WALL:       DrawPanels(0, gWalls);
+    PANEL_CLOSEDOOR:  DrawPanels(0, gWalls, True);
+    PANEL_BACK:       DrawPanels(1, gRenderBackgrounds);
+    PANEL_FORE:       DrawPanels(2, gRenderForegrounds);
+    PANEL_WATER:      DrawPanels(3, gWater);
+    PANEL_ACID1:      DrawPanels(4, gAcid1);
+    PANEL_ACID2:      DrawPanels(5, gAcid2);
+    PANEL_STEP:       DrawPanels(6, gSteps);
+  end;
+  e_WriteLog('==================', MSG_NOTIFY);
+  }
+
+  // sort and draw the list (we need to sort it, or rendering is fucked)
+  while gDrawPanelList.count > 0 do
+  begin
+    (gDrawPanelList.front() as TPanel).Draw();
+    gDrawPanelList.popFront();
+  end;
+end;
+
+
+procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
+  function qq (obj: TObject; tag: Integer): Boolean;
+  var
+    pan: TPanel;
+  begin
+    result := false; // don't stop, ever
+    if (tag <> 0 {panelTypeToTag(PANEL_WALL)}) then exit; // only walls
+    pan := (obj as TPanel);
+    pan.DrawShadowVolume(lightX, lightY, radius);
   end;
+
+begin
+  gMapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, qq);
 end;
 
+
 procedure g_Map_DrawBack(dx, dy: Integer);
 begin
   if gDrawBackGround and (BackID <> DWORD(-1)) then
@@ -1549,7 +1925,7 @@ begin
     e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
 end;
 
-function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word;
+function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word;
                             PanelType: Word; b1x3: Boolean): Boolean;
 var
   a, h: Integer;
@@ -1669,6 +2045,104 @@ begin
     end;
 end;
 
+function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean;
+
+  function qq (obj: TObject; tag: Integer): Boolean;
+  var
+    pan: TPanel;
+    a: Integer;
+  begin
+    result := false; // don't stop, ever
+
+    //e_WriteLog(Format('  *body: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
+
+    if obj = nil then
+    begin
+      e_WriteLog(Format('  !bodyFUUUUU0: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
+    end;
+    if not (obj is TPanel) then
+    begin
+      e_WriteLog(Format('  !bodyFUUUUU1: tag:%d; qtag:%d', [tag, PanelType]), MSG_NOTIFY);
+      exit;
+    end;
+
+    pan := (obj as TPanel);
+    a := pan.ArrIdx;
+
+    if WordBool(PanelType and PANEL_WALL) and (tag = panelTypeToTag(PANEL_WALL)) then
+    begin
+      if gWalls[a].Enabled and g_Collide(X, Y, Width, Height, gWalls[a].X, gWalls[a].Y, gWalls[a].Width, gWalls[a].Height) then
+      begin
+        result := true;
+        exit;
+      end;
+    end;
+
+    if WordBool(PanelType and PANEL_WATER) and (tag = panelTypeToTag(PANEL_WATER)) then
+    begin
+      if g_Collide(X, Y, Width, Height, gWater[a].X, gWater[a].Y, gWater[a].Width, gWater[a].Height) then
+      begin
+        result := True;
+        exit;
+      end;
+    end;
+
+    if WordBool(PanelType and PANEL_ACID1) and (tag = panelTypeToTag(PANEL_ACID1)) then
+    begin
+      if g_Collide(X, Y, Width, Height, gAcid1[a].X, gAcid1[a].Y, gAcid1[a].Width, gAcid1[a].Height) then
+      begin
+        result := True;
+        exit;
+      end;
+    end;
+
+    if WordBool(PanelType and PANEL_ACID2) and (tag = panelTypeToTag(PANEL_ACID2)) then
+    begin
+      if g_Collide(X, Y, Width, Height, gAcid2[a].X, gAcid2[a].Y, gAcid2[a].Width, gAcid2[a].Height) then
+      begin
+        result := True;
+        exit;
+      end;
+    end;
+
+    if WordBool(PanelType and PANEL_STEP) and (tag = panelTypeToTag(PANEL_STEP)) then
+    begin
+      if g_Collide(X, Y, Width, Height, gSteps[a].X, gSteps[a].Y, gSteps[a].Width, gSteps[a].Height) then
+      begin
+        result := True;
+        exit;
+      end;
+    end;
+
+    if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) and (tag = panelTypeToTag(PANEL_LIFTUP)) then
+    begin
+      if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = 0)) or
+          (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = 1)) or
+          (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = 2)) or
+          (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = 3))) and
+          g_Collide(X, Y, Width, Height, gLifts[a].X, gLifts[a].Y, gLifts[a].Width, gLifts[a].Height) then
+      begin
+        result := true;
+        exit;
+      end;
+    end;
+
+    if WordBool(PanelType and PANEL_BLOCKMON)and (tag = panelTypeToTag(PANEL_BLOCKMON)) then
+    begin
+      if ((not b1x3) or ((gBlockMon[a].Width + gBlockMon[a].Height) >= 64)) and
+         g_Collide(X, Y, Width, Height, gBlockMon[a].X, gBlockMon[a].Y, gBlockMon[a].Width, gBlockMon[a].Height) then
+      begin
+        result := True;
+        exit;
+      end;
+    end;
+  end;
+
+begin
+  result := gMapGrid.forEachInAABB(X, Y, Width, Height, qq);
+end;
+
+
 function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD;
 var
   a, h: Integer;
@@ -2092,8 +2566,9 @@ begin
   LoadPanelArray(gLifts);
 ///// /////
 
-// Îáíîâëÿåì êàðòó ñòîëêíîâåíèé:
+// Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó:
   g_GFX_Init();
+  mapCreateGrid();
 
 ///// Çàãðóæàåì ìóçûêó: /////
 // Ñèãíàòóðà ìóçûêè:
@@ -2142,4 +2617,15 @@ begin
 ///// /////
 end;
 
+function g_Map_PanelForPID(PanelID: Integer; var PanelArrayID: Integer): PPanel;
+var
+  Arr: TPanelArray;
+begin
+  Result := nil;
+  if (PanelID < 0) or (PanelID > High(PanelByID)) then Exit;
+  Arr := PanelByID[PanelID].PWhere^;
+  PanelArrayID := PanelByID[PanelID].PArrID;
+  Result := Addr(Arr[PanelByID[PanelID].PArrID]);
+end;
+
 end.