X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_grid.pas;h=d7263f36e0c244dedd9a8424e113e41e397e1981;hb=1a0deddcb78946c8f7eba838d00fec5d6192501f;hp=475cb68d7ec3b961c324f504bcf802230ae2bbfc;hpb=4d64aecc835437d733321a8497b4c872c2b1c085;p=d2df-sdl.git diff --git a/src/game/g_grid.pas b/src/game/g_grid.pas index 475cb68..d7263f3 100644 --- a/src/game/g_grid.pas +++ b/src/game/g_grid.pas @@ -20,7 +20,7 @@ {.$DEFINE D2F_DEBUG_XXQ} {.$DEFINE D2F_DEBUG_MOVER} {$ENDIF} -{$DEFINE GRID_USE_ORTHO_ACCEL} +{.$DEFINE GRID_USE_ORTHO_ACCEL} unit g_grid; interface @@ -64,6 +64,9 @@ type function getEnabled (): Boolean; inline; procedure setEnabled (v: Boolean); inline; + function getX1 (): Integer; inline; + function getY1 (): Integer; inline; + public property x: Integer read mX; property y: Integer read mY; @@ -72,6 +75,11 @@ type property tag: Integer read getTag write setTag; property enabled: Boolean read getEnabled write setEnabled; property obj: ITP read mObj; + + property x0: Integer read mX; + property y0: Integer read mY; + property x1: Integer read getX1; + property y1: Integer read getY1; end; private @@ -189,8 +197,11 @@ type function traceRay (const x0, y0, x1, y1: Integer; cb: TGridRayQueryCB; tagmask: Integer=-1): ITP; overload; function traceRay (out ex, ey: Integer; const ax0, ay0, ax1, ay1: Integer; cb: TGridRayQueryCB; tagmask: Integer=-1): ITP; - //function traceOrthoRayWhileIn (const x0, y0, x1, y1: Integer; tagmask: Integer=-1): ITP; overload; - //function traceOrthoRayWhileIn (out ex, ey: Integer; const ax0, ay0, ax1, ay1: Integer; tagmask: Integer=-1): ITP; + // return `false` if we're still inside at the end + // line should be either strict horizontal, or strict vertical, otherwise an exception will be thrown + // `true`: endpoint will point at the last "inside" pixel + // `false`: endpoint will be (ax1, ay1) + function traceOrthoRayWhileIn (out ex, ey: Integer; ax0, ay0, ax1, ay1: Integer; tagmask: Integer=-1): Boolean; //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) @@ -232,7 +243,7 @@ function maxInt (a, b: Integer): Integer; inline; implementation uses - SysUtils, e_log, g_console; + SysUtils, e_log, g_console, utils; // ////////////////////////////////////////////////////////////////////////// // @@ -438,6 +449,16 @@ begin if v then mTag := mTag and (not TagDisabled) else mTag := mTag or TagDisabled; end; +function TBodyGridBase.TBodyProxyRec.getX1 (): Integer; inline; +begin + result := mX+mWidth-1; +end; + +function TBodyGridBase.TBodyProxyRec.getY1 (): Integer; inline; +begin + result := mY+mHeight-1; +end; + // ////////////////////////////////////////////////////////////////////////// // constructor TBodyGridBase.TAtPointEnumerator.Create (acells: TCellArray; aidx: Integer; agetpx: TGetProxyFn); @@ -666,13 +687,13 @@ end; // ////////////////////////////////////////////////////////////////////////// // function TBodyGridBase.getProxyEnabled (pid: TBodyProxyId): Boolean; inline; begin - if (pid >= 0) then result := ((mProxies[pid].mTag and TagDisabled) = 0) else result := false; + if (pid >= 0) and (pid < Length(mProxies)) 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 + if (pid >= 0) and (pid < Length(mProxies)) then begin if val then begin @@ -688,7 +709,7 @@ end; function TBodyGridBase.getProxyById (idx: TBodyProxyId): PBodyProxyRec; inline; begin - if (idx >= 0) and (idx < High(mProxies)) then result := @mProxies[idx] else result := nil; + if (idx >= 0) and (idx < Length(mProxies)) then result := @mProxies[idx] else result := nil; end; @@ -1654,18 +1675,6 @@ begin // one of those will never change x := xptr^+minx; 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)} @@ -1688,13 +1697,13 @@ begin ptag := px.mTag; if ((ptag and TagDisabled) = 0) and ((ptag and tagmask) <> 0) and (px.mQueryMark <> lq) and // constant coord should be inside - ((hopt and (y >= px.mY) and (y < px.mY+px.mHeight)) or - ((not hopt) and (x >= px.mX) and (x < px.mX+px.mWidth))) then + ((hopt and (y >= px.y0) and (y <= px.y1)) or + ((not hopt) and (x >= px.x0) and (x <= px.x1))) then begin px.mQueryMark := lq; // mark as processed // inside the proxy? - if (hopt and (x > px.mX) and (x < px.mX+px.mWidth-1)) or - ((not hopt) and (y > px.mY) and (y < px.mY+px.mHeight-1)) then + if (hopt and (x > px.x0) and (x < px.x1)) or + ((not hopt) and (y > px.y0) and (y < px.y1)) then begin // setup prev[xy] if assigned(cb) then @@ -1735,16 +1744,16 @@ begin if (stx < 0) then begin // going left - if (x < px.mX+px.mWidth-1) then continue; // not on the right edge - prevx := px.mX+px.mWidth; - x := prevx-1; + if (x < px.x1) then continue; // not on the right edge + x := px.x1; + prevx := x+1; end else begin // going right - if (x > px.mX) then continue; // not on the left edge - prevx := px.mX-1; - x := prevx+1; + if (x > px.x0) then continue; // not on the left edge + x := px.x0; + prevx := x-1; end; end else @@ -1755,16 +1764,16 @@ begin if (stx < 0) then begin // going up - if (y < px.mY+px.mHeight-1) then continue; // not on the bottom edge - prevy := px.mY+px.mHeight; - y := prevy-1; + if (y < px.y1) then continue; // not on the bottom edge + y := px.y1; + prevy := x+1; end else begin // going down - if (y > px.mY) then continue; // not on the top edge - prevy := px.mY-1; - y := prevy+1; + if (y > px.y0) then continue; // not on the top edge + y := px.y0; + prevy := y-1; end; end; if assigned(cb) then @@ -1838,7 +1847,7 @@ begin {$ENDIF} if (wkstep >= wklen) then break; Inc(yptr^, wkstep); - Inc(ga, mHeight); + Inc(ga, mWidth); end else begin @@ -1849,7 +1858,7 @@ begin {$ENDIF} if (wkstep >= wklen) then break; Dec(yptr^, wkstep); - Dec(ga, mHeight); + Dec(ga, mWidth); end; end; Dec(wklen, wkstep); @@ -2254,16 +2263,6 @@ begin if dbgShowTraceLog then e_LogWritefln('optimized htrace; wklen=%d', [wklen]); {$ENDIF} ga := (yptr^ div tsize)*gw+(xptr^ div tsize); - {$IF DEFINED(D2F_DEBUG)} - if hopt then - begin - if (yptr^ <> ay0) then raise Exception.Create('htrace fatal internal error'); - end - else - begin - if (xptr^ <> ax0) then raise Exception.Create('vtrace fatal internal error'); - end; - {$ENDIF} while (wklen > 0) do begin {$IF DEFINED(D2F_DEBUG)} @@ -2339,7 +2338,7 @@ begin {$ENDIF} if (wkstep >= wklen) then break; Inc(yptr^, wkstep); - Inc(ga, mHeight); + Inc(ga, mWidth); end else begin @@ -2350,7 +2349,7 @@ begin {$ENDIF} if (wkstep >= wklen) then break; Dec(yptr^, wkstep); - Dec(ga, mHeight); + Dec(ga, mWidth); end; end; Dec(wklen, wkstep); @@ -2445,4 +2444,135 @@ begin end; +{.$DEFINE D2F_DEBUG_OTR} +function TBodyGridBase.traceOrthoRayWhileIn (out ex, ey: Integer; ax0, ay0, ax1, ay1: Integer; tagmask: Integer=-1): Boolean; +var + ccidx: Integer; + cc: PGridCell; + px: PBodyProxyRec; + ptag: Integer; + minx, miny: Integer; + f, c0, c1: Integer; + x0, y0, x1, y1: Integer; + celly0, celly1: Integer; + dy: Integer; + filled: array[0..mTileSize-1] of Byte; + {$IF DEFINED(D2F_DEBUG_OTR)} + s: AnsiString = ''; + {$ENDIF} +begin + result := false; + ex := ax1; + ey := ay1; + if not ((ax0 = ax1) or (ay0 = ay1)) then raise Exception.Create('orthoray is not orthogonal'); + + tagmask := tagmask and TagFullMask; + if (tagmask = 0) then exit; + + if (forEachAtPoint(ax0, ay0, nil, tagmask) = nil) then exit; + + minx := mMinX; + miny := mMinY; + + // offset query coords to (0,0)-based + x0 := ax0-minx; + y0 := ay0-miny; + x1 := ax1-minx; + y1 := ay1-miny; + + if (x0 = x1) then + begin + if (x0 < 0) or (x0 >= mWidth*mTileSize) then exit; // oops + // vertical + if (y0 < y1) then + begin + // down + if (y1 < 0) or (y0 >= mHeight*mTileSize) then exit; + //if (ay0 < 0) then ay0 := 0; + if (y0 < 0) then exit; + if (y1 >= mHeight*mTileSize) then y1 := mHeight*mTileSize-1; + dy := 1; + end + else + begin + // up + if (y0 < 0) or (y1 >= mHeight*mTileSize) then exit; + //if (ay1 < 0) then ay1 := 0; + if (y1 < 0) then exit; + if (y0 >= mHeight*mTileSize) then y0 := mHeight*mTileSize-1; + dy := -1; + end; + // check tile + while true do + begin + ccidx := mGrid[(y0 div mTileSize)*mWidth+(x0 div mTileSize)]; + FillChar(filled, sizeof(filled), 0); + celly0 := y0 and (not (mTileSize-1)); + celly1 := celly0+mTileSize-1; + while (ccidx <> -1) do + begin + cc := @mCells[ccidx]; + for f := 0 to GridCellBucketSize-1 do + begin + if (cc.bodies[f] = -1) then break; + px := @mProxies[cc.bodies[f]]; + ptag := px.mTag; + if ((ptag and TagDisabled) = 0) and ((ptag and tagmask) <> 0) and + (ax0 >= px.x0) and (ax0 <= px.x1) then + begin + // bound c0 and c1 to cell + c0 := nclamp(px.y0-miny, celly0, celly1); + c1 := nclamp(px.y1-miny, celly0, celly1); + // fill the thing + {$IF DEFINED(D2F_DEBUG_OTR)} + e_LogWritefln('**px.y0=%s; px.y1=%s; c0=%s; c1=%s; celly0=%s; celly1=%s; [%s..%s]', [px.y0-miny, px.y1-miny, c0, c1, celly0, celly1, c0-celly0, (c0-celly0)+(c1-c0)]); + {$ENDIF} + //assert(c0 <= c1); + FillChar(filled[c0-celly0], c1-c0+1, 1); + end; + end; + // next cell + ccidx := cc.next; + end; + {$IF DEFINED(D2F_DEBUG_OTR)} + s := formatstrf(' x=%s; ay0=%s; ay1=%s; y0=%s; celly0=%s; celly1=%s; dy=%s; [', [ax0, ay0, ay1, y0, celly0, celly1, dy]); + for f := 0 to High(filled) do if (filled[f] <> 0) then s += '1' else s += '0'; + s += ']'; + e_LogWriteln(s); + {$ENDIF} + // now go till we hit cell boundary or empty space + if (dy < 0) then + begin + // up + while (y0 >= celly0) and (filled[y0-celly0] <> 0) do + begin + {$IF DEFINED(D2F_DEBUG_OTR)} + e_LogWritefln(' filled: cdy=%s; y0=%s; celly0=%s; ay0=%s; ay1=%s', [y0-celly0, y0, celly0, ay0, ay1]); + {$ENDIF} + Dec(y0); + Dec(ay0); + end; + {$IF DEFINED(D2F_DEBUG_OTR)} + e_LogWritefln(' span done: cdy=%s; y0=%s; celly0=%s; ay0=%s; ay1=%s', [y0-celly0, y0, celly0, ay0, ay1]); + {$ENDIF} + if (ay0 <= ay1) then begin ey := ay1; result := false; exit; end; + if (y0 >= celly0) then begin ey := ay0+1; {assert(forEachAtPoint(ex, ey, nil, tagmask) <> nil);} result := true; exit; end; + end + else + begin + // down + while (y0 <= celly1) and (filled[y0-celly0] <> 0) do begin Inc(y0); Inc(ay0); end; + if (ay0 >= ay1) then begin ey := ay1; result := false; exit; end; + if (y0 <= celly1) then begin ey := ay0-1; result := true; exit; end; + end; + end; + end + else + begin + // horizontal + assert(false); + end; +end; + + end.