DEADSOFTWARE

more cosmetix
[d2df-sdl.git] / src / game / g_grid.pas
index b4b0e32795cddfaa38864fa8154c2f260e903328..06790755698dfce7e15d73241fdabd65b4cbe689 100644 (file)
@@ -71,6 +71,7 @@ type
         property height: Integer read mHeight;
         property tag: Integer read getTag write setTag;
         property enabled: Boolean read getEnabled write setEnabled;
+        property obj: ITP read mObj;
       end;
 
   private
@@ -81,20 +82,36 @@ type
         next: Integer; // in this cell; index in mCells
       end;
 
+      TCellArray = array of TGridCell;
+
       TGridInternalCB = function (grida: Integer; bodyId: TBodyProxyId): Boolean of object; // return `true` to stop
 
   private
     //mTileSize: Integer;
     const mTileSize = GridDefaultTileSize;
+    type TGetProxyFn = function (pxidx: Integer): PBodyProxyRec of object;
 
   public
     const tileSize = mTileSize;
 
+    type
+      TAtPointEnumerator = record
+      private
+        mCells: TCellArray;
+        curidx, curbki: Integer;
+        getpx: TGetProxyFn;
+      public
+        constructor Create (acells: TCellArray; aidx: Integer; agetpx: TGetProxyFn);
+        function MoveNext (): Boolean; inline;
+        function getCurrent (): PBodyProxyRec; inline;
+        property Current: PBodyProxyRec read getCurrent;
+      end;
+
   private
     mMinX, mMinY: Integer; // so grids can start at any origin
     mWidth, mHeight: Integer; // in tiles
     mGrid: array of Integer; // mWidth*mHeight, index in mCells
-    mCells: array of TGridCell; // cell pool
+    mCells: TCellArray; // cell pool
     mFreeCell: Integer; // first free cell index or -1
     mLastQuery: LongWord;
     mUsedCells: Integer;
@@ -102,6 +119,7 @@ type
     mProxyFree: TBodyProxyId; // free
     mProxyCount: Integer; // currently used
     mProxyMaxCount: Integer;
+    mInQuery: Boolean;
 
   public
     dbgShowTraceLog: Boolean;
@@ -160,6 +178,8 @@ type
     // no callback: return object on the first hit or nil
     function forEachAtPoint (x, y: Integer; cb: TGridQueryCB; tagmask: Integer=-1; exittag: PInteger=nil): ITP;
 
+    function atCellInPoint (x, y: Integer): TAtPointEnumerator;
+
     //WARNING: don't modify grid while any query is in progress (no checks are made!)
     //         you can set enabled/disabled flag, tho (but iterator can still return objects disabled inside it)
     // cb with `(nil)` will be called before processing new tile
@@ -419,6 +439,40 @@ begin
 end;
 
 
+// ////////////////////////////////////////////////////////////////////////// //
+constructor TBodyGridBase.TAtPointEnumerator.Create (acells: TCellArray; aidx: Integer; agetpx: TGetProxyFn);
+begin
+  mCells := acells;
+  curidx := aidx;
+  curbki := -1;
+  getpx := agetpx;
+end;
+
+
+function TBodyGridBase.TAtPointEnumerator.MoveNext (): Boolean; inline;
+begin
+  while (curidx <> -1) do
+  begin
+    while (curbki < GridCellBucketSize) do
+    begin
+      Inc(curbki);
+      if (mCells[curidx].bodies[curbki] = -1) then break;
+      result := true;
+      exit;
+    end;
+    curidx := mCells[curidx].next;
+    curbki := -1;
+  end;
+  result := false;
+end;
+
+
+function TBodyGridBase.TAtPointEnumerator.getCurrent (): PBodyProxyRec; inline;
+begin
+  result := getpx(mCells[curidx].bodies[curbki]);
+end;
+
+
 // ////////////////////////////////////////////////////////////////////////// //
 constructor TBodyGridBase.Create (aMinPixX, aMinPixY, aPixWidth, aPixHeight: Integer{; aTileSize: Integer=GridDefaultTileSize});
 var
@@ -1107,6 +1161,18 @@ begin
 end;
 
 
+// ////////////////////////////////////////////////////////////////////////// //
+function TBodyGridBase.atCellInPoint (x, y: Integer): TAtPointEnumerator;
+var
+  cidx: Integer = -1;
+begin
+  Dec(x, mMinX);
+  Dec(y, mMinY);
+  if (x >= 0) and (y >= 0) and (x < mWidth*mTileSize) and (y < mHeight*mTileSize) then cidx := mGrid[(y div mTileSize)*mWidth+(x div mTileSize)];
+  result := TAtPointEnumerator.Create(mCells, cidx, getProxyById);
+end;
+
+
 // ////////////////////////////////////////////////////////////////////////// //
 // no callback: return `true` on the first hit
 function TBodyGridBase.forEachAtPoint (x, y: Integer; cb: TGridQueryCB; tagmask: Integer=-1; exittag: PInteger=nil): ITP;
