DEADSOFTWARE

net: game: other: hash database and resource downloader converted to new dirsys
[d2df-sdl.git] / src / game / g_map.pas
index 3b2ad4ca4d927e35096756e5ad702226b56664b2..e99d47b19186dd65974751d9a2dd3e90539e3439 100644 (file)
@@ -2,8 +2,7 @@
  *
  * 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.
+ * the Free Software Foundation, version 3 of the License ONLY.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -196,6 +195,7 @@ const
   GridTagLift = 1 shl 8; // gLifts
   GridTagBlockMon = 1 shl 9; // gBlockMon
 
+  GridTagSolid = (GridTagWall or GridTagDoor);
   GridTagObstacle = (GridTagStep or GridTagWall or GridTagDoor);
   GridTagLiquid = (GridTagAcid1 or GridTagAcid2 or GridTagWater);
 
@@ -226,7 +226,7 @@ var
   gLiftMap: array of array of DWORD;
   gWADHash: TMD5Digest;
   BackID:  DWORD = DWORD(-1);
-  gExternalResources: TStringList;
+  gExternalResources: array of TDiskFileInfo = nil;
   gMovingWallIds: array of Integer = nil;
 
   gdbg_map_use_accel_render: Boolean = true;
@@ -252,18 +252,15 @@ var
 implementation
 
 uses
-{$IFDEF USE_NANOGL}
-  nanoGL,
-{$ELSE}
-  GL, GLExt,
-{$ENDIF}
-  e_input, g_main, e_log, e_texture, g_items, g_gfx, g_console,
+  {$INCLUDE ../nogl/noGLuses.inc}
+  e_input, g_main, e_log, e_texture, e_res, g_items, g_gfx, g_console,
   g_weapons, g_game, g_sound, e_sound, CONFIG,
   g_options, g_triggers, g_player,
   Math, g_monsters, g_saveload, g_language, g_netmsg,
   sfs, xstreams, hashtable, wadreader,
   ImagingTypes, Imaging, ImagingUtility,
-  ImagingGif, ImagingNetworkGraphics;
+  ImagingGif, ImagingNetworkGraphics,
+  g_res_downloader;
 
 const
   FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52);
@@ -348,8 +345,8 @@ begin
 
   try
     e_LogWritefln('parsing "mapdef.txt"...', []);
-    st := openDiskFileRO(DataDir+'mapdef.txt');
-    e_LogWritefln('found local "%smapdef.txt"', [DataDir]);
+    st := e_OpenResourceRO(DataDirs, 'mapdef.txt');
+    e_LogWritefln('found local "mapdef.txt"', []);
   except
     st := nil;
   end;
@@ -439,17 +436,19 @@ end;
 // ////////////////////////////////////////////////////////////////////////// //
 var
   NNF_PureName: String; // Èìÿ òåêñòóðû áåç öèôð â êîíöå
+  NNF_PureExt: String; // extension postfix
   NNF_FirstNum: Integer; // ×èñëî ó íà÷àëüíîé òåêñòóðû
   NNF_CurrentNum: Integer; // Ñëåäóþùåå ÷èñëî ó òåêñòóðû
 
 
 function g_Texture_NumNameFindStart(name: String): Boolean;
 var
-  i: Integer;
+  i, j: Integer;
 
 begin
   Result := False;
   NNF_PureName := '';
+  NNF_PureExt := '';
   NNF_FirstNum := -1;
   NNF_CurrentNum := -1;
 
@@ -462,8 +461,11 @@ begin
         end
       else
         begin
+          j := i + 1;
+          while (j <= Length(name)) and (name[j] <> '.') do inc(j);
           NNF_PureName := Copy(name, 1, i);
-          Delete(name, 1, i);
+          NNF_PureExt := Copy(name, j);
+          name := Copy(name, i + 1, j - i - 1);
           Break;
         end;
     end;
@@ -487,7 +489,7 @@ begin
     Exit;
   end;
 
-  newName := NNF_PureName + IntToStr(NNF_CurrentNum);
+  newName := NNF_PureName + IntToStr(NNF_CurrentNum) + NNF_PureExt;
 
   if NNF_CurrentNum < NNF_FirstNum then
     Result := NNF_NAME_BEFORE
@@ -545,9 +547,9 @@ var
 procedure g_Map_ProfilersBegin ();
 begin
   if (profMapCollision = nil) then profMapCollision := TProfiler.Create('COLSOLID', g_profile_history_size);
-  profMapCollision.mainBegin(g_profile_collision);
+  if (profMapCollision <> nil) then profMapCollision.mainBegin(g_profile_collision);
   // create sections
-  if g_profile_collision then
+  if g_profile_collision and (profMapCollision <> nil) then
   begin
     profMapCollision.sectionBegin('*solids');
     profMapCollision.sectionEnd();
