WIP: Fix and improve interaction scenes
All checks were successful
Build / build (push) Successful in 1m14s
All checks were successful
Build / build (push) Successful in 1m14s
This commit is contained in:
parent
63516d0267
commit
dd5e018477
12 changed files with 132 additions and 66 deletions
|
@ -100,7 +100,7 @@ export class InteractionSceneState implements SceneState<InteractionSceneEvent>
|
|||
finishInteractionExecution(interactionId: string, onlyIfOngoing: boolean = true) {
|
||||
if (onlyIfOngoing && (this.ongoingInteractionExecution === null || interactionId !== getSuggestedInteractionId(this.ongoingInteractionExecution))) return
|
||||
this.ongoingInteractionExecution = null
|
||||
this.emit({ type: "interaction-execution-finished" })
|
||||
this.emit({ type: "interaction-execution-finished", interactionId })
|
||||
this.removeInteractionFromQueue(interactionId)
|
||||
|
||||
const interaction = this.definition.interactionsById.get(interactionId)
|
||||
|
@ -112,6 +112,10 @@ export class InteractionSceneState implements SceneState<InteractionSceneEvent>
|
|||
this.setObjectVisibility(interaction.objectId, false)
|
||||
}
|
||||
|
||||
interaction.outputObjectIds.forEach(objectId => {
|
||||
this.revealOrUpgradeObject(objectId, interaction.objectId === objectId && interaction.consume)
|
||||
})
|
||||
|
||||
break
|
||||
|
||||
case "combine":
|
||||
|
@ -119,26 +123,8 @@ export class InteractionSceneState implements SceneState<InteractionSceneEvent>
|
|||
if (consume) this.setObjectVisibility(id, false)
|
||||
})
|
||||
|
||||
interaction.outputObjectIds.forEach(id => {
|
||||
const object = this.definition.objectsById.get(id)!
|
||||
this.setObjectVisibility(id, true)
|
||||
|
||||
if (object.completion !== undefined) {
|
||||
let step = (this.objectCompletionStepById.get(id) ?? -1)
|
||||
if (interaction.inputObjects.get(id)?.consume === true) {
|
||||
step = 0
|
||||
} else {
|
||||
step = Math.min(step + 1, object.completion.steps)
|
||||
}
|
||||
|
||||
this.objectCompletionStepById.set(id, step)
|
||||
this.emit({ type: "object-completion-step-changed", objectId: id, step })
|
||||
|
||||
if (step === object.completion.steps) {
|
||||
this.objectVisibilityById.set(id, false)
|
||||
this.objectVisibilityById.set(object.completion.replaceWith, true)
|
||||
}
|
||||
}
|
||||
interaction.outputObjectIds.forEach(objectId => {
|
||||
this.revealOrUpgradeObject(objectId, interaction.inputObjects.get(objectId)?.consume === true)
|
||||
})
|
||||
|
||||
break
|
||||
|
@ -166,4 +152,26 @@ export class InteractionSceneState implements SceneState<InteractionSceneEvent>
|
|||
this.objectVisibilityById.set(objectId, isVisible)
|
||||
this.emit({ type: "object-visibility-changed", objectId: objectId, isVisible })
|
||||
}
|
||||
|
||||
revealOrUpgradeObject(objectId: string, resetCompletionSteps: boolean = false) {
|
||||
const object = this.definition.objectsById.get(objectId)!
|
||||
this.setObjectVisibility(objectId, true)
|
||||
|
||||
if (object.completion !== undefined) {
|
||||
let step = (this.objectCompletionStepById.get(objectId) ?? -1)
|
||||
if (resetCompletionSteps) {
|
||||
step = 0
|
||||
} else {
|
||||
step = Math.min(step + 1, object.completion.steps)
|
||||
}
|
||||
|
||||
this.objectCompletionStepById.set(objectId, step)
|
||||
this.emit({ type: "object-completion-step-changed", objectId, step })
|
||||
|
||||
if (step === object.completion.steps) {
|
||||
this.objectVisibilityById.set(objectId, false)
|
||||
this.objectVisibilityById.set(object.completion.replaceWith, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,33 +1,62 @@
|
|||
<template>
|
||||
<div class="bg-dark-600 rounded-lg flex flex-shrink-0 overflow-hidden">
|
||||
<div class="flex-grow flex items-center justify-center gap-2 px-3 py-4">
|
||||
<template v-if="interaction.type === 'use'">
|
||||
<HandPointingIcon class="text-4xl mb-6"/>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<ObjectPicture :object-id="interaction.objectId"/>
|
||||
<div class="text-sm text-gray-200 text-center">
|
||||
{{ definition.objectsById.get(interaction.objectId)!.label }}
|
||||
<div class="bg-dark-600 rounded-lg flex-shrink-0 flex flex-col">
|
||||
<div class="flex">
|
||||
<div class="flex-grow flex items-center justify-center gap-2 px-3 py-4 text-xl">
|
||||
<template v-if="interaction.type === 'use'">
|
||||
<HandPointingIcon class="mb-6"/>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<ObjectPicture :object-id="interaction.objectId"/>
|
||||
<div class="text-sm text-gray-200 text-center">
|
||||
{{ sceneDefinition.objectsById.get(interaction.objectId)!.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else v-for="(objectId, index) in interaction.objectIds" :key="objectId">
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<ObjectPicture :object-id="objectId"/>
|
||||
<div class="text-sm text-gray-200 text-center">
|
||||
{{ definition.objectsById.get(objectId)!.label }}
|
||||
</template>
|
||||
<template v-else v-for="(objectId, index) in interaction.objectIds" :key="objectId">
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<ObjectPicture :object-id="objectId"/>
|
||||
<div class="text-sm text-gray-200 text-center">
|
||||
{{ sceneDefinition.objectsById.get(objectId)!.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<PlusIcon v-if="index < interaction.objectIds.size - 1" class="text-3xl mb-6"/>
|
||||
</template>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between items-center bg-gray-800 w-17">
|
||||
<div class="flex flex-col justify-center items-center pt-3 pb-2">
|
||||
<PlusIcon v-if="index < interaction.objectIds.size - 1" class="mb-6"/>
|
||||
</template>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center items-center pt-3 pb-2 w-17 bg-gray-800">
|
||||
<div class="text-2xl">{{ votes }}</div>
|
||||
<div class="text-sm text-center">
|
||||
Vote{{votes === 1 ? "" : "s" }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<button
|
||||
class="w-50% pt-1"
|
||||
:class="isOngoing ? 'bg-amber-600' : 'bg-blue-700'"
|
||||
@click="isOngoing ? controller.cancelInteractionExecution(interactionId, true) : controller.startInteractionExecution(interaction)"
|
||||
>
|
||||
<StopIcon v-if="isOngoing"/>
|
||||
<PlayIcon v-else/>
|
||||
</button>
|
||||
<button v-if="definition !== null" class="w-50% pt-1 bg-green-700" @click="(event: MouseEvent) => controller.finishInteractionExecution(interactionId, !event.shiftKey)">
|
||||
<CheckIcon/>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="definition !== null" class="p-2 border-t border-t-solid border-gray-700 text-sm flex flex-col gap-2">
|
||||
<template v-if="(definition.type === 'use' || definition.type === 'combine')">
|
||||
<p v-if="outcomeObjectLabels.consumed.length > 0" class="text-gray-300 m-0">
|
||||
Versteckt {{ outcomeObjectLabels.consumed.join(", ") }}.
|
||||
</p>
|
||||
<p v-if="outcomeObjectLabels.revealed.length > 0" class="text-gray-300 m-0">
|
||||
Offenbart {{ outcomeObjectLabels.revealed.join(", ") }}.
|
||||
</p>
|
||||
<p v-if="outcomeObjectLabels.upgraded.length > 0" class="text-gray-300 m-0">
|
||||
Entwickelt {{ outcomeObjectLabels.upgraded.join(", ") }}.
|
||||
</p>
|
||||
</template>
|
||||
<p v-if="definition.note" class="m-0">
|
||||
{{ definition.note }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -37,11 +66,11 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import PlusIcon from "virtual:icons/ph/plus-bold"
|
||||
import TrashIcon from "virtual:icons/ph/trash-bold"
|
||||
import CheckIcon from "virtual:icons/ph/check-bold"
|
||||
import PlayIcon from "virtual:icons/ph/play-duotone"
|
||||
import StopIcon from "virtual:icons/ph/stop-duotone"
|
||||
import CheckIcon from "virtual:icons/ph/check"
|
||||
import HandPointingIcon from "virtual:icons/ph/hand-pointing-duotone"
|
||||
import ObjectPicture from "./ObjectPicture.vue"
|
||||
import { useGame } from "../../game"
|
||||
import type { SuggestedInteraction } from "../../../shared/mutations"
|
||||
import type { InteractionSceneController } from "./index"
|
||||
import { getSuggestedInteractionId } from "../../../shared/scene-types/interaction"
|
||||
|
@ -51,7 +80,32 @@
|
|||
const props = defineProps<{
|
||||
interaction: SuggestedInteraction
|
||||
controller: InteractionSceneController
|
||||
definition: InteractionSceneDefinition
|
||||
sceneDefinition: InteractionSceneDefinition
|
||||
votes: number
|
||||
}>()
|
||||
|
||||
const interactionId = computed(() => getSuggestedInteractionId(props.interaction))
|
||||
const isOngoing = computed(() => props.controller.ongoingInteractionExecutionId.value === interactionId.value)
|
||||
|
||||
const definition = computed(() => props.sceneDefinition.interactionsById.get(interactionId.value) ?? null)
|
||||
|
||||
const outcomeObjectLabels = computed(() => {
|
||||
const result = { revealed: [] as string[], upgraded: [] as string[], consumed: [] as string[] }
|
||||
if (definition.value === null) return result
|
||||
if (definition.value.type === "use") {
|
||||
if (definition.value.consume) result.consumed.push(props.sceneDefinition.objectsById.get(definition.value.objectId)!.label)
|
||||
} else {
|
||||
for (const [objectId, { consume }] of definition.value.inputObjects.entries()) {
|
||||
if (consume) result.consumed.push(props.sceneDefinition.objectsById.get(objectId)!.label)
|
||||
}
|
||||
}
|
||||
|
||||
for (const objectId of definition.value.outputObjectIds.values()) {
|
||||
const object = props.sceneDefinition.objectsById.get(objectId)!
|
||||
if (object.completion === undefined) result.revealed.push(object.label)
|
||||
else result.upgraded.push(object.label)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
</script>
|
|
@ -4,7 +4,7 @@
|
|||
{{ object.label }}
|
||||
</span>
|
||||
<div class="flex gap-3 w-full">
|
||||
<ObjectPicture class="w-10" :object-id="objectId"/>
|
||||
<ObjectPicture class="w-10 text-2xl" :object-id="objectId"/>
|
||||
<div class="flex flex-col gap-1">
|
||||
<button
|
||||
class="flex items-center gap-1 rounded-full border border-solid border-green-600 px-2 py-1 transition-colors text-xs"
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
:style="{ zIndex: 1000 - index }"
|
||||
:interaction="item.interaction"
|
||||
:controller="controller"
|
||||
:definition="definition"
|
||||
:sceneDefinition="definition"
|
||||
:votes="item.votes"
|
||||
/>
|
||||
</transition-group>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<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">
|
||||
<div class="flex-grow flex items-center justify-center gap-2 px-3 py-4 text-4xl">
|
||||
<template v-if="interaction.type === 'use'">
|
||||
<HandPointingIcon class="text-4xl mb-6"/>
|
||||
<HandPointingIcon class="mb-6"/>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<ObjectPicture :object-id="interaction.objectId"/>
|
||||
<div class="text-sm text-gray-200 text-center">
|
||||
|
@ -17,7 +17,7 @@
|
|||
{{ definition.objectsById.get(objectId)!.label }}
|
||||
</div>
|
||||
</div>
|
||||
<PlusIcon v-if="index < interaction.objectIds.size - 1" class="text-3xl mb-6"/>
|
||||
<PlusIcon v-if="index < interaction.objectIds.size - 1" class="mb-6"/>
|
||||
</template>
|
||||
</div>
|
||||
<div class="flex flex-col justify-between items-center bg-gray-800 w-17">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div
|
||||
class="flex flex-col items-center gap-2 bg-dark-600 rounded-lg p-3 border border-solid"
|
||||
class="flex flex-col items-center gap-3 bg-dark-600 rounded-lg p-3 border border-solid"
|
||||
:class="[$style.root, borderClass]"
|
||||
:style="{
|
||||
transform: dragPosition === null ? undefined : `translate(${dragPosition.x}px, ${dragPosition.y}px) scale(${isOverDropzone ? 0.5 : 1})`,
|
||||
|
@ -8,8 +8,8 @@
|
|||
}"
|
||||
:data-object-id="objectId"
|
||||
>
|
||||
<ObjectPicture :object-id="objectId"/>
|
||||
<div class="text-sm text-gray-200">
|
||||
<ObjectPicture class="text-4xl" :object-id="objectId"/>
|
||||
<div class="text-gray-200">
|
||||
{{ object.label }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<template>
|
||||
<img :src="`/objects/${objectId}.png`" alt="" class="object-contain max-w-15 pointer-events-none" draggable="false"/>
|
||||
<img :src="`/objects/${objectId}.png`" alt="" class="object-contain pointer-events-none" draggable="false" :class="$style.root"/>
|
||||
</template>
|
||||
|
||||
<style module lang="scss">
|
||||
|
||||
.root {
|
||||
max-width: calc(2.5em - 30px)
|
||||
}
|
||||
</style>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
|
@ -22,6 +22,7 @@ export const InteractionSceneType: SceneType<InteractionSceneDefinition, Interac
|
|||
const suggestedInteractionId = computed(() => suggestedInteraction.value === null ? null : getSuggestedInteractionId(suggestedInteraction.value))
|
||||
|
||||
const ongoingInteractionExecution = ref<SuggestedInteraction | null>(null)
|
||||
const ongoingInteractionExecutionId = computed(() => ongoingInteractionExecution.value === null ? null : getSuggestedInteractionId(ongoingInteractionExecution.value))
|
||||
|
||||
return {
|
||||
visibleObjectIds,
|
||||
|
@ -62,6 +63,7 @@ export const InteractionSceneType: SceneType<InteractionSceneDefinition, Interac
|
|||
suggestedInteraction,
|
||||
suggestedInteractionId,
|
||||
ongoingInteractionExecution,
|
||||
ongoingInteractionExecutionId,
|
||||
handleEvent(event: InteractionSceneEvent) {
|
||||
switch (event.type) {
|
||||
case "votes-changed":
|
||||
|
@ -110,10 +112,9 @@ export const InteractionSceneType: SceneType<InteractionSceneDefinition, Interac
|
|||
break
|
||||
|
||||
case "interaction-execution-finished":
|
||||
const interactionId = getSuggestedInteractionId(this.ongoingInteractionExecution.value!)
|
||||
ongoingInteractionExecution.value = null
|
||||
interactionQueue.delete(interactionId)
|
||||
if (suggestedInteractionId.value === interactionId) suggestedInteraction.value = null
|
||||
interactionQueue.delete(event.interactionId)
|
||||
if (suggestedInteractionId.value === event.interactionId) suggestedInteraction.value = null
|
||||
break
|
||||
}
|
||||
},
|
||||
|
@ -151,6 +152,7 @@ export interface InteractionSceneController extends SceneController {
|
|||
suggestedInteraction: Ref<SuggestedInteraction | null>
|
||||
suggestedInteractionId: Readonly<Ref<string | null>>
|
||||
ongoingInteractionExecution: Ref<SuggestedInteraction | null>
|
||||
ongoingInteractionExecutionId: Readonly<Ref<string | null>>
|
||||
|
||||
setObjectVisibility(objectId: string, isVisible: boolean): Promise<void>
|
||||
|
||||
|
|
|
@ -27,5 +27,5 @@ export type InteractionSceneEvent =
|
|||
| { type: "object-visibility-changed"; objectId: string; isVisible: boolean }
|
||||
| { type: "object-completion-step-changed"; objectId: string; step: number }
|
||||
| { type: "interaction-execution-started"; interaction: SuggestedInteraction }
|
||||
| { type: "interaction-execution-finished" }
|
||||
| { type: "interaction-execution-finished"; interactionId: string }
|
||||
| { type: "interaction-execution-cancelled" }
|
|
@ -31,7 +31,7 @@ export const sceneTutorialKettensaege: SceneDefinition = defineInteractionScene(
|
|||
type: "use",
|
||||
objectId: "kettensaege",
|
||||
consume: false,
|
||||
revealedObjectIds: []
|
||||
outputObjectIds: []
|
||||
}
|
||||
]
|
||||
})
|
|
@ -57,7 +57,7 @@ export const sceneTutorialMuesli: SceneDefinition = defineInteractionScene({
|
|||
type: "use",
|
||||
objectId: "kuehlschrank",
|
||||
consume: false,
|
||||
revealedObjectIds: ["milch", "thunfisch", "haferflocken", "kaffeebohnen"],
|
||||
outputObjectIds: ["milch", "thunfisch", "haferflocken", "kaffeebohnen"],
|
||||
},
|
||||
{
|
||||
type: "combine",
|
||||
|
@ -105,7 +105,7 @@ export const sceneTutorialMuesli: SceneDefinition = defineInteractionScene({
|
|||
type: "use",
|
||||
objectId: "milch",
|
||||
consume: false,
|
||||
revealedObjectIds: [],
|
||||
outputObjectIds: [],
|
||||
note: "Leider ist die Milch schon abgelaufen. → Duo: »Hätten wir nur H-Milch besorgt.«"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -28,7 +28,7 @@ export const defineInteractionScene = <ObjectsT extends Record<string, SceneObje
|
|||
type: "use",
|
||||
objectId: raw.objectId,
|
||||
consume: raw.consume,
|
||||
revealedObjectIds: cSet(...raw.revealedObjectIds),
|
||||
outputObjectIds: cSet(...raw.outputObjectIds),
|
||||
note: raw.note
|
||||
}
|
||||
break
|
||||
|
@ -66,7 +66,7 @@ interface RawUseInteractionDefinition<AvailableObjectIds extends string> {
|
|||
type: "use"
|
||||
objectId: AvailableObjectIds
|
||||
consume: boolean
|
||||
revealedObjectIds: Array<AvailableObjectIds>
|
||||
outputObjectIds: Array<AvailableObjectIds>
|
||||
note?: string
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ export interface UseInteractionDefinition<AvailableObjectIds extends string> {
|
|||
type: "use"
|
||||
objectId: AvailableObjectIds
|
||||
consume: boolean
|
||||
revealedObjectIds: Set<AvailableObjectIds>
|
||||
outputObjectIds: Set<AvailableObjectIds>
|
||||
note?: string
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue