DEADSOFTWARE

restarting the same map will not reload textures (yay, quickload!); don't spam log...
authorKetmar Dark <ketmar@ketmar.no-ip.org>
Fri, 1 Sep 2017 00:33:01 +0000 (03:33 +0300)
committerKetmar Dark <ketmar@ketmar.no-ip.org>
Fri, 1 Sep 2017 00:33:55 +0000 (03:33 +0300)
src/engine/e_log.pas
src/game/g_game.pas
src/game/g_map.pas
src/game/g_saveload.pas
src/shared/BinEditor.pas
src/shared/MAPDEF.pas
src/shared/utils.pas
src/shared/wadreader.pas
src/shared/xdynrec.pas

index 3a8d0adc2900ef25e1f3fb3ea896c54320504e3e..937730d5d4fd822260f19fbbe9d42861c8ef4192 100644 (file)
@@ -41,6 +41,7 @@ function DecodeIPV4 (ip: LongWord): string;
 procedure e_InitWritelnDriver ();
 
 procedure e_LogWritefln (const fmt: AnsiString; args: array of const; category: TRecordCategory=MSG_NOTIFY; writeTime: Boolean=true);
+procedure e_LogWriteln (const s: AnsiString; category: TRecordCategory=MSG_NOTIFY; writeTime: Boolean=true);
 
 
 var
@@ -70,6 +71,12 @@ begin
 end;
 
 
+procedure e_LogWriteln (const s: AnsiString; category: TRecordCategory=MSG_NOTIFY; writeTime: Boolean=true);
+begin
+  e_LogWritefln('%s', [s], category, writeTime);
+end;
+
+
 // returns formatted string if `writerCB` is `nil`, empty string otherwise
 //function formatstrf (const fmt: AnsiString; args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
 //TFormatStrFCallback = procedure (constref buf; len: SizeUInt);
index cc71ea1a8c64fde887d14fa2a1e908600ada9bfd..a7f5a48d198af886852aae7361f3972b88fa39b9 100644 (file)
@@ -70,7 +70,7 @@ function  g_Game_IsNet(): Boolean;
 function  g_Game_IsServer(): Boolean;
 function  g_Game_IsClient(): Boolean;
 procedure g_Game_Init();
-procedure g_Game_Free();
+procedure g_Game_Free (freeTextures: Boolean=true);
 procedure g_Game_LoadData();
 procedure g_Game_FreeData();
 procedure g_Game_Update();
@@ -95,7 +95,7 @@ procedure g_Game_RestartLevel();
 procedure g_Game_RestartRound(NoMapRestart: Boolean = False);
 procedure g_Game_ClientWAD(NewWAD: String; WHash: TMD5Digest);
 procedure g_Game_SaveOptions();
-function  g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
+function  g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
 procedure g_Game_ChangeMap(MapPath: String);
 procedure g_Game_ExitLevel(Map: Char16);
 function  g_Game_GetFirstMap(WAD: String): String;
@@ -1299,12 +1299,12 @@ begin
   end;
 end;
 
-procedure g_Game_Free();
+procedure g_Game_Free(freeTextures: Boolean=true);
 begin
   if NetMode = NET_CLIENT then g_Net_Disconnect();
   if NetMode = NET_SERVER then g_Net_Host_Die();
 
-  g_Map_Free();
+  g_Map_Free(freeTextures);
   g_Player_Free();
   g_Player_RemoveAllCorpses();
 
@@ -4050,15 +4050,15 @@ begin
   MessageTime := 0;
   gGameOn := False;
   g_Game_ClearLoading();
-  g_Game_StartMap(Map, True);
+  g_Game_StartMap(Map, True, gCurrentMapFileName);
 end;
 
-function g_Game_StartMap(Map: String; Force: Boolean = False): Boolean;
+function g_Game_StartMap(Map: String; Force: Boolean = False; const oldMapPath: AnsiString=''): Boolean;
 var
   NewWAD, ResName: String;
   I: Integer;
 begin
-  g_Map_Free();
+  g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
   g_Player_RemoveAllCorpses();
 
   if (not g_Game_IsClient) and
index bd2b4508903eba80ee82a6956af853c201594878..22a9aee280e1d2d8295ea8191655dd3645a10936 100644 (file)
@@ -60,7 +60,7 @@ 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_Free(freeTextures: Boolean=true);
 procedure g_Map_Update();
 
 procedure g_Map_DrawPanels (PanelType: Word); // unaccelerated
@@ -202,6 +202,7 @@ var
   gDrawPanelList: TBinaryHeapObj = nil; // binary heap of all walls we have to render, populated by `g_Map_CollectDrawPanels()`
 
   gCurrentMap: TDynRecord = nil;
+  gCurrentMapFileName: AnsiString = ''; // so we can skip texture reloading
 
 
 function panelTypeToTag (panelType: Word): Integer; // returns GridTagXXX
@@ -438,7 +439,9 @@ type
 
 var
   PanelById:     array of TPanelID;
-  Textures:      TLevelTextureArray;
+  Textures:      TLevelTextureArray = nil;
+  TextNameHash: THashStrInt = nil; // key: texture name; value: index in `Textures`
+  BadTextNameHash: THashStrInt = nil; // set; so we won't spam with non-existing texture messages
   RespawnPoints: Array of TRespawnPoint;
   FlagPoints:    Array [FLAG_RED..FLAG_BLUE] of PFlagPoint;
   //DOMFlagPoints: Array of TFlagPoint;
@@ -784,8 +787,13 @@ begin
   PanelByID[len].PArrID := Result;
 end;
 
+
 function CreateNullTexture(RecName: String): Integer;
 begin