@@ -1235,6 +1301,9 @@ begin
   if (x+w <= 0) or (y+h <= 0) then exit;
   if (x >= gw*tsize) or (y >= mHeight*tsize) then exit;
 
+  if mInQuery then raise Exception.Create('recursive queries aren''t supported');
+  mInQuery := true;
+
   // increase query counter
   Inc(mLastQuery);
   if (mLastQuery = 0) then
@@ -1274,11 +1343,12 @@ begin
           if (x0+w <= px.mX) or (y0+h <= px.mY) then continue;
           if assigned(cb) then
           begin
-            if cb(px.mObj, ptag) then begin result := px.mObj; exit; end;
+            if cb(px.mObj, ptag) then begin result := px.mObj; mInQuery := false; exit; end;
           end
           else
           begin
             result := px.mObj;
+            mInQuery := false;
             exit;
           end;
         end;
@@ -1286,6 +1356,8 @@ begin
       end;
     end;
   end;
+
+  mInQuery := false;
 end;
 
 
@@ -1552,6 +1624,9 @@ begin
 
   //if (dbgShowTraceLog) then e_WriteLog(Format('raycast start: (%d,%d)-(%d,%d); xptr^=%d; yptr^=%d', [ax0, ay0, ax1, ay1, xptr^, yptr^]), MSG_NOTIFY);
 
+  if mInQuery then raise Exception.Create('recursive queries aren''t supported');
+  mInQuery := true;
+
   // increase query counter
   Inc(mLastQuery);
   if (mLastQuery = 0) then
@@ -1581,16 +1656,6 @@ begin
     y := yptr^+miny;
     //prevx := x;
     //prevy := y;
-    {$IF DEFINED(D2F_DEBUG)}
-    if hopt then
-    begin
-      if (y <> ay0) then raise Exception.Create('htrace fatal internal error');
-    end
-    else
-    begin
-      if (x <> ax0) then raise Exception.Create('vtrace fatal internal error');
-    end;
-    {$ENDIF}
     while (wklen > 0) do
     begin
       {$IF DEFINED(D2F_DEBUG)}
@@ -1629,6 +1694,7 @@ begin
                     result := px.mObj;
                     ex := x;
                     ey := y;
+                    mInQuery := false;
                     exit;
                   end;
                 end
@@ -1643,6 +1709,7 @@ begin
                     ex := x;
                     ey := y;
                     result := px.mObj;
+                    mInQuery := false;
                     exit;
                   end;
                 end;
@@ -1697,6 +1764,7 @@ begin
                   result := px.mObj;
                   ex := prevx;
                   ey := prevy;
+                  mInQuery := false;
                   exit;
                 end;
               end
@@ -1720,8 +1788,8 @@ begin
           // next cell
           ccidx := cc.next;
         end;
-        if wasHit and not assigned(cb) then begin result := lastObj; exit; end;
-        if assigned(cb) and cb(nil, 0, x, y, x, y) then begin result := lastObj; exit; end;
+        if wasHit and not assigned(cb) then begin result := lastObj; mInQuery := false; exit; end;
+        if assigned(cb) and cb(nil, 0, x, y, x, y) then begin result := lastObj; mInQuery := false; exit; end;
       end;
       // skip to next tile
       if hopt then
@@ -1778,6 +1846,7 @@ begin
     end;
     // we can travel less than one cell
     if wasHit and not assigned(cb) then result := lastObj else begin ex := ax1; ey := ay1; end;
+    mInQuery := false;
     exit;
   end;
   {$ENDIF}
@@ -1811,11 +1880,12 @@ begin
         // signal cell completion
         if assigned(cb) then
         begin
-          if cb(nil, 0, xptr^+minx, yptr^+miny, prevx, prevy) then begin result := lastObj; exit; end;
+          if cb(nil, 0, xptr^+minx, yptr^+miny, prevx, prevy) then begin result := lastObj; mInQuery := false; exit; end;
         end
         else if wasHit then
         begin
           result := lastObj;
+          mInQuery := false;
           exit;
         end;
       end;
@@ -1853,6 +1923,7 @@ begin
                   result := px.mObj;
                   ex := prevx;
                   ey := prevy;
