diff --git a/public/index.html b/public/index.html index 80aa7cdeb8fcac62033133fdb714642cc7b117c1..a0aafe62a8c91a55b46d7c5749b5ea679cbc5d53 100644 --- a/public/index.html +++ b/public/index.html @@ -1,19 +1,17 @@ <!DOCTYPE html> <html lang="en"> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width,initial-scale=1" /> -<head> - <meta charset="utf-8" /> - <meta name="viewport" content="width=device-width,initial-scale=1" /> + <title>OH14 Work Adventure Story</title> - <title>OH14 Work Adventure Story</title> + <link rel="icon" type="image/png" href="./favicon.png" /> + <link rel="stylesheet" href="./global.css" /> + <link rel="stylesheet" href="./build/bundle.css" /> - <link rel="icon" type="image/png" href="./favicon.png" /> - <link rel="stylesheet" href="./global.css" /> - <link rel="stylesheet" href="./build/bundle.css" /> + <script defer src="./build/bundle.js"></script> + </head> - <script defer src="./build/bundle.js"></script> -</head> - -<body></body> - -</html> \ No newline at end of file + <body></body> +</html> diff --git a/src/App.svelte b/src/App.svelte index c8348b1e204b4ba9fe3d2d5f4612aea08cd80b5c..67ce1d6c5e39b1455c79e80caf614a9d532a32d7 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -10,14 +10,15 @@ <main> {#await dialogSetPromise then dialogSet} - <DialogSetComponent bind:currentDialog={currentDialog} {...dialogSet} /> + <DialogSetComponent bind:currentDialog {...dialogSet} /> - <br> - <Debugger dialogSet={dialogSet} bind:currentDialog={currentDialog}/> + <br /> + <Debugger {dialogSet} bind:currentDialog /> {:catch _error} <h3>Oh no :(</h3> <p> - We could not load that dialogSet that you specified in the url. Sorry. Please go complain to your friendly admin(s). + We could not load that dialogSet that you specified in the url. Sorry. + Please go complain to your friendly admin(s). </p> {/await} </main> diff --git a/src/Debugger.svelte b/src/Debugger.svelte index 247fb853cd48a4584178eb82698c1bf8c4647b06..9875530426b06d010a31c0236b8e935518ad45af 100644 --- a/src/Debugger.svelte +++ b/src/Debugger.svelte @@ -1,19 +1,23 @@ <script lang="ts"> - import { gameFactsStore, addGameFactToFactArray, toggleFactInFactArray } from './gameFacts' - import type { Dialog, DialogMap, DialogSet } from './types'; - import {findDialogSetProblems} from "./utils" - + import { + gameFactsStore, + addGameFactToFactArray, + toggleFactInFactArray, + } from "./gameFacts"; + import type { Dialog, DialogMap, DialogSet } from "./types"; + import { findDialogSetProblems } from "./utils"; + export let currentDialog: Dialog; export let dialogSet: DialogSet; - $: dialogNames = Object.keys(dialogSet.dialogs) - export let selectedDialogName: string + $: dialogNames = Object.keys(dialogSet.dialogs); + export let selectedDialogName: string; /** * Collect all facts referenced in these dialogs */ function getDialogFacts(dialogMap: DialogMap): String[] { const dialogs: Dialog[] = []; - for (const dmKey of Object.keys(dialogMap)) dialogs.push(dialogMap[dmKey]); + for (const dmKey of Object.keys(dialogMap)) dialogs.push(dialogMap[dmKey]); const allFacts = dialogs .map((dialog) => [ ...(dialog.addFacts || []), @@ -27,57 +31,74 @@ return [...factsSet]; } - let seenFactIds = new Set<String>([...$gameFactsStore, ...getDialogFacts(dialogSet.dialogs)]); - $: seenFactIdsArray = Array.from(seenFactIds) + let seenFactIds = new Set<String>([ + ...$gameFactsStore, + ...getDialogFacts(dialogSet.dialogs), + ]); + $: seenFactIdsArray = Array.from(seenFactIds); let addFactInputValue: string; </script> <div> - <h3>Dialog-Debugger</h3> - Jump to dialog: - <!-- svelte-ignore a11y-no-onchange --> - <select bind:value={selectedDialogName} on:change="{() => currentDialog = dialogSet.dialogs[selectedDialogName]}"> - {#each dialogNames as dialogName} - <option value={dialogName} selected={dialogName === selectedDialogName}> - {dialogName} - </option> - {/each} - </select> - <br> - <ol> - {#each findDialogSetProblems(dialogSet) as {sourceDialog, text}} - <li> - {sourceDialog}: <code style="color: orange">{text}</code> - </li> + <h3>Dialog-Debugger</h3> + Jump to dialog: + <!-- svelte-ignore a11y-no-onchange --> + <select + bind:value={selectedDialogName} + on:change={() => (currentDialog = dialogSet.dialogs[selectedDialogName])} + > + {#each dialogNames as dialogName} + <option value={dialogName} selected={dialogName === selectedDialogName}> + {dialogName} + </option> + {/each} + </select> + <br /> + <ol> + {#each findDialogSetProblems(dialogSet) as { sourceDialog, text }} + <li> + {sourceDialog}: <code style="color: orange">{text}</code> + </li> {/each} - </ol> - - <hr> + </ol> - <h3>Quest-Debugger</h3> - <b>GameFacts:</b> - <ul> - {#each seenFactIdsArray as gameFact} - <li> - <input type="checkbox" checked={$gameFactsStore.includes(gameFact)} on:change={() => gameFactsStore.set(toggleFactInFactArray(gameFact, $gameFactsStore))}> - {gameFact} - </li> - {/each} - </ul> - Add fact: - <input type="text" bind:value={addFactInputValue}> - <button style="width: 3rem;" on:click={() => { - gameFactsStore.set(addGameFactToFactArray(addFactInputValue, $gameFactsStore)); - seenFactIds = seenFactIds.add(addFactInputValue); - addFactInputValue = ''; - }}>Add</button> + <hr /> + <h3>Quest-Debugger</h3> + <b>GameFacts:</b> + <ul> + {#each seenFactIdsArray as gameFact} + <li> + <input + type="checkbox" + checked={$gameFactsStore.includes(gameFact)} + on:change={() => + gameFactsStore.set( + toggleFactInFactArray(gameFact, $gameFactsStore) + )} + /> + {gameFact} + </li> + {/each} + </ul> + Add fact: + <input type="text" bind:value={addFactInputValue} /> + <button + style="width: 3rem;" + on:click={() => { + gameFactsStore.set( + addGameFactToFactArray(addFactInputValue, $gameFactsStore) + ); + seenFactIds = seenFactIds.add(addFactInputValue); + addFactInputValue = ""; + }}>Add</button + > </div> <style> - div { - border: 1px solid; - padding: 10px; - margin: 5px; - } -</style> \ No newline at end of file + div { + border: 1px solid; + padding: 10px; + margin: 5px; + } +</style> diff --git a/src/DialogSetComponent.svelte b/src/DialogSetComponent.svelte index af30fc1150aa50bbbedc29878814cfbf5ba279f2..fcb403ec15e413dd617b5dd53753ac9f65cea9a3 100644 --- a/src/DialogSetComponent.svelte +++ b/src/DialogSetComponent.svelte @@ -14,6 +14,8 @@ </script> <SingleDialogComponent - imageUrl={imageUrl} title={title} {...currentDialog} + {imageUrl} + {title} + {...currentDialog} on:switchToDialog={(event) => switchDialog(event.detail)} /> diff --git a/src/SingleDialogComponent.svelte b/src/SingleDialogComponent.svelte index b6f019120a2eddaa646e1ce5b28ba7f9cf90678d..daab5d48136fcdb414c868de0369530116e8e39e 100644 --- a/src/SingleDialogComponent.svelte +++ b/src/SingleDialogComponent.svelte @@ -1,8 +1,8 @@ <script lang="ts"> import type { DialogOption } from "./types"; import { createEventDispatcher } from "svelte"; - import { gameFactsStore, addGameFactToFactArray } from './gameFacts' - import Typewriter from 'svelte-typewriter' + import { gameFactsStore, addGameFactToFactArray } from "./gameFacts"; + import Typewriter from "svelte-typewriter"; const dispatch = createEventDispatcher(); @@ -10,26 +10,39 @@ export let title: string | undefined; export let text: string; export let options: DialogOption[] = []; - $: usableOptions = options.filter(option => isDialogOptionAllowedByGameFacts(option, $gameFactsStore)) + $: usableOptions = options.filter((option) => + isDialogOptionAllowedByGameFacts(option, $gameFactsStore) + ); export let addFacts: String[] = []; $: { if (Array.isArray(addFacts)) { - const gameFactsAfterAddedFacts = addFacts.reduce((accFacts, factToAdd) => addGameFactToFactArray(factToAdd, accFacts), $gameFactsStore) + const gameFactsAfterAddedFacts = addFacts.reduce( + (accFacts, factToAdd) => addGameFactToFactArray(factToAdd, accFacts), + $gameFactsStore + ); $gameFactsStore = gameFactsAfterAddedFacts; } } export let removeFacts: String[] = []; $: { if (Array.isArray(removeFacts)) { - const gameFactsAfterRemovedFacts = removeFacts.reduce((accFacts, factToRemove) => accFacts.filter(f => f !== factToRemove), $gameFactsStore) + const gameFactsAfterRemovedFacts = removeFacts.reduce( + (accFacts, factToRemove) => accFacts.filter((f) => f !== factToRemove), + $gameFactsStore + ); $gameFactsStore = gameFactsAfterRemovedFacts; } } - function isDialogOptionAllowedByGameFacts(option: DialogOption, gameFacts: String[]): boolean { + function isDialogOptionAllowedByGameFacts( + option: DialogOption, + gameFacts: String[] + ): boolean { // Check if all required facts are given if (Array.isArray(option.requiredFacts)) { - const isOk = option.requiredFacts.every(requiredFact => gameFacts.includes(requiredFact)); + const isOk = option.requiredFacts.every((requiredFact) => + gameFacts.includes(requiredFact) + ); if (!isOk) { return false; } @@ -37,7 +50,9 @@ // Check if no forbidden facts are given if (Array.isArray(option.forbiddenFacts)) { - const isOk = option.forbiddenFacts.every(forbiddenFact => !gameFacts.includes(forbiddenFact)); + const isOk = option.forbiddenFacts.every( + (forbiddenFact) => !gameFacts.includes(forbiddenFact) + ); if (!isOk) { return false; } @@ -47,7 +62,7 @@ } function handleDialogOptionClick(option: DialogOption): void { - dispatch("switchToDialog", option.linksToDialog) + dispatch("switchToDialog", option.linksToDialog); } </script> @@ -62,7 +77,7 @@ <div> <Typewriter cascade interval={10} cursor={false}> {#each text.split("\n") as para} - <p>{para}</p> + <p>{para}</p> {/each} </Typewriter> </div> diff --git a/src/dialogSet.schema.json b/src/dialogSet.schema.json index 185912f8e6c827c2cc2bdb37ce3661ff7836f27e..bf7416d6eb6de64042d659824e06b43c720c0e23 100644 --- a/src/dialogSet.schema.json +++ b/src/dialogSet.schema.json @@ -1,123 +1,122 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "definitions": { - "Dialog": { - "description": "A dialog situation that a player can encounter in this dialogSet.\n\nPrinciple: NPC says something, user has options how to react.", - "properties": { - "addFacts": { - "description": "Upon entering this dialog, register these facts for the user.\n\nFacts are texts and may unlock or block dialog options across the game.\nE.g. The fact \"acceptedMissionX\" could unlock a dialog option regarding Mission X with another NPC.", - "items": { - "additionalProperties": false, - "description": "Allows manipulation and formatting of text strings and determination and location of substrings within strings.", - "patternProperties": { - "^[0-9]+$": { - "type": "string" - } - }, - "type": "object" - }, - "type": "array" - }, - "imageUrl": { - "description": "Can be used to overwrite imageUrl from DialogSet.\nE.g. when a different emotion should be shown for this dialog,", - "type": "string" - }, - "options": { - "description": "Choices of what the user might say in this dialog and where those choices lead to.\nThe player should be given between 0 and 4 options.", - "items": { - "$ref": "#/definitions/DialogOption" - }, - "type": "array" - }, - "removeFacts": { - "description": "Upon entering this dialog, remove these facts from the user.\n\nFacts are texts and may unlock or block dialog options across the game.\nE.g. The fact \"acceptedMissionX\" could unlock a dialog option regarding Mission X with another NPC.", - "items": { - "additionalProperties": false, - "description": "Allows manipulation and formatting of text strings and determination and location of substrings within strings.", - "patternProperties": { - "^[0-9]+$": { - "type": "string" - } - }, - "type": "object" - }, - "type": "array" - }, - "text": { - "description": "What the characters says to the player.\nLine breaks (\\n) are rendered as paragraphs.\nYou may leave this empty or omit it completely.", - "type": "string" - }, - "title": { - "description": "The title is displayed at the very top of the dialog screen\nand usually corresponds to the name of the NPC the player is speaking to.\n\nCan be used to overwrite title from DialogSet.", - "type": "string" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "Dialog": { + "description": "A dialog situation that a player can encounter in this dialogSet.\n\nPrinciple: NPC says something, user has options how to react.", + "properties": { + "addFacts": { + "description": "Upon entering this dialog, register these facts for the user.\n\nFacts are texts and may unlock or block dialog options across the game.\nE.g. The fact \"acceptedMissionX\" could unlock a dialog option regarding Mission X with another NPC.", + "items": { + "additionalProperties": false, + "description": "Allows manipulation and formatting of text strings and determination and location of substrings within strings.", + "patternProperties": { + "^[0-9]+$": { + "type": "string" + } }, "type": "object" + }, + "type": "array" }, - "DialogOption": { - "properties": { - "forbiddenFacts": { - "description": "All facts in this array must *not* be registered for this player for this option to show up.\n\nFacts are texts and may unlock or block dialog options across the game.\nE.g. The fact \"acceptedMissionX\" could unlock a dialog option regarding Mission X with another NPC.", - "items": { - "additionalProperties": false, - "description": "Allows manipulation and formatting of text strings and determination and location of substrings within strings.", - "patternProperties": { - "^[0-9]+$": { - "type": "string" - } - }, - "type": "object" - }, - "type": "array" - }, - "linksToDialog": { - "description": "Which dialog in this dialogSet to jump to if this option is chosen.", - "type": "string" - }, - "requiredFacts": { - "description": "All facts in this array must be registered for this player for this option to show up.\n\nFacts are texts and may unlock or block dialog options across the game.\nE.g. The fact \"acceptedMissionX\" could unlock a dialog option regarding Mission X with another NPC.", - "items": { - "additionalProperties": false, - "description": "Allows manipulation and formatting of text strings and determination and location of substrings within strings.", - "patternProperties": { - "^[0-9]+$": { - "type": "string" - } - }, - "type": "object" - }, - "type": "array" - }, - "text": { - "description": "Text on option button.\nShould be rather short and not have line breaks.", - "type": "string" - } + "imageUrl": { + "description": "Can be used to overwrite imageUrl from DialogSet.\nE.g. when a different emotion should be shown for this dialog,", + "type": "string" + }, + "options": { + "description": "Choices of what the user might say in this dialog and where those choices lead to.\nThe player should be given between 0 and 4 options.", + "items": { + "$ref": "#/definitions/DialogOption" + }, + "type": "array" + }, + "removeFacts": { + "description": "Upon entering this dialog, remove these facts from the user.\n\nFacts are texts and may unlock or block dialog options across the game.\nE.g. The fact \"acceptedMissionX\" could unlock a dialog option regarding Mission X with another NPC.", + "items": { + "additionalProperties": false, + "description": "Allows manipulation and formatting of text strings and determination and location of substrings within strings.", + "patternProperties": { + "^[0-9]+$": { + "type": "string" + } }, "type": "object" + }, + "type": "array" + }, + "text": { + "description": "What the characters says to the player.\nLine breaks (\\n) are rendered as paragraphs.\nYou may leave this empty or omit it completely.", + "type": "string" + }, + "title": { + "description": "The title is displayed at the very top of the dialog screen\nand usually corresponds to the name of the NPC the player is speaking to.\n\nCan be used to overwrite title from DialogSet.", + "type": "string" } + }, + "type": "object" }, - "description": "Set of dialogs, beginning with startDialogName.\nThis should be", - "properties": { - "dialogs": { - "additionalProperties": { - "$ref": "#/definitions/Dialog" + "DialogOption": { + "properties": { + "forbiddenFacts": { + "description": "All facts in this array must *not* be registered for this player for this option to show up.\n\nFacts are texts and may unlock or block dialog options across the game.\nE.g. The fact \"acceptedMissionX\" could unlock a dialog option regarding Mission X with another NPC.", + "items": { + "additionalProperties": false, + "description": "Allows manipulation and formatting of text strings and determination and location of substrings within strings.", + "patternProperties": { + "^[0-9]+$": { + "type": "string" + } }, - "description": "All dialogs in this dialogSet.", "type": "object" + }, + "type": "array" }, - "imageUrl": { - "description": "Url of image.\n\nShould link to image in this repo, to avoid CORS errors.", - "type": "string" + "linksToDialog": { + "description": "Which dialog in this dialogSet to jump to if this option is chosen.", + "type": "string" }, - "startDialogName": { - "description": "At which dialog to start when the dialogSet is first opened by the player.", - "type": "string" + "requiredFacts": { + "description": "All facts in this array must be registered for this player for this option to show up.\n\nFacts are texts and may unlock or block dialog options across the game.\nE.g. The fact \"acceptedMissionX\" could unlock a dialog option regarding Mission X with another NPC.", + "items": { + "additionalProperties": false, + "description": "Allows manipulation and formatting of text strings and determination and location of substrings within strings.", + "patternProperties": { + "^[0-9]+$": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" }, - "title": { - "description": "The title is displayed at the very top of the dialog screen\nand usually corresponds to the name of the NPC the player is speaking to.", - "type": "string" + "text": { + "description": "Text on option button.\nShould be rather short and not have line breaks.", + "type": "string" } + }, + "type": "object" + } + }, + "description": "Set of dialogs, beginning with startDialogName.\nThis should be", + "properties": { + "dialogs": { + "additionalProperties": { + "$ref": "#/definitions/Dialog" + }, + "description": "All dialogs in this dialogSet.", + "type": "object" + }, + "imageUrl": { + "description": "Url of image.\n\nShould link to image in this repo, to avoid CORS errors.", + "type": "string" + }, + "startDialogName": { + "description": "At which dialog to start when the dialogSet is first opened by the player.", + "type": "string" }, - "type": "object" + "title": { + "description": "The title is displayed at the very top of the dialog screen\nand usually corresponds to the name of the NPC the player is speaking to.", + "type": "string" + } + }, + "type": "object" } -