diff --git a/src/game/g_grid.pas b/src/game/g_grid.pas
index 55feca9a094f1ac89568f79b79e78f5ab2e72d93..280fd90f3fd967f0753a31ee69cb6386ceb5fd05 100644 (file)
--- a/src/game/g_grid.pas
+++ b/src/game/g_grid.pas
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;
// ////////////////////////////////////////////////////////////////////////// //
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;
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;
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;
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;
// ////////////////////////////////////////////////////////////////////////// //
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;
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;
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;
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;
// 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
end;
end;
+
//TODO: optimize for horizontal/vertical moves
procedure TBodyGridBase.moveBody (body: TBodyProxyId; nx, ny: Integer);
var
px.mY := ny+mMinY;
end;
+
procedure TBodyGridBase.resizeBody (body: TBodyProxyId; nw, nh: Integer);
var
px: PBodyProxyRec;
{$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
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;
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
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;
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;