DEADSOFTWARE

slightly better water particles
authorKetmar Dark <ketmar@ketmar.no-ip.org>
Thu, 24 Aug 2017 02:23:12 +0000 (05:23 +0300)
committerKetmar Dark <ketmar@ketmar.no-ip.org>
Thu, 24 Aug 2017 02:41:18 +0000 (05:41 +0300)
src/game/g_gfx.pas
src/game/g_grid.pas
src/game/g_map.pas

index ab895762d3cae8f6b59ed2cc18fe77fc87ed3650..bbdc4329cd2ee406e874f82158a40f61651f5c8e 100644 (file)
@@ -87,6 +87,8 @@ type
     offsetX, offsetY:   ShortInt;
     // for bubbles
     liquidTopY: Integer; // don't float higher than this
+    // for water
+    stickDX: Integer;
 
     //k8: sorry, i have to emulate virtual methods this way, 'cause i haet `Object`
 
@@ -690,71 +692,48 @@ begin
 end;
 
 
-// ////////////////////////////////////////////////////////////////////////// //
-procedure TParticle.thinkerBubble ();
-var
-  h: Integer;
-  dY: SmallInt;
-  b: Integer;
-  s: ShortInt;
-begin
-  h := gMapInfo.Height;
-
-  dY := Round(VelY);
-
-  if dY <> 0 then
-  begin
-    if dY > 0 then
-      s := 1
-    else
-      s := -1;
-
-    for b := 1 to Abs(dY) do
-    begin
-      if (Y+s >= h) or (Y+s <= 0) then begin die(); break; end;
-
-      (*
-      if not isLiquidAt(X, Y+s) {ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID)} then
-      begin // Óæå íå æèäêîñòü
-        State := STATE_FREE;
-        Break;
-      end;
-      *)
-      // we traced liquid before, so don't bother checking
-      if (Y+s <= liquidTopY) then begin die(); break; end;
-
-      Y := Y+s;
-    end;
-  end;
-
-  if VelY > -4 then
-    VelY := VelY + AccelY;
-
-  Time := Time + 1;
-end;
-
-
 // ////////////////////////////////////////////////////////////////////////// //
 procedure TParticle.thinkerWater ();
 var
-  w, h: Integer;
   dX, dY: SmallInt;
+  {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
+  w, h: Integer;
   b: Integer;
   s: ShortInt;
+  {$ELSE}
+  pan: TPanel;
+  ex, ey: Integer;
+  {$ENDIF}
 begin
+  {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
   w := gMapInfo.Width;
   h := gMapInfo.Height;
+  {$ENDIF}
 
+  //TODO: trace wall end when water becomes stick
   if (State = STATE_STICK) and (Random(30) = 15) then
   begin // Ñòåêàåò/îòëèïàåò
     VelY := 0.5;
     AccelY := 0.15;
+    {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
     if (not isBlockedAt(X-1, Y) {ByteBool(gCollideMap[Y, X-1] and MARK_BLOCKED)}) and
        (not isBlockedAt(X+1, Y) {ByteBool(gCollideMap[Y, X+1] and MARK_BLOCKED)}) then
       State := STATE_NORMAL;
+    {$ELSE}
+    if (stickDX = 0) then
+    begin
+      // no walls around, drop
+      State := STATE_NORMAL;
+    end
+    else
+    begin
+      if (g_Map_PanelAtPoint(X+stickDX, Y, (GridTagWall or GridTagDoor or GridTagStep)) = nil) then State := STATE_NORMAL;
+    end;
+    {$ENDIF}
     exit;
   end;
 
+  {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
   if not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)} then
   begin
     if isLiftUpAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_LIFTUP)} then
@@ -779,11 +758,37 @@ begin
       AccelY := 0.15;
     end;
   end;
+  {$ELSE}
+  pan := g_Map_PanelAtPoint(X, Y, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater or GridTagLift));
+  if (pan <> nil) then
+  begin
+    if ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
+    if ((pan.PanelType and PANEL_LIFTUP) <> 0) then
+    begin
+      if (VelY > -4-Random(3)) then VelY -= 0.8;
+      if (Abs(VelX) > 0.1) then VelX -= VelX/10.0;
+      VelX += (Random-Random)*0.2;
+      AccelY := 0.15;
+    end;
+    if ((pan.PanelType and PANEL_LIFTLEFT) <> 0) then
+    begin
+      if (VelX > -8-Random(3)) then VelX -= 0.8;
+      AccelY := 0.15;
+    end;
+    if ((pan.PanelType and PANEL_LIFTRIGHT) <> 0) then
+    begin
+      if (VelX < 8+Random(3)) then VelX += 0.8;
+      AccelY := 0.15;
+    end;
+  end;
+  {$ENDIF}
 
   dX := Round(VelX);
   dY := Round(VelY);
 
+  {$IF not DEFINED(D2F_NEW_SPARK_THINKER)}
   if (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
+  begin
     if (State <> STATE_STICK) and
        (not isBlockedAt(X, Y-1) {ByteBool(gCollideMap[Y-1, X] and MARK_BLOCKED)}) and
        (not isBlockedAt(X, Y) {ByteBool(gCollideMap[Y, X] and MARK_BLOCKED)}) and
@@ -793,24 +798,88 @@ begin
       AccelY := 0.5;
       State := STATE_NORMAL;
     end;
-
-  if dX <> 0 then
+  end;
+  {$ELSE}
+  if (State <> STATE_STICK) and (Abs(VelX) < 0.1) and (Abs(VelY) < 0.1) then
   begin
-    if dX > 0 then
-      s := 1
-    else
-      s := -1;
+    // Âèñèò â âîçäóõå - êàïàåò
+    if (nil = g_Map_traceToNearest(X, Y-1, X, Y+1, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey)) then
+    begin
+      VelY := 0.8;
+      AccelY := 0.5;
+      State := STATE_NORMAL;
+    end;
+  end;
+  {$ENDIF}
 
+  {$IF DEFINED(D2F_NEW_SPARK_THINKER)}
+  // horizontal
+  if (dX <> 0) then
+  begin
+    pan := g_Map_traceToNearest(X, Y, X+dX, Y, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
+    X := ex;
+    // free to ride?
+    if (pan <> nil) then
+    begin
+      // nope
+      if (dY > 0) and ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
+      // Ñòåíà/äâåðü?
+      if ((pan.tag and (GridTagWall or GridTagDoor or GridTagStep)) <> 0) then
+      begin
+        VelX := 0;
+        VelY := 0;
+        AccelX := 0;
+        AccelY := 0;
+        State := STATE_STICK;
+        if (dX > 0) then stickDX := 1 else stickDX := -1;
+      end;
+    end;
+    if (X < 0) or (X >= gMapInfo.Width) then begin die(); exit; end;
+  end;
+  // vertical
+  if (dY <> 0) then
+  begin
+    pan := g_Map_traceToNearest(X, Y, X, Y+dY, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
+    Y := ey;
+    // free to ride?
+    if (pan <> nil) then
+    begin
+      // nope
+      if (dY > 0) and ((pan.tag and (GridTagAcid1 or GridTagAcid2 or GridTagWater)) <> 0) then begin die(); exit; end;
+      // Ñòåíà/äâåðü?
+      if ((pan.tag and (GridTagWall or GridTagDoor or GridTagStep)) <> 0) then
+      begin
+        VelX := 0;
+        VelY := 0;
+        AccelX := 0;
+        AccelY := 0;
+        if (dY > 0) and (State <> STATE_STICK) then
+        begin
+          State := STATE_NORMAL;
+        end
+        else
+        begin
+          State := STATE_STICK;
+               if (g_Map_PanelAtPoint(X-1, Y, (GridTagWall or GridTagDoor or GridTagStep)) <> nil) then stickDX := -1
+          else if (g_Map_PanelAtPoint(X+1, Y, (GridTagWall or GridTagDoor or GridTagStep)) <> nil) then stickDX := 1
+          else stickDX := 0;
+        end;
+      end;
+    end;
+    if (Y < 0) or (Y >= gMapInfo.Height) then begin die(); exit; end;
+  end;
+  {$ELSE}
+  // horizontal
+  if (dX <> 0) then
+  begin
+    if (dX > 0) then s := 1 else s := -1;
     for b := 1 to Abs(dX) do
     begin
-       // Ñáîêó ãðàíèöà?
+      // Ñáîêó ãðàíèöà?
       if (X+s >= w) or (X+s <= 0) then begin die(); break;end;
-
       //c := gCollideMap[Y, X+s];
-
       // Ñáîêó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò?
       if isLiquidAt(X+s, Y) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then begin die(); break; end;
-
       if isBlockedAt(X+s, Y) {ByteBool(c and MARK_BLOCKED)} then
       begin // Ñòåíà/äâåðü
         VelX := 0;
@@ -820,47 +889,80 @@ begin
         State := STATE_STICK;
         Break;
       end;
-
       X := X+s;
     end;
   end;
-
-  if dY <> 0 then
+  // vertical
+  if (dY <> 0) then
   begin
-    if dY > 0 then
-      s := 1
-    else
-      s := -1;
-
+    if (dY > 0) then s := 1 else s := -1;
     for b := 1 to Abs(dY) do
     begin
       // Ñíèçó/ñâåðõó ãðàíèöà
       if (Y+s >= h) or (Y+s <= 0) then begin die(); break; end;
-
       //c := gCollideMap[Y+s, X];
-
       // Ñíèçó æèäêîñòü, à ÷àñòèöà óæå ïàäàåò
       if isLiquidAt(X, Y+s) {ByteBool(c and MARK_LIQUID)} and (dY > 0) then begin die(); break; end;
-
       if isBlockedAt(X, Y+s) {ByteBool(c and MARK_BLOCKED)} then
       begin // Ñòåíà/äâåðü
         VelX := 0;
         VelY := 0;
         AccelX := 0;
         AccelY := 0;
-        if (s > 0) and (State <> STATE_STICK) then
-          State := STATE_NORMAL
-        else
-          State := STATE_STICK;
+        if (s > 0) and (State <> STATE_STICK) then State := STATE_NORMAL else State := STATE_STICK;
+        break;
+      end;
+      Y := Y+s;
+    end;
+  end;
+  {$ENDIF}
+
+  VelX += AccelX;
+  VelY += AccelY;
+
+  Time += 1;
+end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+procedure TParticle.thinkerBubble ();
+var
+  h: Integer;
+  dY: SmallInt;
+  b: Integer;
+  s: ShortInt;
+begin
+  h := gMapInfo.Height;
+
+  dY := Round(VelY);
+
+  if dY <> 0 then
+  begin
+    if dY > 0 then
+      s := 1
+    else
+      s := -1;
+
+    for b := 1 to Abs(dY) do
+    begin
+      if (Y+s >= h) or (Y+s <= 0) then begin die(); break; end;
+
+      (*
+      if not isLiquidAt(X, Y+s) {ByteBool(gCollideMap[Y+s, X] and MARK_LIQUID)} then
+      begin // Óæå íå æèäêîñòü
+        State := STATE_FREE;
         Break;
       end;
+      *)
+      // we traced liquid before, so don't bother checking
+      if (Y+s <= liquidTopY) then begin die(); break; end;
 
       Y := Y+s;
     end;
   end;
 
-  VelX := VelX + AccelX;
-  VelY := VelY + AccelY;
+  if VelY > -4 then
+    VelY := VelY + AccelY;
 
   Time := Time + 1;
 end;
@@ -1286,7 +1388,7 @@ begin
 
       // 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;
+      if not g_Map_TraceLiquidNonPrecise(X, Y, 0, -8, liquidx, liquidTopY) then continue;
 
       VelX := 0;
       VelY := -1-Random;
index 3ff9230a3e1c27414a28fd4867ab6df0280bd759..2afca2f3c16c0db534a6ec68cb4bb665c200e186 100644 (file)
@@ -146,10 +146,14 @@ type
     //         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
     // no callback: return object of the nearest hit or nil
+    // if `inverted` is true, trace will register bodies *exluding* tagmask
     //WARNING: don't change tags in callbacks here!
     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 traceRayWhileIn (const x0, y0, x1, y1: Integer; tagmask: Integer=-1): ITP; overload;
+    //function traceRayWhileIn (out ex, ey: Integer; const ax0, ay0, ax1, ay1: Integer; tagmask: Integer=-1): ITP;
+
     //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)
     // trace line along the grid, calling `cb` for all objects in passed cells, in no particular order
@@ -1291,23 +1295,7 @@ begin
     result := forEachAtPoint(ax0, ay0, nil, tagmask, @ptag);
     if (result <> nil) then
     begin
-      if assigned(cb) then
-      begin
-        if cb(result, ptag, ax0, ay0, ax0, ay0) then
-        begin
-          ex := ax0;
-          ey := ay0;
-        end
-        else
-        begin
-          result := nil;
-        end;
-      end
-      else
-      begin
-        ex := ax0;
-        ey := ay0;
-      end;
+      if assigned(cb) and not cb(result, ptag, ax0, ay0, ax0, ay0) then result := Default(ITP);
     end;
     exit;
   end;
@@ -1880,4 +1868,324 @@ begin
 end;
 
 
+// ////////////////////////////////////////////////////////////////////////// //
+(*
+function TBodyGridBase.traceRayWhileIn (const x0, y0, x1, y1: Integer; tagmask: Integer=-1): ITP; overload;
+var
+  ex, ey: Integer;
+begin
+  result := traceRayWhileIn(ex, ey, x0, y0, x1, y1, tagmask);
+end;
+
+
+// FUCKIN' PASTA!
+function TBodyGridBase.traceRayWhileIn (out ex, ey: Integer; const ax0, ay0, ax1, ay1: Integer; tagmask: Integer=-1): ITP;
+const
+  tsize = mTileSize;
+var
+  wx0, wy0, wx1, wy1: Integer; // window coordinates
+  stx, sty: Integer; // "steps" for x and y axes
+  dsx, dsy: Integer; // "lengthes" for x and y axes
+  dx2, dy2: Integer; // "double lengthes" for x and y axes
+  xd, yd: Integer; // current coord
+  e: Integer; // "error" (as in bresenham algo)
+  rem: Integer;
+  term: Integer;
+  xptr, yptr: PInteger;
+  xfixed: Boolean;
+  temp: Integer;
+  prevx, prevy: Integer;
+  lastDistSq: Integer;
+  ccidx, curci: Integer;
+  hasUntried: Boolean;
+  lastGA: Integer = -1;
+  ga, x, y: Integer;
+  lastObj: ITP;
+  wasHit: Boolean = false;
+  gw, gh, minx, miny, maxx, maxy: Integer;
+  cc: PGridCell;
+  px: PBodyProxyRec;
+  lq: LongWord;
+  f, ptag, distSq: Integer;
+  x0, y0, x1, y1: Integer;
+  inx, iny: Integer;
+begin
+  result := Default(ITP);
+  lastObj := Default(ITP);
+  tagmask := tagmask and TagFullMask;
+  ex := ax1; // why not?
+  ey := ay1; // why not?
+  if (tagmask = 0) then exit;
+
+  if (ax0 = ax1) and (ay0 = ay1) then exit; // doesn't matter
+
+  // we should start inside
+  if (forEachAtPoint(ax0, ay0, nil, tagmask, @ptag) = nil) then
+  begin
+    ex := ax0; // why not?
+    ey := ay0; // why not?
+    exit;
+  end;
+
+  lastDistSq := distanceSq(ax0, ay0, ax1, ay1)+1;
+
+  gw := mWidth;
+  gh := mHeight;
+  minx := mMinX;
+  miny := mMinY;
+  maxx := gw*tsize-1;
+  maxy := gh*tsize-1;
+
+  x0 := ax0;
+  y0 := ay0;
+  x1 := ax1;
+  y1 := ay1;
+
+  // offset query coords to (0,0)-based
+  Dec(x0, minx);
+  Dec(y0, miny);
+  Dec(x1, minx);
+  Dec(y1, miny);
+
+  // clip rectange
+  wx0 := 0;
+  wy0 := 0;
+  wx1 := maxx;
+  wy1 := maxy;
+
+  // horizontal setup
+  if (x0 < x1) then
+  begin
+    // from left to right
+    if (x0 > wx1) or (x1 < wx0) then exit; // out of screen
+    stx := 1; // going right
+  end
+  else
+  begin
+    // from right to left
+    if (x1 > wx1) or (x0 < wx0) then exit; // out of screen
+    stx := -1; // going left
+    x0 := -x0;
+    x1 := -x1;
+    wx0 := -wx0;
+    wx1 := -wx1;
+    swapInt(wx0, wx1);
+  end;
+
+  // vertical setup
+  if (y0 < y1) then
+  begin
+    // from top to bottom
+    if (y0 > wy1) or (y1 < wy0) then exit; // out of screen
+    sty := 1; // going down
+  end
+  else
+  begin
+    // from bottom to top
+    if (y1 > wy1) or (y0 < wy0) then exit; // out of screen
+    sty := -1; // going up
+    y0 := -y0;
+    y1 := -y1;
+    wy0 := -wy0;
+    wy1 := -wy1;
+    swapInt(wy0, wy1);
+  end;
+
+  dsx := x1-x0;
+  dsy := y1-y0;
+
+  if (dsx < dsy) then
+  begin
+    xptr := @yd;
+    yptr := @xd;
+    swapInt(x0, y0);
+    swapInt(x1, y1);
+    swapInt(dsx, dsy);
+    swapInt(wx0, wy0);
+    swapInt(wx1, wy1);
+    swapInt(stx, sty);
+  end
+  else
+  begin
+    xptr := @xd;
+    yptr := @yd;
+  end;
+
+  dx2 := 2*dsx;
+  dy2 := 2*dsy;
+  xd := x0;
+  yd := y0;
+  e := 2*dsy-dsx;
+  term := x1;
+
+  xfixed := false;
+  if (y0 < wy0) then
+  begin
+    // clip at top
+    temp := dx2*(wy0-y0)-dsx;
+    xd += temp div dy2;
+    rem := temp mod dy2;
+    if (xd > wx1) then exit; // x is moved out of clipping rect, nothing to do
+    if (xd+1 >= wx0) then
+    begin
+      yd := wy0;
+      e -= rem+dsx;
+      if (rem > 0) then begin Inc(xd); e += dy2; end;
+      xfixed := true;
+    end;
+  end;
+
+  if (not xfixed) and (x0 < wx0) then
+  begin
+    // clip at left
+    temp := dy2*(wx0-x0);
+    yd += temp div dx2;
+    rem := temp mod dx2;
+    if (yd > wy1) or (yd = wy1) and (rem >= dsx) then exit;
+    xd := wx0;
+    e += rem;
+    if (rem >= dsx) then begin Inc(yd); e -= dx2; end;
+  end;
+
+  if (y1 > wy1) then
+  begin
+    // clip at bottom
+    temp := dx2*(wy1-y0)+dsx;
+    term := x0+temp div dy2;
+    rem := temp mod dy2;
+    if (rem = 0) then Dec(term);
+  end;
+
+  if (term > wx1) then term := wx1; // clip at right
+
+  Inc(term); // draw last point
+  //if (term = xd) then exit; // this is the only point, get out of here
+
+  if (sty = -1) then yd := -yd;
+  if (stx = -1) then begin xd := -xd; term := -term; end;
+  dx2 -= dy2;
+
+  // first move, to skip starting point
+  // DON'T DO THIS! loop will take care of that
+  if (xd = term) then
+  begin
+    result := forEachAtPoint(ax0, ay0, nil, tagmask, @ptag);
+    if (result <> nil) and ((ptag and tagmask) <> 0) then result := nil;
+    exit;
+  end;
+
+  prevx := xptr^+minx;
+  prevy := yptr^+miny;
+
+  // increase query counter
+  Inc(mLastQuery);
+  if (mLastQuery = 0) then
+  begin
+    // just in case of overflow
+    mLastQuery := 1;
+    for f := 0 to High(mProxies) do mProxies[f].mQueryMark := 0;
+  end;
+  lq := mLastQuery;
+
+  ccidx := -1;
+  // draw it; can omit checks
+  while (xd <> term) do
+  begin
+    // check cell(s)
+    // new tile?
+    ga := (yptr^ div tsize)*gw+(xptr^ div tsize);
+    if (ga <> lastGA) then
+    begin
+      // yes
+      lastGA := ga;
+      ccidx := mGrid[lastGA];
+      // no objects in cell == exit
+      if (ccidx = -1) then exit;
+    end;
+    // has something to process in this tile?
+    if (ccidx <> -1) then
+    begin
+      // process cell
+      curci := ccidx;
+      // convert coords to map (to avoid ajdusting coords inside the loop)
+      x := xptr^+minx;
+      y := yptr^+miny;
+      wasHit := false;
+      // process cell list
+      while (curci <> -1) do
+      begin
+        cc := @mCells[curci];
+        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 (px.mQueryMark <> lq) then
+          begin
+function lineAABBIntersects (x0, y0, x1, y1: Integer; bx, by, bw, bh: Integer; out inx, iny: Integer): Boolean;
+            // can we process this proxy?
+            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; // mark as processed
+              if ((ptag and tagmask) = 0) then
+              begin
+                result := px.mObj;
+                ex := x;
+                ey := y;
+                exit;
+              end;
+              // march out of the panel/cell
+              while (xd <> term) do
+              begin
+                if (e >= 0) then begin yd += sty; e -= dx2; end else e += dy2;
+                xd += stx;
+                // new cell?
+                ga := (yptr^ div tsize)*gw+(xptr^ div tsize);
+                if (ga <> lastGA) then break;
+                // out of panel?
+                if not ((x >= px.mX) and (y >= px.mY) and (x < px.mX+px.mWidth) and (y < px.mY+px.mHeight)) then break;
+              end;
+            end;
+          end;
+        end;
+        // next cell
+        curci := cc.next;
+      end;
+      // still has something interesting in this cell?
+      if not hasUntried then
+      begin
+        // nope, don't process this cell anymore; signal cell completion
+        ccidx := -1;
+        if assigned(cb) then
+        begin
+          if cb(nil, 0, x, y, prevx, prevy) then begin result := lastObj; exit; end;
+        end
+        else if wasHit then
+        begin
+          result := lastObj;
+          exit;
+        end;
+      end;
+    end;
+    //putPixel(xptr^, yptr^);
+    // move coords
+    prevx := xptr^+minx;
+    prevy := yptr^+miny;
+    if (e >= 0) then begin yd += sty; e -= dx2; end else e += dy2;
+    xd += stx;
+  end;
+  // we can travel less than one cell
+  if wasHit and not assigned(cb) then
+  begin
+    result := lastObj;
+  end
+  else
+  begin
+    ex := ax1; // why not?
+    ey := ay1; // why not?
+  end;
+end;
+*)
+
+
 end.
index 2c2a6b5c1a18c16e16c35cd2f4210eb79c5b6662..f0419bf49cf4314ad47ee168b1eb3fd78c87ad8a 100644 (file)
@@ -104,10 +104,11 @@ type
   TForEachPanelCB = function (pan: TPanel): Boolean; // return `true` to stop
 
 function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean;
+function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel;
 
 // 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;
+function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
 
 
 procedure g_Map_ProfilersBegin ();
@@ -376,6 +377,14 @@ begin
 end;
 
 
+function g_Map_PanelAtPoint (x, y: Integer; tagmask: Integer=-1): TPanel;
+begin
+  result := nil;
+  if (tagmask = 0) then exit;
+  result := mapGrid.forEachAtPoint(x, y, nil, tagmask);
+end;
+
+
 function g_Map_IsSpecialTexture(Texture: String): Boolean;
 begin
   Result := (Texture = TEXTURE_NAME_WATER) or
@@ -2873,7 +2882,7 @@ 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;
+function g_Map_TraceLiquidNonPrecise (x, y, dx, dy: Integer; out topx, topy: Integer): Boolean;
 const
   MaskLiquid = GridTagWater or GridTagAcid1 or GridTagAcid2;
 begin