diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | .husky/pre-commit | 4 | ||||
| -rw-r--r-- | package.json | 12 | ||||
| -rw-r--r-- | public/client.html (renamed from webAO/client.html) | 4 | ||||
| -rw-r--r-- | public/index.html (renamed from webAO/index.html) | 29 | ||||
| -rw-r--r-- | static/desc.png (renamed from webAO/images/desc.png) | bin | 50785 -> 50785 bytes | |||
| -rw-r--r-- | static/favicon.ico (renamed from webAO/favicon.ico) | bin | 5694 -> 5694 bytes | |||
| -rw-r--r-- | static/logo-new-512.png (renamed from webAO/logo-new-512.png) | bin | 238578 -> 238578 bytes | |||
| -rw-r--r-- | static/logo-new.png (renamed from webAO/logo-new.png) | bin | 29302 -> 29302 bytes | |||
| -rw-r--r-- | static/manifest.json (renamed from webAO/manifest.json) | 4 | ||||
| -rw-r--r-- | webAO/client.js | 342 | ||||
| -rw-r--r-- | webAO/client/setEmote.js | 43 | ||||
| -rw-r--r-- | webAO/dom/changeSFXVolume.js | 10 | ||||
| -rw-r--r-- | webAO/dom/changeShoutVolume.js | 10 | ||||
| -rw-r--r-- | webAO/dom/toggleEffect.js | 13 | ||||
| -rw-r--r-- | webAO/master.js | 2 | ||||
| -rw-r--r-- | webAO/packets/ms.js | 28 | ||||
| -rw-r--r-- | webAO/styles/master.css | 2 | ||||
| -rw-r--r-- | webAO/sw.js | 26 | ||||
| -rw-r--r-- | webAO/utils/fileExists.js | 17 | ||||
| -rw-r--r-- | webAO/utils/fileExistsSync.js | 11 | ||||
| -rw-r--r-- | webAO/utils/getAnimLength.js | 22 | ||||
| -rw-r--r-- | webAO/utils/getResources.js | 39 | ||||
| -rw-r--r-- | webAO/utils/queryParser.js | 9 | ||||
| -rw-r--r-- | webpack.config.js | 37 |
25 files changed, 327 insertions, 338 deletions
@@ -3,6 +3,7 @@ Thumbs.db ehthumbs.db coverage/ +dist/ # Folder config file Desktop.ini diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..6d395b8 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npm run lint:fix diff --git a/package.json b/package.json index 8cea437..6865892 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,9 @@ "test:coverage": "jest --coverage", "build": "webpack --config webpack.config.js", "start": "webpack serve --config webpack.config.js", - "lint": "eslint . --ext .js", - "lint:fix": "eslint --fix . --ext .js" + "lint": "eslint webAO --ext .js", + "lint:fix": "npm run lint -- --fix", + "prepare": "husky install" }, "repository": { "type": "git", @@ -28,14 +29,19 @@ "@babel/plugin-transform-modules-commonjs": "^7.16.8", "@babel/preset-env": "^7.16.11", "babel-loader": "^8.2.3", + "copy-webpack-plugin": "^10.2.4", "dotenv": "^16.0.0", "eslint": "^8.10.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.25.4", + "glob": "^7.2.0", + "html-webpack-plugin": "^5.5.0", + "husky": "^7.0.0", "jest": "^27.5.1", "webpack": "^5.69.1", "webpack-cli": "^4.9.2", - "webpack-dev-server": "^4.7.4" + "webpack-dev-server": "^4.7.4", + "workbox-webpack-plugin": "^6.5.1" }, "dependencies": { "@fingerprintjs/fingerprintjs": "^3.3.3", diff --git a/webAO/client.html b/public/client.html index 5dd0264..dc924fb 100644 --- a/webAO/client.html +++ b/public/client.html @@ -29,7 +29,7 @@ media-src 'self' file: *;"> <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"> - <link rel="apple-touch-icon" href="/images/logo-new.png"/> + <link rel="apple-touch-icon" href="logo-new.png"/> <link rel="stylesheet" type="text/css" href="styles/client.css?v=1.0.3" id="client_stylesheet"> <link rel="stylesheet" type="text/css" href="styles/default.css?v=1.0.1" id="client_theme"> <link rel="stylesheet" type="text/css" href="styles/chatbox/aa.css?v=1.0.1" id="chatbox_theme"> @@ -567,7 +567,7 @@ <!-- About section --> <span class="menu_content" id="content_4"> <meta name="frame-title" lang="en" content="About"> - <img id="about-logo" src="images/logo-new.png" alt="Attorney Online logo"> + <img id="about-logo" src="logo-new.png" alt="Attorney Online logo"> <h1 style="line-height: .3em;">webAO</h1> <h3 id="client_version">version</h3> <p>Client created by diff --git a/webAO/index.html b/public/index.html index c82cacf..2242081 100644 --- a/webAO/index.html +++ b/public/index.html @@ -1,6 +1,6 @@ <html> <head> - <title>Attorney Online ONLINE</title> + <title><%= htmlWebpackPlugin.options.title %></title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content="An off-the-cuff courtroom drama simulator"> @@ -30,24 +30,21 @@ <link href="https://fonts.googleapis.com/css?family=Poiret+One" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Oswald%7CRoboto+Condensed" rel="stylesheet"> - <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"> - <link rel="apple-touch-icon" href="/images/logo-new.png"/> + <link rel="shortcut icon" href="favicon.ico" type="image/x-icon"> + <link rel="apple-touch-icon" href="logo-new.png"/> <link rel="stylesheet" type="text/css" href="styles/master.css"> - <link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet"> - <link rel="icon" href="images/favicon.ico"> - <script src="master.b.js"></script> - <link rel="manifest" href="manifest.json"> + <link rel="icon" href="favicon.ico"> + <link rel="manifest" href="manifest.json"> <script> + // Check that service workers are supported if ('serviceWorker' in navigator) { - window.addEventListener('load', () => { - navigator.serviceWorker.register('../sw.js').then( () => { - console.log('Service Worker Registered') - }) - }) -} - </script> - - + // Use the window load event to keep the page load performant + window.addEventListener('load', () => { + navigator.serviceWorker.register('service-worker.js'); + }); + } + </script> + </head> <body> diff --git a/webAO/images/desc.png b/static/desc.png Binary files differindex 4d8ec37..4d8ec37 100644 --- a/webAO/images/desc.png +++ b/static/desc.png diff --git a/webAO/favicon.ico b/static/favicon.ico Binary files differindex 83b380d..83b380d 100644 --- a/webAO/favicon.ico +++ b/static/favicon.ico diff --git a/webAO/logo-new-512.png b/static/logo-new-512.png Binary files differindex 33fd586..33fd586 100644 --- a/webAO/logo-new-512.png +++ b/static/logo-new-512.png diff --git a/webAO/logo-new.png b/static/logo-new.png Binary files differindex f53fe30..f53fe30 100644 --- a/webAO/logo-new.png +++ b/static/logo-new.png diff --git a/webAO/manifest.json b/static/manifest.json index acda5d5..f60fd17 100644 --- a/webAO/manifest.json +++ b/static/manifest.json @@ -5,12 +5,12 @@ "scope": "./", "icons": [ { - "src": "/logo-new.png", + "src": "logo-new.png", "sizes": "128x128", "type": "image/png" }, { - "src": "/logo-new-512.png", + "src": "logo-new-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" diff --git a/webAO/client.js b/webAO/client.js index c10d25b..adab9dd 100644 --- a/webAO/client.js +++ b/webAO/client.js @@ -19,28 +19,29 @@ import vanilla_evidence_arr from './constants/evidence.js'; import chatbox_arr from './styles/chatbox/chatboxes.js'; import iniParse from './iniParse'; -import calculatorHandler from './utils/calculatorHandler.js'; import getCookie from './utils/getCookie.js'; import setCookie from './utils/setCookie.js'; import request from './services/request.js'; +import { changeShoutVolume } from './dom/changeShoutVolume.js'; +import { changeSFXVolume } from './dom/changeSFXVolume.js'; +import setEmote from './client/setEmote.js'; +import fileExists from './utils/fileExists.js'; +import queryParser from './utils/queryParser.js'; +import getAnimLength from './utils/getAnimLength.js'; +import getResources from './utils/getResources.js'; const version = process.env.npm_package_version; let client; let viewport; - // Get the arguments from the URL bar -const queryDict = {}; -location.search.substr(1).split('&').forEach((item) => { - queryDict[item.split('=')[0]] = item.split('=')[1]; -}); - -let { ip: serverIP, mode } = queryDict; - +let { + ip: serverIP, mode, asset, theme, +} = queryParser(); // Unless there is an asset URL specified, use the wasabi one const DEFAULT_HOST = 'http://attorneyoffline.de/base/'; -let AO_HOST = queryDict.asset || DEFAULT_HOST; -const THEME = queryDict.theme || 'default'; +let AO_HOST = asset || DEFAULT_HOST; +const THEME = theme || 'default'; const UPDATE_INTERVAL = 60; @@ -121,44 +122,7 @@ class Client extends EventEmitter { this.banned = false; - this.resources = { - holdit: { - src: `${AO_HOST}misc/default/holdit_bubble.png`, - duration: 720, - }, - objection: { - src: `${AO_HOST}misc/default/objection_bubble.png`, - duration: 720, - }, - takethat: { - src: `${AO_HOST}misc/default/takethat_bubble.png`, - duration: 840, - }, - custom: { - src: '', - duration: 840, - }, - witnesstestimony: { - src: `${AO_HOST}themes/${THEME}/witnesstestimony.gif`, - duration: 1560, - sfx: `${AO_HOST}sounds/general/sfx-testimony.opus`, - }, - crossexamination: { - src: `${AO_HOST}themes/${THEME}/crossexamination.gif`, - duration: 1600, - sfx: `${AO_HOST}sounds/general/sfx-testimony2.opus`, - }, - guilty: { - src: `${AO_HOST}themes/${THEME}/guilty.gif`, - duration: 2870, - sfx: `${AO_HOST}sounds/general/sfx-guilty.opus`, - }, - notguilty: { - src: `${AO_HOST}themes/${THEME}/notguilty.gif`, - duration: 2440, - sfx: `${AO_HOST}sounds/general/sfx-notguilty.opus`, - }, - }; + this.resources = getResources(AO_HOST, THEME); this.selectedEmote = -1; this.selectedEvidence = 0; @@ -220,15 +184,15 @@ class Client extends EventEmitter { } /** - * Gets the player's currently selected emote. - */ + * Gets the player's currently selected emote. + */ get emote() { return this.emotes[this.selectedEmote]; } /** - * Gets the current evidence ID unless the player doesn't want to present any evidence - */ + * Gets the current evidence ID unless the player doesn't want to present any evidence + */ get evidence() { return (document.getElementById('button_present').classList.contains('dark')) ? this.selectedEvidence : 0; } @@ -238,12 +202,7 @@ class Client extends EventEmitter { * @param {string} message the message to send */ sendServer(message) { - console.debug(`C: ${message}`); - if (mode === 'replay') { - this.sendSelf(message); - } else { - this.serv.send(message); - } + mode === 'replay' ? this.sendSelf(message) : this.serv.send(message); } /** @@ -270,7 +229,9 @@ class Client extends EventEmitter { */ sendOOC(message) { setCookie('OOC_name', document.getElementById('OOC_name').value); - this.sendServer(`CT#${escapeChat(encodeChat(document.getElementById('OOC_name').value))}#${escapeChat(encodeChat(message))}#%`); + const oocName = `${escapeChat(encodeChat(document.getElementById('OOC_name').value))}`; + const oocMessage = `${escapeChat(encodeChat(message))}`; + this.sendServer(`CT#${oocName}#${oocMessage}#%`); } /** @@ -427,8 +388,6 @@ class Client extends EventEmitter { * to the server. */ joinServer() { - console.log(`Your emulated HDID is ${hdid}`); - this.sendServer(`HI#${hdid}#%`); this.sendServer('ID#webAO#webAO#%'); if (mode !== 'replay') { this.checkUpdater = setInterval(() => this.sendCheck(), 5000); } @@ -1537,7 +1496,7 @@ class Client extends EventEmitter { this.charID = Number(args[3]); document.getElementById('client_charselect').style.display = 'none'; - const me = this.character; + const me = this.chars[this.charID]; this.selectedEmote = -1; const { emotes } = this; const emotesList = document.getElementById('client_emo'); @@ -1742,22 +1701,6 @@ class Viewport { } /** - * Returns whether or not the viewport is busy - * performing a task (animating). - */ - get isAnimating() { - return this._animating; - } - - /** - * Sets the volume of the blip sounds. - * @param {number} volume - */ - set blipVolume(volume) { - this.blipChannels.forEach((channel) => channel.volume = volume); - } - - /** * Sets the volume of the music. * @param {number} volume */ @@ -1794,21 +1737,21 @@ class Viewport { const bgfolder = viewport.bgFolder; const view = document.getElementById('client_fullview'); - + let bench; if ('def,pro,wit'.includes(position)) { - bench = document.getElementById('client_'+position+'_bench'); + bench = document.getElementById(`client_${position}_bench`); } else { bench = document.getElementById('client_bench_classic'); } - + let court; if ('def,pro,wit'.includes(position)) { - court = document.getElementById('client_court_'+position); + court = document.getElementById(`client_court_${position}`); } else { court = document.getElementById('client_court_classic'); } - + const positions = { def: { bg: 'defenseempty.png', @@ -1867,7 +1810,7 @@ class Viewport { } if (viewport.chatmsg.type === 5) { - console.warn("this is a zoom"); + console.warn('this is a zoom'); court.src = `${AO_HOST}themes/default/${encodeURI(speedLines)}`; bench.style.opacity = 0; } else { @@ -1930,76 +1873,6 @@ class Viewport { } /** - * Gets animation length. If the animation cannot be found, it will - * silently fail and return 0 instead. - * @param {string} filename the animation file name - */ - - async getAnimLength(url) { - const extensions = ['.gif', '.webp']; - for (const extension of extensions) { - const urlWithExtension = url + extension; - const exists = await fileExists(urlWithExtension); - if (exists) { - const fileBuffer = await requestBuffer(urlWithExtension); - const length = calculatorHandler[extension](fileBuffer); - return length; - } - } - return 0; - } - - oneSuccess(promises) { - return Promise.all(promises.map((p) => - // If a request fails, count that as a resolution so it will keep - // waiting for other possible successes. If a request succeeds, - // treat it as a rejection so Promise.all immediately bails out. - p.then( - (val) => Promise.reject(val), - (err) => Promise.resolve(err), - ))).then( - // If '.all' resolved, we've just got an array of errors. - (errors) => Promise.reject(errors), - // If '.all' rejected, we've got the result we wanted. - (val) => Promise.resolve(val), - ); - } - - rejectOnError(f) { - return new Promise((resolve, reject) => f.then((res) => { - if (res.ok) resolve(f); - else reject(f); - })); - } - - /** - * Adds up the frame delays to find out how long a GIF is - * I totally didn't steal this - * @param {data} gifFile the GIF data - */ - calculateGifLength(gifFile) { - const d = new Uint8Array(gifFile); - // Thanks to http://justinsomnia.org/2006/10/gif-animation-duration-calculation/ - // And http://www.w3.org/Graphics/GIF/spec-gif89a.txt - let duration = 0; - for (let i = 0; i < d.length; i++) { - // Find a Graphic Control Extension hex(21F904__ ____ __00) - if (d[i] === 0x21 - && d[i + 1] === 0xF9 - && d[i + 2] === 0x04 - && d[i + 7] === 0x00) { - // Swap 5th and 6th bytes to get the delay per frame - const delay = (d[i + 5] << 8) | (d[i + 4] & 0xFF); - - // Should be aware browsers have a minimum frame delay - // e.g. 6ms for IE, 2ms modern browsers (50fps) - duration += delay < 2 ? 10 : delay; - } - } - return duration * 10; - } - - /** * Updates the testimony overaly */ updateTestimony() { @@ -2038,45 +1911,6 @@ class Viewport { } /** - * Sets all the img tags to the right sources - * @param {*} chatmsg - */ - setEmote(charactername, emotename, prefix, pair, side) { - const pairID = pair ? 'pair' : 'char'; - const characterFolder = `${AO_HOST}characters/`; - const position = 'def,pro,wit'.includes(side) ? `${side}_` : ''; - - const gif_s = document.getElementById(`client_${position}${pairID}_gif`); - const png_s = document.getElementById(`client_${position}${pairID}_png`); - const apng_s = document.getElementById(`client_${position}${pairID}_apng`); - const webp_s = document.getElementById(`client_${position}${pairID}_webp`); - const extensionsMap = { - '.gif': gif_s, - '.png': png_s, - '.apng': apng_s, - '.webp': webp_s, - }; - - for (const [extension, htmlElement] of Object.entries(extensionsMap)) { - // Hides all sprites before creating a new sprite - if (this.lastChar !== this.chatmsg.name) { - htmlElement.src = transparentPNG; - } - let url; - if (extension === '.png') { - url = `${characterFolder}${encodeURI(charactername)}/${encodeURI(emotename)}${extension}`; - } else { - url = `${characterFolder}${encodeURI(charactername)}/${encodeURI(prefix)}${encodeURI(emotename)}${extension}`; - } - const exists = fileExistsSync(url); - if (exists) { - htmlElement.src = url; - return; - } - } - } - - /** * Sets a new emote. * This sets up everything before the tick() loops starts * a lot of things can probably be moved here, like starting the shout animation if there is one @@ -2114,7 +1948,8 @@ class Viewport { } this.lastEvi = this.chatmsg.evidence; - if ('def,pro,wit'.includes(this.chatmsg.side)) { + const validSides = ['def', 'pro', 'wit']; + if (validSides.includes(this.chatmsg.side)) { charLayers = document.getElementById(`client_${this.chatmsg.side}_char`); pairLayers = document.getElementById(`client_${this.chatmsg.side}_pair_char`); } @@ -2139,10 +1974,10 @@ class Viewport { checkCallword(this.chatmsg.content); - this.setEmote(this.chatmsg.name.toLowerCase(), this.chatmsg.sprite, '(a)', false, this.chatmsg.side); + setEmote(AO_HOST, this, this.chatmsg.name.toLowerCase(), this.chatmsg.sprite, '(a)', false, this.chatmsg.side); if (this.chatmsg.other_name) { - this.setEmote(this.chatmsg.other_name.toLowerCase(), this.chatmsg.other_emote, '(a)', false, this.chatmsg.side); + setEmote(AO_HOST, this, this.chatmsg.other_name.toLowerCase(), this.chatmsg.other_emote, '(a)', false, this.chatmsg.side); } // gets which shout shall played @@ -2168,23 +2003,15 @@ class Viewport { this.chatmsg.startpreanim = true; let gifLength = 0; - switch (this.chatmsg.type) { - // case 0: - // normal emote, no preanim - case 1: - // play preanim - // Hide message box - chatContainerBox.style.opacity = 0; - // If preanim existed then determine the length - gifLength = await this.getAnimLength(`${AO_HOST}characters/${encodeURI(this.chatmsg.name.toLowerCase())}/${encodeURI(this.chatmsg.preanim)}`); - this.chatmsg.startspeaking = false; - break; - // case 5: - // zoom - default: - this.chatmsg.startspeaking = true; - break; + + if (this.chatmsg.type === 1) { + chatContainerBox.style.opacity = 0; + gifLength = await getAnimLength(`${AO_HOST}characters/${encodeURI(this.chatmsg.name.toLowerCase())}/${encodeURI(this.chatmsg.preanim)}`); + this.chatmsg.startspeaking = false; + } else { + this.chatmsg.startspeaking = true; } + this.chatmsg.preanimdelay = parseInt(gifLength); this.changeBackground(chatmsg.side); @@ -2193,11 +2020,7 @@ class Viewport { resizeChatbox(); // Flip the character - if (this.chatmsg.flip === 1) { - charLayers.style.transform = 'scaleX(-1)'; - } else { - charLayers.style.transform = 'scaleX(1)'; - } + charLayers.style.transform = this.chatmsg.flip === 1 ? 'scaleX(-1)' : 'scaleX(1)'; // Shift by the horizontal offset switch (this.chatmsg.side) { @@ -2220,11 +2043,7 @@ class Viewport { charLayers.style.top = `${Number(this.chatmsg.self_offset[1])}%`; // flip the paired character - if (this.chatmsg.other_flip === 1) { - pairLayers.style.transform = 'scaleX(-1)'; - } else { - pairLayers.style.transform = 'scaleX(1)'; - } + pairLayers.style.transform = this.chatmsg.other_flip === 1 ? 'scaleX(-1)' : 'scaleX(1)'; this.blipChannels.forEach((channel) => channel.src = `${AO_HOST}sounds/general/sfx-blip${encodeURI(this.chatmsg.blips.toLowerCase())}.opus`); @@ -2238,10 +2057,18 @@ class Viewport { // apply effects fg.style.animation = ''; + const badEffects = ['-', 'none']; + if (this.chatmsg.effects[0] && !badEffects.includes(this.chatmsg.effects[i].toLowerCase())) { + const baseEffectUrl = `${AO_HOST}themes/default/effects/`; + fg.src = `${baseEffectUrl}${encodeURI(this.chatmsg.effects[0].toLowerCase())}.webp`; + } else { + fg.src = transparentPNG; + } - if (this.chatmsg.effects[0] && this.chatmsg.effects[0] !== '-' && this.chatmsg.effects[0].toLowerCase() !== 'none' ) { fg.src = `${AO_HOST}themes/default/effects/${encodeURI(this.chatmsg.effects[0].toLowerCase())}.webp`; } else { fg.src = transparentPNG; } - - if (this.chatmsg.sound === '0' || this.chatmsg.sound === '1' || this.chatmsg.sound === '' || this.chatmsg.sound === undefined) { this.chatmsg.sound = this.chatmsg.effects[2]; } + const soundChecks = ['0', '1', '', undefined]; + if (soundChecks.some((check) => this.chatmsg.sound === check)) { + this.chatmsg.sound = this.chatmsg.effects[2]; + } this.tick(); } @@ -2320,7 +2147,7 @@ class Viewport { shoutSprite.style.opacity = 0; shoutSprite.style.animation = ''; const preanim = this.chatmsg.preanim.toLowerCase(); - this.setEmote(charName, preanim, '', false, this.chatmsg.side); + setEmote(AO_HOST, this, charName, preanim, '', false, this.chatmsg.side); charLayers.style.opacity = 1; } @@ -2371,17 +2198,17 @@ class Viewport { } if (this.chatmsg.other_name) { - this.setEmote(pairName, pairEmote, '(a)', true, this.chatmsg.side); + setEmote(AO_HOST, this, pairName, pairEmote, '(a)', true, this.chatmsg.side); pairLayers.style.opacity = 1; } else { pairLayers.style.opacity = 0; } - this.setEmote(charName, charEmote, '(b)', false, this.chatmsg.side); + setEmote(AO_HOST, this, charName, charEmote, '(b)', false, this.chatmsg.side); charLayers.style.opacity = 1; if (this.textnow === this.chatmsg.content) { - this.setEmote(charName, charEmote, '(a)', false, this.chatmsg.side); + setEmote(AO_HOST, this, charName, charEmote, '(a)', false, this.chatmsg.side); charLayers.style.opacity = 1; waitingBox.style.opacity = 1; this._animating = false; @@ -2402,7 +2229,7 @@ class Viewport { if (this.textnow === this.chatmsg.content) { this._animating = false; - this.setEmote(charName, charEmote, '(a)', false, this.chatmsg.side); + setEmote(AO_HOST, this, charName, charEmote, '(a)', false, this.chatmsg.side); charLayers.style.opacity = 1; waitingBox.style.opacity = 1; clearTimeout(this.updater); @@ -2629,22 +2456,6 @@ export function changeMusicVolume() { window.changeMusicVolume = changeMusicVolume; /** - * Triggered by the sound effect volume slider. - */ -export function changeSFXVolume() { - setCookie('sfxVolume', document.getElementById('client_sfxaudio').volume); -} -window.changeSFXVolume = changeSFXVolume; - -/** - * Triggered by the shout volume slider. - */ -export function changeShoutVolume() { - setCookie('shoutVolume', document.getElementById('client_shoutaudio').volume); -} -window.changeShoutVolume = changeShoutVolume; - -/** * Triggered by the testimony volume slider. */ export function changeTestimonyVolume() { @@ -2833,29 +2644,6 @@ async function requestBuffer(url) { } /** - * Checks if a file exists at the specified URI. - * @param {string} url the URI to be checked - */ -async function fileExists(url) { - try { - await request(url); - return true; - } catch (err) { - return false; - } -} -const fileExistsSync = (url) => { - try { - const http = new XMLHttpRequest(); - http.open('HEAD', url, false); - http.send(); - return http.status != 404; - } catch (e) { - return false; - } -}; - -/** * Triggered when the reconnect button is pushed. */ export function ReconnectButton() { @@ -3316,20 +3104,6 @@ export function updateBackgroundPreview() { window.updateBackgroundPreview = updateBackgroundPreview; /** - * Highlights and selects an effect for in-character chat. - * If the same effect button is selected, then the effect is canceled. - * @param {string} effect the new effect to be selected - */ -export function toggleEffect(button) { - if (button.classList.contains('dark')) { - button.className = 'client_button'; - } else { - button.className = 'client_button dark'; - } -} -window.toggleEffect = toggleEffect; - -/** * Highlights and selects a menu. * @param {string} menu the menu to be selected */ diff --git a/webAO/client/setEmote.js b/webAO/client/setEmote.js new file mode 100644 index 0000000..16c95be --- /dev/null +++ b/webAO/client/setEmote.js @@ -0,0 +1,43 @@ +import fileExistsSync from '../utils/fileExistsSync'; + +/** + * Sets all the img tags to the right sources + * @param {*} chatmsg + */ + +const setEmote = (AO_HOST, client, charactername, emotename, prefix, pair, side) => { + const pairID = pair ? 'pair' : 'char'; + const characterFolder = `${AO_HOST}characters/`; + const acceptedPositions = ['def', 'pro', 'wit']; + const position = acceptedPositions.includes(side) ? `${side}_` : ''; + + const gif_s = document.getElementById(`client_${position}${pairID}_gif`); + const png_s = document.getElementById(`client_${position}${pairID}_png`); + const apng_s = document.getElementById(`client_${position}${pairID}_apng`); + const webp_s = document.getElementById(`client_${position}${pairID}_webp`); + const extensionsMap = { + '.gif': gif_s, + '.png': png_s, + '.apng': apng_s, + '.webp': webp_s, + }; + + for (const [extension, htmlElement] of Object.entries(extensionsMap)) { + // Hides all sprites before creating a new sprite + if (client.lastChar !== client.chatmsg.name) { + htmlElement.src = transparentPNG; + } + let url; + if (extension === '.png') { + url = `${characterFolder}${encodeURI(charactername)}/${encodeURI(emotename)}${extension}`; + } else { + url = `${characterFolder}${encodeURI(charactername)}/${encodeURI(prefix)}${encodeURI(emotename)}${extension}`; + } + const exists = fileExistsSync(url); + if (exists) { + htmlElement.src = url; + return; + } + } +}; +export default setEmote; diff --git a/webAO/dom/changeSFXVolume.js b/webAO/dom/changeSFXVolume.js new file mode 100644 index 0000000..0c3009f --- /dev/null +++ b/webAO/dom/changeSFXVolume.js @@ -0,0 +1,10 @@ +import setCookie from '../utils/setCookie'; + +/** + * Triggered by the sound effect volume slider. + */ + +export function changeSFXVolume() { + setCookie('sfxVolume', document.getElementById('client_sfxaudio').volume); +} +window.changeSFXVolume = changeSFXVolume; diff --git a/webAO/dom/changeShoutVolume.js b/webAO/dom/changeShoutVolume.js new file mode 100644 index 0000000..db67e2d --- /dev/null +++ b/webAO/dom/changeShoutVolume.js @@ -0,0 +1,10 @@ +import setCookie from '../utils/setCookie'; + +/** + * Triggered by the shout volume slider. + */ + +export function changeShoutVolume() { + setCookie('shoutVolume', document.getElementById('client_shoutaudio').volume); +} +window.changeShoutVolume = changeShoutVolume; diff --git a/webAO/dom/toggleEffect.js b/webAO/dom/toggleEffect.js new file mode 100644 index 0000000..3d19c9c --- /dev/null +++ b/webAO/dom/toggleEffect.js @@ -0,0 +1,13 @@ +/** + * Highlights and selects an effect for in-character chat. + * If the same effect button is selected, then the effect is canceled. + * @param {string} effect the new effect to be selected + */ +export function toggleEffect(button) { + if (button.classList.contains('dark')) { + button.className = 'client_button'; + } else { + button.className = 'client_button dark'; + } +} +window.toggleEffect = toggleEffect; diff --git a/webAO/master.js b/webAO/master.js index 7e4906e..df79d85 100644 --- a/webAO/master.js +++ b/webAO/master.js @@ -58,7 +58,6 @@ export function setServ(ID) { window.setServ = setServ; function onOpen(_e) { - console.log(`Your emulated HDID is ${hdid}`); masterserver.send('ID#webAO#webAO#%'); masterserver.send('ALL#%'); @@ -120,7 +119,6 @@ function checkOnline(serverID, coIP) { }; oserv.onerror = function (_evt) { - console.warn(`${coIP} threw an error.`); document.getElementById(`server${serverID}`).className = 'unavailable'; }; } diff --git a/webAO/packets/ms.js b/webAO/packets/ms.js new file mode 100644 index 0000000..26851bd --- /dev/null +++ b/webAO/packets/ms.js @@ -0,0 +1,28 @@ +export default { + deskmod, + preanim, + name, + emote, + message, + side, + sfx_name, + emote_modifier, + sfx_delay, + objection_modifier, + evidence, + flip, + realization, + text_color, + showname, + other_charid, + self_hoffset, + self_yoffset, + noninterrupting_preanim, + looping_sfx, + screenshake, + frame_screenshake, + frame_realization, + frame_sfx, + additive, + effect, +}; diff --git a/webAO/styles/master.css b/webAO/styles/master.css index 5d957a0..1ea4796 100644 --- a/webAO/styles/master.css +++ b/webAO/styles/master.css @@ -53,7 +53,7 @@ width: 185px; height: 276px; padding: 0px; - background: url("../images/desc.png") no-repeat; + background: url("../desc.png") no-repeat; border: 2px solid #888; border-radius: 5px; } diff --git a/webAO/sw.js b/webAO/sw.js deleted file mode 100644 index 09a2251..0000000 --- a/webAO/sw.js +++ /dev/null @@ -1,26 +0,0 @@ -const cacheName = 'webAO'; - -// Cache all the files to make a PWA -self.addEventListener('install', (e) => { - e.waitUntil( - caches.open(cacheName).then((cache) => - // Our application only has two files here index.html and manifest.json - // but you can add more such as style.css as your app grows - cache.addAll([ - './', - './index.html', - '../manifest.json', - ])), - ); -}); - -// Our service worker will intercept all fetch requests -// and check if we have cached the file -// if so it will serve the cached file -self.addEventListener('fetch', (event) => { - event.respondWith( - caches.open(cacheName) - .then((cache) => cache.match(event.request, { ignoreSearch: true })) - .then((response) => response || fetch(event.request)), - ); -}); diff --git a/webAO/utils/fileExists.js b/webAO/utils/fileExists.js new file mode 100644 index 0000000..261acda --- /dev/null +++ b/webAO/utils/fileExists.js @@ -0,0 +1,17 @@ +const fileExists = async (url) => { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.onload = function (e) { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + return true; + } + return false; + } + }; + xhr.onerror = function (e) { + return false; + }; + xhr.send(null); +}; +export default fileExists; diff --git a/webAO/utils/fileExistsSync.js b/webAO/utils/fileExistsSync.js new file mode 100644 index 0000000..1d7fde2 --- /dev/null +++ b/webAO/utils/fileExistsSync.js @@ -0,0 +1,11 @@ +const fileExistsSync = (url) => { + try { + const http = new XMLHttpRequest(); + http.open('HEAD', url, false); + http.send(); + return http.status != 404; + } catch (e) { + return false; + } +}; +export default fileExistsSync; diff --git a/webAO/utils/getAnimLength.js b/webAO/utils/getAnimLength.js new file mode 100644 index 0000000..e64703f --- /dev/null +++ b/webAO/utils/getAnimLength.js @@ -0,0 +1,22 @@ +import calculatorHandler from './calculatorHandler'; +import fileExists from './fileExists.js'; +/** + * Gets animation length. If the animation cannot be found, it will + * silently fail and return 0 instead. + * @param {string} filename the animation file name + */ + +const getAnimLength = async (url) => { + const extensions = ['.gif', '.webp']; + for (const extension of extensions) { + const urlWithExtension = url + extension; + const exists = await fileExists(urlWithExtension); + if (exists) { + const fileBuffer = await requestBuffer(urlWithExtension); + const length = calculatorHandler[extension](fileBuffer); + return length; + } + } + return 0; +}; +export default getAnimLength; diff --git a/webAO/utils/getResources.js b/webAO/utils/getResources.js new file mode 100644 index 0000000..a0c513e --- /dev/null +++ b/webAO/utils/getResources.js @@ -0,0 +1,39 @@ +const getResources = (AO_HOST, THEME) => ({ + holdit: { + src: `${AO_HOST}misc/default/holdit_bubble.png`, + duration: 720, + }, + objection: { + src: `${AO_HOST}misc/default/objection_bubble.png`, + duration: 720, + }, + takethat: { + src: `${AO_HOST}misc/default/takethat_bubble.png`, + duration: 840, + }, + custom: { + src: '', + duration: 840, + }, + witnesstestimony: { + src: `${AO_HOST}themes/${THEME}/witnesstestimony.gif`, + duration: 1560, + sfx: `${AO_HOST}sounds/general/sfx-testimony.opus`, + }, + crossexamination: { + src: `${AO_HOST}themes/${THEME}/crossexamination.gif`, + duration: 1600, + sfx: `${AO_HOST}sounds/general/sfx-testimony2.opus`, + }, + guilty: { + src: `${AO_HOST}themes/${THEME}/guilty.gif`, + duration: 2870, + sfx: `${AO_HOST}sounds/general/sfx-guilty.opus`, + }, + notguilty: { + src: `${AO_HOST}themes/${THEME}/notguilty.gif`, + duration: 2440, + sfx: `${AO_HOST}sounds/general/sfx-notguilty.opus`, + }, +}); +export default getResources; diff --git a/webAO/utils/queryParser.js b/webAO/utils/queryParser.js new file mode 100644 index 0000000..1c2b83a --- /dev/null +++ b/webAO/utils/queryParser.js @@ -0,0 +1,9 @@ +// Get the arguments from the URL bar +const queryParser = () => { + const queryDict = {}; + location.search.substr(1).split('&').forEach((item) => { + queryDict[item.split('=')[0]] = item.split('=')[1]; + }); + return queryDict; +}; +export default queryParser; diff --git a/webpack.config.js b/webpack.config.js index 75a8c0b..06b067d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,6 +3,10 @@ const path = require('path'); const dotenv = require('dotenv'); const webpack = require('webpack'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const CopyPlugin = require('copy-webpack-plugin'); +const WorkboxPlugin = require('workbox-webpack-plugin'); +const glob = require('glob'); // this will update the process.env with environment variables in .env file dotenv.config(); @@ -12,6 +16,10 @@ module.exports = { ui: './webAO/ui.js', client: './webAO/client.js', master: './webAO/master.js', + dom: glob.sync('./webAO/dom/*.js'), + }, + node: { + global: true, }, devtool: 'source-map', devServer: { @@ -50,8 +58,9 @@ module.exports = { ], }, output: { - path: path.resolve(__dirname, 'webAO'), - filename: '[name].b.js', + path: path.resolve(__dirname, 'dist'), + filename: '[name].[contenthash].bundle.js', + clean: true, }, performance: { hints: false, @@ -59,9 +68,33 @@ module.exports = { maxAssetSize: 512000, }, plugins: [ + new CopyPlugin({ + patterns: [ + { from: path.resolve(__dirname, 'webAO', 'styles'), to: 'styles' }, + { from: path.resolve(__dirname, 'static') }, + { from: path.resolve(__dirname, 'webAO', 'golden'), to: 'golden' }, + { from: path.resolve(__dirname, 'webAO', 'lib'), to: 'lib' }, + { from: path.resolve(__dirname, 'webAO', 'sounds'), to: 'sounds' }, + ], + }), + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'public/index.html', + chunks: ['master', 'sw'], + title: 'Attorney Online', + }), + + new HtmlWebpackPlugin({ + title: 'Attorney Online', + filename: 'client.html', + chunks: ['client', 'ui', 'dom'], + template: 'public/client.html', + }), new webpack.DefinePlugin({ 'process.env': JSON.stringify(process.env), }), + new WorkboxPlugin.GenerateSW(), + ], }; |