+  RecName := toLowerCase1251(RecName);
+  if (TextNameHash = nil) then TextNameHash := hashNewStrInt();
+  if TextNameHash.get(RecName, result) then exit; // i found her!
+
   SetLength(Textures, Length(Textures)+1);
   result := High(Textures);
 
@@ -797,26 +805,47 @@ begin
     Anim := False;
     TextureID := LongWord(TEXTURE_NONE);
   end;
+
+  TextNameHash.put(RecName, result);
 end;
 
-function CreateTexture(RecName: String; Map: string; log: Boolean): Integer;
+
+function CreateTexture(RecName: AnsiString; Map: string; log: Boolean): Integer;
 var
   WAD: TWADFile;
   TextureData: Pointer;
-  WADName, txname: String;
+  WADName: String;
   a, ResLength: Integer;
 begin
+  RecName := toLowerCase1251(RecName);
+  if (TextNameHash = nil) then TextNameHash := hashNewStrInt();
+  if TextNameHash.get(RecName, result) then
+  begin
+    // i found her!
+    //e_LogWritefln('texture ''%s'' already loaded', [RecName]);
+    exit;
+  end;
+
   Result := -1;
 
+  if (BadTextNameHash <> nil) and BadTextNameHash.has(RecName) then exit; // don't do it again and again
+
+  {
   if Textures <> nil then
+  begin
     for a := 0 to High(Textures) do
-      if Textures[a].TextureName = RecName then
+    begin
+      if (Textures[a].TextureName = RecName) then
       begin // Òåêñòóðà ñ òàêèì èìåíåì óæå åñòü
+        e_LogWritefln('texture ''%s'' already loaded', [RecName]);
         Result := a;
         Exit;
       end;
+    end;
+  end;
+  }
 
-// Òåêñòóðû ñî ñïåöèàëüíûìè èìåíàìè (âîäà, ëàâà, êèñëîòà):
+  // Òåêñòóðû ñî ñïåöèàëüíûìè èìåíàìè (âîäà, ëàâà, êèñëîòà):
   if (RecName = TEXTURE_NAME_WATER) or
      (RecName = TEXTURE_NAME_ACID1) or
      (RecName = TEXTURE_NAME_ACID2) then
@@ -826,36 +855,28 @@ begin
     with Textures[High(Textures)] do
     begin
       TextureName := RecName;
-
-      if TextureName = TEXTURE_NAME_WATER then
-        TextureID := LongWord(TEXTURE_SPECIAL_WATER)
-      else
-        if TextureName = TEXTURE_NAME_ACID1 then
-          TextureID := LongWord(TEXTURE_SPECIAL_ACID1)
-        else
-          if TextureName = TEXTURE_NAME_ACID2 then
-            TextureID := LongWord(TEXTURE_SPECIAL_ACID2);
+           if (TextureName = TEXTURE_NAME_WATER) then TextureID := LongWord(TEXTURE_SPECIAL_WATER)
+      else if (TextureName = TEXTURE_NAME_ACID1) then TextureID := LongWord(TEXTURE_SPECIAL_ACID1)
+      else if (TextureName = TEXTURE_NAME_ACID2) then TextureID := LongWord(TEXTURE_SPECIAL_ACID2);
 
       Anim := False;
     end;
 
     result := High(Textures);
+    TextNameHash.put(RecName, result);
     Exit;
   end;
 
-// Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
+  // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
   WADName := g_ExtractWadName(RecName);
 
   WAD := TWADFile.Create();
 
-  if WADName <> '' then
-    WADName := GameDir+'/wads/'+WADName
-  else
-    WADName := Map;
+  if WADName <> '' then WADName := GameDir+'/wads/'+WADName else WADName := Map;
 
   WAD.ReadFile(WADName);
 
-  txname := RecName;
+  //txname := RecName;
   {
   if (WADName = Map) and WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
   begin
@@ -864,33 +885,38 @@ begin
   end;
   }
 
-  if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength) then
+  if WAD.GetResource(g_ExtractFilePathName(RecName), TextureData, ResLength, log) then
+  begin
+    SetLength(Textures, Length(Textures)+1);
+    if not e_CreateTextureMem(TextureData, ResLength, Textures[High(Textures)].TextureID) then
     begin
-      SetLength(Textures, Length(Textures)+1);
-      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}txname;
-      Textures[High(Textures)].Anim := False;
-
-      result := High(Textures);
-    end
+      SetLength(Textures, Length(Textures)-1);
+      Exit;
+    end;
+    e_GetTextureSize(Textures[High(Textures)].TextureID, @Textures[High(Textures)].Width, @Textures[High(Textures)].Height);
+    FreeMem(TextureData);
+    Textures[High(Textures)].TextureName := RecName;
+    Textures[High(Textures)].Anim := False;
+
+    result := High(Textures);
+    TextNameHash.put(RecName, result);
+  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);
-      end;
+    //e_WriteLog(Format('SHIT! Error loading texture %s : %s', [RecName, g_ExtractFilePathName(RecName)]), MSG_WARNING);
+    if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
+    if log and (not BadTextNameHash.get(RecName, a)) then
+    begin
+      e_WriteLog(Format('Error loading texture %s', [RecName]), MSG_WARNING);
+      //e_WriteLog(Format('WAD Reader error: %s', [WAD.GetLastErrorStr]), MSG_WARNING);
+    end;
+    BadTextNameHash.put(RecName, -1);
   end;
 
   WAD.Free();
 end;
 
+
 function CreateAnimTexture(RecName: String; Map: string; log: Boolean): Integer;
 var
   WAD: TWADFile;
