DEADSOFTWARE

more grid code uglification -- should be a little faster now
authorKetmar Dark <ketmar@ketmar.no-ip.org>
Tue, 22 Aug 2017 12:12:24 +0000 (15:12 +0300)
committerKetmar Dark <ketmar@ketmar.no-ip.org>
Wed, 23 Aug 2017 18:23:55 +0000 (21:23 +0300)
  uglified grid code even more: each proxy now has "enabled" flag,
  and traces without callbacks (most "did collide with anything?"
  traces aren't interested in exact collision info) will return
  simple true/false flag, so we can avoid expensive callback
  invocation.

  not working yet, tho: i fucked up flag checking somewhere

src/game/g_game.pas
src/game/g_gfx.pas
src/game/g_grid.pas
src/game/g_map.pas
src/game/g_panel.pas

index 38fb35a5f5b8792903142ade3cfcfcd5e6df3fe3..6cd9970a59315a03cf65315942ce166ce1c6eb63 100644 (file)
@@ -2730,7 +2730,7 @@ type
       while (gDrawPanelList.count > 0) do
       begin
         pan := TPanel(gDrawPanelList.front());
-        e_WriteLog(Format('tagmask: 0x%04x; pan.tag: 0x%04x; pan.ArrIdx: %d', [tagmask, pan.tag, pan.ArrIdx]), MSG_NOTIFY);
+        e_WriteLog(Format('tagmask: 0x%04x; pan.tag: 0x%04x; pan.arrIdx: %d', [tagmask, pan.tag, pan.arrIdx]), MSG_NOTIFY);
         pan.Draw();
         gDrawPanelList.popFront();
       end;
index 7afc4ee0c50267988a295d9e199c456eeb5d75cc..c84ba47eda80a1e2c6097a134a78d5ba0929a212 100644 (file)
@@ -83,6 +83,8 @@ type
     State:              Byte;
     ParticleType:       Byte;
     offsetX, offsetY:   ShortInt;
+    // for bubbles
+    liquidTopY: Integer; // don't float higher than this
   end;
 
   TOnceAnim = record
@@ -713,12 +715,13 @@ begin
   end;
 end;
 
+
 procedure g_GFX_Bubbles(fX, fY: Integer; Count: Word; DevX, DevY: Byte);
 var
   a: Integer;
   DevX1, DevX2,
   DevY1, DevY2: Byte;
-  l: Integer;
+  l, liquidx: Integer;
 begin
   l := Length(Particles);
   if l = 0 then
@@ -742,8 +745,15 @@ begin
          (Y >= gMapInfo.Height) or (Y <= 0) then
         Continue;
 
+      (*
+      // don't spawn bubbles outside of the liquid
       if not isLiquidAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIQUID)} then
         Continue;
+      *)
+
+      // trace liquid, so we'll know where it ends; do it in 8px steps for speed
+      // tracer will return `false` if we started outside of the liquid
+      if not g_Map_TraceLiquid(X, Y, 0, -8, liquidx, liquidTopY) then continue;
 
       VelX := 0;
       VelY := -1-Random;
index d56a24365ec397c8c071b1bfa7f648709e8ae473..13c57c98500101653d346c82599259cb5fa1d120 100644 (file)
@@ -15,7 +15,6 @@
  *)
 // universal spatial grid
 {$INCLUDE ../shared/a_modes.inc}
-{$DEFINE grid_use_buckets}
 unit g_grid;
 
 interface
@@ -29,12 +28,12 @@ type
     type TGridQueryCB = function (obj: ITP; tag: Integer): Boolean is nested; // return `true` to stop
     type TGridRayQueryCB = function (obj: ITP; tag: Integer; x, y, prevx, prevy: Integer): Boolean is nested; // return `true` to stop
 
+    const TagDisabled = $40000000;
+
   private
     const
       GridDefaultTileSize = 32;
-      {$IFDEF grid_use_buckets}
       GridCellBucketSize = 8; // WARNING! can't be less than 2!
-      {$ENDIF}
 
   private
     type
@@ -53,11 +52,7 @@ type
 
       PGridCell = ^TGridCell;
       TGridCell = record
-        {$IFDEF grid_use_buckets}
         bodies: array [0..GridCellBucketSize-1] of Integer; // -1: end of list
-        {$ELSE}
-        body: Integer;
-        {$ENDIF}
         next: Integer; // in this cell; index in mCells
       end;
 