@@ -605,10 +607,10 @@ begin
   begin
     // stop if the lift of the right type
     result :=
-      ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
-       (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
-       (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
-       (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3)));
+      ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = LIFTTYPE_UP)) or
+       (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = LIFTTYPE_DOWN)) or
+       (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = LIFTTYPE_LEFT)) or
+       (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = LIFTTYPE_RIGHT)));
     exit;
   end;
   result := true; // otherwise, stop anyway, 'cause `forEachAtPoint()` is guaranteed to call this only for correct panels
@@ -898,7 +900,100 @@ begin
 end;
 
 
-function CreateTexture(RecName: AnsiString; Map: string; log: Boolean): Integer;
+function extractWadName (resourceName: string): string;
+var
+  posN: Integer;
+begin
+  posN := Pos(':', resourceName);
+  if posN > 0 then
+    Result:= Copy(resourceName, 0, posN-1)
+  else
+    Result := '';
+end;
+
+
+procedure addResToExternalResList (res: AnsiString);
+var
+  uname: AnsiString;
+  f: Integer;
+  fi: TDiskFileInfo;
+begin
+  if g_Game_IsClient or not g_Game_IsNet then exit;
+  if (length(res) = 0) then exit; // map wad
+  //res := extractWadName(res);
+  //if (length(res) = 0) then exit; // map wad
+  uname := toLowerCase1251(res);
+  // do not add duplicates
+  for f := 0 to High(gExternalResources) do
+  begin
+    if (gExternalResources[f].userName = uname) then exit;
+  end;
+  //writeln('***(000) addResToExternalResList: res=[', res, ']');
+  // add new resource
+  fi.userName := uname;
+  if not findFileCI(res) then exit;
+  //writeln('***(001) addResToExternalResList: res=[', res, ']');
+  fi.diskName := res;
+  if (not GetDiskFileInfo(res, fi)) then
+  begin
+    fi.tag := -1;
+  end
+  else
+  begin
+    //writeln('***(002) addResToExternalResList: res=[', res, ']');
+    fi.tag := 0; // non-zero means "cannot caclucate hash"
+    try
+      fi.hash := MD5File(fi.diskName);
+    except
+      fi.tag := -1;
+    end;
+  end;
+  //e_LogWritefln('addext: res=[%s]; uname=[%s]; diskName=[%s]', [res, fi.userName, fi.diskName]);
+  SetLength(gExternalResources, length(gExternalResources)+1);
+  gExternalResources[High(gExternalResources)] := fi;
+end;
+
+
+procedure compactExtResList ();
+var
+  src, dest: Integer;
+begin
+  src := 0;
+  dest := 0;
+  for src := 0 to High(gExternalResources) do
+  begin
+    if (gExternalResources[src].tag = 0) then
+    begin
+      // copy it
+      if (dest <> src) then gExternalResources[dest] := gExternalResources[src];
+      Inc(dest);
+    end;
+  end;
+  if (dest <> length(gExternalResources)) then SetLength(gExternalResources, dest);
+end;
+
+
+function GetReplacementWad (WadName: AnsiString): AnsiString;
+begin
+  result := '';
+  if WadName <> '' then
+  begin
+    result := WadName;
+    if g_Game_IsClient then result := g_Res_FindReplacementWad(WadName);
+    if (result = WadName) then result := e_FindWad(WadDirs, result)
+  end;
+end;
+
+
+procedure generateExternalResourcesList (map: TDynRecord);
+begin
+  SetLength(gExternalResources, 0);
+  addResToExternalResList(GetReplacementWad(g_ExtractWadName(map.MusicName)));
+  addResToExternalResList(GetReplacementWad(g_ExtractWadName(map.SkyName)));
+end;
+
+
+function CreateTexture (RecName: AnsiString; Map: string; log: Boolean): Integer;
 var
   WAD: TWADFile;
   TextureData: Pointer;
@@ -956,12 +1051,11 @@ begin
   end;
 
   // Çàãðóæàåì ðåñóðñ òåêñòóðû â ïàìÿòü èç WAD'à:
-  WADName := g_ExtractWadName(RecName);
+  WADName := GetReplacementWad(g_ExtractWadName(RecName));
+  if (WADName <> '') then addResToExternalResList(WADName);
+  if WADName = '' then WADName := Map; //WADName := GameDir+'/wads/'+WADName else
 
   WAD := TWADFile.Create();
-
-  if WADName <> '' then WADName := GameDir+'/wads/'+WADName else WADName := Map;
-
   WAD.ReadFile(WADName);
 
   //txname := RecName;
@@ -1044,11 +1138,13 @@ begin
   end;
 
   // ×èòàåì WAD-ðåñóðñ àíèì.òåêñòóðû èç WAD'à â ïàìÿòü:
