1
0
forked from noxious/client

Several fixes, improvements, refactor for authentication

This commit is contained in:
Dennis Postma 2024-12-27 19:00:53 +01:00
parent 9d0f810ab3
commit 18b07d2f46
20 changed files with 113 additions and 131 deletions

6
package-lock.json generated
View File

@ -3690,9 +3690,9 @@
} }
}, },
"node_modules/es-module-lexer": { "node_modules/es-module-lexer": {
"version": "1.5.4", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
"integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },

View File

@ -21,7 +21,7 @@ export type AssetDataT = {
frameRate?: number frameRate?: number
frameWidth?: number frameWidth?: number
frameHeight?: number frameHeight?: number
frameRate?: number frameCount?: number
} }
export type Tile = { export type Tile = {

View File

@ -55,20 +55,23 @@ const initializeEffects = (scene: Phaser.Scene) => {
effects.light.value = scene.add.graphics().setDepth(1000) effects.light.value = scene.add.graphics().setDepth(1000)
// Rain // Rain
effects.rain.value = scene.add.particles(0, 0, 'raindrop', { effects.rain.value = scene.add
x: { min: 0, max: window.innerWidth }, .particles(0, 0, 'raindrop', {
y: -50, x: { min: 0, max: window.innerWidth },
quantity: 5, y: -50,
lifespan: 2000, quantity: 5,
speedY: { min: 300, max: 500 }, lifespan: 2000,
scale: { start: 0.005, end: 0.005 }, speedY: { min: 300, max: 500 },
alpha: { start: 0.5, end: 0 }, scale: { start: 0.005, end: 0.005 },
blendMode: 'ADD' alpha: { start: 0.5, end: 0 },
}).setDepth(900) blendMode: 'ADD'
})
.setDepth(900)
effects.rain.value.stop() effects.rain.value.stop()
// Fog // Fog
effects.fog.value = scene.add.sprite(window.innerWidth / 2, window.innerHeight / 2, 'fog') effects.fog.value = scene.add
.sprite(window.innerWidth / 2, window.innerHeight / 2, 'fog')
.setScale(2) .setScale(2)
.setAlpha(0) .setAlpha(0)
.setDepth(950) .setDepth(950)
@ -77,10 +80,13 @@ const initializeEffects = (scene: Phaser.Scene) => {
// Effect updates // Effect updates
const updateScene = () => { const updateScene = () => {
const timeBasedLight = calculateLightStrength(gameStore.world.date) const timeBasedLight = calculateLightStrength(gameStore.world.date)
const zoneEffects = zoneStore.zone?.zoneEffects?.reduce((acc, curr) => ({ const zoneEffects = zoneStore.zone?.zoneEffects?.reduce(
...acc, (acc, curr) => ({
[curr.effect]: curr.strength ...acc,
}), {}) as { [key: string]: number } [curr.effect]: curr.strength
}),
{}
) as { [key: string]: number }
// Only update effects once zoneEffects are loaded // Only update effects once zoneEffects are loaded
if (!zoneEffectsReady.value) { if (!zoneEffectsReady.value) {
@ -91,13 +97,14 @@ const updateScene = () => {
} }
} }
const finalEffects = zoneEffects && Object.keys(zoneEffects).length const finalEffects =
? zoneEffects zoneEffects && Object.keys(zoneEffects).length
: { ? zoneEffects
light: timeBasedLight, : {
rain: weatherState.value.isRainEnabled ? weatherState.value.rainPercentage : 0, light: timeBasedLight,
fog: weatherState.value.isFogEnabled ? weatherState.value.fogDensity * 100 : 0 rain: weatherState.value.isRainEnabled ? weatherState.value.rainPercentage : 0,
} fog: weatherState.value.isFogEnabled ? weatherState.value.fogDensity * 100 : 0
}
applyEffects(finalEffects) applyEffects(finalEffects)
} }
@ -105,16 +112,12 @@ const updateScene = () => {
const applyEffects = (effectValues: any) => { const applyEffects = (effectValues: any) => {
if (effects.light.value) { if (effects.light.value) {
const darkness = 1 - (effectValues.light ?? 100) / 100 const darkness = 1 - (effectValues.light ?? 100) / 100
effects.light.value.clear() effects.light.value.clear().fillStyle(0x000000, darkness).fillRect(0, 0, window.innerWidth, window.innerHeight)
.fillStyle(0x000000, darkness)
.fillRect(0, 0, window.innerWidth, window.innerHeight)
} }
if (effects.rain.value) { if (effects.rain.value) {
const strength = effectValues.rain ?? 0 const strength = effectValues.rain ?? 0
strength > 0 strength > 0 ? effects.rain.value.start().setQuantity(Math.floor((strength / 100) * 10)) : effects.rain.value.stop()
? effects.rain.value.start().setQuantity(Math.floor((strength / 100) * 10))
: effects.rain.value.stop()
} }
if (effects.fog.value) { if (effects.fog.value) {
@ -126,19 +129,14 @@ const calculateLightStrength = (time: Date): number => {
const hour = time.getHours() const hour = time.getHours()
const minute = time.getMinutes() const minute = time.getMinutes()
if (hour >= LIGHT_CONFIG.SUNSET_HOUR || hour < LIGHT_CONFIG.SUNRISE_HOUR) if (hour >= LIGHT_CONFIG.SUNSET_HOUR || hour < LIGHT_CONFIG.SUNRISE_HOUR) return LIGHT_CONFIG.NIGHT_STRENGTH
return LIGHT_CONFIG.NIGHT_STRENGTH
if (hour > LIGHT_CONFIG.SUNRISE_HOUR && hour < LIGHT_CONFIG.SUNSET_HOUR - 2) if (hour > LIGHT_CONFIG.SUNRISE_HOUR && hour < LIGHT_CONFIG.SUNSET_HOUR - 2) return LIGHT_CONFIG.DAY_STRENGTH
return LIGHT_CONFIG.DAY_STRENGTH
if (hour === LIGHT_CONFIG.SUNRISE_HOUR) if (hour === LIGHT_CONFIG.SUNRISE_HOUR) return LIGHT_CONFIG.NIGHT_STRENGTH + ((LIGHT_CONFIG.DAY_STRENGTH - LIGHT_CONFIG.NIGHT_STRENGTH) * minute) / 60
return LIGHT_CONFIG.NIGHT_STRENGTH +
((LIGHT_CONFIG.DAY_STRENGTH - LIGHT_CONFIG.NIGHT_STRENGTH) * minute) / 60
const totalMinutes = (hour - (LIGHT_CONFIG.SUNSET_HOUR - 2)) * 60 + minute const totalMinutes = (hour - (LIGHT_CONFIG.SUNSET_HOUR - 2)) * 60 + minute
return LIGHT_CONFIG.DAY_STRENGTH - return LIGHT_CONFIG.DAY_STRENGTH - (LIGHT_CONFIG.DAY_STRENGTH - LIGHT_CONFIG.NIGHT_STRENGTH) * (totalMinutes / 120)
(LIGHT_CONFIG.DAY_STRENGTH - LIGHT_CONFIG.NIGHT_STRENGTH) * (totalMinutes / 120)
} }
// Socket and window handlers // Socket and window handlers
@ -157,17 +155,19 @@ const setupSocketListeners = () => {
} }
const handleResize = () => { const handleResize = () => {
if (effects.rain.value) if (effects.rain.value) effects.rain.value.updateConfig({ x: { min: 0, max: window.innerWidth } })
effects.rain.value.updateConfig({ x: { min: 0, max: window.innerWidth } }) if (effects.fog.value) effects.fog.value.setPosition(window.innerWidth / 2, window.innerHeight / 2)
if (effects.fog.value)
effects.fog.value.setPosition(window.innerWidth / 2, window.innerHeight / 2)
} }
// Lifecycle // Lifecycle
watch(() => zoneStore.zone, () => { watch(
zoneEffectsReady.value = false () => zoneStore.zone,
updateScene() () => {
}, { deep: true }) zoneEffectsReady.value = false
updateScene()
},
{ deep: true }
)
onMounted(() => window.addEventListener('resize', handleResize)) onMounted(() => window.addEventListener('resize', handleResize))

View File

@ -3,7 +3,7 @@
<Healthbar :zoneCharacter="props.zoneCharacter" :currentX="currentX" :currentY="currentY" /> <Healthbar :zoneCharacter="props.zoneCharacter" :currentX="currentX" :currentY="currentY" />
<Container ref="charContainer" :depth="isometricDepth" :x="currentX" :y="currentY"> <Container ref="charContainer" :depth="isometricDepth" :x="currentX" :y="currentY">
<CharacterHair :zoneCharacter="props.zoneCharacter" :currentX="currentX" :currentY="currentY" /> <CharacterHair :zoneCharacter="props.zoneCharacter" :currentX="currentX" :currentY="currentY" />
<!-- <CharacterChest :zoneCharacter="props.zoneCharacter" :currentX="currentX" :currentY="currentY" />--> <!-- <CharacterChest :zoneCharacter="props.zoneCharacter" :currentX="currentX" :currentY="currentY" />-->
<Sprite ref="charSprite" :origin-y="1" :flipX="isFlippedX" /> <Sprite ref="charSprite" :origin-y="1" :flipX="isFlippedX" />
</Container> </Container>
</template> </template>

View File

@ -31,16 +31,14 @@ const isFlippedX = computed(() => [6, 4].includes(props.zoneCharacter.character.
const imageProps = computed(() => { const imageProps = computed(() => {
// Get the current sprite action based on direction // Get the current sprite action based on direction
const direction = [0, 6].includes(props.zoneCharacter.character.rotation ?? 0) ? 'back' : 'front' const direction = [0, 6].includes(props.zoneCharacter.character.rotation ?? 0) ? 'back' : 'front'
const spriteAction = props.zoneCharacter.character.characterHair?.sprite?.spriteActions?.find( const spriteAction = props.zoneCharacter.character.characterHair?.sprite?.spriteActions?.find((spriteAction) => spriteAction.action === direction)
spriteAction => spriteAction.action === direction
)
return { return {
depth: 1, depth: 1,
originX: Number(spriteAction?.originX) ?? 0, originX: Number(spriteAction?.originX) ?? 0,
originY: Number(spriteAction?.originY) ?? 0, originY: Number(spriteAction?.originY) ?? 0,
flipX: isFlippedX.value, flipX: isFlippedX.value,
texture: texture.value, texture: texture.value
// y: props.zoneCharacter.isMoving ? Math.floor(Date.now() / 250) % 2 : 0 // y: props.zoneCharacter.isMoving ? Math.floor(Date.now() / 250) % 2 : 0
} }
}) })

View File

@ -31,9 +31,7 @@ const isFlippedX = computed(() => [6, 4].includes(props.zoneCharacter.character.
const imageProps = computed(() => { const imageProps = computed(() => {
// Get the current sprite action based on direction // Get the current sprite action based on direction
const direction = [0, 6].includes(props.zoneCharacter.character.rotation ?? 0) ? 'back' : 'front' const direction = [0, 6].includes(props.zoneCharacter.character.rotation ?? 0) ? 'back' : 'front'
const spriteAction = props.zoneCharacter.character.characterHair?.sprite?.spriteActions?.find( const spriteAction = props.zoneCharacter.character.characterHair?.sprite?.spriteActions?.find((spriteAction) => spriteAction.action === direction)
spriteAction => spriteAction.action === direction
)
return { return {
depth: 1, depth: 1,

View File

@ -51,7 +51,7 @@ gameStore.connection!.on('zone:character:leave', (characterId: number) => {
zoneStore.removeCharacter(characterId) zoneStore.removeCharacter(characterId)
}) })
gameStore.connection!.on('character:move', (data: { id: number, positionX: number, positionY: number, rotation: number, isMoving: boolean }) => { gameStore.connection!.on('character:move', (data: { id: number; positionX: number; positionY: number; rotation: number; isMoving: boolean }) => {
zoneStore.updateCharacterPosition(data) zoneStore.updateCharacterPosition(data)
}) })

View File

@ -11,7 +11,13 @@
</div> </div>
<div v-bind="containerProps" class="overflow-y-auto relative p-2.5 rounded-md default-border bg-gray" @scroll="onScroll"> <div v-bind="containerProps" class="overflow-y-auto relative p-2.5 rounded-md default-border bg-gray" @scroll="onScroll">
<div v-bind="wrapperProps" ref="elementToScroll"> <div v-bind="wrapperProps" ref="elementToScroll">
<a v-for="{ data: characterHair } in list" :key="characterHair.id" class="relative p-2.5 cursor-pointer block rounded hover:bg-cyan group" :class="{ 'bg-cyan': assetManagerStore.selectedCharacterHair?.id === characterHair.id }" @click="assetManagerStore.setSelectedCharacterHair(characterHair as CharacterHair)"> <a
v-for="{ data: characterHair } in list"
:key="characterHair.id"
class="relative p-2.5 cursor-pointer block rounded hover:bg-cyan group"
:class="{ 'bg-cyan': assetManagerStore.selectedCharacterHair?.id === characterHair.id }"
@click="assetManagerStore.setSelectedCharacterHair(characterHair as CharacterHair)"
>
<div class="flex items-center gap-2.5"> <div class="flex items-center gap-2.5">
<span class="group-hover:text-white" :class="{ 'text-white': assetManagerStore.selectedCharacterHair?.id === characterHair.id }">{{ characterHair.name }}</span> <span class="group-hover:text-white" :class="{ 'text-white': assetManagerStore.selectedCharacterHair?.id === characterHair.id }">{{ characterHair.name }}</span>
</div> </div>

View File

@ -11,7 +11,13 @@
</div> </div>
<div v-bind="containerProps" class="overflow-y-auto relative p-2.5 rounded-md default-border bg-gray" @scroll="onScroll"> <div v-bind="containerProps" class="overflow-y-auto relative p-2.5 rounded-md default-border bg-gray" @scroll="onScroll">
<div v-bind="wrapperProps" ref="elementToScroll"> <div v-bind="wrapperProps" ref="elementToScroll">
<a v-for="{ data: characterType } in list" :key="characterType.id" class="relative p-2.5 cursor-pointer block rounded hover:bg-cyan group" :class="{ 'bg-cyan': assetManagerStore.selectedCharacterType?.id === characterType.id }" @click="assetManagerStore.setSelectedCharacterType(characterType as CharacterType)"> <a
v-for="{ data: characterType } in list"
:key="characterType.id"
class="relative p-2.5 cursor-pointer block rounded hover:bg-cyan group"
:class="{ 'bg-cyan': assetManagerStore.selectedCharacterType?.id === characterType.id }"
@click="assetManagerStore.setSelectedCharacterType(characterType as CharacterType)"
>
<div class="flex items-center gap-2.5"> <div class="flex items-center gap-2.5">
<span class="group-hover:text-white" :class="{ 'text-white': assetManagerStore.selectedCharacterType?.id === characterType.id }">{{ characterType.name }}</span> <span class="group-hover:text-white" :class="{ 'text-white': assetManagerStore.selectedCharacterType?.id === characterType.id }">{{ characterType.name }}</span>
</div> </div>

View File

@ -64,10 +64,7 @@ const filteredItems = computed(() => {
if (!searchQuery.value) { if (!searchQuery.value) {
return assetManagerStore.itemList return assetManagerStore.itemList
} }
return assetManagerStore.itemList.filter((item) => return assetManagerStore.itemList.filter((item) => item.name.toLowerCase().includes(searchQuery.value.toLowerCase()) || item.itemType.toLowerCase().includes(searchQuery.value.toLowerCase()))
item.name.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
item.itemType.toLowerCase().includes(searchQuery.value.toLowerCase())
)
}) })
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(filteredItems, { const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(filteredItems, {

View File

@ -1,5 +1,5 @@
<template> <template>
<form @submit.prevent="loginFunc" class="relative px-6 py-11"> <form @submit.prevent="submit" class="relative px-6 py-11">
<div class="flex flex-col gap-5 p-2 mb-8 relative"> <div class="flex flex-col gap-5 p-2 mb-8 relative">
<div class="w-full grid gap-3 relative"> <div class="w-full grid gap-3 relative">
<input class="input-field xs:min-w-[350px] min-w-64" id="username-login" v-model="username" type="text" name="username" placeholder="Username" required autofocus /> <input class="input-field xs:min-w-[350px] min-w-64" id="username-login" v-model="username" type="text" name="username" placeholder="Username" required autofocus />
@ -7,7 +7,7 @@
<input class="input-field xs:min-w-[350px] min-w-64" id="password-login" v-model="password" :type="showPassword ? 'text' : 'password'" name="password" placeholder="Password" required /> <input class="input-field xs:min-w-[350px] min-w-64" id="password-login" v-model="password" :type="showPassword ? 'text' : 'password'" name="password" placeholder="Password" required />
<button type="button" @click.prevent="showPassword = !showPassword" :class="{ 'eye-open': showPassword }" class="bg-[url('/assets/icons/eye.svg')] p-0 absolute right-3 w-5 h-4 top-1/2 -translate-y-1/2 bg-no-repeat bg-center"></button> <button type="button" @click.prevent="showPassword = !showPassword" :class="{ 'eye-open': showPassword }" class="bg-[url('/assets/icons/eye.svg')] p-0 absolute right-3 w-5 h-4 top-1/2 -translate-y-1/2 bg-no-repeat bg-center"></button>
</div> </div>
<span v-if="loginError" class="text-red-200 text-xs absolute top-full mt-1">{{ loginError }}</span> <span v-if="formError" class="text-red-200 text-xs absolute top-full mt-1">{{ formError }}</span>
</div> </div>
<button @click.stop="() => emit('openResetPasswordModal')" type="button" class="inline-flex self-end p-0 text-cyan-300 text-base">Forgot password?</button> <button @click.stop="() => emit('openResetPasswordModal')" type="button" class="inline-flex self-end p-0 text-cyan-300 text-base">Forgot password?</button>
<button class="btn-cyan px-0 xs:w-full" type="submit">Play now</button> <button class="btn-cyan px-0 xs:w-full" type="submit">Play now</button>
@ -36,7 +36,7 @@ const emit = defineEmits(['openResetPasswordModal', 'switchToRegister'])
const gameStore = useGameStore() const gameStore = useGameStore()
const username = ref('') const username = ref('')
const password = ref('') const password = ref('')
const loginError = ref('') const formError = ref('')
const showPassword = ref(false) const showPassword = ref(false)
// automatic login because of development // automatic login because of development
@ -48,10 +48,10 @@ onMounted(async () => {
} }
}) })
async function loginFunc() { async function submit() {
// check if username and password are valid // check if username and password are valid
if (username.value === '' || password.value === '') { if (username.value === '' || password.value === '') {
loginError.value = 'Please enter a valid username and password' formError.value = 'Please enter a valid username and password'
return return
} }
@ -59,7 +59,7 @@ async function loginFunc() {
const response = await login(username.value, password.value) const response = await login(username.value, password.value)
if (response.success === undefined) { if (response.success === undefined) {
loginError.value = response.error formError.value = response.error
return return
} }
gameStore.setToken(response.token) gameStore.setToken(response.token)

View File

@ -1,5 +1,5 @@
<template> <template>
<form @submit.prevent="registerFunc" class="relative px-6 py-11"> <form @submit.prevent="submit" class="relative px-6 py-11">
<div class="flex flex-col gap-5 p-2 mb-8 relative"> <div class="flex flex-col gap-5 p-2 mb-8 relative">
<div class="w-full grid gap-3 relative"> <div class="w-full grid gap-3 relative">
<input class="input-field xs:min-w-[350px] min-w-64" id="username-register" v-model="username" type="text" name="username" placeholder="Username" required autofocus /> <input class="input-field xs:min-w-[350px] min-w-64" id="username-register" v-model="username" type="text" name="username" placeholder="Username" required autofocus />
@ -8,7 +8,7 @@
<input class="input-field xs:min-w-[350px] min-w-64" id="password-register" v-model="password" :type="showPassword ? 'text' : 'password'" name="password" placeholder="Password" required /> <input class="input-field xs:min-w-[350px] min-w-64" id="password-register" v-model="password" :type="showPassword ? 'text' : 'password'" name="password" placeholder="Password" required />
<button type="button" @click.prevent="showPassword = !showPassword" :class="{ 'eye-open': showPassword }" class="bg-[url('/assets/icons/eye.svg')] p-0 absolute right-3 w-4 h-3 top-1/2 -translate-y-1/2 bg-no-repeat"></button> <button type="button" @click.prevent="showPassword = !showPassword" :class="{ 'eye-open': showPassword }" class="bg-[url('/assets/icons/eye.svg')] p-0 absolute right-3 w-4 h-3 top-1/2 -translate-y-1/2 bg-no-repeat"></button>
</div> </div>
<span v-if="loginError" class="text-red-200 text-xs -mt-2">{{ loginError }}</span> <span v-if="formError" class="text-red-200 text-xs -mt-2">{{ formError }}</span>
</div> </div>
<button class="btn-cyan xs:w-full" type="submit">Register now</button> <button class="btn-cyan xs:w-full" type="submit">Register now</button>
@ -37,7 +37,7 @@ const gameStore = useGameStore()
const username = ref('') const username = ref('')
const password = ref('') const password = ref('')
const email = ref('') const email = ref('')
const loginError = ref('') const formError = ref('')
const showPassword = ref(false) const showPassword = ref(false)
// automatic login because of development // automatic login because of development
@ -49,34 +49,15 @@ onMounted(async () => {
} }
}) })
async function loginFunc() { async function submit() {
// check if username and password are valid
if (username.value === '' || password.value === '') {
loginError.value = 'Please enter a valid username and password'
return
}
// send login event to server
const response = await login(username.value, password.value)
if (response.success === undefined) {
loginError.value = response.error
return
}
gameStore.setToken(response.token)
gameStore.initConnection()
return true // Indicate success
}
async function registerFunc() {
// check if username and password are valid // check if username and password are valid
if (username.value === '' || email.value === '' || password.value === '') { if (username.value === '' || email.value === '' || password.value === '') {
loginError.value = 'Please enter a valid username, email, and password' formError.value = 'Please enter a valid username, email, and password'
return return
} }
if (email.value === '') { if (email.value === '') {
loginError.value = 'Please enter an email' formError.value = 'Please enter an email'
return return
} }
@ -84,14 +65,18 @@ async function registerFunc() {
const response = await register(username.value, email.value, password.value) const response = await register(username.value, email.value, password.value)
if (response.success === undefined) { if (response.success === undefined) {
loginError.value = response.error formError.value = response.error
return return
} }
const loginSuccess = await loginFunc() const loginResponse = await login(username.value, password.value)
if (!loginSuccess) {
loginError.value = 'Login after registration failed. Please try logging in manually.' if (!loginResponse) {
formError.value = 'Login after registration failed. Please try logging in manually.'
return return
} }
gameStore.setToken(loginResponse.token)
gameStore.initConnection()
} }
</script> </script>

View File

@ -16,12 +16,7 @@
<div class="flex w-full max-lg:flex-col lg:h-[400px] default-border rounded-md rounded-tl-none bg-gray"> <div class="flex w-full max-lg:flex-col lg:h-[400px] default-border rounded-md rounded-tl-none bg-gray">
<div class="lg:min-w-[285px] max-lg:min-h-[383px] lg:w-1/3 h-full bg-[url('/assets/ui-texture.png')] bg-no-repeat bg-cover bg-center border-0 max-lg:border-b lg:border-r border-solid border-gray-500 max-lg:rounded-tr-md lg:rounded-bl-md relative"> <div class="lg:min-w-[285px] max-lg:min-h-[383px] lg:w-1/3 h-full bg-[url('/assets/ui-texture.png')] bg-no-repeat bg-cover bg-center border-0 max-lg:border-b lg:border-r border-solid border-gray-500 max-lg:rounded-tr-md lg:rounded-bl-md relative">
<div class="absolute right-full -top-px flex gap-1 flex-col"> <div class="absolute right-full -top-px flex gap-1 flex-col">
<div <div v-for="character in characters" :key="character.id" class="character relative rounded-l default-border w-9 h-[50px] bg-[url('/assets/ui-texture.png')] after:absolute after:w-full after:h-px after:bg-gray-500" :class="{ active: selectedCharacterId === character.id }">
v-for="character in characters"
:key="character.id"
class="character relative rounded-l default-border w-9 h-[50px] bg-[url('/assets/ui-texture.png')] after:absolute after:w-full after:h-px after:bg-gray-500"
:class="{ active: selectedCharacterId === character.id }"
>
<img src="/assets/placeholders/head.png" class="w-9 h-9 object-contain absolute top-1/2 -translate-y-1/2" alt="Player head" /> <img src="/assets/placeholders/head.png" class="w-9 h-9 object-contain absolute top-1/2 -translate-y-1/2" alt="Player head" />
<input class="h-full w-full absolute m-0 z-10 hover:cursor-pointer focus-visible:outline-offset-0" type="radio" name="character" :value="character.id" v-model="selectedCharacterId" /> <input class="h-full w-full absolute m-0 z-10 hover:cursor-pointer focus-visible:outline-offset-0" type="radio" name="character" :value="character.id" v-model="selectedCharacterId" />
</div> </div>
@ -33,13 +28,14 @@
</div> </div>
<div class="py-6 px-8 h-[calc(100%_-_48px)] flex flex-col items-center gap-6 justify-center" v-if="selectedCharacterId"> <div class="py-6 px-8 h-[calc(100%_-_48px)] flex flex-col items-center gap-6 justify-center" v-if="selectedCharacterId">
<input class="input-field w-[158px]" type="text" name="name" :placeholder="characters.find((c) => c.id == selectedCharacterId)?.name" /> <input class="input-field w-[158px]" type="text" name="name" :placeholder="characters.find((c) => c.id == selectedCharacterId)?.name" />
<div class="flex flex-col gap-4 items-center">s <div class="flex flex-col gap-4 items-center">
s
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
<div class="bg-[url('/assets/ui-elements/character-select-ui-shape.svg')] w-[190px] h-52 bg-no-repeat bg-center flex items-center justify-between"> <div class="bg-[url('/assets/ui-elements/character-select-ui-shape.svg')] w-[190px] h-52 bg-no-repeat bg-center flex items-center justify-between">
<button class="ml-6 w-4 h-8 p-0"> <button class="ml-6 w-4 h-8 p-0">
<img src="/assets/icons/triangle-icon.svg" class="w-3 h-3.5 m-auto" alt="Arrow left" /> <img src="/assets/icons/triangle-icon.svg" class="w-3 h-3.5 m-auto" alt="Arrow left" />
</button> </button>
<img class="w-24 object-contain mb-3.5" alt="Player avatar" :src="config.server_endpoint + '/avatar/s/' + characters.find(c => c.id === selectedCharacterId)?.characterType?.id + '/' + (selectedHairId ?? 'default')" /> <img class="w-24 object-contain mb-3.5" alt="Player avatar" :src="config.server_endpoint + '/avatar/s/' + characters.find((c) => c.id === selectedCharacterId)?.characterType?.id + '/' + (selectedHairId ?? 'default')" />
<button class="mr-6 w-4 h-8 p-0"> <button class="mr-6 w-4 h-8 p-0">
<img src="/assets/icons/triangle-icon.svg" class="w-3 h-3.5 -scale-x-100" alt="Arrow right" /> <img src="/assets/icons/triangle-icon.svg" class="w-3 h-3.5 -scale-x-100" alt="Arrow right" />
</button> </button>

View File

@ -6,7 +6,7 @@
<div class="bg-[url('/assets/login/login-bg.png')] 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"></div> <div class="bg-[url('/assets/login/login-bg.png')] 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"></div>
<div class="bg-gray-900 z-20 w-full lg:w-1/2 h-[65dvh] lg:h-dvh relative"> <div class="bg-gray-900 z-20 w-full lg:w-1/2 h-[65dvh] lg:h-dvh relative">
<div class="h-dvh flex items-center lg:justify-center flex-col px-8 max-lg:pt-20"> <div class="h-dvh flex items-center lg:justify-center flex-col px-8 max-lg:pt-20">
<!-- <img src="/assets/tlogo.png" class="mb-10 w-52" alt="Noxious logo" />--> <!-- <img src="/assets/tlogo.png" class="mb-10 w-52" alt="Noxious logo" />-->
<div class="relative"> <div class="relative">
<img src="/assets/ui-elements/login-ui-box-outer.svg" class="absolute w-full h-full" alt="UI box outer" /> <img src="/assets/ui-elements/login-ui-box-outer.svg" class="absolute w-full h-full" alt="UI box outer" />
<img src="/assets/ui-elements/login-ui-box-inner.svg" class="absolute left-2 top-2 w-[calc(100%_-_16px)] h-[calc(100%_-_16px)] max-lg:hidden" alt="UI box inner" /> <img src="/assets/ui-elements/login-ui-box-inner.svg" class="absolute left-2 top-2 w-[calc(100%_-_16px)] h-[calc(100%_-_16px)] max-lg:hidden" alt="UI box inner" />

View File

@ -32,21 +32,13 @@ const { setupPointerHandlers, cleanupPointerHandlers } = usePointerHandlers(scen
function handleScrollZoom(pointer: Phaser.Input.Pointer) { function handleScrollZoom(pointer: Phaser.Input.Pointer) {
if (!(pointer.event instanceof WheelEvent && pointer.event.shiftKey)) return if (!(pointer.event instanceof WheelEvent && pointer.event.shiftKey)) return
const zoomLevel = Phaser.Math.Clamp( const zoomLevel = Phaser.Math.Clamp(camera.zoom - pointer.event.deltaY * ZOOM_SETTINGS.WHEEL_FACTOR, ZOOM_SETTINGS.MIN, ZOOM_SETTINGS.MAX)
camera.zoom - pointer.event.deltaY * ZOOM_SETTINGS.WHEEL_FACTOR,
ZOOM_SETTINGS.MIN,
ZOOM_SETTINGS.MAX
)
camera.setZoom(zoomLevel) camera.setZoom(zoomLevel)
} }
function handleKeyComboZoom(event: { keyCodes: number[] }) { function handleKeyComboZoom(event: { keyCodes: number[] }) {
const deltaY = event.keyCodes[1] === 38 ? 1 : -1 // 38 is Up, 40 is Down const deltaY = event.keyCodes[1] === 38 ? 1 : -1 // 38 is Up, 40 is Down
const zoomLevel = Phaser.Math.Clamp( const zoomLevel = Phaser.Math.Clamp(camera.zoom + deltaY * ZOOM_SETTINGS.KEY_FACTOR, ZOOM_SETTINGS.MIN, ZOOM_SETTINGS.MAX)
camera.zoom + deltaY * ZOOM_SETTINGS.KEY_FACTOR,
ZOOM_SETTINGS.MIN,
ZOOM_SETTINGS.MAX
)
camera.setZoom(zoomLevel) camera.setZoom(zoomLevel)
} }

View File

@ -63,6 +63,7 @@ export async function loadSpriteTextures(scene: Phaser.Scene, sprite: Sprite) {
const sprite_actions: HttpResponse<any[]> = await fetch(config.server_endpoint + '/assets/list_sprite_actions/' + sprite?.id).then((response) => response.json()) const sprite_actions: HttpResponse<any[]> = await fetch(config.server_endpoint + '/assets/list_sprite_actions/' + sprite?.id).then((response) => response.json())
for await (const sprite_action of sprite_actions.data ?? []) { for await (const sprite_action of sprite_actions.data ?? []) {
console.log(sprite_action)
await loadTexture(scene, { await loadTexture(scene, {
key: sprite_action.key, key: sprite_action.key,
data: sprite_action.data, data: sprite_action.data,
@ -73,7 +74,7 @@ export async function loadSpriteTextures(scene: Phaser.Scene, sprite: Sprite) {
isAnimated: sprite_action.isAnimated, isAnimated: sprite_action.isAnimated,
frameWidth: sprite_action.frameWidth, frameWidth: sprite_action.frameWidth,
frameHeight: sprite_action.frameHeight, frameHeight: sprite_action.frameHeight,
frameRate: sprite_action.frameRate, frameRate: sprite_action.frameRate
} as AssetDataT) } as AssetDataT)
// If the sprite is not animated, skip // If the sprite is not animated, skip
@ -88,7 +89,7 @@ export async function loadSpriteTextures(scene: Phaser.Scene, sprite: Sprite) {
scene.anims.create({ scene.anims.create({
key: sprite_action.key, key: sprite_action.key,
frameRate: sprite_action.frameRate, frameRate: sprite_action.frameRate,
frames: scene.anims.generateFrameNumbers(sprite_action.key, { start: 0, end: sprite_action.frameRate! - 1 }), frames: scene.anims.generateFrameNumbers(sprite_action.key, { start: 0, end: sprite_action.frameCount! - 1 }),
repeat: -1 repeat: -1
}) })
} }

View File

@ -6,8 +6,10 @@ import { getDomain } from '@/application/utilities'
export async function register(username: string, email: string, password: string) { export async function register(username: string, email: string, password: string) {
try { try {
const response = await axios.post(`${config.server_endpoint}/register`, { username, email, password }) const response = await axios.post(`${config.server_endpoint}/register`, { username, email, password })
useCookies().set('token', response.data.data.token as string) if (response.status === 200) {
return { success: true, token: response.data.data.token } return { success: true }
}
return { error: response.data.message }
} catch (error: any) { } catch (error: any) {
if (typeof error.response?.data === 'undefined') { if (typeof error.response?.data === 'undefined') {
return { error: 'Could not connect to server' } return { error: 'Could not connect to server' }

View File

@ -33,9 +33,10 @@ export class AssetStorage {
originX: asset.originX, originX: asset.originX,
originY: asset.originY, originY: asset.originY,
isAnimated: asset.isAnimated, isAnimated: asset.isAnimated,
frameRate: asset.frameRate,
frameWidth: asset.frameWidth, frameWidth: asset.frameWidth,
frameHeight: asset.frameHeight, frameHeight: asset.frameHeight,
frameRate: asset.frameRate frameCount: asset.frameCount
}) })
} catch (error) { } catch (error) {
console.error(`Failed to add asset ${asset.key}:`, error) console.error(`Failed to add asset ${asset.key}:`, error)

View File

@ -40,10 +40,10 @@ export const useZoneStore = defineStore('zone', {
setCharacterLoaded(loaded: boolean) { setCharacterLoaded(loaded: boolean) {
this.characterLoaded = loaded this.characterLoaded = loaded
}, },
updateCharacterPosition(data: { id: number, positionX: number, positionY: number, rotation: number, isMoving: boolean }) { updateCharacterPosition(data: { id: number; positionX: number; positionY: number; rotation: number; isMoving: boolean }) {
const character = this.characters.find(char => char.character.id === data.id) const character = this.characters.find((char) => char.character.id === data.id)
if (character) { if (character) {
character.character.positionX= data.positionX character.character.positionX = data.positionX
character.character.positionY = data.positionY character.character.positionY = data.positionY
character.character.rotation = data.rotation character.character.rotation = data.rotation
character.isMoving = data.isMoving character.isMoving = data.isMoving