diff --git a/package-lock.json b/package-lock.json index fc3e187..c91e10e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@vueuse/core": "^10.5.0", "@vueuse/integrations": "^10.5.0", "axios": "^1.7.7", + "dexie": "^4.0.8", "phaser": "^3.86.0", "pinia": "^2.1.6", "socket.io-client": "^4.8.0", @@ -3549,6 +3550,12 @@ "node": ">=0.10" } }, + "node_modules/dexie": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.8.tgz", + "integrity": "sha512-1G6cJevS17KMDK847V3OHvK2zei899GwpDiqfEXHP1ASvme6eWJmAp9AU4s1son2TeGkWmC0g3y8ezOBPnalgQ==", + "license": "Apache-2.0" + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -3884,9 +3891,9 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "9.29.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.29.0.tgz", - "integrity": "sha512-hamyjrBhNH6Li6R1h1VF9KHfshJlKgKEg3ARbGTn72CMNDSMhWbgC7NdkRDEh25AFW+4SDATzyNM+3gWuZii8g==", + "version": "9.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.29.1.tgz", + "integrity": "sha512-MH/MbVae4HV/tM8gKAVWMPJbYgW04CK7SuzYRrlNERpxbO0P3+Zdsa2oAcFBW6xNu7W6lIkGOsFAMCRTYmrlWQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 63b3c51..b54fe60 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@vueuse/core": "^10.5.0", "@vueuse/integrations": "^10.5.0", "axios": "^1.7.7", + "dexie": "^4.0.8", "phaser": "^3.86.0", "pinia": "^2.1.6", "socket.io-client": "^4.8.0", diff --git a/public/assets/music/click-btn.mp3 b/public/assets/music/click-btn.mp3 new file mode 100644 index 0000000..6ef1729 Binary files /dev/null and b/public/assets/music/click-btn.mp3 differ diff --git a/public/assets/music/login.mp3 b/public/assets/music/login.mp3 new file mode 100644 index 0000000..1943d41 Binary files /dev/null and b/public/assets/music/login.mp3 differ diff --git a/src/App.vue b/src/App.vue index d188312..ba67548 100644 --- a/src/App.vue +++ b/src/App.vue @@ -15,6 +15,7 @@ import GmPanel from '@/components/gameMaster/GmPanel.vue' import Login from '@/screens/Login.vue' import Characters from '@/screens/Characters.vue' import Game from '@/screens/Game.vue' +import Loading from '@/screens/Loading.vue' import ZoneEditor from '@/screens/ZoneEditor.vue' import { computed } from 'vue' @@ -22,16 +23,11 @@ const gameStore = useGameStore() const zoneEditorStore = useZoneEditorStore() const currentScreen = computed(() => { + if (!gameStore.isAssetsLoaded) return Loading if (!gameStore.connection) return Login if (!gameStore.token) return Login if (!gameStore.character) return Characters if (zoneEditorStore.active) return ZoneEditor return Game }) - -// Disable right click -addEventListener('contextmenu', (event) => event.preventDefault()) - -// Disable pinch zoom -addEventListener('gesturestart', (event) => event.preventDefault()) diff --git a/src/components/gameMaster/zoneEditor/ZoneEditor.vue b/src/components/gameMaster/zoneEditor/ZoneEditor.vue index dc9ee28..6dad5d4 100644 --- a/src/components/gameMaster/zoneEditor/ZoneEditor.vue +++ b/src/components/gameMaster/zoneEditor/ZoneEditor.vue @@ -14,11 +14,9 @@ \ No newline at end of file diff --git a/src/screens/ZoneEditor.vue b/src/screens/ZoneEditor.vue index 239d036..b4b463e 100644 --- a/src/screens/ZoneEditor.vue +++ b/src/screens/ZoneEditor.vue @@ -16,7 +16,6 @@ import { Game, Scene } from 'phavuer' import { useGameStore } from '@/stores/gameStore' import { useZoneEditorStore } from '@/stores/zoneEditorStore' import ZoneEditor from '@/components/gameMaster/zoneEditor/ZoneEditor.vue' -import { loadAssets } from '@/composables/zoneComposable' const gameStore = useGameStore() const zoneEditorStore = useZoneEditorStore() @@ -93,11 +92,6 @@ const preloadScene = async (scene: Phaser.Scene) => { scene.load.image('TELEPORT', '/assets/zone/tp_tile.png') scene.load.image('blank_tile', '/assets/zone/blank_tile.png') scene.load.image('waypoint', '/assets/waypoint.png') - - /** - * Load the assets into the Phaser scene - */ - await loadAssets(scene) } const createScene = async (scene: Phaser.Scene) => { diff --git a/src/services/authentication.ts b/src/services/authentication.ts index 1fed2d3..4ccb044 100644 --- a/src/services/authentication.ts +++ b/src/services/authentication.ts @@ -1,9 +1,8 @@ import axios from 'axios' import config from '@/config' -import { useGameStore } from '@/stores/gameStore' import { useCookies } from '@vueuse/integrations/useCookies' -export async function register(username: string, password: string, gameStore = useGameStore()) { +export async function register(username: string, password: string) { try { const response = await axios.post(`${config.server_endpoint}/register`, { username, password }) useCookies().set('token', response.data.token as string) @@ -13,13 +12,13 @@ export async function register(username: string, password: string, gameStore = u } } -export async function login(username: string, password: string, gameStore = useGameStore()) { +export async function login(username: string, password: string) { try { const response = await axios.post(`${config.server_endpoint}/login`, { username, password }) useCookies().set('token', response.data.token as string, { // for whole domain // @TODO : #190 - domain: window.location.hostname.split('.').slice(-2).join('.') + // domain: window.location.hostname.split('.').slice(-2).join('.') }) return { success: true, token: response.data.token } } catch (error: any) { diff --git a/src/stores/gameStore.ts b/src/stores/gameStore.ts index df3ca75..c6737fd 100644 --- a/src/stores/gameStore.ts +++ b/src/stores/gameStore.ts @@ -7,9 +7,8 @@ import { useCookies } from '@vueuse/integrations/useCookies' export const useGameStore = defineStore('game', { state: () => { return { - loginMessage: null as string | null, notifications: [] as Notification[], - assets: [] as Asset[], + isAssetsLoaded: false, token: '' as string | null, connection: null as Socket | null, user: null as User | null, @@ -47,54 +46,6 @@ export const useGameStore = defineStore('game', { removeNotification(id: string) { this.notifications = this.notifications.filter((notification: Notification) => notification.id !== id) }, - setAssets(assets: Asset[]) { - this.assets = assets - }, - addAsset(asset: Asset) { - this.assets.push(asset) - }, - addAssets(assets: Asset[]) { - this.assets = this.assets.concat(assets) - }, - async fetchSpriteAssets() { - return fetch(config.server_endpoint + '/assets/sprites') - .then((response) => response.json()) - .then((assets) => { - // Only add the sprites that are not already in the store - this.addAssets(assets.filter((asset: Asset) => !this.getAssetByKey(asset.key))) - return true - }) - .catch((error) => { - console.error('Error fetching assets:', error) - return false - }) - }, - async fetchZoneAssets(zoneId: number) { - return fetch(config.server_endpoint + '/assets/zone/' + zoneId) - .then((response) => response.json()) - .then((assets) => { - // Only add the zones that are not already in the store - this.addAssets(assets.filter((asset: Asset) => !this.getAssetByKey(asset.key))) - return true - }) - .catch((error) => { - console.error('Error fetching assets:', error) - return false - }) - }, - async fetchAllZoneAssets() { - return fetch(config.server_endpoint + '/assets/zone') - .then((response) => response.json()) - .then((assets) => { - // Only add the zones that are not already in the store - this.addAssets(assets.filter((asset: Asset) => !this.getAssetByKey(asset.key))) - return true - }) - .catch((error) => { - console.error('Error fetching assets:', error) - return false - }) - }, setToken(token: string) { this.token = token }, @@ -152,15 +103,15 @@ export const useGameStore = defineStore('game', { }) }, disconnectSocket() { - if (this.connection) this.connection.disconnect() + this.connection?.disconnect() useCookies().remove('token', { // for whole domain // @TODO : #190 - domain: window.location.hostname.split('.').slice(-2).join('.') + // domain: window.location.hostname.split('.').slice(-2).join('.') }) - this.assets = [] + // this.isAssetsLoaded = false this.connection = null this.token = null this.user = null diff --git a/src/types.ts b/src/types.ts index a784736..0ffa1d8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,10 +4,11 @@ export type Notification = { message?: string } -export type Asset = { +export type AssetT = { key: string url: string group: 'tiles' | 'objects' | 'sprites' | 'sprite_animations' | 'sound' | 'music' | 'ui' | 'font' | 'other' + updatedAt: Date frameCount?: number frameWidth?: number frameHeight?: number diff --git a/src/utilities/assets.ts b/src/utilities/assets.ts new file mode 100644 index 0000000..e33db9d --- /dev/null +++ b/src/utilities/assets.ts @@ -0,0 +1,101 @@ +import config from '@/config'; +import Dexie from 'dexie'; + +interface Asset { + key: string; + data: Blob; + group: string; + updatedAt: Date; + frameCount?: number; + frameWidth?: number; + frameHeight?: number; +} + +interface AssetWithUrl extends Omit { + data: string; +} + +class AssetManager extends Dexie { + assets!: Dexie.Table; + + constructor() { + super('AssetManager'); + this.version(1).stores({ + assets: 'key, group' + }); + } + + async getAssetCount(): Promise { + try { + const count = await this.assets.count(); + return count; + } catch (error) { + console.error('Failed to get asset count:', error); + return 0; + } + } + + async addAsset( + key: string, + url: string, + group: string, + updatedAt: Date, + frameCount?: number, + frameWidth?: number, + frameHeight?: number + ): Promise { + try { + const response = await fetch(config.server_endpoint + url); + const blob = await response.blob(); + await this.assets.put({ key, data: blob, group, updatedAt, frameCount, frameWidth, frameHeight }); + } catch (error) { + console.error(`Failed to add asset ${key}:`, error); + } + } + + async getAsset(key: string): Promise { + try { + const asset = await this.assets.get(key); + if (asset) { + return { + ...asset, + data: URL.createObjectURL(asset.data) + }; + } + } catch (error) { + console.error(`Failed to retrieve asset ${key}:`, error); + } + return null; + } + + async getAssetsByGroup(group: string): Promise { + try { + const assets = await this.assets.where('group').equals(group).toArray(); + return assets.map(asset => ({ + ...asset, + data: URL.createObjectURL(asset.data) + })); + } catch (error) { + console.error(`Failed to retrieve assets for group ${group}:`, error); + return []; + } + } + + async clearAssets(): Promise { + try { + await this.assets.clear(); + } catch (error) { + console.error('Failed to clear assets:', error); + } + } + + async deleteAsset(key: string): Promise { + try { + await this.assets.delete(key); + } catch (error) { + console.error(`Failed to delete asset ${key}:`, error); + } + } +} + +export const useAssetManager = new AssetManager(); \ No newline at end of file