From 5ab4cc55be460fb6d900a48f0f38ea75a1c0a29b Mon Sep 17 00:00:00 2001 From: Johannes Baum Date: Mon, 27 Nov 2023 18:29:58 +0100 Subject: [PATCH] #451 fix depth for containers --- src/GridEngine.test.ts | 33 ++------ .../GridCharacterPhaser.test.ts | 84 ++++++++++++------- .../GridCharacterPhaser.ts | 48 +++++++++-- .../GridTilemapPhaser/GridTilemapPhaser.ts | 2 +- src/Utils/MockFactory/MockFactory.ts | 29 ++++++- 5 files changed, 129 insertions(+), 67 deletions(-) diff --git a/src/GridEngine.test.ts b/src/GridEngine.test.ts index 75c46fb5..a076610e 100644 --- a/src/GridEngine.test.ts +++ b/src/GridEngine.test.ts @@ -47,14 +47,17 @@ jest.mock("../package.json", () => ({ import { GridEngine, PathfindingOptions } from "./GridEngine.js"; import { NoPathFoundStrategy } from "./Pathfinding/NoPathFoundStrategy.js"; import { PathBlockedStrategy } from "./Pathfinding/PathBlockedStrategy.js"; -import { createSpriteMock } from "./Utils/MockFactory/MockFactory.js"; +import { + createContainerMock, + createSpriteMock, +} from "./Utils/MockFactory/MockFactory.js"; import { createPhaserTilemapStub } from "./Utils/MockFactory/MockPhaserTilemap.js"; import { GridEngineStatePhaser } from "./GridEnginePhaser/GridEngineStatePhaser.js"; describe("GridEngine", () => { let gridEngine: GridEngine; let sceneMock; - let playerSpriteMock; + let playerSpriteMock: Phaser.GameObjects.Sprite; let consoleLogBackup; afterEach(() => { @@ -88,25 +91,7 @@ describe("GridEngine", () => { add: { sprite: jest.fn().mockReturnValue(mockNewSprite) }, }; - playerSpriteMock = { - x: 10, - y: 12, - displayWidth: 20, - displayHeight: 40, - width: 20, - setOrigin: jest.fn(), - texture: { - source: [{ width: 240 }], - }, - setFrame: jest.fn(function (name) { - this.frame.name = name; - }), - setDepth: jest.fn(), - scale: 2, - frame: { - name: "1", - }, - } as any; + playerSpriteMock = createSpriteMock(); gridEngine = new GridEngine(sceneMock); gridEngine.create(createDefaultMockWithLayer(undefined), { @@ -131,7 +116,7 @@ describe("GridEngine", () => { }); it("should init player", () => { - const containerMock = { setDepth: jest.fn() }; + const containerMock = createContainerMock(); gridEngine.create(createDefaultMockWithLayer(undefined), { characters: [ { @@ -191,7 +176,7 @@ describe("GridEngine", () => { }); it("should init player with facingDirection", () => { - const containerMock = { setDepth: jest.fn() }; + const containerMock = createContainerMock(); gridEngine.create(createDefaultMockWithLayer(undefined), { characters: [ { @@ -560,7 +545,7 @@ describe("GridEngine", () => { it("should get facing position", () => { const rightStandingFrameNo = 25; - playerSpriteMock.setFrame.mockClear(); + (playerSpriteMock.setFrame as any).mockClear(); gridEngine.turnTowards("player", Direction.RIGHT); expect(playerSpriteMock.setFrame).toHaveBeenCalledWith( rightStandingFrameNo, diff --git a/src/GridEnginePhaser/GridCharacterPhaser/GridCharacterPhaser.test.ts b/src/GridEnginePhaser/GridCharacterPhaser/GridCharacterPhaser.test.ts index a4e7293f..1a704a8d 100644 --- a/src/GridEnginePhaser/GridCharacterPhaser/GridCharacterPhaser.test.ts +++ b/src/GridEnginePhaser/GridCharacterPhaser/GridCharacterPhaser.test.ts @@ -5,7 +5,10 @@ import { GridCharacterPhaser } from "./GridCharacterPhaser.js"; import * as Phaser from "phaser"; import { Direction, NumberOfDirections } from "../../Direction/Direction.js"; import { CharacterData, GridEngineHeadless } from "../../GridEngine.js"; -import { createSpriteMock } from "../../Utils/MockFactory/MockFactory.js"; +import { + createContainerMock, + createSpriteMock, +} from "../../Utils/MockFactory/MockFactory.js"; import { take } from "rxjs/operators"; import { PhaserTilemap } from "../../GridTilemap/Phaser/PhaserTilemap.js"; import { GridTilemapPhaser } from "../GridTilemapPhaser/GridTilemapPhaser.js"; @@ -118,7 +121,7 @@ describe("GridCharacterPhaser", () => { it("should create a grid character", () => { const walkingAnimationMock = {} as any; const startPos = { x: 5, y: 6 }; - const containerMock = { x: 0, y: 0, setDepth: jest.fn() } as any; + const containerMock = createContainerMock(); const charData = { id: "charID", sprite: spriteMock, @@ -237,7 +240,7 @@ describe("GridCharacterPhaser", () => { it("should keep a container", () => { const { gridCharPhaser } = createChar({}, false); - const containerMock = {} as any; + const containerMock = createContainerMock(); gridCharPhaser.setContainer(containerMock); expect(gridCharPhaser.getContainer()).toBe(containerMock); @@ -303,7 +306,7 @@ describe("GridCharacterPhaser", () => { const oldAnimation = gridCharPhaser.getAnimation(); gridCharPhaser.setSprite(newSpriteMock); - newSpriteMock.setFrame.mockReset(); + (newSpriteMock.setFrame as any).mockReset(); (oldAnimation?.frameChange() as any).next(13); (gridCharPhaser.getAnimation()?.frameChange() as any).next(13); @@ -349,7 +352,12 @@ describe("GridCharacterPhaser", () => { gridCharPhaser.setSprite(newSpriteMock); - checkSpriteDepth(newSpriteMock, charLayerDepth, "0000"); + checkSpriteDepth( + newSpriteMock, + newSpriteMock.displayHeight, + charLayerDepth, + "0000", + ); }); it("should set depth of sprite on char layer", () => { @@ -364,15 +372,17 @@ describe("GridCharacterPhaser", () => { gridCharPhaser.setSprite(newSpriteMock); - checkSpriteDepth(newSpriteMock, charLayerDepth, "0000"); + checkSpriteDepth( + newSpriteMock, + newSpriteMock.displayHeight, + charLayerDepth, + "0000", + ); }); it("should set depth of container", () => { - const containerMock = { - y: 20, - displayHeight: 21, - setDepth: jest.fn(), - } as any; + const height = 21; + const containerMock = createContainerMock(0, 20, height); const startPos = { x: 2, y: 2 }; const charData = { container: containerMock, @@ -385,7 +395,7 @@ describe("GridCharacterPhaser", () => { gridCharPhaser.setSprite(newSpriteMock); - checkSpriteDepth(containerMock, uppermostCharLayerDepth, "0000"); + checkSpriteDepth(containerMock, height, uppermostCharLayerDepth, "0000"); }); it("should set depth of pos above for overlay sprite", () => { @@ -400,17 +410,18 @@ describe("GridCharacterPhaser", () => { gridCharPhaser.setSprite(newSpriteMock); - checkSpriteDepth(overlaySpriteMock, charLayerDepth, "00000"); + checkSpriteDepth( + overlaySpriteMock, + overlaySpriteMock.displayHeight, + charLayerDepth, + "00000", + ); }); }); describe("On pixel position change", () => { it("should update container pixel pos", () => { - const containerMock = { - x: 0, - y: 0, - setDepth: jest.fn(), - } as any; + const containerMock = createContainerMock(); const charData = { container: containerMock, offsetX: 10, @@ -658,7 +669,12 @@ describe("GridCharacterPhaser", () => { gridEngineHeadless.update(1000, 10); gridCharPhaser.update(10); - checkSpriteDepth(spriteMock, charLayerDepth, "0000"); + checkSpriteDepth( + spriteMock, + spriteMock.displayHeight, + charLayerDepth, + "0000", + ); }); it("should set depth of sprite on char layer", () => { @@ -675,15 +691,17 @@ describe("GridCharacterPhaser", () => { gridEngineHeadless.update(1000, 10); gridCharPhaser.update(10); - checkSpriteDepth(spriteMock, charLayerDepth, "0000"); + checkSpriteDepth( + spriteMock, + spriteMock.displayHeight, + charLayerDepth, + "0000", + ); }); it("should set depth of container", () => { - const containerMock = { - y: 20, - displayHeight: 21, - setDepth: jest.fn(), - } as any; + const height = 21; + const containerMock = createContainerMock(0, 20, height); const startPos = { x: 2, y: 2 }; const charData = { container: containerMock, @@ -696,7 +714,7 @@ describe("GridCharacterPhaser", () => { gridEngineHeadless.move("charID", Direction.RIGHT); gridCharPhaser.update(10); - checkSpriteDepth(containerMock, uppermostCharLayerDepth, "0000"); + checkSpriteDepth(containerMock, height, uppermostCharLayerDepth, "0000"); }); describe("for overlay sprite", () => { @@ -719,7 +737,12 @@ describe("GridCharacterPhaser", () => { gridEngineHeadless.update(1000, 10); gridCharPhaser.update(10); - checkSpriteDepth(overlaySpriteMock, lowerCharLayerDepth, "00000"); + checkSpriteDepth( + overlaySpriteMock, + overlaySpriteMock.displayHeight, + lowerCharLayerDepth, + "00000", + ); }); }); }); @@ -831,12 +854,13 @@ describe("GridCharacterPhaser", () => { }); function checkSpriteDepth( - spriteMock, + gameObject: Phaser.GameObjects.Container | Phaser.GameObjects.Sprite, + height: number, charLayerDepth: number, zeroPrefix: string, ) { - const pixelDepth = spriteMock.y + spriteMock.displayHeight; - expect(spriteMock.setDepth).toHaveBeenCalledWith( + const pixelDepth = gameObject.y + height; + expect(gameObject.setDepth).toHaveBeenCalledWith( +`${charLayerDepth}.${zeroPrefix}${pixelDepth}`, ); } diff --git a/src/GridEnginePhaser/GridCharacterPhaser/GridCharacterPhaser.ts b/src/GridEnginePhaser/GridCharacterPhaser/GridCharacterPhaser.ts index b0e4cde4..9f5eab50 100644 --- a/src/GridEnginePhaser/GridCharacterPhaser/GridCharacterPhaser.ts +++ b/src/GridEnginePhaser/GridCharacterPhaser/GridCharacterPhaser.ts @@ -243,12 +243,17 @@ export class GridCharacterPhaser { } private updateDepth() { - const gameObject = this.getGameObj(); + const gameObj = this.getGameObj(); + if (!gameObj) return; - if (!gameObject) return; const position = new Vector2(this.geHeadless.getPosition(this.charData.id)); const layer = this.geHeadless.getCharLayer(this.charData.id); - this.setDepth(gameObject, { position, layer }); + + if (this.container) { + this.setContainerDepth(this.container, { position, layer }); + } else if (this.sprite) { + this.setSpriteDepth(this.sprite, { position, layer }); + } const layerOverlaySprite = this.getLayerOverlaySprite(); if (layerOverlaySprite) { @@ -256,22 +261,47 @@ export class GridCharacterPhaser { ...position, y: position.y - 1, }); - this.setDepth(layerOverlaySprite, { + this.setSpriteDepth(layerOverlaySprite, { position: posAbove, layer, }); } } - private setDepth(gameObject: GameObject, position: LayerVecPos): void { - gameObject.setDepth( + private setSpriteDepth( + sprite: Phaser.GameObjects.Sprite, + position: LayerVecPos, + ): void { + sprite.setDepth( this.tilemap.getDepthOfCharLayer(this.getTransitionLayer(position)) + - this.getPaddedPixelDepth(gameObject), + this.getPaddedPixelDepthSprite(sprite), ); } - private getPaddedPixelDepth(gameObject: GameObject): number { - return Utils.shiftPad(gameObject.y + gameObject.displayHeight, 7); + private setContainerDepth( + container: Phaser.GameObjects.Container, + position: LayerVecPos, + ): void { + container.setDepth( + this.tilemap.getDepthOfCharLayer(this.getTransitionLayer(position)) + + this.getPaddedPixelDepthContainer(container), + ); + } + + private getPaddedPixelDepthSprite(sprite: Phaser.GameObjects.Sprite): number { + return Utils.shiftPad( + sprite.y + sprite.displayHeight, + GridTilemapPhaser.Z_INDEX_PADDING, + ); + } + + private getPaddedPixelDepthContainer( + container: Phaser.GameObjects.Container, + ): number { + return Utils.shiftPad( + container.y + container.getBounds().height, + GridTilemapPhaser.Z_INDEX_PADDING, + ); } private getTransitionLayer(position: LayerVecPos): CharLayer { diff --git a/src/GridEnginePhaser/GridTilemapPhaser/GridTilemapPhaser.ts b/src/GridEnginePhaser/GridTilemapPhaser/GridTilemapPhaser.ts index b6734c8f..ae77c892 100644 --- a/src/GridEnginePhaser/GridTilemapPhaser/GridTilemapPhaser.ts +++ b/src/GridEnginePhaser/GridTilemapPhaser/GridTilemapPhaser.ts @@ -7,7 +7,7 @@ export class GridTilemapPhaser { private static readonly ALWAYS_TOP_PROP_NAME = "ge_alwaysTop"; private static readonly CHAR_LAYER_PROP_NAME = "ge_charLayer"; private static readonly HEIGHT_SHIFT_PROP_NAME = "ge_heightShift"; - private static readonly Z_INDEX_PADDING = 7; + static readonly Z_INDEX_PADDING = 7; private charLayerDepths = new Map(); constructor(private tilemap: Phaser.Tilemaps.Tilemap) { diff --git a/src/Utils/MockFactory/MockFactory.ts b/src/Utils/MockFactory/MockFactory.ts index 64be4132..417cde1d 100644 --- a/src/Utils/MockFactory/MockFactory.ts +++ b/src/Utils/MockFactory/MockFactory.ts @@ -7,6 +7,15 @@ import { LayerVecPos } from "../../Pathfinding/ShortestPathAlgorithm.js"; import { TileLayer, Tile, Tilemap } from "../../GridTilemap/Tilemap.js"; import { MockTile, MockTileLayer, MockTilemap } from "./MockTilemap.js"; +export interface LayerData { + name?: string; + properties?: Record; + height?: number; + width?: number; + scale?: number; + tilesets?: string[]; + data?: Array>; +} export interface TileCost { ge_cost?: number; ge_cost_left?: number; @@ -30,7 +39,7 @@ export interface CostMapLayer { costMap: CostMap; } -export function createSpriteMock() { +export function createSpriteMock(): Phaser.GameObjects.Sprite { return { x: 10, y: 12, @@ -50,10 +59,24 @@ export function createSpriteMock() { frame: { name: "1", }, - } as any; + } as unknown as Phaser.GameObjects.Sprite; +} + +export function createContainerMock( + x = 0, + y = 0, + height = 0, +): Phaser.GameObjects.Container { + return { + x, + y, + displayHeight: 0, + setDepth: jest.fn(), + getBounds: jest.fn(() => ({ height })), + } as unknown as Phaser.GameObjects.Container; } -export function createMockLayer(layerData: any): TileLayer { +export function createMockLayer(layerData: LayerData): TileLayer { return new MockTileLayer( layerData.name, layerData.properties,