From d0b8df95b3d21ba0f512284cc3ea532c3e95347f Mon Sep 17 00:00:00 2001 From: Rolf Bjaanes Date: Fri, 6 Aug 2021 09:25:25 +0200 Subject: [PATCH 1/3] Change pars to parse --- game/js/game.mjs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/game/js/game.mjs b/game/js/game.mjs index 2a970ea..156817a 100644 --- a/game/js/game.mjs +++ b/game/js/game.mjs @@ -150,7 +150,7 @@ export default class Game { const img = node.querySelector("img"); img.src = contnet.peek.img; const msg = node.querySelector("#peekMessage") - msg.innerText = this.parsText(contnet.peek.text); + msg.innerText = this.parseText(contnet.peek.text); footer.appendChild(node) footer.addEventListener("click", (e) => { @@ -230,7 +230,7 @@ export default class Game { actions.forEach(action => { if (validateConditions(action.conditions, this.state)) { let bt = document.createElement("button"); - bt.innerText = this.parsText(action.description); + bt.innerText = this.parseText(action.description); bt.title = action.title; bt.onclick = e => { if (action.statechange) { @@ -301,10 +301,10 @@ export default class Game { let node = document.querySelector("#monologTemplate").content.cloneNode(true); let portrait = node.querySelector(".portrait"); - portrait.src = this.parsText(description.img); + portrait.src = this.parseText(description.img); let name = node.querySelector(".actorName"); - name.innerText = this.parsText(description.name); + name.innerText = this.parseText(description.name); let monolog = node.querySelector(".monologContent"); @@ -327,10 +327,10 @@ export default class Game { } const portrait = node.querySelector(".portrait"); - portrait.src = this.parsText(description.img); + portrait.src = this.parseText(description.img); const name = node.querySelector("#actorName"); - name.innerText = this.parsText(description.name); + name.innerText = this.parseText(description.name); const dioalouge = node.querySelector(".dialogueContent"); @@ -371,25 +371,25 @@ export default class Game { createLinkNode(description) { let a = document.createElement("a"); - a.href = this.parsText(description.url); + a.href = this.parseText(description.url); a.target = "_blank"; - a.innerText = this.parsText(description.text) + a.innerText = this.parseText(description.text) return a; } createTextNode(text) { let p = document.createElement("p"); - p.innerHTML = this.parsText(text); + p.innerHTML = this.parseText(text); return p; } createShoutNode(text) { let h = document.createElement("h1"); - h.innerHTML = this.parsText(text); + h.innerHTML = this.parseText(text); return h; } - parsText(text) { + parseText(text) { if (text) { const stateKeys = /{\$\S+}/gm; let matches = text.match(stateKeys); From 81d8bb8fba7e2e6a92b9bd744f26d46488d7daf6 Mon Sep 17 00:00:00 2001 From: Rolf Bjaanes Date: Fri, 6 Aug 2021 15:30:01 +0200 Subject: [PATCH 2/3] Add array as default for badges, fix increment and decrement state changes --- editor/js/views/badgesView.js | 66 ++++---- game/js/game.mjs | 278 ++++++++++++++++++++-------------- 2 files changed, 194 insertions(+), 150 deletions(-) diff --git a/editor/js/views/badgesView.js b/editor/js/views/badgesView.js index d245bf3..8fe385c 100644 --- a/editor/js/views/badgesView.js +++ b/editor/js/views/badgesView.js @@ -1,10 +1,12 @@ -import { HTMLUtilityTools, HTMLSelectorTools } from '../uiExt.js' +import { HTMLUtilityTools, HTMLSelectorTools } from "../uiExt.js"; export default class BadgesView { constructor(source, container, delegates) { - this.view = HTMLUtilityTools.createInstanceOfTemplate("badgesSectionTemplate"); + this.view = HTMLUtilityTools.createInstanceOfTemplate( + "badgesSectionTemplate" + ); container.appendChild(this.view); - this.delegates = delegates + this.delegates = delegates; this.addBadgeButton = document.getElementById("addNewBadgeBt"); this.badgesDisplay = document.getElementById("regBadges"); @@ -13,48 +15,42 @@ export default class BadgesView { if (source.badges == null || source.badges == undefined) { source.badges = []; } - source.badges.push(badge) + source.badges.push(badge); await delegates.onChange(); - this.populateBadges(this.badgesDisplay, source) - } - + this.populateBadges(this.badgesDisplay, source); + }; } - populateBadges(tbl, source) { let tbody = tbl.getElementsByTagName("tbody")[0]; - tbody.innerHTML = ""; ///TODO: Fiks dette, til å slette elementer riktig. - - if (source.badges) { - - console.log(source.badges) + const badges = source.badges || []; - source.badges.forEach(badge => { - - const row = tbody.insertRow(); - const titleCell = row.insertCell(); - const descriptionCell = row.insertCell(); - const rulesCell = row.insertCell(); - const imageCell = row.insertCell(); - imageCell.classList.add("text-right"); - - titleCell.appendChild(document.createTextNode(badge.name)) - descriptionCell.appendChild(document.createTextNode(badge.description)) - - const conditions = badge.conditions.reduce((acc, val, index, arr) => { - - acc += val.target + " : " + val.value; - return acc; - }, ""); + tbody.innerHTML = ""; ///TODO: Fiks dette, til å slette elementer riktig. - rulesCell.appendChild(document.createTextNode(conditions)) + console.log({ badges: badges }); - imageCell.appendChild(document.createTextNode(badge.img)); + badges.forEach((badge) => { + const row = tbody.insertRow(); + const titleCell = row.insertCell(); + const descriptionCell = row.insertCell(); + const rulesCell = row.insertCell(); + const imageCell = row.insertCell(); + imageCell.classList.add("text-right"); - }); + titleCell.appendChild(document.createTextNode(badge.name)); + descriptionCell.appendChild( + document.createTextNode(badge.description) + ); + const conditions = badge.conditions + .map((condition) => + [condition.target, condition.value].join(" : ") + ) + .join(", "); - } + rulesCell.appendChild(document.createTextNode(conditions)); + imageCell.appendChild(document.createTextNode(badge.img)); + }); } -} \ No newline at end of file +} diff --git a/game/js/game.mjs b/game/js/game.mjs index 156817a..e9f4b6b 100644 --- a/game/js/game.mjs +++ b/game/js/game.mjs @@ -1,14 +1,15 @@ -import d from './debug.mjs'; -import ErrorMessages from './l8n.mjs' -import { copy, merge, validateConditions, animateCSS } from './util.mjs' -import Badges from './badges.mjs' -import { Profile } from './profile.mjs' +import d from "./debug.mjs"; +import ErrorMessages from "./l8n.mjs"; +import { copy, merge, validateConditions, animateCSS } from "./util.mjs"; +import Badges from "./badges.mjs"; +import { Profile } from "./profile.mjs"; export default class Game { constructor(gameID, container, presetState) { - if (Game._instance) { - throw new Error("Singleton classes can't be instantiated more than once.") + throw new Error( + "Singleton classes can't be instantiated more than once." + ); } Game._instance = this; @@ -24,10 +25,9 @@ export default class Game { this.overlay = null; - this.badges = null; - - this.player = Profile.storedProfile() + this.badges = []; + this.player = Profile.storedProfile(); } reset(gameID, container, presetState) { @@ -40,34 +40,44 @@ export default class Game { this.currentSceneData = null; this.currentSceneUI = null; this.overlay = null; - this.badges = null; + this.badges = []; } run() { - d("Run game") + d("Run game"); - if (this.sourceCode.initialState == null || this.sourceCode.initialState == undefined) { - this.sourceCode.initialState = {} + if ( + this.sourceCode.initialState == null || + this.sourceCode.initialState == undefined + ) { + this.sourceCode.initialState = {}; } - this.state = this.state ? merge(copy(this.sourceCode.initialState), this.state) : copy(this.sourceCode.initialState); + this.state = this.state + ? merge(copy(this.sourceCode.initialState), this.state) + : copy(this.sourceCode.initialState); this.scenes = copy(this.sourceCode.scenes); - this.badges = new Badges(this.sourceCode.badges) + this.badges = new Badges(this.sourceCode.badges); + console.log({ prev: this.player.badges }); this.badges.addPreviouslyEarndBadges(this.player.badges); this.overlay = document.body.querySelector(".exp"); - this.overlay.addEventListener("click", e => { - d("Remove modal") - this.overlay.classList.remove("clickable") - animateCSS(this.overlay.firstChild, "fadeOutDownBig").then(evt => { - this.overlay.removeChild(this.overlay.firstChild); - // Hvorfor virker ikke this.currentSceneUI her? - window.document.querySelector(".footer").classList.remove("disabled"); - }); - }) + this.overlay.addEventListener("click", (e) => { + d("Remove modal"); + this.overlay.classList.remove("clickable"); + animateCSS(this.overlay.firstChild, "fadeOutDownBig").then( + (evt) => { + this.overlay.removeChild(this.overlay.firstChild); + // Hvorfor virker ikke this.currentSceneUI her? + window.document + .querySelector(".footer") + .classList.remove("disabled"); + } + ); + }); - this.loadScene(this.sourceCode.start, this.scenes) + this.loadScene(this.sourceCode.start, this.scenes); } loadScene(sceneId, scenes) { @@ -77,10 +87,12 @@ export default class Game { let sceneIsReset = false; //if (!this.currentSceneUI || this.currentSceneData.clearSceneHistory) { - d("Reset scene flow") + d("Reset scene flow"); sceneIsReset = true; this.clearScene(); - this.currentSceneUI = document.querySelector("#sceneTemplate").content.cloneNode(true); + this.currentSceneUI = document + .querySelector("#sceneTemplate") + .content.cloneNode(true); this.container.appendChild(this.currentSceneUI); this.currentSceneUI = this.container.querySelector("#holder"); /*} else { @@ -89,79 +101,93 @@ export default class Game { }*/ if (this.currentSceneData) { - d("construct scene") - this.addBookmark(this.sceneId, this.currentSceneUI.querySelector(".content")); - - this.applyStatChanges(this.currentSceneData.statechange); - this.applyHeaderImage(this.currentSceneData.headerImage, this.currentSceneUI); + d("construct scene"); + this.addBookmark( + this.sceneId, + this.currentSceneUI.querySelector(".content") + ); + + this.applyStateChanges(this.currentSceneData.statechange); + this.applyHeaderImage( + this.currentSceneData.headerImage, + this.currentSceneUI + ); this.applyHeader(this.currentSceneData.header, this.currentSceneUI); - this.applyContent(this.currentSceneData.content, this.currentSceneUI); - this.applyActions(this.currentSceneData.actions, this.currentSceneUI); - this.applyAuxiliaryContent(this.currentSceneData.auxiliaryContent, this.currentSceneUI) + this.applyContent( + this.currentSceneData.content, + this.currentSceneUI + ); + this.applyActions( + this.currentSceneData.actions, + this.currentSceneUI + ); + this.applyAuxiliaryContent( + this.currentSceneData.auxiliaryContent, + this.currentSceneUI + ); let newBadges = this.badges.findNewlyEarndBadges(this.state); + console.log({ state: this.state }); if (newBadges.length > 0) { - let overlay = document.getElementById("overlay"); - newBadges.forEach(badge => { + newBadges.forEach((badge) => { let display = document.createElement("img"); display.src = badge.img; + display.alt = badge.name; overlay.appendChild(display); this.player.badges.push(badge); - }); - this.player.save() - overlay.classList.remove("hidden") + this.player.save(); + overlay.classList.remove("hidden"); overlay.onclick = () => { - overlay.classList.add("hidden") + overlay.classList.add("hidden"); overlay.innerHTML = ""; - } - + }; } - } else { //TODO : skall vi ha falback for feil. ? } - - } - addBookmark(bookmarkID, container) { const bookmark = document.createElement("a"); - bookmark.href = `#${bookmarkID}` + bookmark.href = `#${bookmarkID}`; container.appendChild(bookmark); } disableAllButtonsIn(container) { const buttons = container.querySelector("button"); - buttons.forEach(bt => { - bt.setAttribute("disabled", "true") + buttons.forEach((bt) => { + bt.setAttribute("disabled", "true"); }); } - applyAuxiliaryContent(contnet, container) { + applyAuxiliaryContent(content, container) { const footer = container.querySelector(".footer"); - if (contnet && contnet.peek) { - const node = document.querySelector("#peekTemplate").content.cloneNode(true); + if (content && content.peek) { + const node = document + .querySelector("#peekTemplate") + .content.cloneNode(true); const img = node.querySelector("img"); - img.src = contnet.peek.img; - const msg = node.querySelector("#peekMessage") - msg.innerText = this.parseText(contnet.peek.text); - footer.appendChild(node) + img.src = content.peek.img; + const msg = node.querySelector("#peekMessage"); + msg.innerText = this.parseText(content.peek.text); + footer.appendChild(node); footer.addEventListener("click", (e) => { - footer.classList.add("disabled") - this.displayAuxiliaryContent(this.currentSceneData.auxiliaryContent.content) - }) + footer.classList.add("disabled"); + this.displayAuxiliaryContent( + this.currentSceneData.auxiliaryContent.content + ); + }); } } displayAuxiliaryContent(content) { - d("Display modal") + d("Display modal"); const container = document.body.querySelector(".exp"); const modal = document.createElement("div"); modal.classList.add("modal"); @@ -169,13 +195,12 @@ export default class Game { div.classList.add("content"); modal.appendChild(div); - - this.applyContent(content, modal) + this.applyContent(content, modal); container.appendChild(modal); - animateCSS(modal, "fadeInUpBig").then(e => { + animateCSS(modal, "fadeInUpBig").then((e) => { container.classList.add("clickable"); - }) + }); } applyHeaderImage(headerImage, container) { @@ -185,14 +210,14 @@ export default class Game { header.classList.add("headerWithBgImage"); } else { header.classList.remove("headerWithBgImage"); - header.style.backgroundImage = "" + header.style.backgroundImage = ""; } } applyHeader(content, container) { let header = container.querySelector("header > #headerContent"); if (content && content.length > 0) { - content.forEach(node => { + content.forEach((node) => { let p = this.createTextNode(node.text); header.appendChild(p); }); @@ -203,8 +228,8 @@ export default class Game { applyContent(content, container) { if (content) { - let contentUI = container.querySelector(".content") - content.forEach(element => { + let contentUI = container.querySelector(".content"); + content.forEach((element) => { if (validateConditions(element.conditions, this.state)) { let node = this.createContentNode(element); if (node) { @@ -217,7 +242,6 @@ export default class Game { animateCSS(node, "bounceInRight"); } }*/ - } } }); @@ -227,16 +251,19 @@ export default class Game { applyActions(actions, container) { if (actions) { let actionsUI = container.querySelector(".actions"); - actions.forEach(action => { + actions.forEach((action) => { if (validateConditions(action.conditions, this.state)) { let bt = document.createElement("button"); bt.innerText = this.parseText(action.description); bt.title = action.title; - bt.onclick = e => { + bt.onclick = (e) => { if (action.statechange) { - this.applyStatChanges(action.statechange); + this.applyStateChanges(action.statechange); } - this.loadScene(action.target || this.sceneId, this.scenes); + this.loadScene( + action.target || this.sceneId, + this.scenes + ); }; actionsUI.appendChild(bt); @@ -245,68 +272,87 @@ export default class Game { } } - applyStatChanges(changes) { + applyStateChanges(changes) { if (changes) { - changes.forEach(change => { + changes.forEach((change) => { switch (change.type) { - case "set": this.state[change.target] = change.value; + case "set": + this.state[change.target] = change.value; break; - case "dec": this.state[change.target] = (this.state[change.target] || 0) - change.value; + case "decr": + this.state[change.target] = + Number(this.state[change.target] || 0) - + Number(change.value); break; - case "inc": this.state[change.target] = (this.state[change.target] || 0) + change.value; + case "incr": + this.state[change.target] = + Number(this.state[change.target] || 0) + + Number(change.value); break; - - case "apend": this.state[change.target] = (this.state[change.target] || "") + change.value; + case "append": + this.state[change.target] = + (this.state[change.target] || "") + change.value; break; - case "remove": this.state[change.target] = (this.state[change.target] || 0).replace(change.value, ""); + case "remove": + this.state[change.target] = ( + this.state[change.target] || 0 + ).replace(change.value, ""); break; default: - d(`No such state opperation ${change.type}`) - + d(`Missing state operation ${change.type}`); } - }) + }); } } createContentNode(description) { let node = null; switch (description.type) { - case "monolog": node = this.createMonologNode(description); + case "monolog": + node = this.createMonologNode(description); break; - case "dialogue": node = this.createDialogueNode(description); + case "dialogue": + node = this.createDialogueNode(description); break; - case "text": node = this.createTextNode(description.text); + case "text": + node = this.createTextNode(description.text); break; - case "shout": node = this.createShoutNode(description.text); + case "shout": + node = this.createShoutNode(description.text); break; - case "shout": node = this.createTextNode(description.text); + case "shout": + node = this.createTextNode(description.text); break; - case "link": node = this.createLinkNode(description); + case "link": + node = this.createLinkNode(description); break; - case "img": node = this.createImageNode(description); + case "img": + node = this.createImageNode(description); break; - case "video": node = this.createVideoNode(description); + case "video": + node = this.createVideoNode(description); break; - default: d(`Typen ${description.type} is not a recognized type`); + default: + d(`Typen ${description.type} is not a recognized type`); } return node; } createMonologNode(description) { - - let node = document.querySelector("#monologTemplate").content.cloneNode(true); + let node = document + .querySelector("#monologTemplate") + .content.cloneNode(true); let portrait = node.querySelector(".portrait"); portrait.src = this.parseText(description.img); let name = node.querySelector(".actorName"); name.innerText = this.parseText(description.name); - let monolog = node.querySelector(".monologContent"); let p = this.createTextNode(description.text); monolog.appendChild(p); @@ -315,8 +361,9 @@ export default class Game { } createDialogueNode(description) { - - const node = document.querySelector("#dialogueTemplate").content.cloneNode(true); + const node = document + .querySelector("#dialogueTemplate") + .content.cloneNode(true); const header = node.querySelector("header"); const aling = description.layout || "right"; @@ -332,7 +379,6 @@ export default class Game { const name = node.querySelector("#actorName"); name.innerText = this.parseText(description.name); - const dioalouge = node.querySelector(".dialogueContent"); const p = this.createTextNode(description.text); dioalouge.appendChild(p); @@ -344,20 +390,23 @@ export default class Game { let a = document.createElement("a"); a.src = "#"; a.innerText = description.alt; - a.classList.add("videoLink") + a.classList.add("videoLink"); a.onclick = () => { let overlay = document.getElementById("overlay"); - overlay.innerHTML = "" + overlay.innerHTML = ""; let frame = document.createElement("iframe"); overlay.appendChild(frame); - frame.setAttribute("src", `https://www.youtube.com/embed/${description.src}`) - overlay.classList.remove("hidden") + frame.setAttribute( + "src", + `https://www.youtube.com/embed/${description.src}` + ); + overlay.classList.remove("hidden"); overlay.onclick = () => { - overlay.classList.add("hidden") + overlay.classList.add("hidden"); overlay.innerHTML = ""; - } - } + }; + }; return a; } @@ -373,7 +422,7 @@ export default class Game { let a = document.createElement("a"); a.href = this.parseText(description.url); a.target = "_blank"; - a.innerText = this.parseText(description.text) + a.innerText = this.parseText(description.text); return a; } @@ -394,7 +443,7 @@ export default class Game { const stateKeys = /{\$\S+}/gm; let matches = text.match(stateKeys); if (matches) { - matches.forEach(match => { + matches.forEach((match) => { let key = match.replace(/[{,\$,}]/g, "").trim(); let stateValue = this.state[key]; text = text.replace(match, stateValue); @@ -402,11 +451,11 @@ export default class Game { } return text; } - return ''; + return ""; } clearScene() { - this.container.innerHTML = "" + this.container.innerHTML = ""; } async load() { @@ -425,12 +474,11 @@ export default class Game { testGame(gameName, source, container, state) { this.reset(gameName, container, state); this.sourceCode = JSON.parse(source); - this.run() + this.run(); } findScene(sceneId, scenes) { - let scene = Object.entries(scenes).find(item => item[0] == sceneId) + let scene = Object.entries(scenes).find((item) => item[0] == sceneId); return scene ? scene[1] : null; } } - From 8ba38f215d434ea8450f0ea4458529f1d52dc2b8 Mon Sep 17 00:00:00 2001 From: Rolf Bjaanes Date: Tue, 10 Aug 2021 15:50:52 +0200 Subject: [PATCH 3/3] Add multipleChoice and numberInput --- editor/components/sectionView.html | 6 +- editor/index.html | 24 +- editor/js/views/sectionView.js | 492 +++++++++++++++++------------ game/components/profile.html | 13 +- game/css/base.css | 151 +++++++-- game/games/default.json | 34 +- game/js/actions.mjs | 105 ++++++ game/js/app.mjs | 87 +++-- game/js/badges.mjs | 22 +- game/js/game.mjs | 20 +- game/js/profile.mjs | 129 ++++---- 11 files changed, 712 insertions(+), 371 deletions(-) create mode 100644 game/js/actions.mjs diff --git a/editor/components/sectionView.html b/editor/components/sectionView.html index 68d1297..ffff364 100644 --- a/editor/components/sectionView.html +++ b/editor/components/sectionView.html @@ -45,7 +45,9 @@

- + + +
@@ -83,4 +85,4 @@

- \ No newline at end of file + diff --git a/editor/index.html b/editor/index.html index 7b8f561..5f938e7 100644 --- a/editor/index.html +++ b/editor/index.html @@ -126,17 +126,17 @@

- +
- - + +
@@ -153,11 +153,11 @@ - +
- - + +
@@ -178,4 +178,4 @@ - \ No newline at end of file + diff --git a/editor/js/views/sectionView.js b/editor/js/views/sectionView.js index 0804e00..c181366 100644 --- a/editor/js/views/sectionView.js +++ b/editor/js/views/sectionView.js @@ -1,34 +1,42 @@ -import { HTMLUtilityTools, HTMLSelectorTools } from '../uiExt.js' -import { readLocalBinaryFile, saveToLocalCache, getFromLocalCache } from '../utils.js' -import Suggestion from '../suggestion.js' +import { HTMLUtilityTools, HTMLSelectorTools } from "../uiExt.js"; +import { + readLocalBinaryFile, + saveToLocalCache, + getFromLocalCache, +} from "../utils.js"; +import Suggestion from "../suggestion.js"; export default class SectionView { - static SECTION_TYPES = { MONOLOG: "monolog", DIALOGUE: "dialogue", SHOUT: "shout", LINK: "link", - IMAGE: "img" - } + IMAGE: "img", + }; - static sourceView = null + static sourceView = null; constructor(title, source, container, delegates) { - this.title = title; this.source = source; this.container = container; - this.delegates = delegates - this.sectionID = window.crypto.getRandomValues(new Uint8Array(10)).join("-"); - - let template = HTMLUtilityTools.createInstanceOfTemplate("sectionViewTemplate", this.sectionID); - const anchor = this.container.firstElementChild.nextSibling || this.container.firstElementChild; - this.view = this.container.insertBefore(template, anchor) + this.delegates = delegates; + this.sectionID = window.crypto + .getRandomValues(new Uint8Array(10)) + .join("-"); + + let template = HTMLUtilityTools.createInstanceOfTemplate( + "sectionViewTemplate", + this.sectionID + ); + const anchor = + this.container.firstElementChild.nextSibling || + this.container.firstElementChild; + this.view = this.container.insertBefore(template, anchor); this.view = document.getElementById(this.sectionID); - this.setup() - + this.setup(); } setup() { @@ -39,248 +47,315 @@ export default class SectionView { this.setupActionsEdit(); this.setupStateEdit(); this.setupAuxEdit(); - this.setupDelete() + this.setupDelete(); } setupDelete() { - let deleteBT = this.view.querySelector("button[data-role=deleteSection]"); + let deleteBT = this.view.querySelector( + "button[data-role=deleteSection]" + ); deleteBT.onclick = async () => { delete this.source.scenes[this.title]; this.view.parentNode.removeChild(this.view); await this.delegates.onChange(); - } + }; } setupAuxEdit() { - let scene = this.source.scenes[this.title] - const contentView = this.view.querySelector("textarea[data-role=sceneExtended]"); + let scene = this.source.scenes[this.title]; + const contentView = this.view.querySelector( + "textarea[data-role=sceneExtended]" + ); let addExtended = this.view.querySelector("button[data-role=extended]"); - let addContent = this.view.querySelector("button[data-role=extendedContent]"); - let addContentVideo = this.view.querySelector("button[data-role=extendedContentVideo]"); + let addContent = this.view.querySelector( + "button[data-role=extendedContent]" + ); + let addContentVideo = this.view.querySelector( + "button[data-role=extendedContentVideo]" + ); const structures = { extended: { - "peek": { - "img": "images/xxxxxx.xxx", - "name": "xxxxx xxxx xx", - "text": "xxxx xxx xxxxxx xxxx xxx xxx " + peek: { + img: "images/xxxxxx.xxx", + name: "xxxxx xxxx xx", + text: "xxxx xxx xxxxxx xxxx xxx xxx ", }, - "content": [ - - ] + content: [], }, monolog: { type: "monolog", layout: "right", img: "images/xxxxxx.xxx", - name: "xxxxx xxxxx" + name: "xxxxx xxxxx", }, dialogue: { - "type": "dialogue", - "layout": "left x||x right", - "img": "images/xxxxx.xxx", - "name": "xxxxxx xxxx", - "text": "xxxxxxx xxxxx xxxx xxxxx" + type: "dialogue", + layout: "left x||x right", + img: "images/xxxxx.xxx", + name: "xxxxxx xxxx", + text: "xxxxxxx xxxxx xxxx xxxxx", }, text: { - "type": "text", - "text": "xxxxxxx xxxxx xxxx xxxxx" + type: "text", + text: "xxxxxxx xxxxx xxxx xxxxx", }, shout: { - "type": "shout", - "text": "xxxxxxx xxxxx xxxx xxxxx" + type: "shout", + text: "xxxxxxx xxxxx xxxx xxxxx", }, link: { - "type": "link", - "url": "https://xxxxxxx.xx/xxxx", - "text": "xxxxxxx xxxxx xxxx xxxxx" + type: "link", + url: "https://xxxxxxx.xx/xxxx", + text: "xxxxxxx xxxxx xxxx xxxxx", }, image: { - "type": "img", - "src": "images/xxxxxx.xxx", - "alt": "xxxxxxx xxxxx xxxx xxxxx" + type: "img", + src: "images/xxxxxx.xxx", + alt: "xxxxxxx xxxxx xxxx xxxxx", }, video: { - "type": "video", - "src": "xxxx Embed Code xxxx", - "alt": "xxxxxxx xxxxx xxxx xxxxx" - } + type: "video", + src: "xxxx Embed Code xxxx", + alt: "xxxxxxx xxxxx xxxx xxxxx", + }, }; - addExtended.onclick = async e => { + addExtended.onclick = async (e) => { try { - scene.auxiliaryContent = JSON.parse(JSON.stringify(structures.extended)) - contentView.value = JSON.stringify(scene.auxiliaryContent, null, 3); + scene.auxiliaryContent = JSON.parse( + JSON.stringify(structures.extended) + ); + contentView.value = JSON.stringify( + scene.auxiliaryContent, + null, + 3 + ); await this.delegates.onChange(); } catch (error) { console.log(error); ///TODO: Bedre tilbake melding om hva feilen er - this.delegates.onError("Extension contains errors that must be fixed before adding new content"); + this.delegates.onError( + "Extension contains errors that must be fixed before adding new content" + ); } - } + }; - addContent.onclick = async e => { + addContent.onclick = async (e) => { try { - let current = JSON.parse(contentView.value) + let current = JSON.parse(contentView.value); if (current.content == null || current.content == undefined) { current.content = []; } - current.content.push(JSON.parse(JSON.stringify(structures.dialogue))); + current.content.push( + JSON.parse(JSON.stringify(structures.dialogue)) + ); scene.auxiliaryContent = current; - contentView.value = JSON.stringify(scene.auxiliaryContent, null, 3); + contentView.value = JSON.stringify( + scene.auxiliaryContent, + null, + 3 + ); await this.delegates.onChange(); } catch (error) { console.log(error); ///TODO: Bedre tilbake melding om hva feilen er - this.delegates.onError("Extension contains errors that must be fixed before adding new content"); + this.delegates.onError( + "Extension contains errors that must be fixed before adding new content" + ); } - } + }; - addContentVideo.onclick = async e => { + addContentVideo.onclick = async (e) => { try { - let current = JSON.parse(contentView.value) + let current = JSON.parse(contentView.value); if (current.content == null || current.content == undefined) { current.content = []; } - current.content.push(JSON.parse(JSON.stringify(structures.video))); + current.content.push( + JSON.parse(JSON.stringify(structures.video)) + ); scene.auxiliaryContent = current; - contentView.value = JSON.stringify(scene.auxiliaryContent, null, 3); + contentView.value = JSON.stringify( + scene.auxiliaryContent, + null, + 3 + ); await this.delegates.onChange(); } catch (error) { console.log(error); ///TODO: Bedre tilbake melding om hva feilen er - this.delegates.onError("Extension contains errors that must be fixed before adding new content"); + this.delegates.onError( + "Extension contains errors that must be fixed before adding new content" + ); } - } - + }; if (scene.auxiliaryContent) { contentView.value = JSON.stringify(scene.auxiliaryContent, null, 3); } else { - contentView.value = "" + contentView.value = ""; } - contentView.onchange = async e => { + contentView.onchange = async (e) => { try { - scene.auxiliaryContent = JSON.parse(contentView.value) + scene.auxiliaryContent = JSON.parse(contentView.value); await this.delegates.onChange(); } catch (error) { console.log(error); ///TODO: Bedre tilbake melding om hva feilen er - this.delegates.onError("Extension contains errors that must be fixed before continuing"); + this.delegates.onError( + "Extension contains errors that must be fixed before continuing" + ); } - } - + }; } - - setupStateEdit() { - let scene = this.source.scenes[this.title] - const contentView = this.view.querySelector("textarea[data-role=sceneStateChanges]"); + let scene = this.source.scenes[this.title]; + const contentView = this.view.querySelector( + "textarea[data-role=sceneStateChanges]" + ); let addStatebt = this.view.querySelector("button[data-role=state]"); - addStatebt.onclick = async e => { + addStatebt.onclick = async (e) => { try { let state = await this.delegates.onAddNewState(); - if (scene.statechange == null || scene.statechange == undefined) { + if ( + scene.statechange == null || + scene.statechange == undefined + ) { scene.statechange = []; } - scene.statechange.push({ type: state.type, target: state.key, value: state.value }) + scene.statechange.push({ + type: state.type, + target: state.key, + value: state.value, + }); contentView.value = JSON.stringify(scene.statechange, null, 3); await this.delegates.onChange(); } catch (error) { console.log(error); ///TODO: Bedre tilbake melding om hva feilen er - this.delegates.onError("Actions contains errors that must be fixed before adding new content"); + this.delegates.onError( + "Actions contains errors that must be fixed before adding new content" + ); } - } - - + }; if (scene.statechange && scene.statechange.length > 0) { contentView.value = JSON.stringify(scene.statechange, null, 3); } else { - contentView.value = "" + contentView.value = ""; } - contentView.onchange = async e => { + contentView.onchange = async (e) => { try { - scene.statechange = JSON.parse(contentView.value) + scene.statechange = JSON.parse(contentView.value); await this.delegates.onChange(); } catch (error) { console.log(error); ///TODO: Bedre tilbake melding om hva feilen er - this.delegates.onError("Actions contains errors that must be fixed before continuing"); + this.delegates.onError( + "Actions contains errors that must be fixed before continuing" + ); } - } - + }; } - setupActionsEdit() { - let scene = this.source.scenes[this.title] - const contentView = this.view.querySelector("textarea[data-role=sceneActions]"); + let scene = this.source.scenes[this.title]; + const contentView = this.view.querySelector( + "textarea[data-role=sceneActions]" + ); - let addActionbt = this.view.querySelector("button[data-role=action]"); + let addActionBtns = this.view.querySelectorAll( + "button[data-role=action]" + ); const structures = { - action: { - "type": "button", - "title": "xxxx", - "description": "xxx xxxxxx xxxxx xxxxx xxx", - "target": "xxxxxxxxxx", - "statechange": [] - } + button: { + type: "button", + title: "Add title", + description: "Add description", + target: "Section target", + statechange: [], + }, + multipleChoice: { + type: "multipleChoice", + title: "Add title", + description: "Add description of action", + target: "Section target if correct", + optionalTarget: "Section target if incorrect", + statechange: [], + choices: ["Bad answer", "Right answer"], + answers: ["Right answer"], + }, + numberInput: { + type: "numberInput", + title: "Add title", + description: "Add description", + target: "Section target if correct", + optionalTarget: "Section target if incorrect", + statechange: [], + answer: 123, + }, }; - [addActionbt].forEach(bt => { - bt.onclick = async e => { + addActionBtns.forEach((bt) => { + bt.onclick = async (e) => { try { - let currentScene = contentView.value ? JSON.parse(contentView.value) : []; - let key = e.currentTarget.getAttribute("data-role"); - let newStruct = { ...structures[key] } + let currentScene = contentView.value + ? JSON.parse(contentView.value) + : []; + let key = e.currentTarget.getAttribute("data-action"); + let newStruct = { ...structures[key] }; currentScene.push(newStruct); contentView.value = JSON.stringify(currentScene, null, 3); - scene.actions = [...currentScene] + scene.actions = [...currentScene]; await this.delegates.onChange(); } catch (error) { console.log(error); ///TODO: Bedre tilbake melding om hva feilen er - this.delegates.onError("Actions contains errors that must be fixed before adding new content"); + this.delegates.onError( + "Actions contains errors that must be fixed before adding new content" + ); } - } + }; }); - if (scene.actions && scene.actions.length > 0) { contentView.value = JSON.stringify(scene.actions, null, 3); } else { - contentView.value = "" + contentView.value = ""; } - contentView.onchange = async e => { + contentView.onchange = async (e) => { try { - scene.actions = JSON.parse(contentView.value) + scene.actions = JSON.parse(contentView.value); await this.delegates.onChange(); } catch (error) { console.log(error); ///TODO: Bedre tilbake melding om hva feilen er - this.delegates.onError("Actions contains errors that must be fixed before continuing"); + this.delegates.onError( + "Actions contains errors that must be fixed before continuing" + ); } - } - + }; } - setupContentEdit() { - let scene = this.source.scenes[this.title] - const contentView = this.view.querySelector("textarea[data-role=sceneContent]"); + let scene = this.source.scenes[this.title]; + const contentView = this.view.querySelector( + "textarea[data-role=sceneContent]" + ); let addMonologbt = this.view.querySelector("button[data-role=monolog]"); - let addDialoguebt = this.view.querySelector("button[data-role=dialogue]"); + let addDialoguebt = this.view.querySelector( + "button[data-role=dialogue]" + ); let addTextbt = this.view.querySelector("button[data-role=text]"); let addShoutbt = this.view.querySelector("button[data-role=shout]"); let addLinkbt = this.view.querySelector("button[data-role=link]"); @@ -292,92 +367,109 @@ export default class SectionView { type: "monolog", layout: "right", img: "images/xxxxxx.xxx", - name: "xxxxx xxxxx" + name: "Character Name", }, dialogue: { - "type": "dialogue", - "layout": "left x||x right", - "img": "images/xxxxx.xxx", - "name": "xxxxxx xxxx", - "text": "xxxxxxx xxxxx xxxx xxxxx" + type: "dialogue", + layout: "left x||x right", + img: "images/xxxxx.xxx", + name: "Character Name", + text: "xxxxxxx xxxxx xxxx xxxxx", }, text: { - "type": "text", - "text": "xxxxxxx xxxxx xxxx xxxxx" + type: "text", + text: "xxxxxxx xxxxx xxxx xxxxx", }, shout: { - "type": "shout", - "text": "xxxxxxx xxxxx xxxx xxxxx" + type: "shout", + text: "xxxxxxx xxxxx xxxx xxxxx", }, link: { - "type": "link", - "url": "https://xxxxxxx.xx/xxxx", - "text": "xxxxxxx xxxxx xxxx xxxxx" + type: "link", + url: "https://xxxxxxx.xx/xxxx", + text: "xxxxxxx xxxxx xxxx xxxxx", }, image: { - "type": "img", - "src": "images/xxxxxx.xxx", - "alt": "xxxxxxx xxxxx xxxx xxxxx" + type: "img", + src: "images/xxxxxx.xxx", + alt: "xxxxxxx xxxxx xxxx xxxxx", }, video: { - "type": "video", - "src": "xxxx Embed Code xxxx", - "alt": "xxxxxxx xxxxx xxxx xxxxx" - } + type: "video", + src: "xxxx Embed Code xxxx", + alt: "xxxxxxx xxxxx xxxx xxxxx", + }, }; - [addMonologbt, addDialoguebt, addTextbt, addShoutbt, addLinkbt, addImagebt, addVideobt].forEach(bt => { - bt.onclick = async e => { + [ + addMonologbt, + addDialoguebt, + addTextbt, + addShoutbt, + addLinkbt, + addImagebt, + addVideobt, + ].forEach((bt) => { + bt.onclick = async (e) => { try { - let currentScene = contentView.value ? JSON.parse(contentView.value) : []; + let currentScene = contentView.value + ? JSON.parse(contentView.value) + : []; let key = e.currentTarget.getAttribute("data-role"); - let newStruct = { ...structures[key] } + let newStruct = { ...structures[key] }; currentScene.push(newStruct); contentView.value = JSON.stringify(currentScene, null, 3); - scene.content = [...currentScene] + scene.content = [...currentScene]; await this.delegates.onChange(); } catch (error) { console.log(error); ///TODO: Bedre tilbake melding om hva feilen er - this.delegates.onError("Scene contains errors that must be fixed before adding new content"); + this.delegates.onError( + "Scene contains errors that must be fixed before adding new content" + ); } - } + }; }); - if (scene.content && scene.content.length > 0) { contentView.value = JSON.stringify(scene.content, null, 3); } else { - contentView.value = "" + contentView.value = ""; } - contentView.onchange = async e => { + contentView.onchange = async (e) => { try { - scene.content = JSON.parse(contentView.value) + scene.content = JSON.parse(contentView.value); await this.delegates.onChange(); } catch (error) { console.log(error); ///TODO: Bedre tilbake melding om hva feilen er - this.delegates.onError("Scene contains errors that must be fixed before continuing"); + this.delegates.onError( + "Scene contains errors that must be fixed before continuing" + ); } - } - + }; } setupHeaderEdit() { - - let scene = this.source.scenes[this.title] + let scene = this.source.scenes[this.title]; const imagePicker = this.view.querySelector("#header-image-selector"); - const description = this.view.querySelector("input[data-role=header-text]"); - const imageDisplay = this.view.querySelector("img[data-role=image-display]"); + const description = this.view.querySelector( + "input[data-role=header-text]" + ); + const imageDisplay = this.view.querySelector( + "img[data-role=image-display]" + ); description.onchange = async (e) => { - scene.header = [{ - "type": "text", - "text": description.value - }] + scene.header = [ + { + type: "text", + text: description.value, + }, + ]; await this.delegates.onChange(); - } + }; if (scene.header && scene.header.length > 0) { description.value = scene.header[0].text; @@ -386,10 +478,10 @@ export default class SectionView { imagePicker.setAttribute("list", Suggestion.IMAGES); imagePicker.onchange = async (e) => { scene.headerImage = scene.headerImage || {}; - scene.headerImage.src = imagePicker.value - scene.headerImage.alt = "decorative header" ///TODO: UI for å sette alt tekst. + scene.headerImage.src = imagePicker.value; + scene.headerImage.alt = "decorative header"; ///TODO: UI for å sette alt tekst. await this.delegates.onChange(); - } + }; /*imagePicker.onchange = async (e) => { const file = e.target.files[0]; @@ -424,49 +516,58 @@ export default class SectionView { } catch (error) { console.log(error) }*/ - - - } setupDisplaySource(title, source) { - if (SectionView.sourceView == null) { - let template = HTMLUtilityTools.createInstanceOfTemplate("sourceEditModal",); + let template = + HTMLUtilityTools.createInstanceOfTemplate("sourceEditModal"); SectionView.sourceView = document.body.appendChild(template); let addBt = document.querySelector("#saveSourcecodeChange"); let cancelBt = document.querySelector("#cancelSourceCodeEdit"); cancelBt.onclick = () => { - halfmoon.toggleModal('source-view'); - } + halfmoon.toggleModal("source-view"); + }; addBt.onclick = async () => { - - this.source.scenes[this.title] = JSON.parse(sourceCodeView.innerText, null, 3); ///TODO: valider at json parse er vellyket. - halfmoon.toggleModal('source-view'); + this.source.scenes[this.title] = JSON.parse( + sourceCodeView.innerText, + null, + 3 + ); ///TODO: valider at json parse er vellyket. + halfmoon.toggleModal("source-view"); await this.delegates.onChange(); this.setup(); - } + }; } - let sourceCodeView = document.querySelector("#sourceCode") + let sourceCodeView = document.querySelector("#sourceCode"); - const soruceViewBT = this.view.querySelector("button[data-role=displaySource]"); + const soruceViewBT = this.view.querySelector( + "button[data-role=displaySource]" + ); soruceViewBT.onclick = () => { - sourceCodeView.innerText = JSON.stringify(source.scenes[this.title], null, " "); - halfmoon.toggleModal('source-view'); - } + sourceCodeView.innerText = JSON.stringify( + source.scenes[this.title], + null, + " " + ); + halfmoon.toggleModal("source-view"); + }; } setupTitleEdit(source) { - let titleTxt = this.view.querySelector("#sectionName"); const editBT = this.view.querySelector("button[data-role=edit]"); const saveBT = this.view.querySelector("button[data-role=save]"); const cancelBT = this.view.querySelector("button[data-role=cancel]"); - HTMLUtilityTools.setAttribute("data-target", "sectionName", [editBT, saveBT, cancelBT]); + HTMLUtilityTools.setAttribute("data-target", "sectionName", [ + editBT, + saveBT, + cancelBT, + ]); titleTxt.innerText = this.title; let orgTitle = this.title; @@ -476,8 +577,8 @@ export default class SectionView { let target = this.view.querySelector(`#${targetId}`); HTMLUtilityTools.show([saveBT, cancelBT]); HTMLUtilityTools.hide([editBT]); - target.setAttribute("contenteditable", true) - } + target.setAttribute("contenteditable", true); + }; saveBT.onclick = async (e) => { let targetId = e.currentTarget.getAttribute("data-target"); @@ -487,7 +588,9 @@ export default class SectionView { let newTitle = target.innerText.replaceAll(" ", ""); if (newTitle !== orgTitle) { - this.source.scenes[newTitle] = JSON.parse(JSON.stringify(source.scenes[orgTitle])); + this.source.scenes[newTitle] = JSON.parse( + JSON.stringify(source.scenes[orgTitle]) + ); delete this.source.scenes[orgTitle]; orgTitle = newTitle; this.title = newTitle; @@ -495,8 +598,8 @@ export default class SectionView { await this.delegates.onChange(); } - target.removeAttribute("contenteditable") - } + target.removeAttribute("contenteditable"); + }; cancelBT.onclick = (e) => { let targetId = e.currentTarget.getAttribute("data-target"); @@ -504,10 +607,7 @@ export default class SectionView { HTMLUtilityTools.hide([saveBT, cancelBT]); HTMLUtilityTools.show([editBT]); target.innerText = orgTitle; - target.removeAttribute("contenteditable") - } + target.removeAttribute("contenteditable"); + }; } - - - -} \ No newline at end of file +} diff --git a/game/components/profile.html b/game/components/profile.html index 9fb6239..c507baf 100644 --- a/game/components/profile.html +++ b/game/components/profile.html @@ -1,15 +1,16 @@ \ No newline at end of file + diff --git a/game/css/base.css b/game/css/base.css index ea057d6..f51119b 100644 --- a/game/css/base.css +++ b/game/css/base.css @@ -1,19 +1,19 @@ :root { - --font-family: 'Roboto', sans-serif; + --font-family: "Roboto", sans-serif; --bacground-color: #262626; - --button-background-color: #74B194; - --button-text-color: #FFFFFF; + --button-background-color: #74b194; + --button-text-color: #ffffff; --footerBackgroundColor: #365555; - --headerBackgroundColor: #F5F5F5; - --text-color: #FFFFFF; - --peek-text-color: #FFFFFF; + --headerBackgroundColor: #f5f5f5; + --text-color: #ffffff; + --peek-text-color: #ffffff; --headerFontColor: #020202; --footerHeight: 5vh; --sceenHeight: calc(100vh - var(--footerHeight)); --baseWidth: 375px; --headerTextBoxMargin: 10px; --headerTextBoxPadding: 10px; - --baseGrow: calc(var(--baseWidth)/10); + --baseGrow: calc(var(--baseWidth) / 10); } .left { @@ -31,7 +31,7 @@ } .hidden { - display: none!important; + display: none !important; } .overlay { @@ -48,11 +48,18 @@ align-items: flex-start; } -.overlay>iframe { +.overlay > iframe { width: var(--baseWidth); height: var(--sceenHeight); } +.numberInput { + padding: 0.3rem; + margin: 0.2rem; + width: 90%; + border-radius: 35px; +} + .dialogue { display: flex; flex-wrap: nowrap; @@ -75,15 +82,16 @@ font-size: 16px; line-height: 19px; text-align: left; - color: #FFFFFF; + color: #ffffff; padding: 1em; } -.dialogueContent>p, .dialogue>header>p { +.dialogueContent > p, +.dialogue > header > p { margin-block: 0em; } -.dialogue>header { +.dialogue > header { font-family: Lato; font-style: normal; font-weight: normal; @@ -93,7 +101,7 @@ color: #929292; } -.dialogue>header>.portrait { +.dialogue > header > .portrait { width: 34px; height: 34px; } @@ -103,21 +111,22 @@ width: 100%; } -.monolog>header { +.monolog > header { position: relative; text-align: center; } -.monolog>header>.portrait { +.monolog > header > .portrait { width: 117px; height: 117px; } -.monolog>header>* { +.monolog > header > * { margin-block-start: 0; } -html, body { +html, +body { width: 100%; height: 100%; margin: 0; @@ -142,7 +151,8 @@ body { z-index: 1000; } -.exp, .wrapper { +.exp, +.wrapper { width: 100%; height: 100%; display: flex; @@ -164,7 +174,7 @@ body { overflow-x: hidden; } -.scene>header { +.scene > header { background-position: center; background-repeat: no-repeat; background-size: cover; @@ -173,7 +183,7 @@ body { z-index: 100; } -header>#headerContent { +header > #headerContent { background-color: var(--headerBackgroundColor); color: black; margin: var(--headerTextBoxMargin); @@ -207,11 +217,11 @@ header>#headerContent { } .clickable { - pointer-events: all!important; + pointer-events: all !important; } .disabled { - pointer-events: none!important; + pointer-events: none !important; } .modal { @@ -225,7 +235,16 @@ header>#headerContent { pointer-events: all; } -.s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10 { +.s1, +.s2, +.s3, +.s4, +.s5, +.s6, +.s7, +.s8, +.s9, +.s10 { flex-basis: var(--baseGrow); } @@ -270,7 +289,9 @@ header>#headerContent { } @media screen and (max-width: 1000px) { - .scene, .footer, .sceneImage { + .scene, + .footer, + .sceneImage { width: 100%; } } @@ -283,18 +304,18 @@ header>#headerContent { z-index: 1; } -.content>* { +.content > * { margin-left: 10px; margin-right: 10px; overflow-x: hidden; } -.content>p { +.content > p { line-height: 19px; font-weight: 400; } -.content>h1 { +.content > h1 { text-align: center; width: 100%; } @@ -338,7 +359,7 @@ button:hover { color: var(--text-color); border: none; text-decoration: none; - padding: 1.0rem; + padding: 1rem; margin: 0.2rem; border-radius: 18px; cursor: pointer; @@ -348,6 +369,73 @@ button:hover { filter: brightness(130%); } +.multipleChoice { + display: flex; + flex: 1; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.multipleChoice > div { + width: 90%; +} + +.multipleChoice label { + display: flex; + flex: 1; + flex-direction: row; + color: var(--text-color); + justify-content: space-between; + align-items: center; + cursor: pointer; + padding: 0.3rem; + padding-left: 18px; + padding-right: 18px; + margin: 0.2rem 0; + border: 1px solid #f30393; + border-radius: 18px; + min-height: 26px; + line-height: 26px; +} + +.multipleChoice label .checkbox { + border: 1px solid #f30393; + width: 20px; + height: 20px; + border-radius: 20px; +} + +.multipleChoice input:checked + label { + background-color: #83004e; +} + +.multipleChoice input:checked + label > .checkbox { + color: #000; + background-color: #fff; +} + +.multipleChoice input:checked + label > .checkbox:before { + display: flex; + content: "✓"; + color: #000; + justify-content: center; + line-height: 22px; + font-size: 18px; +} + +.multipleChoice input { + position: absolute; + left: 0; + top: 0; + color: var(--text-color); + border: none; + text-decoration: none; + cursor: pointer; + width: 0; + height: 0; +} + .break { flex-basis: 100%; height: 0; @@ -355,4 +443,9 @@ button:hover { .badge { width: 33%; -} \ No newline at end of file + margin: 1rem; +} + +#playerAvatar { + margin: 1rem; +} diff --git a/game/games/default.json b/game/games/default.json index a082141..dd27d14 100644 --- a/game/games/default.json +++ b/game/games/default.json @@ -56,6 +56,26 @@ "value": 0 } ] + }, + { + "type": "numberInput", + "target": "TeTime", + "optionalTarget": "NoTeTime", + "answers": [123], + "description": "Fortsett" + }, + { + "type": "multipleChoice", + "target": "TeTime", + "optionalTarget": "NoTeTime", + "answers": ["Riktig svar 1", "Riktig svar 2"], + "choices": [ + "Feil svar 1", + "Feil svar 2", + "Riktig svar 1", + "Riktig svar 2" + ], + "description": "Fortsett" } ], "auxiliaryContent": { @@ -221,7 +241,7 @@ "target": "WhiskeyTime3", "statechange": [ { - "type": "inc", + "type": "incr", "target": "drunk", "value": 1 } @@ -292,8 +312,14 @@ "type": "button", "title": "stop", "description": "Sett i gang med forskningen", - "target": "forskning1", - "statechange": [] + "target": "Velkommen", + "statechange": [ + { + "type": "set", + "target": "levelComplete", + "value": true + } + ] } ] } @@ -326,4 +352,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/game/js/actions.mjs b/game/js/actions.mjs new file mode 100644 index 0000000..28d25f4 --- /dev/null +++ b/game/js/actions.mjs @@ -0,0 +1,105 @@ +const Actions = { + button: (action, context) => { + let btn = document.createElement("button"); + btn.innerText = context.parseText(action.description); + btn.title = action.title; + btn.onclick = (e) => { + if (action.statechange) { + context.applyStateChanges(action.statechange); + } + this.loadScene(action.target || context.sceneId, context.scenes); + }; + return btn; + }, + numberInput: (action, context) => { + const correctAnswer = action.answer; + let form = document.createElement("form"); + let input = document.createElement("input"); + input.setAttribute("type", "number"); + input.setAttribute("placeholder", "Enter a number"); + input.classList.add("numberInput"); + + let btn = document.createElement("button"); + + btn.title = action.title; + btn.innerText = context.parseText(action.description); + btn.onclick = (e) => { + if (action.statechange) { + context.applyStateChanges(action.statechange); + } + + const currentAnswer = Number(form.querySelector("input").value); + + console.log({ currentAnswer }); + + const isCorrect = + Number(correctAnswer) && + Number(correctAnswer) === currentAnswer; + + const nextScene = + action.target && action.optionalTarget && isCorrect + ? action.target + : action.optionalTarget; + + context.loadScene(nextScene || context.sceneId, context.scenes); + }; + + form.appendChild(input); + form.appendChild(btn); + + return form; + }, + multipleChoice: (action, context) => { + const correctAnswers = JSON.stringify(action.answers.sort()); + let form = document.createElement("form"); + + form.classList.add("multipleChoice"); + + action.choices.map((choice, idx) => { + let container = document.createElement("div"); + let input = document.createElement("input"); + let label = document.createElement("label"); + let checkbox = document.createElement("span"); + checkbox.classList.add("checkbox"); + input.setAttribute("type", "checkbox"); + input.setAttribute("id", ["choice", idx].join("-")); + input.setAttribute("value", choice); + label.setAttribute("for", ["choice", idx].join("-")); + label.innerText = choice; + label.appendChild(checkbox); + container.appendChild(input); + container.appendChild(label); + form.appendChild(container); + }); + + let btn = document.createElement("button"); + btn.title = action.title; + btn.innerText = context.parseText(action.description); + btn.onclick = (e) => { + if (action.statechange) { + context.applyStateChanges(action.statechange); + } + + const currentAnswers = [ + ...document.querySelectorAll(".multipleChoice input:checked"), + ].map((answer) => { + return answer.value; + }); + + const isCorrect = + correctAnswers === JSON.stringify(currentAnswers.sort()); + + const nextScene = + action.target && action.optionalTarget && isCorrect + ? action.target + : action.optionalTarget; + + context.loadScene(nextScene || context.sceneId, context.scenes); + }; + + form.appendChild(btn); + return form; + }, +}; + +export default Actions; diff --git a/game/js/app.mjs b/game/js/app.mjs index b71faf6..25e1def 100644 --- a/game/js/app.mjs +++ b/game/js/app.mjs @@ -1,12 +1,16 @@ - -import Game from './game.mjs' -import { ProfileBuilder, Profile } from './profile.mjs' -import d from './debug.mjs' -import { copy, merge, validateConditions, animateCSS, Storage } from './util.mjs' -import { HTMLUtilityTools } from './uiExt.js' +import Game from "./game.mjs"; +import { ProfileBuilder, Profile } from "./profile.mjs"; +import d from "./debug.mjs"; +import { + copy, + merge, + validateConditions, + animateCSS, + Storage, +} from "./util.mjs"; +import { HTMLUtilityTools } from "./uiExt.js"; class Application { - constructor() { this.game = null; this.player = null; @@ -14,15 +18,17 @@ class Application { async loadGame(gameID, gamePresetState) { gameID ||= "default"; - this.game = new Game(gameID, container, gamePresetState) - await this.game.load() + this.game = new Game(gameID, container, gamePresetState); + await this.game.load(); } async playerProfile() { - let p = Profile.storedProfile() + let p = Profile.storedProfile(); if (p === null) { - let profileData = await (new ProfileBuilder(container)).queryProfile(); - p = new Profile(profileData) + let profileData = await new ProfileBuilder( + container + ).queryProfile(); + p = new Profile(profileData); } else { await p.show(container); } @@ -32,34 +38,36 @@ class Application { testGame(source) { if (!this.game) { - this.game = new Game("EditorGame", container, null) + this.game = new Game("EditorGame", container, null); } this.game.testGame("EditorGame", source, container, null); } async registerServiceWorker() { const urlParams = new URLSearchParams(window.location.search); - const reset = urlParams.get('resetPWA'); + const reset = urlParams.get("resetPWA"); if (reset) { - d("Deliting PWA cache") + d("Deliting PWA cache"); caches.delete("metodeSpillPWA"); - Storage.clear() + Storage.clear(); } - if ('serviceWorker' in navigator) { - await navigator.serviceWorker.register('./js/sw.js').catch(err => console.log(err)); + if ("serviceWorker" in navigator) { + await navigator.serviceWorker + .register("./js/sw.js") + .catch((err) => console.log(err)); } - }; + } } const app = new Application(); const container = document.createElement("div"); container.classList.add("wrapper"); -document.body.appendChild(container) +document.body.appendChild(container); window.onload = async () => { - await app.registerServiceWorker() + await app.registerServiceWorker(); try { await HTMLUtilityTools.loadAndEmbedTemplate("components/dialogue.html"); @@ -67,22 +75,31 @@ window.onload = async () => { await HTMLUtilityTools.loadAndEmbedTemplate("components/peek.html"); await HTMLUtilityTools.loadAndEmbedTemplate("components/profile.html"); await HTMLUtilityTools.loadAndEmbedTemplate("components/scene.html"); - await HTMLUtilityTools.loadAndEmbedTemplate("components/createProfile.html"); - await HTMLUtilityTools.loadAndEmbedTemplate("components/avatarselection.html"); + await HTMLUtilityTools.loadAndEmbedTemplate( + "components/createProfile.html" + ); + await HTMLUtilityTools.loadAndEmbedTemplate( + "components/avatarselection.html" + ); } catch (error) { console.error(error); } - await app.playerProfile() - await app.loadGame(window.location.hash, { playerName: app.player.name, playerAvatar: app.player.avatar }); + await app.playerProfile(); + await app.loadGame(window.location.hash, { + playerName: app.player.name, + playerAvatar: app.player.avatar, + }); app.game.run(); -} - -window.addEventListener("message", async (event) => { - let newGame = JSON.stringify(event.data); - await app.playerProfile() - d("Runing sync game") - app.testGame(newGame); - -}, false); - +}; + +window.addEventListener( + "message", + async (event) => { + let newGame = JSON.stringify(event.data); + await app.playerProfile(); + d("Running sync game"); + app.testGame(newGame); + }, + false +); diff --git a/game/js/badges.mjs b/game/js/badges.mjs index 7cf59e3..020f2fc 100644 --- a/game/js/badges.mjs +++ b/game/js/badges.mjs @@ -1,9 +1,8 @@ -import { copy, validateConditions } from './util.mjs' +import { copy, validateConditions } from "./util.mjs" export default class BadgeManager { - constructor(badges) { - this.badges = badges; + this.badges = badges || [] this.collected = [] } @@ -12,17 +11,16 @@ export default class BadgeManager { } findNewlyEarndBadges(state) { - return this.badges.filter(badge => { - let collect = false; - if (!this.collected.find(c => badge.name == c.name)) { + return this.badges.filter((badge) => { + let collect = false + if (!this.collected.find((c) => badge.name == c.name)) { if (validateConditions(badge.conditions, state)) { - this.collected.push(copy(badge)); - collect = true; + this.collected.push(copy(badge)) + collect = true } } - return collect;; - }); + return collect + }) } +} - -} \ No newline at end of file diff --git a/game/js/game.mjs b/game/js/game.mjs index e9f4b6b..26afd5d 100644 --- a/game/js/game.mjs +++ b/game/js/game.mjs @@ -1,6 +1,7 @@ import d from "./debug.mjs"; import ErrorMessages from "./l8n.mjs"; import { copy, merge, validateConditions, animateCSS } from "./util.mjs"; +import Actions from "./actions.mjs"; import Badges from "./badges.mjs"; import { Profile } from "./profile.mjs"; @@ -232,6 +233,7 @@ export default class Game { content.forEach((element) => { if (validateConditions(element.conditions, this.state)) { let node = this.createContentNode(element); + console.log({ node }); if (node) { node = contentUI.appendChild(node); @@ -253,20 +255,8 @@ export default class Game { let actionsUI = container.querySelector(".actions"); actions.forEach((action) => { if (validateConditions(action.conditions, this.state)) { - let bt = document.createElement("button"); - bt.innerText = this.parseText(action.description); - bt.title = action.title; - bt.onclick = (e) => { - if (action.statechange) { - this.applyStateChanges(action.statechange); - } - this.loadScene( - action.target || this.sceneId, - this.scenes - ); - }; - - actionsUI.appendChild(bt); + const createActionNode = Actions[action.type]; + actionsUI.appendChild(createActionNode(action, this)); } }); } @@ -338,7 +328,7 @@ export default class Game { node = this.createVideoNode(description); break; default: - d(`Typen ${description.type} is not a recognized type`); + d(`${description.type} is not a recognized content type`); } return node; } diff --git a/game/js/profile.mjs b/game/js/profile.mjs index 2c6837c..7a7d548 100644 --- a/game/js/profile.mjs +++ b/game/js/profile.mjs @@ -1,97 +1,102 @@ -import { HTMLUtilityTools } from './uiExt.js' -import { Storage } from './util.mjs' +import { HTMLUtilityTools } from "./uiExt.js" +import { Storage } from "./util.mjs" const PROFILE_KEY = "player.profile.storage.key" - class Profile { - constructor(profile) { + this.name = profile.name || null + this.avatar = profile.avatar || "images/avatar/avatar_1.svg" + this.badges = profile.badges || [] - this.name = profile.name || null; - this.avatar = profile.avatar || null; - this.badges = profile.badges || []; - - this.save(); + this.save() } async show(container) { container.innerHTML = "" - const view = HTMLUtilityTools.createInstanceOfTemplate("playerProfile"); - container.appendChild(view); + const view = HTMLUtilityTools.createInstanceOfTemplate("playerProfile") + container.appendChild(view) let avatar = document.getElementById("playerAvatar") - avatar.src = this.avatar; + avatar.src = this.avatar let name = document.getElementById("playerName") - name.innerText = this.name; + name.innerText = this.name let badges = document.getElementById("badgesListing") - this.badges.forEach(badge => { - const img = document.createElement("img"); - img.src = badge.img; - img.alt = badge.name; + this.badges.forEach((badge) => { + const img = document.createElement("img") + img.src = badge.img + img.alt = badge.name img.classList.add("badge") - badges.appendChild(img); - }); + badges.appendChild(img) + }) return new Promise((res, reject) => { - const bt = document.getElementById("continueBt"); - bt.addEventListener("click", () => { - console.log("Exiting profile view") - res(this) - }, { once: true }) - }); + const bt = document.getElementById("continueBt") + bt.addEventListener( + "click", + () => { + console.log("Exiting profile view") + res(this) + }, + { once: true } + ) + }) } save() { - Storage.save(PROFILE_KEY, JSON.stringify({ name: this.name, avatar: this.avatar, badges: this.badges })); + Storage.save( + PROFILE_KEY, + JSON.stringify({ + name: this.name, + avatar: this.avatar, + badges: this.badges, + }) + ) } static storedProfile() { - let profile = JSON.parse(Storage.retrive(PROFILE_KEY)); + let profile = JSON.parse(Storage.retrive(PROFILE_KEY)) if (profile) { - return new Profile(profile); + return new Profile(profile) } - return null; + return null } - } - - class ProfileBuilder { - constructor(container) { - this.container = container; - this.view = HTMLUtilityTools.createInstanceOfTemplate("createPlayerProfile"); - container.appendChild(this.view); - + this.container = container + this.view = HTMLUtilityTools.createInstanceOfTemplate( + "createPlayerProfile" + ) + container.appendChild(this.view) let selectedAvatar = document.getElementById("avatarImage") selectedAvatar.onclick = (e) => { - let profile = document.getElementById("holder"); + let profile = document.getElementById("holder") profile.classList.add("hidden") - let selectAvatar = HTMLUtilityTools.createInstanceOfTemplate("avatarSelectionTemplate"); + let selectAvatar = HTMLUtilityTools.createInstanceOfTemplate( + "avatarSelectionTemplate" + ) - container.appendChild(selectAvatar); + container.appendChild(selectAvatar) let images = document.querySelectorAll(".badge") - images.forEach(img => { + images.forEach((img) => { img.onclick = (e) => { - selectedAvatar.src = e.target.src; - let avatars = document.getElementById("avatarSelector"); - let storage = document.getElementById("profileAvatar"); - storage.value = e.target.dataset.img; - avatars.parentElement.removeChild(avatars); - profile.classList.remove("hidden"); + selectedAvatar.src = e.target.src + let avatars = document.getElementById("avatarSelector") + let storage = document.getElementById("profileAvatar") + storage.value = e.target.dataset.img + avatars.parentElement.removeChild(avatars) + profile.classList.remove("hidden") } }) - } - } async queryProfile() { @@ -99,20 +104,24 @@ class ProfileBuilder { const profile = JSON.parse(Storage.retrive(PROFILE_KEY)) if (profile) { - res(profile); + res(profile) } else { - const bt = document.getElementById("saveProfileBt"); - bt.addEventListener("click", () => { - let name = document.getElementById("profileName").value - let avatar = document.getElementById("profileAvatar").value - const p = { name: name, avatar: avatar, badges: [] } - res(p) - }, { once: true }) + const bt = document.getElementById("saveProfileBt") + bt.addEventListener( + "click", + () => { + let name = document.getElementById("profileName").value + let avatar = + document.getElementById("profileAvatar").value + const p = { name: name, avatar: avatar, badges: [] } + res(p) + }, + { once: true } + ) } - - }); + }) } } +export { ProfileBuilder, Profile } -export { ProfileBuilder, Profile } \ No newline at end of file