-  WADName := g_ExtractWadName(RecName);
+  WADName := GetReplacementWad(g_ExtractWadName(RecName));
+  if (WADName <> '') then addResToExternalResList(WADName);
+  if WADName = '' then WADName := Map; //WADName := GameDir+'/wads/'+WADName else
 
   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);
 
@@ -1082,8 +1178,7 @@ begin
     end;
 
     // ýòî ïòèöà? ýòî ñàìîë¸ò?
-    if (TextureWAD[0] = 'D') and (TextureWAD[1] = 'F') and
-       (TextureWAD[2] = 'W') and (TextureWAD[3] = 'A') and (TextureWAD[4] = 'D') then
+    if isWadData(TextureWAD, ResLength) then
     begin
       // íåò, ýòî ñóïåðìåí!
       if not WAD.ReadMemory(TextureWAD, ResLength) then
@@ -1435,66 +1530,6 @@ begin
   g_Mons_ForEach(monsDieTrig);
 end;
 
-function extractWadName(resourceName: string): string;
-var
-  posN: Integer;
-begin
-  posN := Pos(':', resourceName);
-  if posN > 0 then
-    Result:= Copy(resourceName, 0, posN-1)
-  else
-    Result := '';
-end;
-
-procedure addResToExternalResList(res: string);
-begin
-  res := extractWadName(res);
-  if (res <> '') and (gExternalResources.IndexOf(res) = -1) then
-    gExternalResources.Add(res);
-end;
-
-procedure generateExternalResourcesList({mapReader: TMapReader_1}map: TDynRecord);
-//var
-  //textures: TTexturesRec1Array;
-  //textures: TDynField;
-  //trec: TDynRecord;
-  //mapHeader: TMapHeaderRec_1;
-  //i: integer;
-  //resFile: String = '';
-begin
-  if gExternalResources = nil then
-    gExternalResources := TStringList.Create;
-
-  gExternalResources.Clear;
-
-  (*
-  {
-  textures := GetTextures(map);
-  for i := 0 to High(textures) do
-  begin
-    addResToExternalResList(resFile);
-  end;
-  }
-
-  textures := map['texture'];
-  if (textures <> nil) then
-  begin
-    for trec in textures do
-    begin
-      addResToExternalResList(resFile);
-    end;
-  end;
-
-  textures := nil;
-  *)
-
-  //mapHeader := GetMapHeader(map);
-
-  addResToExternalResList(map.MusicName);
-  addResToExternalResList(map.SkyName);
-end;
-
-
 procedure mapCreateGrid ();
 var
   mapX0: Integer = $3fffffff;
@@ -1674,7 +1709,7 @@ begin
     if (gCurrentMap = nil) then
     begin
       FileName := g_ExtractWadName(Res);
-      e_WriteLog('Loading map WAD: '+FileName, TMsgType.Notify);
+      e_LogWritefln('Loading map WAD [%s] (res=[%s])', [FileName, Res], TMsgType.Notify);
       g_Game_SetLoadingText(_lc[I_LOAD_WAD_FILE], 0, False);
 
       WAD := TWADFile.Create();
@@ -2180,21 +2215,13 @@ begin
     begin
       e_WriteLog('  Loading sky: ' + gMapInfo.SkyName, TMsgType.Notify);
       g_Game_SetLoadingText(_lc[I_LOAD_SKY], 0, False);
-      FileName := g_ExtractWadName(gMapInfo.SkyName);
-
-      if (FileName <> '') then FileName := GameDir+'/wads/'+FileName else FileName := g_ExtractWadName(Res);
-
       if gTextureFilter then TEXTUREFILTER := GL_LINEAR else TEXTUREFILTER := GL_NEAREST;
       try
-        s := FileName+':'+g_ExtractFilePathName(gMapInfo.SkyName);
+        s := e_GetResourcePath(WadDirs, gMapInfo.SkyName, g_ExtractWadName(Res));
         if g_Texture_CreateWAD(BackID, s) then
-        begin
-          g_Game_SetupScreenSize();
-        end
+          g_Game_SetupScreenSize
         else
-        begin
-          g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]));
-        end;
+          g_FatalError(Format(_lc[I_GAME_ERROR_SKY], [s]))
       finally
         TEXTUREFILTER := GL_NEAREST;
       end;
@@ -2206,16 +2233,8 @@ begin
     begin
       e_WriteLog('  Loading music: ' + gMapInfo.MusicName, TMsgType.Notify);
       g_Game_SetLoadingText(_lc[I_LOAD_MUSIC], 0, False);
-      FileName := g_ExtractWadName(gMapInfo.MusicName);
-
-      if FileName <> '' then
-        FileName := GameDir+'/wads/'+FileName
-      else
-        begin
-          FileName := g_ExtractWadName(Res);
-        end;
 