@@ -96,6 +91,9 @@ type
     function inserter (grida: Integer): Boolean;
     function remover (grida: Integer): Boolean;
 
+    function getProxyEnabled (pid: TBodyProxyId): Boolean; inline;
+    procedure setProxyEnabled (pid: TBodyProxyId; val: Boolean); inline;
+
   public
     constructor Create (aMinPixX, aMinPixY, aPixWidth, aPixHeight: Integer; aTileSize: Integer=GridDefaultTileSize);
     destructor Destroy (); override;
@@ -107,17 +105,26 @@ type
     procedure resizeBody (body: TBodyProxyId; sx, sy: Integer);
     procedure moveResizeBody (body: TBodyProxyId; dx, dy, sx, sy: Integer);
 
+    function insideGrid (x, y: Integer): Boolean; inline;
+
     //WARNING: can't do recursive queries
+    // no callback: return `true` on the first hit
     function forEachInAABB (x, y, w, h: Integer; cb: TGridQueryCB; tagmask: Integer=-1): Boolean;
 
     //WARNING: can't do recursive queries
+    // no callback: return `true` on the first hit
     function forEachAtPoint (x, y: Integer; cb: TGridQueryCB; tagmask: Integer=-1): Boolean;
 
     //WARNING: can't do recursive queries
     // cb with `(nil)` will be called before processing new tile
+    // no callback: return `true` on the nearest hit
     function traceRay (x0, y0, x1, y1: Integer; cb: TGridRayQueryCB; tagmask: Integer=-1): Boolean; overload;
+    function traceRay (out ex, ey: Integer; x0, y0, x1, y1: Integer; cb: TGridRayQueryCB; tagmask: Integer=-1): Boolean;
 
     procedure dumpStats ();
+
+    //WARNING! no sanity checks!
+    property proxyEnabled[pid: TBodyProxyId]: Boolean read getProxyEnabled write setProxyEnabled;
   end;
 
 
@@ -162,11 +169,7 @@ begin
   // init free list
   for idx := 0 to High(mCells) do
   begin
-    {$IFDEF grid_use_buckets}
     mCells[idx].bodies[0] := -1;
-    {$ELSE}
-    mCells[idx].body := -1;
-    {$ENDIF}
     mCells[idx].next := idx+1;
   end;
   mCells[High(mCells)].next := -1; // last cell
@@ -216,6 +219,37 @@ begin
 end;
 
 
+function TBodyGridBase.insideGrid (x, y: Integer): Boolean; inline;
+begin
+  // fix coords
+  Dec(x, mMinX);
+  Dec(y, mMinY);
+  result := (x >= 0) and (y >= 0) and (x < mWidth*mTileSize) and (y < mHeight*mTileSize);
+end;
+
+
+function TBodyGridBase.getProxyEnabled (pid: TBodyProxyId): Boolean; inline;
+begin
+  if (pid >= 0) then result := ((mProxies[pid].mTag and TagDisabled) = 0) else result := false;
+end;
+
+
+procedure TBodyGridBase.setProxyEnabled (pid: TBodyProxyId; val: Boolean); inline;
+begin
+  if (pid >= 0) then
+  begin
+    if val then
+    begin
+      mProxies[pid].mTag := mProxies[pid].mTag and not TagDisabled;
+    end
+    else
+    begin
+      mProxies[pid].mTag := mProxies[pid].mTag or TagDisabled
+    end;
+  end;
+end;
+
+
 function TBodyGridBase.allocCell: Integer;
 var
   idx: Integer;
@@ -227,11 +261,7 @@ begin
     SetLength(mCells, mFreeCell+32768); // arbitrary number
     for idx := mFreeCell to High(mCells) do
     begin
-      {$IFDEF grid_use_buckets}
       mCells[idx].bodies[0] := -1;
-      {$ELSE}
-      mCells[idx].body := -1;
-      {$ENDIF}
       mCells[idx].next := idx+1;
     end;
     mCells[High(mCells)].next := -1; // last cell
@@ -239,11 +269,7 @@ begin
   result := mFreeCell;
   mFreeCell := mCells[result].next;
   mCells[result].next := -1;
-  {$IFDEF grid_use_buckets}
   mCells[result].bodies[0] := -1;
-  {$ELSE}
-  mCells[result].body := -1;
-  {$ENDIF}
   Inc(mUsedCells);
   //e_WriteLog(Format('grid: allocated new cell #%d (total: %d)', [result, mUsedCells]), MSG_NOTIFY);
 end;
