diff --git a/gameserver/schema/TilemapState.ts b/gameserver/schema/TilemapState.ts index 33a8500..0e55ba1 100644 --- a/gameserver/schema/TilemapState.ts +++ b/gameserver/schema/TilemapState.ts @@ -1,4 +1,5 @@ import { Schema, type, ArraySchema, MapSchema } from "@colyseus/schema"; +import { createNoise2D } from 'simplex-noise'; const TilesType = { dirt: 16, @@ -17,21 +18,25 @@ export class TilemapState extends Schema { /** width in tiles */ @type("number") tilemapWidth = 60; + @type("boolean") ready = false; + simplex: any; + constructor() { super(); + this.simplex = createNoise2D(); + this.generateTilemap(); } - generateTilemap() { + async generateTilemap() { + this.ready = false; const { tilemapWidth, tilemapHeight } = this; - const perlinScale = 1.5; - this.tilemap1D.clear(); - console.log('generating tilemap'); - const noiseMap: number[][] = []; + const perlinScale = Math.max(0.03,0.05*Math.random()); // Lower scale for larger features + const noiseMap = Array.from({ length: tilemapHeight }, () => Array(tilemapWidth).fill(0)); + for (let y = 0; y < tilemapHeight; y++) { - noiseMap[y] = []; for (let x = 0; x < tilemapWidth; x++) { - noiseMap[y][x] = this.perlin(x * perlinScale, y * perlinScale); + noiseMap[y][x] = this.simplex(x * perlinScale, y * perlinScale); } } @@ -39,10 +44,11 @@ export class TilemapState extends Schema { for (let x = 0; x < tilemapWidth; x++) { const noiseValue = noiseMap[y][x]; let tileTypeIndex = TilesType.dirt; - - if (noiseValue < 0.05*Math.random()) { + if (noiseValue < -0.5) { + tileTypeIndex = TilesType.water; + } else if (noiseValue < -0.1 && Math.random() < 0.05) { tileTypeIndex = TilesType.water; - } else if (noiseValue < Math.random()) { + } else if (noiseValue < 0.5*Math.random()) { tileTypeIndex = TilesType.dirt; } else { tileTypeIndex = TilesType.grass; @@ -50,6 +56,7 @@ export class TilemapState extends Schema { this.tilemap1D.push(tileTypeIndex); } } + this.ready = true; } perlin(x: number, y: number): number { diff --git a/gameserver/stateMachines/server-state-machine/SessionStateBehaviour.ts b/gameserver/stateMachines/server-state-machine/SessionStateBehaviour.ts index 9fab1d1..2f9c3a1 100644 --- a/gameserver/stateMachines/server-state-machine/SessionStateBehaviour.ts +++ b/gameserver/stateMachines/server-state-machine/SessionStateBehaviour.ts @@ -31,7 +31,6 @@ export default { if (allClientsLoaded && atleastMinimumPlayersJoined) { sessionState.countdown = Math.max(0, sessionState.countdown - delta); if (sessionState.countdown === 0) { - console.log("session-lobby-state timed out, starting match/game"); sessionState.countdown = SERVER_CONFIG.COUNTDOWN_SPAWN_SELECTIONS; gameStateManager.stateMachine.controller.send("StartMatch"); } diff --git a/package-lock.json b/package-lock.json index c11b322..94ac5e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "phaser3-rex-plugins": "^1.80.2", "quadtree-lib": "^1.0.9", "sat": "^0.9.0", + "simplex-noise": "^4.0.1", "socket.io": "^4.4.1", "style-loader": "^3.3.4", "ts-loader": "^9.5.1", @@ -11311,6 +11312,11 @@ "semver": "bin/semver.js" } }, + "node_modules/simplex-noise": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simplex-noise/-/simplex-noise-4.0.1.tgz", + "integrity": "sha512-zl/+bdSqW7HJOQ0oDbxrNYaF4F5ik0i7M6YOYmEoIJNtg16NpvWaTTM1Y7oV/7T0jFljawLgYPS81Uu2rsfo1A==" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", diff --git a/package.json b/package.json index d22a6de..2356453 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "phaser3-rex-plugins": "^1.80.2", "quadtree-lib": "^1.0.9", "sat": "^0.9.0", + "simplex-noise": "^4.0.1", "socket.io": "^4.4.1", "style-loader": "^3.3.4", "ts-loader": "^9.5.1", diff --git a/public/scenes/BaseScene.ts b/public/scenes/BaseScene.ts index ab5d23c..8ea0a83 100644 --- a/public/scenes/BaseScene.ts +++ b/public/scenes/BaseScene.ts @@ -53,7 +53,8 @@ export class BaseScene extends Phaser.Scene { ); } - AddStateChangeListener(cleanupFunction: Function, key?: string) { + AddStateChangeListener(cleanupFunction ?: Function, key?: string) { + if(!cleanupFunction) return; const mKey = key || nanoid(); let existingCbSet = this.networkCallsCleanup.get(mKey) || new Set(); existingCbSet.add(cleanupFunction); @@ -105,6 +106,8 @@ export class BaseScene extends Phaser.Scene { // Recursively destroy an object, including any children if it's a group DestroyObject(obj: T) { + if(!obj) + return; if ((obj as any)?.type === "Group") obj.destroy(true); else obj.destroy(); } diff --git a/public/scenes/SessionLobbyScene.ts b/public/scenes/SessionLobbyScene.ts index c7f9633..8159234 100644 --- a/public/scenes/SessionLobbyScene.ts +++ b/public/scenes/SessionLobbyScene.ts @@ -2,11 +2,14 @@ import CONSTANT from "../constant"; import { BaseScene } from "./BaseScene"; import { PacketType } from "../../common/PacketType"; import { NetworkManager } from "../NetworkManager"; +import SpinnerPlugin from "phaser3-rex-plugins/templates/spinner/spinner-plugin.js"; +import Spinner from "phaser3-rex-plugins/templates/spinner/spinner/Spinner"; var networkManager: NetworkManager; var buttonState = false; export class SessionLobbyScene extends BaseScene { + rexSpinner: SpinnerPlugin | undefined; constructor() { super(CONSTANT.SCENES.SESSIONLOBBY); } @@ -34,9 +37,18 @@ export class SessionLobbyScene extends BaseScene { ); this.AddObject( - this.add.text(50, 40, `Fetching Map...`).setScale(1.5, 1.5), + this.add.text(50, 40, `Procedurally Generating Tilemap...`), "obj_mapLoadStatus" ); + this.AddObject( + this.rexSpinner!.add.spinner({ + width: 40, + height: 40, + x: 10, + y: 40, + }), + "obj_tilemapLoadingSpinner" + ); const cb = networkManager.getState()?.listen("sessionState", (value) => { console.log("session state updated ", value) @@ -132,19 +144,21 @@ export class SessionLobbyScene extends BaseScene { })! ); - networkManager.room?.state.tilemap.onChange(() => { - this.GetObject("obj_mapLoadStatus")?.setText( - `Map Successfully Fetched.` - ); - if (networkManager.room!.state.tilemap.tilemap1D.toArray().length > 0) { + this.AddStateChangeListener( + networkManager.room?.state.tilemap.listen("ready", (isTilemapReady) => { + if (!isTilemapReady) return; + this.GetObject("obj_mapLoadStatus")?.setText( + `Generated Tilemap.` + ); + this.DestroyObject(this.GetObject('obj_tilemapLoadingSpinner') as Spinner); networkManager.sendEventToServer( PacketType.ByClient.CLIENT_MAP_LOADED, { isLoaded: true, } ); - } - }); + }) + ); this.AddSceneEvent("shutdown", (data: any) => { console.log("shutdown ", data.config.key); diff --git a/public/scenes/SpawnSelectionScene.ts b/public/scenes/SpawnSelectionScene.ts index cdee0bb..90daf3a 100644 --- a/public/scenes/SpawnSelectionScene.ts +++ b/public/scenes/SpawnSelectionScene.ts @@ -37,11 +37,6 @@ export class SpawnSelectionScene extends BaseScene { create() { networkManager = this.registry.get("networkManager") as NetworkManager; const parsedMap = networkManager.getMapData(); - if(!parsedMap) { - console.error('Failed to parse map'); - networkManager.disconnectGameServer(); - return; - } const map = this.setupSceneTilemap(parsedMap!); this.data.set("map1", map);