Skip to content

Commit

Permalink
feat: character levels (#390)
Browse files Browse the repository at this point in the history
  • Loading branch information
hmbanan666 authored Jan 7, 2025
1 parent f1678b8 commit 37a5489
Show file tree
Hide file tree
Showing 14 changed files with 222 additions and 105 deletions.
3 changes: 3 additions & 0 deletions apps/telegram-game/auto-imports.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const Icon: typeof import('@iconify/vue')['Icon']
const _useCharacter: typeof import('./src/composables/useCharacter')['_useCharacter']
const addon: typeof import('./src/utils/gameClient')['addon']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
Expand Down Expand Up @@ -61,8 +62,10 @@ declare global {
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useApiFetch: typeof import('./src/composables/useTelegramProfile')['useApiFetch']
const useAttrs: typeof import('vue')['useAttrs']
const useBackButton: typeof import('./src/composables/useBackButton')['useBackButton']
const useCharacter: typeof import('./src/composables/useCharacter')['useCharacter']
const useCharacters: typeof import('./src/composables/useCharacters')['useCharacters']
const useConfetti: typeof import('./src/composables/useConfetti')['useConfetti']
const useCssModule: typeof import('vue')['useCssModule']
Expand Down
1 change: 1 addition & 0 deletions apps/telegram-game/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ declare module 'vue' {
ConfettiBackground: typeof import('./src/components/ConfettiBackground.vue')['default']
CouponActivationBlock: typeof import('./src/components/CouponActivationBlock.vue')['default']
Game: typeof import('./src/components/Game.vue')['default']
GameCharacterProgression: typeof import('./src/components/GameCharacterProgression.vue')['default']
GameNavigator: typeof import('./src/components/GameNavigator.vue')['default']
Image: typeof import('./src/components/Image.vue')['default']
Modal: typeof import('./src/components/Modal.vue')['default']
Expand Down
1 change: 1 addition & 0 deletions apps/telegram-game/src/components/Game.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

<div class="tg-content-safe-area-top touch-pan-x absolute top-0 left-0 right-0 w-full h-16">
<div class="max-w-[28rem] mx-auto px-4">
<GameCharacterProgression />
<GameNavigator :player-x="game.player?.x" :wagon-x="game.wagon?.x" />

<div v-if="profile && profile.energy >= 0" class="hidden w-fit h-10 px-5 py-0 flex-row items-center gap-2 bg-orange-100/80 text-amber-600 rounded-full">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<template>
{{ character?.level }} / {{ character?.xp }} / {{ character?.xpToNextLevel }}
</template>

<script setup lang="ts">
const { character } = useCharacter()
</script>
2 changes: 1 addition & 1 deletion apps/telegram-game/src/components/PageContainer.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="tg-secondary-bg tg-text tg-content-safe-area overflow-hidden z-30 h-full min-h-dvh w-full">
<div class="px-4 py-2 max-w-[28rem] mx-auto flex flex-col">
<div class="px-4 py-2 max-w-[28rem] mx-auto flex flex-col gap-y-6">
<slot />
</div>
</div>
Expand Down
17 changes: 17 additions & 0 deletions apps/telegram-game/src/composables/useCharacter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { CharacterEdition, CharacterLevel } from '@chat-game/types'
import { createSharedComposable } from '@vueuse/core'
import { useApiFetch } from './useTelegramProfile'

type CharacterEditionData = CharacterEdition & {
levels: CharacterLevel[]
nextLevel: CharacterLevel | null
xpToNextLevel: number | null
}

export function _useCharacter() {
const { data, execute: refreshCharacter } = useApiFetch('/character/active').get().json<CharacterEditionData>()

return { character: data, refreshCharacter }
}

export const useCharacter = createSharedComposable(_useCharacter)
2 changes: 1 addition & 1 deletion apps/telegram-game/src/composables/useTelegramProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type TelegramProfileWithProfile = TelegramProfile & {
}
}

const useApiFetch = createFetch({
export const useApiFetch = createFetch({
baseUrl: 'https://chatgame.space/api/telegram',
options: {
beforeFetch({ options }) {
Expand Down
6 changes: 3 additions & 3 deletions apps/telegram-game/src/views/InventoryView.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<PageContainer>
<div class="tg-section-bg mb-4 px-3 py-3 flex flex-row gap-2 items-center rounded-2xl">
<div class="tg-section-bg px-3 py-3 flex flex-row gap-2 items-center rounded-2xl">
<img :src="data?.photoUrl" alt="avatar" class="w-14 h-14 rounded-full">
<div>
<div class="text-xl font-medium">
Expand All @@ -22,13 +22,13 @@
</div>
</ActiveCard>
</div>
<div v-else class="tg-section-bg mb-4 p-3 flex flex-col gap-2 items-center rounded-2xl">
<div v-else class="tg-section-bg p-3 flex flex-col gap-2 items-center rounded-2xl">
<p class="font-medium tg-hint">
Нет предметов в инвентаре
</p>
</div>

<div v-if="isEmptyProfile" class="tg-section-bg mb-4 p-3 flex flex-col gap-2 items-center rounded-2xl">
<div v-if="isEmptyProfile" class="tg-section-bg p-3 flex flex-col gap-2 items-center rounded-2xl">
<div class="w-full space-y-3">
<div class="text-xl font-medium">
Есть профиль на ChatGame?
Expand Down
30 changes: 16 additions & 14 deletions apps/telegram-game/src/views/QuestView.vue
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
<template>
<PageContainer>
<SectionHeader text="Активные комнаты" />

<div class="flex flex-col gap-2">
<div v-for="room in rooms" :key="room.id" class="tg-section-bg mb-4 px-3 py-3 flex flex-col gap-2 items-center rounded-2xl">
<div class="space-y-3">
<div class="text-xl font-medium">
{{ room.name }}
</div>
<div class="tg-hint text-sm">
{{ room.description }}
<div>
<SectionHeader text="Активные комнаты" />

<div class="flex flex-col gap-2">
<div v-for="room in rooms" :key="room.id" class="tg-section-bg mb-4 px-3 py-3 flex flex-col gap-2 items-center rounded-2xl">
<div class="space-y-3">
<div class="text-xl font-medium">
{{ room.name }}
</div>
<div class="tg-hint text-sm">
{{ room.description }}
</div>

<Button @click="connectToRoom(room.roomId)">
Подключиться
</Button>
</div>

<Button @click="connectToRoom(room.roomId)">
Подключиться
</Button>
</div>
</div>
</div>
Expand Down
76 changes: 40 additions & 36 deletions apps/telegram-game/src/views/ShopView.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<PageContainer>
<div class="mb-4 grid grid-cols-2 gap-2">
<div class="grid grid-cols-2 gap-2">
<ActiveCard class="!px-3 !py-2 flex flex-row gap-3 items-center" @click="isCoinOpened = true">
<Image src="coin.png" class="w-12 h-12" />
<div>
Expand All @@ -26,47 +26,51 @@
</ActiveCard>
</div>

<SectionHeader text="Специальные предложения" />

<div class="mb-4 grid grid-cols-2 gap-2">
<ActiveCard v-for="product in products" :key="product.id" class="aspect-square" @click="selectProduct(product.id)">
<p class="font-medium text-lg leading-tight">
{{ product.title }}
</p>
<p v-if="product.bonusCoins" class="text-sm tg-hint">
+ бонусы
</p>
<div v-if="product.singlePurchase === true" class="mt-2 w-fit tg-button px-3 py-1 rounded-2xl border-0 font-medium text-sm">
Лимитированный
</div>
<div>
<SectionHeader text="Специальные предложения" />

<div class="grid grid-cols-2 gap-2">
<ActiveCard v-for="product in products" :key="product.id" class="aspect-square" @click="selectProduct(product.id)">
<p class="font-medium text-lg leading-tight">
{{ product.title }}
</p>
<p v-if="product.bonusCoins" class="text-sm tg-hint">
+ бонусы
</p>
<div v-if="product.singlePurchase === true" class="mt-2 w-fit tg-button px-3 py-1 rounded-2xl border-0 font-medium text-sm">
Лимитированный
</div>

<div :style="{ 'background-image': `url('/shop-assets/${product.id}/512.png')` }" class="absolute top-0 left-0 right-0 bottom-0 bg-bottom bg-no-repeat bg-cover" />
</ActiveCard>
<div :style="{ 'background-image': `url('/shop-assets/${product.id}/512.png')` }" class="absolute top-0 left-0 right-0 bottom-0 bg-bottom bg-no-repeat bg-cover" />
</ActiveCard>
</div>
</div>

<SectionHeader text="Коллекция персонажей 2024" />
<div>
<SectionHeader text="Коллекция персонажей 2024" />

<div class="grid grid-cols-2 gap-2">
<ActiveCard v-for="char in characters" :key="char.id" class="aspect-square" @click="selectCharacter(char.id)">
<div v-if="!char?.editions?.find(({ profileId }) => profileId === profile?.profile.id)" class="z-10 absolute top-0 left-0 right-0 bottom-0 tg-secondary-bg opacity-40" />
<div class="grid grid-cols-2 gap-2">
<ActiveCard v-for="char in characters" :key="char.id" class="aspect-square" @click="selectCharacter(char.id)">
<div v-if="!char?.editions?.find(({ profileId }) => profileId === profile?.profile.id)" class="z-10 absolute top-0 left-0 right-0 bottom-0 tg-secondary-bg opacity-40" />

<div v-if="profile?.profile?.activeEditionId && profile?.profile?.activeEditionId === char?.editions?.find(({ profileId }) => profileId === profile?.profile.id)?.id" class="tg-accent-text text-base font-medium leading-tight">
Активный
</div>
<p class="font-medium text-lg leading-tight">
{{ char?.nickname }}
</p>
<p v-if="char?.editions?.find(({ profileId }) => profileId === profile?.profile.id)" class="text-sm tg-hint">
{{ char?.editions?.find(({ profileId }) => profileId === profile?.profile.id)?.level }} уровень
</p>

<div v-if="char?.price && !char?.editions?.find(({ profileId }) => profileId === profile?.profile.id)" class="flex flex-row gap-1 items-center">
<Image src="coin-small.png" class="w-5 h-5 grayscale-100" />
<p>{{ char?.price }}</p>
</div>
<div v-if="profile?.profile?.activeEditionId && profile?.profile?.activeEditionId === char?.editions?.find(({ profileId }) => profileId === profile?.profile.id)?.id" class="tg-accent-text text-base font-medium leading-tight">
Активный
</div>
<p class="font-medium text-lg leading-tight">
{{ char?.nickname }}
</p>
<p v-if="char?.editions?.find(({ profileId }) => profileId === profile?.profile.id)" class="text-sm tg-hint">
{{ char?.editions?.find(({ profileId }) => profileId === profile?.profile.id)?.level }} уровень
</p>

<div v-if="char?.price && !char?.editions?.find(({ profileId }) => profileId === profile?.profile.id)" class="flex flex-row gap-1 items-center">
<Image src="coin-small.png" class="w-5 h-5 grayscale-100" />
<p>{{ char?.price }}</p>
</div>

<Image :src="`units/${char?.codename}/128.png`" class="absolute bottom-0 right-0 w-32 h-auto" :class="{ 'grayscale-100 opacity-70': !char?.editions?.find(({ profileId }) => profileId === profile?.profile.id) }" />
</ActiveCard>
<Image :src="`units/${char?.codename}/128.png`" class="absolute bottom-0 right-0 w-32 h-auto" :class="{ 'grayscale-100 opacity-70': !char?.editions?.find(({ profileId }) => profileId === profile?.profile.id) }" />
</ActiveCard>
</div>
</div>
</PageContainer>

Expand Down
104 changes: 54 additions & 50 deletions apps/telegram-game/src/views/TopView.vue
Original file line number Diff line number Diff line change
@@ -1,69 +1,73 @@
<template>
<PageContainer>
<SectionHeader text="Активное событие" />
<div>
<SectionHeader text="Активное событие" />

<div class="tg-section-bg mb-1 px-3 py-3 rounded-2xl">
<div class="flex flex-row gap-2 items-center">
<Image src="units/santa/head.png" class="w-12 h-12" />
<div>
<h3 class="text-xl font-medium">
{{ leaderboard?.title }}
</h3>
<div v-if="leaderboard?.finishedAt">
Окончание {{ new Date(leaderboard.finishedAt).toLocaleString('ru-RU', { day: 'numeric', month: 'long', year: 'numeric' }) }}
<div class="tg-section-bg mb-1 px-3 py-3 rounded-2xl">
<div class="flex flex-row gap-2 items-center">
<Image src="units/santa/head.png" class="w-12 h-12" />
<div>
<h3 class="text-xl font-medium">
{{ leaderboard?.title }}
</h3>
<div v-if="leaderboard?.finishedAt">
Окончание {{ new Date(leaderboard.finishedAt).toLocaleString('ru-RU', { day: 'numeric', month: 'long', year: 'numeric' }) }}
</div>
</div>
</div>
</div>

<p class="mt-1 tg-hint text-sm leading-tight">
{{ leaderboard?.description }}
</p>
<p class="mt-1 tg-hint text-sm leading-tight">
{{ leaderboard?.description }}
</p>

<div v-if="profileInLeaderboard" class="mt-3 flex flex-row gap-2 justify-between">
<div class="flex flex-row gap-3 items-center">
<p class="font-medium text-lg">
{{ profileInLeaderboard.position }}
</p>
<p class="font-medium text-lg">
Мой результат
</p>
</div>
<div class="flex flex-row gap-1 items-center text-lg">
{{ profileInLeaderboard.points }} <Image src="items/k3bitdush5wqbwphhdfnxqtl/128.png" class="w-6 h-6" />
<div v-if="profileInLeaderboard" class="mt-3 flex flex-row gap-2 justify-between">
<div class="flex flex-row gap-3 items-center">
<p class="font-medium text-lg">
{{ profileInLeaderboard.position }}
</p>
<p class="font-medium text-lg">
Мой результат
</p>
</div>
<div class="flex flex-row gap-1 items-center text-lg">
{{ profileInLeaderboard.points }} <Image src="items/k3bitdush5wqbwphhdfnxqtl/128.png" class="w-6 h-6" />
</div>
</div>
</div>
</div>

<div class="mb-4 flex flex-col gap-1">
<div v-for="member in leaderboard?.members" :key="member.id" class="px-3 py-2 tg-section-bg rounded-2xl flex flex-row gap-2 justify-between">
<div class="flex flex-row gap-3 items-center">
<p class="font-medium text-lg">
{{ member.position }}
</p>
<p class="font-medium text-lg">
{{ member.profile.telegramProfile?.firstName }}
</p>
</div>
<div class="flex flex-row gap-1 items-center text-lg">
{{ member.points }} <Image src="items/k3bitdush5wqbwphhdfnxqtl/128.png" class="w-6 h-6" />
<div class="flex flex-col gap-1">
<div v-for="member in leaderboard?.members" :key="member.id" class="px-3 py-2 tg-section-bg rounded-2xl flex flex-row gap-2 justify-between">
<div class="flex flex-row gap-3 items-center">
<p class="font-medium text-lg">
{{ member.position }}
</p>
<p class="font-medium text-lg">
{{ member.profile.telegramProfile?.firstName }}
</p>
</div>
<div class="flex flex-row gap-1 items-center text-lg">
{{ member.points }} <Image src="items/k3bitdush5wqbwphhdfnxqtl/128.png" class="w-6 h-6" />
</div>
</div>
</div>
</div>

<SectionHeader text="Мои трофеи" />
<div>
<SectionHeader text="Мои трофеи" />

<div v-if="trophies.length" class="grid grid-cols-3 gap-2">
<ActiveCard v-for="edition in trophies" :key="edition.id" class="flex flex-col gap-2 items-center" @click="selectTrophy(edition.id)">
<Image :src="getTrophyImage(edition.trophy)" class="w-full h-auto" />
<p class="text-center text-sm font-medium leading-3 line-clamp-2">
{{ edition.trophy.name }}
<div v-if="trophies.length" class="grid grid-cols-3 gap-2">
<ActiveCard v-for="edition in trophies" :key="edition.id" class="flex flex-col gap-2 items-center" @click="selectTrophy(edition.id)">
<Image :src="getTrophyImage(edition.trophy)" class="w-full h-auto" />
<p class="text-center text-sm font-medium leading-3 line-clamp-2">
{{ edition.trophy.name }}
</p>
</ActiveCard>
</div>
<div v-else class="tg-section-bg p-3 flex flex-col gap-2 items-center rounded-2xl">
<p class="font-medium tg-hint">
Нет полученных трофеев
</p>
</ActiveCard>
</div>
<div v-else class="tg-section-bg mb-4 p-3 flex flex-col gap-2 items-center rounded-2xl">
<p class="font-medium tg-hint">
Нет полученных трофеев
</p>
</div>
</div>
</PageContainer>

Expand Down
Loading

0 comments on commit 37a5489

Please sign in to comment.