commit 03

This commit is contained in:
Moritz Ruth 2024-12-26 20:50:05 +01:00
parent 6b9b020c5d
commit 9a71f52528
Signed by: moritzruth
GPG key ID: C9BBAB79405EE56D
7 changed files with 158 additions and 13 deletions

View file

@ -2,12 +2,31 @@
<div class="bg-gray-900 h-100vh w-100vw overflow-hidden text-white"> <div class="bg-gray-900 h-100vh w-100vw overflow-hidden text-white">
<div :class="$style.noise"/> <div :class="$style.noise"/>
<div :class="$style.vignette"/> <div :class="$style.vignette"/>
<div class="relative h-full">
<div v-if="isLoading" class="flex flex-col justify-center items-center text-lg inset-0 absolute"> <div v-if="isLoading" class="flex flex-col justify-center items-center text-lg inset-0 absolute">
<span class="text-center">Verbindung wird hergestellt</span> <span class="text-center">Verbindung wird hergestellt</span>
</div> </div>
<main> <div class="h-full relative flex flex-col">
<InteractionsScreen/> <nav class="mx-auto py-4">
<div class="flex items-center rounded-lg bg-dark-400 overflow-hidden">
<button
class="px-4 py-2 bg-gray-700"
:class="activeTab !== 'interactions' && 'bg-opacity-0'"
@click="activeTab = 'interactions'"
>
Interagieren
</button>
<button
class="px-4 py-2 bg-gray-700"
:class="activeTab !== 'queue' && 'bg-opacity-0'"
@click="activeTab = 'queue'"
>
Abstimmen
</button>
</div>
</nav>
<main class="flex-grow">
<InteractionsScreen v-if="activeTab === 'interactions'"/>
<QueueScreen v-else-if="activeTab === 'queue'"/>
</main> </main>
</div> </div>
</div> </div>
@ -74,8 +93,10 @@
import { trpcClient } from "./trpc" import { trpcClient } from "./trpc"
import { useGame } from "./game" import { useGame } from "./game"
import InteractionsScreen from "./screens/InteractionsScreen.vue" import InteractionsScreen from "./screens/InteractionsScreen.vue"
import QueueScreen from "./screens/QueueScreen.vue"
const isLoading = ref(true) const isLoading = ref(true)
const activeTab = ref<"interactions" | "queue">("interactions")
const game = useGame() const game = useGame()
trpcClient.join.subscribe(undefined, { trpcClient.join.subscribe(undefined, {

View file

@ -0,0 +1,65 @@
<template>
<div class="bg-dark-600 rounded-lg flex overflow-hidden">
<div class="flex-grow flex items-center justify-center gap-2 px-3 py-4">
<template v-if="item.interaction.type === 'use'">
<HandPointingIcon class="text-4xl mb-6"/>
<div class="flex flex-col items-center gap-2">
<ObjectPicture :object-id="item.interaction.objectId"/>
<div class="text-sm text-gray-300 text-center">
{{ game.allObjectsById.get(item.interaction.objectId)!.label }}
</div>
</div>
</template>
<template v-else v-for="(objectId, index) in item.interaction.objectIds" :key="objectId">
<div class="flex flex-col items-center gap-2">
<ObjectPicture :object-id="objectId"/>
<div class="text-sm text-gray-300 text-center">
{{ game.allObjectsById.get(objectId)!.label }}
</div>
</div>
<PlusIcon v-if="index < item.interaction.objectIds.size - 1" class="text-3xl mb-6"/>
</template>
</div>
<div class="flex flex-col gap-2 justify-between items-center bg-gray-800 w-17">
<div class="flex flex-col justify-center items-center py-3">
<div class="text-2xl">{{ item.votes }}</div>
<div class="text-sm text-center">
Vote{{item.votes === 1 ? "" : "s" }}
</div>
</div>
<button
class="align-end flex-grow py-2 w-full"
:class="game.currentInteractionId === item.id ? 'bg-blue-500' : 'bg-gray-700'"
@click="toggleVote()"
>
+1
</button>
</div>
</div>
</template>
<style module lang="scss">
</style>
<script setup lang="ts">
import PlusIcon from "virtual:icons/ph/plus-bold"
import HandPointingIcon from "virtual:icons/ph/hand-pointing-duotone"
import type { InteractionQueueItem } from "../shared/script/types"
import ObjectPicture from "./ObjectPicture.vue"
import { useGame } from "../game"
const props = defineProps<{
item: InteractionQueueItem
}>()
const game = useGame()
function toggleVote() {
if (game.currentInteractionId === props.item.id) {
game.revokeCurrentInteractionVote()
} else {
game.voteForInteraction(props.item.interaction)
}
}
</script>

View file

@ -8,7 +8,7 @@
}" }"
:data-object-id="object.id" :data-object-id="object.id"
> >
<img :src="`/objects/${props.object.id}.png`" :alt="object.label" class="invert filter object-contain max-w-15"/> <ObjectPicture :object-id="object.id"/>
<div class="text-sm text-gray-300"> <div class="text-sm text-gray-300">
{{ object.label }} {{ object.label }}
</div> </div>
@ -41,6 +41,7 @@
import interact from "@interactjs/interact" import interact from "@interactjs/interact"
import { useCurrentElement } from "@vueuse/core" import { useCurrentElement } from "@vueuse/core"
import { computed, onMounted, onUnmounted, ref, useCssModule } from "vue" import { computed, onMounted, onUnmounted, ref, useCssModule } from "vue"
import ObjectPicture from "./ObjectPicture.vue"
const props = defineProps<{ const props = defineProps<{
object: GameObject object: GameObject

View file

@ -0,0 +1,13 @@
<template>
<img :src="`/objects/${objectId}.png`" alt="" class="invert filter object-contain max-w-15"/>
</template>
<style module lang="scss">
</style>
<script setup lang="ts">
const props = defineProps<{
objectId: string
}>()
</script>

View file

@ -1,7 +1,7 @@
import { defineStore } from "pinia" import { defineStore } from "pinia"
import { computed, reactive, ref } from "vue" import { computed, reactive, ref } from "vue"
import { script } from "./shared/script" import { script } from "./shared/script"
import type { Interaction, InteractionQueueItem } from "./shared/script/types" import type { GameObject, Interaction, InteractionQueueItem } from "./shared/script/types"
import { trpcClient } from "./trpc" import { trpcClient } from "./trpc"
import type { GameEvent } from "./shared/gameEvents" import type { GameEvent } from "./shared/gameEvents"
import { getInteractionQueueItemId } from "./shared/util" import { getInteractionQueueItemId } from "./shared/util"
@ -13,10 +13,20 @@ export const useGame = defineStore("gameState", () => {
const currentInteractionId = computed(() => const currentInteractionId = computed(() =>
currentInteraction.value === null ? null : getInteractionQueueItemId(currentInteraction.value)) currentInteraction.value === null ? null : getInteractionQueueItemId(currentInteraction.value))
const sortedInteractionQueue = computed(() => [...interactionQueue.values()].sort((a, b) => b.votes - a.votes))
const currentRoom = computed(() => script.roomsById.get(currentRoomId.value)!)
return { return {
currentRoomId, currentRoomId,
interactionQueue, interactionQueue,
currentRoom: computed(() => script.roomsById.get(currentRoomId.value)!), sortedInteractionQueue,
currentRoom,
allObjectsById: computed(() => {
const map = new Map<string, GameObject>()
currentRoom.value.initialObjects.union(currentRoom.value.hiddenObjects).forEach(o => map.set(o.id, o))
return map
}),
currentInteraction, currentInteraction,
currentInteractionId, currentInteractionId,
voteForInteraction(interaction: Interaction) { voteForInteraction(interaction: Interaction) {

View file

@ -1,22 +1,22 @@
<template> <template>
<div class="h-100vh flex flex-col relative"> <div class="h-full flex flex-col relative">
<div class="flex-shrink-0 flex order-1"> <div class="flex-shrink-0 flex order-1">
<ObjectCardDropZone <ObjectCardDropZone
label="Use" label="Benutzen"
class="bg-green-800" class="bg-green-800"
:has-floating="useFloatingObjectIds.size > 0" :has-floating="useFloatingObjectIds.size > 0"
@object-change="(v, i) => createSetInclusionSetter(useFloatingObjectIds)(v, i)" @object-change="(v, i) => createSetInclusionSetter(useFloatingObjectIds)(v, i)"
@object-drop="onObjectUseDrop" @object-drop="onObjectUseDrop"
/> />
<ObjectCardDropZone <ObjectCardDropZone
label="Combine" label="Kombinieren"
class="bg-blue-900" class="bg-blue-900"
:has-floating="combineFloatingObjectIds.size > 0" :has-floating="combineFloatingObjectIds.size > 0"
@object-change="(v, i) => createSetInclusionSetter(combineFloatingObjectIds)(v, i)" @object-change="(v, i) => createSetInclusionSetter(combineFloatingObjectIds)(v, i)"
@object-drop="onObjectInteractionDrop" @object-drop="onObjectInteractionDrop"
/> />
</div> </div>
<div ref="objectsContainerElement" class="grid gap-3 grid-cols-2 flex-grow auto-rows-min p-4"> <div ref="objectsContainerElement" class="grid gap-3 grid-cols-2 flex-grow auto-rows-min p-4 pt-0">
<ObjectCard <ObjectCard
v-for="object in game.currentRoom.initialObjects" v-for="object in game.currentRoom.initialObjects"
:key="object.id" :key="object.id"

View file

@ -0,0 +1,35 @@
<template>
<transition-group tag="div" name="list" class="h-full flex flex-col gap-4 p-4 pt-0 relative">
<InteractionQueueItemCard
v-for="item in game.sortedInteractionQueue"
:key="item.id"
:item="item"
/>
</transition-group>
</template>
<style scoped lang="scss">
.list-move,
.list-enter-active,
.list-leave-active {
transition: 400ms ease;
transition-property: opacity, transform;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(-30px);
}
.list-leave-active {
position: absolute;
}
</style>
<script setup lang="ts">
import InteractionQueueItemCard from "../components/InteractionQueueItemCard.vue"
import { useGame } from "../game"
const game = useGame()
</script>