Renamed hair > characterHair, split character logic (chat bubble, healthbar etc) into separate components for better DX
This commit is contained in:
parent
89d83efca4
commit
f7b8c235d8
6
package-lock.json
generated
6
package-lock.json
generated
@ -3147,9 +3147,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001683",
|
"version": "1.0.30001684",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001683.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz",
|
||||||
"integrity": "sha512-iqmNnThZ0n70mNwvxpEC2nBJ037ZHZUoBI5Gorh1Mw6IlEAZujEoU1tXA628iZfzm7R9FvFzxbfdgml82a3k8Q==",
|
"integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -1,19 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- Chat bubble -->
|
<ChatBubble :zoneCharacter="props.zoneCharacter" :currentX="currentX" :currentY="currentY" />
|
||||||
<Container ref="charChatContainer" :depth="999" :x="currentX" :y="currentY">
|
<Healthbar :zoneCharacter="props.zoneCharacter" :currentX="currentX" :currentY="currentY" />
|
||||||
<RoundRectangle @create="createChatBubble" :origin-x="0.5" :origin-y="7.5" :fillColor="0xffffff" :width="194" :height="21" :radius="20" />
|
|
||||||
<Text @create="createChatText" :style="{ fontSize: 13, fontFamily: 'Arial', color: '#000' }" />
|
|
||||||
</Container>
|
|
||||||
<!-- Character name and health -->
|
|
||||||
<Container :depth="999" :x="currentX" :y="currentY">
|
|
||||||
<Text @create="createNicknameText" :text="props.zoneCharacter.character.name" />
|
|
||||||
<RoundRectangle :origin-x="0.5" :origin-y="18.5" :fillColor="0xffffff" :width="74" :height="6" :radius="5" />
|
|
||||||
<RoundRectangle :origin-x="0.5" :origin-y="36.4" :fillColor="0x00b3b3" :width="70" :height="3" :radius="5" />
|
|
||||||
</Container>
|
|
||||||
<!-- Character sprite -->
|
|
||||||
<Container ref="charContainer" :depth="isometricDepth" :x="currentX" :y="currentY">
|
<Container ref="charContainer" :depth="isometricDepth" :x="currentX" :y="currentY">
|
||||||
<!-- HAIR-->
|
<CharacterHair :zoneCharacter="props.zoneCharacter" :currentX="currentX" :currentY="currentY" />
|
||||||
<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>
|
||||||
@ -27,22 +16,24 @@ import { watch, computed, ref, onMounted, onUnmounted } from 'vue'
|
|||||||
import { Container, Image, 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'
|
||||||
|
import ChatBubble from '@/components/game/character/partials/ChatBubble.vue'
|
||||||
|
import Healthbar from '@/components/game/character/partials/Healthbar.vue'
|
||||||
|
import CharacterHair from '@/components/game/character/partials/CharacterHair.vue'
|
||||||
|
|
||||||
enum Direction {
|
enum Direction {
|
||||||
POSITIVE,
|
POSITIVE,
|
||||||
NEGATIVE,
|
NEGATIVE,
|
||||||
UNCHANGED
|
UNCHANGED
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
layer: Phaser.Tilemaps.TilemapLayer
|
layer: Phaser.Tilemaps.TilemapLayer
|
||||||
zoneCharacter: ZoneCharacter
|
zoneCharacter: ZoneCharacter
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const charChatContainer = refObj<Phaser.GameObjects.Container>()
|
|
||||||
const charContainer = refObj<Phaser.GameObjects.Container>()
|
const charContainer = refObj<Phaser.GameObjects.Container>()
|
||||||
const charSprite = refObj<Phaser.GameObjects.Sprite>()
|
const charSprite = refObj<Phaser.GameObjects.Sprite>()
|
||||||
|
|
||||||
const game = useGame()
|
|
||||||
const gameStore = useGameStore()
|
const gameStore = useGameStore()
|
||||||
const zoneStore = useZoneStore()
|
const zoneStore = useZoneStore()
|
||||||
const scene = useScene()
|
const scene = useScene()
|
||||||
@ -133,41 +124,6 @@ const updateSprite = () => {
|
|||||||
charSprite.value!.setTexture(charTexture.value)
|
charSprite.value!.setTexture(charTexture.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const createChatBubble = (container: Phaser.GameObjects.Container) => {
|
|
||||||
container.setName(`${props.zoneCharacter.character.name}_chatBubble`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const createChatText = (text: Phaser.GameObjects.Text) => {
|
|
||||||
text.setName(`${props.zoneCharacter.character.name}_chatText`)
|
|
||||||
text.setFontSize(13)
|
|
||||||
text.setFontFamily('Arial')
|
|
||||||
text.setOrigin(0.5, 10.9)
|
|
||||||
|
|
||||||
// Fix text alignment on Windows and Android
|
|
||||||
if (game.device.os.windows || game.device.os.android) {
|
|
||||||
text.setOrigin(0.5, 9.75)
|
|
||||||
|
|
||||||
if (game.device.browser.firefox) {
|
|
||||||
text.setOrigin(0.5, 10.9)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const createNicknameText = (text: Phaser.GameObjects.Text) => {
|
|
||||||
text.setFontSize(13)
|
|
||||||
text.setFontFamily('Arial')
|
|
||||||
text.setOrigin(0.5, 9)
|
|
||||||
|
|
||||||
// Fix text alignment on Windows and Android
|
|
||||||
if (game.device.os.windows || game.device.os.android) {
|
|
||||||
text.setOrigin(0.5, 8)
|
|
||||||
|
|
||||||
if (game.device.browser.firefox) {
|
|
||||||
text.setOrigin(0.5, 9)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.zoneCharacter.character,
|
() => props.zoneCharacter.character,
|
||||||
(newChar, oldChar) => {
|
(newChar, oldChar) => {
|
||||||
@ -182,14 +138,6 @@ 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)
|
||||||
@ -200,8 +148,6 @@ loadSpriteTextures(scene, props.zoneCharacter.character.characterType?.sprite as
|
|||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
charChatContainer.value!.setName(`${props.zoneCharacter.character!.name}_chatContainer`)
|
|
||||||
charChatContainer.value!.setVisible(false)
|
|
||||||
charContainer.value!.setName(props.zoneCharacter.character!.name)
|
charContainer.value!.setName(props.zoneCharacter.character!.name)
|
||||||
|
|
||||||
if (props.zoneCharacter.character.id === gameStore.character!.id) {
|
if (props.zoneCharacter.character.id === gameStore.character!.id) {
|
||||||
|
50
src/components/game/character/partials/CharacterHair.vue
Normal file
50
src/components/game/character/partials/CharacterHair.vue
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<Image v-bind="imageProps" v-if="gameStore.getLoadedAsset(props.zoneCharacter.character.characterHair?.spriteId)" ref="image" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { Image, refObj, useScene } from 'phavuer'
|
||||||
|
import type { Sprite as SpriteT, ZoneCharacter } from '@/types'
|
||||||
|
import { loadSpriteTextures } from '@/composables/gameComposable'
|
||||||
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
zoneCharacter: ZoneCharacter
|
||||||
|
currentX: number
|
||||||
|
currentY: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const gameStore = useGameStore()
|
||||||
|
const scene = useScene()
|
||||||
|
|
||||||
|
const image = refObj<Phaser.GameObjects.Image>()
|
||||||
|
const isFlippedX = computed(() => [6, 4].includes(props.zoneCharacter.character.rotation ?? 0))
|
||||||
|
|
||||||
|
const texture = computed(() => {
|
||||||
|
const { rotation, characterHair } = props.zoneCharacter.character
|
||||||
|
const spriteId = characterHair?.sprite?.id
|
||||||
|
const direction = [0, 6].includes(rotation) ? 'back' : 'front'
|
||||||
|
|
||||||
|
return `${spriteId}-${direction}`
|
||||||
|
})
|
||||||
|
|
||||||
|
const imageProps = computed(() => ({
|
||||||
|
depth: 1,
|
||||||
|
x: props.currentX,
|
||||||
|
y: props.currentY,
|
||||||
|
flipX: false,
|
||||||
|
texture: texture.value,
|
||||||
|
}))
|
||||||
|
|
||||||
|
loadSpriteTextures(scene, props.zoneCharacter.character.characterHair?.sprite as SpriteT)
|
||||||
|
.then(() => {
|
||||||
|
console.log(texture.value)
|
||||||
|
image.value!.setTexture(texture.value!)
|
||||||
|
image.value!.setFlipX(isFlippedX.value)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Error loading texture:', error)
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
@ -0,0 +1,46 @@
|
|||||||
|
<template>
|
||||||
|
<Container ref="charChatContainer" :depth="999" :x="currentX" :y="currentY">
|
||||||
|
<RoundRectangle @create="createChatBubble" :origin-x="0.5" :origin-y="7.5" :fillColor="0xffffff" :width="194" :height="21" :radius="20" />
|
||||||
|
<Text @create="createChatText" :style="{ fontSize: 13, fontFamily: 'Arial', color: '#000' }" />
|
||||||
|
</Container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Container, refObj, RoundRectangle, Text, useGame } from 'phavuer'
|
||||||
|
import type { ZoneCharacter } from '@/types'
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
zoneCharacter: ZoneCharacter
|
||||||
|
currentX: number
|
||||||
|
currentY: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const game = useGame()
|
||||||
|
const charChatContainer = refObj<Phaser.GameObjects.Container>()
|
||||||
|
|
||||||
|
const createChatBubble = (container: Phaser.GameObjects.Container) => {
|
||||||
|
container.setName(`${props.zoneCharacter.character.name}_chatBubble`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const createChatText = (text: Phaser.GameObjects.Text) => {
|
||||||
|
text.setName(`${props.zoneCharacter.character.name}_chatText`)
|
||||||
|
text.setFontSize(13)
|
||||||
|
text.setFontFamily('Arial')
|
||||||
|
text.setOrigin(0.5, 10.9)
|
||||||
|
|
||||||
|
// Fix text alignment on Windows and Android
|
||||||
|
if (game.device.os.windows || game.device.os.android) {
|
||||||
|
text.setOrigin(0.5, 9.75)
|
||||||
|
|
||||||
|
if (game.device.browser.firefox) {
|
||||||
|
text.setOrigin(0.5, 10.9)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
charChatContainer.value!.setName(`${props.zoneCharacter.character!.name}_chatContainer`)
|
||||||
|
charChatContainer.value!.setVisible(false)
|
||||||
|
})
|
||||||
|
</script>
|
35
src/components/game/character/partials/Healthbar.vue
Normal file
35
src/components/game/character/partials/Healthbar.vue
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<Container :depth="999" :x="currentX" :y="currentY">
|
||||||
|
<Text @create="createNicknameText" :text="props.zoneCharacter.character.name" />
|
||||||
|
<RoundRectangle :origin-x="0.5" :origin-y="18.5" :fillColor="0xffffff" :width="74" :height="6" :radius="5" />
|
||||||
|
<RoundRectangle :origin-x="0.5" :origin-y="36.4" :fillColor="0x00b3b3" :width="70" :height="3" :radius="5" />
|
||||||
|
</Container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Container, RoundRectangle, Text, useGame } from 'phavuer'
|
||||||
|
import type { ZoneCharacter } from '@/types'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
zoneCharacter: ZoneCharacter
|
||||||
|
currentX: number
|
||||||
|
currentY: number
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const game = useGame()
|
||||||
|
|
||||||
|
const createNicknameText = (text: Phaser.GameObjects.Text) => {
|
||||||
|
text.setFontSize(13)
|
||||||
|
text.setFontFamily('Arial')
|
||||||
|
text.setOrigin(0.5, 9)
|
||||||
|
|
||||||
|
// Fix text alignment on Windows and Android
|
||||||
|
if (game.device.os.windows || game.device.os.android) {
|
||||||
|
text.setOrigin(0.5, 8)
|
||||||
|
|
||||||
|
if (game.device.browser.firefox) {
|
||||||
|
text.setOrigin(0.5, 9)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -3,7 +3,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { Image, useScene } from 'phavuer'
|
import { Image, useScene } from 'phavuer'
|
||||||
import { calculateIsometricDepth, tileToWorldX, tileToWorldY } from '@/composables/zoneComposable'
|
import { calculateIsometricDepth, tileToWorldX, tileToWorldY } from '@/composables/zoneComposable'
|
||||||
import { loadTexture } from '@/composables/gameComposable'
|
import { loadTexture } from '@/composables/gameComposable'
|
||||||
|
@ -166,10 +166,6 @@ const deletingCharacter = ref(null as CharacterT | null)
|
|||||||
const hairs = ref([] as CharacterHair[])
|
const hairs = ref([] as CharacterHair[])
|
||||||
const selectedHair = ref(null as CharacterHair | null)
|
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
|
||||||
@ -194,7 +190,7 @@ function select_character() {
|
|||||||
deletingCharacter.value = null
|
deletingCharacter.value = null
|
||||||
gameStore.connection?.emit('character:connect', {
|
gameStore.connection?.emit('character:connect', {
|
||||||
characterId: selected_character.value,
|
characterId: selected_character.value,
|
||||||
hairId: selectedHair.value?.id
|
characterHairId: selectedHair.value?.id
|
||||||
})
|
})
|
||||||
gameStore.connection?.on('character:connect', (data: CharacterT) => gameStore.setCharacter(data))
|
gameStore.connection?.on('character:connect', (data: CharacterT) => gameStore.setCharacter(data))
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,8 @@ export async function loadTexture(scene: Phaser.Scene, assetData: AssetDataT): P
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function loadSpriteTextures(scene: Phaser.Scene, sprite: Sprite) {
|
export async function loadSpriteTextures(scene: Phaser.Scene, sprite: Sprite) {
|
||||||
|
if (!sprite) return
|
||||||
|
|
||||||
const sprite_actions = await fetch(config.server_endpoint + '/assets/list_sprite_actions/' + sprite?.id).then((response) => response.json())
|
const sprite_actions = await fetch(config.server_endpoint + '/assets/list_sprite_actions/' + sprite?.id).then((response) => response.json())
|
||||||
for await (const sprite_action of sprite_actions) {
|
for await (const sprite_action of sprite_actions) {
|
||||||
await loadTexture(scene, {
|
await loadTexture(scene, {
|
||||||
|
@ -38,7 +38,10 @@ export const useGameStore = defineStore('game', {
|
|||||||
return state.game.loadedAssets
|
return state.game.loadedAssets
|
||||||
},
|
},
|
||||||
getLoadedAsset: (state) => {
|
getLoadedAsset: (state) => {
|
||||||
return (key: string) => state.game.loadedAssets.find((asset) => asset.key === key)
|
return (key: string | undefined) => {
|
||||||
|
if (!key) return null
|
||||||
|
return state.game.loadedAssets.find((asset) => asset.key === key)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getLoadedAssetsByGroup: (state) => {
|
getLoadedAssetsByGroup: (state) => {
|
||||||
return (group: string) => state.game.loadedAssets.filter((asset) => asset.group === group)
|
return (group: string) => state.game.loadedAssets.filter((asset) => asset.group === group)
|
||||||
|
@ -169,12 +169,12 @@ export type Character = {
|
|||||||
positionX: number
|
positionX: number
|
||||||
positionY: number
|
positionY: number
|
||||||
rotation: number
|
rotation: number
|
||||||
zoneId: number
|
|
||||||
zone: Zone
|
|
||||||
characterTypeId: number | null
|
characterTypeId: number | null
|
||||||
characterType: CharacterType | null
|
characterType: CharacterType | null
|
||||||
hairId: number | null
|
characterHairId: number | null
|
||||||
hair: CharacterHair | null
|
characterHair: CharacterHair | null
|
||||||
|
zoneId: number
|
||||||
|
zone: Zone
|
||||||
chats: Chat[]
|
chats: Chat[]
|
||||||
items: CharacterItem[]
|
items: CharacterItem[]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user