WIP: Fix and improve interaction scenes
All checks were successful
Build / build (push) Successful in 1m8s
All checks were successful
Build / build (push) Successful in 1m8s
This commit is contained in:
parent
97e70432f0
commit
63516d0267
16 changed files with 260 additions and 66 deletions
|
@ -21,6 +21,7 @@ const stop = () => {
|
|||
console.log("Received stop signal")
|
||||
server.closeAllConnections()
|
||||
server.close()
|
||||
server.unref()
|
||||
}
|
||||
|
||||
process.on("SIGTERM", stop)
|
||||
|
|
|
@ -17,6 +17,7 @@ export class InteractionSceneState implements SceneState<InteractionSceneEvent>
|
|||
private suggestedInteractionIdBySessionId: Map<SessionId, string> = new Map()
|
||||
private ongoingInteractionExecution: SuggestedInteraction | null = null
|
||||
private objectVisibilityById = new Map<string, boolean>()
|
||||
private objectCompletionStepById = new Map<string, number>()
|
||||
|
||||
constructor(private game: Game, private definition: InteractionSceneDefinition) {
|
||||
definition.objectsById.entries().forEach(([id, o]) => this.setObjectVisibility(id, o.reveal))
|
||||
|
@ -27,6 +28,7 @@ export class InteractionSceneState implements SceneState<InteractionSceneEvent>
|
|||
events.push(this.getVotesChangedEvent())
|
||||
|
||||
this.objectVisibilityById.entries().forEach(([id, isVisible]) => events.push({ type: "object-visibility-changed", objectId: id, isVisible }))
|
||||
this.objectCompletionStepById.entries().forEach(([id, step]) => events.push({ type: "object-completion-step-changed", objectId: id, step }))
|
||||
|
||||
if (this.ongoingInteractionExecution !== null) events.push({ type: "interaction-execution-started", interaction: this.ongoingInteractionExecution })
|
||||
|
||||
|
@ -95,19 +97,19 @@ export class InteractionSceneState implements SceneState<InteractionSceneEvent>
|
|||
this.emit({ type: "interaction-execution-started", interaction })
|
||||
}
|
||||
|
||||
finishInteractionExecution(id: string, onlyIfOngoing: boolean = true) {
|
||||
if (onlyIfOngoing && (this.ongoingInteractionExecution === null || id !== getSuggestedInteractionId(this.ongoingInteractionExecution))) return
|
||||
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.removeInteractionFromQueue(id)
|
||||
this.removeInteractionFromQueue(interactionId)
|
||||
|
||||
const interaction = this.definition.interactionsById.get(id)
|
||||
const interaction = this.definition.interactionsById.get(interactionId)
|
||||
if (interaction === undefined) return
|
||||
|
||||
switch (interaction.type) {
|
||||
case "use":
|
||||
if (interaction.consume) {
|
||||
this.emit({ type: "object-visibility-changed", objectId: interaction.objectId, isVisible: false })
|
||||
this.setObjectVisibility(interaction.objectId, false)
|
||||
}
|
||||
|
||||
break
|
||||
|
@ -117,14 +119,34 @@ export class InteractionSceneState implements SceneState<InteractionSceneEvent>
|
|||
if (consume) this.setObjectVisibility(id, false)
|
||||
})
|
||||
|
||||
interaction.outputObjectIds.forEach(id => this.setObjectVisibility(id, true))
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
cancelInteractionExecution(id: string, onlyIfOngoing: boolean = true) {
|
||||
if (onlyIfOngoing && (this.ongoingInteractionExecution === null || id !== getSuggestedInteractionId(this.ongoingInteractionExecution))) return
|
||||
cancelInteractionExecution(interactionId: string, onlyIfOngoing: boolean = true) {
|
||||
if (onlyIfOngoing && (this.ongoingInteractionExecution === null || interactionId !== getSuggestedInteractionId(this.ongoingInteractionExecution))) return
|
||||
this.ongoingInteractionExecution = null
|
||||
this.emit({ type: "interaction-execution-cancelled" })
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { Component } from "vue"
|
|||
export interface SceneType<DefinitionT extends SceneDefinitionBase, ControllerT extends SceneController> {
|
||||
id: DefinitionT["type"]
|
||||
playerView: Component<{ controller: ControllerT, definition: DefinitionT }>,
|
||||
duoView: Component<{ controller: ControllerT, definition: DefinitionT }>
|
||||
crewView: Component<{ controller: ControllerT, definition: DefinitionT }>
|
||||
createController(definition: DefinitionT): ControllerT
|
||||
}
|
||||
|
||||
|
|
|
@ -2,12 +2,12 @@ import type { SceneController, SceneType } from "../base"
|
|||
import type { ChoiceSceneDefinition } from "../../../shared/script/types"
|
||||
import PlayerView from "./PlayerView.vue"
|
||||
import type { ChoiceSceneEvent } from "../../../shared/scene-types/choice"
|
||||
import DuoView from "./DuoView.vue"
|
||||
import CrewView from "./CrewView.vue"
|
||||
|
||||
export const ChoiceSceneType: SceneType<ChoiceSceneDefinition, ChoiceSceneController> = {
|
||||
id: "choice",
|
||||
playerView: PlayerView,
|
||||
duoView: DuoView,
|
||||
crewView: CrewView,
|
||||
createController(definition: ChoiceSceneDefinition): ChoiceSceneController {
|
||||
return {
|
||||
handleEvent(event: ChoiceSceneEvent) {
|
||||
|
|
57
frontend/scene-types/interaction/CrewInteractionCard.vue
Normal file
57
frontend/scene-types/interaction/CrewInteractionCard.vue
Normal file
|
@ -0,0 +1,57 @@
|
|||
<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>
|
||||
</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 }}
|
||||
</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">
|
||||
<div class="text-2xl">{{ votes }}</div>
|
||||
<div class="text-sm text-center">
|
||||
Vote{{votes === 1 ? "" : "s" }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style module lang="scss">
|
||||
|
||||
</style>
|
||||
|
||||
<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 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"
|
||||
import type { InteractionSceneDefinition } from "../../../shared/script/types"
|
||||
import { computed } from "vue"
|
||||
|
||||
const props = defineProps<{
|
||||
interaction: SuggestedInteraction
|
||||
controller: InteractionSceneController
|
||||
definition: InteractionSceneDefinition
|
||||
votes: number
|
||||
}>()
|
||||
</script>
|
42
frontend/scene-types/interaction/CrewObjectCard.vue
Normal file
42
frontend/scene-types/interaction/CrewObjectCard.vue
Normal file
|
@ -0,0 +1,42 @@
|
|||
<template>
|
||||
<div class="flex flex-col items-center gap-2 bg-dark-600 rounded-lg px-4 py-3">
|
||||
<span class="text-sm text-gray-200 text-center font-bold">
|
||||
{{ object.label }}
|
||||
</span>
|
||||
<div class="flex gap-3 w-full">
|
||||
<ObjectPicture class="w-10" :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"
|
||||
:class="isVisible ? 'bg-green-800 text-gray-100' : 'bg-transparent text-gray-300'"
|
||||
@click="emit('set-visibility', !isVisible)"
|
||||
>
|
||||
<EyeIcon v-if="isVisible"/>
|
||||
<EyeClosedIcon v-else/>
|
||||
<span>{{ isVisible ? "Sichtbar" : "Versteckt" }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style module lang="scss">
|
||||
|
||||
</style>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ObjectPicture from "./ObjectPicture.vue"
|
||||
import EyeIcon from "virtual:icons/ph/eye-duotone"
|
||||
import EyeClosedIcon from "virtual:icons/ph/eye-closed-duotone"
|
||||
import type { SceneObjectDefinition } from "../../../shared/script/types"
|
||||
|
||||
const props = defineProps<{
|
||||
objectId: string
|
||||
object: SceneObjectDefinition
|
||||
isVisible: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
"set-visibility": [boolean]
|
||||
}>()
|
||||
</script>
|
|
@ -1,33 +1,32 @@
|
|||
<template>
|
||||
<section class="flex-grow">
|
||||
<div class="font-bold text-2xl pb-2">Versteckte Objekte</div>
|
||||
<div class="grid gap-3 grid-cols-2 flex-grow auto-rows-min p-4 pt-0 relative">
|
||||
<button
|
||||
v-for="[objectId, object] in definition.objectsById.entries().filter(([id, _o]) => !controller.visibleObjectIds.has(id))"
|
||||
<div class="font-bold text-2xl pb-2">Objekte</div>
|
||||
<div class="grid gap-3 grid-cols-3 flex-grow auto-rows-min p-4 pt-0 relative">
|
||||
<CrewObjectCard
|
||||
v-for="[objectId, object] in definition.objectsById.entries()"
|
||||
:key="objectId"
|
||||
class="flex flex-col items-center gap-2 bg-dark-600 rounded-lg p-3"
|
||||
@click="controller.setObjectVisibility(objectId, true)"
|
||||
>
|
||||
<ObjectPicture :object-id="objectId"/>
|
||||
<span class="text-sm text-gray-200 text-center">
|
||||
{{ object.label }}
|
||||
</span>
|
||||
</button>
|
||||
:object-id="objectId"
|
||||
:object="object"
|
||||
:is-visible="controller.visibleObjectIds.has(objectId)"
|
||||
@set-visibility="isVisible => controller.setObjectVisibility(objectId, isVisible)"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section class="w-80">
|
||||
<div class="font-bold text-2xl pb-2">Interaktionen</div>
|
||||
<transition name="fade" mode="out-in">
|
||||
<div v-if="controller.sortedInteractionQueue.value.length === 0" class="text-xl text-gray-200">
|
||||
<div v-if="controller.sortedInteractionQueueWithDefined.value.length === 0" class="text-xl text-gray-200">
|
||||
Keine Interaktionen vorhanden.
|
||||
</div>
|
||||
<transition-group v-else tag="div" name="list" class="flex flex-col gap-4 relative h-80vh">
|
||||
<InteractionQueueItemCard
|
||||
v-for="(item, index) in controller.sortedInteractionQueue.value"
|
||||
<transition-group v-else tag="div" name="list" class="flex flex-col gap-4 relative h-90vh overflow-y-auto">
|
||||
<CrewInteractionCard
|
||||
v-for="(item, index) in controller.sortedInteractionQueueWithDefined.value"
|
||||
:key="item.id"
|
||||
:style="{ zIndex: 1000 - index }"
|
||||
:item="item"
|
||||
mode="director"
|
||||
:interaction="item.interaction"
|
||||
:controller="controller"
|
||||
:definition="definition"
|
||||
:votes="item.votes"
|
||||
/>
|
||||
</transition-group>
|
||||
</transition>
|
||||
|
@ -39,11 +38,11 @@
|
|||
</style>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ObjectPicture from "./ObjectPicture.vue"
|
||||
import InteractionQueueItemCard from "./InteractionQueueItemCard.vue"
|
||||
import CrewInteractionCard from "./CrewInteractionCard.vue"
|
||||
import type { InteractionSceneController } from "./index"
|
||||
import type { InteractionSceneDefinition } from "../../../shared/script/types"
|
||||
import { useGame } from "../../game"
|
||||
import CrewObjectCard from "./CrewObjectCard.vue"
|
||||
|
||||
const props = defineProps<{
|
||||
controller: InteractionSceneController
|
|
@ -28,25 +28,12 @@
|
|||
</div>
|
||||
</div>
|
||||
<button
|
||||
v-if="mode === 'audience'"
|
||||
class="align-end py-2 w-full"
|
||||
:class="isCurrentSuggestion ? 'bg-blue-500' : 'bg-gray-700'"
|
||||
@click="toggleVote()"
|
||||
>
|
||||
+1
|
||||
</button>
|
||||
<button
|
||||
v-if="mode === 'director'"
|
||||
class="align-end py-1 w-full bg-green-800"
|
||||
>
|
||||
<CheckIcon class="relative top-2px"/>
|
||||
</button>
|
||||
<button
|
||||
v-if="mode === 'director'"
|
||||
class="align-end py-1 w-full bg-red-900"
|
||||
>
|
||||
<TrashIcon class="relative top-2px"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -73,11 +60,8 @@
|
|||
votes: number
|
||||
controller: InteractionSceneController
|
||||
definition: InteractionSceneDefinition
|
||||
mode: "audience" | "director"
|
||||
}>()
|
||||
|
||||
const game = useGame()
|
||||
|
||||
const isCurrentSuggestion = computed(() => props.controller.suggestedInteractionId.value === getSuggestedInteractionId(props.interaction))
|
||||
|
||||
function toggleVote() {
|
|
@ -1,6 +1,10 @@
|
|||
<template>
|
||||
<TabbedContainer v-model:activeTabId="activeTabId" :tabs="TABS" :content-props="{ controller, definition }"
|
||||
@event:switch-tab="(tabId: any) => (activeTabId = tabId)"/>
|
||||
<TabbedContainer
|
||||
v-model:activeTabId="activeTabId"
|
||||
:tabs="TABS"
|
||||
:content-props="{ controller, definition }"
|
||||
@event:switch-tab="(tabId: any) => (activeTabId = tabId)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style module lang="scss">
|
||||
|
|
|
@ -11,11 +11,10 @@
|
|||
</button>
|
||||
</div>
|
||||
<transition-group v-else tag="div" name="list" class="flex flex-col gap-4 relative">
|
||||
<InteractionQueueItemCard
|
||||
<InteractionQueueItem
|
||||
v-for="(item, index) in controller.sortedInteractionQueue.value"
|
||||
:key="item.id"
|
||||
:style="{ zIndex: 1000 - index }"
|
||||
mode="audience"
|
||||
:interaction="item.interaction"
|
||||
:votes="item.votes"
|
||||
:controller="controller"
|
||||
|
@ -31,7 +30,7 @@
|
|||
</style>
|
||||
|
||||
<script setup lang="ts">
|
||||
import InteractionQueueItemCard from "./InteractionQueueItemCard.vue"
|
||||
import InteractionQueueItem from "./InteractionQueueItem.vue"
|
||||
import { useGame } from "../../game"
|
||||
import ArrowRightIcon from "virtual:icons/ph/arrow-right"
|
||||
import type { InteractionSceneController } from "./index"
|
||||
|
@ -45,6 +44,4 @@
|
|||
const emit = defineEmits<{
|
||||
"switch-tab": [string]
|
||||
}>()
|
||||
|
||||
const game = useGame()
|
||||
</script>
|
|
@ -3,7 +3,7 @@ import type { InteractionSceneDefinition } from "../../../shared/script/types"
|
|||
import PlayerView from "./PlayerView.vue"
|
||||
import type { InteractionSceneEvent } from "../../../shared/scene-types/interaction"
|
||||
import { getSuggestedInteractionId } from "../../../shared/scene-types/interaction"
|
||||
import DuoView from "./DuoView.vue"
|
||||
import CrewView from "./CrewView.vue"
|
||||
import type { SuggestedInteraction } from "../../../shared/mutations"
|
||||
import { computed, reactive, type Reactive, ref, type Ref } from "vue"
|
||||
import { trpcClient } from "../../trpc"
|
||||
|
@ -11,27 +11,71 @@ import { trpcClient } from "../../trpc"
|
|||
export const InteractionSceneType: SceneType<InteractionSceneDefinition, InteractionSceneController> = {
|
||||
id: "interaction",
|
||||
playerView: PlayerView,
|
||||
duoView: DuoView,
|
||||
crewView: CrewView,
|
||||
createController(definition: InteractionSceneDefinition): InteractionSceneController {
|
||||
const visibleObjectIds = reactive(new Set<string>())
|
||||
const objectCompletionById = reactive(new Map<string, number>())
|
||||
const replacedObjectIdMap = reactive(new Map<string, string>())
|
||||
const interactionQueue = reactive(new Map<string, InteractionQueueItem>())
|
||||
|
||||
const suggestedInteraction = ref<SuggestedInteraction | null>(null)
|
||||
const suggestedInteractionId = computed(() => suggestedInteraction.value === null ? null : getSuggestedInteractionId(suggestedInteraction.value))
|
||||
|
||||
const ongoingInteractionExecution = ref<SuggestedInteraction | null>(null)
|
||||
|
||||
return {
|
||||
visibleObjectIds,
|
||||
interactionQueue,
|
||||
sortedInteractionQueue: computed(() => [...interactionQueue.values()].sort((a, b) => a.votes - b.votes)),
|
||||
sortedInteractionQueueWithDefined: computed(() => {
|
||||
const result: InteractionQueueItem[] = [...interactionQueue.values()].sort((a, b) => a.votes - b.votes)
|
||||
|
||||
for (const [interactionId, interaction] of definition.interactionsById.entries()) {
|
||||
let suggestion: SuggestedInteraction
|
||||
switch (interaction.type) {
|
||||
case "use":
|
||||
suggestion = {
|
||||
type: "use",
|
||||
objectId: interaction.objectId
|
||||
}
|
||||
|
||||
break
|
||||
|
||||
case "combine":
|
||||
suggestion = {
|
||||
type: "combine",
|
||||
objectIds: new Set(interaction.inputObjects.keys())
|
||||
}
|
||||
}
|
||||
|
||||
if (!interactionQueue.has(interactionId)) {
|
||||
result.push({
|
||||
id: interactionId,
|
||||
interaction: suggestion,
|
||||
votes: 0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}),
|
||||
suggestedInteraction,
|
||||
suggestedInteractionId,
|
||||
ongoingInteractionExecution,
|
||||
handleEvent(event: InteractionSceneEvent) {
|
||||
switch (event.type) {
|
||||
case "votes-changed":
|
||||
const removedInteractionIds = new Set(interactionQueue.keys())
|
||||
Object.entries(event.votesByInteractionId).forEach(([interactionId, votes]) => {
|
||||
if (votes <= 0) {
|
||||
interactionQueue.delete(interactionId)
|
||||
if (votes > 0) {
|
||||
interactionQueue.get(interactionId)!.votes = votes
|
||||
removedInteractionIds.delete(interactionId)
|
||||
}
|
||||
})
|
||||
|
||||
removedInteractionIds.forEach(interactionId => {
|
||||
if (interactionId === suggestedInteractionId.value) suggestedInteraction.value = null
|
||||
} else interactionQueue.get(interactionId)!.votes = votes
|
||||
return interactionQueue.delete(interactionId)
|
||||
})
|
||||
|
||||
break
|
||||
|
@ -42,8 +86,34 @@ export const InteractionSceneType: SceneType<InteractionSceneDefinition, Interac
|
|||
break
|
||||
|
||||
case "object-visibility-changed":
|
||||
if (event.isVisible) visibleObjectIds.delete(event.objectId)
|
||||
else visibleObjectIds.add(event.objectId)
|
||||
if (event.isVisible) visibleObjectIds.add(event.objectId)
|
||||
else visibleObjectIds.delete(event.objectId)
|
||||
break
|
||||
|
||||
case "object-completion-step-changed":
|
||||
objectCompletionById.set(event.objectId, event.step)
|
||||
const object = definition.objectsById.get(event.objectId)!
|
||||
if (object.completion!.steps === event.step) {
|
||||
replacedObjectIdMap.set(event.objectId, object.completion!.replaceWith)
|
||||
visibleObjectIds.delete(event.objectId)
|
||||
visibleObjectIds.add(object.completion!.replaceWith)
|
||||
}
|
||||
|
||||
break
|
||||
|
||||
case "interaction-execution-started":
|
||||
ongoingInteractionExecution.value = event.interaction
|
||||
break
|
||||
|
||||
case "interaction-execution-cancelled":
|
||||
ongoingInteractionExecution.value = null
|
||||
break
|
||||
|
||||
case "interaction-execution-finished":
|
||||
const interactionId = getSuggestedInteractionId(this.ongoingInteractionExecution.value!)
|
||||
ongoingInteractionExecution.value = null
|
||||
interactionQueue.delete(interactionId)
|
||||
if (suggestedInteractionId.value === interactionId) suggestedInteraction.value = null
|
||||
break
|
||||
}
|
||||
},
|
||||
|
@ -53,7 +123,16 @@ export const InteractionSceneType: SceneType<InteractionSceneDefinition, Interac
|
|||
async setInteractionVote(interaction: SuggestedInteraction | null) {
|
||||
await trpcClient.player.interactionScene.setInteractionVote.mutate({ interaction })
|
||||
suggestedInteraction.value = interaction
|
||||
}
|
||||
},
|
||||
async startInteractionExecution(interaction: SuggestedInteraction) {
|
||||
await trpcClient.crew.interactionScene.startInteractionExecution.mutate({ interaction })
|
||||
},
|
||||
async cancelInteractionExecution(interactionId: string, onlyIfOngoing: boolean) {
|
||||
await trpcClient.crew.interactionScene.cancelInteractionExecution.mutate({ interactionId, onlyIfOngoing })
|
||||
},
|
||||
async finishInteractionExecution(interactionId: string, onlyIfOngoing: boolean) {
|
||||
await trpcClient.crew.interactionScene.finishInteractionExecution.mutate({ interactionId, onlyIfOngoing })
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,10 +147,18 @@ export interface InteractionSceneController extends SceneController {
|
|||
visibleObjectIds: Reactive<Set<string>>
|
||||
interactionQueue: Reactive<Map<string, InteractionQueueItem>>
|
||||
sortedInteractionQueue: Readonly<Ref<Array<InteractionQueueItem>>>
|
||||
sortedInteractionQueueWithDefined: Readonly<Ref<Array<InteractionQueueItem>>>
|
||||
suggestedInteraction: Ref<SuggestedInteraction | null>
|
||||
suggestedInteractionId: Readonly<Ref<string | null>>
|
||||
ongoingInteractionExecution: Ref<SuggestedInteraction | null>
|
||||
|
||||
setObjectVisibility(objectId: string, isVisible: boolean): Promise<void>
|
||||
|
||||
setInteractionVote(interaction: SuggestedInteraction | null): Promise<void>
|
||||
|
||||
startInteractionExecution(interaction: SuggestedInteraction): Promise<void>
|
||||
|
||||
cancelInteractionExecution(interactionId: string, onlyIfOngoing: boolean): Promise<void>
|
||||
|
||||
finishInteractionExecution(interactionId: string, onlyIfOngoing: boolean): Promise<void>
|
||||
}
|
|
@ -2,12 +2,12 @@ import type { SceneController, SceneType } from "../base"
|
|||
import type { TextSceneDefinition } from "../../../shared/script/types"
|
||||
import type { TextSceneEvent } from "../../../shared/scene-types/text"
|
||||
import PlayerView from "./PlayerView.vue"
|
||||
import DuoView from "./DuoView.vue"
|
||||
import CrewView from "./CrewView.vue"
|
||||
|
||||
export const TextSceneType: SceneType<TextSceneDefinition, TextSceneController> = {
|
||||
id: "text",
|
||||
playerView: PlayerView,
|
||||
duoView: DuoView,
|
||||
crewView: CrewView,
|
||||
createController(definition: TextSceneDefinition): TextSceneController {
|
||||
return {
|
||||
handleEvent(event: TextSceneEvent) {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</template>
|
||||
</div>
|
||||
</section>
|
||||
<component :is="game.currentScene.type.duoView" :controller="game.currentScene.controller" :definition="game.currentScene.definition"/>
|
||||
<component :is="game.currentScene.type.crewView" :controller="game.currentScene.controller" :definition="game.currentScene.definition"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ export type InteractionSceneEvent =
|
|||
| { type: "interaction-queued"; interaction: SuggestedInteraction }
|
||||
| { type: "votes-changed"; votesByInteractionId: Record<string, number> }
|
||||
| { 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-cancelled" }
|
Loading…
Add table
Reference in a new issue