DEADSOFTWARE

ambient light for level (doesn't work with dynamic lights; I. WANT. SHADERS!)
[d2df-sdl.git] / src / game / g_grid.pas
index 55feca9a094f1ac89568f79b79e78f5ab2e72d93..280fd90f3fd967f0753a31ee69cb6386ceb5fd05 100644 (file)
@@ -142,9 +142,6 @@ type
     function allocProxy (aX, aY, aWidth, aHeight: Integer; aObj: ITP; aTag: Integer): TBodyProxyId;
     procedure freeProxy (body: TBodyProxyId);
 
-    procedure insertInternal (body: TBodyProxyId);
-    procedure removeInternal (body: TBodyProxyId);
-
     function forGridRect (x, y, w, h: Integer; cb: TGridInternalCB; bodyId: TBodyProxyId): Boolean;
 
     function inserter (grida: Integer; bodyId: TBodyProxyId): Boolean;
@@ -327,6 +324,7 @@ uses
 
 // ////////////////////////////////////////////////////////////////////////// //
 procedure swapInt (var a: Integer; var b: Integer); inline; var t: Integer; begin t := a; a := b; b := t; end;
+//procedure swapInt (var a: Integer; var b: Integer); inline; begin a := a xor b; b := b xor a; a := a xor b; end;
 //function minInt (a, b: Integer): Integer; inline; begin if (a < b) then result := a else result := b; end;
 //function maxInt (a, b: Integer): Integer; inline; begin if (a > b) then result := a else result := b; end;
 
@@ -357,69 +355,100 @@ begin
   result := (xd = term);
 end;
 
-// move to next tile; return `true` if the line is complete (and walker state is undefined then)
 function TLineWalker.stepToNextTile (): Boolean; inline;
 var
-  ex, ey, wklen, f: Integer;
+  ex, ey: Integer;
+  xwalk, ywalk, wklen: Integer; // to the respective edges
+  lstx, lsty, lterm: Integer;
+  le, ldx2, ldy2: Integer;
+  lxd, lyd: Integer;
+  f: Integer;
 begin
   result := false;
 
-  //writeln('stx=', stx, '; sty=', sty);
+  lstx := stx;
+  lsty := sty;
+  lterm := term;
+  lxd := xd;
 
   // ortho?
-  if (sty = 0) then
+  if (lsty = 0) then
   begin
     // only xd
-    assert(sty <> 0);
-    if (stx < 0) then
+    //assert(lsty <> 0);
+    if (lstx < 0) then
     begin
       // xd: to left edge
-      xd := (xd and (not (TileSize-1)))-1;
-      result := (xd <= term);
+      xd := (lxd and (not (TileSize-1)))-1;
+      result := (lxd <= lterm);
       exit;
     end
     else
     begin
       // xd: to right edge
-      xd := (xd or (TileSize-1))+1;
-      result := (xd >= term);
+      xd := (lxd or (TileSize-1))+1;
+      result := (lxd >= lterm);
       exit;
     end;
   end;
 
-  assert(stx <> 0); // invariant
-
   // not ortho
-  if (sty < 0) then ey := (yd and (not (TileSize-1)))-1 else ey := (yd or (TileSize-1))+1;
+  //assert(lstx <> 0); // invariant
+
+  lyd := yd;
+  le := e;
+  ldx2 := dx2;
+  ldy2 := dy2;
 
-  //FIXME: do some math to avoid single-stepping
-  if (stx < 0) then
+  // calculate xwalk
+  if (lstx < 0) then
   begin
-    // xd: to left edge
-    ex := (xd and (not (TileSize-1)))-1;
-    if (ex <= term) then begin result := true; exit; end;
-    wklen := xd-ex;
+    ex := (lxd and (not (TileSize-1)))-1;
+    xwalk := lxd-ex;
   end
   else
   begin
-    // xd: to right edge
-    ex := (xd or (TileSize-1))+1;
-    if (ex >= term) then begin result := true; exit; end;
-    wklen := ex-xd;
+    ex := (lxd or (TileSize-1))+1;
+    xwalk := ex-lxd;
   end;
 
-  //writeln('xd=', xd, '; yd=', yd, '; ex=', ex, '; ey=', ey, '; term=', term, '; wklen=', wklen);
-
-  for f := wklen downto 1 do
+  // calculate ywalk
+  if (lsty < 0) then
   begin
-    // do step
-    if (e >= 0) then begin yd += sty; e -= dx2; end else e += dy2;
-    xd += stx;
-    if (xd = term) then begin result := true; exit; end;
-    if (xd = ex) or (yd = ey) then exit; // done
+    ey := (lyd and (not (TileSize-1)))-1;
+    ywalk := lyd-ey;
+  end
+  else
+  begin
+    ey := (lyd or (TileSize-1))+1;
+    ywalk := ey-lyd;
   end;
 
-  raise Exception.Create('TLineWalker.stepToNextTile: the thing that should not be!');
+  while true do
+  begin
+    // in which dir we want to walk?
+    if (xwalk <= ywalk) then wklen := xwalk else wklen := ywalk;
+    // walk x
+    if (lstx < 0) then
+    begin
+      lxd -= wklen;
+      if (lxd <= lterm) then begin xd := lxd; result := true; exit; end;
+    end
+    else
+    begin
+      lxd += wklen;
+      if (lxd >= lterm) then begin xd := lxd; result := true; exit; end;
+    end;
+    // walk y
+    for f := 1 to wklen do if (le >= 0) then begin lyd += lsty; le -= ldx2; end else le += ldy2;
+    if (lxd = ex) or (lyd = ey) then break;
+    xwalk -= wklen; if (xwalk = 0) then xwalk := TileSize;
+    ywalk -= wklen; if (ywalk = 0) then ywalk := TileSize;
+  end;
+  //assert((xd div TileSize <> lxd div TileSize) or (yd div TileSize <> lyd div TileSize));
+  xd := lxd;
+  yd := lyd;
+  e := le;
 end;
 
 // NOT TESTED!
@@ -446,7 +475,7 @@ function TLineWalker.dx (): Integer; inline; begin if xyswapped then result := s
 function TLineWalker.dy (): Integer; inline; begin if xyswapped then result := sty else result := stx; end;
 
 function TLineWalker.setup (x0, y0, x1, y1: Integer): Boolean;
-  procedure swapInt (var a: Integer; var b: Integer); inline; var t: Integer; begin t := a; a := b; b := t; end;
+  procedure swapInt (var a: Integer; var b: Integer); inline; begin a := a xor b; b := b xor a; a := a xor b; end;
 var
   dsx, dsy: Integer; // "lengthes" for x and y axes
   rem: Integer;
@@ -534,7 +563,8 @@ begin
     begin
       yd := wy0;
       e -= rem+dsx;
-      if (rem > 0) then begin Inc(xd); e += dy2; end;
+      //if (rem > 0) then begin Inc(xd); e += dy2; end; //BUGGY
+      if (xd < wx0) then begin xd += 1; e += dy2; end; //???
       xfixed := true;
     end;
   end;
@@ -691,7 +721,8 @@ begin
     begin
       yd := wy0;
       e -= rem+dsx;
-      if (rem > 0) then begin Inc(xd); e += dy2; end;
+      //if (rem > 0) then begin Inc(xd); e += dy2; end; //BUGGY
+      if (xd < wx0) then begin xd += 1; e += dy2; end; //???
       xfixed := true;
     end;
   end;
@@ -1198,8 +1229,9 @@ end;
 // ////////////////////////////////////////////////////////////////////////// //
 function TBodyGridBase.forGridRect (x, y, w, h: Integer; cb: TGridInternalCB; bodyId: TBodyProxyId): Boolean;
 var
-  gx, gy: Integer;
   gw, gh: Integer;
+  ex, ey: Integer;
+  gx, gy: Integer;
 begin
   result := false;
   if (w < 1) or (h < 1) or not assigned(cb) then exit;
@@ -1210,16 +1242,22 @@ begin
   if (x+w <= 0) or (y+h <= 0) then exit;
   gw := mWidth;
   gh := mHeight;
-  //tsize := mTileSize;
   if (x >= gw*mTileSize) or (y >= gh*mTileSize) then exit;
-  for gy := y div mTileSize to (y+h-1) div mTileSize do
-  begin
-    if (gy < 0) then continue;
-    if (gy >= gh) then break;
-    for gx := x div mTileSize to (x+w-1) div mTileSize do
+  ex := (x+w-1) div mTileSize;
+  ey := (y+h-1) div mTileSize;
+  x := x div mTileSize;
+  y := y div mTileSize;
+  // clip rect
+  if (x < 0) then x := 0 else if (x >= gw) then x := gw-1;
+  if (y < 0) then y := 0 else if (y >= gh) then y := gh-1;
+  if (ex < 0) then ex := 0 else if (ex >= gw) then ex := gw-1;
+  if (ey < 0) then ey := 0 else if (ey >= gh) then ey := gh-1;
+  if (x > ex) or (y > ey) then exit; // just in case
+  // do the work
+  for gy := y to ey do
+  begin
+    for gx := x to ex do
     begin
-      if (gx < 0) then continue;
-      if (gx >= gw) then break;
       result := cb(gy*gw+gx, bodyId);
       if result then exit;
     end;
@@ -1286,15 +1324,6 @@ begin
   mGrid[grida] := ccidx;
 end;
 
-procedure TBodyGridBase.insertInternal (body: TBodyProxyId);
-var
-  px: PBodyProxyRec;
-begin
-  if (body < 0) or (body > High(mProxies)) then exit; // just in case
-  px := @mProxies[body];
-  forGridRect(px.mX, px.mY, px.mWidth, px.mHeight, inserter, body);
-end;
-
 
 // assume that we cannot have one object added to bucket twice
 function TBodyGridBase.remover (grida: Integer; bodyId: TBodyProxyId): Boolean;
@@ -1337,29 +1366,25 @@ begin
   end;
 end;
 
-procedure TBodyGridBase.removeInternal (body: TBodyProxyId);
-var
-  px: PBodyProxyRec;
-begin
-  if (body < 0) or (body > High(mProxies)) then exit; // just in case
-  px := @mProxies[body];
-  forGridRect(px.mX, px.mY, px.mWidth, px.mHeight, remover, body);
-end;
-
 
 // ////////////////////////////////////////////////////////////////////////// //
 function TBodyGridBase.insertBody (aObj: ITP; aX, aY, aWidth, aHeight: Integer; aTag: Integer=-1): TBodyProxyId;
 begin
   aTag := aTag and TagFullMask;
   result := allocProxy(aX, aY, aWidth, aHeight, aObj, aTag);
-  insertInternal(result);
+  //insertInternal(result);
+  forGridRect(aX, aY, aWidth, aHeight, inserter, result);
 end;
 
 
 procedure TBodyGridBase.removeBody (body: TBodyProxyId);
+var
+  px: PBodyProxyRec;
 begin
   if (body < 0) or (body > High(mProxies)) then exit; // just in case
-  removeInternal(body);
+  px := @mProxies[body];
+  //removeInternal(body);
+  forGridRect(px.mX, px.mY, px.mWidth, px.mHeight, remover, body);
   freeProxy(body);
 end;
 
@@ -1388,15 +1413,18 @@ begin
   // did any corner crossed tile boundary?
   if (x0 div mTileSize <> nx div mTileSize) or
      (y0 div mTileSize <> ny div mTileSize) or
-     ((x0+w) div mTileSize <> (nx+nw) div mTileSize) or
-     ((y0+h) div mTileSize <> (ny+nh) div mTileSize) then
+     ((x0+w-1) div mTileSize <> (nx+nw-1) div mTileSize) or
+     ((y0+h-1) div mTileSize <> (ny+nh-1) div mTileSize) then
   begin
-    removeInternal(body);
+    //writeln('moveResizeBody: cell occupation changed! old=(', x0, ',', y0, ')-(', x0+w-1, ',', y0+h-1, '); new=(', nx, ',', ny, ')-(', nx+nw-1, ',', ny+nh-1, ')');
+    //removeInternal(body);
+    forGridRect(px.mX, px.mY, px.mWidth, px.mHeight, remover, body);
     px.mX := nx+mMinX;
     px.mY := ny+mMinY;
     px.mWidth := nw;
     px.mHeight := nh;
-    insertInternal(body);
+    //insertInternal(body);
+    forGridRect(px.mX, px.mY, nw, nh, inserter, body);
   end
   else
   begin
@@ -1407,6 +1435,7 @@ begin
   end;
 end;
 
+
 //TODO: optimize for horizontal/vertical moves
 procedure TBodyGridBase.moveBody (body: TBodyProxyId; nx, ny: Integer);
 var
@@ -1546,6 +1575,7 @@ begin
   px.mY := ny+mMinY;
 end;
 
+
 procedure TBodyGridBase.resizeBody (body: TBodyProxyId; nw, nh: Integer);
 var
   px: PBodyProxyRec;
@@ -1561,14 +1591,16 @@ begin
   {$IF DEFINED(D2F_DEBUG_MOVER)}
   e_WriteLog(Format('proxy #%d: RESIZE: xg=%d;yg=%d;w=%d;h=%d;nw=%d;nh=%d', [body, x0, y0, w, h, nw, nh]), MSG_NOTIFY);
   {$ENDIF}
-  if ((x0+w) div mTileSize <> (x0+nw) div mTileSize) or
-     ((y0+h) div mTileSize <> (y0+nh) div mTileSize) then
+  if ((x0+w-1) div mTileSize <> (x0+nw-1) div mTileSize) or
+     ((y0+h-1) div mTileSize <> (y0+nh-1) div mTileSize) then
   begin
     // crossed tile boundary, do heavy work
-    removeInternal(body);
+    //removeInternal(body);
+    forGridRect(px.mX, px.mY, px.mWidth, px.mHeight, remover, body);
     px.mWidth := nw;
     px.mHeight := nh;
-    insertInternal(body);
+    //insertInternal(body);
+    forGridRect(px.mX, px.mY, nw, nh, inserter, body);
   end
   else
   begin
@@ -1690,12 +1722,13 @@ function TBodyGridBase.forEachInAABB (x, y, w, h: Integer; cb: TGridQueryCB; tag
 var
   idx: Integer;
   gx, gy: Integer;
+  sx, sy, ex, ey: Integer;
   curci: Integer;
   f: Integer;
   cc: PGridCell = nil;
   px: PBodyProxyRec;
   lq: LongWord;
-  gw: Integer;
+  gw, gh: Integer;
   x0, y0: Integer;
   ptag: Integer;
 begin
@@ -1712,11 +1745,24 @@ begin
   Dec(y, mMinY);
 
   gw := mWidth;
-  //tsize := mTileSize;
+  gh := mHeight;
 
   if (x+w <= 0) or (y+h <= 0) then exit;
-  if (x >= gw*mTileSize) or (y >= mHeight*mTileSize) then exit;
+  if (x >= gw*mTileSize) or (y >= gh*mTileSize) then exit;
+
+  sx := x div mTileSize;
+  sy := y div mTileSize;
+  ex := (x+w-1) div mTileSize;
+  ey := (y+h-1) div mTileSize;
+
+  // clip rect
+  if (sx < 0) then sx := 0 else if (sx >= gw) then sx := gw-1;
+  if (sy < 0) then sy := 0 else if (sy >= gh) then sy := gh-1;
+  if (ex < 0) then ex := 0 else if (ex >= gw) then ex := gw-1;
+  if (ey < 0) then ey := 0 else if (ey >= gh) then ey := gh-1;
+  if (sx > ex) or (sy > ey) then exit; // just in case
 
+  // has something to do
   if mInQuery then raise Exception.Create('recursive queries aren''t supported');
   mInQuery := true;
 
@@ -1732,14 +1778,10 @@ begin
   lq := mLastQuery;
 
   // go on
-  for gy := y div mTileSize to (y+h-1) div mTileSize do
+  for gy := sy to ey do
   begin
-    if (gy < 0) then continue;
-    if (gy >= mHeight) then break;
-    for gx := x div mTileSize to (x+w-1) div mTileSize do
+    for gx := sx to ex do
     begin
-      if (gx < 0) then continue;
-      if (gx >= gw) then break;
       // process cells
       curci := mGrid[gy*gw+gx];
       while (curci <> -1) do
@@ -1749,7 +1791,7 @@ begin
         begin
           if (cc.bodies[f] = -1) then break;
           px := @mProxies[cc.bodies[f]];
-          // shit. has to do it this way, so i can change tag in callback
+          // shit! has to do it this way, so i can change tag in callback
           if (px.mQueryMark = lq) then continue;
           px.mQueryMark := lq;
           ptag := px.mTag;
@@ -2406,7 +2448,8 @@ begin
     begin
       yd := wy0;
       e -= rem+dsx;
-      if (rem > 0) then begin Inc(xd); e += dy2; end;
+      //if (rem > 0) then begin Inc(xd); e += dy2; end; //BUGGY
+      if (xd < wx0) then begin xd += 1; e += dy2; end; //???
       xfixed := true;
     end;
   end;