diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..673cb6d --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +typewriter.min.js +simpleNotification.min.js +Drift.min.js \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json index 01d7286..35424c0 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -11,5 +11,6 @@ "ecmaVersion": "latest" }, "rules": { + "indent": ["error", "tab", { "SwitchCase": 1 }] } } diff --git a/.gitattributes b/.gitattributes index f1b93c9..e0c891c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,5 +3,6 @@ *.jpg filter=lfs diff=lfs merge=lfs -text *.woff filter=lfs diff=lfs merge=lfs -text *.woff2 filter=lfs diff=lfs merge=lfs -text - -dist/**/* !filter !diff !merge \ No newline at end of file +*.avi filter=lfs diff=lfs merge=lfs -text +*.gif filter=lfs diff=lfs merge=lfs -text +*.mp3 filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/.github/workflows/butler.yml b/.github/workflows/butler.yml index 4a39842..a83cda0 100644 --- a/.github/workflows/butler.yml +++ b/.github/workflows/butler.yml @@ -1,4 +1,4 @@ -name: Lint & Deploy +name: Lint, Copy, & Deploy on: push jobs: @@ -24,11 +24,25 @@ jobs: uses: c-hive/gha-npm-cache@v1 - name: npm Install run: npm ci - - name: Lint Javascript + - name: Lint CSS run: npm run csslint + copy: + name: Copy Files To dist + needs: [jsLint, cssLint] + runs-on: ubuntu-latest + if: contains(github.ref, 'refs/tags/') || github.ref == 'refs/heads/develop' + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + - name: Cache npm Directory + uses: c-hive/gha-npm-cache@v1 + - name: npm Install + run: npm ci + - name: Copy Files To dist + run: npm run copy deploy: name: Deploy to itch.io - needs: [jsLint, cssLint] + needs: copy runs-on: ubuntu-latest if: contains(github.ref, 'refs/tags/') || github.ref == 'refs/heads/develop' steps: @@ -39,16 +53,6 @@ jobs: with: source: "Purpose.html" target: "dist/index.html" - - name: Copy Javascript File to dist - uses: canastro/copy-file-action@master - with: - source: "index.js" - target: "dist/assets/javascript/index.js" - - name: Copy CSS File to dist - uses: canastro/copy-file-action@master - with: - source: "style.css" - target: "dist/assets/css/style.css" - name: Minify JS & CSS dist files uses: nizarmah/auto-minify@v2.1 with: diff --git a/.gitignore b/.gitignore index e0d4b66..c29e0ed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ .vscode .DS_Store node_modules -dist/Purpose.html -dist/assets/javascript/index.js -dist/assets/css/style.css \ No newline at end of file +dist/* \ No newline at end of file diff --git a/.stylelintignore b/.stylelintignore new file mode 100644 index 0000000..a103360 --- /dev/null +++ b/.stylelintignore @@ -0,0 +1,2 @@ +simpleNotification.min.css +drift-basic.min.css \ No newline at end of file diff --git a/CREDITS.md b/CREDITS.md index f527ca2..eead67d 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -1,9 +1,21 @@ Game & Infrastructure - Christopher "Inferno" M. > https://purpose-game.com/ -Artwork - Daniel "DanDarkDesigns" Ayala +Artwork, UI Design, & Sound Design - Daniel "DanDarkDesigns" Ayala > https://www.artstation.com/darkcan +UI Sound Effects - Kenney Vleugels +> https://kenney.nl + +SFX - Reference Individual File Metadata +https://freesound.org/ +(https://creativecommons.org/publicdomain/zero/1.0/) +(https://creativecommons.org/licenses/by/3.0/) +(https://creativecommons.org/licenses/by/4.0/) + +Music - Reference Individual File Metadata +> https://www.humblebundle.com + Dead Font Walking Font - Allison James > https://www.1001fonts.com/dead-font-walking-font.html @@ -16,6 +28,18 @@ SimpleNotification - Glagan & Contributors toggleFullscreen.js - demonixis > https://gist.github.com/demonixis/5188326 +jquery-typewriter-plugin - 0xPranavDoshi +> https://github.com/0xPranavDoshi/jquery-typewriter + +Steam-like Trading Card Interaction - imchell +> https://github.com/imchell/steam-like-card-curation + +Drift - imgix +> https://github.com/imgix/drift + +Underscore.js - DocumentCloud & Contributors +> https://underscorejs.org/ + jQuery - OpenJS Foundation & Contributors > https://jquery.com/ diff --git a/README.md b/README.md index 506838b..135058a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Banner](https://i.imgur.com/YcvSwDo.png) +![Banner](https://i.imgur.com/RvCGS6I.png) # Purpose @@ -24,7 +24,7 @@ Made in [Twine 2](https://twinery.org/) with [Snowman 2](https://github.com/vide ##### Content Warning: Mature themes including trauma, depression, suicidal ideation, swearing, and written depictions of death and gore, in a post-apocalyptic setting. -##### Donations go towards paying for artwork, and the server that hosts the Cloud-Saving. +##### Donations go towards paying for artwork, code helpers, and the server that hosts the Cloud-Saving. ## Contributing If you have a story related suggestion, the best place to put it is in our Discord. If you have a technical suggestion or contribution, feel free to make an issue and/or PR. diff --git a/dist/.gitkeep b/dist/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dist/assets/fonts/dead_font_walking-webfont.woff b/dist/assets/fonts/dead_font_walking-webfont.woff deleted file mode 100644 index 8b65fc3..0000000 Binary files a/dist/assets/fonts/dead_font_walking-webfont.woff and /dev/null differ diff --git a/dist/assets/fonts/dead_font_walking-webfont.woff2 b/dist/assets/fonts/dead_font_walking-webfont.woff2 deleted file mode 100644 index 5323d3e..0000000 Binary files a/dist/assets/fonts/dead_font_walking-webfont.woff2 and /dev/null differ diff --git a/dist/assets/images/icons/android-chrome-192x192.png b/dist/assets/images/icons/android-chrome-192x192.png deleted file mode 100644 index 1f1d86b..0000000 Binary files a/dist/assets/images/icons/android-chrome-192x192.png and /dev/null differ diff --git a/dist/assets/images/icons/android-chrome-512x512.png b/dist/assets/images/icons/android-chrome-512x512.png deleted file mode 100644 index dcec477..0000000 Binary files a/dist/assets/images/icons/android-chrome-512x512.png and /dev/null differ diff --git a/dist/assets/images/icons/apple-touch-icon.png b/dist/assets/images/icons/apple-touch-icon.png deleted file mode 100644 index ef38c00..0000000 Binary files a/dist/assets/images/icons/apple-touch-icon.png and /dev/null differ diff --git a/dist/assets/images/icons/favicon-16x16.png b/dist/assets/images/icons/favicon-16x16.png deleted file mode 100644 index b837f13..0000000 Binary files a/dist/assets/images/icons/favicon-16x16.png and /dev/null differ diff --git a/dist/assets/images/icons/favicon-32x32.png b/dist/assets/images/icons/favicon-32x32.png deleted file mode 100644 index bdb4063..0000000 Binary files a/dist/assets/images/icons/favicon-32x32.png and /dev/null differ diff --git a/dist/assets/images/ui/sarah.png b/dist/assets/images/ui/sarah.png deleted file mode 100644 index 9b5490a..0000000 Binary files a/dist/assets/images/ui/sarah.png and /dev/null differ diff --git a/dist/assets/images/ui/tiffany.png b/dist/assets/images/ui/tiffany.png deleted file mode 100644 index 050fb51..0000000 Binary files a/dist/assets/images/ui/tiffany.png and /dev/null differ diff --git a/dist/assets/images/ui/title-image.png b/dist/assets/images/ui/title-image.png deleted file mode 100644 index 713d53b..0000000 Binary files a/dist/assets/images/ui/title-image.png and /dev/null differ diff --git a/index.js b/index.js deleted file mode 100644 index 3f08206..0000000 --- a/index.js +++ /dev/null @@ -1,787 +0,0 @@ -/* global _, SimpleNotification */ - -// -// Variables -// - -// Game Version -const version = "1.1.1"; -// API URL Root -const apiUrl = "https://purpose-game.com/api/"; - -// Shows debug notification when true -let debug = true; -// Shows achievement notifications when true -let achievements = true; -// Uses custom font when true -let font = true; -// Prevents saving of last saved page -let justLoaded = false; - -let debugNotification; -let saveNotification; - -let prePausePassage; - -window.story.version = version; - -// Current Chapter, i.e. "Chapter1" -window.story.state.chapter = ""; -window.story.state.choices = {}; -window.story.state.achievements = {}; - -window.story.player = {}; -window.story.player.name = "Player"; -window.story.player.id = "0"; -window.story.player.key = "0"; - -// If Cloud Saving is enabled -window.story.saving = false; -window.story.saveSlot = 1; -// If game is being played online -window.story.network = false; - -// Used for statistics passages, see "Choices" passage for all choices -// "Choice Internal Name": [ -// You and X% of players: -// "did some actions or made some choice", -// You and X% of players: -// "did some opposite actions or made some other choice" -// ], -window.story.choiceDescriptions = { - "": { - // Empty for debugging - }, - "Chapter1": { - "Lied": [ - "lied to Lucy about how you hurt your leg", - "told Lucy the truth about how you hurt your leg" - ], - "NewPurpose": [ - "told yourself you needed to find a new purpose in life", - "told yourself you have no purpose in life and should die" - ], - "Laughed": [ - "found the time to have a laugh", - "did not find the time to have a laugh" - ], - "Tiffany": [ - "called Tiffany by her full name", - "called Tiffany by the nickname you made" - ], - "Haircut": [ - "gave yourself a haircut", - "passed up the opportunity for a haircut" - ], - "WillHelp": [ - "told Lucy people will help Tiffany in her time of need", - "told Lucy people will not help Tiffany in her time of need" - ], - "Protect": [ - "chose to protected Tiffany", - "chose to investigate the noise" - ], - "SaveStranger": [ - "went outside to save the stranger", - "stayed inside and tried to keep the stranger out" - ], - "LucySavesStranger": [ - "influenced Lucy to save the stranger", - "influenced Lucy to try and keep the stranger out" - ], - "TriedToSaveLucy": [ - "tried to save Lucy, but let Tiffany watch her die", - "did as Lucy asked, saved Tiffany from watching her die" - ], - "StrangerDied": [ - "watched the stranger die", - "left with the stranger" - ] - } -}; - -// Used for achievements and end passages -// "Achievement Internal Name": [ -// "Achievement Name", "Achievement Description" -// ], -window.story.achievementDescriptions = { - "": { - // Empty for debugging - }, - "Chapter1": { - "Knocked": [ - "Knock, knock", "Who's *there?*" - ], - "Soap": [ - "Nothing wasted", "You never know when it will come in handy!" - ], - "Filter": [ - "No Filter", "Speaking your mind, *even when you shouldn't.*" - ], - "Nothing": [ - "Silent Treatment", "You have the right to remain silent..." - ], - "NoChoice": [ - "Broken Record", "You need some new material." - ], - "RollCredits": [ - "Roll Credits", "And that's a Sin." - ], - "Drama": [ - "Drama Queen", "You should have been an actor." - ], - "Walkers": [ - "Walkers?", "What do you call the ones that run?" - ], - "Nerve": [ - "Struck a Nerve", "Found a touchy subject" - ], - "Scorpion": [ - "Scorpion", "*Get over here*" - ] - } -} - -// -// Events -// -$(document).on("sm.passage.showing", function(_, data) { - if (data.passage === undefined) return; - - const passage = data.passage; - const twPassage = $("tw-passage"); - - debugMessage(`Passage changed to ${passage.name}`); - - // Set browser tab name, doesn't work when inside iFrame - $(document).attr("title", "Purpose - " + passage.name); - // Scroll to top of screen on passage change for mobile users - window.scrollTo(0, 0); - - if (debug) { - if (!debugNotification) { - debugNotification = SimpleNotification.message({ - title: "Alpha Build" - }, { - position: "bottom-right", - sticky: true, - closeButton: false, - closeOnClick: false - }); - } - - debugNotification.setText(`Content subject to change!\nCurrent Passage: \`\`${passage.name}\`\``); - } - - if (passage.tags) { - if (!passage.tags.includes("noFade") && !passage.tags.includes("redirect")) { - twPassage.hide(0).fadeIn(500); - } - - if ((passage.tags.includes("page") || passage.tags.includes("variation"))) { - // Replace %Tiffany% with what the player chose to call Tiffany - if (passage.source.includes("%Tiffany%")) { - passage.source = passage.source.replaceAll("%Tiffany%", window.story.tiffany()); - } - } - - if (window.story.saving && passage.tags.includes("save")) { - // Stop saving of the page we just loaded back into - if (justLoaded) { - debugMessage(`Not saving ${passage.name} as justLoaded is ${true}`); - justLoaded = false; - return; - } - - debugMessage(`Game saved at ${passage.name}`); - - window.story.state.lastPassage = passage.name; - window.story.saveGame(); - } - } -}); - -$(document).on("sm.passage.shown", function(_, data) { - const passage = data.passage; - const twPassage = $("tw-passage"); - const pauseMenuHTML = '