@@ -254,11 +280,7 @@ begin
   if (idx >= 0) and (idx < Length(mCells)) then
   begin
     //if mCells[idx].body = -1 then exit; // the thing that should not be
-    {$IFDEF grid_use_buckets}
     mCells[idx].bodies[0] := -1;
-    {$ELSE}
-    mCells[idx].body := -1;
-    {$ENDIF}
     mCells[idx].next := mFreeCell;
     mFreeCell := idx;
     Dec(mUsedCells);
@@ -339,15 +361,12 @@ function TBodyGridBase.inserter (grida: Integer): Boolean;
 var
   cidx: Integer;
   pc: Integer;
-  {$IFDEF grid_use_buckets}
   pi: PGridCell;
   f: Integer;
-  {$ENDIF}
 begin
   result := false; // never stop
   // add body to the given grid cell
   pc := mGrid[grida];
-  {$IFDEF grid_use_buckets}
   if (pc <> -1) then
   begin
     pi := @mCells[pc];
@@ -369,13 +388,6 @@ begin
   mCells[cidx].bodies[1] := -1;
   mCells[cidx].next := pc;
   mGrid[grida] := cidx;
-  {$ELSE}
-  cidx := allocCell();
-  //e_WriteLog(Format('  01: allocated cell for grid coords (%d,%d), body coords:(%d,%d): #%d', [gx, gy, dx, dy, cidx]), MSG_NOTIFY);
-  mCells[cidx].body := mUData;
-  mCells[cidx].next := pc;
-  mGrid[grida] := cidx;
-  {$ENDIF}
 end;
 
 
@@ -392,13 +404,9 @@ end;
 
 function TBodyGridBase.remover (grida: Integer): Boolean;
 var
-  {$IFDEF grid_use_buckets}
   f: Integer;
-  {$ENDIF}
   pidx, idx, tmp: Integer;
-  {$IFDEF grid_use_buckets}
   pc: PGridCell;
-  {$ENDIF}
 begin
   result := false; // never stop
   // find and remove cell
@@ -407,7 +415,6 @@ begin
   while (idx >= 0) do
   begin
     tmp := mCells[idx].next;
-    {$IFDEF grid_use_buckets}
     pc := @mCells[idx];
     f := 0;
     while (f < High(TGridCell.bodies)) do
@@ -438,14 +445,6 @@ begin
       end;
       Inc(f);
     end;
-    {$ELSE}
-    if (mCells[idx].body = mUData) then
-    begin
-      if (pidx = -1) then mGrid[grida] := tmp else mCells[pidx].next := tmp;
-      freeCell(idx);
-      exit; // assume that we cannot have one object added to bucket twice
-    end;
-    {$ENDIF}
     pidx := idx;
     idx := tmp;
   end;
@@ -506,18 +505,18 @@ end;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
+// no callback: return `true` on the first hit
 function TBodyGridBase.forEachAtPoint (x, y: Integer; cb: TGridQueryCB; tagmask: Integer=-1): Boolean;
 var
-  {$IFDEF grid_use_buckets}
   f: Integer;
-  {$ENDIF}
   idx, curci: Integer;
   cc: PGridCell = nil;
   px: PBodyProxyRec;
   lq: LongWord;
+  ptag: Integer;
 begin
   result := false;
-  if not assigned(cb) or (tagmask = 0) then exit;
+  if (tagmask = 0) then exit;
 
   // make coords (0,0)-based
   Dec(x, mMinX);
@@ -542,57 +541,45 @@ begin
   while (curci <> -1) do
   begin
     cc := @mCells[curci];
-    {$IFDEF grid_use_buckets}
     for f := 0 to High(TGridCell.bodies) do
     begin
       if (cc.bodies[f] = -1) then break;
       px := @mProxies[cc.bodies[f]];
-      if (px.mQueryMark <> lq) and ((px.mTag and tagmask) <> 0) then
+      if (px.mQueryMark <> lq) then
       begin
-        if (x >= px.mX) and (y >= px.mY) and (x < px.mX+px.mWidth) and (y < px.mY+px.mHeight) then
+        ptag := px.mTag;
+        if ((ptag and TagDisabled) = 0) and ((px.mTag and tagmask) <> 0) then
         begin
