diff --git a/src/application/types.ts b/src/application/types.ts index 09154b9..130296b 100644 --- a/src/application/types.ts +++ b/src/application/types.ts @@ -26,7 +26,7 @@ export type TextureData = { } export type Tile = { - id: UUID + id: string name: string tags: any | null createdAt: Date @@ -34,7 +34,7 @@ export type Tile = { } export type MapObject = { - id: UUID + id: string name: string tags: any | null originX: number @@ -47,7 +47,7 @@ export type MapObject = { } export type Item = { - id: UUID + id: string name: string description: string | null itemType: ItemType @@ -62,7 +62,7 @@ export type ItemType = 'WEAPON' | 'HELMET' | 'CHEST' | 'LEGS' | 'BOOTS' | 'GLOVE export type ItemRarity = 'COMMON' | 'UNCOMMON' | 'RARE' | 'EPIC' | 'LEGENDARY' export type Map = { - id: UUID + id: string name: string width: number height: number @@ -78,17 +78,15 @@ export type Map = { } export type MapEffect = { - id: UUID + id: string map: Map effect: string strength: number } export type PlacedMapObject = { - id: UUID - map: Map - mapObject: MapObject - depth: number + id: string + mapObject: MapObject | string isRotated: boolean positionX: number positionY: number @@ -102,8 +100,8 @@ export enum MapEventTileType { } export type MapEventTile = { - id: UUID - mapId: UUID + id: string + mapid: string type: MapEventTileType positionX: number positionY: number @@ -111,7 +109,7 @@ export type MapEventTile = { } export type MapEventTileTeleport = { - id: UUID + id: string mapEventTile: MapEventTile toMap: Map toPositionX: number @@ -120,7 +118,7 @@ export type MapEventTileTeleport = { } export type User = { - id: UUID + id: string username: string password: string characters: Character[] @@ -140,7 +138,7 @@ export enum CharacterRace { } export type CharacterType = { - id: UUID + id: string name: string gender: CharacterGender race: CharacterRace @@ -151,7 +149,7 @@ export type CharacterType = { } export type CharacterHair = { - id: UUID + id: string name: string sprite?: Sprite gender: CharacterGender @@ -159,8 +157,8 @@ export type CharacterHair = { } export type Character = { - id: UUID - userId: UUID + id: string + userid: string user: User name: string hitpoints: number @@ -187,14 +185,14 @@ export type MapCharacter = { } export type CharacterItem = { - id: UUID + id: string character: Character item: Item quantity: number } export type CharacterEquipment = { - id: UUID + id: string slot: CharacterEquipmentSlotType characterItem: CharacterItem } @@ -209,7 +207,7 @@ export enum CharacterEquipmentSlotType { } export type Sprite = { - id: UUID + id: string name: string createdAt: Date updatedAt: Date @@ -256,6 +254,6 @@ export type WeatherState = { } export type mapLoadData = { - mapId: UUID + mapId: string characters: MapCharacter[] } diff --git a/src/components/game/map/Map.vue b/src/components/game/map/Map.vue index 8e28e27..5a35963 100644 --- a/src/components/game/map/Map.vue +++ b/src/components/game/map/Map.vue @@ -1,20 +1,21 @@ <template> - <MapTiles :key="mapStore.mapId" :tileMap :tileMapLayer /> - <PlacedMapObjects v-if="tileMap" :key="mapStore.mapId" :tileMap :tileMapLayer /> + <MapTiles v-if="tileMap && tileMapLayer" :tileMap :tileMapLayer /> + <PlacedMapObjects v-if="tileMap && tileMapLayer" :tileMap :tileMapLayer /> <Characters v-if="tileMap && mapStore.characters" :tileMap /> </template> <script setup lang="ts"> import type { MapCharacter, mapLoadData, UUID } from '@/application/types' +import { unduplicateArray } from '@/application/utilities' import Characters from '@/components/game/map/Characters.vue' import MapTiles from '@/components/game/map/MapTiles.vue' import PlacedMapObjects from '@/components/game/map/PlacedMapObjects.vue' +import { createTileLayer, createTileMap, loadTileTexturesFromMapTileArray } from '@/services/mapService' +import { MapStorage } from '@/storage/storages' import { useGameStore } from '@/stores/gameStore' import { useMapStore } from '@/stores/mapStore' -import { onMounted, onUnmounted, shallowRef } from 'vue' -import { createTileLayer, createTileMap } from '@/services/mapService' import { useScene } from 'phavuer' -import { MapStorage } from '@/storage/storages' +import { onUnmounted, shallowRef, watch } from 'vue' const scene = useScene() @@ -54,17 +55,32 @@ gameStore.connection?.on('map:character:move', (data: { characterId: UUID; posit } }) -onMounted(async () => { +async function initialize() { if (!mapStore.mapId) return const map = await mapStorage.get(mapStore.mapId) if (!map) return + await loadTileTexturesFromMapTileArray(mapStore.mapId, scene) + tileMap.value = createTileMap(scene, map) - tileMapLayer.value = createTileLayer(tileMap.value, map) -}) + tileMapLayer.value = createTileLayer(tileMap.value, unduplicateArray(map.tiles.flat())) +} + +watch( + () => mapStore.mapId, + async () => { + await initialize() + } +) onUnmounted(() => { + if (tileMap.value) { + tileMap.value.destroyLayer('tiles') + tileMap.value.removeAllLayers() + tileMap.value.destroy() + } + mapStore.reset() gameStore.connection?.off('map:character:teleport') gameStore.connection?.off('map:character:join') diff --git a/src/components/game/map/MapTiles.vue b/src/components/game/map/MapTiles.vue index a636b31..9ec2fc2 100644 --- a/src/components/game/map/MapTiles.vue +++ b/src/components/game/map/MapTiles.vue @@ -3,7 +3,6 @@ </template> <script setup lang="ts"> -import type { UUID } from '@/application/types' import Controls from '@/components/utilities/Controls.vue' import { loadTileTexturesFromMapTileArray, placeTiles } from '@/services/mapService' import { MapStorage } from '@/storage/storages' @@ -20,19 +19,14 @@ const props = defineProps<{ tileMapLayer: Phaser.Tilemaps.TilemapLayer }>() -loadTileTexturesFromMapTileArray(mapStore.mapId as UUID, scene) - .then(() => mapStorage.get(mapStore.mapId)) - .then((mapData) => { - if (!mapData || !mapData?.tiles) return - }) - .catch((error) => console.error('Failed to initialize map:', error)) - onMounted(async () => { if (!mapStore.mapId) return const map = await mapStorage.get(mapStore.mapId) if (!map) return + await loadTileTexturesFromMapTileArray(mapStore.mapId, scene) + placeTiles(props.tileMap, props.tileMapLayer, map.tiles) }) </script> diff --git a/src/components/game/map/PlacedMapObjects.vue b/src/components/game/map/PlacedMapObjects.vue index 516a3ad..8a6a0f9 100644 --- a/src/components/game/map/PlacedMapObjects.vue +++ b/src/components/game/map/PlacedMapObjects.vue @@ -8,6 +8,7 @@ import PlacedMapObject from '@/components/game/map/partials/PlacedMapObject.vue' import { MapStorage } from '@/storage/storages' import { useMapStore } from '@/stores/mapStore' import { onMounted, ref } from 'vue' + import TilemapLayer = Phaser.Tilemaps.TilemapLayer defineProps<{ diff --git a/src/components/game/map/partials/PlacedMapObject.vue b/src/components/game/map/partials/PlacedMapObject.vue index 4d95a89..bb94afd 100644 --- a/src/components/game/map/partials/PlacedMapObject.vue +++ b/src/components/game/map/partials/PlacedMapObject.vue @@ -1,16 +1,16 @@ <template> - <Image v-if="gameStore.isTextureLoaded(props.placedMapObject.mapObject.id)" v-bind="imageProps" /> + <Image v-if="mapObject && gameStore.isTextureLoaded(props.placedMapObject.mapObject as string)" v-bind="imageProps" /> </template> <script setup lang="ts"> import config from '@/application/config' -import type { PlacedMapObject, TextureData } from '@/application/types' +import type { MapObject, PlacedMapObject } from '@/application/types' import { useMapEditorComposable } from '@/composables/useMapEditorComposable' -import { calculateIsometricDepth, tileToWorldXY } from '@/services/mapService' -import { loadTexture } from '@/services/textureService' +import { calculateIsometricDepth, loadMapObjectTextures, tileToWorldXY } from '@/services/mapService' +import { MapObjectStorage } from '@/storage/storages' import { useGameStore } from '@/stores/gameStore' import { Image, useScene } from 'phavuer' -import { computed, onMounted } from 'vue' +import { computed, onMounted, ref } from 'vue' import Tilemap = Phaser.Tilemaps.Tilemap import TilemapLayer = Phaser.Tilemaps.TilemapLayer @@ -21,38 +21,55 @@ const props = defineProps<{ tileMapLayer: TilemapLayer }>() -const gameStore = useGameStore() const scene = useScene() + +const gameStore = useGameStore() const mapEditor = useMapEditorComposable() -const imageProps = computed(() => ({ - alpha: mapEditor.movingPlacedObject.value?.id == props.placedMapObject.id ? 0.5 : 1, - tint: mapEditor.selectedPlacedObject.value?.id == props.placedMapObject.id ? 0x00ff00 : 0xffffff, - depth: calculateIsometricDepth(props.placedMapObject.positionX, props.placedMapObject.positionY, props.placedMapObject.mapObject.frameWidth, props.placedMapObject.mapObject.frameHeight), - ...calculateObjectPlacement(props.placedMapObject), - flipX: props.placedMapObject.isRotated, - texture: props.placedMapObject.mapObject.id, - originX: props.placedMapObject.mapObject.originX, - originY: props.placedMapObject.mapObject.originY -})) +const mapObject = ref<MapObject>() + +async function initialize() { + if (!props.placedMapObject.mapObject) return + + /** + * Check if mapObject is an string or object, if its an object we assume its a mapObject and change it to a string + * We do this because this component is shared with the map editor, which gets sent the mapObject as an object by the server + */ + if (typeof props.placedMapObject.mapObject === 'object') { + // @ts-ignore + props.placedMapObject.mapObject = props.placedMapObject.mapObject.id + } + + const mapObjectStorage = new MapObjectStorage() + const _mapObject = await mapObjectStorage.get(props.placedMapObject.mapObject) + if (!_mapObject) return + + mapObject.value = _mapObject + + await loadMapObjectTextures([_mapObject], scene) +} function calculateObjectPlacement(mapObj: PlacedMapObject): { x: number; y: number } { let position = tileToWorldXY(props.tileMapLayer, mapObj.positionX, mapObj.positionY) return { - x: position.worldPositionX - mapObj.mapObject.frameWidth / 2, - y: position.worldPositionY - mapObj.mapObject.frameHeight / 2 + config.tile_size.height + x: position.worldPositionX - mapObject.value!.frameWidth / 2, + y: position.worldPositionY - mapObject.value!.frameHeight / 2 + config.tile_size.height } } -loadTexture(scene, { - key: props.placedMapObject.mapObject.id, - data: '/textures/map_objects/' + props.placedMapObject.mapObject.id + '.png', - group: 'map_objects', - updatedAt: props.placedMapObject.mapObject.updatedAt, - frameWidth: props.placedMapObject.mapObject.frameWidth, - frameHeight: props.placedMapObject.mapObject.frameHeight -} as TextureData).catch((error) => { - console.error('Error loading texture:', error) +const imageProps = computed(() => ({ + alpha: mapEditor.movingPlacedObject.value?.id == props.placedMapObject.id ? 0.5 : 1, + tint: mapEditor.selectedPlacedObject.value?.id == props.placedMapObject.id ? 0x00ff00 : 0xffffff, + depth: calculateIsometricDepth(props.placedMapObject.positionX, props.placedMapObject.positionY, mapObject.value!.frameWidth, mapObject.value!.frameHeight), + ...calculateObjectPlacement(props.placedMapObject), + flipX: props.placedMapObject.isRotated, + texture: mapObject.value!.id, + originX: mapObject.value!.originX, + originY: mapObject.value!.originY +})) + +onMounted(async () => { + await initialize() }) </script> diff --git a/src/components/gameMaster/mapEditor/Map.vue b/src/components/gameMaster/mapEditor/Map.vue index 8c1b7e9..2a926ae 100644 --- a/src/components/gameMaster/mapEditor/Map.vue +++ b/src/components/gameMaster/mapEditor/Map.vue @@ -1,6 +1,6 @@ <template> - <MapTiles ref="mapTiles" v-if="tileMap" :tileMap :tileMapLayer /> - <PlacedMapObjects ref="mapObjects" v-if="tileMap" :tileMap :tileMapLayer /> + <MapTiles ref="mapTiles" v-if="tileMap && tileMapLayer" :tileMap :tileMapLayer /> + <PlacedMapObjects ref="mapObjects" v-if="tileMap && tileMapLayer" :tileMap :tileMapLayer /> <MapEventTiles ref="eventTiles" v-if="tileMap" :tileMap /> </template> @@ -10,6 +10,7 @@ import MapTiles from '@/components/gameMaster/mapEditor/mapPartials/MapTiles.vue import PlacedMapObjects from '@/components/gameMaster/mapEditor/mapPartials/PlacedMapObjects.vue' import { useMapEditorComposable } from '@/composables/useMapEditorComposable' import { createTileLayer, createTileMap } from '@/services/mapService' +import { TileStorage } from '@/storage/storages' import { useScene } from 'phavuer' import { onBeforeUnmount, onMounted, onUnmounted, shallowRef, useTemplateRef } from 'vue' @@ -74,11 +75,16 @@ function handlePointerUp(pointer: Phaser.Input.Pointer) { } } -onMounted(() => { +onMounted(async () => { let mapValue = mapEditor.currentMap.value if (!mapValue) return + + const tileStorage = new TileStorage() + const allTiles = await tileStorage.getAll() + const allTileIds = allTiles.map((tile) => tile.id) + tileMap.value = createTileMap(scene, mapValue) - tileMapLayer.value = createTileLayer(tileMap.value, mapValue) + tileMapLayer.value = createTileLayer(tileMap.value, allTileIds) addEventListener('keydown', handleKeyDown) scene.input.on(Phaser.Input.Events.POINTER_DOWN, handlePointerDown) diff --git a/src/components/gameMaster/mapEditor/mapPartials/PlacedMapObjects.vue b/src/components/gameMaster/mapEditor/mapPartials/PlacedMapObjects.vue index ae2cac4..52ddc97 100644 --- a/src/components/gameMaster/mapEditor/mapPartials/PlacedMapObjects.vue +++ b/src/components/gameMaster/mapEditor/mapPartials/PlacedMapObjects.vue @@ -4,7 +4,7 @@ </template> <script setup lang="ts"> -import type { Map as MapT, PlacedMapObject as PlacedMapObjectT, UUID } from '@/application/types' +import type { MapObject, Map as MapT, PlacedMapObject as PlacedMapObjectT, UUID } from '@/application/types' import { uuidv4 } from '@/application/utilities' import PlacedMapObject from '@/components/game/map/partials/PlacedMapObject.vue' import SelectedPlacedMapObjectComponent from '@/components/gameMaster/mapEditor/partials/SelectedPlacedMapObject.vue' @@ -36,9 +36,7 @@ function pencil(pointer: Phaser.Input.Pointer, map: MapT) { if (!mapEditor.selectedMapObject.value) return const newPlacedMapObject: PlacedMapObjectT = { - id: uuidv4() as UUID, - depth: 0, - map: map, + id: uuidv4(), mapObject: mapEditor.selectedMapObject.value, isRotated: false, positionX: tile.x, @@ -72,7 +70,7 @@ function objectPicker(pointer: Phaser.Input.Pointer, map: MapT) { if (!existingPlacedMapObject) return // Select the object - mapEditor.setSelectedMapObject(existingPlacedMapObject.mapObject) + mapEditor.setSelectedMapObject(existingPlacedMapObject.mapObject as MapObject) } function moveMapObject(id: string, map: MapT) { @@ -113,7 +111,7 @@ function clickPlacedMapObject(placedMapObject: PlacedMapObjectT) { // If alt is pressed, select the object if (scene.input.activePointer.event.altKey) { - mapEditor.setSelectedMapObject(placedMapObject.mapObject) + mapEditor.setSelectedMapObject(placedMapObject.mapObject as MapObject) } } diff --git a/src/components/screens/Game.vue b/src/components/screens/Game.vue index ef23bfc..7be880c 100644 --- a/src/components/screens/Game.vue +++ b/src/components/screens/Game.vue @@ -1,7 +1,7 @@ <template> <div class="flex justify-center items-center h-dvh relative"> <Game :config="gameConfig" @create="createGame"> - <Scene name="main" @preload="preloadScene" @create="createScene"> + <Scene name="main" @preload="preloadScene"> <Menu /> <Hud /> <Hotkeys /> @@ -29,7 +29,6 @@ import Menu from '@/components/game/gui/Menu.vue' import Map from '@/components/game/map/Map.vue' import { useGameStore } from '@/stores/gameStore' import { Game, Scene } from 'phavuer' -import { onBeforeUnmount } from 'vue' const gameStore = useGameStore() @@ -62,8 +61,4 @@ function preloadScene(scene: Phaser.Scene) { scene.load.image('blank_tile', '/assets/map/blank_tile.png') scene.load.image('waypoint', '/assets/waypoint.png') } - -function createScene(scene: Phaser.Scene) {} - -onBeforeUnmount(() => {}) </script> diff --git a/src/components/screens/MapEditor.vue b/src/components/screens/MapEditor.vue index 879b84b..c518393 100644 --- a/src/components/screens/MapEditor.vue +++ b/src/components/screens/MapEditor.vue @@ -104,7 +104,7 @@ function save() { pvp: currentMap.pvp, mapEffects: currentMap.mapEffects, mapEventTiles: currentMap.mapEventTiles, - placedMapObjects: currentMap.placedMapObjects.map(({ id, mapObject, depth, isRotated, positionX, positionY }) => ({ id, mapObject: { ...mapObject }, depth, isRotated, positionX, positionY })) ?? [] + placedMapObjects: currentMap.placedMapObjects.map(({ id, mapObject, depth, isRotated, positionX, positionY }) => ({ id, mapObject, depth, isRotated, positionX, positionY })) ?? [] } gameStore.connection?.emit('gm:map:update', data, (response: MapT) => { diff --git a/src/composables/useMapEditorComposable.ts b/src/composables/useMapEditorComposable.ts index 2e6aef8..4b095ae 100644 --- a/src/composables/useMapEditorComposable.ts +++ b/src/composables/useMapEditorComposable.ts @@ -63,8 +63,8 @@ export function useMapEditorComposable() { selectedTile.value = tile } - const setSelectedMapObject = (object: MapObject) => { - selectedMapObject.value = object + const setSelectedMapObject = (mapObject: MapObject) => { + selectedMapObject.value = mapObject } const setTeleportSettings = (settings: TeleportSettings) => { diff --git a/src/services/mapService.ts b/src/services/mapService.ts index 68a1b7f..1fb7af7 100644 --- a/src/services/mapService.ts +++ b/src/services/mapService.ts @@ -1,5 +1,5 @@ import config from '@/application/config' -import type { Map as MapT, TextureData, Tile as TileT, UUID } from '@/application/types' +import type { MapObject, Map as MapT, TextureData, Tile as TileT, UUID } from '@/application/types' import { unduplicateArray } from '@/application/utilities' import { loadTexture } from '@/services/textureService' import { MapStorage, TileStorage } from '@/storage/storages' @@ -39,11 +39,6 @@ export function tileToWorldY(layer: TilemapLayer | Tilemap, positionX: number, p /** * Can also be used to replace tiles - * @param map - * @param layer - * @param positionX - * @param positionY - * @param tileName */ export function placeTile(map: Tilemap, layer: TilemapLayer, positionX: number, positionY: number, tileName: string) { let tileImg = map.getTileset(tileName) as Tileset @@ -78,7 +73,6 @@ export const calculateIsometricDepth = (positionX: number, positionY: number, wi async function loadTileTextures(tiles: TileT[], scene: Phaser.Scene) { // Load each tile into the scene for (const tile of tiles) { - if (!tile) continue const textureData = { key: tile.id, data: '/textures/tiles/' + tile.id + '.png', @@ -89,9 +83,10 @@ async function loadTileTextures(tiles: TileT[], scene: Phaser.Scene) { } } -export async function loadTileTexturesFromMapTileArray(map_id: UUID, scene: Phaser.Scene) { - const tileStorage = new TileStorage() +export async function loadTileTexturesFromMapTileArray(map_id: string, scene: Phaser.Scene) { const mapStorage = new MapStorage() + const tileStorage = new TileStorage() + const map = await mapStorage.get(map_id) if (!map) return @@ -101,19 +96,25 @@ export async function loadTileTexturesFromMapTileArray(map_id: UUID, scene: Phas await loadTileTextures(tiles, scene) } -export async function loadTileTexturesFromTileIds(tileIds: string[], scene: Phaser.Scene) { - const tileStorage = new TileStorage() - const tiles = await tileStorage.getByIds(tileIds) - - await loadTileTextures(tiles, scene) -} - export async function loadAllTileTextures(scene: Phaser.Scene) { const tileStorage = new TileStorage() const tiles = await tileStorage.getAll() await loadTileTextures(tiles, scene) - scene.load.start() +} + +export async function loadMapObjectTextures(mapObjects: MapObject[], scene: Phaser.Scene) { + for (const mapObject of mapObjects) { + const textureData = { + key: mapObject.id, + data: '/textures/map_objects/' + mapObject.id + '.png', + group: 'map_objects', + updatedAt: mapObject.updatedAt, + frameWidth: mapObject.frameWidth, + frameHeight: mapObject.frameHeight + } as TextureData + await loadTexture(scene, textureData) + } } export function createTileMap(scene: Phaser.Scene, map: MapT) { @@ -129,19 +130,20 @@ export function createTileMap(scene: Phaser.Scene, map: MapT) { return new Phaser.Tilemaps.Tilemap(scene, mapConfig) } -export function createTileLayer(currentTileMap: Phaser.Tilemaps.Tilemap, mapData: MapT) { - const tilesArray = unduplicateArray(mapData?.tiles.flat()) - +export function createTileLayer(tileMap: Phaser.Tilemaps.Tilemap, tilesArray: string[]) { + // Load tiles into tileset const tilesetImages = tilesArray.map((tile: string, index: number) => { - return currentTileMap.addTilesetImage(tile, tile, config.tile_size.width, config.tile_size.height, 1, 2, index + 1, { x: 0, y: -config.tile_size.height }) + return tileMap.addTilesetImage(tile, tile, config.tile_size.width, config.tile_size.height, 1, 2, index + 1, { x: 0, y: -config.tile_size.height }) }) // Add blank tile - tilesetImages.push(currentTileMap.addTilesetImage('blank_tile', 'blank_tile', config.tile_size.width, config.tile_size.height, 1, 2, 0, { x: 0, y: -config.tile_size.height })) + tilesetImages.push(tileMap.addTilesetImage('blank_tile', 'blank_tile', config.tile_size.width, config.tile_size.height, 1, 2, 0, { x: 0, y: -config.tile_size.height })) - const layer = currentTileMap.createBlankLayer('tiles', tilesetImages as Tileset[], 0, config.tile_size.height) as Phaser.Tilemaps.TilemapLayer + // Create layer + const layer = tileMap.createBlankLayer('tiles', tilesetImages as Tileset[], 0, config.tile_size.height) as Phaser.Tilemaps.TilemapLayer layer.setDepth(0) layer.setCullPadding(2, 2) + return layer } diff --git a/src/stores/mapStore.ts b/src/stores/mapStore.ts index 50811c6..da2a01f 100644 --- a/src/stores/mapStore.ts +++ b/src/stores/mapStore.ts @@ -18,7 +18,7 @@ export const useMapStore = defineStore('map', { } }, actions: { - setMapId(mapId: UUID) { + setMapId(mapId: string) { this.mapId = mapId }, setCharacters(characters: MapCharacter[]) { @@ -27,11 +27,6 @@ export const useMapStore = defineStore('map', { addCharacter(character: MapCharacter) { this.characters.push(character) }, - updateCharacter(updatedCharacter: MapCharacter) { - const index = this.characters.findIndex((char) => char.character.id === updatedCharacter.character.id) - if (index !== -1) this.characters[index] = updatedCharacter - }, - // Property is mapCharacter key updateCharacterProperty<K extends keyof MapCharacter>(characterId: UUID, property: K, value: MapCharacter[K]) { const character = this.characters.find((char) => char.character.id === characterId) if (character) {