@@ -907,29 +933,44 @@ var
   ia: TDynImageDataArray = nil;
   f, c, frdelay, frloop: Integer;
 begin
+  RecName := toLowerCase1251(RecName);
+  if (TextNameHash = nil) then TextNameHash := hashNewStrInt();
+  if TextNameHash.get(RecName, result) then
+  begin
+    // i found her!
+    //e_LogWritefln('animated texture ''%s'' already loaded', [RecName]);
+    exit;
+  end;
+
   result := -1;
 
-  //e_WriteLog(Format('*** Loading animated texture "%s"', [RecName]), MSG_NOTIFY);
+  //e_LogWritefln('*** Loading animated texture "%s"', [RecName]);
+
+  if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
+  if BadTextNameHash.get(RecName, f) then
+  begin
+    //e_WriteLog(Format('no animation texture %s (don''t worry)', [RecName]), MSG_NOTIFY);
+    exit;
+  end;
 
   // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
   WADName := g_ExtractWadName(RecName);
 
   WAD := TWADFile.Create();
   try
-    if WADName <> '' then
-      WADName := GameDir+'/wads/'+WADName
-    else
-      WADName := Map;
+    if WADName <> '' then WADName := GameDir+'/wads/'+WADName else WADName := Map;
 
     WAD.ReadFile(WADName);
 
-    if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength) then
+    if not WAD.GetResource(g_ExtractFilePathName(RecName), TextureWAD, ResLength, log) then
     begin
-      if log then
+      if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
+      if log and (not BadTextNameHash.get(RecName, f)) 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;
+      BadTextNameHash.put(RecName, -1);
       exit;
     end;
 
@@ -946,6 +987,7 @@ begin
     if ResLength < 6 then
     begin
       e_WriteLog(Format('Animated texture file "%s" too short', [RecName]), MSG_WARNING);
+      BadTextNameHash.put(RecName, -1);
       exit;
     end;
 
@@ -957,6 +999,7 @@ begin
       if not WAD.ReadMemory(TextureWAD, ResLength) then
       begin
         e_WriteLog(Format('Animated texture WAD file "%s" is invalid', [RecName]), MSG_WARNING);
+        BadTextNameHash.put(RecName, -1);
         exit;
       end;
 
@@ -964,6 +1007,7 @@ begin
       if not WAD.GetResource('TEXT/ANIM', TextData, ResLength) then
       begin
         e_WriteLog(Format('Animated texture file "%s" has invalid INI', [RecName]), MSG_WARNING);
+        BadTextNameHash.put(RecName, -1);
         exit;
       end;
 
@@ -973,6 +1017,7 @@ begin
       if TextureResource = '' then
       begin
         e_WriteLog(Format('Animated texture WAD file "%s" has no "resource"', [RecName]), MSG_WARNING);
+        BadTextNameHash.put(RecName, -1);
         exit;
       end;
 
@@ -989,6 +1034,7 @@ begin
       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);
+        BadTextNameHash.put(RecName, -1);
         exit;
       end;
 
@@ -1008,10 +1054,16 @@ begin
           FramesCount := _framecount;
           Speed := _speed;
           result := High(Textures);
+          TextNameHash.put(RecName, result);
         end
         else
         begin
-          if log then e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
+          if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
+          if log and (not BadTextNameHash.get(RecName, f)) then
+          begin
+            e_WriteLog(Format('Error loading animation texture %s', [RecName]), MSG_WARNING);
+          end;
+          BadTextNameHash.put(RecName, -1);
         end;
       end;
     end
@@ -1031,11 +1083,13 @@ begin
       if not LoadMultiImageFromMemory(TextureWAD, ResLength, ia) then
       begin
         e_WriteLog(Format('Animated texture file "%s" cannot be loaded', [RecName]), MSG_WARNING);
+        BadTextNameHash.put(RecName, -1);
         exit;
       end;
       if length(ia) = 0 then
       begin
         e_WriteLog(Format('Animated texture file "%s" has no frames', [RecName]), MSG_WARNING);
+        BadTextNameHash.put(RecName, -1);
         exit;
       end;
 
@@ -1091,20 +1145,26 @@ begin
         Textures[High(Textures)].FramesCount := length(ia);
         Textures[High(Textures)].Speed := _speed;
         result := High(Textures);
+        TextNameHash.put(RecName, result);
         //writeln(' CREATED!');
       end
       else
       begin
-        if log then e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), MSG_WARNING);
+        if (BadTextNameHash = nil) then BadTextNameHash := hashNewStrInt();
+        if log  and (not BadTextNameHash.get(RecName, f)) then
+        begin
+          e_WriteLog(Format('Error loading animation texture "%s" images', [RecName]), MSG_WARNING);
+        end;
+        BadTextNameHash.put(RecName, -1);
       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);
+    if (TextureWAD <> nil) then FreeMem(TextureWAD);
+    if (TextData <> nil) then FreeMem(TextData);
+    if (TextureData <> nil) then FreeMem(TextureData);
   end;
 end;
 
@@ -1698,6 +1758,11 @@ begin
           pttit.shotPan := mapReader.panel[rec.trigRec.tgShotPanelID];
         end;
 
+        if (pttit.texPan <> nil) then pttit.texPan.userPanelTrigRef := true;
+        if (pttit.liftPan <> nil) then pttit.liftPan.userPanelTrigRef := true;
+        if (pttit.doorPan <> nil) then pttit.doorPan.userPanelTrigRef := true;
+        if (pttit.shotPan <> nil) then pttit.shotPan.userPanelTrigRef := true;
+
         g_Game_StepLoading();
       end;
     end;
