X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_map.pas;h=1695460c162e06e3b8b36e9c0f3b6b629390cc4a;hb=923fa980434e55419f35422119af2faae2bf68d7;hp=358b16d15e44d7ec97e9eb3e63c6af22871e29dc;hpb=955bce518d1ce6de18133b8ee19a6436aef174cd;p=d2df-sdl.git diff --git a/src/game/g_map.pas b/src/game/g_map.pas index 358b16d..1695460 100644 --- a/src/game/g_map.pas +++ b/src/game/g_map.pas @@ -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 . + *) +{$INCLUDE ../shared/a_modes.inc} +{$DEFINE MAP_DEBUG_ENABLED_FLAG} unit g_map; interface uses - e_graphics, g_basic, MAPSTRUCT, g_textures, Classes, - g_phys, WADEDITOR, BinEditor, g_panel, md5; + e_graphics, g_basic, MAPDEF, g_textures, Classes, + g_phys, wadreader, BinEditor, g_panel, g_grid, md5, binheap, xprofiler, xparser, xdynrec; type TMapInfo = record @@ -39,17 +56,19 @@ 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 (PanelType: Word); // unaccelerated +procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer); + procedure g_Map_DrawBack(dx, dy: Integer); function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; - PanelType: Word; b1x3: Boolean): Boolean; + PanelType: Word; b1x3: Boolean=false): Boolean; function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD; procedure g_Map_EnableWall(ID: DWORD); procedure g_Map_DisableWall(ID: DWORD); @@ -66,9 +85,47 @@ 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); + +// returns panel or nil +// sets `ex` and `ey` to `x1` and `y1` when no hit was detected +function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel; + +// returns panel or nil +// sets `ex` and `ey` to `x1` and `y1` when no hit was detected +function g_Map_traceToNearest (x0, y0, x1, y1: Integer; tag: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel; + +type + TForEachPanelCB = function (pan: TPanel): Boolean; // return `true` to stop + +function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean; +function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel; + +// trace liquid, stepping by `dx` and `dy` +// return last seen liquid coords, and `false` if we're started outside of the liquid +function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean; + + +procedure g_Map_ProfilersBegin (); +procedure g_Map_ProfilersEnd (); + + +function g_Map_ParseMap (data: Pointer; dataLen: Integer): TDynRecord; + +const + NNF_NO_NAME = 0; + NNF_NAME_BEFORE = 1; + NNF_NAME_EQUALS = 2; + NNF_NAME_AFTER = 3; + +function g_Texture_NumNameFindStart(name: String): Boolean; +function g_Texture_NumNameFindNext(var newName: String): Byte; + const RESPAWNPOINT_PLAYER1 = 1; RESPAWNPOINT_PLAYER2 = 2; @@ -92,6 +149,35 @@ const SKY_STRETCH: Single = 1.5; +const + GridTagInvalid = 0; + + (* draw order: + PANEL_BACK + PANEL_STEP + PANEL_WALL + PANEL_CLOSEDOOR + PANEL_ACID1 + PANEL_ACID2 + PANEL_WATER + PANEL_FORE + *) + // sorted by draw priority + GridTagBack = 1 shl 0; + GridTagStep = 1 shl 1; + GridTagWall = 1 shl 2; + GridTagDoor = 1 shl 3; + GridTagAcid1 = 1 shl 4; + GridTagAcid2 = 1 shl 5; + GridTagWater = 1 shl 6; + GridTagFore = 1 shl 7; + // the following are invisible + GridTagLift = 1 shl 8; + GridTagBlockMon = 1 shl 9; + + GridDrawableMask = (GridTagBack or GridTagStep or GridTagWall or GridTagDoor or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagFore); + + var gWalls: TPanelArray; gRenderBackgrounds: TPanelArray; @@ -103,33 +189,379 @@ var gFlags: array [FLAG_RED..FLAG_BLUE] of TFlag; //gDOMFlags: array of TFlag; gMapInfo: TMapInfo; - gBackSize: TPoint; + gBackSize: TDFPoint; gDoorMap: array of array of DWORD; gLiftMap: array of array of DWORD; gWADHash: TMD5Digest; BackID: DWORD = DWORD(-1); gExternalResources: TStringList; + gdbg_map_use_accel_render: Boolean = true; + gdbg_map_use_accel_coldet: Boolean = true; + profMapCollision: TProfiler = nil; //WARNING: FOR DEBUGGING ONLY! + gDrawPanelList: TBinaryHeapObj = nil; // binary heap of all walls we have to render, populated by `g_Map_CollectDrawPanels()` + + gCurrentMap: TDynRecord = nil; + + +function panelTypeToTag (panelType: Word): Integer; // returns GridTagXXX + + +type + TPanelGrid = specialize TBodyGridBase; + +var + mapGrid: TPanelGrid = nil; // DO NOT USE! public for debugging only! + + implementation 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; + g_options, g_triggers, g_player, + Math, g_monsters, g_saveload, g_language, g_netmsg, + utils, sfs, xstreams, hashtable, + ImagingTypes, Imaging, ImagingUtility, + ImagingGif, ImagingNetworkGraphics; const FLAGRECT: TRectWH = (X:15; Y:12; Width:33; Height:52); MUSIC_SIGNATURE = $4953554D; // 'MUSI' FLAG_SIGNATURE = $47414C46; // 'FLAG' + +var + dfmapdef: TDynMapDef = nil; + +procedure loadMapDefinition (); +var + pr: TTextParser = nil; + st: TStream = nil; + WAD: TWADFile = nil; +begin + if (dfmapdef <> nil) then exit; + try + e_LogWritefln('parsing "mapdef.txt"...', []); + st := openDiskFileRO(DataDir+'mapdef.txt'); + except + st := nil; + e_LogWritefln('local "%smapdef.txt" not found', [DataDir]); + end; + if (st = nil) then + begin + WAD := TWADFile.Create(); + if not WAD.ReadFile(GameWAD) then + begin + //raise Exception.Create('cannot load "game.wad"'); + st := nil; + end + else + begin + st := WAD.openFileStream('mapdef.txt'); + end; + end; + + if (st = nil) then + begin + //raise Exception.Create('cannot open "mapdef.txt"'); + e_LogWritefln('using default "mapdef.txt"...', [], MSG_WARNING); + pr := TStrTextParser.Create(defaultMapDef); + end + else + begin + pr := TFileTextParser.Create(st); + end; + + try + dfmapdef := TDynMapDef.Create(pr); + except on e: Exception do + raise Exception.Create(Format('ERROR in "mapdef.txt" at (%s,%s): %s', [pr.line, pr.col, e.message])); + end; + + st.Free(); + WAD.Free(); +end; + + +function g_Map_ParseMap (data: Pointer; dataLen: Integer): TDynRecord; +var + wst: TSFSMemoryChunkStream = nil; + pr: TTextParser = nil; +begin + result := nil; + if (dataLen < 4) then exit; + loadMapDefinition(); + if (dfmapdef = nil) then raise Exception.Create('internal map loader error'); + + wst := TSFSMemoryChunkStream.Create(data, dataLen); + + if (PAnsiChar(data)[0] = 'M') and (PAnsiChar(data)[1] = 'A') and (PAnsiChar(data)[2] = 'P') and (PByte(data)[3] = 1) then + begin + // binary map + try + result := dfmapdef.parseBinMap(wst); + except on e: Exception do + begin + e_LogWritefln('ERROR: %s', [e.message]); + wst.Free(); + result := nil; + exit; + end; + end; + wst.Free(); + end + else + begin + // text map + pr := TFileTextParser.Create(wst); + try + result := dfmapdef.parseMap(pr); + except on e: Exception do + begin + if (pr <> nil) then e_LogWritefln('ERROR at (%s,%s): %s', [pr.line, pr.col, e.message]) + else e_LogWritefln('ERROR: %s', [e.message]); + pr.Free(); // will free `wst` + result := nil; + exit; + end; + end; + pr.Free(); // will free `wst` + end; +end; + + +var + NNF_PureName: String; // Èìÿ òåêñòóðû áåç öèôð â êîíöå + NNF_FirstNum: Integer; // ×èñëî ó íà÷àëüíîé òåêñòóðû + NNF_CurrentNum: Integer; // Ñëåäóþùåå ÷èñëî ó òåêñòóðû + + +function g_Texture_NumNameFindStart(name: String): Boolean; +var + i: Integer; + +begin + Result := False; + NNF_PureName := ''; + NNF_FirstNum := -1; + NNF_CurrentNum := -1; + + for i := Length(name) downto 1 do + if (name[i] = '_') then // "_" - ñèìâîë íà÷àëà íîìåðíîãî ïîñòôèêñà + begin + if i = Length(name) then + begin // Íåò öèôð â êîíöå ñòðîêè + Exit; + end + else + begin + NNF_PureName := Copy(name, 1, i); + Delete(name, 1, i); + Break; + end; + end; + +// Íå ïåðåâåñòè â ÷èñëî: + if not TryStrToInt(name, NNF_FirstNum) then + Exit; + + NNF_CurrentNum := 0; + + Result := True; +end; + + +function g_Texture_NumNameFindNext(var newName: String): Byte; +begin + if (NNF_PureName = '') or (NNF_CurrentNum < 0) then + begin + newName := ''; + Result := NNF_NO_NAME; + Exit; + end; + + newName := NNF_PureName + IntToStr(NNF_CurrentNum); + + if NNF_CurrentNum < NNF_FirstNum then + Result := NNF_NAME_BEFORE + else + if NNF_CurrentNum > NNF_FirstNum then + Result := NNF_NAME_AFTER + else + Result := NNF_NAME_EQUALS; + + Inc(NNF_CurrentNum); +end; + + +function panelTypeToTag (panelType: Word): Integer; +begin + case panelType of + PANEL_WALL: result := GridTagWall; // gWalls + PANEL_OPENDOOR, PANEL_CLOSEDOOR: result := GridTagDoor; // gWalls + PANEL_BACK: result := GridTagBack; // gRenderBackgrounds + PANEL_FORE: result := GridTagFore; // gRenderForegrounds + PANEL_WATER: result := GridTagWater; // gWater + PANEL_ACID1: result := GridTagAcid1; // gAcid1 + PANEL_ACID2: result := GridTagAcid2; // gAcid2 + PANEL_STEP: result := GridTagStep; // gSteps + PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: result := GridTagLift; // gLifts -- this is for all lifts + PANEL_BLOCKMON: result := GridTagBlockMon; // gBlockMon -- this is for all blockmons + else result := GridTagInvalid; + end; +end; + + +function dplLess (a, b: TObject): Boolean; var + pa, pb: TPanel; +begin + pa := TPanel(a); + pb := TPanel(b); + if (pa.tag < pb.tag) then begin result := true; exit; end; + if (pa.tag > pb.tag) then begin result := false; exit; end; + result := (pa.arrIdx < pb.arrIdx); +end; + +procedure dplClear (); +begin + if (gDrawPanelList = nil) then gDrawPanelList := TBinaryHeapObj.Create(@dplLess) else gDrawPanelList.clear(); +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; +procedure g_Map_ProfilersBegin (); +begin + if (profMapCollision = nil) then profMapCollision := TProfiler.Create('COLSOLID', g_profile_history_size); + profMapCollision.mainBegin(g_profile_collision); + // create sections + if g_profile_collision then + begin + profMapCollision.sectionBegin('*solids'); + profMapCollision.sectionEnd(); + profMapCollision.sectionBegin('liquids'); + profMapCollision.sectionEnd(); + end; +end; + +procedure g_Map_ProfilersEnd (); +begin + if (profMapCollision <> nil) then profMapCollision.mainEnd(); +end; + + +// wall index in `gWalls` or -1 +function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel; +var + ex, ey: Integer; +begin + result := mapGrid.traceRay(ex, ey, x0, y0, x1, y1, nil, (GridTagWall or GridTagDoor)); + if (result <> nil) then + begin + if (hitx <> nil) then hitx^ := ex; + if (hity <> nil) then hity^ := ey; + end + else + begin + if (hitx <> nil) then hitx^ := x1; + if (hity <> nil) then hity^ := y1; + end; +end; + +// returns panel or nil +function g_Map_traceToNearest (x0, y0, x1, y1: Integer; tag: Integer; hitx: PInteger=nil; hity: PInteger=nil): TPanel; +var + ex, ey: Integer; +begin + result := mapGrid.traceRay(ex, ey, x0, y0, x1, y1, nil, tag); + if (result <> nil) then + begin + if (hitx <> nil) then hitx^ := ex; + if (hity <> nil) then hity^ := ey; + end + else + begin + if (hitx <> nil) then hitx^ := x1; + if (hity <> nil) then hity^ := y1; + end; +end; + + +function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean; + + function checker (pan: TPanel; tag: Integer): Boolean; + begin + { + if ((tag and (GridTagWall or GridTagDoor)) <> 0) then + begin + result := pan.Enabled; // stop if wall is enabled + exit; + end; + } + + if ((tag and GridTagLift) <> 0) then + 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))); + exit; + end; + + result := true; // otherwise, stop anyway, 'cause `forEachAtPoint()` is guaranteed to call this only for correct panels + end; + +var + tagmask: Integer = 0; +begin + result := false; + + if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor); + if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater; + if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1; + if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2; + if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep; + if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift; + if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon; + + if (tagmask = 0) then exit;// just in case + if ((tagmask and GridTagLift) <> 0) then + begin + // slow + result := (mapGrid.forEachAtPoint(x, y, checker, tagmask) <> nil); + end + else + begin + // fast + result := (mapGrid.forEachAtPoint(x, y, nil, tagmask) <> nil); + end; +end; + + +function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel; +begin + result := nil; + if (tagmask = 0) then exit; + result := mapGrid.forEachAtPoint(x, y, nil, tagmask); +end; + + function g_Map_IsSpecialTexture(Texture: String): Boolean; begin Result := (Texture = TEXTURE_NAME_WATER) or @@ -303,7 +735,7 @@ begin PanelArray := nil; end; -function CreatePanel(PanelRec: TPanelRec_1; AddTextures: TAddTextureArray; +function CreatePanel(PanelRec: TDynRecord; AddTextures: TAddTextureArray; CurTex: Integer; sav: Boolean): Integer; var len: Integer; @@ -337,17 +769,25 @@ 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; + panels^[len].proxyId := -1; + panels^[len].tag := panelTypeToTag(PanelRec.PanelType); 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 @@ -355,26 +795,24 @@ begin Width := 1; Height := 1; Anim := False; - TextureID := TEXTURE_NONE; + TextureID := LongWord(TEXTURE_NONE); 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; @@ -390,25 +828,25 @@ begin TextureName := RecName; if TextureName = TEXTURE_NAME_WATER then - TextureID := TEXTURE_SPECIAL_WATER + TextureID := LongWord(TEXTURE_SPECIAL_WATER) else if TextureName = TEXTURE_NAME_ACID1 then - TextureID := TEXTURE_SPECIAL_ACID1 + TextureID := LongWord(TEXTURE_SPECIAL_ACID1) else if TextureName = TEXTURE_NAME_ACID2 then - TextureID := TEXTURE_SPECIAL_ACID2; + TextureID := LongWord(TEXTURE_SPECIAL_ACID2); 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,145 +855,260 @@ 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); 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); +procedure CreateItem(Item: TDynRecord); begin if g_Game_IsClient then Exit; @@ -567,10 +1120,10 @@ begin gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF, GM_COOP]); end; -procedure CreateArea(Area: TAreaRec_1); +procedure CreateArea(Area: TDynRecord); var a: Integer; - id: DWORD; + id: DWORD = 0; begin case Area.AreaType of AREA_DMPOINT, AREA_PLAYERPOINT1, AREA_PLAYERPOINT2, @@ -637,7 +1190,7 @@ begin end; end; -procedure CreateTrigger(Trigger: TTriggerRec_1; fTexturePanel1Type, fTexturePanel2Type: Word); +procedure CreateTrigger(Trigger: TDynRecord; atpanid, ashotpanid: Integer; fTexturePanel1Type, fTexturePanel2Type: Word); var _trigger: TTrigger; begin @@ -656,53 +1209,65 @@ begin TriggerType := Trigger.TriggerType; ActivateType := Trigger.ActivateType; Keys := Trigger.Keys; - Data.Default := Trigger.DATA; + trigPanelId := atpanid; + trigShotPanelId := ashotpanid; + //Data.Default := Trigger.DATA; + trigData := Trigger.trigRec.clone(); end; g_Triggers_Create(_trigger); end; -procedure CreateMonster(monster: TMonsterRec_1); +procedure CreateMonster(monster: TDynRecord); var - a, i: Integer; + a: Integer; + mon: TMonster; begin if g_Game_IsClient then Exit; if (gGameSettings.GameType = GT_SINGLE) or LongBool(gGameSettings.Options and GAME_OPTION_MONSTERS) then begin - i := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y, - TDirection(monster.Direction)); + mon := g_Monsters_Create(monster.MonsterType, monster.X, monster.Y, TDirection(monster.Direction)); if gTriggers <> nil then + begin for a := 0 to High(gTriggers) do - if gTriggers[a].TriggerType in [TRIGGER_PRESS, - TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then - if (gTriggers[a].Data.MonsterID-1) = gMonsters[i].StartID then - gMonsters[i].AddTrigger(a); + begin + if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then + begin + //if (gTriggers[a].Data.MonsterID-1) = Integer(mon.StartID) then mon.AddTrigger(a); + if (gTriggers[a].trigData.trigMonsterId) = Integer(mon.StartID) then mon.AddTrigger(a); + end; + end; + end; - if monster.MonsterType <> MONSTER_BARREL then - Inc(gTotalMonsters); + if monster.MonsterType <> MONSTER_BARREL then Inc(gTotalMonsters); end; end; procedure g_Map_ReAdd_DieTriggers(); -var - i, a: Integer; -begin - if g_Game_IsClient then Exit; - for i := 0 to High(gMonsters) do - if gMonsters[i] <> nil then + function monsDieTrig (mon: TMonster): Boolean; + var + a: Integer; + begin + result := false; // don't stop + mon.ClearTriggers(); + for a := 0 to High(gTriggers) do + begin + if gTriggers[a].TriggerType in [TRIGGER_PRESS, TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then begin - gMonsters[i].ClearTriggers(); - - for a := 0 to High(gTriggers) do - if gTriggers[a].TriggerType in [TRIGGER_PRESS, - TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF] then - if (gTriggers[a].Data.MonsterID-1) = gMonsters[i].StartID then - gMonsters[i].AddTrigger(a); + //if (gTriggers[a].Data.MonsterID-1) = Integer(mon.StartID) then mon.AddTrigger(a); + if (gTriggers[a].trigData.trigMonsterId) = Integer(mon.StartID) then mon.AddTrigger(a); end; + end; + end; + +begin + if g_Game_IsClient then Exit; + + g_Mons_ForEach(monsDieTrig); end; function extractWadName(resourceName: string): string; @@ -723,501 +1288,807 @@ begin gExternalResources.Add(res); end; -procedure generateExternalResourcesList(mapReader: TMapReader_1); -var - textures: TTexturesRec1Array; - mapHeader: TMapHeaderRec_1; - i: integer; - resFile: String = ''; +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 := mapReader.GetTextures(); + + (* + { + 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 := mapReader.GetMapHeader; + //mapHeader := GetMapHeader(map); - addResToExternalResList(mapHeader.MusicName); - addResToExternalResList(mapHeader.SkyName); + addResToExternalResList(map.MusicName); + addResToExternalResList(map.SkyName); end; + +procedure mapCreateGrid (); +var + mapX0: Integer = $3fffffff; + mapY0: Integer = $3fffffff; + mapX1: Integer = -$3fffffff; + mapY1: Integer = -$3fffffff; + + procedure calcBoundingBox (constref panels: TPanelArray); + var + idx: Integer; + pan: TPanel; + begin + for idx := 0 to High(panels) do + begin + pan := panels[idx]; + if not pan.visvalid then continue; + if (pan.Width < 1) or (pan.Height < 1) then continue; + if (mapX0 > pan.x0) then mapX0 := pan.x0; + if (mapY0 > pan.y0) then mapY0 := pan.y0; + if (mapX1 < pan.x1) then mapX1 := pan.x1; + if (mapY1 < pan.y1) then mapY1 := pan.y1; + end; + end; + + procedure addPanelsToGrid (constref panels: TPanelArray); + var + idx: Integer; + pan: TPanel; + newtag: Integer; + begin + //tag := panelTypeToTag(tag); + for idx := 0 to High(panels) do + begin + pan := panels[idx]; + if not pan.visvalid then continue; + if (pan.proxyId <> -1) then + begin + {$IF DEFINED(D2F_DEBUG)} + e_WriteLog(Format('DUPLICATE wall #%d(%d) enabled (%d); type:%08x', [Integer(idx), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.PanelType]), MSG_NOTIFY); + {$ENDIF} + continue; + end; + case pan.PanelType of + PANEL_WALL: newtag := GridTagWall; + PANEL_OPENDOOR, PANEL_CLOSEDOOR: newtag := GridTagDoor; + PANEL_BACK: newtag := GridTagBack; + PANEL_FORE: newtag := GridTagFore; + PANEL_WATER: newtag := GridTagWater; + PANEL_ACID1: newtag := GridTagAcid1; + PANEL_ACID2: newtag := GridTagAcid2; + PANEL_STEP: newtag := GridTagStep; + PANEL_LIFTUP, PANEL_LIFTDOWN, PANEL_LIFTLEFT, PANEL_LIFTRIGHT: newtag := GridTagLift; + PANEL_BLOCKMON: newtag := GridTagBlockMon; + else continue; // oops + end; + pan.tag := newtag; + + pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, newtag); + // "enabled" flag has meaning only for doors and walls (engine assumes it); but meh... + mapGrid.proxyEnabled[pan.proxyId] := pan.Enabled; + {$IFDEF MAP_DEBUG_ENABLED_FLAG} + { + if ((tag and (GridTagWall or GridTagDoor)) <> 0) then + begin + e_WriteLog(Format('INSERTED wall #%d(%d) enabled (%d)', [Integer(idx), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId])]), MSG_NOTIFY); + end; + } + {$ENDIF} + end; + end; + +begin + mapGrid.Free(); + mapGrid := nil; + + calcBoundingBox(gWalls); + calcBoundingBox(gRenderBackgrounds); + calcBoundingBox(gRenderForegrounds); + calcBoundingBox(gWater); + calcBoundingBox(gAcid1); + calcBoundingBox(gAcid2); + calcBoundingBox(gSteps); + calcBoundingBox(gLifts); + calcBoundingBox(gBlockMon); + + e_LogWritefln('map dimensions: (%d,%d)-(%d,%d); editor size:(0,0)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1, gMapInfo.Width, gMapInfo.Height]); + + if (mapX0 > 0) then mapX0 := 0; + if (mapY0 > 0) then mapY0 := 0; + + if (mapX1 < gMapInfo.Width-1) then mapX1 := gMapInfo.Width-1; + if (mapY1 < gMapInfo.Height-1) then mapY1 := gMapInfo.Height-1; + + mapGrid := TPanelGrid.Create(mapX0-128, mapY0-128, mapX1-mapX0+1+128*2, mapY1-mapY0+1+128*2); + //mapGrid := TPanelGrid.Create(0, 0, gMapInfo.Width, gMapInfo.Height); + + addPanelsToGrid(gWalls); + addPanelsToGrid(gRenderBackgrounds); + addPanelsToGrid(gRenderForegrounds); + addPanelsToGrid(gWater); + addPanelsToGrid(gAcid1); + addPanelsToGrid(gAcid2); + addPanelsToGrid(gSteps); + addPanelsToGrid(gLifts); // it doesn't matter which LIFT type is used here + addPanelsToGrid(gBlockMon); + + mapGrid.dumpStats(); + + g_Mons_InitTree(mapGrid.gridX0, mapGrid.gridY0, mapGrid.gridWidth, mapGrid.gridHeight); +end; + + function g_Map_Load(Res: String): Boolean; const DefaultMusRes = 'Standart.wad:STDMUS\MUS1'; DefaultSkyRes = 'Standart.wad:STDSKY\SKY0'; +type + PTRec = ^TTRec; + TTRec = record + //TexturePanel: Integer; + texPanIdx: Integer; + LiftPanelIdx: Integer; + DoorPanelIdx: Integer; + ShotPanelIdx: Integer; + trigrec: TDynRecord; + texPan: TDynRecord; + liftPan: TDynRecord; + doorPan: TDynRecord; + shotPan: TDynRecord; + end; var - WAD: TWADEditor_1; - MapReader: TMapReader_1; - Header: TMapHeaderRec_1; - _textures: TTexturesRec1Array; - panels: TPanelsRec1Array; - items: TItemsRec1Array; - monsters: TMonsterRec1Array; - areas: TAreasRec1Array; - triggers: TTriggersRec1Array; - a, b, c, k: Integer; + WAD: TWADFile; + //MapReader: TMapReader_1; + mapReader: TDynRecord = nil; + //Header: TMapHeaderRec_1; + _textures: TDynField = nil; //TTexturesRec1Array; tagInt: texture index + //_texnummap: array of Integer = nil; // `_textures` -> `Textures` + panels: TDynField = nil; //TPanelsRec1Array; + items: TDynField = nil; //TItemsRec1Array; + monsters: TDynField = nil; //TMonsterRec1Array; + areas: TDynField = nil; //TAreasRec1Array; + triggers: TDynField = nil; //TTriggersRec1Array; + b, c, k: Integer; PanelID: DWORD; AddTextures: TAddTextureArray; - texture: TTextureRec_1; - TriggersTable: Array of record - TexturePanel: Integer; - LiftPanel: Integer; - DoorPanel: Integer; - ShotPanel: Integer; - end; - FileName, SectionName, ResName, - FileName2, s, TexName: String; + //texture: TTextureRec_1; + TriggersTable: array of TTRec; + FileName, mapResName, s, TexName: String; Data: Pointer; Len: Integer; ok, isAnim, trigRef: Boolean; - CurTex: Integer; + CurTex, ntn: Integer; + rec, texrec: TDynRecord; + pttit: PTRec; + pannum, trignum, cnt: Integer; + // key: panel index; value: `TriggersTable` index + hashTextPan: THashIntInt = nil; + hashLiftPan: THashIntInt = nil; + hashDoorPan: THashIntInt = nil; + hashShotPan: THashIntInt = nil; begin + mapGrid.Free(); + mapGrid := nil; + + gCurrentMap.Free(); + gCurrentMap := nil; + Result := False; gMapInfo.Map := Res; TriggersTable := nil; - FillChar(texture, SizeOf(texture), 0); + mapReader := nil; + //FillChar(texture, SizeOf(texture), 0); + + 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 := TWADFile.Create(); + if not WAD.ReadFile(FileName) then + begin + g_FatalError(Format(_lc[I_GAME_ERROR_MAP_WAD], [FileName])); + WAD.Free(); + Exit; + end; -// Çàãðóçêà 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); + //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; - 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(); + if (Len < 4) then + begin + e_LogWritefln('invalid map file: ''%s''', [mapResName]); + FreeMem(Data); + 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; + // Çàãðóçêà êàðòû: + e_LogWritefln('Loading map: %s', [mapResName], MSG_NOTIFY); + g_Game_SetLoadingText(_lc[I_LOAD_MAP], 0, False); - FreeMem(Data); - generateExternalResourcesList(MapReader); -// Çàãðóçêà òåêñòóð: - g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False); - _textures := MapReader.GetTextures(); + try + mapReader := g_Map_ParseMap(Data, Len); + except + mapReader.Free(); + g_FatalError(Format(_lc[I_GAME_ERROR_MAP_LOAD], [Res])); + FreeMem(Data); + Exit; + end; -// Äîáàâëåíèå òåêñòóð â Textures[]: - if _textures <> nil then - begin - e_WriteLog(' Loading textures:', MSG_NOTIFY); - g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], High(_textures), False); + FreeMem(Data); - for a := 0 to High(_textures) do + hashTextPan := hashNewIntInt(); + hashLiftPan := hashNewIntInt(); + hashDoorPan := hashNewIntInt(); + hashShotPan := hashNewIntInt(); + + generateExternalResourcesList(MapReader); + //_textures := GetTextures(mapReader); + _textures := mapReader['texture']; + //_texnummap := nil; + // get all other lists here too + //panels := GetPanels(mapReader); + panels := mapReader['panel']; + //triggers := GetTriggers(mapReader); + triggers := mapReader['trigger']; + //items := GetItems(mapReader); + items := mapReader['item']; + //areas := GetAreas(mapReader); + areas := mapReader['area']; + //monsters := GetMonsters(mapReader); + monsters := mapReader['monster']; + + // Çàãðóçêà îïèñàíèÿ êàðòû: + e_WriteLog(' Reading map info...', MSG_NOTIFY); + g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False); + //Header := GetMapHeader(mapReader); + + with gMapInfo 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 - SetLength(s, b-1); - Break; - end; - e_WriteLog(' Loading texture: ' + s, MSG_NOTIFY); - // Àíèìèðîâàííàÿ òåêñòóðà: - if ByteBool(_textures[a].Anim) then + Name := mapReader.MapName; + Description := mapReader.MapDesc; + Author := mapReader.MapAuthor; + MusicName := mapReader.MusicName; + SkyName := mapReader.SkyName; + Height := mapReader.Height; + Width := mapReader.Width; + end; + + // Çàãðóçêà òåêñòóð: + g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], 0, False); + // Äîáàâëåíèå òåêñòóð â Textures[]: + if (_textures <> nil) and (_textures.count > 0) then + begin + e_WriteLog(' Loading textures:', MSG_NOTIFY); + g_Game_SetLoadingText(_lc[I_LOAD_TEXTURES], _textures.count-1, False); + //SetLength(_texnummap, _textures.count); + + cnt := -1; + for rec in _textures do + begin + Inc(cnt); + s := rec.Resource; + {$IF DEFINED(D2F_DEBUG_TXLOAD)} + e_WriteLog(Format(' Loading texture #%d: %s', [cnt, s]), MSG_NOTIFY); + {$ENDIF} + //if g_Map_IsSpecialTexture(s) then e_WriteLog(' SPECIAL!', MSG_NOTIFY); + if rec.Anim then begin - if not CreateAnimTexture(_textures[a].Resource, FileName, True) then - begin - g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s])); - CreateNullTexture(_textures[a].Resource); - end; + // Àíèìèðîâàííàÿ òåêñòóðà + ntn := CreateAnimTexture(rec.Resource, FileName, True); + if (ntn < 0) then g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_ANIM], [s])); end - else // Îáû÷íàÿ òåêñòóðà: - if not CreateTexture(_textures[a].Resource, FileName, True) then + else begin - g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s])); - CreateNullTexture(_textures[a].Resource); + // Îáû÷íàÿ òåêñòóðà + ntn := CreateTexture(rec.Resource, FileName, True); + if (ntn < 0) then g_SimpleError(Format(_lc[I_GAME_ERROR_TEXTURE_SIMPLE], [s])); end; + if (ntn < 0) then ntn := CreateNullTexture(rec.Resource); - g_Game_StepLoading(); + //_texnummap[a] := ntn; // fix texture number + rec.tagInt := ntn; + g_Game_StepLoading(); + end; + + // set panel tagInt to texture index + if (panels <> nil) then + begin + for rec in panels do + begin + texrec := rec.TextureRec; + if (texrec = nil) then rec.tagInt := -1 else rec.tagInt := texrec.tagInt; + end; + 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); -// Çàãðóçêà ïàíåëåé: - 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); -// Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì): - 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); + // check texture numbers for panels + if (panels <> nil) and (panels.count > 0) then + begin + for rec in panels do + begin + if (rec.tagInt < 0) then + begin + e_WriteLog('error loading map: invalid texture index for panel', MSG_FATALERROR); + result := false; + exit; + end; + end; + end; - for a := 0 to High(TriggersTable) do + // Ñîçäàíèå òàáëèöû òðèããåðîâ (ñîîòâåòñòâèå ïàíåëåé òðèããåðàì) + if (triggers <> nil) and (triggers.count > 0) then 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; + e_WriteLog(' Setting up trigger table...', MSG_NOTIFY); + //SetLength(TriggersTable, triggers.count); + g_Game_SetLoadingText(_lc[I_LOAD_TRIGGERS_TABLE], triggers.count-1, False); - g_Game_StepLoading(); - end; - end; + for rec in triggers do + begin + SetLength(TriggersTable, Length(TriggersTable)+1); + pttit := @TriggersTable[High(TriggersTable)]; + pttit.trigrec := rec; + pttit.texPan := mapReader.panel[rec.TexturePanel]; + pttit.liftPan := nil; + pttit.doorPan := nil; + pttit.shotPan := nil; + pttit.texPanIdx := -1; + pttit.LiftPanelIdx := -1; + pttit.DoorPanelIdx := -1; + pttit.ShotPanelIdx := -1; + // Ñìåíà òåêñòóðû (âîçìîæíî, êíîïêè) + //if (rec.TexturePanel >= 0) and (pttit.texPan = nil) then e_WriteLog('error loading map: invalid texture panel index for trigger', MSG_WARNING); + // Ëèôòû + if rec.TriggerType in [TRIGGER_LIFTUP, TRIGGER_LIFTDOWN, TRIGGER_LIFT] then + begin + //pttit.LiftPanel := TTriggerData(rec.DATA).PanelID + pttit.liftPan := mapReader.panel[rec.trigRec.tgPanelID]; + //if (rec.trigRec.trigPanelID >= 0) and (pttit.liftPan = nil) then e_WriteLog('error loading map: invalid lift panel index for trigger', MSG_WARNING); + end; + // Äâåðè + if rec.TriggerType in [TRIGGER_OPENDOOR, TRIGGER_CLOSEDOOR, TRIGGER_DOOR, TRIGGER_DOOR5, TRIGGER_CLOSETRAP, TRIGGER_TRAP] then + begin + //pttit.DoorPanel := TTriggerData(rec.DATA).PanelID + pttit.doorPan := mapReader.panel[rec.trigRec.tgPanelID]; + end; + // Òóðåëü + if (rec.TriggerType = TRIGGER_SHOT) then + begin + //pttit.ShotPanel := TTriggerData(rec.DATA).ShotPanelID + pttit.shotPan := mapReader.panel[rec.trigRec.tgShotPanelID]; + 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); + // update hashes + if (pttit.texPan <> nil) then hashTextPan.put(rec.TexturePanel, High(TriggersTable)); + if (pttit.liftPan <> nil) then hashLiftPan.put(rec.trigRec.tgPanelID, High(TriggersTable)); + if (pttit.doorPan <> nil) then hashDoorPan.put(rec.trigRec.tgPanelID, High(TriggersTable)); + if (pttit.shotPan <> nil) then hashShotPan.put(rec.trigRec.tgShotPanelID, High(TriggersTable)); - for a := 0 to High(panels) do + g_Game_StepLoading(); + end; + end; + + // Ñîçäàåì ïàíåëè + if (panels <> nil) and (panels.count > 0) 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 links...', MSG_NOTIFY); + g_Game_SetLoadingText(_lc[I_LOAD_LINK_TRIGGERS], panels.count-1, False); - if ok then + pannum := -1; + for rec in panels 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 + Inc(pannum); + texrec := nil; + SetLength(AddTextures, 0); + trigRef := False; + CurTex := -1; + ok := false; + + if (_textures <> nil) then + begin + texrec := rec.TextureRec; + ok := (texrec <> nil); + end; + + if ok then + begin + // Ñìîòðèì, ññûëàþòñÿ ëè íà ýòó ïàíåëü òðèããåðû. + // Åñëè äà - òî íàäî ñîçäàòü åùå òåêñòóð + ok := false; + if (TriggersTable <> nil) and (_textures <> nil) then + begin + for b := 0 to High(TriggersTable) do begin - trigRef := True; - ok := True; - Break; + if (TriggersTable[b].texPan = rec) or (TriggersTable[b].shotPan = rec) then + begin + trigRef := True; + ok := True; + break; + end; 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); + end; - // Åñëè ok, çíà÷èò åñòü öèôðû â êîíöå. - // Çàãðóæàåì òåêñòóðû ñ îñòàëüíûìè #: if ok then begin - k := NNF_NAME_BEFORE; - // Öèêë ïî èçìåíåíèþ èìåíè òåêñòóðû: - while ok or (k = NNF_NAME_BEFORE) or - (k = NNF_NAME_EQUALS) do + // Åñòü ññûëêè òðèããåðîâ íà ýòó ïàíåëü + s := texrec.Resource; + + // Ñïåö-òåêñòóðû çàïðåùåíû + if g_Map_IsSpecialTexture(s) then + begin + ok := false + end + else + begin + // Îïðåäåëÿåì íàëè÷èå è ïîëîæåíèå öèôð â êîíöå ñòðîêè + ok := g_Texture_NumNameFindStart(s); + end; + + // Åñëè 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 + 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 // Íà÷àëüíàÿ - îáû÷íàÿ, èùåì îáû÷íóþ + // Ïðîáóåì äîáàâèòü íîâóþ òåêñòóðó + if texrec.Anim then + begin + // Íà÷àëüíàÿ - àíèìèðîâàííàÿ, èùåì àíèìèðîâàííóþ + isAnim := True; + ok := CreateAnimTexture(TexName, FileName, False) >= 0; + if not ok then + begin + // Íåò àíèìèðîâàííîé, èùåì îáû÷íóþ isAnim := False; - ok := CreateTexture(TexName, FileName, False); - if not ok then - begin // Íåò îáû÷íîé, èùåì àíèìèðîâàííóþ - isAnim := True; - ok := CreateAnimTexture(TexName, FileName, False); - end; + 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; - // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè: + // Îíà ñóùåñòâóåò. Çàíîñèì åå ID â ñïèñîê ïàíåëè if ok then begin for c := 0 to High(Textures) do - if Textures[c].TextureName = TexName then + begin + if (Textures[c].TextureName = TexName) then begin SetLength(AddTextures, Length(AddTextures)+1); AddTextures[High(AddTextures)].Texture := c; AddTextures[High(AddTextures)].Anim := isAnim; - Break; + break; end; + end; end; end - else - if k = NNF_NAME_EQUALS then + else + begin + 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); + //AddTextures[High(AddTextures)].Texture := _texnummap[rec.TextureNum]; //panels[a].TextureNum; + AddTextures[High(AddTextures)].Texture := rec.tagInt; //panels[a].TextureNum; + AddTextures[High(AddTextures)].Anim := texrec.Anim; CurTex := High(AddTextures); - ok := True; + ok := true; end - else // NNF_NO_NAME - ok := False; - end; // while ok... + else // NNF_NO_NAME + begin + ok := false; + end; + end; + end; // while ok... - ok := True; - end; // if ok - åñòü ñìåæíûå òåêñòóðû - end; // if 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; + if not ok then + begin + // Çàíîñèì òîëüêî òåêóùóþ òåêñòóðó + SetLength(AddTextures, 1); + AddTextures[0].Texture := rec.tagInt; //panels[a].TextureNum; + AddTextures[0].Anim := false; + if (texrec <> nil) then AddTextures[0].Anim := texrec.Anim; + CurTex := 0; + end; + + //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(rec, AddTextures, CurTex, trigRef); + //e_LogWritefln('panel #%s of type %s got id #%s', [pannum, rec.PanelType, PanelID]); - // Ñîçäàåì ïàíåëü è çàïîìèíàåì åå íîìåð: - PanelID := CreatePanel(panels[a], AddTextures, CurTex, trigRef); + // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID + if hashTextPan.get(pannum, b) then TriggersTable[b].texPanIdx := PanelID; + if hashLiftPan.get(pannum, b) then TriggersTable[b].LiftPanelIdx := PanelID; + if hashDoorPan.get(pannum, b) then TriggersTable[b].DoorPanelIdx := PanelID; + if hashShotPan.get(pannum, b) then TriggersTable[b].ShotPanelIdx := PanelID; - // Åñëè èñïîëüçóåòñÿ â òðèããåðàõ, òî ñòàâèì òî÷íûé ID: - if TriggersTable <> nil then - for b := 0 to High(TriggersTable) do + (* + if (TriggersTable <> nil) 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; + for b := 0 to High(TriggersTable) do + begin + if (TriggersTable[b].texPan = rec) then TriggersTable[b].texPanIdx := PanelID; + if (TriggersTable[b].liftPan = rec) then TriggersTable[b].LiftPanelIdx := PanelID; + if (TriggersTable[b].doorPan = rec) then TriggersTable[b].DoorPanelIdx := PanelID; + if (TriggersTable[b].shotPan = rec) then TriggersTable[b].ShotPanelIdx := PanelID; + { + // Òðèããåð äâåðè/ëèôòà + if (TriggersTable[b].LiftPanel = pannum) or + (TriggersTable[b].DoorPanel = pannum) then + //TTriggerData(TriggersTable[b].trigrec.DATA).PanelID := PanelID; + TriggersTable[b].trigrec.trigRec.trigPanelID := PanelID; + // Òðèããåð ñìåíû òåêñòóðû + if TriggersTable[b].texPanIdx = pannum then + TriggersTable[b].trigrec.TexturePanel := PanelID; + // Òðèããåð "Òóðåëü" + if TriggersTable[b].ShotPanel = pannum then + TriggersTable[b].trigrec.trigRec.trigShotPanelID := PanelID; + } + end; end; + *) - g_Game_StepLoading(); + 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 + (* 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); + for b := 0 to High(TriggersTable) do + begin + // Òðèããåð äâåðè/ëèôòà + if (TriggersTable[b].texPan <> nil) then e_LogWritefln('trigger #%s: textPan=%s; panidx=%s', [b, TriggersTable[b].texPanIdx, mapReader.panelIndex[TriggersTable[b].texPan]]); + if (TriggersTable[b].liftPan <> nil) then e_LogWritefln('trigger #%s: liftPan=%s; panidx=%s', [b, TriggersTable[b].LiftPanelIdx, mapReader.panelIndex[TriggersTable[b].liftPan]]); + if (TriggersTable[b].doorPan <> nil) then e_LogWritefln('trigger #%s: doorPan=%s; panidx=%s', [b, TriggersTable[b].DoorPanelIdx, mapReader.panelIndex[TriggersTable[b].doorPan]]); + if (TriggersTable[b].shotPan <> nil) then e_LogWritefln('trigger #%s: shotPan=%s; panidx=%s', [b, TriggersTable[b].ShotPanelIdx, mapReader.panelIndex[TriggersTable[b].shotPan]]); + end; end; - end; + *) -// Çàãðóçêà ïðåäìåòîâ: - e_WriteLog(' Loading triggers...', MSG_NOTIFY); - g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False); - items := MapReader.GetItems(); + // create map grid, init other grids (for monsters, for example) + e_WriteLog('Creating map grid', MSG_NOTIFY); + mapCreateGrid(); -// Åñëè íå 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 (triggers <> nil) and (panels <> nil) and (not gLoadGameMode) then + begin + e_LogWritefln(' Creating triggers (%d)...', [triggers.count]); + g_Game_SetLoadingText(_lc[I_LOAD_CREATE_TRIGGERS], 0, False); + // Óêàçûâàåì òèï ïàíåëè, åñëè åñòü + trignum := -1; + for rec in triggers do + begin + Inc(trignum); + if (TriggersTable[trignum].texPan <> nil) then b := TriggersTable[trignum].texPan.PanelType else b := 0; + if (TriggersTable[trignum].shotPan <> nil) then c := TriggersTable[trignum].shotPan.PanelType else c := 0; + (* + if (rec.TexturePanel <> -1) then + begin + { + if (TriggersTable[trignum].TexturePanel < 0) or (TriggersTable[trignum].TexturePanel >= panels.count) then + begin + e_WriteLog('error loading map: invalid panel index for trigger', MSG_FATALERROR); + result := false; + exit; + end; + } + //b := panels[TriggersTable[a].TexturePanel].PanelType; + //b := mapReader.panel[TriggersTable[trignum].texPanIdx].PanelType; + assert(TriggersTable[trignum].texPanIdx >= 0); + b := TriggersTable[trignum].texPanIdx; + end + else + begin + b := 0; + end; + e_LogWritefln('trigger #%s: type=%s; texPanIdx=%s; b=%s', [trignum, rec.TriggerType, TriggersTable[trignum].texPanIdx, b]); + if (rec.TriggerType = TRIGGER_SHOT) then e_LogWritefln(' SHOT: shotpanidx=%s', [rec.trigRec.trigShotPanelID]); + if (rec.TriggerType = TRIGGER_SHOT) and {(rec.trigRec.trigShotPanelID <> -1)} (TriggersTable[trignum].shotPan <> nil) then + begin + //c := panels[TriggersTable[a].ShotPanel].PanelType; + //c := mapReader.panel[TriggersTable[trignum].ShotPanel].PanelType; + assert(TriggersTable[trignum].ShotPanelIdx >= 0); + c := TriggersTable[trignum].ShotPanelIdx; + end + else + begin + c := 0; + end; + *) + //e_LogWritefln('creating trigger #%s; texpantype=%s; shotpantype=%s (%d,%d)', [trignum, b, c, TriggersTable[trignum].texPanIdx, TriggersTable[trignum].ShotPanelIdx]); + CreateTrigger(rec, TriggersTable[trignum].texPanIdx, TriggersTable[trignum].ShotPanelIdx, Word(b), Word(c)); + end; + end; -// Çàãðóçêà îáëàñòåé: - e_WriteLog(' Loading areas...', MSG_NOTIFY); - g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False); - areas := MapReader.GetAreas(); + // Çàãðóçêà ïðåäìåòîâ + e_WriteLog(' Loading items...', MSG_NOTIFY); + g_Game_SetLoadingText(_lc[I_LOAD_ITEMS], 0, False); -// Åñëè íå 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 (items <> nil) and not gLoadGameMode then + begin + e_WriteLog(' Spawning items...', MSG_NOTIFY); + g_Game_SetLoadingText(_lc[I_LOAD_CREATE_ITEMS], 0, False); + for rec in items do CreateItem(rec); + end; -// Çàãðóçêà ìîíñòðîâ: - e_WriteLog(' Loading monsters...', MSG_NOTIFY); - g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False); - monsters := MapReader.GetMonsters(); + // Çàãðóçêà îáëàñòåé + e_WriteLog(' Loading areas...', MSG_NOTIFY); + g_Game_SetLoadingText(_lc[I_LOAD_AREAS], 0, False); - gTotalMonsters := 0; + // Åñëè íå LoadState, òî ñîçäàåì îáëàñòè + if areas <> nil then + begin + e_WriteLog(' Creating areas...', MSG_NOTIFY); + g_Game_SetLoadingText(_lc[I_LOAD_CREATE_AREAS], 0, False); + for rec in areas do CreateArea(rec); + 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(' Loading monsters...', MSG_NOTIFY); + g_Game_SetLoadingText(_lc[I_LOAD_MONSTERS], 0, False); -// Çàãðóçêà îïèñàíèÿ êàðòû: - e_WriteLog(' Reading map info...', MSG_NOTIFY); - g_Game_SetLoadingText(_lc[I_LOAD_MAP_HEADER], 0, False); - Header := MapReader.GetMapHeader(); + gTotalMonsters := 0; - MapReader.Free(); + // Åñëè íå 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 rec in monsters do CreateMonster(rec); + 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; + //MapReader.Free(); + gCurrentMap := mapReader; + mapReader := nil; -// Çàãðóçêà íåáà: - 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 - else - g_FatalError(Format(_lc[I_GAME_ERROR_MUSIC], [s])); - end; + 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(); + // Îñòàëüíûå óñòàíâêè + CreateDoorMap(); + CreateLiftMap(); - g_Items_Init(); - g_Weapon_Init(); - g_Monsters_Init(); + g_Items_Init(); + g_Weapon_Init(); + g_Monsters_Init(); -// Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé: - if not gLoadGameMode then - g_GFX_Init(); + // Åñëè íå LoadState, òî ñîçäàåì êàðòó ñòîëêíîâåíèé: + if not gLoadGameMode then g_GFX_Init(); -// Ñáðîñ ëîêàëüíûõ ìàññèâîâ: - _textures := nil; - panels := nil; - items := nil; - areas := nil; - triggers := nil; - TriggersTable := nil; - AddTextures := nil; + // Ñáðîñ ëîêàëüíûõ ìàññèâîâ: + _textures := nil; + panels := nil; + items := nil; + areas := nil; + triggers := nil; + TriggersTable := nil; + AddTextures := nil; -// Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà: - if ok and (not gLoadGameMode) then + // Âêëþ÷àåì ìóçûêó, åñëè ýòî íå çàãðóçêà: + if ok and (not gLoadGameMode) then begin gMusic.SetByName(gMapInfo.MusicName); gMusic.Play(); end - else - gMusic.SetByName(''); + else + begin + gMusic.SetByName(''); + end; + finally + sfsGCEnable(); // enable releasing unused volumes + mapReader.Free(); + hashTextPan.Free(); + hashLiftPan.Free(); + hashDoorPan.Free(); + hashShotPan.Free(); + end; e_WriteLog('Done loading map.', MSG_NOTIFY); Result := True; @@ -1225,24 +2096,25 @@ end; function g_Map_GetMapInfo(Res: String): TMapInfo; var - WAD: TWADEditor_1; - MapReader: TMapReader_1; - Header: TMapHeaderRec_1; - FileName, SectionName, ResName: String; + WAD: TWADFile; + MapReader: TDynRecord; + //Header: TMapHeaderRec_1; + 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; @@ -1250,8 +2122,8 @@ 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); @@ -1265,80 +2137,92 @@ begin Result.Name := Header.MapName; Result.Description := Header.MapDescription; end; + } + try + mapReader := g_Map_ParseMap(Data, Len); + except + mapReader := nil; + end; FreeMem(Data); - MapReader.Free(); + //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 + Result.Name := mapReader.MapName; + Result.Description := mapReader.MapDesc; + Result.Map := Res; + Result.Author := mapReader.MapAuthor; + Result.Height := mapReader.Height; + Result.Width := mapReader.Width; + end + else + 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]; + Result.Map := Res; + Result.Author := ''; + Result.Height := 0; + Result.Width := 0; + end; - Result.Map := Res; - Result.Author := Header.MapAuthor; - Result.Height := Header.Height; - Result.Width := Header.Width; + mapReader.Free(); 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) and (Pos('.pk3', 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; @@ -1391,7 +2275,7 @@ begin if Textures[a].Anim then g_Frames_DeleteByID(Textures[a].FramesID) else - if Textures[a].TextureID <> TEXTURE_NONE then + if Textures[a].TextureID <> LongWord(TEXTURE_NONE) then e_DeleteTexture(Textures[a].TextureID); Textures := nil; @@ -1427,6 +2311,8 @@ begin gDoorMap := nil; gLiftMap := nil; + + PanelByID := nil; end; procedure g_Map_Update(); @@ -1514,18 +2400,22 @@ begin end; end; -procedure g_Map_DrawPanels(PanelType: Word); - procedure DrawPanels(var panels: TPanelArray; - drawDoors: Boolean = False); - var - a: Integer; +// old algo +procedure g_Map_DrawPanels (PanelType: Word); + procedure DrawPanels (constref panels: TPanelArray; drawDoors: Boolean=False); + var + idx: Integer; begin - if panels <> nil then - for a := 0 to High(panels) do - if not (drawDoors xor panels[a].Door) then - panels[a].Draw(); + if (panels <> nil) 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 @@ -1541,6 +2431,38 @@ begin end; end; + +// new algo +procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer); + + function checker (pan: TPanel; tag: Integer): Boolean; + begin + result := false; // don't stop, ever + if ((tag and GridTagDoor) <> 0) <> pan.Door then exit; + gDrawPanelList.insert(pan); + end; + +begin + dplClear(); + //tagmask := panelTypeToTag(PanelType); + mapGrid.forEachInAABB(x0, y0, wdt, hgt, checker, GridDrawableMask); + // list will be rendered in `g_game.DrawPlayer()` +end; + + +procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer); + + function checker (pan: TPanel; tag: Integer): Boolean; + begin + result := false; // don't stop, ever + pan.DrawShadowVolume(lightX, lightY, radius); + end; + +begin + mapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor)); +end; + + procedure g_Map_DrawBack(dx, dy: Integer); begin if gDrawBackGround and (BackID <> DWORD(-1)) then @@ -1549,8 +2471,8 @@ begin e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0); end; -function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; - PanelType: Word; b1x3: Boolean): Boolean; +function g_Map_CollidePanelOld(X, Y: Integer; Width, Height: Word; + PanelType: Word; b1x3: Boolean=false): Boolean; var a, h: Integer; begin @@ -1669,75 +2591,209 @@ begin end; end; -function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD; +function g_Map_CollideLiquid_TextureOld(X, Y: Integer; Width, Height: Word): DWORD; var - a, h: Integer; -begin - Result := TEXTURE_NONE; + texid: DWORD; - if gWater <> nil then + function checkPanels (constref panels: TPanelArray): Boolean; + var + a: Integer; begin - h := High(gWater); - - for a := 0 to h do - if g_Collide(X, Y, Width, Height, - gWater[a].X, gWater[a].Y, - gWater[a].Width, gWater[a].Height) then + result := false; + if panels = nil then exit; + for a := 0 to High(panels) do + begin + if g_Collide(X, Y, Width, Height, panels[a].X, panels[a].Y, panels[a].Width, panels[a].Height) then begin - Result := gWater[a].GetTextureID(); - Exit; + result := true; + texid := panels[a].GetTextureID(); + exit; end; + end; end; - if gAcid1 <> nil then +begin + texid := LongWord(TEXTURE_NONE); + result := texid; + if not checkPanels(gWater) then + if not checkPanels(gAcid1) then + if not checkPanels(gAcid2) then exit; + result := texid; +end; + + +function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; b1x3: Boolean): Boolean; +const + SlowMask = GridTagLift or GridTagBlockMon; + function checker (pan: TPanel; tag: Integer): Boolean; begin - h := High(gAcid1); + { + if ((tag and (GridTagWall or GridTagDoor)) <> 0) then + begin + result := pan.Enabled; + exit; + end; + } - for a := 0 to h do - if g_Collide(X, Y, Width, Height, - gAcid1[a].X, gAcid1[a].Y, - gAcid1[a].Width, gAcid1[a].Height) then + 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 + g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height)}; + exit; + end; + + if ((tag and GridTagBlockMon) <> 0) then + begin + result := ((not b1x3) or (pan.Width+pan.Height >= 64)); //and g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height); + exit; + end; + + // other shit + //result := g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height); + result := true; // i found her! + end; + +var + tagmask: Integer = 0; +begin + if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor); + if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater; + if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1; + if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2; + if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep; + if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift; + if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon; + + if (tagmask = 0) then begin result := false; exit; end; // just in case + + if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('*solids'); + if gdbg_map_use_accel_coldet then + begin + if (Width = 1) and (Height = 1) then + begin + if ((tagmask and SlowMask) <> 0) then begin - Result := gAcid1[a].GetTextureID(); - Exit; + // slow + result := (mapGrid.forEachAtPoint(X, Y, checker, tagmask) <> nil); + end + else + begin + // fast + result := (mapGrid.forEachAtPoint(X, Y, nil, tagmask) <> nil); + end; + end + else + begin + if ((tagmask and SlowMask) <> 0) then + begin + // slow + result := (mapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask) <> nil); + end + else + begin + // fast + result := (mapGrid.forEachInAABB(X, Y, Width, Height, nil, tagmask) <> nil); end; + end; + end + else + begin + result := g_Map_CollidePanelOld(X, Y, Width, Height, PanelType, b1x3); end; + if (profMapCollision <> nil) then profMapCollision.sectionEnd(); +end; + + +function g_Map_CollideLiquid_Texture(X, Y: Integer; Width, Height: Word): DWORD; +var + cctype: Integer = 3; // priority: 0: water was hit, 1: acid1 was hit, 2: acid2 was hit; 3: nothing was hit + texid: DWORD; - if gAcid2 <> nil then + // slightly different from the old code, but meh... + function checker (pan: TPanel; tag: Integer): Boolean; begin - h := High(gAcid2); + result := false; // don't stop, ever + //if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2)) = 0) then exit; + // check priorities + case cctype of + 0: if ((tag and GridTagWater) = 0) then exit; // allowed: water + 1: if ((tag and (GridTagWater or GridTagAcid1)) = 0) then exit; // allowed: water, acid1 + //2: if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2) = 0) then exit; // allowed: water, acid1, acid2 + end; + // collision? + //if not g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height) then exit; + // yeah + texid := pan.GetTextureID(); + // water? water has the highest priority, so stop right here + if ((tag and GridTagWater) <> 0) then begin cctype := 0; result := true; exit; end; + // acid2? + if ((tag and GridTagAcid2) <> 0) then cctype := 2; + // acid1? + if ((tag and GridTagAcid1) <> 0) then cctype := 1; + end; - for a := 0 to h do - if g_Collide(X, Y, Width, Height, - gAcid2[a].X, gAcid2[a].Y, - gAcid2[a].Width, gAcid2[a].Height) then - begin - Result := gAcid2[a].GetTextureID(); - Exit; - end; +begin + if (profMapCollision <> nil) then profMapCollision.sectionBeginAccum('liquids'); + if gdbg_map_use_accel_coldet then + begin + texid := LongWord(TEXTURE_NONE); + if (Width = 1) and (Height = 1) then + begin + mapGrid.forEachAtPoint(X, Y, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2)); + end + else + begin + mapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2)); + end; + result := texid; + end + else + begin + result := g_Map_CollideLiquid_TextureOld(X, Y, Width, Height); end; + if (profMapCollision <> nil) then profMapCollision.sectionEnd(); end; + procedure g_Map_EnableWall(ID: DWORD); +var + pan: TPanel; begin - with gWalls[ID] do - begin - Enabled := True; - g_Mark(X, Y, Width, Height, MARK_DOOR, True); + pan := gWalls[ID]; + pan.Enabled := True; + g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, True); - if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID); - end; + mapGrid.proxyEnabled[pan.proxyId] := true; + //if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := true + //else pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, GridTagDoor); + + if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(gWalls[ID].PanelType, ID); + + {$IFDEF MAP_DEBUG_ENABLED_FLAG} + //e_WriteLog(Format('ENABLE: wall #%d(%d) enabled (%d) (%d,%d)-(%d,%d)', [Integer(ID), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.x, pan.y, pan.width, pan.height]), MSG_NOTIFY); + {$ENDIF} end; procedure g_Map_DisableWall(ID: DWORD); +var + pan: TPanel; begin - with gWalls[ID] do - begin - Enabled := False; - g_Mark(X, Y, Width, Height, MARK_DOOR, False); + pan := gWalls[ID]; + pan.Enabled := False; + g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, False); - if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID); - end; + mapGrid.proxyEnabled[pan.proxyId] := false; + //if (pan.proxyId >= 0) then begin mapGrid.removeBody(pan.proxyId); pan.proxyId := -1; end; + + if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(pan.PanelType, ID); + + {$IFDEF MAP_DEBUG_ENABLED_FLAG} + //e_WriteLog(Format('DISABLE: wall #%d(%d) disabled (%d) (%d,%d)-(%d,%d)', [Integer(ID), Integer(pan.proxyId), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.x, pan.y, pan.width, pan.height]), MSG_NOTIFY); + {$ENDIF} end; procedure g_Map_SwitchTexture(PanelType: Word; ID: DWORD; AnimLoop: Byte = 0); @@ -1778,6 +2834,7 @@ begin LiftType := t; g_Mark(X, Y, Width, Height, MARK_LIFT, False); + //TODO: make separate lift tags, and change tag here if LiftType = 0 then g_Mark(X, Y, Width, Height, MARK_LIFTUP, True) @@ -2031,6 +3088,7 @@ var PAMem.LoadFromMemory(Mem); for i := 0 to Length(panels)-1 do + begin if panels[i].SaveIt then begin // ID ïàíåëè: @@ -2041,7 +3099,10 @@ var end; // Çàãðóæàåì ïàíåëü: panels[i].LoadState(PAMem); + if (panels[i].arrIdx <> i) then raise Exception.Create('g_Map_LoadState: LoadPanelArray: Wrong Panel arrIdx'); + if (panels[i].proxyId >= 0) then mapGrid.proxyEnabled[panels[i].proxyId] := panels[i].Enabled; end; + end; // Ýòîò ñïèñîê ïàíåëåé çàãðóæåí: PAMem.Free(); @@ -2092,8 +3153,9 @@ begin LoadPanelArray(gLifts); ///// ///// -// Îáíîâëÿåì êàðòó ñòîëêíîâåíèé: +// Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó: g_GFX_Init(); + //mapCreateGrid(); ///// Çàãðóæàåì ìóçûêó: ///// // Ñèãíàòóðà ìóçûêè: @@ -2142,4 +3204,39 @@ 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; + + +// trace liquid, stepping by `dx` and `dy` +// return last seen liquid coords, and `false` if we're started outside of the liquid +function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean; +const + MaskLiquid = GridTagWater or GridTagAcid1 or GridTagAcid2; +begin + topx := x; + topy := y; + // started outside of the liquid? + if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then begin result := false; exit; end; + if (dx = 0) and (dy = 0) then begin result := false; exit; end; // sanity check + result := true; + while true do + begin + Inc(x, dx); + Inc(y, dy); + if (mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) = nil) then exit; // out of the water, just exit + topx := x; + topy := y; + end; +end; + + end.