From e22f8ea57f3687ac76b79df3a22040c79363726d Mon Sep 17 00:00:00 2001 From: neki-dev Date: Tue, 3 Oct 2023 12:38:55 +0200 Subject: [PATCH] Added mobile controls to build --- src/assets/sprites/building/icons/confirm.png | Bin 0 -> 550 bytes .../building/icons/confirm_disabled.png | Bin 0 -> 457 bytes src/assets/sprites/building/icons/decline.png | Bin 0 -> 431 bytes .../sprites/building/icons/placeholder.png | Bin 1860 -> 311 bytes src/const/world/world.ts | 2 +- src/game/scenes/world/builder.ts | 153 ++++++++++++------ src/game/scenes/world/entities/npc/npc.ts | 4 +- src/game/scenes/world/entities/sprite.ts | 4 +- src/game/scenes/world/level/level.ts | 18 +++ src/types/world/entities/building.ts | 3 + src/types/world/level/level.ts | 6 + 11 files changed, 137 insertions(+), 53 deletions(-) create mode 100644 src/assets/sprites/building/icons/confirm.png create mode 100644 src/assets/sprites/building/icons/confirm_disabled.png create mode 100644 src/assets/sprites/building/icons/decline.png diff --git a/src/assets/sprites/building/icons/confirm.png b/src/assets/sprites/building/icons/confirm.png new file mode 100644 index 0000000000000000000000000000000000000000..1f1f7ebd45ac0739eaced2b2b08df0f1e401a0f2 GIT binary patch literal 550 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TG^84PRa@r2LTTWuF#Y`OGe>y=kKFTdPz<<%~r8lZU5 zPDvnR-@VC|dzAnD{W<4sOxZ5k*I(~F`E>c*vvo@^W!CN2x%ckm%{PaxzS?#8;oO~f zx;NiwT79)_%1OWW!&b&8LmvU1s8AB*7tGM``}@NsJ>`*3#!`a3t7ax$EXWsQXS=j+ z=8Wmnr=?uXZxP(dz`!Wx>EaloaenILmwC+w0xi~@GqfN2EU4ewO1(V`o4QO^N(#($tM#uKsM!v174J-8<{ih=c}bI;wK9w)tW zio&K3#uq<6pZmf$@L0{xN9L*pR+hS^?-gPu Vu^|4v3otYpJYD@<);T3K0RTQt9(@1+ literal 0 HcmV?d00001 diff --git a/src/assets/sprites/building/icons/confirm_disabled.png b/src/assets/sprites/building/icons/confirm_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..71b87251ae7317c144dcbbd696e70aecb97c18fb GIT binary patch literal 457 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TG`2jv5u0Z<#|NpC2t$O_U@z$+d zuUxru>(;FgA3gvDae=2#p8}=!?Afz>_il)ql`B^Q8CS1f{rK_YpFe*N9z3{d)28Ro zpFeo;;OyD6$BrF)_39PSSfKj8J4u~DE5%EK{DK)8e$ITjdPz@tex#GJlprtL^rTA{ z#Zt=EfBpn2`Q+*17@~20>g3l&O$Gw3zg3Iar*^9;Y}@+ww|#QyV}S)0Gv~=qIB7on z?7Mk;{<^oXvfATt+{k^(^yB-toM>=)&2pkk^v-<6cT;wHml~St3CZU z-WC6}PGR%giHh`#-&1`*+tVt%n!?bN#&|zs~7?@8o5(9?stTIp5v(Tl;;zZ%J?4 z*Zh6>FY9rWR)pv`<1fNf4>9F(nuWRr?U(9YaZ@Ja2H!oOuWd7HYM2XOues4B%IwBx f)BHi1>$Uh35%oDcPdTRneb3gTe~DWM4fQtAHf literal 0 HcmV?d00001 diff --git a/src/assets/sprites/building/icons/decline.png b/src/assets/sprites/building/icons/decline.png new file mode 100644 index 0000000000000000000000000000000000000000..07a1bcaebf96e05376f9393e8a9ccf2861703efb GIT binary patch literal 431 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX3?zBp#Z3TG2?0JKu0Z<#|Nr;Ao$vX&JxYxF zux{1Qdv}2%Si!r+^S>P0|MmFMrv+JGjvPc&^CUm>?Yvn)@~*o*Q0n8Bjjt#6I;`2M z3$#O~B*-tAq2c%Uhf8|OlOvs^1bNw3&76L5WHIH153_BOh^D;F*pEFm*f6=Tb+gAH(_-}gr#rx6gV6V!BDtFf`y_;2A z+grNrMaPrgj(cCt1-gsk<(?ObtEPp_HMF!oe>&^=&7W;PrWyNJN9>jRRmnEj*zL*7 zt5!VyKc9y@+PghyB74b$=Z&j(9{a~9#oeQ75^^+1-p2ERBXi-0#8v! z|M|R=wl-=t?&z+#s}NZt*Sw^#+ud}(g>d`Ej~98ZyWTh-Kh75M>`rID-|;!D0yAEI z3_b7Tuw_}zf&&M)hsN<7?Ql|VZTv7vPh|HK|Nl=k+ayDuSr{$Nc+=~(-PD7rPT+#c zS$VE5#j9>X?_K_eUHjg&Yvz}`EArC6)-Ko=5ZCfjZ0?QcD*}PeWAJqKb6Mw<&;$T4 C4W?TF literal 1860 zcmbVN32f6=7Azl)!)z26LhaigEgp{ZP;TozEE)yjV zHfXdcgX1`CFlvkp!{}MgsGA5AI6+{z4%6yUt)9dUBtgL82LS>l)zA=rp3_#I#1E5yJ_LAW(ony4si#nJ#Y3fuNXv}|wmaaa{6i++jWEDb>7=!jxIm#B!cOMHqQ(c`E!KC(>WXhoFl zqUedlsxra?6FQ9!eu<(PUQl_*sdB2JWO+y9zd~7tbcm9d22SF=w3EYpf-@wIqDY${E409HHnRxzylkeaNlrykKRF6AYv<^3F(fm5CR!?ZP`7G^-+&7DiUkza7KKnoKS4cdlA=>lo zJkbD=WuixvD3Jv&)~V?mN+Z(B%OF;b;ewdO%?+QD?R@jb<<`_v!`n9}F7~CD28WfNOUhk$lF^+GzQaF1b=MlI z<%Jr}n%jnpM=ne++9YkgnjyTG*_&o~7}IgjeK2wFfv(|(*4S*Ub(k0w8`ug`eAym8}M%gkQ5SK4;JFX@(y6#kL2@A16X z-#s;gGGz31y*2o|wdnBbC8ZT#4_KG|t9$>&nL}2;Qe6KdRF!C@Mitk`w6zViqHvxc zY8(9VCn@mGmNRE!l8l!>wOjJio3cg}S6nP`RZ!*r4hz<`v3nzQ|C>9TUTnYDJ~@v+ z?X3QN`RXThHemPR_YW`X$!c7R>OW^JyH9oA szBFNA(--CyrfZ3tkKM|@@hEXk5%jP!zprO(t@@v{S<20ymA = null; - private buildingPreview: Nullable = null; + private buildPreview: Nullable = null; - private buildingPlaceholder: Nullable = null; + private buildPlaceholder: Nullable = null; + + private buildControls: Nullable = null; private buildings: Partial> = {}; @@ -72,9 +74,9 @@ export class Builder extends EventEmitter implements IBuilder { public update() { if (this.isCanBuild()) { if (this.isBuild) { - this.updateAssumedPosition(); + this.updateSupposedPosition(); this.updateBuildAreaPosition(); - this.updateBuildingPreview(); + this.updateBuildInstance(); } else { this.open(); } @@ -106,8 +108,8 @@ export class Builder extends EventEmitter implements IBuilder { this.variant = variant; - if (this.buildingPreview) { - this.buildingPreview.setTexture(BuildingInstance.Texture); + if (this.buildPreview) { + this.buildPreview.setTexture(BuildingInstance.Texture); } } @@ -217,11 +219,18 @@ export class Builder extends EventEmitter implements IBuilder { return; } - this.createBuildArea(); - this.createBuildingPreview(); - this.isBuild = true; + if (!this.scene.game.device.os.desktop) { + this.supposedPosition = this.scene.level.getFreeAdjacentTile({ + ...this.scene.player.positionAtMatrix, + z: 1, + }) ?? this.scene.player.positionAtMatrix; + } + + this.createBuildArea(); + this.createBuildInstance(); + this.emit(BuilderEvents.BUILD_START); } @@ -230,7 +239,7 @@ export class Builder extends EventEmitter implements IBuilder { return; } - this.destroyBuildingPreview(); + this.destroyBuildInstance(); this.destroyBuildArea(); this.isBuild = false; @@ -341,7 +350,14 @@ export class Builder extends EventEmitter implements IBuilder { this.scene.sound.play(BuildingAudio.BUILD); if (!this.scene.game.device.os.desktop) { - this.unsetBuildingVariant(true); + const adjacentPosition = this.scene.level.getFreeAdjacentTile({ + ...this.supposedPosition, + z: 1, + }); + + if (adjacentPosition) { + this.supposedPosition = adjacentPosition; + } } } @@ -410,7 +426,7 @@ export class Builder extends EventEmitter implements IBuilder { this.radius * 2 * LEVEL_TILE_SIZE.persperctive, ); this.buildArea.updateDisplayOrigin(); - this.buildArea.setDepth(WORLD_DEPTH_EFFECT); + this.buildArea.setDepth(WORLD_DEPTH_GRAPHIC); } private updateBuildAreaPosition() { @@ -432,24 +448,54 @@ export class Builder extends EventEmitter implements IBuilder { this.buildArea = null; } - private createBuildingPreview() { + private createBuildPreview() { if (!this.variant) { return; } const BuildingInstance = BUILDINGS[this.variant]; - this.buildingPreview = this.scene.add.image(0, 0, BuildingInstance.Texture); - this.buildingPreview.setOrigin(0.5, LEVEL_TILE_SIZE.origin); + this.buildPreview = this.scene.add.image(0, 0, BuildingInstance.Texture); + this.buildPreview.setOrigin(0.5, LEVEL_TILE_SIZE.origin); + } + + private createBuildPlaceholder() { + this.buildPlaceholder = this.scene.add.image(0, 0, BuildingIcon.PLACEHOLDER); + } + + private createBuildControls() { + this.buildControls = this.scene.add.container(0, 0); + + const confirm = this.scene.add.image(-16, 0, BuildingIcon.CONFIRM); + + confirm.setInteractive(); + confirm.on(Phaser.Input.Events.POINTER_DOWN, (pointer: Phaser.Input.Pointer) => { + pointer.reset(); + this.build(); + }); + + const decline = this.scene.add.image(16, 0, BuildingIcon.DECLINE); + + decline.setInteractive(); + decline.on(Phaser.Input.Events.POINTER_DOWN, () => { + this.unsetBuildingVariant(); + }); + + this.buildControls.add([confirm, decline]); + } + + private createBuildInstance() { + this.createBuildPreview(); + this.createBuildPlaceholder(); if (!this.scene.game.device.os.desktop) { - this.buildingPlaceholder = this.scene.add.image(0, 0, BuildingIcon.PLACEHOLDER); + this.createBuildControls(); } - this.updateBuildingPreview(); + this.updateBuildInstance(); } - private updateBuildingPreview() { + private updateBuildInstance() { if (!this.supposedPosition) { return; } @@ -459,28 +505,41 @@ export class Builder extends EventEmitter implements IBuilder { const depth = Level.GetTileDepth(positionAtWorld.y, tilePosition.z) + 1; const isAllow = this.isAllowBuild(); - if (this.buildingPreview) { - this.buildingPreview.setPosition(positionAtWorld.x, positionAtWorld.y); - this.buildingPreview.setDepth(depth); - this.buildingPreview.setAlpha(isAllow ? 1.0 : 0.25); + if (this.buildPreview) { + this.buildPreview.setPosition(positionAtWorld.x, positionAtWorld.y); + this.buildPreview.setDepth(depth); + this.buildPreview.setAlpha(isAllow ? 1.0 : 0.25); + } + + if (this.buildPlaceholder) { + this.buildPlaceholder.setPosition(positionAtWorld.x, positionAtWorld.y + LEVEL_TILE_SIZE.height * 0.5); + this.buildPlaceholder.setDepth(depth); + this.buildPlaceholder.setAlpha(isAllow ? 0.75 : 0.25); } - if (this.buildingPlaceholder) { - this.buildingPlaceholder.setPosition(positionAtWorld.x, positionAtWorld.y + LEVEL_TILE_SIZE.height * 0.5); - this.buildingPlaceholder.setDepth(depth); - this.buildingPlaceholder.setAlpha(isAllow ? 0.75 : 0.25); + if (this.buildControls) { + const confirmBtton = this.buildControls.getAt(0); + + this.buildControls.setPosition(positionAtWorld.x, positionAtWorld.y + LEVEL_TILE_SIZE.height); + this.buildControls.setDepth(WORLD_DEPTH_GRAPHIC); + confirmBtton.setTexture(isAllow ? BuildingIcon.CONFIRM : BuildingIcon.CONFIRM_DISABLED); } } - private destroyBuildingPreview() { - if (this.buildingPreview) { - this.buildingPreview.destroy(); - this.buildingPreview = null; + private destroyBuildInstance() { + if (this.buildPreview) { + this.buildPreview.destroy(); + this.buildPreview = null; } - if (this.buildingPlaceholder) { - this.buildingPlaceholder.destroy(); - this.buildingPlaceholder = null; + if (this.buildPlaceholder) { + this.buildPlaceholder.destroy(); + this.buildPlaceholder = null; + } + + if (this.buildControls) { + this.buildControls.destroy(); + this.buildControls = null; } } @@ -492,7 +551,7 @@ export class Builder extends EventEmitter implements IBuilder { : this.scene.input.pointer1; } - private updateAssumedPosition() { + private updateSupposedPosition() { let position: Vector2D; if (this.scene.game.device.os.desktop) { @@ -503,17 +562,15 @@ export class Builder extends EventEmitter implements IBuilder { } else { const pointer = this.getCurrentPointer(); - if (!pointer.active) { + if (!pointer.active || pointer.event.target !== this.scene.sys.canvas) { return; } - // Using instead of pointer.worldXY - // for get actual position in camera moving state - const worldPosition = this.scene.cameras.main.getWorldPoint(pointer.x, pointer.y); + pointer.updateWorldPoint(this.scene.cameras.main); position = { - x: worldPosition.x, - y: worldPosition.y - LEVEL_TILE_SIZE.height / this.scene.cameras.main.zoom, + x: pointer.worldX, + y: pointer.worldY - LEVEL_TILE_SIZE.height / this.scene.cameras.main.zoom, }; } @@ -529,19 +586,19 @@ export class Builder extends EventEmitter implements IBuilder { } private handlePointer() { + if (!this.scene.game.device.os.desktop) { + return; + } + this.scene.input.on(Phaser.Input.Events.POINTER_UP, (pointer: Phaser.Input.Pointer) => { if (!this.isBuild) { return; } - if (this.scene.game.device.os.desktop) { - if (pointer.button === 0) { - this.build(); - } else if (pointer.button === 2) { - this.unsetBuildingVariant(); - } - } else if (pointer === this.getCurrentPointer()) { + if (pointer.button === 0) { this.build(); + } else if (pointer.button === 2) { + this.unsetBuildingVariant(); } }); } diff --git a/src/game/scenes/world/entities/npc/npc.ts b/src/game/scenes/world/entities/npc/npc.ts index 6fd0016b..4b1cd046 100644 --- a/src/game/scenes/world/entities/npc/npc.ts +++ b/src/game/scenes/world/entities/npc/npc.ts @@ -1,7 +1,7 @@ import Phaser from 'phaser'; import { DEBUG_MODS } from '~const/game'; -import { WORLD_DEPTH_DEBUG } from '~const/world'; +import { WORLD_DEPTH_GRAPHIC } from '~const/world'; import { NPC_PATH_FIND_RATE } from '~const/world/entities/npc'; import { Sprite } from '~entity/sprite'; import { equalPositions } from '~lib/utils'; @@ -265,7 +265,7 @@ export class NPC extends Sprite implements INPC { } this.pathDebug = this.scene.add.graphics(); - this.pathDebug.setDepth(WORLD_DEPTH_DEBUG); + this.pathDebug.setDepth(WORLD_DEPTH_GRAPHIC); this.on(Phaser.GameObjects.Events.DESTROY, () => { this.pathDebug?.destroy(); diff --git a/src/game/scenes/world/entities/sprite.ts b/src/game/scenes/world/entities/sprite.ts index 802bdf58..bad8f71a 100644 --- a/src/game/scenes/world/entities/sprite.ts +++ b/src/game/scenes/world/entities/sprite.ts @@ -1,7 +1,7 @@ import Phaser from 'phaser'; import { DEBUG_MODS } from '~const/game'; -import { WORLD_COLLIDE_SPEED_FACTOR, WORLD_DEPTH_DEBUG } from '~const/world'; +import { WORLD_COLLIDE_SPEED_FACTOR, WORLD_DEPTH_GRAPHIC } from '~const/world'; import { Live } from '~lib/live'; import { equalPositions } from '~lib/utils'; import { Particles } from '~scene/world/effects'; @@ -260,7 +260,7 @@ export class Sprite extends Phaser.Physics.Arcade.Sprite implements ISprite { } this.positionDebug = this.scene.add.graphics(); - this.positionDebug.setDepth(WORLD_DEPTH_DEBUG); + this.positionDebug.setDepth(WORLD_DEPTH_GRAPHIC); this.on(Phaser.GameObjects.Events.DESTROY, () => { this.positionDebug?.destroy(); diff --git a/src/game/scenes/world/level/level.ts b/src/game/scenes/world/level/level.ts index 50c66be4..6c26a8e8 100644 --- a/src/game/scenes/world/level/level.ts +++ b/src/game/scenes/world/level/level.ts @@ -134,6 +134,24 @@ export class Level extends TileMatrix implements ILevel { return LEVEL_PLANETS[this.planet].BIOMES.find((biome) => (biome.data.type === type))?.data ?? null; } + public getFreeAdjacentTile(position: Vector3D) { + const positions: Vector2D[] = [ + { x: position.x + 1, y: position.y }, + { x: position.x, y: position.y + 1 }, + { x: position.x - 1, y: position.y }, + { x: position.x, y: position.y - 1 }, + { x: position.x + 1, y: position.y - 1 }, + { x: position.x + 1, y: position.y + 1 }, + { x: position.x - 1, y: position.y + 1 }, + { x: position.x - 1, y: position.y - 1 }, + ]; + + return positions.find((p) => this.isFreePoint({ + ...p, + z: position.z, + })) ?? null; + } + private addTilemap() { const data = new Phaser.Tilemaps.MapData({ width: LEVEL_MAP_SIZE, diff --git a/src/types/world/entities/building.ts b/src/types/world/entities/building.ts index 4c63649d..181fe0c7 100644 --- a/src/types/world/entities/building.ts +++ b/src/types/world/entities/building.ts @@ -176,6 +176,9 @@ export enum BuildingTexture { } export enum BuildingIcon { + CONFIRM = 'building/icons/confirm', + CONFIRM_DISABLED = 'building/icons/confirm_disabled', + DECLINE = 'building/icons/decline', ALERT = 'building/icons/alert', PLACEHOLDER = 'building/icons/placeholder', UPGRADE = 'building/icons/upgrade', diff --git a/src/types/world/level/level.ts b/src/types/world/level/level.ts index 1cd546f2..37fde5df 100644 --- a/src/types/world/level/level.ts +++ b/src/types/world/level/level.ts @@ -68,6 +68,12 @@ export interface ILevel extends ITileMatrix { */ getBiome(type: BiomeType): Nullable + /** + * Get free adjacent tile around source position. + * @param position - Source position + */ + getFreeAdjacentTile(position: Vector3D): Nullable + /** * Get data for saving. */