@@ -1712,6 +1777,7 @@ begin
       for rec in panels do
       begin
         Inc(pannum);
+        //e_LogWritefln('PANSTART: pannum=%s', [pannum]);
         texrec := nil;
         SetLength(AddTextures, 0);
         trigRef := False;
@@ -1731,6 +1797,7 @@ begin
           ok := false;
           if (TriggersTable <> nil) and (mapTextureList <> nil) then
           begin
+            {
             for b := 0 to High(TriggersTable) do
             begin
               if (TriggersTable[b].texPan = rec) or (TriggersTable[b].shotPan = rec) then
@@ -1740,6 +1807,13 @@ begin
                 break;
               end;
             end;
+            }
+            if rec.userPanelTrigRef then
+            begin
+              // e_LogWritefln('trigref for panel %s', [pannum]);
+              trigRef := True;
+              ok := True;
+            end;
           end;
         end;
 
@@ -1776,30 +1850,39 @@ begin
                 begin
                   // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ
                   isAnim := True;
+                  //e_LogWritefln('000: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
                   ok := CreateAnimTexture(TexName, FileName, False) >= 0;
+                  //e_LogWritefln('001: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
                   if not ok then
                   begin
                     // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ
                     isAnim := False;
+                    //e_LogWritefln('002: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
                     ok := CreateTexture(TexName, FileName, False) >= 0;
+                    //e_LogWritefln('003: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
                   end;
                 end
                 else
                 begin
                   // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ
                   isAnim := False;
+                  //e_LogWritefln('004: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
                   ok := CreateTexture(TexName, FileName, False) >= 0;
+                  //e_LogWritefln('005: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
                   if not ok then
                   begin
                     // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ
                     isAnim := True;
+                    //e_LogWritefln('006: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
                     ok := CreateAnimTexture(TexName, FileName, False) >= 0;
+                    //e_LogWritefln('007: pannum=%s; TexName=[%s]; FileName=[%s]', [pannum, TexName, FileName]);
                   end;
                 end;
 
                 // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè
                 if ok then
                 begin
+                  {
                   for c := 0 to High(Textures) do
                   begin
                     if (Textures[c].TextureName = TexName) then
@@ -1810,6 +1893,13 @@ begin
                       break;
                     end;
                   end;
+                  }
+                  if (TextNameHash <> nil) and TextNameHash.get(toLowerCase1251(TexName), c) then
+                  begin
+                    SetLength(AddTextures, Length(AddTextures)+1);
+                    AddTextures[High(AddTextures)].Texture := c;
+                    AddTextures[High(AddTextures)].Anim := isAnim;
+                  end;
                 end;
               end
               else
@@ -1846,11 +1936,15 @@ begin
 
         //e_WriteLog(Format('panel #%d: TextureNum=%d; ht=%d; ht1=%d; atl=%d', [a, panels[a].TextureNum, High(mapTextureList), High(Textures), High(AddTextures)]), MSG_NOTIFY);
 
+        //e_LogWritefln('PANADD: pannum=%s', [pannum]);
+
         // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð
         PanelID := CreatePanel(rec, AddTextures, CurTex, trigRef);
         //e_LogWritefln('panel #%s of type %s got id #%s', [pannum, rec.PanelType, PanelID]);
         // set 'gamePanelId' field to panel id
-        rec.gamePanelId := PanelID; // remember game panel id, we'll fix triggers later
+        rec.userPanelId := PanelID; // remember game panel id, we'll fix triggers later
+
+        //e_LogWritefln('PANEND: pannum=%s', [pannum]);
 
         g_Game_StepLoading();
       end;
@@ -1859,10 +1953,10 @@ begin
     // ×èíèì ID'û ïàíåëåé, êîòîðûå èñïîëüçóþòñÿ â òðèããåðàõ
     for b := 0 to High(TriggersTable) do
     begin
-      if (TriggersTable[b].texPan <> nil) then TriggersTable[b].texPanIdx := TriggersTable[b].texPan.gamePanelId;
-      if (TriggersTable[b].liftPan <> nil) then TriggersTable[b].LiftPanelIdx := TriggersTable[b].liftPan.gamePanelId;
-      if (TriggersTable[b].doorPan <> nil) then TriggersTable[b].DoorPanelIdx := TriggersTable[b].doorPan.gamePanelId;
-      if (TriggersTable[b].shotPan <> nil) then TriggersTable[b].ShotPanelIdx := TriggersTable[b].shotPan.gamePanelId;
+      if (TriggersTable[b].texPan <> nil) then TriggersTable[b].texPanIdx := TriggersTable[b].texPan.userPanelId;
+      if (TriggersTable[b].liftPan <> nil) then TriggersTable[b].LiftPanelIdx := TriggersTable[b].liftPan.userPanelId;
+      if (TriggersTable[b].doorPan <> nil) then TriggersTable[b].DoorPanelIdx := TriggersTable[b].doorPan.userPanelId;
+      if (TriggersTable[b].shotPan <> nil) then TriggersTable[b].ShotPanelIdx := TriggersTable[b].shotPan.userPanelId;
     end;
 
     // create map grid, init other grids (for monsters, for example)
@@ -1930,6 +2024,7 @@ begin
     end;
 
     gCurrentMap := mapReader; // this will be our current map now
+    gCurrentMapFileName := Res;
     mapReader := nil;
 
     // Çàãðóçêà íåáà
@@ -2048,22 +2143,6 @@ begin
 
   WAD.Free();
 
