diff --git a/package.json b/package.json index 6c4576a..926467b 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "typescript": "^5.7.2", "unplugin-icons": "^0.22.0", "vite": "^6.0.3", - "vite-plugin-pages": "^0.32.4", "vite-plugin-windicss": "^1.9.4", "windicss": "^3.5.6" }, @@ -46,7 +45,6 @@ "pinia": "^2.3.0", "superjson": "^2.2.2", "vue": "^3.5.13", - "vue-router": "^4.5.0", "ws": "^8.18.0", "zod": "^3.24.1" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 068618c..5fcca92 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -68,9 +68,6 @@ importers: vue: specifier: ^3.5.13 version: 3.5.13(typescript@5.7.2) - vue-router: - specifier: ^4.5.0 - version: 4.5.0(vue@3.5.13(typescript@5.7.2)) ws: specifier: ^8.18.0 version: 8.18.0(bufferutil@4.0.8) @@ -111,9 +108,6 @@ importers: vite: specifier: ^6.0.3 version: 6.0.3(@types/node@22.10.2)(jiti@2.4.2)(sass@1.83.0)(tsx@4.19.2)(yaml@2.6.1) - vite-plugin-pages: - specifier: ^0.32.4 - version: 0.32.4(@vue/compiler-sfc@3.5.13)(vite@6.0.3(@types/node@22.10.2)(jiti@2.4.2)(sass@1.83.0)(tsx@4.19.2)(yaml@2.6.1))(vue-router@4.5.0(vue@3.5.13(typescript@5.7.2))) vite-plugin-windicss: specifier: ^1.9.4 version: 1.9.4(vite@6.0.3(@types/node@22.10.2)(jiti@2.4.2)(sass@1.83.0)(tsx@4.19.2)(yaml@2.6.1)) @@ -701,9 +695,6 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - '@types/debug@4.1.12': - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -725,9 +716,6 @@ packages: '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - '@types/ms@0.7.34': - resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} - '@types/node@22.10.2': resolution: {integrity: sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==} @@ -954,10 +942,6 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - destr@2.0.3: resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==} @@ -1014,15 +998,6 @@ packages: escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - esprima-extract-comments@1.1.0: - resolution: {integrity: sha512-sBQUnvJwpeE9QnPrxh7dpI/dp67erYG4WXEAreAMoelPRpMR7NWb4YtwRPn9b+H1uLQKl/qS8WYmyaljTpjIsw==} - engines: {node: '>=4'} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -1041,10 +1016,6 @@ packages: resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} - extract-comments@1.1.0: - resolution: {integrity: sha512-dzbZV2AdSSVW/4E7Ti5hZdHWbA+Z80RJsJhr5uiL10oyjl/gy7/o+HI1HwK4/WSZhlq4SNKU3oUzXlM13Qx02Q==} - engines: {node: '>=6'} - fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -1194,11 +1165,6 @@ packages: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} @@ -1323,10 +1289,6 @@ packages: package-manager-detector@0.2.7: resolution: {integrity: sha512-g4+387DXDKlZzHkP+9FLt8yKj8+/3tOkPv7DVTJGGRm00RkEWgqbFstX1mXJ4M0VDYhUqsTOiISqNOJnhAu3PQ==} - parse-code-context@1.0.0: - resolution: {integrity: sha512-OZQaqKaQnR21iqhlnPfVisFjBWjhnMl5J9MgbP8xC+EwoVqbXrq78lp+9Zb3ahmLzrIX5Us/qbvBnaS3hkH6OA==} - engines: {node: '>=6'} - parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -1565,24 +1527,6 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vite-plugin-pages@0.32.4: - resolution: {integrity: sha512-OM8CNb8mAzyYR8ASRC0+2LXVB8ecR/5JHc5RpxbWtF+CmhjhmIELs0iV5y8qvU48soZbk+NsFOYlhoIcjw3+ew==} - peerDependencies: - '@solidjs/router': '*' - '@vue/compiler-sfc': ^2.7.0 || ^3.0.0 - react-router: '*' - vite: ^2.0.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0 || ^6.0.0 - vue-router: '*' - peerDependenciesMeta: - '@solidjs/router': - optional: true - '@vue/compiler-sfc': - optional: true - react-router: - optional: true - vue-router: - optional: true - vite-plugin-windicss@1.9.4: resolution: {integrity: sha512-3t1AUVrs2XBXGc2BefRPRvy1CLy8qA/5A1J1Z73Ej1DIx+puXn39MQSWluxZ2FHEz8z9OEIvsoIIPc/s/P3OmQ==} peerDependencies: @@ -1639,11 +1583,6 @@ packages: '@vue/composition-api': optional: true - vue-router@4.5.0: - resolution: {integrity: sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==} - peerDependencies: - vue: ^3.2.0 - vue@3.5.13: resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==} peerDependencies: @@ -2067,10 +2006,6 @@ snapshots: dependencies: '@types/node': 22.10.2 - '@types/debug@4.1.12': - dependencies: - '@types/ms': 0.7.34 - '@types/estree@1.0.6': {} '@types/express-serve-static-core@5.0.2': @@ -2097,8 +2032,6 @@ snapshots: '@types/mime@1.3.5': {} - '@types/ms@0.7.34': {} - '@types/node@22.10.2': dependencies: undici-types: 6.20.0 @@ -2334,8 +2267,6 @@ snapshots: depd@2.0.0: {} - dequal@2.0.3: {} - destr@2.0.3: {} destroy@1.2.0: {} @@ -2420,12 +2351,6 @@ snapshots: escape-html@1.0.3: {} - esprima-extract-comments@1.1.0: - dependencies: - esprima: 4.0.1 - - esprima@4.0.1: {} - estree-walker@2.0.2: {} etag@1.8.1: {} @@ -2480,11 +2405,6 @@ snapshots: transitivePeerDependencies: - supports-color - extract-comments@1.1.0: - dependencies: - esprima-extract-comments: 1.1.0 - parse-code-context: 1.0.0 - fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -2628,8 +2548,6 @@ snapshots: jiti@2.4.2: {} - json5@2.2.3: {} - kolorist@1.8.0: {} listhen@1.9.0: @@ -2736,8 +2654,6 @@ snapshots: package-manager-detector@0.2.7: {} - parse-code-context@1.0.0: {} - parseurl@1.3.3: {} path-key@3.1.1: {} @@ -2991,24 +2907,6 @@ snapshots: vary@1.1.2: {} - vite-plugin-pages@0.32.4(@vue/compiler-sfc@3.5.13)(vite@6.0.3(@types/node@22.10.2)(jiti@2.4.2)(sass@1.83.0)(tsx@4.19.2)(yaml@2.6.1))(vue-router@4.5.0(vue@3.5.13(typescript@5.7.2))): - dependencies: - '@types/debug': 4.1.12 - debug: 4.4.0 - dequal: 2.0.3 - extract-comments: 1.1.0 - fast-glob: 3.3.2 - json5: 2.2.3 - local-pkg: 0.5.1 - picocolors: 1.1.1 - vite: 6.0.3(@types/node@22.10.2)(jiti@2.4.2)(sass@1.83.0)(tsx@4.19.2)(yaml@2.6.1) - yaml: 2.6.1 - optionalDependencies: - '@vue/compiler-sfc': 3.5.13 - vue-router: 4.5.0(vue@3.5.13(typescript@5.7.2)) - transitivePeerDependencies: - - supports-color - vite-plugin-windicss@1.9.4(vite@6.0.3(@types/node@22.10.2)(jiti@2.4.2)(sass@1.83.0)(tsx@4.19.2)(yaml@2.6.1)): dependencies: '@windicss/plugin-utils': 1.9.4 @@ -3036,11 +2934,6 @@ snapshots: dependencies: vue: 3.5.13(typescript@5.7.2) - vue-router@4.5.0(vue@3.5.13(typescript@5.7.2)): - dependencies: - '@vue/devtools-api': 6.6.4 - vue: 3.5.13(typescript@5.7.2) - vue@3.5.13(typescript@5.7.2): dependencies: '@vue/compiler-dom': 3.5.13 @@ -3063,6 +2956,7 @@ snapshots: optionalDependencies: bufferutil: 4.0.8 - yaml@2.6.1: {} + yaml@2.6.1: + optional: true zod@3.24.1: {} diff --git a/public/objects/schüssel.png b/public/objects/schüssel.png new file mode 100644 index 0000000..1b4f63e Binary files /dev/null and b/public/objects/schüssel.png differ diff --git a/src/App.vue b/src/App.vue index df8228a..de8295f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -6,21 +6,16 @@ Verbindung wird hergestellt…
-
@@ -78,34 +74,76 @@ opacity: 0; } - .slide-down-enter-active, - .slide-down-leave-active { - position: absolute; - transition: 200ms ease; + .list-move, + .list-enter-active, + .list-leave-active { + transition: 400ms ease; transition-property: opacity, transform; } - .slide-down-enter-from, - .slide-down-leave-to { + .list-enter-from, + .list-leave-to { opacity: 0; - transform: translateY(40%); + transform: scale(0.9); + } + + .list-leave-active { + position: absolute; } diff --git a/src/components/InteractionQueueItemCard.vue b/src/components/InteractionQueueItemCard.vue index 8f58bcf..2452b19 100644 --- a/src/components/InteractionQueueItemCard.vue +++ b/src/components/InteractionQueueItemCard.vue @@ -5,7 +5,7 @@
-
+
{{ game.allObjectsById.get(item.interaction.objectId)!.label }}
@@ -13,27 +13,42 @@
-
-
+
+
{{ item.votes }}
Vote{{item.votes === 1 ? "" : "s" }}
+ +
@@ -44,13 +59,17 @@ \ No newline at end of file diff --git a/src/screens/DirectorScreen.vue b/src/screens/DirectorScreen.vue new file mode 100644 index 0000000..8715c13 --- /dev/null +++ b/src/screens/DirectorScreen.vue @@ -0,0 +1,70 @@ + + + + + \ No newline at end of file diff --git a/src/screens/InteractionsScreen.vue b/src/screens/InteractionsScreen.vue index d515b6a..9659781 100644 --- a/src/screens/InteractionsScreen.vue +++ b/src/screens/InteractionsScreen.vue @@ -16,9 +16,9 @@ @object-drop="onObjectInteractionDrop" />
-
+ -
+ diff --git a/src/screens/QueueScreen.vue b/src/screens/QueueScreen.vue index 97b4765..36c24d0 100644 --- a/src/screens/QueueScreen.vue +++ b/src/screens/QueueScreen.vue @@ -1,37 +1,40 @@ \ No newline at end of file diff --git a/src/server/game.ts b/src/server/game.ts index 6edebb8..c3ba59f 100644 --- a/src/server/game.ts +++ b/src/server/game.ts @@ -2,7 +2,7 @@ import EventEmitter from "eventemitter3" import type { GameEvent } from "../shared/gameEvents" import type { Interaction, InteractionQueueItem } from "../shared/script/types" import { script } from "../shared/script" -import { getInteractionQueueItemId } from "../shared/util" +import { findMatchingCombination, getInteractionQueueItemId } from "../shared/util" interface Events { "game-event": [GameEvent] @@ -59,11 +59,42 @@ export class Game extends EventEmitter { this.emit("game-event", { type: "room-changed", roomId }) } + activateInteractionQueueItem(id: string) { + const item = this.interactionQueue.get(id) + if (item === undefined) return + this.interactionQueue.delete(id) + this.emit("game-event", { type: "interaction-votes-changed", id, votes: 0 }) + switch (item.interaction.type) { + case "use": + this.emit("game-event", { type: "object-visibility-changed", id: item.interaction.objectId, isVisible: false }) + break + + case "combine": + const matchingCombination = findMatchingCombination(script.roomsById.get(this.currentRoomId)!.combinations, item.interaction.objectIds) + + if (matchingCombination !== undefined) { + matchingCombination.inputs.forEach(input => { + if (input.isConsumed) this.emit("game-event", { type: "object-visibility-changed", id: input.objectId, isVisible: false }) + }) + + matchingCombination.outputIds.forEach(outputId => { + this.emit("game-event", { type: "object-visibility-changed", id: outputId, isVisible: true }) + }) + } + + break + } + } + removeInteractionQueueItem(id: string) { if (!this.interactionQueue.has(id)) return - this.emit("game-event", { type: "interaction-votes-changed", id: id, votes: 0 }) + this.emit("game-event", { type: "interaction-votes-changed", id, votes: 0 }) this.interactionQueue.delete(id) } + + setObjectVisibility(id: string, isVisible: boolean) { + this.emit("game-event", { type: "object-visibility-changed", id, isVisible }) + } } export const game = new Game() \ No newline at end of file diff --git a/src/server/trpc/director.ts b/src/server/trpc/director.ts index 4705819..4763427 100644 --- a/src/server/trpc/director.ts +++ b/src/server/trpc/director.ts @@ -1,20 +1,38 @@ import { t } from "./base" import { z } from "zod" +import { game } from "../game" export const directorRouter = t.router({ switchRoom: t.procedure .input(z.object({ roomId: z.string() })) - .mutation(async ({ input, ctx }) => { - ctx.game.switchRoom(input.roomId) + .mutation(async ({ input }) => { + game.switchRoom(input.roomId) }), removeInteractionQueueItem: t.procedure .input(z.object({ id: z.string() })) - .mutation(async ({ input, ctx }) => { - ctx.game.removeInteractionQueueItem(input.id) + .mutation(async ({ input }) => { + game.removeInteractionQueueItem(input.id) + }), + + activateInteractionQueueItem: t.procedure + .input(z.object({ + id: z.string() + })) + .mutation(async ({ input }) => { + game.activateInteractionQueueItem(input.id) + }), + + setObjectVisibility: t.procedure + .input(z.object({ + id: z.string(), + isVisible: z.boolean() + })) + .mutation(async ({ input }) => { + game.setObjectVisibility(input.id, input.isVisible) }), }) \ No newline at end of file diff --git a/src/shared/gameEvents.ts b/src/shared/gameEvents.ts index a5daadc..b913d95 100644 --- a/src/shared/gameEvents.ts +++ b/src/shared/gameEvents.ts @@ -3,4 +3,5 @@ import type { InteractionQueueItem } from "./script/types" export type GameEvent = | { type: "room-changed"; roomId: string } | { type: "interaction-queued"; item: InteractionQueueItem } - | { type: "interaction-votes-changed"; id: string; votes: number } \ No newline at end of file + | { type: "interaction-votes-changed"; id: string; votes: number } + | { type: "object-visibility-changed"; id: string; isVisible: boolean } \ No newline at end of file diff --git a/src/shared/util.ts b/src/shared/util.ts index 4601d7c..da8195a 100644 --- a/src/shared/util.ts +++ b/src/shared/util.ts @@ -1,4 +1,5 @@ -import type { Interaction } from "./script/types" +import type { Combination, Interaction } from "./script/types" +import { isEqual } from "lodash-es" export const cSet = (...values: T[]) => new Set(values) export const cMap = (object: Record) => new Map(Object.entries(object)) @@ -9,4 +10,8 @@ export function getInteractionQueueItemId(interaction: Interaction) { else if (interaction.type === "combine") id = `combine-${[...interaction.objectIds].sort().join("_")}` else throw new Error("Unknown interaction type") return id +} + +export function findMatchingCombination(combinations: Set, objectIds: Set) { + return combinations.values().find(c => isEqual(new Set(c.inputs.values().map(i => i.objectId)), objectIds)) } \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index f33ee20..d175473 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,14 +1,12 @@ import { defineConfig } from "vite" import vuePlugin from "@vitejs/plugin-vue" import iconsPlugin from "unplugin-icons/vite" -import pagesPlugin from "vite-plugin-pages" import windiPlugin from "vite-plugin-windicss" export default defineConfig({ plugins: [ vuePlugin(), iconsPlugin(), - pagesPlugin(), windiPlugin() ], server: {