-          px.mQueryMark := lq;
-          result := cb(px.mObj, px.mTag);
-          if result then exit;
-        end;
-      end;
-    end;
-    {$ELSE}
-    if (cc.body <> -1) then
-    begin
-      px := @mProxies[cc.body];
-      if (px.mQueryMark <> lq) and ((px.mTag and tagmask) <> 0) then
-      begin
-        if (x >= px.mX) and (y >= px.mY) and (x < px.mX+px.mWidth) and (y < px.mY+px.mHeight) then
-        begin
-          px.mQueryMark := lq;
-          result := cb(px.mObj, px.mTag);
-          if result then exit;
+          if (x >= px.mX) and (y >= px.mY) and (x < px.mX+px.mWidth) and (y < px.mY+px.mHeight) then
+          begin
+            px.mQueryMark := lq;
+            if assigned(cb) then result := cb(px.mObj, px.mTag) else result := true;
+            if result then exit;
+          end;
         end;
       end;
     end;
-    {$ENDIF}
     curci := cc.next;
   end;
 end;
 
 
+// no callback: return `true` on the first hit
 function TBodyGridBase.forEachInAABB (x, y, w, h: Integer; cb: TGridQueryCB; tagmask: Integer=-1): Boolean;
 var
   idx: Integer;
   gx, gy: Integer;
   curci: Integer;
-  {$IFDEF grid_use_buckets}
   f: Integer;
-  {$ENDIF}
   cc: PGridCell = nil;
   px: PBodyProxyRec;
   lq: LongWord;
   tsize, gw: Integer;
   x0, y0: Integer;
+  ptag: Integer;
 begin
   result := false;
-  if (w < 1) or (h < 1) or not assigned(cb) then exit;
+  if (w < 1) or (h < 1) then exit;
 
   x0 := x;
   y0 := y;
@@ -632,39 +619,23 @@ begin
       while (curci <> -1) do
       begin
         cc := @mCells[curci];
-        {$IFDEF grid_use_buckets}
         for f := 0 to High(TGridCell.bodies) do
         begin
           if (cc.bodies[f] = -1) then break;
           px := @mProxies[cc.bodies[f]];
-          if (px.mQueryMark <> lq) and ((px.mTag and tagmask) <> 0) then
-          begin
-            if (x0 >= px.mX+px.mWidth) or (y0 >= px.mY+px.mHeight) then continue;
-            if (x0+w <= px.mX) or (y0+h <= px.mY) then continue;
-            px.mQueryMark := lq;
-            result := cb(px.mObj, px.mTag);
-            if result then exit;
-          end;
-        end;
-        {$ELSE}
-        if (cc.body <> 1) then
-        begin
-          px := @mProxies[cc.body];
-          if (px.mQueryMark <> lq) and ((px.mTag and tagmask) <> 0) then
+          if (px.mQueryMark <> lq) then
           begin
-            if (x0 >= px.mX+px.mWidth) or (y0 >= px.mY+px.mHeight) or (x0+w <= px.mX) or (y0+h <= px.mY) then
-            begin
-              // no intersection
-            end
-            else
+            ptag := px.mTag;
+            if ((ptag and TagDisabled) = 0) and ((px.mTag and tagmask) <> 0) then
             begin
+              if (x0 >= px.mX+px.mWidth) or (y0 >= px.mY+px.mHeight) then continue;
+              if (x0+w <= px.mX) or (y0+h <= px.mY) then continue;
               px.mQueryMark := lq;
-              result := cb(px.mObj, px.mTag);
+              if assigned(cb) then result := cb(px.mObj, px.mTag) else result := true;
               if result then exit;
             end;
           end;
         end;
-        {$ENDIF}
         curci := cc.next;
       end;
     end;
@@ -673,7 +644,17 @@ end;
 
 
 // ////////////////////////////////////////////////////////////////////////// //
+// no callback: return `true` on the nearest hit
 function TBodyGridBase.traceRay (x0, y0, x1, y1: Integer; cb: TGridRayQueryCB; tagmask: Integer=-1): Boolean;
+var
+  ex, ey: Integer;
+begin
+  result := traceRay(ex, ey, x0, y0, x1, y1, cb, tagmask);
+end;
+
+
+// no callback: return `true` on the nearest hit
+function TBodyGridBase.traceRay (out ex, ey: Integer; x0, y0, x1, y1: Integer; cb: TGridRayQueryCB; tagmask: Integer=-1): Boolean;
 var
   i: Integer;
   dx, dy: Integer;