-  {
-  MapReader := TMapReader_1.Create();
-  if not MapReader.LoadMap(Data) then
-    begin
-      g_Console_Add(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res]), True);
-      ZeroMemory(@Header, SizeOf(Header));
-      Result.Name := _lc[I_GAME_ERROR_MAP_SELECT];
-      Result.Description := _lc[I_GAME_ERROR_MAP_SELECT];
-    end
-  else
-    begin
-      Header := MapReader.GetMapHeader();
-      Result.Name := Header.MapName;
-      Result.Description := Header.MapDescription;
-    end;
-  }
   try
     mapReader := g_Map_ParseMap(Data, Len);
   except
@@ -2071,10 +2150,6 @@ begin
   end;
 
   FreeMem(Data);
-  //MapReader.Free();
-
-  //if (mapReader <> nil) then Header := GetMapHeader(mapReader) else FillChar(Header, sizeof(Header), 0);
-  //MapReader.Free();
 
   if (mapReader.Width > 0) and (mapReader.Height > 0) then
   begin
@@ -2155,7 +2230,7 @@ begin
     end;
 end;
 
-procedure g_Map_Free();
+procedure g_Map_Free(freeTextures: Boolean=true);
 var
   a: Integer;
 
@@ -2194,17 +2269,42 @@ begin
 
   //gDOMFlags := nil;
 
-  if Textures <> nil then
+  if (Length(gCurrentMapFileName) <> 0) then
   begin
-    for a := 0 to High(Textures) do
-      if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
-        if Textures[a].Anim then
-          g_Frames_DeleteByID(Textures[a].FramesID)
-        else
-          if Textures[a].TextureID <> LongWord(TEXTURE_NONE) then
-            e_DeleteTexture(Textures[a].TextureID);
-
-    Textures := nil;
+    e_LogWritefln('g_Map_Free: previous map was ''%s''...', [gCurrentMapFileName]);
+  end
+  else
+  begin
+    e_LogWritefln('g_Map_Free: no previous map.', []);
+  end;
+  if freeTextures then
+  begin
+    e_LogWritefln('g_Map_Free: clearing textures...', []);
+    if (Textures <> nil) then
+    begin
+      for a := 0 to High(Textures) do
+      begin
+        if not g_Map_IsSpecialTexture(Textures[a].TextureName) then
+        begin
+          if Textures[a].Anim then
+          begin
+            g_Frames_DeleteByID(Textures[a].FramesID)
+          end
+          else
+          begin
+            if (Textures[a].TextureID <> LongWord(TEXTURE_NONE)) then
+            begin
+              e_DeleteTexture(Textures[a].TextureID);
+            end;
+          end;
+        end;
+      end;
+      Textures := nil;
+    end;
+    TextNameHash.Free();
+    TextNameHash := nil;
+    BadTextNameHash.Free();
+    BadTextNameHash := nil;
   end;
 
   FreePanelArray(gWalls);
index 5a130981eae9232f2e22269e20275397098f1829..a54e26be05338c02430c418a9954762f1091a213 100644 (file)
@@ -21,21 +21,13 @@ interface
 uses
   e_graphics, g_phys, g_textures, BinEditor;
 
+
 function g_GetSaveName(n: Integer): String;
 function g_SaveGame(n: Integer; Name: String): Boolean;
 function g_LoadGame(n: Integer): Boolean;
 procedure Obj_SaveState(o: PObj; var Mem: TBinMemoryWriter);
 procedure Obj_LoadState(o: PObj; var Mem: TBinMemoryReader);
 
-type
-  TLoadSaveHook = procedure ();
-
-procedure g_SetPreLoadHook (ahook: TLoadSaveHook);
-procedure g_SetPostLoadHook (ahook: TLoadSaveHook);
-
-procedure g_CallPreLoadHooks ();
-procedure g_CallPostLoadHooks ();
-
 
 implementation
 
@@ -47,49 +39,12 @@ uses
 
 const
   SAVE_SIGNATURE = $56534644; // 'DFSV'
-  SAVE_VERSION = $03;
+  SAVE_VERSION = $04;
   END_MARKER_STRING = 'END';
   PLAYER_VIEW_SIGNATURE = $57564C50; // 'PLVW'
   OBJ_SIGNATURE = $4A424F5F; // '_OBJ'
 
 
-var
-  preloadHooks: array of TLoadSaveHook = nil;
-  postloadHooks: array of TLoadSaveHook = nil;
-
-
-procedure g_SetPreLoadHook (ahook: TLoadSaveHook);
-begin
-  if not assigned(ahook) then exit;
-  SetLength(preloadHooks, Length(preloadHooks)+1);
-  preloadHooks[High(preloadHooks)] := ahook;
-end;
-
-
-procedure g_SetPostLoadHook (ahook: TLoadSaveHook);
-begin
-  if not assigned(ahook) then exit;
-  SetLength(postloadHooks, Length(postloadHooks)+1);
-  postloadHooks[High(postloadHooks)] := ahook;
-end;
-
-
-procedure g_CallPreLoadHooks ();
-var
-  f: Integer;
-begin
-  for f := 0 to High(preloadHooks) do preloadHooks[f]();
-end;
-
-
-procedure g_CallPostLoadHooks ();
-var
-  f: Integer;
-begin
-  for f := 0 to High(postloadHooks) do postloadHooks[f]();
-end;
-
-
 procedure Obj_SaveState(o: PObj; var Mem: TBinMemoryWriter);
 var
   sig: DWORD;
@@ -211,6 +166,9 @@ begin
     bMem := TBinMemoryWriter.Create(256);
   // Èìÿ èãðû:
     bMem.WriteString(Name, 32);
