X-Git-Url: https://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_map.pas;h=bd8acf8ef1904f4e322fb628b431814e42961064;hb=c414b87769c21e17010b8de0dfe36da681edcdbb;hp=e67e5359be1d51ac372100db16ff444515b6619a;hpb=9c2247dcfd066a8ffed3137433a909e862a65640;p=d2df-sdl.git diff --git a/src/game/g_map.pas b/src/game/g_map.pas index e67e535..bd8acf8 100644 --- a/src/game/g_map.pas +++ b/src/game/g_map.pas @@ -13,14 +13,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . *) -{$MODE DELPHI} +{$INCLUDE ../shared/a_modes.inc} unit g_map; interface uses e_graphics, g_basic, MAPSTRUCT, g_textures, Classes, - g_phys, wadreader, BinEditor, g_panel, md5; + g_phys, wadreader, BinEditor, g_panel, g_grid, z_aabbtree, md5, binheap, xprofiler; type TMapInfo = record @@ -62,15 +62,12 @@ function g_Map_Exist(Res: String): Boolean; procedure g_Map_Free(); procedure g_Map_Update(); -// build "potentially visible panels" set, so we can avoid looping over all level panels again and again -procedure g_Map_BuildPVP (minx, miny, maxx, maxy: Integer); -procedure g_Map_ResetPVP (); -// do not call this without calling `g_Map_BuildPVP()` or `g_Map_ResetPVP()` first! -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); @@ -92,6 +89,22 @@ 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 wall index in `gWalls` or -1 +function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): Boolean; + +type + TForEachPanelCB = function (pan: TPanel): Boolean; // return `true` to stop + +function g_Map_ForEachPanelAt (x, y: Integer; cb: TForEachPanelCB; panelType: Word): Boolean; + +function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean; + + +procedure g_Map_ProfilersBegin (); +procedure g_Map_ProfilersEnd (); + const RESPAWNPOINT_PLAYER1 = 1; RESPAWNPOINT_PLAYER2 = 2; @@ -115,6 +128,33 @@ 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; + + var gWalls: TPanelArray; gRenderBackgrounds: TPanelArray; @@ -133,6 +173,17 @@ var BackID: DWORD = DWORD(-1); gExternalResources: TStringList; + gdbg_map_use_accel_render: Boolean = true; + gdbg_map_use_accel_coldet: Boolean = true; + //gdbg_map_use_tree_draw: Boolean = false; + //gdbg_map_use_tree_coldet: Boolean = false; + //gdbg_map_dump_coldet_tree_queries: Boolean = false; + profMapCollision: TProfiler = nil; //WARNING: FOR DEBUGGING ONLY! + gDrawPanelList: TBinaryHeapObj = nil; // binary heap of all walls we have to render, populated by `g_Map_CollectDrawPanels()` + +function panelTypeToTag (panelType: Word): Integer; // returns GridTagXXX + + implementation uses @@ -149,6 +200,67 @@ const MUSIC_SIGNATURE = $4953554D; // 'MUSI' FLAG_SIGNATURE = $47414C46; // 'FLAG' + +type + TPanelGrid = specialize TBodyGridBase; + + { + TDynAABBTreePanelBase = specialize TDynAABBTreeBase; + + TDynAABBTreeMap = class(TDynAABBTreePanelBase) + function getFleshAABB (out aabb: AABB2D; pan: TPanel; tag: Integer): Boolean; override; + end; + { + +{ +function TDynAABBTreeMap.getFleshAABB (out aabb: AABB2D; pan: TPanel; tag: Integer): Boolean; +begin + result := false; + if (pan = nil) then begin aabb := AABB2D.Create(0, 0, 0, 0); exit; end; + aabb := AABB2D.Create(pan.X, pan.Y, pan.X+pan.Width-1, pan.Y+pan.Height-1); + if (pan.Width < 1) or (pan.Height < 1) then exit; + if not aabb.valid then raise Exception.Create('wutafuuuuuuu?!'); + result := true; +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 + //result := ((a as TPanel).ArrIdx < (b as TPanel).ArrIdx); + 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; @@ -161,6 +273,231 @@ var RespawnPoints: Array of TRespawnPoint; FlagPoints: Array [FLAG_RED..FLAG_BLUE] of PFlagPoint; //DOMFlagPoints: Array of TFlagPoint; + gMapGrid: TPanelGrid = nil; + //mapTree: TDynAABBTreeMap = nil; + + +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_traceToNearestWallOld (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): Integer; + + function sqchecker (pan: TPanel; var ray: Ray2D): Single; + var + aabb: AABB2D; + tmin: Single; + begin + result := -666.0; // invalid + if not pan.Enabled then exit; + aabb := AABB2D.CreateWH(pan.X, pan.Y, pan.Width, pan.Height); + if not aabb.valid then exit; + if aabb.intersects(ray, @tmin) then + begin + //if (tmin*tmin > maxDistSq) then exit; + if (tmin >= 0.0) then + begin + //e_WriteLog(Format('sqchecker(%d,%d,%d,%d): panel #%d (%d,%d)-(%d,%d); tmin=%f', [x0, y0, x1, y1, pan.arrIdx, pan.X, pan.Y, pan.Width, pan.Height, tmin]), MSG_NOTIFY); + //if (tmin < 0.0) then tmin := 0.0; + result := tmin; + end; + end; + end; + +var + qr: TDynAABBTreeMap.TSegmentQueryResult; + ray: Ray2D; + hxf, hyf: Single; + hx, hy: Integer; + maxDistSq: Single; +begin + result := -1; + if (mapTree = nil) then exit; + if mapTree.segmentQuery(qr, x0, y0, x1, y1, sqchecker, (GridTagWall or GridTagDoor)) then + begin + maxDistSq := (x1-x0)*(x1-x0)+(y1-y0)*(y1-y0); + if (qr.flesh <> nil) and (qr.time*qr.time <= maxDistSq) then + begin + result := qr.flesh.arrIdx; + if (hitx <> nil) or (hity <> nil) then + begin + ray := Ray2D.Create(x0, y0, x1, y1); + hxf := ray.origX+ray.dirX*qr.time; + hyf := ray.origY+ray.dirY*qr.time; + while true do + begin + hx := trunc(hxf); + hy := trunc(hyf); + if (hx >= qr.flesh.X) and (hy >= qr.flesh.Y) and (hx < qr.flesh.X+qr.flesh.Width) and (hy < qr.flesh.Y+qr.flesh.Height) then + begin + // go back a little + hxf -= ray.dirX; + hyf -= ray.dirY; + end + else + begin + break; + end; + end; + if (hitx <> nil) then hitx^ := hx; + if (hity <> nil) then hity^ := hy; + end; + end; + end; +end; +*) + + +// wall index in `gWalls` or -1 +function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): Boolean; +var + lastX, lastY, lastDist: Integer; + wasHit: Boolean = false; + + // pan=nil: before processing new tile + function sqchecker (pan: TPanel; tag: Integer; x, y, prevx, prevy: Integer): Boolean; + var + dist: Integer; + begin + if (pan = nil) then + begin + // stop if something was hit at the previous tile + result := wasHit; + end + else + begin + result := false; + if ((tag and (GridTagWall or GridTagDoor)) <> 0) then + begin + if not pan.Enabled then exit; + end; + dist := (prevx-x0)*(prevx-x0)+(prevy-y0)*(prevy-y0); + if (dist < lastDist) then + begin + wasHit := true; + lastDist := dist; + lastX := prevx; + lastY := prevy; + end; + end; + end; + +begin + result := false; + if (gMapGrid = nil) then exit; + lastDist := (x1-x0)*(x1-x0)+(y1-y0)*(y1-y0)+1; + lastX := 0; + lastY := 0; + result := gMapGrid.traceRay(x0, y0, x1, y1, sqchecker, (GridTagWall or GridTagDoor)); + if (hitx <> nil) then hitx^ := lastX; + if (hity <> nil) then hity^ := lastY; +end; + + +function g_Map_ForEachPanelAt (x, y: Integer; cb: TForEachPanelCB; panelType: Word): Boolean; + + function checker (pan: TPanel; tag: Integer): Boolean; + begin + result := false; // don't stop, ever + + if ((tag and (GridTagWall or GridTagDoor)) <> 0) then + begin + if not pan.Enabled then exit; + end; + + result := (x >= pan.X) and (y >= pan.Y) and (x < pan.X+pan.Width) and (y < pan.Y+pan.Height); + if not result then exit; + + 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))); + end; + + // other shit + if result then result := cb(pan); + end; + +var + tagmask: Integer = 0; +begin + result := false; + if not assigned(cb) then exit; + + 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 + result := gMapGrid.forEachInAABB(x, y, 1, 1, checker, tagmask); +end; + + +function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean; + + function checker (pan: TPanel; tag: Integer): Boolean; + begin + result := false; // don't stop, ever + + if ((tag and (GridTagWall or GridTagDoor)) <> 0) then + begin + if not pan.Enabled then exit; + end; + + result := (x >= pan.X) and (y >= pan.Y) and (x < pan.X+pan.Width) and (y < pan.Y+pan.Height); + if not result then exit; + + 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))); + end; + 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 + result := gMapGrid.forEachAtPoint(x, y, checker, tagmask); +end; function g_Map_IsSpecialTexture(Texture: String): Boolean; @@ -370,8 +707,9 @@ 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].tag := panelTypeToTag(PanelRec.PanelType); if sav then panels^[len].SaveIt := True; @@ -816,45 +1154,52 @@ end; procedure CreateMonster(monster: TMonsterRec_1); 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) = 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) = 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; @@ -900,6 +1245,84 @@ begin addResToExternalResList(mapHeader.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; tag: Integer); + var + idx: Integer; + pan: TPanel; + begin + tag := panelTypeToTag(tag); + for idx := High(panels) downto 0 do + begin + pan := panels[idx]; + pan.tag := tag; + if not pan.visvalid then continue; + gMapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, tag); + //mapTree.insertObject(pan, tag, true); // as static object + end; + end; + +begin + gMapGrid.Free(); + gMapGrid := nil; + //mapTree.Free(); + //mapTree := nil; + + calcBoundingBox(gWalls); + calcBoundingBox(gRenderBackgrounds); + calcBoundingBox(gRenderForegrounds); + calcBoundingBox(gWater); + calcBoundingBox(gAcid1); + calcBoundingBox(gAcid2); + calcBoundingBox(gSteps); + calcBoundingBox(gLifts); + calcBoundingBox(gBlockMon); + + e_WriteLog(Format('map dimensions: (%d,%d)-(%d,%d)', [mapX0, mapY0, mapX1, mapY1]), MSG_WARNING); + + gMapGrid := TPanelGrid.Create(mapX0, mapY0, mapX1-mapX0+1, mapY1-mapY0+1); + //mapTree := TDynAABBTreeMap.Create(); + + addPanelsToGrid(gWalls, PANEL_WALL); + addPanelsToGrid(gWalls, PANEL_CLOSEDOOR); + addPanelsToGrid(gWalls, PANEL_OPENDOOR); + addPanelsToGrid(gRenderBackgrounds, PANEL_BACK); + addPanelsToGrid(gRenderForegrounds, PANEL_FORE); + addPanelsToGrid(gWater, PANEL_WATER); + addPanelsToGrid(gAcid1, PANEL_ACID1); + addPanelsToGrid(gAcid2, PANEL_ACID2); + addPanelsToGrid(gSteps, PANEL_STEP); + addPanelsToGrid(gLifts, PANEL_LIFTUP); // it doesn't matter which LIFT type is used here + addPanelsToGrid(gBlockMon, PANEL_BLOCKMON); + + gMapGrid.dumpStats(); + //e_WriteLog(Format('tree depth: %d; %d nodes used, %d nodes allocated', [mapTree.computeTreeHeight, mapTree.nodeCount, mapTree.nodeAlloced]), MSG_NOTIFY); + //mapTree.forEachLeaf(nil); +end; + function g_Map_Load(Res: String): Boolean; const DefaultMusRes = 'Standart.wad:STDMUS\MUS1'; @@ -930,7 +1353,13 @@ var Len: Integer; ok, isAnim, trigRef: Boolean; CurTex, ntn: Integer; + begin + gMapGrid.Free(); + gMapGrid := nil; + //mapTree.Free(); + //mapTree := nil; + Result := False; gMapInfo.Map := Res; TriggersTable := nil; @@ -1397,6 +1826,9 @@ begin sfsGCEnable(); // enable releasing unused volumes end; + e_WriteLog('Creating map grid', MSG_NOTIFY); + mapCreateGrid(); + e_WriteLog('Done loading map.', MSG_NOTIFY); Result := True; end; @@ -1681,117 +2113,81 @@ begin end; -var - pvpset: array of PPanel = nil; // potentially lit panels - pvpb, pvpe: array [0..7] of Integer; // start/end (inclusive) of the correspoinding type panels in pvpset - pvpcount: Integer = -1; // to avoid constant reallocations +// old algo +procedure g_Map_DrawPanels (PanelType: Word); -function pvpType (panelType: Word): Integer; -begin - case panelType of - PANEL_WALL, PANEL_CLOSEDOOR: result := 0; // gWalls - PANEL_BACK: result := 1; // gRenderBackgrounds - PANEL_FORE: result := 2; // gRenderForegrounds - PANEL_WATER: result := 3; // gWater - PANEL_ACID1: result := 4; // gAcid1 - PANEL_ACID2: result := 5; // gAcid2 - PANEL_STEP: result := 6; // gSteps - else result := -1; + procedure DrawPanels (constref panels: TPanelArray; drawDoors: Boolean=False); + var + idx: Integer; + begin + 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; -end; -procedure g_Map_ResetPVP (); begin - pvpcount := -1; // special + case PanelType of + PANEL_WALL: DrawPanels(gWalls); + PANEL_CLOSEDOOR: DrawPanels(gWalls, True); + PANEL_BACK: DrawPanels(gRenderBackgrounds); + PANEL_FORE: DrawPanels(gRenderForegrounds); + PANEL_WATER: DrawPanels(gWater); + PANEL_ACID1: DrawPanels(gAcid1); + PANEL_ACID2: DrawPanels(gAcid2); + PANEL_STEP: DrawPanels(gSteps); + end; end; -procedure g_Map_BuildPVP (minx, miny, maxx, maxy: Integer); -var - idx: Integer; - tpc: Integer; - procedure checkPanels (var panels: TPanelArray; stp: Integer); - var - idx, x, y, w, h: Integer; +// new algo +procedure g_Map_CollectDrawPanels (x0, y0, wdt, hgt: Integer); + function checker (pan: TPanel; tag: Integer): Boolean; begin - if panels = nil then exit; - tpc := tpc+Length(panels); - if (stp < 0) or (stp > 6) then exit; - pvpb[stp] := pvpcount; - for idx := 0 to High(panels) do - begin - w := panels[idx].Width; - h := panels[idx].Height; - if (w < 1) or (h < 1) then continue; - x := panels[idx].X; - y := panels[idx].Y; - if (x > maxx) or (y > maxy) then continue; - if (x+w <= minx) or (y+h <= miny) then continue; - if pvpcount = length(pvpset) then SetLength(pvpset, pvpcount+32768); - pvpset[pvpcount] := @panels[idx]; - Inc(pvpcount); - end; - pvpe[stp] := pvpcount-1; + result := false; // don't stop, ever + if ((tag and GridTagDoor) <> 0) <> pan.Door then exit; + gDrawPanelList.insert(pan); end; begin - //e_WriteLog(Format('visible rect: (%d,%d)-(%d,%d)', [minx, miny, maxx, maxy]), MSG_NOTIFY); - pvpcount := 0; - for idx := 0 to High(pvpb) do begin pvpb[idx] := 0; pvpe[idx] := -1; end; - tpc := 0; - checkPanels(gWalls, 0); - checkPanels(gRenderBackgrounds, 1); - checkPanels(gRenderForegrounds, 2); - checkPanels(gWater, 3); - checkPanels(gAcid1, 4); - checkPanels(gAcid2, 5); - checkPanels(gSteps, 6); - //e_WriteLog(Format('total panels: %d; visible panels: %d', [tpc, pvpcount]), MSG_NOTIFY); + dplClear(); + //tagmask := panelTypeToTag(PanelType); + + {if gdbg_map_use_tree_draw then + begin + mapTree.aabbQuery(x0, y0, wdt, hgt, checker, (GridTagBack or GridTagStep or GridTagWall or GridTagDoor or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagFore)); + end + else} + begin + gMapGrid.forEachInAABB(x0, y0, wdt, hgt, checker, (GridTagBack or GridTagStep or GridTagWall or GridTagDoor or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagFore)); + end; + // list will be rendered in `g_game.DrawPlayer()` end; -procedure g_Map_DrawPanels(PanelType: Word); - procedure DrawPanels (stp: Integer; var panels: TPanelArray; drawDoors: Boolean=False); - var - idx: Integer; +procedure g_Map_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer); + function checker (pan: TPanel; tag: Integer): Boolean; begin - if (panels <> nil) and (stp >= 0) and (stp <= 6) then - begin - if pvpcount < 0 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 - else - begin - // wow, use visible set - if pvpb[stp] <= pvpe[stp] then - begin - for idx := pvpb[stp] to pvpe[stp] do - begin - if not (drawDoors xor pvpset[idx].Door) then pvpset[idx].Draw(); - end; - end; - end; - end; + result := false; // don't stop, ever + pan.DrawShadowVolume(lightX, lightY, radius); end; begin - case PanelType of - PANEL_WALL: DrawPanels(0, gWalls); - PANEL_CLOSEDOOR: DrawPanels(0, gWalls, True); - PANEL_BACK: DrawPanels(1, gRenderBackgrounds); - PANEL_FORE: DrawPanels(2, gRenderForegrounds); - PANEL_WATER: DrawPanels(3, gWater); - PANEL_ACID1: DrawPanels(4, gAcid1); - PANEL_ACID2: DrawPanels(5, gAcid2); - PANEL_STEP: DrawPanels(6, gSteps); + {if gdbg_map_use_tree_draw then + begin + mapTree.aabbQuery(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor)); + end + else} + begin + gMapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor)); end; end; + procedure g_Map_DrawBack(dx, dy: Integer); begin if gDrawBackGround and (BackID <> DWORD(-1)) then @@ -1800,8 +2196,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 @@ -1920,53 +2316,155 @@ 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 := 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; + function checker (pan: TPanel; tag: Integer): Boolean; begin - h := High(gAcid1); + result := false; // don't stop, ever - 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 (GridTagWall or GridTagDoor)) <> 0) then + begin + if not pan.Enabled then exit; + end; + + 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); + 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 gdbg_map_use_tree_coldet then + begin + //e_WriteLog(Format('coldet query: x=%d; y=%d; w=%d; h=%d', [X, Y, Width, Height]), MSG_NOTIFY); + result := (mapTree.aabbQuery(X, Y, Width, Height, checker, tagmask) <> nil); + if (gdbg_map_dump_coldet_tree_queries) and (mapTree.nodesVisited <> 0) then begin - Result := gAcid1[a].GetTextureID(); - Exit; + //e_WriteLog(Format('map collision: %d nodes visited (%d deep)', [mapTree.nodesVisited, mapTree.nodesDeepVisited]), MSG_NOTIFY); + g_Console_Add(Format('map collision: %d nodes visited (%d deep)', [mapTree.nodesVisited, mapTree.nodesDeepVisited])); end; + end + else} + begin + result := gMapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask); + 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 := TEXTURE_NONE; + {if gdbg_map_use_tree_coldet then + begin + mapTree.aabbQuery(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2)); + end + else} + begin + gMapGrid.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); @@ -1974,7 +2472,7 @@ begin with gWalls[ID] do begin Enabled := True; - g_Mark(X, Y, Width, Height, MARK_DOOR, True); + //g_Mark(X, Y, Width, Height, MARK_DOOR, True); if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID); end; @@ -1985,7 +2483,7 @@ begin with gWalls[ID] do begin Enabled := False; - g_Mark(X, Y, Width, Height, MARK_DOOR, False); + //g_Mark(X, Y, Width, Height, MARK_DOOR, False); if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID); end; @@ -2028,6 +2526,7 @@ begin begin LiftType := t; + { g_Mark(X, Y, Width, Height, MARK_LIFT, False); if LiftType = 0 then @@ -2038,6 +2537,7 @@ begin g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True) else if LiftType = 3 then g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True); + } if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID); end; @@ -2343,8 +2843,9 @@ begin LoadPanelArray(gLifts); ///// ///// -// Îáíîâëÿåì êàðòó ñòîëêíîâåíèé: +// Îáíîâëÿåì êàðòó ñòîëêíîâåíèé è ñåòêó: g_GFX_Init(); + mapCreateGrid(); ///// Çàãðóæàåì ìóçûêó: ///// // Ñèãíàòóðà ìóçûêè: