private static void CastPartialLight(Loc rectStart, Loc rectSize, Loc start, Grid.LocTest checkBlock, LightOperation lightOp, int startColumn, int startSlope, int endSlope, Dir8 dir) { // Set true if the previous cell we encountered was blocked. bool prevBlocked = false; int savedRightSlope = -SLOPE_GRANULARITY; int colVal = OctantTransform[(int)dir, 0] != 0 ? OctantTransform[(int)dir, 0] : OctantTransform[(int)dir, 2]; Loc colDiff = new Loc(); if (colVal > 0) { colDiff = rectStart + rectSize - start - new Loc(1); } else { colDiff = start - rectStart; } int maxCol = OctantTransform[(int)dir, 0] != 0 ? colDiff.X : colDiff.Y; int rowVal = OctantTransform[(int)dir, 3] != 0 ? OctantTransform[(int)dir, 3] : OctantTransform[(int)dir, 1]; Loc rowDiff = new Loc(); if (rowVal > 0) { rowDiff = rectStart + rectSize - start - new Loc(1); } else { rowDiff = start - rectStart; } int maxRow = OctantTransform[(int)dir, 3] != 0 ? rowDiff.Y : rowDiff.X; for (int currentCol = startColumn; currentCol <= maxCol; currentCol++) { int xc = currentCol; for (int yc = Math.Min(maxRow, currentCol); yc >= 0; yc--) { int gridX = start.X + xc * OctantTransform[(int)dir, 0] + yc * OctantTransform[(int)dir, 1]; int gridY = start.Y + xc * OctantTransform[(int)dir, 2] + yc * OctantTransform[(int)dir, 3]; //due to safeguards, this block should never be hit if (!Collision.InBounds(rectStart, rectSize, new Loc(gridX, gridY))) { continue; } int leftBlockSlope = (yc * SLOPE_GRANULARITY + SLOPE_GRANULARITY / 2) * 2 / (xc * 2 - 1); int rightBlockSlope = (yc * SLOPE_GRANULARITY - SLOPE_GRANULARITY / 2) * 2 / (xc * 2 + 1); if (rightBlockSlope > startSlope) // Block is above the left edge of our view area; skip. { continue; } else if (leftBlockSlope < endSlope) // Block is below the right edge of our view area; we're done. { break; } float lighting = 1f; if (leftBlockSlope > startSlope) // Block is above the left edge of our view area; skip. { lighting = 0.5f; } else if (rightBlockSlope < endSlope) // Block is below the right edge of our view area; we're done. { lighting = 0.5f; } if (((int)dir % 2 == 0 || yc != 0) && ((int)dir % 2 == 1 || yc != currentCol)) { lightOp(gridX, gridY, lighting); } bool curBlocked = checkBlock(new Loc(gridX, gridY)); if (prevBlocked) { if (curBlocked) { savedRightSlope = rightBlockSlope; } else { prevBlocked = false; startSlope = savedRightSlope; } } else { if (curBlocked) { if (leftBlockSlope <= startSlope) { CastPartialLight(rectStart, rectSize, start, checkBlock, lightOp, currentCol + 1, startSlope, leftBlockSlope, dir); } prevBlocked = true; savedRightSlope = rightBlockSlope; } } } if (prevBlocked) { break; } } }
public static void CalculateAnalogFOV(Loc rectStart, Loc rectSize, Loc start, Grid.LocTest checkBlock, LightOperation lightOp) { // Viewer's cell is always visible. if (Collision.InBounds(rectStart, rectSize, start)) { lightOp(start.X, start.Y, 1f); } for (int dir = 0; dir < DirExt.DIR8_COUNT; dir++) { CastPartialLight(rectStart, rectSize, start, checkBlock, lightOp, 1, 3 * SLOPE_GRANULARITY, -SLOPE_GRANULARITY, (Dir8)dir); } }