+  // Ïîëíûé ïóòü ê âàäó è êàðòà
+    if (Length(gCurrentMapFileName) <> 0) then e_LogWritefln('SAVE: current map is ''%s''...', [gCurrentMapFileName]);
+    bMem.WriteString(gCurrentMapFileName);
   // Ïóòü ê êàðòå:
     str := gGameSettings.WAD;
     bMem.WriteString(str, 128);
@@ -385,6 +343,8 @@ var
   Game_CoopTotalSecrets,
   PID1, PID2: Word;
   i: Integer;
+  gameCleared: Boolean = false;
+  curmapfile: AnsiString = '';
 begin
   Result := False;
   bMem := nil;
@@ -401,19 +361,27 @@ begin
     end;
 
     e_WriteLog('Loading saved game...', MSG_NOTIFY);
-    g_Game_Free();
+    //g_Game_Free(false); // don't free textures for the same map
 
     g_Game_ClearLoading();
     g_Game_SetLoadingText(_lc[I_LOAD_SAVE_FILE], 0, False);
     gLoadGameMode := True;
 
-    g_CallPreLoadHooks();
-
   ///// Çàãðóæàåì ñîñòîÿíèå èãðû: /////
     bMem := TBinMemoryReader.Create();
     bFile.ReadMemory(bMem);
   // Èìÿ èãðû:
     bMem.ReadString(str);
+
+  // Ïîëíûé ïóòü ê âàäó è êàðòà
+    bMem.ReadString(curmapfile);
+
+    if (Length(gCurrentMapFileName) <> 0) then e_LogWritefln('LOAD: previous map was ''%s''...', [gCurrentMapFileName]);
+    if (Length(curmapfile) <> 0) then e_LogWritefln('LOAD: new map is ''%s''...', [curmapfile]);
+  // À âîò òóò, íàêîíåö, ÷èñòèì ðåñóðñû
+    g_Game_Free(curmapfile <> gCurrentMapFileName); // don't free textures for the same map
+    gameCleared := true;
+
   // Ïóòü ê êàðòå:
     bMem.ReadString(WAD_Path);
   // Èìÿ êàðòû:
@@ -491,7 +459,7 @@ begin
     g_Game_SetupScreenSize();
 
   // Çàãðóçêà è çàïóñê êàðòû:
