Compare commits
17 Commits
feature/#2
...
feature/ne
Author | SHA1 | Date | |
---|---|---|---|
ed6f592606 | |||
46ebfaec01 | |||
1384f50406 | |||
d71f4e7b59 | |||
58929290ab | |||
63146106c0 | |||
7c5602f204 | |||
e711e124ce | |||
e1b39c42ec | |||
d81c889426 | |||
afb0edacf6 | |||
6d7d568746 | |||
8df5b6eb76 | |||
270d12821a | |||
9c244e980c | |||
25ba54c8ac | |||
9c4bef864b |
36
package-lock.json
generated
36
package-lock.json
generated
@ -2451,14 +2451,14 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@vue/devtools-core": {
|
"node_modules/@vue/devtools-core": {
|
||||||
"version": "7.6.2",
|
"version": "7.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.6.3.tgz",
|
||||||
"integrity": "sha512-hJfjNR3ai94Mb6i0PB42kxUPkPreS6Dl07FUaHAcw+umtkUX55jTXe7+mhsHx9NI6NFT+1WMFREIy8O81KLYyA==",
|
"integrity": "sha512-C7FOuh3Z+EmXXzDU9eRjHQL7zW7/CFovM6yCNNpUb+zXxhrn4fiqTum+a3gNau9DuzYfEtQXwZ9F7MeK0JKYVw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/devtools-kit": "^7.6.2",
|
"@vue/devtools-kit": "^7.6.3",
|
||||||
"@vue/devtools-shared": "^7.6.2",
|
"@vue/devtools-shared": "^7.6.3",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"nanoid": "^3.3.4",
|
"nanoid": "^3.3.4",
|
||||||
"pathe": "^1.1.2",
|
"pathe": "^1.1.2",
|
||||||
@ -2469,13 +2469,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/devtools-kit": {
|
"node_modules/@vue/devtools-kit": {
|
||||||
"version": "7.6.2",
|
"version": "7.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.6.3.tgz",
|
||||||
"integrity": "sha512-k61BxHRmcTtIQZFouF9QWt9nCCNtSdw12lhg8VNtHq5/XOBGD+ewiK27a40UJ8UPYoCJvi80hbvbYr5E/Zeu1g==",
|
"integrity": "sha512-ETsFc8GlOp04rSFN79tB2TpVloWfsSx9BoCSElV3w3CaJTSBfz42KsIi5Ka+dNTJs1jY7QVLTDeoBmUGgA9h2A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/devtools-shared": "^7.6.2",
|
"@vue/devtools-shared": "^7.6.3",
|
||||||
"birpc": "^0.2.19",
|
"birpc": "^0.2.19",
|
||||||
"hookable": "^5.5.3",
|
"hookable": "^5.5.3",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
@ -2485,9 +2485,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vue/devtools-shared": {
|
"node_modules/@vue/devtools-shared": {
|
||||||
"version": "7.6.2",
|
"version": "7.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.6.3.tgz",
|
||||||
"integrity": "sha512-lcjyJ7hCC0W0kNwnCGMLVTMvDLoZgjcq9BvboPgS+6jQyDul7fpzRSKTGtGhCHoxrDox7qBAKGbAl2Rcf7GE1A==",
|
"integrity": "sha512-wJW5QF27i16+sNQIaes8QoEZg1eqEgF83GkiPUlEQe9k7ZoHXHV7PRrnrxOKem42sIHPU813J2V/ZK1uqTJe6g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -7430,15 +7430,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite-plugin-vue-devtools": {
|
"node_modules/vite-plugin-vue-devtools": {
|
||||||
"version": "7.6.2",
|
"version": "7.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.6.3.tgz",
|
||||||
"integrity": "sha512-YPE/8AIBsomvHadZ02Kkp8yZo2FR0SFNjbC2lcMgW+hNA1ZoXu9b5oi18gTMzJcLLFRNNSMNjShA4RLqXlIR/A==",
|
"integrity": "sha512-p1rZMKzreWqxj9U05RaxY1vDoOhGYhA6iX8vKfo4nD6jqTmVoGjjk+U1g5HYwwTCdr/eck3kzO2f4gnPCjqVKA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/devtools-core": "^7.6.2",
|
"@vue/devtools-core": "^7.6.3",
|
||||||
"@vue/devtools-kit": "^7.6.2",
|
"@vue/devtools-kit": "^7.6.3",
|
||||||
"@vue/devtools-shared": "^7.6.2",
|
"@vue/devtools-shared": "^7.6.3",
|
||||||
"execa": "^8.0.1",
|
"execa": "^8.0.1",
|
||||||
"sirv": "^3.0.0",
|
"sirv": "^3.0.0",
|
||||||
"vite-plugin-inspect": "^0.8.7",
|
"vite-plugin-inspect": "^0.8.7",
|
||||||
|
10
public/assets/icons/male-icon.svg
Normal file
10
public/assets/icons/male-icon.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_598_541)">
|
||||||
|
<path d="M10.0327 5.69111L12.3905 3.33333H9.33333V2H14.6667V7.33333H13.3333V4.27614L10.9755 6.63392C11.6183 7.47513 12 8.52633 12 9.66667C12 12.4281 9.7614 14.6667 7 14.6667C4.23857 14.6667 2 12.4281 2 9.66667C2 6.90527 4.23857 4.66667 7 4.66667C8.14033 4.66667 9.19153 5.04843 10.0327 5.69111ZM7 13.3333C9.02507 13.3333 10.6667 11.6917 10.6667 9.66667C10.6667 7.6416 9.02507 6 7 6C4.97495 6 3.33333 7.6416 3.33333 9.66667C3.33333 11.6917 4.97495 13.3333 7 13.3333Z" fill="#999999"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_598_541">
|
||||||
|
<rect width="16" height="16" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 729 B |
18
public/assets/ui-elements/ui-border-2-corners-bottom.svg
Normal file
18
public/assets/ui-elements/ui-border-2-corners-bottom.svg
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<svg width="190" height="202" viewBox="0 0 190 202" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g filter="url(#filter0_i_598_514)">
|
||||||
|
<path d="M0 3.60002C0 1.61179 1.61177 0 3.6 0H186.4C188.388 0 190 1.61177 190 3.6V193.658C190 195.646 188.388 197.258 186.4 197.258H184.894C183.584 197.258 182.523 198.32 182.523 199.629C182.523 200.938 181.461 202 180.152 202H9.84847C8.53901 202 7.47748 200.938 7.47748 199.629C7.47748 198.32 6.41596 197.258 5.1065 197.258H3.6C1.61178 197.258 0 195.646 0 193.658V3.60002Z" fill="#181818"/>
|
||||||
|
</g>
|
||||||
|
<path d="M0.3 3.60002C0.3 1.77747 1.77746 0.3 3.6 0.3H186.4C188.223 0.3 189.7 1.77746 189.7 3.6V193.658C189.7 195.481 188.223 196.958 186.4 196.958H184.894C183.418 196.958 182.223 198.154 182.223 199.629C182.223 200.773 181.295 201.7 180.152 201.7H9.84847C8.7047 201.7 7.77748 200.773 7.77748 199.629C7.77748 198.154 6.58164 196.958 5.1065 196.958H3.6C1.77746 196.958 0.3 195.481 0.3 193.658V3.60002Z" stroke="#454442" stroke-width="0.6"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_i_598_514" x="0" y="0" width="190" height="204.4" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2.4"/>
|
||||||
|
<feGaussianBlur stdDeviation="2.34"/>
|
||||||
|
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.13 0"/>
|
||||||
|
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_598_514"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
16
src/App.vue
16
src/App.vue
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<Notifications />
|
<Notifications />
|
||||||
<GmTools v-if="gameStore.character?.role === 'gm'" />
|
<BackgroundImageLoader />
|
||||||
<GmPanel v-if="gameStore.character?.role === 'gm'" />
|
<GmPanel v-if="gameStore.character?.role === 'gm'" />
|
||||||
<component :is="currentScreen" />
|
<component :is="currentScreen" />
|
||||||
</template>
|
</template>
|
||||||
@ -9,7 +9,7 @@
|
|||||||
import { useGameStore } from '@/stores/gameStore'
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
import { useZoneEditorStore } from '@/stores/zoneEditorStore'
|
import { useZoneEditorStore } from '@/stores/zoneEditorStore'
|
||||||
import Notifications from '@/components/utilities/Notifications.vue'
|
import Notifications from '@/components/utilities/Notifications.vue'
|
||||||
import GmTools from '@/components/gameMaster/GmTools.vue'
|
import BackgroundImageLoader from '@/components/utilities/BackgroundImageLoader.vue'
|
||||||
import GmPanel from '@/components/gameMaster/GmPanel.vue'
|
import GmPanel from '@/components/gameMaster/GmPanel.vue'
|
||||||
import Login from '@/components/screens/Login.vue'
|
import Login from '@/components/screens/Login.vue'
|
||||||
import Characters from '@/components/screens/Characters.vue'
|
import Characters from '@/components/screens/Characters.vue'
|
||||||
@ -44,4 +44,16 @@ addEventListener('click', (event) => {
|
|||||||
const audio = new Audio('/assets/music/click-btn.mp3')
|
const audio = new Audio('/assets/music/click-btn.mp3')
|
||||||
audio.play()
|
audio.play()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Watch for "G" key press and toggle the gm panel
|
||||||
|
addEventListener('keydown', (event) => {
|
||||||
|
if (gameStore.character?.role !== 'gm') return // Only allow toggling the gm panel if the character is a gm
|
||||||
|
|
||||||
|
// Check if no input is active
|
||||||
|
if (event.repeat || event.isComposing || event.defaultPrevented) return
|
||||||
|
|
||||||
|
if (event.key === 'G') {
|
||||||
|
gameStore.toggleGmPanel()
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -125,6 +125,12 @@ button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.character {
|
||||||
|
&.active {
|
||||||
|
@apply pr-px border-r-0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.text-pixel {
|
.text-pixel {
|
||||||
@apply text-white font-ui drop-shadow-pixel-black;
|
@apply text-white font-ui drop-shadow-pixel-black;
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<Scene name="effects" @preload="preloadScene" @create="createScene" @update="updateScene"> </Scene>
|
<Scene name="effects" @preload="preloadScene" @create="createScene" @update="updateScene" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Scene } from 'phavuer'
|
import { Scene } from 'phavuer'
|
||||||
import { useZoneStore } from '@/stores/zoneStore'
|
import { useZoneStore } from '@/stores/zoneStore'
|
||||||
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
import { onBeforeUnmount, ref, watch } from 'vue'
|
import { onBeforeUnmount, ref, watch } from 'vue'
|
||||||
|
import type { WeatherState } from '@/types'
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
const SUNRISE_HOUR = 6
|
||||||
|
const SUNSET_HOUR = 20
|
||||||
|
const DAY_STRENGTH = 100
|
||||||
|
const NIGHT_STRENGTH = 30
|
||||||
|
|
||||||
|
// Stores
|
||||||
|
const gameStore = useGameStore()
|
||||||
const zoneStore = useZoneStore()
|
const zoneStore = useZoneStore()
|
||||||
|
|
||||||
|
// Scene ref
|
||||||
const sceneRef = ref<Phaser.Scene | null>(null)
|
const sceneRef = ref<Phaser.Scene | null>(null)
|
||||||
|
|
||||||
// Effect-related refs
|
// Effect refs
|
||||||
const lightEffect = ref<Phaser.GameObjects.Graphics | null>(null)
|
const lightEffect = ref<Phaser.GameObjects.Graphics | null>(null)
|
||||||
const rainEmitter = ref<Phaser.GameObjects.Particles.ParticleEmitter | null>(null)
|
const rainEmitter = ref<Phaser.GameObjects.Particles.ParticleEmitter | null>(null)
|
||||||
const fogSprite = ref<Phaser.GameObjects.Sprite | null>(null)
|
const fogSprite = ref<Phaser.GameObjects.Sprite | null>(null)
|
||||||
|
|
||||||
|
// State refs
|
||||||
|
const weatherState = ref<WeatherState>({
|
||||||
|
isRainEnabled: false,
|
||||||
|
rainPercentage: 0,
|
||||||
|
isFogEnabled: false,
|
||||||
|
fogDensity: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// Scene lifecycle methods
|
||||||
const preloadScene = async (scene: Phaser.Scene) => {
|
const preloadScene = async (scene: Phaser.Scene) => {
|
||||||
scene.load.image('raindrop', 'assets/raindrop.png')
|
scene.load.image('raindrop', 'assets/raindrop.png')
|
||||||
scene.load.image('fog', 'assets/fog.png')
|
scene.load.image('fog', 'assets/fog.png')
|
||||||
@ -23,15 +43,21 @@ const preloadScene = async (scene: Phaser.Scene) => {
|
|||||||
|
|
||||||
const createScene = async (scene: Phaser.Scene) => {
|
const createScene = async (scene: Phaser.Scene) => {
|
||||||
sceneRef.value = scene
|
sceneRef.value = scene
|
||||||
createLightEffect(scene)
|
setupEffects(scene)
|
||||||
createRainEffect(scene)
|
setupSocketListeners()
|
||||||
createFogEffect(scene)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateScene = () => {
|
const updateScene = () => {
|
||||||
updateEffects()
|
updateEffects()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Effect setup
|
||||||
|
const setupEffects = (scene: Phaser.Scene) => {
|
||||||
|
createLightEffect(scene)
|
||||||
|
createRainEffect(scene)
|
||||||
|
createFogEffect(scene)
|
||||||
|
}
|
||||||
|
|
||||||
const createLightEffect = (scene: Phaser.Scene) => {
|
const createLightEffect = (scene: Phaser.Scene) => {
|
||||||
lightEffect.value = scene.add.graphics()
|
lightEffect.value = scene.add.graphics()
|
||||||
lightEffect.value.setDepth(1000)
|
lightEffect.value.setDepth(1000)
|
||||||
@ -59,14 +85,55 @@ const createFogEffect = (scene: Phaser.Scene) => {
|
|||||||
fogSprite.value.setDepth(950)
|
fogSprite.value.setDepth(950)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lighting calculations
|
||||||
|
const calculateLightStrength = (time: Date): number => {
|
||||||
|
const hour = time.getHours()
|
||||||
|
const minute = time.getMinutes()
|
||||||
|
|
||||||
|
let strength = DAY_STRENGTH
|
||||||
|
|
||||||
|
// Night time (10 PM - 6 AM)
|
||||||
|
if (hour >= SUNSET_HOUR || hour < SUNRISE_HOUR) {
|
||||||
|
strength = NIGHT_STRENGTH
|
||||||
|
}
|
||||||
|
// Full daylight (7 AM - 7 PM)
|
||||||
|
else if (hour > SUNRISE_HOUR && hour < SUNSET_HOUR - 2) {
|
||||||
|
strength = DAY_STRENGTH
|
||||||
|
}
|
||||||
|
// Sunrise transition (6 AM - 7 AM)
|
||||||
|
else if (hour === SUNRISE_HOUR) {
|
||||||
|
strength = NIGHT_STRENGTH + ((DAY_STRENGTH - NIGHT_STRENGTH) * minute) / 60
|
||||||
|
}
|
||||||
|
// Sunset transition (8 PM - 10 PM)
|
||||||
|
else if (hour >= SUNSET_HOUR - 2 && hour < SUNSET_HOUR) {
|
||||||
|
const totalMinutes = (hour - (SUNSET_HOUR - 2)) * 60 + minute
|
||||||
|
const transitionProgress = totalMinutes / 120 // 2 hours = 120 minutes
|
||||||
|
strength = DAY_STRENGTH - (DAY_STRENGTH - NIGHT_STRENGTH) * transitionProgress
|
||||||
|
}
|
||||||
|
|
||||||
|
return strength
|
||||||
|
}
|
||||||
|
|
||||||
|
// Effect updates
|
||||||
const updateEffects = () => {
|
const updateEffects = () => {
|
||||||
const effects = zoneStore.zone?.zoneEffects || []
|
const effects = zoneStore.zone?.zoneEffects || []
|
||||||
|
|
||||||
|
if (effects.length > 0) {
|
||||||
|
updateZoneEffects(effects)
|
||||||
|
} else {
|
||||||
|
// Make sure we're getting the current time
|
||||||
|
const lightStrength = calculateLightStrength(gameStore.world.date)
|
||||||
|
updateLightEffect(lightStrength)
|
||||||
|
updateWeatherEffects()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateZoneEffects = (effects: any[]) => {
|
||||||
|
// Always update light based on time when zone effects are present
|
||||||
|
updateLightEffect(calculateLightStrength(gameStore.world.date))
|
||||||
|
|
||||||
effects.forEach((effect) => {
|
effects.forEach((effect) => {
|
||||||
switch (effect.effect) {
|
switch (effect.effect) {
|
||||||
case 'light':
|
|
||||||
updateLightEffect(effect.strength)
|
|
||||||
break
|
|
||||||
case 'rain':
|
case 'rain':
|
||||||
updateRainEffect(effect.strength)
|
updateRainEffect(effect.strength)
|
||||||
break
|
break
|
||||||
@ -77,6 +144,11 @@ const updateEffects = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateWeatherEffects = () => {
|
||||||
|
updateRainEffect(weatherState.value.isRainEnabled ? weatherState.value.rainPercentage : 0)
|
||||||
|
updateFogEffect(weatherState.value.isFogEnabled ? weatherState.value.fogDensity * 100 : 0)
|
||||||
|
}
|
||||||
|
|
||||||
const updateLightEffect = (strength: number) => {
|
const updateLightEffect = (strength: number) => {
|
||||||
if (!lightEffect.value) return
|
if (!lightEffect.value) return
|
||||||
const darkness = 1 - strength / 100
|
const darkness = 1 - strength / 100
|
||||||
@ -100,11 +172,34 @@ const updateFogEffect = (strength: number) => {
|
|||||||
fogSprite.value.setAlpha(strength / 100)
|
fogSprite.value.setAlpha(strength / 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Socket handlers
|
||||||
|
const setupSocketListeners = () => {
|
||||||
|
// Initial weather state
|
||||||
|
gameStore.connection?.emit('weather', (response: WeatherState) => {
|
||||||
|
if (zoneStore.zone?.zoneEffects) return
|
||||||
|
weatherState.value = response
|
||||||
|
updateEffects()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Weather updates
|
||||||
|
gameStore.connection?.on('weather', (data: WeatherState) => {
|
||||||
|
weatherState.value = data
|
||||||
|
updateEffects()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Time updates
|
||||||
|
gameStore.connection?.on('date', () => {
|
||||||
|
if (zoneStore.zone?.zoneEffects) return
|
||||||
|
updateEffects()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watchers
|
||||||
watch(() => zoneStore.zone?.zoneEffects, updateEffects, { deep: true })
|
watch(() => zoneStore.zone?.zoneEffects, updateEffects, { deep: true })
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (sceneRef.value) sceneRef.value.scene.remove('effects')
|
if (sceneRef.value) sceneRef.value.scene.remove('effects')
|
||||||
|
gameStore.connection?.off('weather')
|
||||||
})
|
})
|
||||||
|
|
||||||
// @TODO : Fix resize issue
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
<button @mousedown.stop class="btn-cyan py-1.5 px-4 min-w-24">Users</button>
|
<button @mousedown.stop class="btn-cyan py-1.5 px-4 min-w-24">Users</button>
|
||||||
<button @mousedown.stop class="btn-cyan py-1.5 px-4 min-w-24">Chats</button>
|
<button @mousedown.stop class="btn-cyan py-1.5 px-4 min-w-24">Chats</button>
|
||||||
<button @mousedown.stop class="btn-cyan active py-1.5 px-4 min-w-24">Asset manager</button>
|
<button @mousedown.stop class="btn-cyan active py-1.5 px-4 min-w-24">Asset manager</button>
|
||||||
|
<button class="btn-cyan py-1.5 px-4 min-w-24" type="button" @click="() => zoneEditorStore.toggleActive()">Zone manager</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #modalBody>
|
<template #modalBody>
|
||||||
@ -21,8 +22,10 @@ import { ref } from 'vue'
|
|||||||
import Modal from '@/components/utilities/Modal.vue'
|
import Modal from '@/components/utilities/Modal.vue'
|
||||||
import AssetManager from '@/components/gameMaster/assetManager/AssetManager.vue'
|
import AssetManager from '@/components/gameMaster/assetManager/AssetManager.vue'
|
||||||
import { useGameStore } from '@/stores/gameStore'
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
|
import { useZoneEditorStore } from '@/stores/zoneEditorStore'
|
||||||
|
|
||||||
const gameStore = useGameStore()
|
const gameStore = useGameStore()
|
||||||
|
const zoneEditorStore = useZoneEditorStore()
|
||||||
|
|
||||||
let toggle = ref('asset-manager')
|
let toggle = ref('asset-manager')
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
<template>
|
|
||||||
<Modal :isModalOpen="true" :closable="false" :is-resizable="false" :modal-width="modalWidth" :modal-height="modalHeight" :modal-position-x="posXY.x" :modal-position-y="posXY.y">
|
|
||||||
<template #modalHeader>
|
|
||||||
<h3 class="m-0 font-medium shrink-0 text-white">GM tools</h3>
|
|
||||||
</template>
|
|
||||||
<template #modalBody>
|
|
||||||
<div class="content flex flex-col gap-2.5 m-4 h-20">
|
|
||||||
<button class="btn-cyan py-1.5 px-4 w-full" type="button" @click="gameStore.toggleGmPanel()">Toggle GM panel</button>
|
|
||||||
<button class="btn-cyan py-1.5 px-4 w-full" type="button" @click="() => zoneEditorStore.toggleActive()">Zone manager</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import Modal from '@/components/utilities/Modal.vue'
|
|
||||||
import { useZoneEditorStore } from '@/stores/zoneEditorStore'
|
|
||||||
import { useGameStore } from '@/stores/gameStore'
|
|
||||||
import { onMounted, ref } from 'vue'
|
|
||||||
|
|
||||||
const zoneEditorStore = useZoneEditorStore()
|
|
||||||
const gameStore = useGameStore()
|
|
||||||
const modalWidth = ref(200)
|
|
||||||
const modalHeight = ref(170)
|
|
||||||
|
|
||||||
let posXY = ref({ x: 0, y: 0 })
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
window.addEventListener('resize', () => {
|
|
||||||
posXY.value = customPositionGmPanel(modalWidth.value)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const customPositionGmPanel = (modalWidth: number) => {
|
|
||||||
const padding = 25
|
|
||||||
const width = window.innerWidth
|
|
||||||
|
|
||||||
const x = width - (modalWidth + 4) - 25
|
|
||||||
const y = padding
|
|
||||||
|
|
||||||
return { x, y }
|
|
||||||
}
|
|
||||||
|
|
||||||
posXY.value = customPositionGmPanel(modalWidth.value)
|
|
||||||
</script>
|
|
@ -18,7 +18,6 @@ import { onUnmounted, ref } from 'vue'
|
|||||||
import { useGameStore } from '@/stores/gameStore'
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
import { useZoneEditorStore } from '@/stores/zoneEditorStore'
|
import { useZoneEditorStore } from '@/stores/zoneEditorStore'
|
||||||
import { type Zone } from '@/types'
|
import { type Zone } from '@/types'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Toolbar from '@/components/gameMaster/zoneEditor/partials/Toolbar.vue'
|
import Toolbar from '@/components/gameMaster/zoneEditor/partials/Toolbar.vue'
|
||||||
import TileList from '@/components/gameMaster/zoneEditor/partials/TileList.vue'
|
import TileList from '@/components/gameMaster/zoneEditor/partials/TileList.vue'
|
||||||
@ -32,6 +31,7 @@ import ZoneEventTiles from '@/components/gameMaster/zoneEditor/zonePartials/Zone
|
|||||||
|
|
||||||
const gameStore = useGameStore()
|
const gameStore = useGameStore()
|
||||||
const zoneEditorStore = useZoneEditorStore()
|
const zoneEditorStore = useZoneEditorStore()
|
||||||
|
console.log(zoneEditorStore.zone)
|
||||||
|
|
||||||
const tileMap = ref(null as Phaser.Tilemaps.Tilemap | null)
|
const tileMap = ref(null as Phaser.Tilemaps.Tilemap | null)
|
||||||
|
|
||||||
@ -64,6 +64,7 @@ function save() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gameStore.connection?.emit('gm:zone_editor:zone:update', data, (response: Zone) => {
|
gameStore.connection?.emit('gm:zone_editor:zone:update', data, (response: Zone) => {
|
||||||
|
console.log(response.updatedAt)
|
||||||
zoneEditorStore.setZone(response)
|
zoneEditorStore.setZone(response)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,7 @@ function selectTile(tile: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isActiveTile(tile: Tile): boolean {
|
function isActiveTile(tile: Tile): boolean {
|
||||||
return zoneEditorStore.selectedTile?.id === tile.id
|
return zoneEditorStore.selectedTile === tile.id
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
@ -191,7 +191,22 @@ onMounted(() => {
|
|||||||
if (!zoneEditorStore.zone?.tiles) {
|
if (!zoneEditorStore.zone?.tiles) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setLayerTiles(tileMap, tileLayer, zoneEditorStore.zone.tiles)
|
|
||||||
|
// First fill the entire map with blank tiles using current zone dimensions
|
||||||
|
const blankTiles = createTileArray(zoneEditorStore.zone.width, zoneEditorStore.zone.height, 'blank_tile')
|
||||||
|
|
||||||
|
// Then overlay the zone tiles, but only within the current zone dimensions
|
||||||
|
const zoneTiles = zoneEditorStore.zone.tiles
|
||||||
|
for (let y = 0; y < zoneEditorStore.zone.height; y++) {
|
||||||
|
for (let x = 0; x < zoneEditorStore.zone.width; x++) {
|
||||||
|
// Only copy if the source tiles array has this position
|
||||||
|
if (zoneTiles[y] && zoneTiles[y][x] !== undefined) {
|
||||||
|
blankTiles[y][x] = zoneTiles[y][x]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLayerTiles(tileMap, tileLayer, blankTiles)
|
||||||
|
|
||||||
scene.input.on(Phaser.Input.Events.POINTER_MOVE, pencil)
|
scene.input.on(Phaser.Input.Events.POINTER_MOVE, pencil)
|
||||||
scene.input.on(Phaser.Input.Events.POINTER_MOVE, eraser)
|
scene.input.on(Phaser.Input.Events.POINTER_MOVE, eraser)
|
||||||
|
21
src/components/gui/Clock.vue
Normal file
21
src/components/gui/Clock.vue
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<div class="absolute top-0 right-4 hidden lg:block">
|
||||||
|
<p class="text-white text-lg">{{ gameStore.world.date.toLocaleString() }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
|
import { onUnmounted } from 'vue'
|
||||||
|
|
||||||
|
const gameStore = useGameStore()
|
||||||
|
|
||||||
|
// Listen for new date from socket
|
||||||
|
gameStore.connection?.on('date', (data: Date) => {
|
||||||
|
gameStore.world.date = new Date(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
gameStore.connection?.off('date')
|
||||||
|
})
|
||||||
|
</script>
|
@ -59,6 +59,15 @@ async function newPasswordFunc() {
|
|||||||
newPasswordError.value = response.error
|
newPasswordError.value = response.error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @TODO: #238, this wont work if we redirect to the login page
|
||||||
|
* Find a way to just "close" this screen instead of redirecting
|
||||||
|
*/
|
||||||
|
gameStore.addNotification({
|
||||||
|
title: 'Success',
|
||||||
|
message: 'Password changed successfully'
|
||||||
|
})
|
||||||
window.location.href = '/'
|
window.location.href = '/'
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Modal @modal:close="() => emit('close')" :modal-width="400" :modal-height="300" :is-resizable="false">
|
<Modal :isModalOpen="true" :modal-width="400" :modal-height="300" :is-resizable="false" @modal:close="() => emit('close')">
|
||||||
<template #modalHeader>
|
<template #modalHeader>
|
||||||
<h3 class="m-0 font-medium shrink-0 text-white">Reset Password</h3>
|
<h3 class="m-0 font-medium shrink-0 text-white">Reset Password</h3>
|
||||||
</template>
|
</template>
|
||||||
@ -14,7 +14,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="grid grid-flow-col justify-stretch gap-4">
|
<div class="grid grid-flow-col justify-stretch gap-4">
|
||||||
<button class="btn-empty py-1.5 px-4 min-w-24 inline-block" @click.stop="() => emit('close')">Cancel</button>
|
<button class="btn-empty py-1.5 px-4 min-w-24 inline-block" @click.stop="() => emit('close')">Cancel</button>
|
||||||
<button class="btn-cyan py-1.5 px-4 min-w-24 inline-block" type="submit">Send mail</button>
|
<button class="btn-cyan py-1.5 px-4 min-w-24 inline-flex items-center justify-center" type="submit">
|
||||||
|
<svg v-if="isLoading" xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 animate-spin mr-2" fill="none" viewBox="0 0 24 24">
|
||||||
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||||
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||||
|
</svg>
|
||||||
|
Send mail
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -23,17 +29,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { resetPassword } from '@/services/authentication'
|
import { resetPassword } from '@/services/authentication'
|
||||||
import { useGameStore } from '@/stores/gameStore'
|
|
||||||
import Modal from '@/components/utilities/Modal.vue'
|
import Modal from '@/components/utilities/Modal.vue'
|
||||||
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
|
|
||||||
const emit = defineEmits(['close'])
|
const emit = defineEmits(['close'])
|
||||||
|
|
||||||
const gameStore = useGameStore()
|
const gameStore = useGameStore()
|
||||||
|
const isLoading = ref(false)
|
||||||
const email = ref('')
|
const email = ref('')
|
||||||
const resetPasswordError = ref('')
|
const resetPasswordError = ref('')
|
||||||
const isPasswordResetOpen = ref(false)
|
|
||||||
|
|
||||||
async function resetPasswordFunc() {
|
async function resetPasswordFunc() {
|
||||||
// check if email is valid
|
// check if email is valid
|
||||||
@ -42,14 +48,24 @@ async function resetPasswordFunc() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isLoading.value = true
|
||||||
|
|
||||||
// send reset password event to server
|
// send reset password event to server
|
||||||
const response = await resetPassword(email.value)
|
const response = await resetPassword(email.value)
|
||||||
|
|
||||||
if (response.success === undefined) {
|
if (response.success === undefined) {
|
||||||
resetPasswordError.value = response.error
|
resetPasswordError.value = response.error
|
||||||
|
isLoading.value = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gameStore.addNotification({
|
||||||
|
title: 'Success',
|
||||||
|
message: 'Password reset email sent'
|
||||||
|
})
|
||||||
|
|
||||||
|
isLoading.value = false
|
||||||
|
|
||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
@ -1,60 +1,76 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="bg-gray-900 relative">
|
<div class="relative max-lg:h-dvh flex flex-row-reverse">
|
||||||
<div class="absolute bg-[url('/assets/shapes/select-screen-bg-shape.svg')] bg-no-repeat bg-center w-full h-full"></div>
|
<div class="lg:bg-gradient-to-l bg-gradient-to-b from-gray-900 to-transparent w-full lg:w-1/2 h-[35dvh] lg:h-dvh absolute left-0 max-lg:bottom-0 lg:top-0 z-10"></div>
|
||||||
<div class="ui-wrapper h-dvh flex flex-col justify-center items-center gap-20 px-10 sm:px-20">
|
<div class="bg-[url('/assets/login/login-bg.png')] opacity-20 w-full lg:w-1/2 h-[35dvh] lg:h-dvh absolute left-0 max-lg:bottom-0 lg:top-0 bg-no-repeat bg-cover bg-center grayscale"></div>
|
||||||
<div class="filler"></div>
|
<div class="bg-gray-900 z-20 w-full lg:w-1/2 h-[65dvh] lg:h-dvh relative"></div>
|
||||||
<div class="flex gap-14 w-full max-h-[650px] overflow-x-auto" v-if="!isLoading">
|
<div class="absolute top-8 right-0 py-[18px] pr-[15px] pl-32 bg-gradient-to-r from-transparent to-cyan-900 z-20">
|
||||||
<!-- CHARACTER LIST -->
|
<h2 class="text-white">CHARACTER SELECTION</h2>
|
||||||
<div v-for="character in characters" :key="character.id" class="group first:ml-auto last:mr-auto m-4 w-[170px] h-[275px] flex flex-col shrink-0 relative shadow-character" :class="{ active: selected_character == character.id }">
|
|
||||||
<img src="/assets/ui-elements/ui-box-outer.svg" class="absolute w-full h-full" alt="UI box outer" />
|
|
||||||
<img src="/assets/ui-elements/ui-box-inner.svg" class="absolute left-2 bottom-2 w-[calc(100%_-_16px)] h-[calc(100%_-_40px)]" alt="UI box inner" />
|
|
||||||
<input class="opacity-0 h-full w-full absolute m-0 z-10" type="radio" :id="character.id" name="character" :value="character.id" v-model="selected_character" />
|
|
||||||
<label class="font-bold absolute left-1/2 top-4 max-w-32 -translate-x-1/2 -translate-y-1/2 text-center text-ellipsis overflow-hidden whitespace-nowrap drop-shadow-text" :for="character.id">{{ character.name }}</label>
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="delete bg-red w-8 h-8 p-[3px] rounded-full absolute -right-4 top-0 -translate-y-1/2 z-10 border-2 border-solid border-white hover:bg-red-300"
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
deletingCharacter = character
|
|
||||||
}
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<img draggable="false" src="/assets/icons/trashcan.svg" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="sprite-container flex flex-col items-center m-auto">
|
|
||||||
<img class="drop-shadow-20" draggable="false" src="/assets/avatar/default/0.png" />
|
|
||||||
</div>
|
</div>
|
||||||
<span class="absolute bottom-6 w-full text-center translate-y-1/2 z-10">Lvl. {{ character.level }}</span>
|
<div class="ui-wrapper h-dvh w-[calc(100%_-_80px)] sm:w-[calc(100%_-_160px)] absolute flex flex-col justify-center items-center gap-14 px-10 sm:px-20 z-30">
|
||||||
<div class="selected-character group-[.active]:max-w-[170px] absolute max-w-0 w-4/6 h-[3px] bg-gray-500 rounded-[3px] left-1/2 -bottom-4 -translate-x-1/2 transition-all ease-in-out duration-300"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="character new-character first:ml-auto mr-auto m-4 w-[170px] h-[275px] flex flex-col shrink-0 rounded-2xl relative bg-gray-500/50 bg-no-repeat shadow-character" v-if="characters.length < 4">
|
|
||||||
<button class="h-full w-full py-10 flex flex-col justify-between" @click="isModalOpen = true">
|
|
||||||
<div class="filler"></div>
|
<div class="filler"></div>
|
||||||
<img class="w-24 h-24 m-auto" draggable="false" src="/assets/icons/plus-icon.svg" />
|
<div class="w-2/3 max-w-[860px]" v-if="!isLoading">
|
||||||
<span class="self-center text-base absolute bottom-5 w-full text-center translate-y-1/2 z-10">Create new</span>
|
<div class="mb-5 flex flex-col gap-1">
|
||||||
|
<h1 class="text-white font-bold">SELECT CHARACTER TO PLAY</h1>
|
||||||
|
<p class="m-0">Maximum of 4 characters can be selected per player</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex w-full h-[400px] border border-solid border-gray-500 rounded-md rounded-tl-none bg-gray">
|
||||||
|
<div class="w-1/3 h-full bg-[url('/assets/ui-texture.png')] bg-no-repeat bg-cover bg-center border-0 border-r border-solid border-gray-500 rounded-bl-md relative">
|
||||||
|
<div class="absolute right-full -top-px flex gap-1 flex-col">
|
||||||
|
<div v-for="character in characters" :key="character.id" class="character relative rounded-l border border-solid border-gray-500 w-9 h-[50px] bg-[url('/assets/ui-texture.png')]" :class="{ 'active': selected_character == character.id }">
|
||||||
|
<img src="/assets/avatar/default/head.png" class="w-9 h-9 object-contain absolute top-1/2 -translate-y-1/2" alt="Player head" />
|
||||||
|
<input class="opacity-0 h-full w-full absolute m-0 z-10 hover:cursor-pointer" type="radio" :id="character.id" name="character" :value="character.id" v-model="selected_character" />
|
||||||
|
</div>
|
||||||
|
<div class="character relative rounded-l border border-solid border-gray-500 w-9 h-[50px] bg-[url('/assets/ui-texture.png')]" :class="{'active': characters.length == 0}" v-if="characters.length < 4">
|
||||||
|
<button class="p-0 h-full w-full flex flex-col justify-between" @click="isModalOpen = true">
|
||||||
|
<img class="w-6 h-6 object-contain absolute top-1/2 left-1/2 -translate-y-1/2 -translate-x-1/2" draggable="false" src="/assets/icons/plus-icon.svg" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="py-6 px-8 h-[calc(100%_-_48px)] flex flex-col items-center gap-6" v-if="selected_character">
|
||||||
|
<input class="input-field w-[158px]" type="text" name="name" :placeholder="characters.find(c => c.id == selected_character)?.name" />
|
||||||
|
<div class="flex flex-col gap-4 items-center">
|
||||||
|
<div class="flex flex-col gap-3">
|
||||||
|
<div class="bg-[url('/assets/ui-elements/ui-border-2-corners-bottom.svg')] w-[190px] h-52 bg-no-repeat bg-center flex items-center justify-center">
|
||||||
|
<img class="w-12 object-contain mb-3.5" src="/assets/avatar/default/0.png" alt="Player avatar"/>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between w-[190px]">
|
||||||
|
<!-- TODO: replace with color swatches -->
|
||||||
|
<div v-for="n in 9" class="w-4 h-4 rounded-sm bg-white"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between w-[190px]">
|
||||||
|
<button class="btn-empty flex gap-2">
|
||||||
|
<img src="/assets/icons/male-icon.svg" class="w-4 h-4 m-auto" alt="Male symbol" />
|
||||||
|
<span class="text-white">Male</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn-empty flex gap-2">
|
||||||
|
<img src="/assets/icons/male-icon.svg" class="w-4 h-4 m-auto" alt="Male symbol" />
|
||||||
|
<span class="text-white">Female</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-2/3 h-full bg-[url('/assets/ui-texture.png')] bg-no-repeat bg-cover bg-center rounded-r-md"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<img class="w-20 invert-80" src="/assets/icons/loading-icon1.svg" />
|
<img class="w-20 invert-80" src="/assets/icons/loading-icon1.svg" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="button-wrapper flex gap-8" v-if="!isLoading">
|
<div class="button-wrapper flex self-center justify-end gap-4 max-w-[860px] w-full" v-if="!isLoading">
|
||||||
<button
|
<button
|
||||||
class="btn-red py-2 pr-2.5 pl-8 min-w-24 relative rounded text-xl flex gap-4 items-center transition-all ease-in-out duration-200 hover:gap-5 disabled:bg-red/50 disabled:hover:bg-opacity-50 disabled:cursor-not-allowed disabled:hover:gap-[15px]"
|
class="btn-empty min-w-48"
|
||||||
@click.stop="gameStore.disconnectSocket()"
|
@click.stop="gameStore.disconnectSocket()"
|
||||||
>
|
>
|
||||||
<img class="h-8 drop-shadow-20 rotate-180" draggable="false" src="/assets/icons/arrow.svg" alt="Logout icon" />
|
Back
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn-cyan py-2 px-2.5 pl-8 min-w-24 relative rounded text-xl flex gap-4 items-center transition-all ease-in-out duration-200 hover:gap-5 disabled:bg-cyan-800 disabled:hover:bg-opacity-50 disabled:cursor-not-allowed disabled:hover:gap-[15px]"
|
class="btn-cyan min-w-48 disabled:bg-cyan-800 disabled:cursor-not-allowed"
|
||||||
:disabled="!selected_character"
|
:disabled="!selected_character"
|
||||||
@click="select_character()"
|
@click="select_character()"
|
||||||
>
|
>
|
||||||
PLAY
|
Play now
|
||||||
<img class="h-8 drop-shadow-20" draggable="false" src="/assets/icons/arrow.svg" alt="Play icon" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<Menu />
|
<Menu />
|
||||||
<Hud />
|
<Hud />
|
||||||
<Hotkeys />
|
<Hotkeys />
|
||||||
<Minimap />
|
<Clock />
|
||||||
<Zone />
|
<Zone />
|
||||||
<Chat />
|
<Chat />
|
||||||
<ExpBar />
|
<ExpBar />
|
||||||
@ -30,7 +30,8 @@ import Hotkeys from '@/components/gui/Hotkeys.vue'
|
|||||||
import Chat from '@/components/gui/Chat.vue'
|
import Chat from '@/components/gui/Chat.vue'
|
||||||
import CharacterProfile from '@/components/gui/CharacterProfile.vue'
|
import CharacterProfile from '@/components/gui/CharacterProfile.vue'
|
||||||
import Effects from '@/components/Effects.vue'
|
import Effects from '@/components/Effects.vue'
|
||||||
import Minimap from '@/components/gui/Minimap.vue'
|
// import Minimap from '@/components/gui/Minimap.vue'
|
||||||
|
import Clock from '@/components/gui/Clock.vue'
|
||||||
import AwaitLoaderPlugin from 'phaser3-rex-plugins/plugins/awaitloader-plugin'
|
import AwaitLoaderPlugin from 'phaser3-rex-plugins/plugins/awaitloader-plugin'
|
||||||
|
|
||||||
const gameStore = useGameStore()
|
const gameStore = useGameStore()
|
||||||
|
@ -29,10 +29,10 @@
|
|||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { useGameStore } from '@/stores/gameStore'
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
import { useCookies } from '@vueuse/integrations/useCookies'
|
import { useCookies } from '@vueuse/integrations/useCookies'
|
||||||
import LoginForm from '@/components/screens/partials/login/LoginForm.vue'
|
import LoginForm from '@/components/login/LoginForm.vue'
|
||||||
import RegisterForm from '@/components/screens/partials/login/RegisterForm.vue'
|
import RegisterForm from '@/components/login/RegisterForm.vue'
|
||||||
import NewPasswordForm from '@/components/screens/partials/login/NewPasswordForm.vue'
|
import NewPasswordForm from '@/components/login/NewPasswordForm.vue'
|
||||||
import ResetPassword from '@/components/utilities/ResetPassword.vue'
|
import ResetPassword from '@/components/login/ResetPasswordModal.vue'
|
||||||
|
|
||||||
const isPasswordResetFormShown = ref(false)
|
const isPasswordResetFormShown = ref(false)
|
||||||
const doesUrlHaveToken = window.location.hash.includes('#')
|
const doesUrlHaveToken = window.location.hash.includes('#')
|
||||||
|
23
src/components/utilities/BackgroundImageLoader.vue
Normal file
23
src/components/utilities/BackgroundImageLoader.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<div style="display: none">
|
||||||
|
<img v-for="(url, index) in imageUrls" :key="index" :src="url" alt="" @load="handleImageLoad(index)" @error="handleImageError(index)" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
// Internal array of images to preload
|
||||||
|
const imageUrls = ref<string[]>(['/assets/ui-elements/ui-border-4-corners.svg', '/assets/ui-elements/ui-border-4-corners-light.svg', '/assets/ui-elements/ui-border-4-corners-small.svg'])
|
||||||
|
|
||||||
|
const loadedImages = ref<Set<number>>(new Set())
|
||||||
|
|
||||||
|
const handleImageLoad = (index: number) => {
|
||||||
|
loadedImages.value.add(index)
|
||||||
|
console.log(`Image ${index} loaded:`, imageUrls.value[index])
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleImageError = (index: number) => {
|
||||||
|
console.log(`Image ${index} failed to load:`, imageUrls.value[index])
|
||||||
|
}
|
||||||
|
</script>
|
@ -12,7 +12,7 @@ export type TeleportSettings = {
|
|||||||
export const useZoneEditorStore = defineStore('zoneEditor', {
|
export const useZoneEditorStore = defineStore('zoneEditor', {
|
||||||
state: () => {
|
state: () => {
|
||||||
return {
|
return {
|
||||||
active: true,
|
active: false,
|
||||||
zone: null as Zone | null,
|
zone: null as Zone | null,
|
||||||
tool: 'move',
|
tool: 'move',
|
||||||
drawMode: 'tile',
|
drawMode: 'tile',
|
||||||
@ -123,6 +123,8 @@ export const useZoneEditorStore = defineStore('zoneEditor', {
|
|||||||
this.drawMode = 'tile'
|
this.drawMode = 'tile'
|
||||||
this.selectedTile = ''
|
this.selectedTile = ''
|
||||||
this.selectedObject = null
|
this.selectedObject = null
|
||||||
|
this.isTileListModalShown = false
|
||||||
|
this.isObjectListModalShown = false
|
||||||
this.isSettingsModalShown = false
|
this.isSettingsModalShown = false
|
||||||
this.isZoneListModalShown = false
|
this.isZoneListModalShown = false
|
||||||
this.isCreateZoneModalShown = false
|
this.isCreateZoneModalShown = false
|
||||||
|
@ -224,3 +224,10 @@ export type WorldSettings = {
|
|||||||
isFogEnabled: boolean
|
isFogEnabled: boolean
|
||||||
fogDensity: number
|
fogDensity: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type WeatherState = {
|
||||||
|
isRainEnabled: boolean
|
||||||
|
rainPercentage: number
|
||||||
|
isFogEnabled: boolean
|
||||||
|
fogDensity: number
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user