UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

166 lines (123 loc) 5.87 kB
import { assert } from "../../../core/assert.js"; import { max2 } from "../../../core/math/max2.js"; import { min2 } from "../../../core/math/min2.js"; import { TileMoveProgram } from "./TileMoveProgram.js"; import { TileMoveInstruction } from "./TileMoveInstruction.js"; import { aabb2_overlap_exists } from "../../../core/geom/2d/aabb/aabb2_overlap_exists.js"; import { aabb2_contains } from "../../../core/geom/2d/aabb/aabb2_contains.js"; /** * * @param {Rectangle} tile * @param {number} targetX * @param {number} targetY * @param {TileGrid} source * @param {TileGrid} target * @returns {TileMoveProgram} */ export function computeTileGridMove(tile, targetX, targetY, source, target) { assert.isNumber(targetX, 'targetX'); assert.isNumber(targetY, 'targetY'); assert.defined(tile, 'tile'); assert.defined(source, 'source'); assert.defined(target, 'target'); //calculate new area for the tile const futureTileX0 = targetX; const futureTileY0 = targetY; const futureTileX1 = tile.size.x + futureTileX0; const futureTileY1 = tile.size.y + futureTileY0; const tilePositionDeltaX = targetX - tile.position.x; const tilePositionDeltaY = targetY - tile.position.y; /** * * @type {Rectangle[]} */ const occludedTargetTiles = []; //find occluded tiles const targetTiles = target.tiles; const targetStartingTileCount = targetTiles.length; const sourceTiles = source.tiles; const sourceStartingTileCount = sourceTiles.length; for (let i = 0; i < targetStartingTileCount; i++) { const targetTile = targetTiles.get(i); if (targetTile === tile) { //skip self continue; } if (targetTile._overlaps(futureTileX0, futureTileY0, futureTileX1, futureTileY1)) { //occlusion detected occludedTargetTiles.push(targetTile); } } const result = new TileMoveProgram(); //add initial move command result.add(new TileMoveInstruction(tile, targetX, targetY, target, source)); const occlusionCount = occludedTargetTiles.length; if (occlusionCount > 0) { //find AABB extents of the overlapped tiles const firstOccludedElement = occludedTargetTiles[0]; let occlusionRegionX0 = firstOccludedElement.position.x; let occlusionRegionY0 = firstOccludedElement.position.y; let occlusionRegionX1 = occlusionRegionX0 + firstOccludedElement.size.x; let occlusionRegionY1 = occlusionRegionY0 + firstOccludedElement.size.y; for (let i = 1; i < occlusionCount; i++) { const occludedElement = occludedTargetTiles[i]; const x0 = occludedElement.position.x; const y0 = occludedElement.position.y; const x1 = x0 + occludedElement.size.x; const y1 = y0 + occludedElement.size.y; occlusionRegionX0 = min2(occlusionRegionX0, x0); occlusionRegionY0 = min2(occlusionRegionY0, y0); occlusionRegionX1 = max2(occlusionRegionX1, x1); occlusionRegionY1 = max2(occlusionRegionY1, y1); } //transform target occlusion region to source const sourceOcclusionRegionX0 = occlusionRegionX0 - tilePositionDeltaX; const sourceOcclusionRegionX1 = occlusionRegionX1 - tilePositionDeltaX; const sourceOcclusionRegionY0 = occlusionRegionY0 - tilePositionDeltaY; const sourceOcclusionRegionY1 = occlusionRegionY1 - tilePositionDeltaY; if (sourceOcclusionRegionX0 < 0 || sourceOcclusionRegionX1 > source.size.x || sourceOcclusionRegionY0 < 0 || sourceOcclusionRegionY1 > source.size.y) { //source region is off the grid, swap is not possible return null; } //find all tiles in the source grid that match the occlusion zone for (let i = 0; i < sourceStartingTileCount; i++) { const sourceTile = sourceTiles.get(i); if (sourceTile === tile) { //skip self continue; } const x0 = sourceTile.position.x; const y0 = sourceTile.position.y; const x1 = x0 + sourceTile.size.x; const y1 = y0 + sourceTile.size.y; if (!aabb2_overlap_exists(sourceOcclusionRegionX0, sourceOcclusionRegionY0, sourceOcclusionRegionX1, sourceOcclusionRegionY1, x0, y0, x1, y1)) { //tile is outside of the transfer area continue; } if (!aabb2_contains(sourceOcclusionRegionX0, sourceOcclusionRegionY0, sourceOcclusionRegionX1, sourceOcclusionRegionY1, x0, y0, x1, y1)) { //tile only partially fits the occlusion region, no swap is possible return null; } const targetX = x0 + tilePositionDeltaX; const targetY = y0 + tilePositionDeltaY; //move tile to the target area result.add(new TileMoveInstruction(sourceTile, targetX, targetY, target, source)); } //add instructions to move occluded tiles for (let i = 0; i < occlusionCount; i++) { const occludedElement = occludedTargetTiles[i]; const position = occludedElement.position; const px = position.x; const py = position.y; const x = px - tilePositionDeltaX; const y = py - tilePositionDeltaY; result.add(new TileMoveInstruction(occludedElement, x, y, source, target)); } } // if (!ENV_PRODUCTION) { // if (!result.validate()) { // throw new Error(`Program is invalid`); // } // } return result; }