@@ -696,10 +677,13 @@ var
   lq: LongWord;
   prevX, prevY: Integer;
   minx, miny: Integer;
+  ptag: Integer;
+  lastDistSq, distSq: Integer;
+  wasHit: Boolean = false;
 begin
   result := false;
 
-  if (tagmask = 0) then exit;
+  if (tagmask = 0) then begin ex := x0; ey := y0; exit; end;
 
   // make coords (0,0)-based
   minx := mMinX;
@@ -738,6 +722,7 @@ begin
   gh := mHeight;
   maxx := gw*tsize-1;
   maxy := gh*tsize-1;
+  lastDistSq := (x1-x0)*(x1-x0)+(y1-y0)*(y1-y0)+1;
 
   for i := 1 to d do
   begin
@@ -754,13 +739,6 @@ begin
         // new cell
         lastGA := ga;
         ccidx := mGrid[lastGA];
-        {
-        if (ccidx <> -1) then
-        begin
-          result := cb(nil, 0, x+minx, y+miny, prevX, prevY);
-          if result then exit;
-        end;
-        }
       end;
     end
     else
@@ -768,7 +746,7 @@ begin
       if (ccidx <> -1) then
       begin
         ccidx := -1;
-        result := cb(nil, 0, x+minx, y+miny, prevX, prevY);
+        if assigned(cb) then result := cb(nil, 0, x+minx, y+miny, prevX, prevY) else result := wasHit;
         if result then exit;
       end;
     end;
@@ -782,52 +760,50 @@ begin
       while (curci <> -1) do
       begin
         cc := @mCells[curci];
-        {$IFDEF grid_use_buckets}
         for f := 0 to High(TGridCell.bodies) do
         begin
           if (cc.bodies[f] = -1) then break;
           px := @mProxies[cc.bodies[f]];
-          if (px.mQueryMark <> lq) and ((px.mTag and tagmask) <> 0) then
+          if (px.mQueryMark <> lq) then
           begin
-            if (x >= px.mX) and (y >= px.mY) and (x < px.mX+px.mWidth) and (y < px.mY+px.mHeight) then
+            ptag := px.mTag;
+            if ((ptag and TagDisabled) = 0) and ((px.mTag and tagmask) <> 0) then
             begin
-              px.mQueryMark := lq;
-              result := cb(px.mObj, px.mTag, x, y, prevX, prevY);
-              if result then exit;
-            end
-            else
-            begin
-              hasUntried := true;
+              if (x >= px.mX) and (y >= px.mY) and (x < px.mX+px.mWidth) and (y < px.mY+px.mHeight) then
+              begin
+                px.mQueryMark := lq;
+                if assigned(cb) then
+                begin
+                  result := cb(px.mObj, px.mTag, x, y, prevX, prevY);
+                  if result then begin ex := prevX; ey := prevY; exit; end;
+                end
+                else
+                begin
+                  distSq := (prevX-x)*(prevX-x)+(prevY-y)*(prevY-y);
+                  if (distSq < lastDistSq) then
+                  begin
+                    wasHit := true;
+                    lastDistSq := distSq;
+                    ex := prevx;
+                    ey := prevy;
+                  end;
+                end;
+              end
+              else
+              begin
+                hasUntried := true;
+              end;
             end;
           end;
         end;
-        {$ELSE}
-        if (cc.body <> -1) then
-        begin
-          px := @mProxies[cc.body];
-          if (px.mQueryMark <> lq) and ((px.mTag and tagmask) <> 0) then
-          begin
-            if (x >= px.mX) and (y >= px.mY) and (x < px.mX+px.mWidth) and (y < px.mY+px.mHeight) then
-            begin
-              px.mQueryMark := lq;
-              result := cb(px.mObj, px.mTag, x, y, prevX, prevY);
-              if result then exit;
-            end
-            else
-            begin
-              hasUntried := true;
-            end;
-          end;
-        end;
-        {$ENDIF}
         curci := cc.next;
       end;
       if not hasUntried then
       begin
         // don't process this cell anymore
         ccidx := -1;
-        result := cb(nil, 0, x, y, prevX, prevY);
-        if result then exit;
+        if assigned(cb) then result := cb(nil, 0, x, y, prevX, prevY) else result := wasHit;
+        if result then exit; // don't update lasthit: it is done in real checker
       end;
       Dec(x, minx);
       Dec(y, miny);
