From bc547522564823ba12c2cab5fc120b3b7fe00c9c Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 10 Jan 2025 13:11:16 -0600 Subject: [PATCH 01/16] add various forEachInRay helpers --- flixel/tile/FlxBaseTilemap.hx | 18 +++ flixel/tile/FlxTilemap.hx | 278 +++++++++++++++++++++++++++------- 2 files changed, 245 insertions(+), 51 deletions(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index f60d6eeb08..af98c2b069 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -221,6 +221,24 @@ class FlxBaseTilemap extends FlxObject return false; } + /** + * Shoots a ray from the start point to the end point. + * If/when it passes through a tile, it stores that point and returns false. + * + * **Note:** In flixel 5.0.0, this was redone, the old method is now `rayStep` + * + * @param start The world coordinates of the start of the ray. + * @param end The world coordinates of the end of the ray. + * @param result Optional result vector, to avoid creating a new instance to be returned. + * Only returned if the line enters the rect. + * @return Returns true if the ray made it from Start to End without hitting anything. + * Returns false and fills Result if a tile was hit. + */ + public function forEachOverlappingRay(start:FlxPoint, end:FlxPoint, ?func:FlxPoint):Void + { + throw "ray must be implemented"; + } + /** * Shoots a ray from the start point to the end point. * If/when it passes through a tile, it stores that point and returns false. diff --git a/flixel/tile/FlxTilemap.hx b/flixel/tile/FlxTilemap.hx index b281c742a7..c81dda1391 100644 --- a/flixel/tile/FlxTilemap.hx +++ b/flixel/tile/FlxTilemap.hx @@ -893,67 +893,127 @@ class FlxTypedTilemap extends FlxBaseTilemap updateWorld ); } - + /** * Shoots a ray from the start point to the end point. * If/when it passes through a tile, it stores that point and returns false. - * Note: In flixel 5.0.0, this was redone, the old method is now `rayStep` - * - * @param start The world coordinates of the start of the ray. - * @param end The world coordinates of the end of the ray. - * @param result Optional result vector, to avoid creating a new instance to be returned. - * Only returned if the line enters the rect. + * + * **Note:** In flixel 5.0.0, this was redone, the old method is now `rayStep` + * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param result Optional result vector, indicating where the ray hit a wall * @return Returns true if the ray made it from Start to End without hitting anything. - * Returns false and fills Result if a tile was hit. + * Returns false and fills `result` if a tile was hit. */ override function ray(start:FlxPoint, end:FlxPoint, ?result:FlxPoint):Bool + { + return -1 < findIndexInRayI(start, end, (_, tile)->tile != null && tile.solid, result); + } + + /** + * Calls `func` on all tiles overlapping a ray from `start` to `end` + * + * **Note:** This skips any tiles with no instance + * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param func The function, where `tile` is the tile instance for that location + */ + inline public function forEachInRay(start:FlxPoint, end:FlxPoint, func:(Tile)->Void) + { + findIndexInRayI(start, end, voidFindIgnoreIndex(func)); + } + + /** + * Calls `func` on all tiles overlapping a ray from `start` to `end` + * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param func The condition, where `index` is the tile's map index, and `tile` is + * the tile instance for that location, or `null` if there is no instance + */ + inline public function forEachInRayI(start:FlxPoint, end:FlxPoint, func:(index:Int, tile:Null)->Void) + { + findIndexInRayI(start, end, voidFind(func)); + } + + /** + * Checks all tiles overlapping a ray from `start` to `end`, + * finds the first tile that satisfies to condition of `func` and returns its index + * + * **Note:** This skips any tiles with no instance + * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param func The condition, where `tile` is the tile instance for that location + * @param result Optional result vector, indicating where the ray hit the found tile + * @return The index of the found tile + */ + inline public function findIndexInRay(start:FlxPoint, end:FlxPoint, func:(Tile)->Bool, ?result:FlxPoint):Int + { + return findIndexInRayI(start, end, ignoreIndex(func), result); + } + + /** + * Checks all tile indices overlapping a ray from `start` to `end`, + * finds the first tile that satisfies to condition of `func` and returns its index + * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param func The condition, where `index` is the tile's map index, and `tile` is + * the tile instance for that location, or `null` if there is no instance + * @param result Optional result vector, indicating where the ray hit the found tile + * @return The index of the found tile + */ + public function findIndexInRayI(start:FlxPoint, end:FlxPoint, func:(index:Int, tile:Null)->Bool, ?result:FlxPoint):Int { // trim the line to the parts inside the map final trimmedStart = calcRayEntry(start, end); final trimmedEnd = calcRayExit(start, end); - + start.putWeak(); end.putWeak(); - + if (trimmedStart == null || trimmedEnd == null) { FlxDestroyUtil.put(trimmedStart); FlxDestroyUtil.put(trimmedEnd); - return true; + return -1; } - + start = trimmedStart; end = trimmedEnd; - + inline function clearRefs() { trimmedStart.put(); trimmedEnd.put(); } - + final startIndex = getMapIndex(start); final endIndex = getMapIndex(end); - + // If the starting tile is solid, return the starting position final tile = getTileData(startIndex); if (tile != null && tile.solid) { if (result != null) result.copyFrom(start); - + clearRefs(); - return false; + return startIndex; } - + final startTileX = getColumn(startIndex); final startTileY = getRow(startIndex); final endTileX = getColumn(endIndex); final endTileY = getRow(endIndex); var hitIndex = -1; - + if (start.x == end.x) { - hitIndex = checkColumn(startTileX, startTileY, endTileY); + hitIndex = findIndexInColumnI(startTileX, startTileY, endTileY, func); if (hitIndex != -1 && result != null) { // check the bottom @@ -969,28 +1029,28 @@ class FlxTypedTilemap extends FlxBaseTilemap final m = (start.y - end.y) / (start.x - end.x); // y - mx = b final b = start.y - m * start.x; - + final movesRight = start.x < end.x; final inc = movesRight ? 1 : -1; final offset = movesRight ? 1 : 0; var tileX = startTileX; var lastTileY = startTileY; - + while (tileX != endTileX) { final xPos = getColumnPos(tileX + offset); final yPos = m * getColumnPos(tileX + offset) + b; final tileY = getRowAt(yPos); - hitIndex = checkColumn(tileX, lastTileY, tileY); + hitIndex = findIndexInColumnI(tileX, lastTileY, tileY, func); if (hitIndex != -1) break; lastTileY = tileY; tileX += inc; } - + if (hitIndex == -1) - hitIndex = checkColumn(endTileX, lastTileY, endTileY); - + hitIndex = findIndexInColumnI(endTileX, lastTileY, endTileY, func); + if (hitIndex != -1 && result != null) { result.copyFrom(getTilePos(hitIndex)); @@ -998,9 +1058,9 @@ class FlxTypedTilemap extends FlxBaseTilemap { if (start.x > end.x) result.x += scaledTileWidth; - + // set result to left side - result.y = m * result.x + b;//mx + b + result.y = m * result.x + b; // mx + b } else { @@ -1011,44 +1071,160 @@ class FlxTypedTilemap extends FlxBaseTilemap result.y += scaledTileHeight; } // otherwise result is top - + // x = (y - b)/m result.x = (result.y - b) / m; } } } - + clearRefs(); - return hitIndex == -1; + return hitIndex; } - - function checkColumn(x:Int, startY:Int, endY:Int):Int + + /** + * Calls `func` on all tiles in the `column` between the specified `startRow` and `endRow` + * + * **Note:** This skips any tiles with no instance + * + * @param column The column to check + * @param startRow The row to check from + * @param endRow The row to check to + * @param func The function, where `tile` is the tile instance for that location + */ + inline public function forEachIndexInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Void) { - if (startY < 0) - startY = 0; - - if (endY < 0) - endY = 0; - - if (startY > heightInTiles - 1) - startY = heightInTiles - 1; + findIndexInColumnI(column, startRow, endRow, voidFindIgnoreIndex(func)); + } + + /** + * Calls `func` on all tiles in the `column` between the specified `startRow` and `endRow` + * + * @param column The column to check + * @param startRow The row to check from + * @param endRow The row to check to + * @param func The function, where `index` is the tile's map index, and `tile` is + * the tile instance for that location, or `null` if there is no instance + */ + inline public function forEachIndexInColumnI(column:Int, startRow:Int, endRow:Int, func:(index:Int, tile:Null)->Bool) + { + findIndexInColumnI(column, startRow, endRow, voidFind(func)); + } + + /** + * Helper to convert `(Int, Null)->Bool` to `(Tile>)->Void`. + * Checks null, ignores index, always returns false + */ + inline function voidFindIgnoreIndex(func:(Tile)->Void):(Int, Null)->Bool + { + return function (_, t) + { + if (t != null) + func(t); + + return false; + }; + } + + /** + * Helper to convert `(Int, Null)->Bool` to `(Int, Null)->Void`. + * Always returns false + */ + inline function voidFind(func:(Int, Null)->Void):(index:Int, tile:Null)->Bool + { + return (_, t)->{ func(_, t); return false; }; + } + + + /** + * Checks all tiles in the `column` between the specified `startRow` and `endRow`, + * Retrieves the first tile that satisfies to condition of `func` and returns it + * + * **Note:** This skips any tiles with no instance + * + * @param column The column to check + * @param startRow The row to check from + * @param endRow The row to check to + * @param func The condition, where `tile` is the tile instance for that location + * @return The found tile + */ + public function findInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Bool):Tile + { + final index = findIndexInColumnI(column, startRow, endRow, ignoreIndex(func)); + if (index < 0) + return null; - if (endY > heightInTiles - 1) - endY = heightInTiles - 1; + final data = getTileData(index); + if (data == null) + throw 'Unexpected null tile at $index'; // internal error - var y = startY; - final step = startY <= endY ? 1 : -1; + return data; + } + + /** + * Checks all tiles in the `column` between the specified `startRow` and `endRow`, + * finds the first tile that satisfies to condition of `func` and returns its index + * + * **Note:** This skips any tiles with no instance + * + * @param column The column to check + * @param startRow The row to check from + * @param endRow The row to check to + * @param func The condition, where `tile` is the tile instance for that location + * @return The index of the found tile + */ + inline public function findIndexInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Bool):Int + { + return findIndexInColumnI(column, startRow, endRow, ignoreIndex(func)); + } + + /** + * Helper to convert `(Int, Null)->Bool` to `(Tile)->Bool`. + * Checks null, ignores index + */ + inline function ignoreIndex(func:(Tile)->Bool):(Int, Null)->Bool + { + return (_, t)->t != null && func(t); + } + + /** + * Checks all tile indices in the `column` between the specified `startRow` and `endRow`, + * finds the first tile that satisfies to condition of `func` and returns its index + * + * @param column The column to check + * @param startRow The row to check from + * @param endRow The row to check to + * @param func The condition, where `index` is the tile's map index, and `tile` is + * the tile instance for that location, or `null` if there is no instance + * @return The index of the found tile + */ + public function findIndexInColumnI(column:Int, startRow:Int, endRow:Int, func:(index:Int, tile:Null)->Bool):Int + { + if (startRow < 0) + startRow = 0; + + if (endRow < 0) + endRow = 0; + + if (startRow > heightInTiles - 1) + startRow = heightInTiles - 1; + + if (endRow > heightInTiles - 1) + endRow = heightInTiles - 1; + + var row = startRow; + final step = startRow <= endRow ? 1 : -1; while (true) { - final index = getMapIndex(x, y); + final index = getMapIndex(column, row); final tile = getTileData(index); - if (tile != null && tile.solid) + if (func(index, tile)) return index; - - if (y == endY) + + if (row == endRow) break; - - y += step; + + row += step; } return -1; From 877a893197a5befb01f6fce8e3e9b6f2aa27d7ef Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 10 Jan 2025 13:36:51 -0600 Subject: [PATCH 02/16] move it all to the base class --- flixel/tile/FlxBaseTilemap.hx | 334 +++++++++++++++++++++++++++++++-- flixel/tile/FlxTilemap.hx | 336 ---------------------------------- 2 files changed, 319 insertions(+), 351 deletions(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index af98c2b069..e4569f05e7 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -6,6 +6,7 @@ import flixel.math.FlxPoint; import flixel.math.FlxRect; import flixel.path.FlxPathfinder; import flixel.system.FlxAssets; +import flixel.util.FlxDestroyUtil; import flixel.util.FlxArrayUtil; import flixel.util.FlxCollision; import flixel.util.FlxColor; @@ -207,18 +208,16 @@ class FlxBaseTilemap extends FlxObject * If/when it passes through a tile, it stores that point and returns false. * * **Note:** In flixel 5.0.0, this was redone, the old method is now `rayStep` - * - * @param start The world coordinates of the start of the ray. - * @param end The world coordinates of the end of the ray. - * @param result Optional result vector, to avoid creating a new instance to be returned. - * Only returned if the line enters the rect. + * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param result Optional result vector, indicating where the ray hit a wall * @return Returns true if the ray made it from Start to End without hitting anything. - * Returns false and fills Result if a tile was hit. + * Returns false and fills `result` if a tile was hit. */ public function ray(start:FlxPoint, end:FlxPoint, ?result:FlxPoint):Bool { throw "ray must be implemented"; - return false; } /** @@ -227,18 +226,323 @@ class FlxBaseTilemap extends FlxObject * * **Note:** In flixel 5.0.0, this was redone, the old method is now `rayStep` * - * @param start The world coordinates of the start of the ray. - * @param end The world coordinates of the end of the ray. - * @param result Optional result vector, to avoid creating a new instance to be returned. - * Only returned if the line enters the rect. - * @return Returns true if the ray made it from Start to End without hitting anything. - * Returns false and fills Result if a tile was hit. + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param func The condition, where `tile` is the tile instance for that location */ - public function forEachOverlappingRay(start:FlxPoint, end:FlxPoint, ?func:FlxPoint):Void + inline public function forEachInRay(start:FlxPoint, end:FlxPoint, ?func:(Tile)->Void):Void { - throw "ray must be implemented"; + findIndexInRayI(start, end, voidFindIgnoreIndex(func)); + } + + /** + * Calls `func` on all tiles overlapping a ray from `start` to `end` + * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param func The condition, where `index` is the tile's map index, and `tile` is + * the tile instance for that location, or `null` if there is no instance + */ + inline public function forEachInRayI(start:FlxPoint, end:FlxPoint, func:(index:Int, tile:Null) -> Void) + { + findIndexInRayI(start, end, voidFind(func)); + } + + /** + * Checks all tiles overlapping a ray from `start` to `end`, + * finds the first tile that satisfies to condition of `func` and returns its index + * + * **Note:** This skips any tiles with no instance + * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param func The condition, where `tile` is the tile instance for that location + * @param result Optional result vector, indicating where the ray hit the found tile + * @return The index of the found tile + */ + inline public function findIndexInRay(start:FlxPoint, end:FlxPoint, func:(Tile) -> Bool, ?result:FlxPoint):Int + { + return findIndexInRayI(start, end, ignoreIndex(func), result); + } + + /** + * Checks all tile indices overlapping a ray from `start` to `end`, + * finds the first tile that satisfies to condition of `func` and returns its index + * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param func The condition, where `index` is the tile's map index, and `tile` is + * the tile instance for that location, or `null` if there is no instance + * @param result Optional result vector, indicating where the ray hit the found tile + * @return The index of the found tile + */ + public function findIndexInRayI(start:FlxPoint, end:FlxPoint, func:(index:Int, tile:Null) -> Bool, ?result:FlxPoint):Int + { + // trim the line to the parts inside the map + final trimmedStart = calcRayEntry(start, end); + final trimmedEnd = calcRayExit(start, end); + + start.putWeak(); + end.putWeak(); + + if (trimmedStart == null || trimmedEnd == null) + { + FlxDestroyUtil.put(trimmedStart); + FlxDestroyUtil.put(trimmedEnd); + return -1; + } + + start = trimmedStart; + end = trimmedEnd; + + inline function clearRefs() + { + trimmedStart.put(); + trimmedEnd.put(); + } + + final startIndex = getMapIndex(start); + final endIndex = getMapIndex(end); + + // If the starting tile is solid, return the starting position + final tile = getTileData(startIndex); + if (tile != null && tile.solid) + { + if (result != null) + result.copyFrom(start); + + clearRefs(); + return startIndex; + } + + final startTileX = getColumn(startIndex); + final startTileY = getRow(startIndex); + final endTileX = getColumn(endIndex); + final endTileY = getRow(endIndex); + + final scaledTileWidth = getColumnPos(1) - getColumnPos(0); + final scaledTileHeight = getRowPos(1) - getRowPos(0); + var hitIndex = -1; + + if (start.x == end.x) + { + hitIndex = findIndexInColumnI(startTileX, startTileY, endTileY, func); + if (hitIndex != -1 && result != null) + { + // check the bottom + result.copyFrom(getTilePos(hitIndex)); + result.x = start.x; + if (start.y > end.y) + result.y += scaledTileHeight; + } + } + else + { + // Use y = mx + b formula + final m = (start.y - end.y) / (start.x - end.x); + // y - mx = b + final b = start.y - m * start.x; + + final movesRight = start.x < end.x; + final inc = movesRight ? 1 : -1; + final offset = movesRight ? 1 : 0; + var tileX = startTileX; + var lastTileY = startTileY; + + while (tileX != endTileX) + { + final xPos = getColumnPos(tileX + offset); + final yPos = m * getColumnPos(tileX + offset) + b; + final tileY = getRowAt(yPos); + hitIndex = findIndexInColumnI(tileX, lastTileY, tileY, func); + if (hitIndex != -1) + break; + lastTileY = tileY; + tileX += inc; + } + + if (hitIndex == -1) + hitIndex = findIndexInColumnI(endTileX, lastTileY, endTileY, func); + + if (hitIndex != -1 && result != null) + { + result.copyFrom(getTilePos(hitIndex)); + if (Std.int(hitIndex / widthInTiles) == lastTileY) + { + if (start.x > end.x) + result.x += scaledTileWidth; + + // set result to left side + result.y = m * result.x + b; // mx + b + } + else + { + // if ascending + if (start.y > end.y) + { + // change result to bottom + result.y += scaledTileHeight; + } + // otherwise result is top + + // x = (y - b)/m + result.x = (result.y - b) / m; + } + } + } + + clearRefs(); + return hitIndex; } + /** + * Calls `func` on all tiles in the `column` between the specified `startRow` and `endRow` + * + * **Note:** This skips any tiles with no instance + * + * @param column The column to check + * @param startRow The row to check from + * @param endRow The row to check to + * @param func The function, where `tile` is the tile instance for that location + */ + inline public function forEachIndexInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Void) + { + findIndexInColumnI(column, startRow, endRow, voidFindIgnoreIndex(func)); + } + + /** + * Calls `func` on all tiles in the `column` between the specified `startRow` and `endRow` + * + * @param column The column to check + * @param startRow The row to check from + * @param endRow The row to check to + * @param func The function, where `index` is the tile's map index, and `tile` is + * the tile instance for that location, or `null` if there is no instance + */ + inline public function forEachIndexInColumnI(column:Int, startRow:Int, endRow:Int, func:(index:Int, tile:Null)->Bool) + { + findIndexInColumnI(column, startRow, endRow, voidFind(func)); + } + + /** + * Helper to convert `(Int, Null)->Bool` to `(Tile>)->Void`. + * Checks null, ignores index, always returns false + */ + inline function voidFindIgnoreIndex(func:(Tile)->Void):(Int, Null)->Bool + { + return function (_, t) + { + if (t != null) + func(t); + + return false; + }; + } + + /** + * Helper to convert `(Int, Null)->Bool` to `(Int, Null)->Void`. + * Always returns false + */ + inline function voidFind(func:(Int, Null)->Void):(index:Int, tile:Null)->Bool + { + return (_, t)->{ func(_, t); return false; }; + } + + + /** + * Checks all tiles in the `column` between the specified `startRow` and `endRow`, + * Retrieves the first tile that satisfies to condition of `func` and returns it + * + * **Note:** This skips any tiles with no instance + * + * @param column The column to check + * @param startRow The row to check from + * @param endRow The row to check to + * @param func The condition, where `tile` is the tile instance for that location + * @return The found tile + */ + public function findInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Bool):Tile + { + final index = findIndexInColumnI(column, startRow, endRow, ignoreIndex(func)); + if (index < 0) + return null; + + final data = getTileData(index); + if (data == null) + throw 'Unexpected null tile at $index'; // internal error + + return data; + } + + /** + * Checks all tiles in the `column` between the specified `startRow` and `endRow`, + * finds the first tile that satisfies to condition of `func` and returns its index + * + * **Note:** This skips any tiles with no instance + * + * @param column The column to check + * @param startRow The row to check from + * @param endRow The row to check to + * @param func The condition, where `tile` is the tile instance for that location + * @return The index of the found tile + */ + inline public function findIndexInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Bool):Int + { + return findIndexInColumnI(column, startRow, endRow, ignoreIndex(func)); + } + + /** + * Helper to convert `(Int, Null)->Bool` to `(Tile)->Bool`. + * Checks null, ignores index + */ + inline function ignoreIndex(func:(Tile)->Bool):(Int, Null)->Bool + { + return (_, t)->t != null && func(t); + } + + /** + * Checks all tile indices in the `column` between the specified `startRow` and `endRow`, + * finds the first tile that satisfies to condition of `func` and returns its index + * + * @param column The column to check + * @param startRow The row to check from + * @param endRow The row to check to + * @param func The condition, where `index` is the tile's map index, and `tile` is + * the tile instance for that location, or `null` if there is no instance + * @return The index of the found tile + */ + public function findIndexInColumnI(column:Int, startRow:Int, endRow:Int, func:(index:Int, tile:Null)->Bool):Int + { + if (startRow < 0) + startRow = 0; + + if (endRow < 0) + endRow = 0; + + if (startRow > heightInTiles - 1) + startRow = heightInTiles - 1; + + if (endRow > heightInTiles - 1) + endRow = heightInTiles - 1; + + var row = startRow; + final step = startRow <= endRow ? 1 : -1; + while (true) + { + final index = getMapIndex(column, row); + final tile = getTileData(index); + if (func(index, tile)) + return index; + + if (row == endRow) + break; + + row += step; + } + + return -1; + } + /** * Shoots a ray from the start point to the end point. * If/when it passes through a tile, it stores that point and returns false. diff --git a/flixel/tile/FlxTilemap.hx b/flixel/tile/FlxTilemap.hx index c81dda1391..871656a6a4 100644 --- a/flixel/tile/FlxTilemap.hx +++ b/flixel/tile/FlxTilemap.hx @@ -894,342 +894,6 @@ class FlxTypedTilemap extends FlxBaseTilemap ); } - /** - * Shoots a ray from the start point to the end point. - * If/when it passes through a tile, it stores that point and returns false. - * - * **Note:** In flixel 5.0.0, this was redone, the old method is now `rayStep` - * - * @param start The world coordinates of the start of the ray - * @param end The world coordinates of the end of the ray - * @param result Optional result vector, indicating where the ray hit a wall - * @return Returns true if the ray made it from Start to End without hitting anything. - * Returns false and fills `result` if a tile was hit. - */ - override function ray(start:FlxPoint, end:FlxPoint, ?result:FlxPoint):Bool - { - return -1 < findIndexInRayI(start, end, (_, tile)->tile != null && tile.solid, result); - } - - /** - * Calls `func` on all tiles overlapping a ray from `start` to `end` - * - * **Note:** This skips any tiles with no instance - * - * @param start The world coordinates of the start of the ray - * @param end The world coordinates of the end of the ray - * @param func The function, where `tile` is the tile instance for that location - */ - inline public function forEachInRay(start:FlxPoint, end:FlxPoint, func:(Tile)->Void) - { - findIndexInRayI(start, end, voidFindIgnoreIndex(func)); - } - - /** - * Calls `func` on all tiles overlapping a ray from `start` to `end` - * - * @param start The world coordinates of the start of the ray - * @param end The world coordinates of the end of the ray - * @param func The condition, where `index` is the tile's map index, and `tile` is - * the tile instance for that location, or `null` if there is no instance - */ - inline public function forEachInRayI(start:FlxPoint, end:FlxPoint, func:(index:Int, tile:Null)->Void) - { - findIndexInRayI(start, end, voidFind(func)); - } - - /** - * Checks all tiles overlapping a ray from `start` to `end`, - * finds the first tile that satisfies to condition of `func` and returns its index - * - * **Note:** This skips any tiles with no instance - * - * @param start The world coordinates of the start of the ray - * @param end The world coordinates of the end of the ray - * @param func The condition, where `tile` is the tile instance for that location - * @param result Optional result vector, indicating where the ray hit the found tile - * @return The index of the found tile - */ - inline public function findIndexInRay(start:FlxPoint, end:FlxPoint, func:(Tile)->Bool, ?result:FlxPoint):Int - { - return findIndexInRayI(start, end, ignoreIndex(func), result); - } - - /** - * Checks all tile indices overlapping a ray from `start` to `end`, - * finds the first tile that satisfies to condition of `func` and returns its index - * - * @param start The world coordinates of the start of the ray - * @param end The world coordinates of the end of the ray - * @param func The condition, where `index` is the tile's map index, and `tile` is - * the tile instance for that location, or `null` if there is no instance - * @param result Optional result vector, indicating where the ray hit the found tile - * @return The index of the found tile - */ - public function findIndexInRayI(start:FlxPoint, end:FlxPoint, func:(index:Int, tile:Null)->Bool, ?result:FlxPoint):Int - { - // trim the line to the parts inside the map - final trimmedStart = calcRayEntry(start, end); - final trimmedEnd = calcRayExit(start, end); - - start.putWeak(); - end.putWeak(); - - if (trimmedStart == null || trimmedEnd == null) - { - FlxDestroyUtil.put(trimmedStart); - FlxDestroyUtil.put(trimmedEnd); - return -1; - } - - start = trimmedStart; - end = trimmedEnd; - - inline function clearRefs() - { - trimmedStart.put(); - trimmedEnd.put(); - } - - final startIndex = getMapIndex(start); - final endIndex = getMapIndex(end); - - // If the starting tile is solid, return the starting position - final tile = getTileData(startIndex); - if (tile != null && tile.solid) - { - if (result != null) - result.copyFrom(start); - - clearRefs(); - return startIndex; - } - - final startTileX = getColumn(startIndex); - final startTileY = getRow(startIndex); - final endTileX = getColumn(endIndex); - final endTileY = getRow(endIndex); - var hitIndex = -1; - - if (start.x == end.x) - { - hitIndex = findIndexInColumnI(startTileX, startTileY, endTileY, func); - if (hitIndex != -1 && result != null) - { - // check the bottom - result.copyFrom(getTilePos(hitIndex)); - result.x = start.x; - if (start.y > end.y) - result.y += scaledTileHeight; - } - } - else - { - // Use y = mx + b formula - final m = (start.y - end.y) / (start.x - end.x); - // y - mx = b - final b = start.y - m * start.x; - - final movesRight = start.x < end.x; - final inc = movesRight ? 1 : -1; - final offset = movesRight ? 1 : 0; - var tileX = startTileX; - var lastTileY = startTileY; - - while (tileX != endTileX) - { - final xPos = getColumnPos(tileX + offset); - final yPos = m * getColumnPos(tileX + offset) + b; - final tileY = getRowAt(yPos); - hitIndex = findIndexInColumnI(tileX, lastTileY, tileY, func); - if (hitIndex != -1) - break; - lastTileY = tileY; - tileX += inc; - } - - if (hitIndex == -1) - hitIndex = findIndexInColumnI(endTileX, lastTileY, endTileY, func); - - if (hitIndex != -1 && result != null) - { - result.copyFrom(getTilePos(hitIndex)); - if (Std.int(hitIndex / widthInTiles) == lastTileY) - { - if (start.x > end.x) - result.x += scaledTileWidth; - - // set result to left side - result.y = m * result.x + b; // mx + b - } - else - { - // if ascending - if (start.y > end.y) - { - // change result to bottom - result.y += scaledTileHeight; - } - // otherwise result is top - - // x = (y - b)/m - result.x = (result.y - b) / m; - } - } - } - - clearRefs(); - return hitIndex; - } - - /** - * Calls `func` on all tiles in the `column` between the specified `startRow` and `endRow` - * - * **Note:** This skips any tiles with no instance - * - * @param column The column to check - * @param startRow The row to check from - * @param endRow The row to check to - * @param func The function, where `tile` is the tile instance for that location - */ - inline public function forEachIndexInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Void) - { - findIndexInColumnI(column, startRow, endRow, voidFindIgnoreIndex(func)); - } - - /** - * Calls `func` on all tiles in the `column` between the specified `startRow` and `endRow` - * - * @param column The column to check - * @param startRow The row to check from - * @param endRow The row to check to - * @param func The function, where `index` is the tile's map index, and `tile` is - * the tile instance for that location, or `null` if there is no instance - */ - inline public function forEachIndexInColumnI(column:Int, startRow:Int, endRow:Int, func:(index:Int, tile:Null)->Bool) - { - findIndexInColumnI(column, startRow, endRow, voidFind(func)); - } - - /** - * Helper to convert `(Int, Null)->Bool` to `(Tile>)->Void`. - * Checks null, ignores index, always returns false - */ - inline function voidFindIgnoreIndex(func:(Tile)->Void):(Int, Null)->Bool - { - return function (_, t) - { - if (t != null) - func(t); - - return false; - }; - } - - /** - * Helper to convert `(Int, Null)->Bool` to `(Int, Null)->Void`. - * Always returns false - */ - inline function voidFind(func:(Int, Null)->Void):(index:Int, tile:Null)->Bool - { - return (_, t)->{ func(_, t); return false; }; - } - - - /** - * Checks all tiles in the `column` between the specified `startRow` and `endRow`, - * Retrieves the first tile that satisfies to condition of `func` and returns it - * - * **Note:** This skips any tiles with no instance - * - * @param column The column to check - * @param startRow The row to check from - * @param endRow The row to check to - * @param func The condition, where `tile` is the tile instance for that location - * @return The found tile - */ - public function findInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Bool):Tile - { - final index = findIndexInColumnI(column, startRow, endRow, ignoreIndex(func)); - if (index < 0) - return null; - - final data = getTileData(index); - if (data == null) - throw 'Unexpected null tile at $index'; // internal error - - return data; - } - - /** - * Checks all tiles in the `column` between the specified `startRow` and `endRow`, - * finds the first tile that satisfies to condition of `func` and returns its index - * - * **Note:** This skips any tiles with no instance - * - * @param column The column to check - * @param startRow The row to check from - * @param endRow The row to check to - * @param func The condition, where `tile` is the tile instance for that location - * @return The index of the found tile - */ - inline public function findIndexInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Bool):Int - { - return findIndexInColumnI(column, startRow, endRow, ignoreIndex(func)); - } - - /** - * Helper to convert `(Int, Null)->Bool` to `(Tile)->Bool`. - * Checks null, ignores index - */ - inline function ignoreIndex(func:(Tile)->Bool):(Int, Null)->Bool - { - return (_, t)->t != null && func(t); - } - - /** - * Checks all tile indices in the `column` between the specified `startRow` and `endRow`, - * finds the first tile that satisfies to condition of `func` and returns its index - * - * @param column The column to check - * @param startRow The row to check from - * @param endRow The row to check to - * @param func The condition, where `index` is the tile's map index, and `tile` is - * the tile instance for that location, or `null` if there is no instance - * @return The index of the found tile - */ - public function findIndexInColumnI(column:Int, startRow:Int, endRow:Int, func:(index:Int, tile:Null)->Bool):Int - { - if (startRow < 0) - startRow = 0; - - if (endRow < 0) - endRow = 0; - - if (startRow > heightInTiles - 1) - startRow = heightInTiles - 1; - - if (endRow > heightInTiles - 1) - endRow = heightInTiles - 1; - - var row = startRow; - final step = startRow <= endRow ? 1 : -1; - while (true) - { - final index = getMapIndex(column, row); - final tile = getTileData(index); - if (func(index, tile)) - return index; - - if (row == endRow) - break; - - row += step; - } - - return -1; - } - /** * Shoots a ray from the start point to the end point. * If/when it passes through a tile, it stores that point and returns false. From 44f71d8360c291583f279164977dca60065c8bcb Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 10 Jan 2025 14:14:59 -0600 Subject: [PATCH 03/16] move more stuff to base, add getTileWidth/height --- flixel/tile/FlxBaseTilemap.hx | 82 +++++++++++++++++++++++++---------- flixel/tile/FlxTilemap.hx | 28 ++---------- 2 files changed, 64 insertions(+), 46 deletions(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index e4569f05e7..eaf448086d 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -146,7 +146,12 @@ class FlxBaseTilemap extends FlxObject */ public function getColumnAt(worldX:Float, bind = false):Int { - throw "getColumnAt must be implemented"; + final result = Math.floor((worldX - x) / getTileWidth()); + + if (bind) + return result < 0 ? 0 : (result >= widthInTiles ? widthInTiles - 1 : result); + + return result; } /** @@ -159,7 +164,12 @@ class FlxBaseTilemap extends FlxObject */ public function getRowAt(worldY:Float, bind = false):Int { - throw "getRowAt must be implemented"; + final result = Math.floor((worldY - y) / getTileWidth()); + + if (bind) + return result < 0 ? 0 : (result >= heightInTiles ? heightInTiles - 1 : result); + + return result; } /** @@ -169,11 +179,11 @@ class FlxBaseTilemap extends FlxObject * @param midpoint Whether to use the tile's midpoint, or upper left corner * @since 5.9.0 */ - public function getColumnPos(column:Float, midPoint = false):Float + public function getColumnPos(column:Int, midpoint = false):Float { - throw "getColumnPos must be implemented"; + return x + column * getTileWidth() + (midpoint ? getTileWidth() * 0.5 : 0); } - + /** * Get the world position of the specified row * @@ -181,9 +191,29 @@ class FlxBaseTilemap extends FlxObject * @param midpoint Whether to use the tile's midpoint, or upper left corner * @since 5.9.0 */ - public function getRowPos(row:Int, midPoint = false):Float + public function getRowPos(row:Int, midpoint = false):Float + { + return y + row * getTileHeight() + (midpoint ? getTileHeight() * 0.5 : 0); + } + + /** + * Get the size of a column, in world coordinates + * + * @since 5.10.0 + */ + public function getTileWidth():Float { - throw "getRowPos must be implemented"; + throw "getTileWidth must be implemented"; + } + + /** + * Get the size of a column, in world coordinates + * + * @since 5.10.0 + */ + public function getTileHeight():Float + { + throw "getTileHeight must be implemented"; } /** @@ -204,31 +234,31 @@ class FlxBaseTilemap extends FlxObject } /** - * Shoots a ray from the start point to the end point. - * If/when it passes through a tile, it stores that point and returns false. + * Determines whether the ray can travel from `start` to `end` without hitting a solid wall * * **Note:** In flixel 5.0.0, this was redone, the old method is now `rayStep` * * @param start The world coordinates of the start of the ray * @param end The world coordinates of the end of the ray - * @param result Optional result vector, indicating where the ray hit a wall - * @return Returns true if the ray made it from Start to End without hitting anything. - * Returns false and fills `result` if a tile was hit. + * @param result Optional result vector, indicating where the ray hit the first wall + * @return Whether the ray can travel from `start` to `end` without hitting a solid wall */ public function ray(start:FlxPoint, end:FlxPoint, ?result:FlxPoint):Bool { - throw "ray must be implemented"; + // TODO: only check for collisions in the direction needed + // This requires findIndexInRayI to pass intersection data back + return findIndexInRayI(start, end, (_, t)->t != null && t.solid, result); } /** - * Shoots a ray from the start point to the end point. - * If/when it passes through a tile, it stores that point and returns false. + * Calls `func` on all tiles overlapping a ray from `start` to `end` * * **Note:** In flixel 5.0.0, this was redone, the old method is now `rayStep` * * @param start The world coordinates of the start of the ray * @param end The world coordinates of the end of the ray - * @param func The condition, where `tile` is the tile instance for that location + * @param func The function, where `tile` is the tile instance for that location + * @since 5.10.0 */ inline public function forEachInRay(start:FlxPoint, end:FlxPoint, ?func:(Tile)->Void):Void { @@ -240,8 +270,9 @@ class FlxBaseTilemap extends FlxObject * * @param start The world coordinates of the start of the ray * @param end The world coordinates of the end of the ray - * @param func The condition, where `index` is the tile's map index, and `tile` is + * @param func The function, where `index` is the tile's map index, and `tile` is * the tile instance for that location, or `null` if there is no instance + * @since 5.10.0 */ inline public function forEachInRayI(start:FlxPoint, end:FlxPoint, func:(index:Int, tile:Null) -> Void) { @@ -259,6 +290,7 @@ class FlxBaseTilemap extends FlxObject * @param func The condition, where `tile` is the tile instance for that location * @param result Optional result vector, indicating where the ray hit the found tile * @return The index of the found tile + * @since 5.10.0 */ inline public function findIndexInRay(start:FlxPoint, end:FlxPoint, func:(Tile) -> Bool, ?result:FlxPoint):Int { @@ -275,6 +307,7 @@ class FlxBaseTilemap extends FlxObject * the tile instance for that location, or `null` if there is no instance * @param result Optional result vector, indicating where the ray hit the found tile * @return The index of the found tile + * @since 5.10.0 */ public function findIndexInRayI(start:FlxPoint, end:FlxPoint, func:(index:Int, tile:Null) -> Bool, ?result:FlxPoint):Int { @@ -320,8 +353,8 @@ class FlxBaseTilemap extends FlxObject final endTileX = getColumn(endIndex); final endTileY = getRow(endIndex); - final scaledTileWidth = getColumnPos(1) - getColumnPos(0); - final scaledTileHeight = getRowPos(1) - getRowPos(0); + final tileWidth = getTileWidth(); + final tileHeight = getTileHeight(); var hitIndex = -1; if (start.x == end.x) @@ -333,7 +366,7 @@ class FlxBaseTilemap extends FlxObject result.copyFrom(getTilePos(hitIndex)); result.x = start.x; if (start.y > end.y) - result.y += scaledTileHeight; + result.y += tileHeight; } } else @@ -370,7 +403,7 @@ class FlxBaseTilemap extends FlxObject if (Std.int(hitIndex / widthInTiles) == lastTileY) { if (start.x > end.x) - result.x += scaledTileWidth; + result.x += tileWidth; // set result to left side result.y = m * result.x + b; // mx + b @@ -381,7 +414,7 @@ class FlxBaseTilemap extends FlxObject if (start.y > end.y) { // change result to bottom - result.y += scaledTileHeight; + result.y += tileHeight; } // otherwise result is top @@ -404,6 +437,7 @@ class FlxBaseTilemap extends FlxObject * @param startRow The row to check from * @param endRow The row to check to * @param func The function, where `tile` is the tile instance for that location + * @since 5.10.0 */ inline public function forEachIndexInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Void) { @@ -418,6 +452,7 @@ class FlxBaseTilemap extends FlxObject * @param endRow The row to check to * @param func The function, where `index` is the tile's map index, and `tile` is * the tile instance for that location, or `null` if there is no instance + * @since 5.10.0 */ inline public function forEachIndexInColumnI(column:Int, startRow:Int, endRow:Int, func:(index:Int, tile:Null)->Bool) { @@ -460,6 +495,7 @@ class FlxBaseTilemap extends FlxObject * @param endRow The row to check to * @param func The condition, where `tile` is the tile instance for that location * @return The found tile + * @since 5.10.0 */ public function findInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Bool):Tile { @@ -485,6 +521,7 @@ class FlxBaseTilemap extends FlxObject * @param endRow The row to check to * @param func The condition, where `tile` is the tile instance for that location * @return The index of the found tile + * @since 5.10.0 */ inline public function findIndexInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Bool):Int { @@ -510,6 +547,7 @@ class FlxBaseTilemap extends FlxObject * @param func The condition, where `index` is the tile's map index, and `tile` is * the tile instance for that location, or `null` if there is no instance * @return The index of the found tile + * @since 5.10.0 */ public function findIndexInColumnI(column:Int, startRow:Int, endRow:Int, func:(index:Int, tile:Null)->Bool):Int { diff --git a/flixel/tile/FlxTilemap.hx b/flixel/tile/FlxTilemap.hx index 871656a6a4..4e829ded5f 100644 --- a/flixel/tile/FlxTilemap.hx +++ b/flixel/tile/FlxTilemap.hx @@ -830,36 +830,16 @@ class FlxTypedTilemap extends FlxBaseTilemap return results; } - override function getColumnAt(worldX:Float, bind = false):Int + override function getTileWidth() { - final result = Math.floor((worldX - x) / scaledTileWidth); - - if (bind) - return result < 0 ? 0 : (result >= widthInTiles ? widthInTiles - 1 : result); - - return result; + return scaledTileWidth; } - override function getRowAt(worldY:Float, bind = false):Int + override function getTileHeight() { - final result = Math.floor((worldY - y) / scaledTileHeight); - - if (bind) - return result < 0 ? 0 : (result >= heightInTiles ? heightInTiles -1 : result); - - return result; + return scaledTileHeight; } - override function getColumnPos(column:Float, midpoint = false):Float - { - return x + column * scaledTileWidth + (midpoint ? scaledTileWidth * 0.5 : 0); - } - - override function getRowPos(row:Int, midpoint = false):Float - { - return y + row * scaledTileHeight + (midpoint ? scaledTileHeight * 0.5 : 0); - } - /** * Returns a new array full of every coordinate of the requested tile type. * From 4d51bd9381a5f8f208f4ddb0795cdcb1ffdb73c1 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 10 Jan 2025 17:25:37 -0600 Subject: [PATCH 04/16] fix typo --- flixel/tile/FlxBaseTilemap.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index eaf448086d..b41d113c07 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -247,7 +247,7 @@ class FlxBaseTilemap extends FlxObject { // TODO: only check for collisions in the direction needed // This requires findIndexInRayI to pass intersection data back - return findIndexInRayI(start, end, (_, t)->t != null && t.solid, result); + return -1 < findIndexInRayI(start, end, (_, t)->t != null && t.solid, result); } /** From c929076db15d98ffbcf685ad1027abcaac171d46 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 10 Jan 2025 17:52:08 -0600 Subject: [PATCH 05/16] add more posAt helpers --- flixel/tile/FlxBaseTilemap.hx | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index b41d113c07..d5f2121729 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -196,6 +196,30 @@ class FlxBaseTilemap extends FlxObject return y + row * getTileHeight() + (midpoint ? getTileHeight() * 0.5 : 0); } + /** + * Get the world position of the column at the specified location + * + * @param worldX An X coordinate in the world + * @param midpoint Whether to use the tile's midpoint, or left edge + * @since 5.10.0 + */ + public function getColumnPosAt(worldX:Float, midpoint = false):Float + { + return getColumnPos(getColumnAt(worldX), midPoint); + } + + /** + * Get the world position of the row at the specified location + * + * @param worldY An X coordinate in the world + * @param midpoint Whether to use the tile's midpoint, or upper edge + * @since 5.10.0 + */ + public function getRowPosAt(worldY:Float, midpoint = false):Float + { + return getRowPos(getRowAt(worldY), midPoint); + } + /** * Get the size of a column, in world coordinates * From 8702aa6d26e9214f2575b6eb4db5bd9967fb0e7d Mon Sep 17 00:00:00 2001 From: George FunBook Date: Fri, 10 Jan 2025 17:53:37 -0600 Subject: [PATCH 06/16] D'OH --- flixel/tile/FlxBaseTilemap.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index d5f2121729..05ea3d0bc0 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -205,7 +205,7 @@ class FlxBaseTilemap extends FlxObject */ public function getColumnPosAt(worldX:Float, midpoint = false):Float { - return getColumnPos(getColumnAt(worldX), midPoint); + return getColumnPos(getColumnAt(worldX), midpoint); } /** @@ -217,7 +217,7 @@ class FlxBaseTilemap extends FlxObject */ public function getRowPosAt(worldY:Float, midpoint = false):Float { - return getRowPos(getRowAt(worldY), midPoint); + return getRowPos(getRowAt(worldY), midpoint); } /** From ff04f6983e4f34e6c85b95261291f4be6e4786d6 Mon Sep 17 00:00:00 2001 From: George FunBook Date: Mon, 13 Jan 2025 12:27:42 -0600 Subject: [PATCH 07/16] add FlxRayResult and many helpers --- flixel/tile/FlxBaseTilemap.hx | 731 ++++++++++++++++++++++++++-------- 1 file changed, 554 insertions(+), 177 deletions(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index 05ea3d0bc0..1e7e8c3915 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -10,11 +10,14 @@ import flixel.util.FlxDestroyUtil; import flixel.util.FlxArrayUtil; import flixel.util.FlxCollision; import flixel.util.FlxColor; +import flixel.util.FlxDirection; import flixel.util.FlxDirectionFlags; import flixel.util.FlxStringUtil; import openfl.display.BitmapData; using StringTools; +using flixel.tile.FlxBaseTilemap.RayTools; +using flixel.tile.FlxBaseTilemap.AmbiIntIterator; @:autoBuild(flixel.system.macros.FlxMacroUtil.deprecateOverride("overlapsWithCallback", "overlapsWithCallback is deprecated, use objectOverlapsTiles")) class FlxBaseTilemap extends FlxObject @@ -257,36 +260,97 @@ class FlxBaseTilemap extends FlxObject return getTilePos(mapIndex, midpoint); } + // ============================================================================= + //{ region Ray + Helpers + // ============================================================================= + /** - * Determines whether the ray can travel from `start` to `end` without hitting a solid wall + * Determines whether the ray can travel from `start` to `end` without hitting a solid wall. * * **Note:** In flixel 5.0.0, this was redone, the old method is now `rayStep` * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param result Optional result vector, indicating where the ray hit the first wall + * @param checkDir Whether this method honors the tiles' `allowCollisions` directions + * @return Whether the ray can travel from `start` to `end` without hitting a wall + */ + public function ray(start:FlxPoint, end:FlxPoint, ?result:FlxPoint, checkDir = false):Bool + { + final func = checkDir ? checkRayDirHelper(start, end) : (_, t:Tile, _)->t != null && t.solid; + switch findInRayHelper(start, end, func) + { + case END: + return true; + + case STOPPED(index, x, y, entry): + if (result != null) + result.set(x, y); + + return false; + } + } + + /** + * Ray func helper, checks tiles' directions compared to the ray direction + */ + function checkRayDirHelper(start:FlxPoint, end:FlxPoint):FindRayFuncI + { + return function (i:Int, tile:Null, entry:FlxRayEntry) + { + return tile != null && switch entry + { + case EDGE(dir): + tile.allowCollisions.has(dir); + + case START: tile.allowCollisions == ANY + || (tile.allowCollisions.left && start.x < end.x) + || (tile.allowCollisions.right && start.x > end.x) + || (tile.allowCollisions.up && start.y < end.y) + || (tile.allowCollisions.down && start.y > end.y); + } + }; + } + + /** + * Calls `func` on all tiles overlapping a ray from `start` to `end` + * * @param start The world coordinates of the start of the ray * @param end The world coordinates of the end of the ray - * @param result Optional result vector, indicating where the ray hit the first wall - * @return Whether the ray can travel from `start` to `end` without hitting a solid wall + * @param func The function, where `tile` is the tile data at that location + * @since 5.10.0 */ - public function ray(start:FlxPoint, end:FlxPoint, ?result:FlxPoint):Bool + inline overload extern public function forEachInRay(start, end, func:(Tile)->Void) { - // TODO: only check for collisions in the direction needed - // This requires findIndexInRayI to pass intersection data back - return -1 < findIndexInRayI(start, end, (_, t)->t != null && t.solid, result); + findIndexInRayHelper(start, end, func.toFindIE()); } - + /** * Calls `func` on all tiles overlapping a ray from `start` to `end` * - * **Note:** In flixel 5.0.0, this was redone, the old method is now `rayStep` + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param func The function, where `index` is the tile's map index, and `tile` is + * the tile data at that location, if one exists + * @since 5.10.0 + */ + inline overload extern public function forEachInRay(start, end, func:(index:Int, tile:Null)->Bool) + { + findIndexInRayHelper(start, end, func.toFindIE()); + } + + /** + * Calls `func` on all tiles overlapping a ray from `start` to `end` * * @param start The world coordinates of the start of the ray * @param end The world coordinates of the end of the ray - * @param func The function, where `tile` is the tile instance for that location + * @param func The function, where `tile` is the tile data at that location and + * `entry` is how the ray entered the tile * @since 5.10.0 */ - inline public function forEachInRay(start:FlxPoint, end:FlxPoint, ?func:(Tile)->Void):Void + inline overload extern public function forEachInRay(start, end, func:(tile:Tile, entry:FlxRayEntry)->Void) { - findIndexInRayI(start, end, voidFindIgnoreIndex(func)); + findIndexInRayHelper(start, end, func.toFindIE()); } /** @@ -294,13 +358,13 @@ class FlxBaseTilemap extends FlxObject * * @param start The world coordinates of the start of the ray * @param end The world coordinates of the end of the ray - * @param func The function, where `index` is the tile's map index, and `tile` is - * the tile instance for that location, or `null` if there is no instance + * @param func The function, where `index` is the tile's map index, `tile` is the tile data + * at that location, if one exists and `entry` is how the ray entered the tile * @since 5.10.0 */ - inline public function forEachInRayI(start:FlxPoint, end:FlxPoint, func:(index:Int, tile:Null) -> Void) + inline overload extern public function forEachInRay(start, end, func:(index:Int, tile:Null, entry:FlxRayEntry)->Bool) { - findIndexInRayI(start, end, voidFind(func)); + findIndexInRayHelper(start, end, func.toFindIE()); } /** @@ -311,14 +375,13 @@ class FlxBaseTilemap extends FlxObject * * @param start The world coordinates of the start of the ray * @param end The world coordinates of the end of the ray - * @param func The condition, where `tile` is the tile instance for that location - * @param result Optional result vector, indicating where the ray hit the found tile + * @param func The stopping condition, where `tile` is the tile data at that location * @return The index of the found tile * @since 5.10.0 */ - inline public function findIndexInRay(start:FlxPoint, end:FlxPoint, func:(Tile) -> Bool, ?result:FlxPoint):Int + inline overload extern public function findIndexInRay(start, end, func:(Tile)->Bool) { - return findIndexInRayI(start, end, ignoreIndex(func), result); + return findIndexInRayHelper(start, end, func.toFindIE()); } /** @@ -327,13 +390,99 @@ class FlxBaseTilemap extends FlxObject * * @param start The world coordinates of the start of the ray * @param end The world coordinates of the end of the ray - * @param func The condition, where `index` is the tile's map index, and `tile` is - * the tile instance for that location, or `null` if there is no instance - * @param result Optional result vector, indicating where the ray hit the found tile + * @param func The stopping condition, where `index` is the tile's map index, `tile` is the + * tile data at that location, if one exists * @return The index of the found tile * @since 5.10.0 */ - public function findIndexInRayI(start:FlxPoint, end:FlxPoint, func:(index:Int, tile:Null) -> Bool, ?result:FlxPoint):Int + inline overload extern public function findIndexInRay + (start, end, func:(index:Int, tile:Null)->Bool) + { + return findIndexInRayHelper(start, end, func.toFindIE()); + } + + /** + * Checks all tile indices overlapping a ray from `start` to `end`, + * finds the first tile that satisfies to condition of `func` and returns its index + * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param func The stopping condition, where `index` is the tile's map index, `tile` is the tile data + * at that location, if one exists, `entry` is how the ray entered the tile + * @return The index of the found tile + * @since 5.10.0 + */ + inline overload extern public function findIndexInRay + (start, end, func:(index:Int, tile:Null, entry:FlxRayEntry)->Bool) + { + return findIndexInRayHelper(start, end, func); + } + + function findIndexInRayHelper + (start, end, func:(index:Int, tile:Null, entry:FlxRayEntry)->Bool):Int + { + switch findInRayHelper(start, end, func) + { + case END: + return -1; + case STOPPED(mapIndex, _, _, _): + return mapIndex; + } + } + + /** + * Checks all tiles overlapping a ray from `start` to `end`, + * finds the first tile that satisfies to condition of `func` + * + * **Note:** This skips any tiles with no instance + * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param func The stopping condition, where `tile` is the tile data at that location + * @return The result of the ray, whether it reached the end or was stopped, and where + * @since 5.10.0 + */ + inline overload extern public function findInRay(start, end, func:(Tile)->Bool) + { + return findInRayHelper(start, end, func.toFindIE()); + } + + /** + * Checks all tile indices overlapping a ray from `start` to `end`, + * finds the first tile that satisfies to condition of `func` + * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param func The stopping condition, where `index` is the tile's map index, `tile` is the + * tile data at that location, if one exists + * @return The result of the ray, whether it reached the end or was stopped, and where + * @since 5.10.0 + */ + inline overload extern public function findInRay + (start, end, func:(index:Int, tile:Null)->Bool) + { + return findInRayHelper(start, end, func.toFindIE()); + } + + /** + * Checks all tile indices overlapping a ray from `start` to `end`, + * finds the first tile that satisfies to condition of `func` + * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param func The stopping condition, where `index` is the tile's map index, `tile` is the tile data + * at that location, if one exists, `entry` is how the ray entered the tile + * @return The result of the ray, whether it reached the end or was stopped, and where + * @since 5.10.0 + */ + inline overload extern public function findInRay + (start, end, func:(index:Int, tile:Null, entry:FlxRayEntry)->Bool) + { + return findInRayHelper(start, end, func); + } + + function findInRayHelper + (start:FlxPoint, end:FlxPoint, func:(index:Int, tile:Null, entry:FlxRayEntry)->Bool):FlxRayResult { // trim the line to the parts inside the map final trimmedStart = calcRayEntry(start, end); @@ -342,114 +491,134 @@ class FlxBaseTilemap extends FlxObject start.putWeak(); end.putWeak(); - if (trimmedStart == null || trimmedEnd == null) - { - FlxDestroyUtil.put(trimmedStart); - FlxDestroyUtil.put(trimmedEnd); - return -1; - } - - start = trimmedStart; - end = trimmedEnd; - - inline function clearRefs() - { - trimmedStart.put(); - trimmedEnd.put(); - } - - final startIndex = getMapIndex(start); - final endIndex = getMapIndex(end); + if (trimmedStart == null && trimmedEnd == null) + return END; - // If the starting tile is solid, return the starting position - final tile = getTileData(startIndex); - if (tile != null && tile.solid) - { - if (result != null) - result.copyFrom(start); - - clearRefs(); - return startIndex; - } + // Cache x/y in floats so we can put() them now + final wasStartTrimmed = trimmedStart.x != start.x || trimmedStart.y != start.y; + final startX = trimmedStart.x; + final startY = trimmedStart.y; + final endX = trimmedEnd.x; + final endY = trimmedEnd.y; + trimmedStart.put(); + trimmedEnd.put(); + final startIndex = getMapIndexAt(startX, startY); + final endIndex = getMapIndexAt(endX, endY); final startTileX = getColumn(startIndex); final startTileY = getRow(startIndex); final endTileX = getColumn(endIndex); final endTileY = getRow(endIndex); - final tileWidth = getTileWidth(); - final tileHeight = getTileHeight(); - var hitIndex = -1; - + // handle vertical line (infinite slope), first if (start.x == end.x) { - hitIndex = findIndexInColumnI(startTileX, startTileY, endTileY, func); - if (hitIndex != -1 && result != null) + // did the ray start here or from outside the map + final entry = wasStartTrimmed ? START : EDGE(start.y < end.y ? UP : DOWN); + final result = findIndexInColumnWithEntry(startTileX, startTileY, endTileY, func, entry); + if (result != null) { // check the bottom - result.copyFrom(getTilePos(hitIndex)); - result.x = start.x; - if (start.y > end.y) - result.y += tileHeight; + final resultX = start.x; + final resultY = getRowPos(getRow(result.index + (start.y > end.y ? 1 : 0))); + return STOPPED(result.index, resultX, resultY, result.entry); } + + return END; } - else + + // Use y = mx + b formula + final m = (start.y - end.y) / (start.x - end.x); + // y - mx = b + final b = start.y - m * start.x; + + final movesRight = start.x < end.x; + final inc = movesRight ? 1 : -1; + final offset = movesRight ? 1 : 0; + var entry = wasStartTrimmed ? START : EDGE(movesRight ? LEFT : RIGHT); + var lastTileY = startTileY; + + for (tileX in startTileX.iter(endTileX)) { - // Use y = mx + b formula - final m = (start.y - end.y) / (start.x - end.x); - // y - mx = b - final b = start.y - m * start.x; - - final movesRight = start.x < end.x; - final inc = movesRight ? 1 : -1; - final offset = movesRight ? 1 : 0; - var tileX = startTileX; - var lastTileY = startTileY; - - while (tileX != endTileX) - { - final xPos = getColumnPos(tileX + offset); - final yPos = m * getColumnPos(tileX + offset) + b; - final tileY = getRowAt(yPos); - hitIndex = findIndexInColumnI(tileX, lastTileY, tileY, func); - if (hitIndex != -1) - break; - lastTileY = tileY; - tileX += inc; - } + final xPos = getColumnPos(tileX + offset); + final yPos = ambiClamp(m * getColumnPos(tileX + offset) + b, startY, endY); + final tileY = getRowAt(yPos); + final result = findIndexInColumnWithEntry(tileX, lastTileY, tileY, func, entry); + if (result != null) + return calcRayResult(result, m, b, start); - if (hitIndex == -1) - hitIndex = findIndexInColumnI(endTileX, lastTileY, endTileY, func); - - if (hitIndex != -1 && result != null) - { - result.copyFrom(getTilePos(hitIndex)); - if (Std.int(hitIndex / widthInTiles) == lastTileY) - { - if (start.x > end.x) - result.x += tileWidth; - - // set result to left side - result.y = m * result.x + b; // mx + b - } - else - { - // if ascending - if (start.y > end.y) - { - // change result to bottom - result.y += tileHeight; - } - // otherwise result is top - - // x = (y - b)/m - result.x = (result.y - b) / m; - } - } + entry = EDGE(movesRight ? LEFT : RIGHT); + lastTileY = tileY; } - clearRefs(); - return hitIndex; + return END; + } + + /** + * Helper to clamp between to values, regardless of which is smaller + */ + function ambiClamp(value:Float, a:Float, b:Float):Float + { + if (a > b) + return ambiClamp(value, b, a); + + if (value < a) + return a; + + if (value > b) + return b; + + return value; + } + + /** + * Helper for retriving ray entry results + */ + static final _rayResultHelper = new RayResultHelper(); + /** + * Helper to add an `entry` to `findIndexInColumn` callbacks + * + * @param column The column to check + * @param startRow The row to check from + * @param endRow The row to check to + * @param func The function, where `tile` is the tile data at that location + * @param entry How the ray entered this column + */ + function findIndexInColumnWithEntry + (column, startRow, endRow, func:FindRayFuncI, entry:FlxRayEntry):RayResultHelper + { + final startI = getMapIndex(column, startRow); + final edge = EDGE(startRow < endRow ? UP : DOWN); + final f = (i, t)->func(i, t, i == startI ? entry : edge); + final index = findIndexInColumn(column, startRow, endRow, f); + if (index == -1) + return null; + return _rayResultHelper.set(index, index == startI ? entry : edge); + } + + function calcRayResult(data:RayResultHelper, m:Float, b:Float, start:FlxPoint):FlxRayResult + { + return switch data.entry + { + case START: + STOPPED(data.index, start.x, start.y, data.entry); + case EDGE(LEFT): + final x = getColumnPos(getColumn(data.index)); + final y = m * x + b; + STOPPED(data.index, x, y, data.entry); + case EDGE(RIGHT): + final x = getColumnPos(getColumn(data.index)) + getTileWidth(); + final y = m * x + b; + STOPPED(data.index, x, y, data.entry); + case EDGE(UP): + final y = getRowPos(getRow(data.index)); + final x = (y - b) / m; + STOPPED(data.index, x, y, data.entry); + case EDGE(DOWN): + final y = getRowPos(getRow(data.index)) + getTileHeight(); + final x = (y - b) / m; + STOPPED(data.index, x, y, data.entry); + } } /** @@ -460,12 +629,12 @@ class FlxBaseTilemap extends FlxObject * @param column The column to check * @param startRow The row to check from * @param endRow The row to check to - * @param func The function, where `tile` is the tile instance for that location + * @param func The function, where `tile` is the tile data at that location * @since 5.10.0 */ - inline public function forEachIndexInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Void) + inline overload extern public function forEachIndexInColumn(column, startRow, endRow, func:(Tile)->Void) { - findIndexInColumnI(column, startRow, endRow, voidFindIgnoreIndex(func)); + findIndexInColumnHelper(column, startRow, endRow, func.toFindI()); } /** @@ -475,39 +644,14 @@ class FlxBaseTilemap extends FlxObject * @param startRow The row to check from * @param endRow The row to check to * @param func The function, where `index` is the tile's map index, and `tile` is - * the tile instance for that location, or `null` if there is no instance + * the tile data at that location, if one exists * @since 5.10.0 */ - inline public function forEachIndexInColumnI(column:Int, startRow:Int, endRow:Int, func:(index:Int, tile:Null)->Bool) - { - findIndexInColumnI(column, startRow, endRow, voidFind(func)); - } - - /** - * Helper to convert `(Int, Null)->Bool` to `(Tile>)->Void`. - * Checks null, ignores index, always returns false - */ - inline function voidFindIgnoreIndex(func:(Tile)->Void):(Int, Null)->Bool - { - return function (_, t) - { - if (t != null) - func(t); - - return false; - }; - } - - /** - * Helper to convert `(Int, Null)->Bool` to `(Int, Null)->Void`. - * Always returns false - */ - inline function voidFind(func:(Int, Null)->Void):(index:Int, tile:Null)->Bool + inline overload extern public function forEachIndexInColumn(column, startRow, endRow, func:(index:Int, tile:Null)->Void) { - return (_, t)->{ func(_, t); return false; }; + findIndexInColumnHelper(column, startRow, endRow, func.toFindI()); } - /** * Checks all tiles in the `column` between the specified `startRow` and `endRow`, * Retrieves the first tile that satisfies to condition of `func` and returns it @@ -517,13 +661,18 @@ class FlxBaseTilemap extends FlxObject * @param column The column to check * @param startRow The row to check from * @param endRow The row to check to - * @param func The condition, where `tile` is the tile instance for that location + * @param func The stopping condition, where `tile` is the tile data at that location * @return The found tile * @since 5.10.0 */ - public function findInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Bool):Tile + inline overload extern public function findInColumn(column, startRow, endRow, func:(Tile)->Bool) { - final index = findIndexInColumnI(column, startRow, endRow, ignoreIndex(func)); + return findInColumnHelper(column, startRow, endRow, func); + } + + function findInColumnHelper(column:Int, startRow:Int, endRow:Int, func:(Tile)->Bool):Tile + { + final index = findIndexInColumnHelper(column, startRow, endRow, func.ignoreIndex()); if (index < 0) return null; @@ -543,22 +692,13 @@ class FlxBaseTilemap extends FlxObject * @param column The column to check * @param startRow The row to check from * @param endRow The row to check to - * @param func The condition, where `tile` is the tile instance for that location + * @param func The stopping condition, where `tile` is the tile data at that location * @return The index of the found tile * @since 5.10.0 */ - inline public function findIndexInColumn(column:Int, startRow:Int, endRow:Int, func:(Tile)->Bool):Int - { - return findIndexInColumnI(column, startRow, endRow, ignoreIndex(func)); - } - - /** - * Helper to convert `(Int, Null)->Bool` to `(Tile)->Bool`. - * Checks null, ignores index - */ - inline function ignoreIndex(func:(Tile)->Bool):(Int, Null)->Bool + inline overload extern public function findIndexInColumn(column, startRow, endRow, func:(Tile)->Bool) { - return (_, t)->t != null && func(t); + return findIndexInColumnHelper(column, startRow, endRow, func.ignoreIndex()); } /** @@ -568,38 +708,34 @@ class FlxBaseTilemap extends FlxObject * @param column The column to check * @param startRow The row to check from * @param endRow The row to check to - * @param func The condition, where `index` is the tile's map index, and `tile` is - * the tile instance for that location, or `null` if there is no instance + * @param func The stopping condition, where `index` is the tile's map index, and + * `tile` is the tile data at that location, if one exists * @return The index of the found tile * @since 5.10.0 */ - public function findIndexInColumnI(column:Int, startRow:Int, endRow:Int, func:(index:Int, tile:Null)->Bool):Int + inline overload extern public function findIndexInColumn(column, startRow, endRow, func:(index:Int, tile:Null)->Bool) + { + return findIndexInColumnHelper(column, startRow, endRow, func); + } + + function findIndexInColumnHelper(column:Int, startRow:Int, endRow:Int, func:(index:Int, tile:Null)->Bool):Int { if (startRow < 0) startRow = 0; + else if (startRow > heightInTiles - 1) + startRow = heightInTiles - 1; if (endRow < 0) endRow = 0; - - if (startRow > heightInTiles - 1) - startRow = heightInTiles - 1; - - if (endRow > heightInTiles - 1) + else if (endRow > heightInTiles - 1) endRow = heightInTiles - 1; - var row = startRow; - final step = startRow <= endRow ? 1 : -1; - while (true) + for (row in startRow.iter(endRow)) { final index = getMapIndex(column, row); final tile = getTileData(index); if (func(index, tile)) return index; - - if (row == endRow) - break; - - row += step; } return -1; @@ -670,6 +806,9 @@ class FlxBaseTilemap extends FlxObject return calcRayEntry(end, start, result); } + //} endregion Ray + Helpers + // ============================================================================= + /** * Searches all tiles near the object for any that satisfy the given filter. Stops searching * when the first overlapping tile that satisfies the condition is found @@ -2035,3 +2174,241 @@ enum FlxTilemapAutoTiling */ FULL; } + +// ============================================================================= +//{ region Ray + Helpers +// ============================================================================= + +/** + * `(index:Int, tile:Null, entry:FlxRayEntry) -> Bool` + * The callback function for helpers like `findIndexInRayI` + * + * @param index The index of the tile in the map, where 0 is the top-left + * @param tile The tile instance, if one exists + * @param entry How the ray entered the tile + * @return Whether this tile matches the search condition + */ +typedef FindRayFuncI = (index:Int, tile:Null, entry:FlxRayEntry) -> Bool; + +/** + * `(index:Int, tile:Null, entry:FlxRayEntry) -> Void` + * The callback function for helpers like `forEachIndexInRayI` + * + * @param index The index of the tile in the map, where 0 is the top-left + * @param tile The tile instance, if one exists + * @param entry How the ray entered the tile + */ +typedef ForEachRayFuncI = (index:Int, tile:Null, entry:FlxRayEntry) -> Void; + +/** + * `(tile:Tile, entry:FlxRayEntry) -> Bool` + * The callback function for helpers like `findIndexInRay` + * + * @param tile The tile instance + * @param entry How the ray entered the tile + * @return Whether this tile matches the search condition + */ +typedef FindRayFunc = (tile:Tile, entry:FlxRayEntry) -> Bool; + +/** + * `(tile:Tile, entry:FlxRayEntry) -> Void` + * The callback function for helpers like `forEachIndexInRay` + * + * @param tile The tile instance + * @param entry How the ray entered the tile + */ +typedef ForEachRayFunc = (tile:Tile, entry:FlxRayEntry) -> Void; + +/** + * `(index:Int, tile:Null) -> Bool` + * The callback function for helpers like `findIndexInRayI` + * + * @param index The index of the tile in the map, where 0 is the top-left + * @param tile The tile instance, if one exists + * @return Whether this tile matches the search condition + */ +typedef FindTileFuncI = (index:Int, tile:Null) -> Bool; + +/** + * `(index:Int, tile:Null) -> Void` + * The callback function for helpers like `forEachIndexInRayI` + * + * @param index The index of the tile in the map, where 0 is the top-left + * @param tile The tile instance, if one exists + */ +typedef ForEachTileFuncI = (index:Int, tile:Null) -> Void; + +/** + * `(tile:Tile) -> Bool` + * The callback function for helpers like `findIndexInRay` + * + * @param tile The tile instance + * @return Whether this tile matches the search condition + */ +typedef FindTileFunc = (tile:Tile) -> Bool; + +/** + * `(tile:Tile) -> Void` + * The callback function for helpers like `forEachIndexInRay` + * + * @param tile The tile instance + */ +typedef ForEachTileFunc = (tile:Tile) -> Void; + +enum FlxRayEntry +{ + EDGE(dir:FlxDirection); + /** + * The ray started in the tile + */ + START; +} + +private class RayResultHelper +{ + public var index:Int = 0; + public var entry:FlxRayEntry = START; + + public function new () {} + public function set(index, entry) + { + this.index = index; + this.entry = entry; + return this; + } +} + +enum FlxRayResult +{ + /** + * The ray reached a stopping tile + */ + STOPPED(mapIndex:Int, x:Float, y:Float, entry:FlxRayEntry); + + /** + * The ray reached the end without being stopped + */ + END; +} + +private class RayTools +{ + inline overload extern static public function ignoreIndex(func:FindTileFunc):FindTileFuncI + { + return (_, t)->t != null && func(t); + } + + inline overload extern static public function toFindIE(func:FindRayFunc):FindRayFuncI + { + return (_, t, e)->t != null && func(t, e); + } + + inline overload extern static public function toFindIE(func:FindTileFunc):FindRayFuncI + { + return (_, t, _)->t != null && func(t); + } + + /** + * Helper to convert `(Int, Null)->Bool` to `(Tile)->Void`. + * Checks null, ignores index, always returns false + */ + inline overload extern static public function toFindI(func:ForEachTileFunc):FindTileFuncI + { + return function (_, t) + { + if (t != null) + func(t); + + return false; + }; + } + + /** + * Helper to convert a `(Int, Null)->Bool` to `(Int, Null)->Void`. + * Always returns false + */ + inline overload extern static public function toFindI(func:ForEachTileFuncI):FindTileFuncI + { + return (i, t)->{ func(i, t); return false; }; + } + + /** + * Helper to convert `(Tile)->Void` to `(Int, Null, FlxRayEntry)->Bool`. + * Checks null, ignores index, always returns false + */ + inline overload extern static public function toFindIE(func:ForEachTileFunc):FindRayFuncI + { + return function (_, t, _) + { + if (t != null) + func(t); + + return false; + }; + } + + /** + * Helper to convert a `(Int, Null)->Bool` to `(Int, Null, FlxRayEntry)->Void`. + * Always returns false + */ + inline overload extern static public function toFindIE(func:ForEachTileFuncI):FindRayFuncI + { + return (i, t, _)->{ func(i, t); return false; }; + } + + /** + * Helper to convert `(Int, Null, FlxRayEntry)->Bool` to `(Tile, FlxRayEntry)->Void`. + * Checks null, ignores index, always returns false + */ + inline overload extern static public function toFindIE(func:ForEachRayFunc):FindRayFuncI + { + return function (_, t, e) + { + if (t != null) + func(t, e); + + return false; + }; + } + + /** + * Helper to convert `(Int, Null)->Bool` to `(Int, Null)->Void`. + * Always returns false + */ + inline overload extern static public function toFindIE(func:ForEachRayFuncI):FindRayFuncI + { + return (i, t, e)->{ func(i, t, e); return false; }; + } +} +private class AmbiIntIterator +{ + final start:Int; + final iterator:IntIterator; + final step:Int; + + inline public function new(start:Int, end:Int, inclusive = true) + { + this.start = start; + this.step = start < end ? 1 : -1; + final dis = (end - start) * step + (inclusive ? 1 : 0); + iterator = (0... dis); + } + + inline public function hasNext() + { + return iterator.hasNext(); + } + + inline public function next() + { + return start + iterator.next() * step; + } + + inline static public function iter(start:Int, end:Int, inclusive = true) + { + return new AmbiIntIterator(start, end, inclusive); + } +} + +//} endregion Ray + Helpers +// ============================================================================= \ No newline at end of file From f7643b306a3653d2f9901951a35c849c94198923 Mon Sep 17 00:00:00 2001 From: GeoKureli-BlackbookPro Date: Fri, 19 Dec 2025 17:28:53 -0600 Subject: [PATCH 08/16] remove RayTools --- flixel/tile/FlxBaseTilemap.hx | 145 ++++++---------------------------- 1 file changed, 25 insertions(+), 120 deletions(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index 32af84ae3d..f0e2750a68 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -6,17 +6,16 @@ import flixel.math.FlxPoint; import flixel.math.FlxRect; import flixel.path.FlxPathfinder; import flixel.system.FlxAssets; -import flixel.util.FlxDestroyUtil; import flixel.util.FlxArrayUtil; import flixel.util.FlxCollision; import flixel.util.FlxColor; +import flixel.util.FlxDestroyUtil; import flixel.util.FlxDirection; import flixel.util.FlxDirectionFlags; import flixel.util.FlxStringUtil; import openfl.display.BitmapData; using StringTools; -using flixel.tile.FlxBaseTilemap.RayTools; using flixel.tile.FlxBaseTilemap.AmbiIntIterator; @:autoBuild(flixel.system.macros.FlxMacroUtil.deprecateOverride("overlapsWithCallback", "overlapsWithCallback is deprecated, use objectOverlapsTiles")) @@ -322,7 +321,7 @@ class FlxBaseTilemap extends FlxObject */ inline overload extern public function forEachInRay(start, end, func:(Tile)->Void) { - findIndexInRayHelper(start, end, func.toFindIE()); + findIndexInRayHelper(start, end, (i, t, e)->{ func(t); return false; }); } /** @@ -334,9 +333,9 @@ class FlxBaseTilemap extends FlxObject * the tile data at that location, if one exists * @since 5.10.0 */ - inline overload extern public function forEachInRay(start, end, func:(index:Int, tile:Null)->Bool) + inline overload extern public function forEachInRay(start, end, func:(index:Int, tile:Null)->Void) { - findIndexInRayHelper(start, end, func.toFindIE()); + findIndexInRayHelper(start, end, (i, t, e)->{ func(i, t); return false; }); } /** @@ -350,7 +349,7 @@ class FlxBaseTilemap extends FlxObject */ inline overload extern public function forEachInRay(start, end, func:(tile:Tile, entry:FlxRayEntry)->Void) { - findIndexInRayHelper(start, end, func.toFindIE()); + findIndexInRayHelper(start, end, (i, t, e)->{ func(t, e); return false; }); } /** @@ -362,9 +361,9 @@ class FlxBaseTilemap extends FlxObject * at that location, if one exists and `entry` is how the ray entered the tile * @since 5.10.0 */ - inline overload extern public function forEachInRay(start, end, func:(index:Int, tile:Null, entry:FlxRayEntry)->Bool) + inline overload extern public function forEachInRay(start, end, func:(index:Int, tile:Null, entry:FlxRayEntry)->Void) { - findIndexInRayHelper(start, end, func.toFindIE()); + findIndexInRayHelper(start, end, (i, t, e)->{ func(i, t, e); return false; }); } /** @@ -379,9 +378,9 @@ class FlxBaseTilemap extends FlxObject * @return The index of the found tile * @since 5.10.0 */ - inline overload extern public function findIndexInRay(start, end, func:(Tile)->Bool) + inline overload extern public function findIndexInRay(start, end, func:(tile:Tile)->Bool) { - return findIndexInRayHelper(start, end, func.toFindIE()); + return findIndexInRayHelper(start, end, (i, t, e)->func(t)); } /** @@ -395,10 +394,9 @@ class FlxBaseTilemap extends FlxObject * @return The index of the found tile * @since 5.10.0 */ - inline overload extern public function findIndexInRay - (start, end, func:(index:Int, tile:Null)->Bool) + inline overload extern public function findIndexInRay(start, end, func:(index:Int, tile:Null)->Bool) { - return findIndexInRayHelper(start, end, func.toFindIE()); + return findIndexInRayHelper(start, end, (i, t, e)->func(i, t)); } /** @@ -412,14 +410,12 @@ class FlxBaseTilemap extends FlxObject * @return The index of the found tile * @since 5.10.0 */ - inline overload extern public function findIndexInRay - (start, end, func:(index:Int, tile:Null, entry:FlxRayEntry)->Bool) + inline overload extern public function findIndexInRay(start, end, func) { return findIndexInRayHelper(start, end, func); } - function findIndexInRayHelper - (start, end, func:(index:Int, tile:Null, entry:FlxRayEntry)->Bool):Int + function findIndexInRayHelper(start, end, func:(index:Int, tile:Null, entry:FlxRayEntry)->Bool):Int { switch findInRayHelper(start, end, func) { @@ -444,7 +440,7 @@ class FlxBaseTilemap extends FlxObject */ inline overload extern public function findInRay(start, end, func:(Tile)->Bool) { - return findInRayHelper(start, end, func.toFindIE()); + return findInRayHelper(start, end, (_, t, _)->func(t)); } /** @@ -458,10 +454,9 @@ class FlxBaseTilemap extends FlxObject * @return The result of the ray, whether it reached the end or was stopped, and where * @since 5.10.0 */ - inline overload extern public function findInRay - (start, end, func:(index:Int, tile:Null)->Bool) + inline overload extern public function findInRay(start, end, func) { - return findInRayHelper(start, end, func.toFindIE()); + return findInRayHelper(start, end, (i:Int, t:Tile, _)->func(i, t)); } /** @@ -475,8 +470,7 @@ class FlxBaseTilemap extends FlxObject * @return The result of the ray, whether it reached the end or was stopped, and where * @since 5.10.0 */ - inline overload extern public function findInRay - (start, end, func:(index:Int, tile:Null, entry:FlxRayEntry)->Bool) + inline overload extern public function findInRay(start, end, func) { return findInRayHelper(start, end, func); } @@ -632,9 +626,9 @@ class FlxBaseTilemap extends FlxObject * @param func The function, where `tile` is the tile data at that location * @since 5.10.0 */ - inline overload extern public function forEachIndexInColumn(column, startRow, endRow, func:(Tile)->Void) + inline overload extern public function forEachIndexInColumn(column, startRow, endRow, func:(tile:Tile)->Void) { - findIndexInColumnHelper(column, startRow, endRow, func.toFindI()); + findIndexInColumnHelper(column, startRow, endRow, (i, t)->{ func(t); return false; }); } /** @@ -649,7 +643,7 @@ class FlxBaseTilemap extends FlxObject */ inline overload extern public function forEachIndexInColumn(column, startRow, endRow, func:(index:Int, tile:Null)->Void) { - findIndexInColumnHelper(column, startRow, endRow, func.toFindI()); + findIndexInColumnHelper(column, startRow, endRow, (i, t)->{ func(i, t); return false; }); } /** @@ -665,14 +659,14 @@ class FlxBaseTilemap extends FlxObject * @return The found tile * @since 5.10.0 */ - inline overload extern public function findInColumn(column, startRow, endRow, func:(Tile)->Bool) + inline overload extern public function findInColumn(column, startRow, endRow, func:(tile:Tile)->Bool) { return findInColumnHelper(column, startRow, endRow, func); } - function findInColumnHelper(column:Int, startRow:Int, endRow:Int, func:(Tile)->Bool):Tile + function findInColumnHelper(column:Int, startRow:Int, endRow:Int, func:(tile:Tile)->Bool):Tile { - final index = findIndexInColumnHelper(column, startRow, endRow, func.ignoreIndex()); + final index = findIndexInColumnHelper(column, startRow, endRow, (i, t)->func(t)); if (index < 0) return null; @@ -696,9 +690,9 @@ class FlxBaseTilemap extends FlxObject * @return The index of the found tile * @since 5.10.0 */ - inline overload extern public function findIndexInColumn(column, startRow, endRow, func:(Tile)->Bool) + inline overload extern public function findIndexInColumn(column, startRow, endRow, func:(tile:Tile)->Bool) { - return findIndexInColumnHelper(column, startRow, endRow, func.ignoreIndex()); + return findIndexInColumnHelper(column, startRow, endRow, (i, t)->func(t)); } /** @@ -2292,95 +2286,6 @@ enum FlxRayResult END; } -private class RayTools -{ - inline overload extern static public function ignoreIndex(func:FindTileFunc):FindTileFuncI - { - return (_, t)->t != null && func(t); - } - - inline overload extern static public function toFindIE(func:FindRayFunc):FindRayFuncI - { - return (_, t, e)->t != null && func(t, e); - } - - inline overload extern static public function toFindIE(func:FindTileFunc):FindRayFuncI - { - return (_, t, _)->t != null && func(t); - } - - /** - * Helper to convert `(Int, Null)->Bool` to `(Tile)->Void`. - * Checks null, ignores index, always returns false - */ - inline overload extern static public function toFindI(func:ForEachTileFunc):FindTileFuncI - { - return function (_, t) - { - if (t != null) - func(t); - - return false; - }; - } - - /** - * Helper to convert a `(Int, Null)->Bool` to `(Int, Null)->Void`. - * Always returns false - */ - inline overload extern static public function toFindI(func:ForEachTileFuncI):FindTileFuncI - { - return (i, t)->{ func(i, t); return false; }; - } - - /** - * Helper to convert `(Tile)->Void` to `(Int, Null, FlxRayEntry)->Bool`. - * Checks null, ignores index, always returns false - */ - inline overload extern static public function toFindIE(func:ForEachTileFunc):FindRayFuncI - { - return function (_, t, _) - { - if (t != null) - func(t); - - return false; - }; - } - - /** - * Helper to convert a `(Int, Null)->Bool` to `(Int, Null, FlxRayEntry)->Void`. - * Always returns false - */ - inline overload extern static public function toFindIE(func:ForEachTileFuncI):FindRayFuncI - { - return (i, t, _)->{ func(i, t); return false; }; - } - - /** - * Helper to convert `(Int, Null, FlxRayEntry)->Bool` to `(Tile, FlxRayEntry)->Void`. - * Checks null, ignores index, always returns false - */ - inline overload extern static public function toFindIE(func:ForEachRayFunc):FindRayFuncI - { - return function (_, t, e) - { - if (t != null) - func(t, e); - - return false; - }; - } - - /** - * Helper to convert `(Int, Null)->Bool` to `(Int, Null)->Void`. - * Always returns false - */ - inline overload extern static public function toFindIE(func:ForEachRayFuncI):FindRayFuncI - { - return (i, t, e)->{ func(i, t, e); return false; }; - } -} private class AmbiIntIterator { final start:Int; From 711412d3426529ffb4af451f0a2e0a45b7690deb Mon Sep 17 00:00:00 2001 From: GeoKureli-BlackbookPro Date: Fri, 19 Dec 2025 17:31:57 -0600 Subject: [PATCH 09/16] add rayAdvanced --- flixel/tile/FlxBaseTilemap.hx | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index f0e2750a68..f610388d2b 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -271,13 +271,11 @@ class FlxBaseTilemap extends FlxObject * @param start The world coordinates of the start of the ray * @param end The world coordinates of the end of the ray * @param result Optional result vector, indicating where the ray hit the first wall - * @param checkDir Whether this method honors the tiles' `allowCollisions` directions * @return Whether the ray can travel from `start` to `end` without hitting a wall */ - public function ray(start:FlxPoint, end:FlxPoint, ?result:FlxPoint, checkDir = false):Bool + public function ray(start:FlxPoint, end:FlxPoint, ?result:FlxPoint):Bool { - final func = checkDir ? checkRayDirHelper(start, end) : (_, t:Tile, _)->t != null && t.solid; - switch findInRayHelper(start, end, func) + return switch rayAdvanced(start, end, false) { case END: return true; @@ -290,11 +288,30 @@ class FlxBaseTilemap extends FlxObject } } + /** + * Determines whether the ray can travel from `start` to `end` without hitting a solid wall. + * + * **Note:** In flixel 5.0.0, this was redone, the old method is now `rayStep` + * + * @param start The world coordinates of the start of the ray + * @param end The world coordinates of the end of the ray + * @param checkDir Whether this method honors the tiles' `allowCollisions` directions + * @return Whether the ray can travel from `start` to `end` without hitting a wall + */ + public function rayAdvanced(start:FlxPoint, end:FlxPoint, checkDir = true) + { + final func = checkRayDirHelper(start, end, checkDir); + return findInRayHelper(start, end, func); + } + /** * Ray func helper, checks tiles' directions compared to the ray direction */ - function checkRayDirHelper(start:FlxPoint, end:FlxPoint):FindRayFuncI + function checkRayDirHelper(start:FlxPoint, end:FlxPoint, checkDir:Bool) { + if (!checkDir) + return (_, t:Tile, _)->t != null && t.solid; + return function (i:Int, tile:Null, entry:FlxRayEntry) { return tile != null && switch entry From 31140339ef1198d4d8048f3a5384984f1c614d2e Mon Sep 17 00:00:00 2001 From: GeoKureli-BlackbookPro Date: Fri, 19 Dec 2025 17:49:14 -0600 Subject: [PATCH 10/16] remove helper types --- flixel/tile/FlxBaseTilemap.hx | 80 +---------------------------------- 1 file changed, 2 insertions(+), 78 deletions(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index f610388d2b..4c7459701b 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -596,7 +596,7 @@ class FlxBaseTilemap extends FlxObject * @param entry How the ray entered this column */ function findIndexInColumnWithEntry - (column, startRow, endRow, func:FindRayFuncI, entry:FlxRayEntry):RayResultHelper + (column, startRow, endRow, func:(index:Int, tile:Null, entry:FlxRayEntry) -> Bool, entry:FlxRayEntry):RayResultHelper { final startI = getMapIndex(column, startRow); final edge = EDGE(startRow < endRow ? UP : DOWN); @@ -645,7 +645,7 @@ class FlxBaseTilemap extends FlxObject */ inline overload extern public function forEachIndexInColumn(column, startRow, endRow, func:(tile:Tile)->Void) { - findIndexInColumnHelper(column, startRow, endRow, (i, t)->{ func(t); return false; }); + findIndexInColumnHelper(column, startRow, endRow, (i, t)->{ if (t != null) func(t); return false; }); } /** @@ -2191,82 +2191,6 @@ enum FlxTilemapAutoTiling //{ region Ray + Helpers // ============================================================================= -/** - * `(index:Int, tile:Null, entry:FlxRayEntry) -> Bool` - * The callback function for helpers like `findIndexInRayI` - * - * @param index The index of the tile in the map, where 0 is the top-left - * @param tile The tile instance, if one exists - * @param entry How the ray entered the tile - * @return Whether this tile matches the search condition - */ -typedef FindRayFuncI = (index:Int, tile:Null, entry:FlxRayEntry) -> Bool; - -/** - * `(index:Int, tile:Null, entry:FlxRayEntry) -> Void` - * The callback function for helpers like `forEachIndexInRayI` - * - * @param index The index of the tile in the map, where 0 is the top-left - * @param tile The tile instance, if one exists - * @param entry How the ray entered the tile - */ -typedef ForEachRayFuncI = (index:Int, tile:Null, entry:FlxRayEntry) -> Void; - -/** - * `(tile:Tile, entry:FlxRayEntry) -> Bool` - * The callback function for helpers like `findIndexInRay` - * - * @param tile The tile instance - * @param entry How the ray entered the tile - * @return Whether this tile matches the search condition - */ -typedef FindRayFunc = (tile:Tile, entry:FlxRayEntry) -> Bool; - -/** - * `(tile:Tile, entry:FlxRayEntry) -> Void` - * The callback function for helpers like `forEachIndexInRay` - * - * @param tile The tile instance - * @param entry How the ray entered the tile - */ -typedef ForEachRayFunc = (tile:Tile, entry:FlxRayEntry) -> Void; - -/** - * `(index:Int, tile:Null) -> Bool` - * The callback function for helpers like `findIndexInRayI` - * - * @param index The index of the tile in the map, where 0 is the top-left - * @param tile The tile instance, if one exists - * @return Whether this tile matches the search condition - */ -typedef FindTileFuncI = (index:Int, tile:Null) -> Bool; - -/** - * `(index:Int, tile:Null) -> Void` - * The callback function for helpers like `forEachIndexInRayI` - * - * @param index The index of the tile in the map, where 0 is the top-left - * @param tile The tile instance, if one exists - */ -typedef ForEachTileFuncI = (index:Int, tile:Null) -> Void; - -/** - * `(tile:Tile) -> Bool` - * The callback function for helpers like `findIndexInRay` - * - * @param tile The tile instance - * @return Whether this tile matches the search condition - */ -typedef FindTileFunc = (tile:Tile) -> Bool; - -/** - * `(tile:Tile) -> Void` - * The callback function for helpers like `forEachIndexInRay` - * - * @param tile The tile instance - */ -typedef ForEachTileFunc = (tile:Tile) -> Void; - enum FlxRayEntry { EDGE(dir:FlxDirection); From 6826897b336908b901eb7ca14b0385e2044489c9 Mon Sep 17 00:00:00 2001 From: GeoKureli-BlackbookPro Date: Fri, 19 Dec 2025 20:10:53 -0600 Subject: [PATCH 11/16] fix bug with starting in tile --- flixel/tile/FlxBaseTilemap.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index 4c7459701b..e3cef71043 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -525,7 +525,7 @@ class FlxBaseTilemap extends FlxObject if (start.x == end.x) { // did the ray start here or from outside the map - final entry = wasStartTrimmed ? START : EDGE(start.y < end.y ? UP : DOWN); + final entry = wasStartTrimmed ? EDGE(start.y < end.y ? UP : DOWN) : START; final result = findIndexInColumnWithEntry(startTileX, startTileY, endTileY, func, entry); if (result != null) { @@ -546,7 +546,7 @@ class FlxBaseTilemap extends FlxObject final movesRight = start.x < end.x; final inc = movesRight ? 1 : -1; final offset = movesRight ? 1 : 0; - var entry = wasStartTrimmed ? START : EDGE(movesRight ? LEFT : RIGHT); + var entry = wasStartTrimmed ? EDGE(movesRight ? LEFT : RIGHT) : START; var lastTileY = startTileY; for (tileX in startTileX.iter(endTileX)) From d69ef4b780a2e86b7503781ce5158ec542cc5900 Mon Sep 17 00:00:00 2001 From: GeoKureli-BlackbookPro Date: Fri, 19 Dec 2025 21:32:34 -0600 Subject: [PATCH 12/16] fix doc --- flixel/tile/FlxBaseTilemap.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index e3cef71043..4fc124fca3 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -1362,7 +1362,7 @@ class FlxBaseTilemap extends FlxObject } /** - * Calculates the column from a map location + * Calculates the row from a map location * * @param mapIndex The location in the map where `mapIndex = row * widthInTiles + column` * @since 5.9.0 From 76328b1fb8250120ab953c1d6edb4e7ca9b48bb3 Mon Sep 17 00:00:00 2001 From: GeoKureli-BlackbookPro Date: Fri, 19 Dec 2025 21:35:22 -0600 Subject: [PATCH 13/16] remove RayResultHelper --- flixel/tile/FlxBaseTilemap.hx | 77 ++++++++++++++--------------------- 1 file changed, 30 insertions(+), 47 deletions(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index 4fc124fca3..a9d860917c 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -520,19 +520,18 @@ class FlxBaseTilemap extends FlxObject final startTileY = getRow(startIndex); final endTileX = getColumn(endIndex); final endTileY = getRow(endIndex); + final dirY = start.y < end.y ? FlxDirection.UP : FlxDirection.DOWN; // handle vertical line (infinite slope), first if (start.x == end.x) { - // did the ray start here or from outside the map - final entry = wasStartTrimmed ? EDGE(start.y < end.y ? UP : DOWN) : START; - final result = findIndexInColumnWithEntry(startTileX, startTileY, endTileY, func, entry); - if (result != null) + final entry = wasStartTrimmed ? EDGE(dirY) : START; + final resultIndex = findIndexInColumnWithEntry(startTileX, startTileY, endTileY, func, entry); + if (resultIndex != -1) { - // check the bottom - final resultX = start.x; - final resultY = getRowPos(getRow(result.index + (start.y > end.y ? 1 : 0))); - return STOPPED(result.index, resultX, resultY, result.entry); + final resultY = getRowPos(getRow(resultIndex + (start.y > end.y ? 1 : 0))); + final colEntry = getRow(resultIndex) == startTileY ? entry : EDGE(dirY); + return STOPPED(resultIndex, start.x, resultY, colEntry); } return END; @@ -546,7 +545,7 @@ class FlxBaseTilemap extends FlxObject final movesRight = start.x < end.x; final inc = movesRight ? 1 : -1; final offset = movesRight ? 1 : 0; - var entry = wasStartTrimmed ? EDGE(movesRight ? LEFT : RIGHT) : START; + var colEntry = wasStartTrimmed ? EDGE(movesRight ? LEFT : RIGHT) : START; var lastTileY = startTileY; for (tileX in startTileX.iter(endTileX)) @@ -554,11 +553,15 @@ class FlxBaseTilemap extends FlxObject final xPos = getColumnPos(tileX + offset); final yPos = ambiClamp(m * getColumnPos(tileX + offset) + b, startY, endY); final tileY = getRowAt(yPos); - final result = findIndexInColumnWithEntry(tileX, lastTileY, tileY, func, entry); - if (result != null) - return calcRayResult(result, m, b, start); + final resultIndex = findIndexInColumnWithEntry(tileX, lastTileY, tileY, func, colEntry); + if (resultIndex != -1) + { + final endY = getRow(resultIndex); + final tileEntry = endY == lastTileY ? colEntry : EDGE(dirY); + return calcRayResult(resultIndex, tileEntry, m, b, start); + } - entry = EDGE(movesRight ? LEFT : RIGHT); + colEntry = EDGE(movesRight ? LEFT : RIGHT); lastTileY = tileY; } @@ -582,10 +585,6 @@ class FlxBaseTilemap extends FlxObject return value; } - /** - * Helper for retriving ray entry results - */ - static final _rayResultHelper = new RayResultHelper(); /** * Helper to add an `entry` to `findIndexInColumn` callbacks * @@ -596,39 +595,37 @@ class FlxBaseTilemap extends FlxObject * @param entry How the ray entered this column */ function findIndexInColumnWithEntry - (column, startRow, endRow, func:(index:Int, tile:Null, entry:FlxRayEntry) -> Bool, entry:FlxRayEntry):RayResultHelper + (column, startRow, endRow, func:(index:Int, tile:Null, entry:FlxRayEntry) -> Bool, entry:FlxRayEntry) { final startI = getMapIndex(column, startRow); final edge = EDGE(startRow < endRow ? UP : DOWN); final f = (i, t)->func(i, t, i == startI ? entry : edge); - final index = findIndexInColumn(column, startRow, endRow, f); - if (index == -1) - return null; - return _rayResultHelper.set(index, index == startI ? entry : edge); + + return findIndexInColumn(column, startRow, endRow, f); } - function calcRayResult(data:RayResultHelper, m:Float, b:Float, start:FlxPoint):FlxRayResult + function calcRayResult(index:Int, entry:FlxRayEntry, m:Float, b:Float, start:FlxPoint):FlxRayResult { - return switch data.entry + return switch entry { case START: - STOPPED(data.index, start.x, start.y, data.entry); + STOPPED(index, start.x, start.y, entry); case EDGE(LEFT): - final x = getColumnPos(getColumn(data.index)); + final x = getColumnPos(getColumn(index)); final y = m * x + b; - STOPPED(data.index, x, y, data.entry); + STOPPED(index, x, y, entry); case EDGE(RIGHT): - final x = getColumnPos(getColumn(data.index)) + getTileWidth(); + final x = getColumnPos(getColumn(index)) + getTileWidth(); final y = m * x + b; - STOPPED(data.index, x, y, data.entry); + STOPPED(index, x, y, entry); case EDGE(UP): - final y = getRowPos(getRow(data.index)); + final y = getRowPos(getRow(index)); final x = (y - b) / m; - STOPPED(data.index, x, y, data.entry); + STOPPED(index, x, y, entry); case EDGE(DOWN): - final y = getRowPos(getRow(data.index)) + getTileHeight(); + final y = getRowPos(getRow(index)) + getTileHeight(); final x = (y - b) / m; - STOPPED(data.index, x, y, data.entry); + STOPPED(index, x, y, entry); } } @@ -2200,20 +2197,6 @@ enum FlxRayEntry START; } -private class RayResultHelper -{ - public var index:Int = 0; - public var entry:FlxRayEntry = START; - - public function new () {} - public function set(index, entry) - { - this.index = index; - this.entry = entry; - return this; - } -} - enum FlxRayResult { /** From eea800c3e7833ac74bb9d9d7a198e58cfc66f9f4 Mon Sep 17 00:00:00 2001 From: GeoKureli-BlackbookPro Date: Sat, 20 Dec 2025 12:46:05 -0600 Subject: [PATCH 14/16] doc + style fixes --- flixel/tile/FlxBaseTilemap.hx | 110 +++++++++++++++++----------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index a9d860917c..906b981c06 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -203,7 +203,7 @@ class FlxBaseTilemap extends FlxObject * * @param worldX An X coordinate in the world * @param midpoint Whether to use the tile's midpoint, or left edge - * @since 5.10.0 + * @since 6.2.0 */ public function getColumnPosAt(worldX:Float, midpoint = false):Float { @@ -215,7 +215,7 @@ class FlxBaseTilemap extends FlxObject * * @param worldY An X coordinate in the world * @param midpoint Whether to use the tile's midpoint, or upper edge - * @since 5.10.0 + * @since 6.2.0 */ public function getRowPosAt(worldY:Float, midpoint = false):Float { @@ -225,7 +225,7 @@ class FlxBaseTilemap extends FlxObject /** * Get the size of a column, in world coordinates * - * @since 5.10.0 + * @since 6.2.0 */ public function getTileWidth():Float { @@ -235,7 +235,7 @@ class FlxBaseTilemap extends FlxObject /** * Get the size of a column, in world coordinates * - * @since 5.10.0 + * @since 6.2.0 */ public function getTileHeight():Float { @@ -334,9 +334,9 @@ class FlxBaseTilemap extends FlxObject * @param start The world coordinates of the start of the ray * @param end The world coordinates of the end of the ray * @param func The function, where `tile` is the tile data at that location - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function forEachInRay(start, end, func:(Tile)->Void) + overload public inline extern function forEachInRay(start, end, func:(Tile)->Void) { findIndexInRayHelper(start, end, (i, t, e)->{ func(t); return false; }); } @@ -348,9 +348,9 @@ class FlxBaseTilemap extends FlxObject * @param end The world coordinates of the end of the ray * @param func The function, where `index` is the tile's map index, and `tile` is * the tile data at that location, if one exists - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function forEachInRay(start, end, func:(index:Int, tile:Null)->Void) + overload public inline extern function forEachInRay(start, end, func:(index:Int, tile:Null)->Void) { findIndexInRayHelper(start, end, (i, t, e)->{ func(i, t); return false; }); } @@ -362,9 +362,9 @@ class FlxBaseTilemap extends FlxObject * @param end The world coordinates of the end of the ray * @param func The function, where `tile` is the tile data at that location and * `entry` is how the ray entered the tile - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function forEachInRay(start, end, func:(tile:Tile, entry:FlxRayEntry)->Void) + overload public inline extern function forEachInRay(start, end, func:(tile:Tile, entry:FlxRayEntry)->Void) { findIndexInRayHelper(start, end, (i, t, e)->{ func(t, e); return false; }); } @@ -376,9 +376,9 @@ class FlxBaseTilemap extends FlxObject * @param end The world coordinates of the end of the ray * @param func The function, where `index` is the tile's map index, `tile` is the tile data * at that location, if one exists and `entry` is how the ray entered the tile - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function forEachInRay(start, end, func:(index:Int, tile:Null, entry:FlxRayEntry)->Void) + overload public inline extern function forEachInRay(start, end, func:(index:Int, tile:Null, entry:FlxRayEntry)->Void) { findIndexInRayHelper(start, end, (i, t, e)->{ func(i, t, e); return false; }); } @@ -393,9 +393,9 @@ class FlxBaseTilemap extends FlxObject * @param end The world coordinates of the end of the ray * @param func The stopping condition, where `tile` is the tile data at that location * @return The index of the found tile - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function findIndexInRay(start, end, func:(tile:Tile)->Bool) + overload public inline extern function findIndexInRay(start, end, func:(tile:Tile)->Bool) { return findIndexInRayHelper(start, end, (i, t, e)->func(t)); } @@ -409,9 +409,9 @@ class FlxBaseTilemap extends FlxObject * @param func The stopping condition, where `index` is the tile's map index, `tile` is the * tile data at that location, if one exists * @return The index of the found tile - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function findIndexInRay(start, end, func:(index:Int, tile:Null)->Bool) + overload public inline extern function findIndexInRay(start, end, func:(index:Int, tile:Null)->Bool) { return findIndexInRayHelper(start, end, (i, t, e)->func(i, t)); } @@ -425,9 +425,9 @@ class FlxBaseTilemap extends FlxObject * @param func The stopping condition, where `index` is the tile's map index, `tile` is the tile data * at that location, if one exists, `entry` is how the ray entered the tile * @return The index of the found tile - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function findIndexInRay(start, end, func) + overload public inline extern function findIndexInRay(start, end, func) { return findIndexInRayHelper(start, end, func); } @@ -453,9 +453,9 @@ class FlxBaseTilemap extends FlxObject * @param end The world coordinates of the end of the ray * @param func The stopping condition, where `tile` is the tile data at that location * @return The result of the ray, whether it reached the end or was stopped, and where - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function findInRay(start, end, func:(Tile)->Bool) + overload public inline extern function findInRay(start, end, func:(Tile)->Bool) { return findInRayHelper(start, end, (_, t, _)->func(t)); } @@ -469,9 +469,9 @@ class FlxBaseTilemap extends FlxObject * @param func The stopping condition, where `index` is the tile's map index, `tile` is the * tile data at that location, if one exists * @return The result of the ray, whether it reached the end or was stopped, and where - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function findInRay(start, end, func) + overload public inline extern function findInRay(start, end, func) { return findInRayHelper(start, end, (i:Int, t:Tile, _)->func(i, t)); } @@ -485,9 +485,9 @@ class FlxBaseTilemap extends FlxObject * @param func The stopping condition, where `index` is the tile's map index, `tile` is the tile data * at that location, if one exists, `entry` is how the ray entered the tile * @return The result of the ray, whether it reached the end or was stopped, and where - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function findInRay(start, end, func) + overload public inline extern function findInRay(start, end, func) { return findInRayHelper(start, end, func); } @@ -638,9 +638,9 @@ class FlxBaseTilemap extends FlxObject * @param startRow The row to check from * @param endRow The row to check to * @param func The function, where `tile` is the tile data at that location - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function forEachIndexInColumn(column, startRow, endRow, func:(tile:Tile)->Void) + overload public inline extern function forEachIndexInColumn(column, startRow, endRow, func:(tile:Tile)->Void) { findIndexInColumnHelper(column, startRow, endRow, (i, t)->{ if (t != null) func(t); return false; }); } @@ -653,9 +653,9 @@ class FlxBaseTilemap extends FlxObject * @param endRow The row to check to * @param func The function, where `index` is the tile's map index, and `tile` is * the tile data at that location, if one exists - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function forEachIndexInColumn(column, startRow, endRow, func:(index:Int, tile:Null)->Void) + overload public inline extern function forEachIndexInColumn(column, startRow, endRow, func:(index:Int, tile:Null)->Void) { findIndexInColumnHelper(column, startRow, endRow, (i, t)->{ func(i, t); return false; }); } @@ -671,9 +671,9 @@ class FlxBaseTilemap extends FlxObject * @param endRow The row to check to * @param func The stopping condition, where `tile` is the tile data at that location * @return The found tile - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function findInColumn(column, startRow, endRow, func:(tile:Tile)->Bool) + overload public inline extern function findInColumn(column, startRow, endRow, func:(tile:Tile)->Bool) { return findInColumnHelper(column, startRow, endRow, func); } @@ -702,9 +702,9 @@ class FlxBaseTilemap extends FlxObject * @param endRow The row to check to * @param func The stopping condition, where `tile` is the tile data at that location * @return The index of the found tile - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function findIndexInColumn(column, startRow, endRow, func:(tile:Tile)->Bool) + overload public inline extern function findIndexInColumn(column, startRow, endRow, func:(tile:Tile)->Bool) { return findIndexInColumnHelper(column, startRow, endRow, (i, t)->func(t)); } @@ -719,9 +719,9 @@ class FlxBaseTilemap extends FlxObject * @param func The stopping condition, where `index` is the tile's map index, and * `tile` is the tile data at that location, if one exists * @return The index of the found tile - * @since 5.10.0 + * @since 6.2.0 */ - inline overload extern public function findIndexInColumn(column, startRow, endRow, func:(index:Int, tile:Null)->Bool) + overload public inline extern function findIndexInColumn(column, startRow, endRow, func:(index:Int, tile:Null)->Bool) { return findIndexInColumnHelper(column, startRow, endRow, func); } @@ -846,7 +846,7 @@ class FlxBaseTilemap extends FlxObject */ public function forEachOverlappingTile(object:FlxObject, func:(tile:Tile)->Void, ?position:FlxPoint):Bool { - throw "overlapsWithCallback must be implemented"; + throw "forEachOverlappingTile must be implemented"; } @:deprecated("overlapsWithCallback is deprecated, use objectOverlapsTiles(object, callback, pos), instead") // 5.9.0 @@ -1314,7 +1314,7 @@ class FlxBaseTilemap extends FlxObject * @param row The grid Y location, in tiles * @since 5.9.0 */ - public overload extern inline function getMapIndex(column:Int, row:Int):Int + overload public inline extern function getMapIndex(column:Int, row:Int):Int { return tileExists(column, row) ? (row * widthInTiles + column) : -1; } @@ -1328,7 +1328,7 @@ class FlxBaseTilemap extends FlxObject * @param worldPos A location in the world * @since 5.9.0 */ - public overload extern inline function getMapIndex(worldPos:FlxPoint):Int + overload public inline extern function getMapIndex(worldPos:FlxPoint):Int { return getMapIndexAt(worldPos.x, worldPos.y); } @@ -1376,7 +1376,7 @@ class FlxBaseTilemap extends FlxObject * @param row The grid Y location, in tiles * @since 5.9.0 */ - public overload extern inline function tileExists(column:Int, row:Int):Bool + overload public inline extern function tileExists(column:Int, row:Int):Bool { return columnExists(column) && rowExists(row); } @@ -1389,7 +1389,7 @@ class FlxBaseTilemap extends FlxObject * @param mapIndex The desired location in the map * @since 5.9.0 */ - public overload extern inline function tileExists(mapIndex:Int):Bool + overload public inline extern function tileExists(mapIndex:Int):Bool { return mapIndex >= 0 && mapIndex < _data.length; } @@ -1400,7 +1400,7 @@ class FlxBaseTilemap extends FlxObject * @param worldPos A location in the map * @since 5.9.0 */ - public overload extern inline function tileExists(worldPos:FlxPoint):Bool + overload public inline extern function tileExists(worldPos:FlxPoint):Bool { return tileExistsAt(worldPos.x, worldPos.y); } @@ -1423,7 +1423,7 @@ class FlxBaseTilemap extends FlxObject * @param column The grid X location, in tiles * @since 5.9.0 */ - public overload extern inline function columnExists(column:Int):Bool + overload public inline extern function columnExists(column:Int):Bool { return column >= 0 && column < widthInTiles; } @@ -1445,7 +1445,7 @@ class FlxBaseTilemap extends FlxObject * @param row The grid Y location, in tiles * @since 5.9.0 */ - public overload extern inline function rowExists(row:Int):Bool + overload public inline extern function rowExists(row:Int):Bool { return row >= 0 && row < heightInTiles; } @@ -1469,7 +1469,7 @@ class FlxBaseTilemap extends FlxObject * @param row The grid Y location, in tiles * @since 5.9.0 */ - public overload extern inline function getTileData(column:Int, row:Int):Null + overload public inline extern function getTileData(column:Int, row:Int):Null { return getTileData(getMapIndex(column, row)); } @@ -1486,7 +1486,7 @@ class FlxBaseTilemap extends FlxObject * @param mapIndex The desired location in the map * @since 5.9.0 */ - public overload extern inline function getTileData(mapIndex:Int):Null + overload public inline extern function getTileData(mapIndex:Int):Null { return _tileObjects[getTileIndex(mapIndex)]; } @@ -1501,7 +1501,7 @@ class FlxBaseTilemap extends FlxObject * @param worldPos A location in the world * @since 5.9.0 */ - public overload extern inline function getTileData(worldPos:FlxPoint):Null + overload public inline extern function getTileData(worldPos:FlxPoint):Null { return getTileDataAt(worldPos.x, worldPos.y); } @@ -1517,7 +1517,7 @@ class FlxBaseTilemap extends FlxObject * @param worldY A Y coordinate in the world * @since 5.9.0 */ - public overload extern inline function getTileDataAt(worldX:Float, worldY:Float):Null + overload public inline extern function getTileDataAt(worldX:Float, worldY:Float):Null { return _tileObjects[getTileIndexAt(worldX, worldY)]; } @@ -1531,7 +1531,7 @@ class FlxBaseTilemap extends FlxObject * @return The tile index of the tile at this location * @since 5.9.0 */ - public overload extern inline function getTileIndex(column:Int, row:Int):Int + overload public inline extern function getTileIndex(column:Int, row:Int):Int { return getTileIndex(getMapIndex(column, row)); } @@ -1546,7 +1546,7 @@ class FlxBaseTilemap extends FlxObject * @return The tileIndex of the tile with this `mapIndex` * @since 5.9.0 */ - public overload extern inline function getTileIndex(mapIndex:Int):Int + overload public inline extern function getTileIndex(mapIndex:Int):Int { return tileExists(mapIndex) ? _data[mapIndex] : -1; } @@ -1559,7 +1559,7 @@ class FlxBaseTilemap extends FlxObject * @return The tileIndex of the tile at this location * @since 5.9.0 */ - public overload extern inline function getTileIndex(worldPos:FlxPoint):Int + overload public inline extern function getTileIndex(worldPos:FlxPoint):Int { return getTileIndexAt(worldPos.x, worldPos.y); } @@ -1589,7 +1589,7 @@ class FlxBaseTilemap extends FlxObject * @return The world position of the matching tile * @since 5.9.0 */ - public overload extern inline function getTilePos(mapIndex:Int, midpoint = false):Null + overload public inline extern function getTilePos(mapIndex:Int, midpoint = false):Null { return tileExists(mapIndex) ? getTilePos(getColumn(mapIndex), getRow(mapIndex), midpoint) : null; } @@ -1606,7 +1606,7 @@ class FlxBaseTilemap extends FlxObject * @return The world position of the matching tile * @since 5.9.0 */ - public overload extern inline function getTilePos(column:Int, row:Int, midpoint = false):FlxPoint + overload public inline extern function getTilePos(column:Int, row:Int, midpoint = false):FlxPoint { return FlxPoint.get(getColumnPos(column, midpoint), getRowPos(row, midpoint)); } @@ -1622,7 +1622,7 @@ class FlxBaseTilemap extends FlxObject * @return The world position of the overlapping tile * @since 5.9.0 */ - public overload extern inline function getTilePos(worldPos:FlxPoint, midpoint = false):FlxPoint + overload public inline extern function getTilePos(worldPos:FlxPoint, midpoint = false):FlxPoint { return getTilePosAt(worldPos.x, worldPos.y, midpoint); } @@ -1778,7 +1778,7 @@ class FlxBaseTilemap extends FlxObject * @return Whether or not the tile was actually changed. * @since 5.9.0 */ - public overload extern inline function setTileIndex(mapIndex:Int, tileIndex:Int, redraw = true):Bool + overload public inline extern function setTileIndex(mapIndex:Int, tileIndex:Int, redraw = true):Bool { return setTileHelper(mapIndex, tileIndex, redraw); } @@ -1793,7 +1793,7 @@ class FlxBaseTilemap extends FlxObject * @return Whether or not the tile was actually changed. * @since 5.9.0 */ - public overload extern inline function setTileIndex(column:Int, row:Int, tileIndex:Int, redraw = true):Bool + overload public inline extern function setTileIndex(column:Int, row:Int, tileIndex:Int, redraw = true):Bool { return setTileHelper(getMapIndex(column, row), tileIndex, redraw); } @@ -1807,7 +1807,7 @@ class FlxBaseTilemap extends FlxObject * @return Whether or not the tile was actually changed. * @since 5.9.0 */ - public overload extern inline function setTileIndex(worldPos:FlxPoint, tileIndex:Int, redraw = true):Bool + overload public inline extern function setTileIndex(worldPos:FlxPoint, tileIndex:Int, redraw = true):Bool { return setTileIndexAt(worldPos.x, worldPos.y, tileIndex, redraw); } From be78d697a16aafd598fe6123f35763f145e4568e Mon Sep 17 00:00:00 2001 From: GeoKureli-BlackbookPro Date: Sat, 20 Dec 2025 12:47:43 -0600 Subject: [PATCH 15/16] remove temp test code --- flixel/tile/FlxBaseTilemap.hx | 2 -- 1 file changed, 2 deletions(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index 906b981c06..e46a25841d 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -685,8 +685,6 @@ class FlxBaseTilemap extends FlxObject return null; final data = getTileData(index); - if (data == null) - throw 'Unexpected null tile at $index'; // internal error return data; } From ed8bbe1e7fa90339a98cf8b8b6a37c69c826ba36 Mon Sep 17 00:00:00 2001 From: GeoKureli-BlackbookPro Date: Sat, 20 Dec 2025 12:48:14 -0600 Subject: [PATCH 16/16] add orient arg to getTileData mthods --- flixel/tile/FlxBaseTilemap.hx | 41 ++++++++++++++++++----------------- flixel/tile/FlxTilemap.hx | 9 ++++++++ 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/flixel/tile/FlxBaseTilemap.hx b/flixel/tile/FlxBaseTilemap.hx index e46a25841d..2767c4e1f7 100644 --- a/flixel/tile/FlxBaseTilemap.hx +++ b/flixel/tile/FlxBaseTilemap.hx @@ -19,7 +19,7 @@ using StringTools; using flixel.tile.FlxBaseTilemap.AmbiIntIterator; @:autoBuild(flixel.system.macros.FlxMacroUtil.deprecateOverride("overlapsWithCallback", "overlapsWithCallback is deprecated, use objectOverlapsTiles")) -class FlxBaseTilemap extends FlxObject +abstract class FlxBaseTilemap extends FlxObject { /** * Set this flag to use one of the 16-tile binary auto-tile algorithms (OFF, AUTO, or ALT). @@ -739,7 +739,7 @@ class FlxBaseTilemap extends FlxObject for (row in startRow.iter(endRow)) { final index = getMapIndex(column, row); - final tile = getTileData(index); + final tile = getTileData(index, true); if (func(index, tile)) return index; } @@ -1465,11 +1465,12 @@ class FlxBaseTilemap extends FlxObject * * @param column The grid X location, in tiles * @param row The grid Y location, in tiles + * @param orient If `true`, positions the tile in the world, useful for collision * @since 5.9.0 */ - overload public inline extern function getTileData(column:Int, row:Int):Null + overload public inline extern function getTileData(column:Int, row:Int, orient = false):Null { - return getTileData(getMapIndex(column, row)); + return getTileData(getMapIndex(column, row), orient); } /** @@ -1477,47 +1478,47 @@ class FlxBaseTilemap extends FlxObject * if the `mapIndex` is invalid, the result is `null` * * **Note:** A tile's `mapIndex` can be calculated via `row * widthInTiles + column` - * - * **Note:** The reulting tile's `x`, `y`, `width` and `height` will not be accurate. - * You can call `tile.orient` or similar methods * * @param mapIndex The desired location in the map + * @param orient If `true`, positions the tile in the world, useful for collision * @since 5.9.0 */ - overload public inline extern function getTileData(mapIndex:Int):Null + overload public inline extern function getTileData(mapIndex:Int, orient = false):Null { - return _tileObjects[getTileIndex(mapIndex)]; + final tile = _tileObjects[getTileIndex(mapIndex)]; + if (orient) + orientTile(tile, mapIndex); + + return tile; } + abstract function orientTile(tile:Null, mapIndex:Int):Null; + /** * Finds the tile instance with the given world location, if the * coordinate does not overlap the tilemap, the result is `null` * - * **Note:** The reulting tile's `x`, `y`, `width` and `height` will not be accurate. - * You can call `tile.orient` or similar methods - * * @param worldPos A location in the world + * @param orient If `true`, positions the tile in the world, useful for collision * @since 5.9.0 */ - overload public inline extern function getTileData(worldPos:FlxPoint):Null + overload public inline extern function getTileData(worldPos:FlxPoint, orient = false):Null { - return getTileDataAt(worldPos.x, worldPos.y); + return getTileDataAt(worldPos.x, worldPos.y, orient); } /** * Finds the tile instance with the given world location, if the * coordinate does not overlap the tilemap, the result is `null` * - * **Note:** The reulting tile's `x`, `y`, `width` and `height` will not be accurate. - * You can call `tile.orient` or similar methods - * * @param worldX An X coordinate in the world - * @param worldY A Y coordinate in the world + * @param worldY An Y coordinate in the world + * @param orient If `true`, positions the tile in the world, useful for collision * @since 5.9.0 */ - overload public inline extern function getTileDataAt(worldX:Float, worldY:Float):Null + overload public inline extern function getTileDataAt(worldX:Float, worldY:Float, orient = false):Null { - return _tileObjects[getTileIndexAt(worldX, worldY)]; + return getTileData(getMapIndexAt(worldX, worldY), orient); } /** diff --git a/flixel/tile/FlxTilemap.hx b/flixel/tile/FlxTilemap.hx index 981474c504..d309580d10 100644 --- a/flixel/tile/FlxTilemap.hx +++ b/flixel/tile/FlxTilemap.hx @@ -1195,6 +1195,15 @@ class FlxTypedTilemap extends FlxBaseTilemap } #end + + function orientTile(tile:Null, mapIndex:Int):Null + { + if (tile != null) + tile.orientByIndex(mapIndex); + + return tile; + } + /** * Internal function used in setTileIndex() and the constructor to update the map. *