Pause Menu


'; - - // Inject pause menu button into all story passages - if (passage.tags && (passage.tags.includes("page") || passage.tags.includes("variation"))) { - twPassage.html(pauseMenuHTML + twPassage.html()); - } -}); - -// -// Helpers -// - -// Prints a debug message to the console if in debug mode -function debugMessage(message) { - if (debug) console.log("DEBUG: " + message); -} - -// Checks network connection -window.story.networkCheck = function (nextPassage) { - $.get(apiUrl + "status").done(function() { - debugMessage("Connection to server made successfully"); - - window.story.network = true; - }).catch(function(error) { - debugMessage("Connection to server failed: " + error); - - saveNotification = SimpleNotification.error({ - title: "Network Connection Failed!", - text: "Could not connect to the remote server, Cloud Saving unavailable." - }, { - duration: 10 * 1000, - position: "bottom-right" - }); - // Show the next passage regardless - }).then(function() { - window.story.show(nextPassage); - }); -} - -// Shows "No Connection" error message -window.story.noConnection = function () { - saveNotification = SimpleNotification.error({ - title: "Network Connection Failed!", - text: "Could not connect to the remote server, Cloud Saving unavailable." - }, { - duration: 10 * 1000, - position: "bottom-right" - }); -} - -// Redirects one passage to another after a given time -window.story.redirect = function (pageName, time = 5) { - debugMessage(`Redirecting to ${pageName} in ${time} seconds`); - - let timeLeft = time; - - function tick() { - if (--timeLeft === 0) window.story.show(pageName); - - _.delay(tick, 1000); - } - - _.delay(tick, 1000); -}; - -// Displays text on screen after x period of time -window.story.delayedText = function (time = 1000, id = "delayed", fadeIn = 1000) { - debugMessage(`Showing delayed text ${id} in ${time} milliseconds`); - - _.delay(function() { - $(`#${id}`).fadeIn(fadeIn); - }, time); -} - -// Sets an achievement and shows a popup -window.story.achievement = function (chapter, shorthand, title, text) { - try { - if (window.story.state.achievements[chapter][shorthand]) { - debugMessage(`Chapter ${chapter} achievement ${shorthand} already earned`); - return; - } else { - debugMessage(`Chapter ${chapter} achievement ${shorthand} earned, showing ${achievements}`); - } - - window.story.state.achievements[chapter][shorthand] = true; - } catch (_) { - window.story.state.achievements[chapter] = {}; - window.story.state.achievements[chapter][shorthand] = true; - } - - if (achievements) { - SimpleNotification.info({ - title: `Achievement: ${title}`, - text, - }, { - duration: 10 * 1000, - position: "bottom-right", - closeButton: false, - closeOnClick: false - }); - } -}; - -// Sets a story choice -window.story.setChoice = function (chapter, choice, value = true) { - debugMessage(`Chapter ${chapter} choice ${choice} set to ${value}`); - - try { - window.story.state.choices[chapter][choice] = value; - } catch (_) { - window.story.state.choices[chapter] = {}; - window.story.state.choices[chapter][choice] = value; - } -}; - -// Gets a story choice -window.story.getChoice = function (chapter, choice) { - try { - return window.story.state.choices[chapter][choice]; - } catch (_) { - // TODO: Replace with an enum or similar, since - // there are cases where the choice not being set - // should result in a different action from the - // choice being set to false - return false; - } -}; - -// Gets all the choices from a chapter -window.story.getChoices = function (chapter) { - try { - return Object.entries(window.story.state.choices[chapter]) || []; - } catch (_) { - return []; - } -} - -// Returns the name picked for Tiffany -window.story.tiffany = function () { - try { - return window.story.state.choices["Chapter1"]["TiffanyName"] || "Tiffany"; - } catch (_) { - return "Tiffany"; - } -} - -// Links a player's account to the current session -window.story.linkCode = function () { - const input = $("#linkingCode"); - const button = $("#linkingCodeButton"); - const code = input.val(); - - saveNotification = SimpleNotification.info({ - title: "Linking account" - }, { - position: "bottom-right" - }); - - input.prop("disabled", true); - button.attr("onclick", ""); - - if (!code || code === "") { - debugMessage(`Empty linking code provided`); - - saveNotification.setType("error"); - saveNotification.setTitle("Error: No Code"); - saveNotification.setText("No code entered!"); - - input.prop("disabled", false); - button.attr("onclick", "window.story.linkCode()"); - return; - } - - if (code.length != 9) { - debugMessage(`Linking code wrong length`); - - saveNotification.setType("error"); - saveNotification.setTitle("Error: Invalid Code"); - saveNotification.setText("Entered code is invalid!"); - input.prop("disabled", false); - button.attr("onclick", "window.story.linkCode()"); - return; - } - - $.post(apiUrl + "link", { - code - }).done(function(data) { - debugMessage(`Account linked with code ${code}`); - - saveNotification.setType("success"); - saveNotification.setTitle("Account Linked!"); - - window.story.player.name = data.userName; - window.story.player.id = data.userId; - window.story.player.key = data.userKey; - - debugMessage(`Name: ${window.story.player.name}, ID: ${window.story.player.id}, User Key: ${window.story.player.key}`); - - window.story.saving = true; - - window.story.show("Linked"); - }).catch(function(error) { - debugMessage(`Account failed to link with code ${code}: ` + error); - - SimpleNotification.error({ - title: `Error: ${error.status}`, - text: error.responseText - }, { - position: "bottom-right" - }); - - input.prop("disabled", false); - button.attr("onclick", "window.story.linkCode()"); - }); -} - -// Saves player's game -window.story.saveGame = function () { - saveNotification = SimpleNotification.info({ - title: "Auto-Saving..." - }, { - position: "bottom-right" - }); - - $.post(apiUrl + "save", { - key: window.story.player.key, - data: { - saveSlot: window.story.saveSlot, - saveData: JSON.stringify(window.story.state) - } - }).done(function() { - debugMessage("Game saved"); - - saveNotification.setType("success"); - saveNotification.setTitle("Auto-Save Complete!"); - }).catch(function(error) { - debugMessage("Game failed to save: " + error); - - saveNotification.setType("error"); - saveNotification.setTitle("Auto-Save Failed!"); - saveNotification.setText(error.responseText); - }); -} - -// Loads all player saves -window.story.loadSaves = function (newGame = false) { - $.post(apiUrl + "saves", { - key: window.story.player.key - }).done(function(data) { - debugMessage(`Loaded ${data.length} saves for ${window.story.player.key}`); - - $("#slotsLoading").hide(); - - if (data.length === 0 && !newGame) { - saveNotification = SimpleNotification.message({ - title: "No Saved Games!" - }, { - position: "bottom-right" - }); - return; - } - - if (!newGame) { - $.each( data, function( _, value ) { - const lastUsed = new Date(value.lastActive); - $("#savesContainer").append(`Save Slot ${value.slot}
Chapter: ${value.data.lastPassage.charAt(1)}
Last passage: ${value.data.lastPassage}
Last Used: ${lastUsed.toLocaleDateString()}
`); - }); - } else { - $.each( data, function( _, value ) { - $("#saveSlot" + value.slot).text("Save Slot " + value.slot + " (In Use)"); - }); - } - - if (newGame) { - $("#saveSlotSelector").fadeIn(500); - } else { - $("#savesContainer").fadeIn(500); - } - }).catch(function(error) { - debugMessage(`Failed to load saves for ${window.story.player.key}: ` + error); - - saveNotification = SimpleNotification.error({ - title: "Load Failed!", - text: error.responseText - }, { - position: "bottom-right" - }); - }); -} - -// Loads a player's save -window.story.loadSave = function (saveSlot) { - window.story.show("Loading Save"); - - saveNotification = SimpleNotification.info({ - title: "Loading Save..." - }, { - position: "bottom-right" - }); - - $.post(apiUrl + "load", { - key: window.story.player.key, - saveSlot: saveSlot, - }).done(function(data) { - debugMessage(`Loaded save ${saveSlot} for ${window.story.player.key}`); - - justLoaded = true; - - window.story.state = data; - window.story.saveSlot = saveSlot; - - saveNotification.setType("success"); - saveNotification.setTitle("Save Loaded!"); - - _.delay(function() { - window.story.show(window.story.state.lastPassage); - }, 1000); - }).catch(function(error) { - debugMessage(`Failed to load save ${saveSlot} for ${window.story.player.key}: ` + error); - - saveNotification.setType("error"); - saveNotification.setTitle("Load Failed!"); - saveNotification.setText(error.responseText); - - _.delay(function() { - window.story.show("Saved Games"); - }, 1000); - }); -} - -// Hides generate button and shows linking form -window.story.toggleLinkingDisplays = function (reverse = false) { - debugMessage(`Linking buttons toggled (${reverse})`); - - if (!reverse) { - $("#generateButton").hide(); - $("#linkingForm").show(); - } else { - $("#generateButton").show(); - $("#linkingForm").hide(); - } -} - -// Selects a slot of a new game, and starts new game -window.story.selectSlot = function () { - debugMessage(`Starting game in save slot ${$("#slots").val()}`); - - window.story.saveSlot = $("#slots").val(); - - saveNotification = SimpleNotification.info({ - title: "Save Slot " + window.story.saveSlot + " Selected" - }, { - position: "bottom-right" - }); - - _.delay(function() { - window.story.show("C1 Intro"); - }, 1000); -} - -// Shows an example save notification -window.story.exampleSave = function () { - saveNotification = SimpleNotification.info({ - title: "Auto-Saving..." - }, { - position: "bottom-right" - }); - - _.delay(function() { - saveNotification.setType("success"); - saveNotification.setTitle("Auto-Save Complete!"); - }, 1000); -} - -// Toggles Debug Mode off -window.story.toggleDebug = function () { - debugMessage(`Debug toggled (${!debug})`); - - debug = !debug; - - if (debug) { - debugNotification = SimpleNotification.message({ - title: "Alpha Build", - text: "Content subject to change!", - }, { - position: "bottom-right", - sticky: true, - closeButton: false, - closeOnClick: false - }); - } else { - debugNotification.remove(); - } - - SimpleNotification.info({ - title: `Debug Mode ${debug ? "Enabled" : "Disabled"}` - }, { - duration: 2 * 1000, - position: "bottom-right", - closeButton: false, - closeOnClick: false - }); -} - -// Toggles Custom Font -window.story.toggleFont = function () { - debugMessage(`Font toggled (${!font})`); - - font = !font; - - if (font) { - $("body").css({ - "font-size": "unset", - "font": "27px 'DeadWalking', Arial, sans-serif" - }); - } else { - $("body").css({ - "font": "unset", - "font-size": "27px" - }); - } - - SimpleNotification.info({ - title: `Font ${font ? "Enabled" : "Disabled"}` - }, { - duration: 2 * 1000, - position: "bottom-right", - closeButton: false, - closeOnClick: false - }); -} - -// Loads statistics -window.story.loadStats = function () { - $.post(apiUrl + "stats", { - key: window.story.player.key, - chapter: window.story.state.chapter - }).done(function(data) { - debugMessage(`Loaded ${data.length} stats for chapter ${window.story.state.chapter} ${window.story.player.key}`); - - $("#statsLoading").hide(); - - for (const stat in data) { - if ( - !window.story.state.choices[window.story.state.chapter] || - // If choice was never set, not to be confused with choice being set to false - !Object.prototype.hasOwnProperty.call(window.story.state.choices[window.story.state.chapter], stat) - ) { - continue; - } - - let message; - - if (window.story.getChoice(window.story.state.chapter, stat)) { - message = `You and ${data[stat][0]} of players ${window.story.choiceDescriptions[window.story.state.chapter][stat][0]}`; - } else { - message = `You and ${data[stat][1]} of players ${window.story.choiceDescriptions[window.story.state.chapter][stat][1]}`; - } - - $("#statsContainer").append(`