index dba67ec98f1d18122f0f420a48e1db41a3cb18fe..d1d824a9396883a9f2a5e3f2038a6bd7cf5dff4c 100644 (file)
@@ -99,10 +99,15 @@ type
 
 function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean;
 
+// 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_TraceLiquid (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
+
 
 procedure g_Map_ProfilersBegin ();
 procedure g_Map_ProfilersEnd ();
 
+
 const
   RESPAWNPOINT_PLAYER1 = 1;
   RESPAWNPOINT_PLAYER2 = 2;
@@ -245,12 +250,11 @@ 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);
+  result := (pa.arrIdx < pb.arrIdx);
 end;
 
 procedure dplClear ();
@@ -271,7 +275,7 @@ var
   RespawnPoints: Array of TRespawnPoint;
   FlagPoints:    Array [FLAG_RED..FLAG_BLUE] of PFlagPoint;
   //DOMFlagPoints: Array of TFlagPoint;
-  gMapGrid: TPanelGrid = nil;
+  mapGrid: TPanelGrid = nil;
   //mapTree: TDynAABBTreeMap = nil;
 
 
@@ -366,6 +370,7 @@ 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, lastDistSq: Integer;
   wasHit: Boolean = false;
@@ -397,16 +402,21 @@ var
       end;
     end;
   end;
-
+*)
+var
+  ex, ey: Integer;
 begin
+  (*
   result := false;
-  if (gMapGrid = nil) then exit;
+  if (mapGrid = nil) then exit;
   lastDistSq := (x1-x0)*(x1-x0)+(y1-y0)*(y1-y0)+1;
   lastX := 0;
   lastY := 0;
-  result := gMapGrid.traceRay(x0, y0, x1, y1, sqchecker, (GridTagWall or GridTagDoor));
+  result := mapGrid.traceRay(x0, y0, x1, y1, sqchecker, (GridTagWall or GridTagDoor));
   if (hitx <> nil) then hitx^ := lastX;
   if (hity <> nil) then hity^ := lastY;
+  *)
+  result := mapGrid.traceRay(ex, ey, x0, y0, x1, y1, nil, (GridTagWall or GridTagDoor));
 end;
 
 
@@ -414,24 +424,26 @@ 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;
+      result := pan.Enabled; // stop if wall is enabled
+      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
+      // 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
@@ -448,7 +460,16 @@ begin
   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);
+  if ((tagmask and GridTagLift) <> 0) then
+  begin
+    // slow
+    result := mapGrid.forEachAtPoint(x, y, checker, tagmask);
+  end
+  else
+  begin
+    // fast
+    result := mapGrid.forEachAtPoint(x, y, nil, tagmask);
+  end;
 end;
 
 
@@ -660,7 +681,8 @@ begin
   SetLength(panels^, len + 1);
 
   panels^[len] := TPanel.Create(PanelRec, AddTextures, CurTex, Textures);
-  panels^[len].ArrIdx := len;
+  panels^[len].arrIdx := len;
+  panels^[len].proxyId := -1;
   panels^[len].tag := panelTypeToTag(PanelRec.PanelType);
   if sav then
     panels^[len].SaveIt := True;
@@ -1232,14 +1254,15 @@ var
       pan := panels[idx];
       pan.tag := tag;
       if not pan.visvalid then continue;
-      gMapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, tag);
+      pan.proxyId := mapGrid.insertBody(pan, pan.X, pan.Y, pan.Width, pan.Height, tag);
+      mapGrid.proxyEnabled[pan.proxyId] := pan.Enabled;
       //mapTree.insertObject(pan, tag, true); // as static object
     end;
   end;
 
 begin
-  gMapGrid.Free();
-  gMapGrid := nil;
+  mapGrid.Free();
+  mapGrid := nil;
   //mapTree.Free();
   //mapTree := nil;
 
@@ -1255,7 +1278,7 @@ begin
 
   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);
+  mapGrid := TPanelGrid.Create(mapX0-512, mapY0-512, mapX1-mapX0+1+512*2, mapY1-mapY0+1+512*2);
   //mapTree := TDynAABBTreeMap.Create();
 
   addPanelsToGrid(gWalls, PANEL_WALL);
@@ -1270,7 +1293,7 @@ begin
   addPanelsToGrid(gLifts, PANEL_LIFTUP); // it doesn't matter which LIFT type is used here
   addPanelsToGrid(gBlockMon, PANEL_BLOCKMON);
 