-    if not g_Game_StartMap(WAD_Path + ':\' + Map_Name, True) then
+    if not g_Game_StartMap(WAD_Path + ':\' + Map_Name, True, curmapfile) then
     begin
       g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [WAD_Path + ':\' + Map_Name]));
       Exit;
@@ -621,7 +589,6 @@ begin
   // Çàêðûâàåì ôàéë çàãðóçêè:
     bFile.Close();
     gLoadGameMode := False;
-    g_CallPostLoadHooks();
     Result := True;
 
   except
@@ -629,11 +596,13 @@ begin
       begin
         g_Console_Add(_lc[I_GAME_ERROR_LOAD]);
         e_WriteLog('LoadState I/O Error: '+E1.Message, MSG_WARNING);
+        if not gameCleared then g_Game_Free();
       end;
     on E2: EBinSizeError do
       begin
         g_Console_Add(_lc[I_GAME_ERROR_LOAD]);
         e_WriteLog('LoadState Size Error: '+E2.Message, MSG_WARNING);
+        if not gameCleared then g_Game_Free();
       end;
   end;
 
index 1f53456f23d6a009e21d136c418c4aff19ab7555..25526e14c0374b5ac3de55ff639872625d34d5a4 100644 (file)
@@ -44,7 +44,7 @@ Type
     Procedure   WriteInt(Var x: Integer);
     Procedure   WriteSingle(Var x: Single);
     Procedure   WriteBoolean(Var x: Boolean);
-    Procedure   WriteString(Var x: String; aMaxLen: Byte = 255);
+    Procedure   WriteString(const x: AnsiString; aMaxLen: Word=65535);
     Procedure   WriteMemory(Var x: Pointer; memSize: Cardinal);
     Procedure   Fill(aLen: Cardinal; aFillSym: Byte);
     Procedure   SaveToFile(Var aFile: File);
@@ -70,7 +70,7 @@ Type
     Procedure   ReadInt(Var x: Integer);
     Procedure   ReadSingle(Var x: Single);
     Procedure   ReadBoolean(Var x: Boolean);
-    Procedure   ReadString(Var x: String);
+    Procedure   ReadString(Var x: AnsiString);
     Procedure   ReadMemory(Var x: Pointer; Var memSize: Cardinal);
     Procedure   Skip(aLen: Cardinal);
     Procedure   LoadFromFile(Var aFile: File);
@@ -245,26 +245,22 @@ begin
   WriteVar(y, SizeOf(Byte));
 end;
 
-Procedure TBinMemoryWriter.WriteString(Var x: String; aMaxLen: Byte = 255);
+Procedure TBinMemoryWriter.WriteString (const x: AnsiString; aMaxLen: Word=65535);
 var
-  len: Byte;
-
+  len: Word;
 begin
-  len := Min(Length(x), aMaxLen);
+  if (Length(x) > aMaxLen) then len := aMaxLen else len := Word(Length(x));
 
-  if (FPosition + SizeOf(Byte) + len) > FSize then
-    ExtendMemory(SizeOf(Byte) + len);
+  if (FPosition+SizeOf(Byte)+len) > FSize then ExtendMemory(SizeOf(Byte)+len);
 
-// Äëèíà ñòðîêè:
-  CopyMemory(Pointer(NativeUInt(FData) + FPosition),
-             @len, SizeOf(Byte));
-  FPosition := FPosition + SizeOf(Byte);
-// Ñòðîêà:
-  if len > 0 then
+  // Äëèíà ñòðîêè:
+  CopyMemory(Pointer(NativeUInt(FData)+FPosition), @len, SizeOf(len));
+  FPosition := FPosition+SizeOf(len);
+  // Ñòðîêà:
+  if (len > 0) then
   begin
-    CopyMemory(Pointer(NativeUInt(FData) + FPosition),
-               @x[1], len);
-    FPosition := FPosition + len;
+    CopyMemory(Pointer(NativeUInt(FData) + FPosition), @x[1], len);
+    FPosition := FPosition+len;
   end;
 end;
 
@@ -410,38 +406,39 @@ begin
     x := False;
 end;
 
-Procedure TBinMemoryReader.ReadString(Var x: String);
+Procedure TBinMemoryReader.ReadString (Var x: AnsiString);
 var
-  len: Byte;
-
+  len: Word;
 begin
-  if (FPosition + SizeOf(Byte)) <= FSize then
-    begin
+  if (FPosition+SizeOf(len)) <= FSize then
+  begin
     // Äëèíà ñòðîêè:
-      CopyMemory(@len,
-                 Pointer(NativeUInt(FData) + FPosition),
-                 SizeOf(Byte));
-
-      if (FPosition + SizeOf(Byte) + len) <= FSize then
-        begin
-          FPosition := FPosition + SizeOf(Byte);
-        // Ñòðîêà:
-          SetLength(x, len);
-          if len > 0 then
-            begin
-              CopyMemory(@x[1],
-                         Pointer(NativeUInt(FData) + FPosition),
-                         len);
-              FPosition := FPosition + len;
-            end
-          else
-            x := '';
-        end
+    CopyMemory(@len, Pointer(NativeUInt(FData)+FPosition), SizeOf(len));
+    if (FPosition+SizeOf(len)+len <= FSize) then
+    begin
+      FPosition := FPosition+SizeOf(len);
+      // Ñòðîêà:
+      UniqueString(x);
+      SetLength(x, len);
+      if (len > 0) then
+      begin
+        CopyMemory(@x[1], Pointer(NativeUInt(FData) + FPosition), len);
+        FPosition := FPosition+len;
+      end
       else
-        raise EBinSizeError.Create('TBinMemoryReader.ReadString: Too Long String');
+      begin
+        x := '';
+      end;
     end
+    else
+    begin
+      raise EBinSizeError.Create('TBinMemoryReader.ReadString: Too Long String');
+    end;
+  end
   else
+  begin
     raise EBinSizeError.Create('TBinMemoryReader.ReadString: End of Memory');
+  end;
 end;
 
 Procedure TBinMemoryReader.ReadMemory(Var x: Pointer; Var memSize: Cardinal);
index ededbdd40c7f885f7d8ea932af4493002678f51d..d22e73f60a6f77aaf4a501e1a2d0cc2c948a40ba 100644 (file)
@@ -119,8 +119,11 @@ type
 
   private
     // user fields
-    function getGamePanelId (): Integer; inline;
-    procedure setGamePanelId (v: Integer); inline;
+    function getUserPanelId (): Integer; inline;
+    procedure setUserPanelId (v: Integer); inline;
+
+    function getUserTrigRef (): Boolean; inline;
+    procedure setUserTrigRef (v: Boolean); inline;
 
   public
     property panel[idx: Integer]: TDynRecord read getPanelByIdx;
@@ -130,7 +133,8 @@ type
     property tgShotPanelID: Integer read getPanelId {write setPanelId};
     property TexturePanel: Integer read getTexturePanel {write setTexturePanel}; // texturepanel, int
     // user fields
-    property gamePanelId: Integer read getGamePanelId write setGamePanelId;
+    property userPanelId: Integer read getUserPanelId write setUserPanelId;
+    property userPanelTrigRef: Boolean read getUserTrigRef write setUserTrigRef;
   end;
 
 implementation
@@ -144,18 +148,33 @@ constructor TDFPoint.Create (ax, ay: LongInt); begin X := ax; Y := ay; end;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
-function TDynRecordHelper.getGamePanelId (): Integer; inline;
+function TDynRecordHelper.getUserPanelId (): Integer; inline;
 var
   fld: TDynField;
 begin
-  fld := field['gamePanelId'];
+  fld := field['userPanelId'];
   if (fld = nil) or (fld.baseType <> TDynField.TType.TInt) then result := -1 else result := fld.ival;
 end;
 
 
-procedure TDynRecordHelper.setGamePanelId (v: Integer); inline;
+procedure TDynRecordHelper.setUserPanelId (v: Integer); inline;
+begin
+  setUserField('userPanelId', Integer(v));
+end;
+
+
+function TDynRecordHelper.getUserTrigRef (): Boolean; inline;
+var
+  fld: TDynField;
+begin
+  fld := field['userPanelTrigRef'];
+  if (fld = nil) or (fld.baseType <> TDynField.TType.TBool) then result := false else result := (fld.ival <> 0);
+end;
+
+
+procedure TDynRecordHelper.setUserTrigRef (v: Boolean); inline;
 begin
-  setUserField('gamePanelId', Integer(v));
+  setUserField('userPanelTrigRef', v);
 end;
 
 
index 1caa1f3338c12bb30327a45ac45a4abff404199b..c01287f2c53e5fd6f3503292ec25a3ad2a9403dd 100644 (file)
@@ -81,6 +81,8 @@ function Int64ToStrComma (i: Int64): AnsiString;
 function UpCase1251 (ch: Char): Char;
 function LoCase1251 (ch: Char): Char;
 
+function toLowerCase1251 (const s: AnsiString): AnsiString;
+
 // `true` if strings are equal; ignoring case for cp1251
 function StrEquCI1251 (const s0, s1: AnsiString): Boolean;
 
@@ -843,6 +845,26 @@ begin
 end;
 
 
+function toLowerCase1251 (const s: AnsiString): AnsiString;
+var
+  f: Integer;
+  ch: AnsiChar;
+begin
+  for ch in s do
+  begin
+    if (ch <> LoCase1251(ch)) then
+    begin
+      result := '';
+      SetLength(result, Length(s));
+      for f := 1 to Length(s) do result[f] := LoCase1251(s[f]);
+      exit;
+    end;
+  end;
+  // nothing to do
+  result := s;
+end;
+
+
 // ////////////////////////////////////////////////////////////////////////// //
 // utils
 // `ch`: utf8 start
index a916ff6ceb694d234c337faeae4646e3cffb3e5f..69d04394ceaeacd10c077c149852d82dd8a210a7 100644 (file)
@@ -36,7 +36,7 @@ type
     function getIsOpen (): Boolean;
     function isMapResource (idx: Integer): Boolean;
 
-    function GetResourceEx (name: AnsiString; wantMap: Boolean; var pData: Pointer; var Len: Integer): Boolean;
+    function GetResourceEx (name: AnsiString; wantMap: Boolean; var pData: Pointer; var Len: Integer; logError: Boolean=true): Boolean;
 
    public
     constructor Create ();
@@ -47,8 +47,8 @@ type
     function ReadFile (FileName: AnsiString): Boolean;
     function ReadMemory (Data: Pointer; Len: LongWord): Boolean;
 
-    function GetResource (name: AnsiString; var pData: Pointer; var Len: Integer): Boolean;
-    function GetMapResource (name: AnsiString; var pData: Pointer; var Len: Integer): Boolean;
+    function GetResource (name: AnsiString; var pData: Pointer; var Len: Integer; logError: Boolean=true): Boolean;
+    function GetMapResource (name: AnsiString; var pData: Pointer; var Len: Integer; logError: Boolean=true): Boolean;
     function GetMapResources (): SArray;
 
     // returns `nil` if file wasn't found
@@ -297,7 +297,7 @@ begin
 end;
 
 
-function TWADFile.GetResourceEx (name: AnsiString; wantMap: Boolean; var pData: Pointer; var Len: Integer): Boolean;
+function TWADFile.GetResourceEx (name: AnsiString; wantMap: Boolean; var pData: Pointer; var Len: Integer; logError: Boolean=true): Boolean;
 var
   f, lastSlash: Integer;
   fi: TSFSFileInfo;
@@ -355,7 +355,7 @@ begin
       if fs = nil then
       begin
         if wantMap then continue;
-        e_WriteLog(Format('DFWAD: can''t open file [%s] in [%s]', [name, fFileName]), MSG_WARNING);
+        if logError then e_WriteLog(Format('DFWAD: can''t open file [%s] in [%s]', [name, fFileName]), MSG_WARNING);
         break;
       end;
       // if we want only maps, check if this is map
@@ -431,17 +431,17 @@ begin
       exit;
     end;
   end;
-  e_WriteLog(Format('DFWAD: file [%s] not found in [%s]', [name, fFileName]), MSG_WARNING);
+  if logError then e_WriteLog(Format('DFWAD: file [%s] not found in [%s]', [name, fFileName]), MSG_WARNING);
 end;
 
-function TWADFile.GetResource (name: AnsiString; var pData: Pointer; var Len: Integer): Boolean;
+function TWADFile.GetResource (name: AnsiString; var pData: Pointer; var Len: Integer; logError: Boolean=true): Boolean;
 begin
-  result := GetResourceEx(name, false, pData, Len);
+  result := GetResourceEx(name, false, pData, Len, logError);
 end;
 
-function TWADFile.GetMapResource (name: AnsiString; var pData: Pointer; var Len: Integer): Boolean;
+function TWADFile.GetMapResource (name: AnsiString; var pData: Pointer; var Len: Integer; logError: Boolean=true): Boolean;
 begin
-  result := GetResourceEx(name, true, pData, Len);
+  result := GetResourceEx(name, true, pData, Len, logError);
 end;
 
 function TWADFile.GetMapResources (): SArray;
index ac55cf5d0360202e28589c97e5525e71253dc3e2..12d7256429d473c5f46061d7a7fe4084d2251f97 100644 (file)
@@ -249,6 +249,7 @@ type
 
     procedure setUserField (const fldname: AnsiString; v: LongInt);
     procedure setUserField (const fldname: AnsiString; v: AnsiString);
+    procedure setUserField (const fldname: AnsiString; v: Boolean);
 
   public
     property id: AnsiString read mId; // for map parser
@@ -1985,6 +1986,31 @@ begin
 end;
 
 
+procedure TDynRecord.setUserField (const fldname: AnsiString; v: Boolean);
+var
+  fld: TDynField;
+begin
+  if (Length(fldname) = 0) then exit;
+  fld := field[fldname];
+  if (fld <> nil) then
+  begin
+    if (fld.mType <> fld.TType.TBool) or (fld.mEBS <> fld.TEBS.TNone) then
+    begin
+      raise Exception.Create(Format('invalid user field ''%s'' type', [fld.name]));
+    end;
+  end
+  else
+  begin
+    fld := TDynField.Create(fldname, fld.TType.TBool);
+    fld.mOwner := self;
+    fld.mIVal := Integer(v);
+    fld.mInternal := true;
+    fld.mDefined := true;
+    addField(fld);
+  end;
+end;
+
+
 procedure TDynRecord.parseDef (pr: TTextParser);
 var
   fld: TDynField;