${message}


`); - } - - $("#statsContainer").fadeIn(500); - }).catch(function(error) { - debugMessage(`Failed to load stats for chapter ${window.story.state.chapter} ${window.story.player.key}`); - - saveNotification = SimpleNotification.error({ - title: "Load Failed!", - text: error.responseText - }, { - position: "bottom-right" - }); - }); -} - -// Loads achievements -window.story.loadAchievements = function () { - // Fake loading to allow the passage to load or else jQuery won't make the changes - _.delay(function() { - $("#achievementsLoading").hide(); - - let earnedAchievements = 0; - const totalAchievements = Object.entries(window.story.achievementDescriptions[window.story.state.chapter]).length; - - $("#achievementsCounter").text(`${earnedAchievements}/${totalAchievements} Achievements Earned`); - - for (const [internal, [name, description]] of Object.entries(window.story.achievementDescriptions[window.story.state.chapter])) { - if (!window.story.state.achievements[window.story.state.chapter] || !window.story.state.achievements[window.story.state.chapter][internal]) { - continue; - } - - $("#achievementsCounter").text(`${++earnedAchievements}/${totalAchievements} Achievements Earned`); - - $("#achievementsContainer").append(`

- ${name}: ${description.replaceAll("*", "")}

`); - } - - if (totalAchievements > 0) $("#achievementsContainer").append(`
`); - - $("#achievementsCounter").fadeIn(500); - $("#achievementsContainer").fadeIn(500); - }, 1000); -} - -// Toggles opening of pause menu -window.story.pauseMenu = function () { - debugMessage(`Pause menu toggled (${prePausePassage == null})`); - - if (prePausePassage == null) { - prePausePassage = window.passage.name; - - window.story.show("Pause Menu"); - } else { - window.story.show(prePausePassage); - - prePausePassage = null; - } -} - -// Toggles showing of achievements -window.story.toggleAchievements = function () { - debugMessage(`Achievements toggled (${!achievements})`); - - achievements = !achievements; - - SimpleNotification.info({ - title: `Achievements ${achievements ? "Enabled" : "Disabled"}` - }, { - duration: 2 * 1000, - position: "bottom-right", - closeButton: false, - closeOnClick: false - }); -} - -// Renders a passage and replaces text -window.story.customRender = function (passageName) { - const passage = window.story.passage(passageName); - - // Replace %Tiffany% with what the player chose to call Tiffany - if (passage.source.includes("%Tiffany%")) { - passage.source = passage.source.replaceAll("%Tiffany%", window.story.tiffany()); - } - - return window.story.render(passageName); -} - -// -// External Scripts -// - -// toggleFullscreen.js -// https://gist.github.com/demonixis/5188326 -window.story.toggleFullscreen = function (event) { - let element = document.documentElement; - - if (event instanceof HTMLElement) element = event; - - const isFullscreen = document.webkitIsFullScreen || document.mozFullScreen || false; - - debugMessage(`Fullscreen toggled (${!isFullscreen})`); - - element.requestFullScreen = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || function () { return false; }; - document.cancelFullScreen = document.cancelFullScreen || document.webkitCancelFullScreen || document.mozCancelFullScreen || function () { return false; }; - - isFullscreen ? document.cancelFullScreen() : element.requestFullScreen(); -} - -// SimpleNotification -// https://github.com/Glagan/SimpleNotification -// https://github.com/Glagan/SimpleNotification/blob/master/LICENSE -const simpleNotification = document.createElement("script"); -simpleNotification.src = "assets/javascript/simpleNotification.min.js"; - -document.head.appendChild(simpleNotification); - -/// -/// Initialization -/// - -// Adds Favicons -$("head").append(''); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8a99346..48497d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,9 @@ "version": "1.1.0", "devDependencies": { "eslint": "^8.41.0", - "stylelint": "15.6.2", - "stylelint-config-standard": "^33.0.0" + "shx": "^0.3.4", + "stylelint": "^15.6.2", + "stylelint-config-standard": "33.0.0" } }, "node_modules/@babel/code-frame": { @@ -1217,6 +1218,15 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -1525,6 +1535,15 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minimist-options": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", @@ -1999,6 +2018,18 @@ "node": ">=8" } }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -2131,6 +2162,39 @@ "node": ">=8" } }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shx": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", + "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", + "dev": true, + "dependencies": { + "minimist": "^1.2.3", + "shelljs": "^0.8.5" + }, + "bin": { + "shx": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/signal-exit": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", @@ -3477,6 +3541,12 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -3711,6 +3781,12 @@ "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, "minimist-options": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", @@ -4038,6 +4114,15 @@ } } }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, "redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -4119,6 +4204,27 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "shx": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", + "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", + "dev": true, + "requires": { + "minimist": "^1.2.3", + "shelljs": "^0.8.5" + } + }, "signal-exit": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", diff --git a/package.json b/package.json index 179b65f..e93d294 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,9 @@ "description": "Purpose is an episodic story about a young woman struggling with the loss of her sister in the Walker-infested post-apocalyptic world. Join her on her journey as you make decisions that influence the outcome of the story, in this text-based multiple-choice adventure inspired by Telltale Games' The Walking Dead.", "main": "index.js", "scripts": { - "jslint": "eslint . index.js", - "csslint": "npx stylelint style.css" + "jslint": "npx eslint --ext .js \"src\"", + "csslint": "npx stylelint \"src/**/*.css\"", + "copy": "shx cp -r src/* dist" }, "repository": { "type": "git", @@ -18,7 +19,8 @@ "homepage": "https://github.com/cm8263/Purpose#readme", "devDependencies": { "eslint": "^8.41.0", - "stylelint": "15.6.2", - "stylelint-config-standard": "^33.0.0" + "shx": "^0.3.4", + "stylelint": "^15.6.2", + "stylelint-config-standard": "33.0.0" } } diff --git a/Purpose.html b/src/Purpose.html similarity index 54% rename from Purpose.html rename to src/Purpose.html index df278e8..fb8f176 100644 --- a/Purpose.html +++ b/src/Purpose.html @@ -10,315 +10,489 @@ - +</div>This game has a lot of images: UI, characters, etc. +We're loading those images right now. +Please sit tight! + +<progress#loading-bar max="100"></progress> +<label#loading-bar-label for="loading-bar">0%</label><input#debugSelector type="text" placeholder="Type Passage Here" autocomplete="off" value="C1P"></input> +<button#debugGo>Go</button> + +<br><hr> + +C<input#debugSelectorC type="text" placeholder="Type Chapter Here" autocomplete="off" value="1"></input> +P<input#debugSelectorP type="text" placeholder="Type Passage Here" autocomplete="off" autofocus></input> +V<input#debugSelectorV type="text" placeholder="Type Variation Here" autocomplete="off"></input> +<button#debugGo2>Go</button> + +<script> + $("#debugGo").click(function() { + const passage = $("#debugSelector").val(); + + window.story.show(passage.startsWith("C1") ? passage.toUpperCase() : passage); + }); + + $("#debugGo2").click(function() { + let chapter = $("#debugSelectorC").val(); + let passage = $("#debugSelectorP").val(); + let variation = $("#debugSelectorV").val(); + + if (chapter) chapter = `C${chapter}`; + if (passage) passage = `P${passage}`; + if (variation) variation = `V${variation}`; + + window.story.show(chapter + passage + variation); + }); +</script><choices>[[[Rollover]|C1P12]]</choices><action>%Tiffany% sounds scared.</action> + +<character.two>Unknown</character> + +<speech.unknown>Get off me you sons of bitches!</speech> + +<action>The voice from outside again.</action> +<action>It sounds like a guy.</action><ui>standard</ui> +<character.one.angry.eyes>Sarah</character> +<character.two.scared.ditto>Tiffany</character> + +<action>I look at %Tiffany%.</action> +<action>She's got her feet up on the seat, and buried her head in her knees.</action> +<action>She must be terrified.</action><div#gallery-container.gallery> + <img#gallery-image-1> + <img#gallery-image-2> + <img#gallery-image-3> + <img#gallery-image-4> + <img#gallery-image-5> + <img#gallery-image-6> + <img#gallery-image-7> + <img#gallery-image-8> +</div> + +Character Artwork - Despoina "DesDarkDesigns"<br> +<a href="https://linktr.ee/despoinanyx" target="_blank">https://linktr.ee/despoinanyx</a> + +[[Back|Artwork]]<%= window.story.render("F: Artwork") %> + +<% window.story.createGalleryItem(1, "Lucy", "neutral"); %> +<% window.story.createGalleryItem(2, "Lucy", "neutral", "neutral-mask", "neutral-mask"); %> +<% window.story.createGalleryItem(3, "Lucy", "fighting"); %> +<% window.story.createGalleryItem(4, "Lucy", "concern", "concern", "neutral"); %> +<% window.story.createGalleryItem(5, "Lucy", "happy", "happy", "neutral"); %> +<% window.story.createGalleryItem(6, "Lucy", "sad", "sad", "neutral"); %><%= window.story.render("F: Artwork") %> + +<% window.story.createGalleryItem(1, "Charles", "neutral"); %> +<% window.story.createGalleryItem(2, "Charles", "fighting"); %> +<% window.story.createGalleryItem(3, "Charles", "angry", "angry", "neutral"); %> +<% window.story.createGalleryItem(4, "Charles", "sad", "sad", "neutral"); %> +<% window.story.createGalleryItem(5, "Charles", "scared", "scared", "neutral"); %><%= window.story.render("F: Artwork") %> + +<% window.story.createGalleryItem(1, "Sophia", "neutral"); %> +<% window.story.createGalleryItem(2, "Sophia", "close-up", "neutral-close-up", "close-up"); %> +<% window.story.createGalleryItem(3, "Sophia", "demonic", "demonic", "neutral"); %> +<% window.story.createGalleryItem(4, "Sophia", "excited", "excited", "neutral"); %>