-  gMapGrid.dumpStats();
+  mapGrid.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;
@@ -1307,8 +1330,8 @@ var
   CurTex, ntn: Integer;
 
 begin
-  gMapGrid.Free();
-  gMapGrid := nil;
+  mapGrid.Free();
+  mapGrid := nil;
   //mapTree.Free();
   //mapTree := nil;
 
@@ -2098,6 +2121,7 @@ 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
@@ -2115,13 +2139,14 @@ begin
   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));
+    mapGrid.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_DrawPanelShadowVolumes(lightX: Integer; lightY: Integer; radius: Integer);
+
   function checker (pan: TPanel; tag: Integer): Boolean;
   begin
     result := false; // don't stop, ever
@@ -2135,7 +2160,7 @@ begin
   end
   else}
   begin
-    gMapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
+    mapGrid.forEachInAABB(lightX-radius, lightY-radius, radius*2, radius*2, checker, (GridTagWall or GridTagDoor));
   end;
 end;
 
@@ -2304,10 +2329,13 @@ function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word;
   begin
     result := false; // don't stop, ever
 
+    {
     if ((tag and (GridTagWall or GridTagDoor)) <> 0) then
     begin
-      if not pan.Enabled then exit;
+      result := pan.Enabled;
+      exit;
     end;
+    }
 
     if ((tag and GridTagLift) <> 0) then
     begin
@@ -2322,7 +2350,7 @@ function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word;
 
     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);
+      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;
 
@@ -2361,11 +2389,29 @@ begin
     begin
       if (Width = 1) and (Height = 1) then
       begin
-        result := gMapGrid.forEachAtPoint(X, Y, checker, tagmask);
+        if ((tagmask and (GridTagLift or GridTagBlockMon)) <> 0) then
+        begin
+          // slow
+          result := mapGrid.forEachAtPoint(X, Y, checker, tagmask);
+        end
+        else
+        begin
+          // fast
+          result := mapGrid.forEachAtPoint(X, Y, nil, tagmask);
+        end;
       end
       else
       begin
-        result := gMapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask);
+        if ((tagmask and (GridTagLift or GridTagBlockMon)) <> 0) then
+        begin
+          // slow
+          result := mapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask);
+        end
+        else
+        begin
+          // fast
+          result := mapGrid.forEachInAABB(X, Y, Width, Height, nil, tagmask);
+        end;
       end;
     end;
   end
@@ -2418,11 +2464,11 @@ begin
     begin
       if (Width = 1) and (Height = 1) then
       begin
-        gMapGrid.forEachAtPoint(X, Y, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
+        mapGrid.forEachAtPoint(X, Y, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
       end
       else
       begin
-        gMapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
+        mapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2));
       end;
     end;
     result := texid;
@@ -2434,12 +2480,14 @@ begin
   if (profMapCollision <> nil) then profMapCollision.sectionEnd();
 end;
 
+
 procedure g_Map_EnableWall(ID: DWORD);
 begin
   with gWalls[ID] do
   begin
     Enabled := True;
     g_Mark(X, Y, Width, Height, MARK_DOOR, True);
+    mapGrid.proxyEnabled[proxyId] := true;
 
     if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
   end;
@@ -2451,6 +2499,7 @@ begin
   begin
     Enabled := False;
     g_Mark(X, Y, Width, Height, MARK_DOOR, False);
+    mapGrid.proxyEnabled[proxyId] := false;
 
     if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID);
   end;
@@ -2494,6 +2543,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)
@@ -2870,4 +2920,28 @@ begin
   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_TraceLiquid (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 not mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) 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 not mapGrid.forEachAtPoint(x, y, nil, MaskLiquid) then exit; // out of the water, just exit
+    topx := x;
+    topy := y;
+  end;
+end;
+
+
 end.
index ab996040406168f29ced2f9c131f576d080b86fd..19a6af815b51077098b1fec363f77bfabdf4370e 100644 (file)
@@ -59,8 +59,9 @@ type
     Moved:            Boolean;
     LiftType:         Byte;
     LastAnimLoop:     Byte;
-    ArrIdx:           Integer; // index in one of internal arrays; sorry
+    arrIdx:           Integer; // index in one of internal arrays; sorry
     tag:              Integer; // used in coldets and such; sorry
+    proxyId:          Integer; // proxy id in map grid (DO NOT USE!)
 
     constructor Create(PanelRec: TPanelRec_1;
                        AddTextures: TAddTextureArray;