-      s := FileName+':'+g_ExtractFilePathName(gMapInfo.MusicName);
+      s := e_GetResourcePath(WadDirs, gMapInfo.MusicName, g_ExtractWadName(Res));
       if g_Sound_CreateWADEx(gMapInfo.MusicName, s, True) then
         ok := True
       else
@@ -2259,7 +2278,7 @@ begin
   finally
     sfsGCEnable(); // enable releasing unused volumes
     //mapReader.Free();
-    e_ClearInputBuffer(); // why not?
+    e_UnpressAllKeys; // why not?
     if not mapOk then
     begin
       gCurrentMap.Free();
@@ -2268,6 +2287,7 @@ begin
     end;
   end;
 
+  compactExtResList();
   e_WriteLog('Done loading map.', TMsgType.Notify);
   Result := True;
 end;
@@ -2513,6 +2533,7 @@ var
   a, d, j: Integer;
   m: Word;
   s: String;
+  b: Byte;
 
   procedure UpdatePanelArray(var panels: TPanelArray);
   var
@@ -2563,6 +2584,15 @@ begin
               s := _lc[I_PLAYER_FLAG_BLUE];
             g_Game_Message(Format(_lc[I_MESSAGE_FLAG_RETURN], [AnsiUpperCase(s)]), 144);
 
+            if (((gPlayer1 <> nil) and (((gPlayer1.Team = TEAM_RED) and (a = FLAG_RED)) or ((gPlayer1.Team = TEAM_BLUE) and (a = FLAG_BLUE))))
+            or ((gPlayer2 <> nil) and (((gPlayer2.Team = TEAM_RED) and (a = FLAG_RED)) or ((gPlayer2.Team = TEAM_BLUE) and (a = FLAG_BLUE))))) then
+              b := 0
+            else
+              b := 1;
+
+            if not sound_ret_flag[b].IsPlaying() then
+              sound_ret_flag[b].Play();
+
             if g_Game_IsNet then
               MH_SEND_FlagEvent(FLAG_STATE_RETURNED, a, 0);
             Continue;
@@ -2747,10 +2777,10 @@ begin
       h := High(gLifts);
 
       for a := 0 to h do
-        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
+        if ((WordBool(PanelType and (PANEL_LIFTUP)) and (gLifts[a].LiftType = LIFTTYPE_UP)) or
+           (WordBool(PanelType and (PANEL_LIFTDOWN)) and (gLifts[a].LiftType = LIFTTYPE_DOWN)) or
+           (WordBool(PanelType and (PANEL_LIFTLEFT)) and (gLifts[a].LiftType = LIFTTYPE_LEFT)) or
+           (WordBool(PanelType and (PANEL_LIFTRIGHT)) and (gLifts[a].LiftType = LIFTTYPE_RIGHT))) and
            g_Collide(X, Y, Width, Height,
            gLifts[a].X, gLifts[a].Y,
            gLifts[a].Width, gLifts[a].Height) then
@@ -2826,10 +2856,10 @@ const
     if ((tag and GridTagLift) <> 0) then
     begin
       result :=
-        ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
-         (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
-         (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
-         (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))) {and
+        ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = LIFTTYPE_UP)) or
+         (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = LIFTTYPE_DOWN)) or
+         (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = LIFTTYPE_LEFT)) or
+         (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = LIFTTYPE_RIGHT))) {and
          g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height)};
       exit;
     end;
@@ -2875,10 +2905,10 @@ begin
         if ((pan.tag and GridTagLift) <> 0) then
         begin
           result :=
-            ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or
-             (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or
-             (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or
-             (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))) {and
+            ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = LIFTTYPE_UP)) or
+             (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = LIFTTYPE_DOWN)) or
+             (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = LIFTTYPE_LEFT)) or
+             (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = LIFTTYPE_RIGHT))) {and
              g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height)};
         end
         else if ((pan.tag and GridTagBlockMon) <> 0) then
@@ -3040,10 +3070,10 @@ begin
     //TODO: make separate lift tags, and change tag here
 
     case LiftType of
-      0: g_Mark(X, Y, Width, Height, MARK_LIFTUP);
-      1: g_Mark(X, Y, Width, Height, MARK_LIFTDOWN);
-      2: g_Mark(X, Y, Width, Height, MARK_LIFTLEFT);
-      3: g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT);
+      LIFTTYPE_UP:    g_Mark(X, Y, Width, Height, MARK_LIFTUP);
+      LIFTTYPE_DOWN:  g_Mark(X, Y, Width, Height, MARK_LIFTDOWN);
+      LIFTTYPE_LEFT:  g_Mark(X, Y, Width, Height, MARK_LIFTLEFT);
+      LIFTTYPE_RIGHT: g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT);
     end;
 
     //if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pguid);