forked from noxious/client
Moved game components into a new folder, working proof of concept hair customisation
This commit is contained in:
parent
ee3e1b55cb
commit
ab97e27f27
@ -12,6 +12,8 @@
|
|||||||
</Container>
|
</Container>
|
||||||
<!-- Character sprite -->
|
<!-- Character sprite -->
|
||||||
<Container ref="charContainer" :depth="isometricDepth" :x="currentX" :y="currentY">
|
<Container ref="charContainer" :depth="isometricDepth" :x="currentX" :y="currentY">
|
||||||
|
<!-- HAIR-->
|
||||||
|
<Image :origin-y="4.2" :depth="1" :texture="props.zoneCharacter.character.hair?.spriteId + '-front'" />
|
||||||
<Sprite ref="charSprite" :origin-y="1" :flipX="isFlippedX" :flipY="false" />
|
<Sprite ref="charSprite" :origin-y="1" :flipX="isFlippedX" :flipY="false" />
|
||||||
</Container>
|
</Container>
|
||||||
</template>
|
</template>
|
||||||
@ -22,7 +24,7 @@ import { type Sprite as SpriteT, type ZoneCharacter } from '@/types'
|
|||||||
import { useGameStore } from '@/stores/gameStore'
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
import { useZoneStore } from '@/stores/zoneStore'
|
import { useZoneStore } from '@/stores/zoneStore'
|
||||||
import { watch, computed, ref, onMounted, onUnmounted } from 'vue'
|
import { watch, computed, ref, onMounted, onUnmounted } from 'vue'
|
||||||
import { Container, refObj, RoundRectangle, Sprite, Text, useGame, useScene } from 'phavuer'
|
import { Container, Image, refObj, RoundRectangle, Sprite, Text, useGame, useScene } from 'phavuer'
|
||||||
import { calculateIsometricDepth, tileToWorldX, tileToWorldY } from '@/composables/zoneComposable'
|
import { calculateIsometricDepth, tileToWorldX, tileToWorldY } from '@/composables/zoneComposable'
|
||||||
import { loadSpriteTextures } from '@/composables/gameComposable'
|
import { loadSpriteTextures } from '@/composables/gameComposable'
|
||||||
|
|
||||||
@ -31,7 +33,6 @@ enum Direction {
|
|||||||
NEGATIVE,
|
NEGATIVE,
|
||||||
UNCHANGED
|
UNCHANGED
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
layer: Phaser.Tilemaps.TilemapLayer
|
layer: Phaser.Tilemaps.TilemapLayer
|
||||||
zoneCharacter: ZoneCharacter
|
zoneCharacter: ZoneCharacter
|
||||||
@ -181,6 +182,14 @@ watch(
|
|||||||
|
|
||||||
watch(() => props.zoneCharacter, updateSprite)
|
watch(() => props.zoneCharacter, updateSprite)
|
||||||
|
|
||||||
|
|
||||||
|
// Hair demo
|
||||||
|
loadSpriteTextures(scene, props.zoneCharacter.character.hair?.sprite as SpriteT)
|
||||||
|
.then(() => {})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Error loading texture:', error)
|
||||||
|
})
|
||||||
|
|
||||||
loadSpriteTextures(scene, props.zoneCharacter.character.characterType?.sprite as SpriteT)
|
loadSpriteTextures(scene, props.zoneCharacter.character.characterType?.sprite as SpriteT)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
charSprite.value!.setTexture(charTexture.value)
|
charSprite.value!.setTexture(charTexture.value)
|
@ -3,7 +3,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Character from '@/components/sprites/Character.vue'
|
import Character from '@/components/game/character/Character.vue'
|
||||||
import { useZoneStore } from '@/stores/zoneStore'
|
import { useZoneStore } from '@/stores/zoneStore'
|
||||||
|
|
||||||
const zoneStore = useZoneStore()
|
const zoneStore = useZoneStore()
|
@ -11,9 +11,9 @@ import { useGameStore } from '@/stores/gameStore'
|
|||||||
import { useZoneStore } from '@/stores/zoneStore'
|
import { useZoneStore } from '@/stores/zoneStore'
|
||||||
import { loadZoneTilesIntoScene } from '@/composables/zoneComposable'
|
import { loadZoneTilesIntoScene } from '@/composables/zoneComposable'
|
||||||
import type { Zone as ZoneT, ZoneCharacter } from '@/types'
|
import type { Zone as ZoneT, ZoneCharacter } from '@/types'
|
||||||
import ZoneTiles from '@/components/zone/ZoneTiles.vue'
|
import ZoneTiles from '@/components/game/zone/ZoneTiles.vue'
|
||||||
import ZoneObjects from '@/components/zone/ZoneObjects.vue'
|
import ZoneObjects from '@/components/game/zone/ZoneObjects.vue'
|
||||||
import Characters from '@/components/zone/Characters.vue'
|
import Characters from '@/components/game/zone/Characters.vue'
|
||||||
|
|
||||||
const scene = useScene()
|
const scene = useScene()
|
||||||
const gameStore = useGameStore()
|
const gameStore = useGameStore()
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useZoneStore } from '@/stores/zoneStore'
|
import { useZoneStore } from '@/stores/zoneStore'
|
||||||
import ZoneObject from '@/components/zone/partials/ZoneObject.vue'
|
import ZoneObject from '@/components/game/zone/partials/ZoneObject.vue'
|
||||||
|
|
||||||
const zoneStore = useZoneStore()
|
const zoneStore = useZoneStore()
|
||||||
|
|
@ -37,11 +37,11 @@ const chats = ref([] as Chat[])
|
|||||||
const chatWindow = ref<HTMLElement | null>(null)
|
const chatWindow = ref<HTMLElement | null>(null)
|
||||||
const chatInput = ref<HTMLElement | null>(null)
|
const chatInput = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
onClickOutside(chatInput, event => unfocusChat(event, chatInput.value as HTMLElement))
|
onClickOutside(chatInput, (event) => unfocusChat(event, chatInput.value as HTMLElement))
|
||||||
|
|
||||||
function unfocusChat(event: Event, targetElement: HTMLElement) {
|
function unfocusChat(event: Event, targetElement: HTMLElement) {
|
||||||
if (!(event.target instanceof Node) || !targetElement.contains(event.target)) {
|
if (!(event.target instanceof Node) || !targetElement.contains(event.target)) {
|
||||||
targetElement.blur();
|
targetElement.blur()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,12 @@
|
|||||||
<div class="flex w-full h-[400px] border border-solid border-gray-500 rounded-md rounded-tl-none bg-gray">
|
<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="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 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')] after:absolute after:w-full after:h-px after:bg-gray-500" :class="{ active: selected_character == character.id }">
|
<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')] after:absolute after:w-full after:h-px after:bg-gray-500"
|
||||||
|
: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" />
|
<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="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="selected_character" />
|
<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="selected_character" />
|
||||||
</div>
|
</div>
|
||||||
@ -66,8 +71,18 @@
|
|||||||
<button class="bg-gray border border-solid border-gray-500 min-w-9 max-w-9 min-h-9 max-h-9 p-2 rounded-sm hover:bg-gray-500 hover:border-gray-400 focus-visible:outline-none focus-visible:border-gray-300 focus-visible:bg-gray-500">
|
<button class="bg-gray border border-solid border-gray-500 min-w-9 max-w-9 min-h-9 max-h-9 p-2 rounded-sm hover:bg-gray-500 hover:border-gray-400 focus-visible:outline-none focus-visible:border-gray-300 focus-visible:bg-gray-500">
|
||||||
<img src="/assets/icons/x-button-gray.svg" class="w-4 h-4 m-auto" alt="Male symbol" />
|
<img src="/assets/icons/x-button-gray.svg" class="w-4 h-4 m-auto" alt="Male symbol" />
|
||||||
</button>
|
</button>
|
||||||
<!-- TODO: replace with hairstyles -->
|
<!-- TODO #255: make radio button so we can set a value, do the same with swatches -->
|
||||||
<button v-for="n in 30" class="bg-gray border border-solid border-gray-500 min-w-9 max-w-9 min-h-9 max-h-9 p-2 rounded-sm hover:bg-gray-500 hover:border-gray-400 focus-visible:outline-none focus-visible:border-gray-300 focus-visible:bg-gray-500"></button>
|
<button
|
||||||
|
v-for="hair in hairs"
|
||||||
|
class="border border-solid border-gray-500 min-w-9 max-w-9 min-h-9 max-h-9 p-2 rounded-sm hover:border-gray-400 focus-visible:outline-none focus-visible:border-gray-300 focus-visible:bg-gray-500"
|
||||||
|
@click="selectedHair = hair"
|
||||||
|
:class="{
|
||||||
|
'bg-cyan': hair.id === selectedHair?.id,
|
||||||
|
'bg-gray-500 hover:bg-gray-500': hair.id !== selectedHair?.id
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<img class="w-4 h-4 m-auto" :src="config.server_endpoint + '/assets/sprites/' + hair.spriteId + '/front.png'" alt="Male symbol" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-3 w-full">
|
<div class="flex flex-col gap-3 w-full">
|
||||||
@ -103,7 +118,7 @@
|
|||||||
<form method="post" @submit.prevent="create" class="h-full flex flex-col justify-between">
|
<form method="post" @submit.prevent="create" class="h-full flex flex-col justify-between">
|
||||||
<div class="form-field-full">
|
<div class="form-field-full">
|
||||||
<label for="name" class="text-white">Nickname</label>
|
<label for="name" class="text-white">Nickname</label>
|
||||||
<input class="input-field" v-model="name" name="name" id="name" placeholder="Enter a nickname.." />
|
<input class="input-field" v-model="name" name="name" id="name" placeholder="Enter a nickname..." />
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-flow-col justify-stretch gap-4">
|
<div class="grid grid-flow-col justify-stretch gap-4">
|
||||||
<button type="button" class="btn-empty py-1.5 px-4 inline-block" @click.prevent="isModalOpen = false">Cancel</button>
|
<button type="button" class="btn-empty py-1.5 px-4 inline-block" @click.prevent="isModalOpen = false">Cancel</button>
|
||||||
@ -129,10 +144,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import config from '@/config'
|
||||||
import { useGameStore } from '@/stores/gameStore'
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
import { onBeforeUnmount, ref, watch } from 'vue'
|
||||||
import Modal from '@/components/utilities/Modal.vue'
|
import Modal from '@/components/utilities/Modal.vue'
|
||||||
import { type Character as CharacterT } from '@/types'
|
import { type Character as CharacterT, type CharacterHair } from '@/types'
|
||||||
import ConfirmationModal from '@/components/utilities/ConfirmationModal.vue'
|
import ConfirmationModal from '@/components/utilities/ConfirmationModal.vue'
|
||||||
|
|
||||||
const gameStore = useGameStore()
|
const gameStore = useGameStore()
|
||||||
@ -140,25 +156,39 @@ const isLoading = ref(true)
|
|||||||
const characters = ref([] as CharacterT[])
|
const characters = ref([] as CharacterT[])
|
||||||
const deletingCharacter = ref(null as CharacterT | null)
|
const deletingCharacter = ref(null as CharacterT | null)
|
||||||
|
|
||||||
|
const hairs = ref([] as CharacterHair[])
|
||||||
|
const selectedHair = ref(null as CharacterHair | null)
|
||||||
|
|
||||||
|
watch(selectedHair, (hair: CharacterHair | null) => {
|
||||||
|
console.log(hair)
|
||||||
|
})
|
||||||
|
|
||||||
// Fetch characters
|
// Fetch characters
|
||||||
gameStore.connection?.on('character:list', (data: any) => {
|
gameStore.connection?.on('character:list', (data: any) => {
|
||||||
characters.value = data
|
characters.value = data
|
||||||
|
isLoading.value = false
|
||||||
|
|
||||||
|
// Fetch hairs
|
||||||
|
// @TODO: This is hacky, we should have a better way to do this
|
||||||
|
gameStore.connection?.emit('character:hair:list', {}, (data: CharacterHair[]) => {
|
||||||
|
console.log(data)
|
||||||
|
hairs.value = data
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
// wait 0.75 sec
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
gameStore.connection?.emit('character:list')
|
gameStore.connection?.emit('character:list')
|
||||||
isLoading.value = false
|
|
||||||
}, 750)
|
}, 750)
|
||||||
})
|
|
||||||
|
|
||||||
// Select character logics
|
// Select character logics
|
||||||
const selected_character = ref(null)
|
const selected_character = ref(null)
|
||||||
function select_character() {
|
function select_character() {
|
||||||
if (!selected_character.value) return
|
if (!selected_character.value) return
|
||||||
deletingCharacter.value = null
|
deletingCharacter.value = null
|
||||||
gameStore.connection?.emit('character:connect', { characterId: selected_character.value })
|
gameStore.connection?.emit('character:connect', {
|
||||||
|
characterId: selected_character.value,
|
||||||
|
hairId: selectedHair.value?.id
|
||||||
|
})
|
||||||
gameStore.connection?.on('character:connect', (data: CharacterT) => gameStore.setCharacter(data))
|
gameStore.connection?.on('character:connect', (data: CharacterT) => gameStore.setCharacter(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ import { useGameStore } from '@/stores/gameStore'
|
|||||||
import Menu from '@/components/gui/Menu.vue'
|
import Menu from '@/components/gui/Menu.vue'
|
||||||
import ExpBar from '@/components/gui/ExpBar.vue'
|
import ExpBar from '@/components/gui/ExpBar.vue'
|
||||||
import Hud from '@/components/gui/Hud.vue'
|
import Hud from '@/components/gui/Hud.vue'
|
||||||
import Zone from '@/components/zone/Zone.vue'
|
import Zone from '@/components/game/zone/Zone.vue'
|
||||||
import Hotkeys from '@/components/gui/Hotkeys.vue'
|
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'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user