+                  mInQuery := false;
                   exit;
                 end;
               end
@@ -1890,11 +1961,12 @@ begin
         ccidx := -1;
         if assigned(cb) then
         begin
-          if cb(nil, 0, x, y, prevx, prevy) then begin result := lastObj; exit; end;
+          if cb(nil, 0, x, y, prevx, prevy) then begin result := lastObj; mInQuery := false; exit; end;
         end
         else if wasHit then
         begin
           result := lastObj;
+          mInQuery := false;
           exit;
         end;
       end;
@@ -1933,6 +2005,8 @@ begin
     ex := ax1; // why not?
     ey := ay1; // why not?
   end;
+
+  mInQuery := false;
 end;
 
 
@@ -1955,7 +2029,7 @@ var
   temp: Integer;
   ccidx, curci: Integer;
   lastGA: Integer = -1;
-  ga, x, y: Integer;
+  ga: Integer;
   gw, gh, minx, miny, maxx, maxy: Integer;
   cc: PGridCell;
   px: PBodyProxyRec;
@@ -2143,6 +2217,9 @@ begin
   //lastGA := (yptr^ div tsize)*gw+(xptr^ div tsize);
   //ccidx := mGrid[lastGA];
 
+  if mInQuery then raise Exception.Create('recursive queries aren''t supported');
+  mInQuery := true;
+
   // increase query counter
   Inc(mLastQuery);
   if (mLastQuery = 0) then
@@ -2167,23 +2244,10 @@ begin
     if dbgShowTraceLog then e_LogWritefln('optimized htrace; wklen=%d', [wklen]);
     {$ENDIF}
     ga := (yptr^ div tsize)*gw+(xptr^ div tsize);
-    // one of those will never change
-    x := xptr^+minx;
-    y := yptr^+miny;
-    {$IF DEFINED(D2F_DEBUG)}
-    if hopt then
-    begin
-      if (y <> ay0) then raise Exception.Create('htrace fatal internal error');
-    end
-    else
-    begin
-      if (x <> ax0) then raise Exception.Create('vtrace fatal internal error');
-    end;
-    {$ENDIF}
     while (wklen > 0) do
     begin
       {$IF DEFINED(D2F_DEBUG)}
-      if dbgShowTraceLog then e_LogWritefln('  htrace; ga=%d; x=%d, y=%d; y=%d; y=%d', [ga, xptr^+minx, yptr^+miny, y, ay0]);
+      if dbgShowTraceLog then e_LogWritefln('  htrace; ga=%d; x=%d, y=%d; ay0=%d', [ga, xptr^+minx, yptr^+miny, ay0]);
       {$ENDIF}
       // new tile?
       if (ga <> lastGA) then
@@ -2191,7 +2255,6 @@ begin
         lastGA := ga;
         ccidx := mGrid[lastGA];
         // convert coords to map (to avoid ajdusting coords inside the loop)
-        if hopt then x := xptr^+minx else y := yptr^+miny;
         while (ccidx <> -1) do
         begin
           cc := @mCells[ccidx];
@@ -2205,11 +2268,12 @@ begin
               px.mQueryMark := lq; // mark as processed
               if assigned(cb) then
               begin
-                if cb(px.mObj, ptag) then begin result := px.mObj; exit; end;
+                if cb(px.mObj, ptag) then begin result := px.mObj; mInQuery := false; exit; end;
               end
               else
               begin
                 result := px.mObj;
+                mInQuery := false;
                 exit;
               end;
             end;
@@ -2271,6 +2335,7 @@ begin
       end;
       Dec(wklen, wkstep);
     end;
+    mInQuery := false;
     exit;
   end;
   {$ENDIF}
@@ -2306,9 +2371,6 @@ begin
     begin
       // process cell
       curci := ccidx;
-      // convert coords to map (to avoid ajdusting coords inside the loop)
-      x := xptr^+minx;
-      y := yptr^+miny;
       // process cell list
       while (curci <> -1) do
       begin
@@ -2323,11 +2385,12 @@ begin
             px.mQueryMark := lq; // mark as processed
             if assigned(cb) then
             begin
-              if cb(px.mObj, ptag) then begin result := px.mObj; exit; end;
+              if cb(px.mObj, ptag) then begin result := px.mObj; mInQuery := false; exit; end;
             end
             else
             begin
               result := px.mObj;
+              mInQuery := false;
               exit;
             end;
           end;
@@ -2357,6 +2420,8 @@ begin
     if (e >= 0) then begin yd += sty; e -= dx2; end else e += dy2;
     xd += stx;
   end;
+
+  mInQuery := false;
 end;