From fc991d3d3e5bf951615b8d72fbc7c5d370833227 Mon Sep 17 00:00:00 2001 From: haliphax Date: Thu, 31 Aug 2023 10:49:26 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A8=20prettier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +- docker-compose.yml | 2 +- html/constants.js | 28 +++--- html/index.html | 2 +- html/oauth/index.html | 12 +-- html/oauth/index.js | 19 ++-- html/overlay/avatars/avatar.js | 96 ++++++++++---------- html/overlay/avatars/mario/avatar.js | 2 +- html/overlay/avatars/skeleton/avatar.js | 2 +- html/overlay/config.json | 5 +- html/overlay/index.html | 2 +- html/overlay/index.js | 59 ++++++------- html/overlay/scenes/director.js | 88 ++++++++++--------- html/overlay/scenes/main.js | 112 +++++++++++++----------- html/overlay/styles.css | 3 +- html/overlay/twitch.js | 27 +++--- html/overlay/webfontfile.js | 22 ++--- html/util.js | 18 ++-- wsl.yml | 2 +- 19 files changed, 257 insertions(+), 249 deletions(-) diff --git a/README.md b/README.md index 8d4b930..4e1f12c 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ viewed in full-screen._ The following commands are available from the overlay's chat integration, with more to come: -| Command | Description | -|---|---| +| Command | Description | +| ----------------- | -------------------------------------------- | | `!avatar ` | Sets the user's avatar (if the value exists) | # 🏠 Running it yourself @@ -52,7 +52,6 @@ You may craft an override file with a different hostname if you just want to use something other than `localhost`, of course; you don't have to be using WSL to take advantage of this. - [hosts file]: https://www.freecodecamp.org/news/how-to-find-and-edit-a-windows-hosts-file/ [nginx]: https://nginx.org [Traefik]: https://traefik.io diff --git a/docker-compose.yml b/docker-compose.yml index 4051ad2..0195c32 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3' +version: "3" services: nginx: diff --git a/html/constants.js b/html/constants.js index ed32d6f..18a55a3 100644 --- a/html/constants.js +++ b/html/constants.js @@ -2,20 +2,20 @@ const constants = { CHANCE_TO_CHANGE: 0.5, CHANCE_TO_CHANGE_IF_WALKING: 0.2, CHANCE_TO_WALK: 0.5, - CLIENT_ID: 'v9n67wa15yeokwksrb6a12x8vybbgw', - FACE_LEFT: 'left', - FACE_RIGHT: 'right', - FONT_FAMILY: 'Syne Mono', + CLIENT_ID: "v9n67wa15yeokwksrb6a12x8vybbgw", + FACE_LEFT: "left", + FACE_RIGHT: "right", + FONT_FAMILY: "Syne Mono", GRAVITY: 0, LABEL_FALL_MARGIN: 12, LABEL_FLOAT_VELOCITY: 100, LABEL_RISE_MARGIN: 8, LABEL_SIZE: 20, - OAUTH_REDIRECT_URI: '', - OAUTH_URL: '', + OAUTH_REDIRECT_URI: "", + OAUTH_URL: "", SCREEN_HEIGHT: 1080, SCREEN_WIDTH: 1920, - STROKE_COLOR: '#000', + STROKE_COLOR: "#000", STROKE_THICKNESS: 6, TIMEOUT_MAX: 5000, WALK_MAX_VELOCITY: 60, @@ -23,12 +23,14 @@ const constants = { }; constants.OAUTH_REDIRECT_URI = encodeURIComponent( - window.location.href.replace(/(?:overlay\/?)?(?:[^/]\.html|$)/i, 'oauth')); + window.location.href.replace(/(?:overlay\/?)?(?:[^/]\.html|$)/i, "oauth"), +); -constants.OAUTH_URL = `https://id.twitch.tv/oauth2/authorize` - + `?client_id=${constants.CLIENT_ID}` - + `&redirect_uri=${constants.OAUTH_REDIRECT_URI}` - + `&response_type=token` - + `&scope=chat:read%20chat:edit`; +constants.OAUTH_URL = + `https://id.twitch.tv/oauth2/authorize` + + `?client_id=${constants.CLIENT_ID}` + + `&redirect_uri=${constants.OAUTH_REDIRECT_URI}` + + `&response_type=token` + + `&scope=chat:read%20chat:edit`; export default constants; diff --git a/html/index.html b/html/index.html index 3abc7e1..e07b20b 100644 --- a/html/index.html +++ b/html/index.html @@ -1,4 +1,4 @@ - + diff --git a/html/oauth/index.html b/html/oauth/index.html index bf82e02..c7d0495 100644 --- a/html/oauth/index.html +++ b/html/oauth/index.html @@ -1,4 +1,4 @@ - + @@ -11,13 +11,13 @@

Overlay URL builder

Use this form to construct your overlay URL. Channel should - be your Twitch username, and Bot username should be - the username of the account your token is authorized for. (Most likely, - that's your username unless you have a separate bot account.) + be your Twitch username, and Bot username should be the + username of the account your token is authorized for. (Most likely, that's + your username unless you have a separate bot account.)

- After submitting the form, you will be taken to the live overlay. Copy - the URL from your browser for use in your streaming software (e.g. OBS). + After submitting the form, you will be taken to the live overlay. Copy the + URL from your browser for use in your streaming software (e.g. OBS).

diff --git a/html/oauth/index.js b/html/oauth/index.js index ba11971..e1b0094 100644 --- a/html/oauth/index.js +++ b/html/oauth/index.js @@ -1,14 +1,17 @@ -import { hs } from '../util.js'; +import { hs } from "../util.js"; -const form = document.querySelector('form'); +const form = document.querySelector("form"); -const onChange = e => { +const onChange = (e) => { const rgx = RegExp(`&${e.target.id}=[^&]+`); - form.action = form.action.replace(rgx, ''); - form.action = - `${form.action}&${e.target.id}=${encodeURIComponent(e.target.value)}`; + form.action = form.action.replace(rgx, ""); + form.action = `${form.action}&${e.target.id}=${encodeURIComponent( + e.target.value, + )}`; }; -form.setAttribute('action', `${form.action}#oauth=${hs.access_token}`); -form.querySelectorAll('input').forEach(v => v.addEventListener('change', onChange)); +form.setAttribute("action", `${form.action}#oauth=${hs.access_token}`); +form + .querySelectorAll("input") + .forEach((v) => v.addEventListener("change", onChange)); diff --git a/html/overlay/avatars/avatar.js b/html/overlay/avatars/avatar.js index 8650194..b74d3be 100644 --- a/html/overlay/avatars/avatar.js +++ b/html/overlay/avatars/avatar.js @@ -1,38 +1,34 @@ -import constants from '../../constants.js'; +import constants from "../../constants.js"; /** on-screen avatar with state machine */ class Avatar extends Phaser.Physics.Arcade.Sprite { constructor(scene, avatarDefs, username, key, x) { super(scene, x, 0, key); - this - .setOrigin(0.5, 1) - .setScale(avatarDefs[key].metadata.scale); + this.setOrigin(0.5, 1).setScale(avatarDefs[key].metadata.scale); /** @type {string} The avatar owner's username */ this.username = username; /** @type {string} The avatar's sprite name */ this.key = key; /** @type {string} The direction the avatar's sprite is facing */ - this.face = Math.random() < constants.CHANCE_TO_CHANGE - ? constants.FACE_LEFT - : constants.FACE_RIGHT; + this.face = + Math.random() < constants.CHANCE_TO_CHANGE + ? constants.FACE_LEFT + : constants.FACE_RIGHT; /** * @type {number} * Half the width of the avatar's sprite; used in calculations */ this.halfWidth = this.displayWidth / 2; /** @type {number} The original label Y position */ - this.labelYPosition = - -this.displayHeight - (constants.LABEL_SIZE / 2); + this.labelYPosition = -this.displayHeight - constants.LABEL_SIZE / 2; /** @type {Phase.GameObjects.Label} The username label for the avatar */ - this.label = - this.scene.add.text( - 0, this.labelYPosition, username, - { - fontFamily: `"${constants.FONT_FAMILY}"`, - fontSize: constants.LABEL_SIZE, - stroke: constants.STROKE_COLOR, - strokeThickness: constants.STROKE_THICKNESS, - }) + this.label = this.scene.add + .text(0, this.labelYPosition, username, { + fontFamily: `"${constants.FONT_FAMILY}"`, + fontSize: constants.LABEL_SIZE, + stroke: constants.STROKE_COLOR, + strokeThickness: constants.STROKE_THICKNESS, + }) .setOrigin(0.5, 1); /** The avatar's container, allowing us to manipulate a single object */ this.container = this.scene.add.container(); @@ -49,8 +45,7 @@ class Avatar extends Phaser.Physics.Arcade.Sprite { /** destroy avatar; stop state machine */ destroy() { - if (this.stateMachine) - this.stateMachine.stateService.stop(); + if (this.stateMachine) this.stateMachine.stateService.stop(); this.cleanup(); } @@ -79,15 +74,14 @@ class Avatar extends Phaser.Physics.Arcade.Sprite { if (!this.container.body || !this.label.body) return setTimeout(this.ready.bind(this), 100); - this.container.body.setSize( - this.displayWidth, this.displayHeight, true); - this.container.setSize( - this.displayWidth, this.displayHeight, true); + this.container.body.setSize(this.displayWidth, this.displayHeight, true); + this.container.setSize(this.displayWidth, this.displayHeight, true); this.container.add(this); this.container.add(this.label); this.container.setPosition( Math.random() * (constants.SCREEN_WIDTH - this.displayWidth), - constants.SCREEN_HEIGHT); + constants.SCREEN_HEIGHT, + ); } /** update the avatar per animation frame */ @@ -97,31 +91,36 @@ class Avatar extends Phaser.Physics.Arcade.Sprite { // raise/lower labels to avoid overlap - const notThisLabel = this.scene.labelGroup.children.entries - .filter(l => l != this); - - const shouldRise = notThisLabel.some(l => - l.body.left <= this.label.body.right - && l.body.right >= this.label.body.left - && l.body.top < this.label.body.bottom + constants.LABEL_RISE_MARGIN - && (l.body.bottom > this.label.body.bottom - || (l.body.bottom == this.label.body.bottom - && l.body.x < this.label.body.x))); - - const shouldFall = !shouldRise - && this.label.y < this.labelYPosition - && !notThisLabel.some(l => - l.body.left <= this.label.body.right - && l.body.right >= this.label.body.left - && l.body.top < this.label.body.bottom + constants.LABEL_FALL_MARGIN - && l.body.bottom > this.label.body.bottom); + const notThisLabel = this.scene.labelGroup.children.entries.filter( + (l) => l != this, + ); + + const shouldRise = notThisLabel.some( + (l) => + l.body.left <= this.label.body.right && + l.body.right >= this.label.body.left && + l.body.top < this.label.body.bottom + constants.LABEL_RISE_MARGIN && + (l.body.bottom > this.label.body.bottom || + (l.body.bottom == this.label.body.bottom && + l.body.x < this.label.body.x)), + ); + + const shouldFall = + !shouldRise && + this.label.y < this.labelYPosition && + !notThisLabel.some( + (l) => + l.body.left <= this.label.body.right && + l.body.right >= this.label.body.left && + l.body.top < this.label.body.bottom + constants.LABEL_FALL_MARGIN && + l.body.bottom > this.label.body.bottom, + ); if (shouldRise) this.label.body.setVelocityY(-constants.LABEL_FLOAT_VELOCITY); else if (shouldFall) this.label.body.setVelocityY(constants.LABEL_FLOAT_VELOCITY); - else - this.label.body.setVelocityY(0); + else this.label.body.setVelocityY(0); if (this.label.y > this.labelYPosition) this.label.setPosition(this.label.x, this.labelYPosition); @@ -131,9 +130,10 @@ class Avatar extends Phaser.Physics.Arcade.Sprite { /** flip the sprite's facing */ changeFace() { - this.face = this.face == constants.FACE_LEFT - ? constants.FACE_RIGHT - : constants.FACE_LEFT; + this.face = + this.face == constants.FACE_LEFT + ? constants.FACE_RIGHT + : constants.FACE_LEFT; //console.debug(`facing ${this.face}`); } } diff --git a/html/overlay/avatars/mario/avatar.js b/html/overlay/avatars/mario/avatar.js index 9b871c4..f5ecc9d 100644 --- a/html/overlay/avatars/mario/avatar.js +++ b/html/overlay/avatars/mario/avatar.js @@ -1,4 +1,4 @@ -import Avatar from '../avatar.js'; +import Avatar from "../avatar.js"; const metadata = { animations: { diff --git a/html/overlay/avatars/skeleton/avatar.js b/html/overlay/avatars/skeleton/avatar.js index 43d9335..1465638 100644 --- a/html/overlay/avatars/skeleton/avatar.js +++ b/html/overlay/avatars/skeleton/avatar.js @@ -1,4 +1,4 @@ -import Avatar from '../avatar.js'; +import Avatar from "../avatar.js"; const metadata = { animations: { diff --git a/html/overlay/config.json b/html/overlay/config.json index 04ec60c..97df48f 100644 --- a/html/overlay/config.json +++ b/html/overlay/config.json @@ -1,6 +1,3 @@ { - "avatars": [ - "mario", - "skeleton" - ] + "avatars": ["mario", "skeleton"] } diff --git a/html/overlay/index.html b/html/overlay/index.html index b6e259f..cbe64a6 100644 --- a/html/overlay/index.html +++ b/html/overlay/index.html @@ -1,4 +1,4 @@ - + diff --git a/html/overlay/index.js b/html/overlay/index.js index 581ab1a..57677f3 100644 --- a/html/overlay/index.js +++ b/html/overlay/index.js @@ -1,26 +1,26 @@ -import constants from '../constants.js'; -import DirectorScene from './scenes/director.js'; -import emitter from './emitter.js'; -import { hs } from '../util.js'; -import { twitchClient } from './twitch.js'; +import constants from "../constants.js"; +import DirectorScene from "./scenes/director.js"; +import emitter from "./emitter.js"; +import { hs } from "../util.js"; +import { twitchClient } from "./twitch.js"; -if (!hs.hasOwnProperty('oauth') && !hs.hasOwnProperty('demo')) +if (!hs.hasOwnProperty("oauth") && !hs.hasOwnProperty("demo")) window.location = constants.OAUTH_URL; const options = { height: constants.SCREEN_HEIGHT, physics: { - default: 'arcade', + default: "arcade", arcade: { debug: false, gravity: { - y: (hs.gravity || constants.GRAVITY), + y: hs.gravity || constants.GRAVITY, }, }, }, pixelArt: true, render: { - transparent: (hs.hasOwnProperty('demo') ? false : true), + transparent: hs.hasOwnProperty("demo") ? false : true, }, scene: DirectorScene, type: Phaser.AUTO, @@ -36,19 +36,18 @@ const avatars = {}; /** late-bound Twitch connection */ const twitch = twitchClient(); -if (hs.hasOwnProperty('oauth')) { +if (hs.hasOwnProperty("oauth")) { /** regex for parsing commands from chat messages */ const commandRgx = /^(\![-_.a-z0-9]+)(?:\s+(.+))?$/i; - twitch.on('message', (channel, tags, message, self) => { - if (self) - return; + twitch.on("message", (channel, tags, message, self) => { + if (self) return; // add avatar for chatter if they don't have one - if (!avatars.hasOwnProperty(tags['display-name'])) { - avatars[tags['display-name']] = true; + if (!avatars.hasOwnProperty(tags["display-name"])) { + avatars[tags["display-name"]] = true; // TODO: remember selection from before - emitter.emit('new-avatar', tags['display-name']); + emitter.emit("new-avatar", tags["display-name"]); } const cmd = commandRgx.exec(message); @@ -63,8 +62,8 @@ if (hs.hasOwnProperty('oauth')) { // TODO: command timeouts switch (command) { - case 'avatar': - emitter.emit('change-avatar', tags['display-name'], args); + case "avatar": + emitter.emit("change-avatar", tags["display-name"], args); break; } }); @@ -73,26 +72,24 @@ if (hs.hasOwnProperty('oauth')) { } // Avatars for testing/demonstration -if (hs.hasOwnProperty('demo')) { +if (hs.hasOwnProperty("demo")) { let howMany = 20; try { - howMany = parseInt(hs.demo) - } - catch { + howMany = parseInt(hs.demo); + } catch { // } - setTimeout(() => - { - const values = crypto.getRandomValues(new Uint16Array(howMany)) - .map(v => v % (8 * howMany)); + setTimeout(() => { + const values = crypto + .getRandomValues(new Uint16Array(howMany)) + .map((v) => v % (8 * howMany)); - for (let i = 0; i < howMany; i++) { - emitter.emit('new-avatar', `Avatar#${i + 1}`, null, -values[i]); - } - }, - 1000); + for (let i = 0; i < howMany; i++) { + emitter.emit("new-avatar", `Avatar#${i + 1}`, null, -values[i]); + } + }, 1000); } // for debugging diff --git a/html/overlay/scenes/director.js b/html/overlay/scenes/director.js index a3a2608..11e1e85 100644 --- a/html/overlay/scenes/director.js +++ b/html/overlay/scenes/director.js @@ -1,40 +1,42 @@ -import constants from '../../constants.js'; -import emitter from '../emitter.js'; -import WebFontFile from '../webfontfile.js'; +import constants from "../../constants.js"; +import emitter from "../emitter.js"; +import WebFontFile from "../webfontfile.js"; // scenes -import MainScene from './main.js'; +import MainScene from "./main.js"; const avatarDefs = {}; const avatarKeys = []; // fetch is relative to document -await fetch('./config.json').then(r => r.json()).then(async d => { - for (let avatar of d.avatars) { - //console.debug(`importing ${avatar}`); - - // import is relative to script - await import(`../avatars/${avatar}/avatar.js`).then(m => { - //console.debug(`defining ${avatar}`); - avatarDefs[avatar] = { - metadata: m.metadata, - class: m.ExtendedAvatar, - }; - }); - } +await fetch("./config.json") + .then((r) => r.json()) + .then(async (d) => { + for (let avatar of d.avatars) { + //console.debug(`importing ${avatar}`); + + // import is relative to script + await import(`../avatars/${avatar}/avatar.js`).then((m) => { + //console.debug(`defining ${avatar}`); + avatarDefs[avatar] = { + metadata: m.metadata, + class: m.ExtendedAvatar, + }; + }); + } - Object.keys(avatarDefs).map(k => avatarKeys.push(k)); -}); + Object.keys(avatarDefs).map((k) => avatarKeys.push(k)); + }); class DirectorScene extends Phaser.Scene { constructor() { - super('director'); + super("director"); - this.currentScene = 'main'; + this.currentScene = "main"; this.avatars = {}; // event handlers - emitter.on('change-avatar', this.onChangeAvatar.bind(this)); - emitter.on('new-avatar', this.onNewAvatar.bind(this)); + emitter.on("change-avatar", this.onChangeAvatar.bind(this)); + emitter.on("new-avatar", this.onNewAvatar.bind(this)); } preload() { @@ -42,10 +44,12 @@ class DirectorScene extends Phaser.Scene { for (let avatar of avatarKeys) { const def = avatarDefs[avatar]; - const extras = (def.metadata.hasOwnProperty('extruded') - && def.metadata.extruded) + const extras = + def.metadata.hasOwnProperty("extruded") && def.metadata.extruded ? { margin: 1, spacing: 2 } - : { /* empty */ }; + : { + /* empty */ + }; // load is relative to document this.load.spritesheet(avatar, `./avatars/${avatar}/avatar.png`, { @@ -55,7 +59,7 @@ class DirectorScene extends Phaser.Scene { }); } - this.load.on('complete', this.ready.bind(this)); + this.load.on("complete", this.ready.bind(this)); } ready() { @@ -67,16 +71,17 @@ class DirectorScene extends Phaser.Scene { for (let animKey of Object.keys(def.metadata.animations)) { const anim = def.metadata.animations[animKey]; - for (let variation of - Object.keys(anim).filter(v => v != 'frameRate')) - { + for (let variation of Object.keys(anim).filter( + (v) => v != "frameRate", + )) { const key = `${avatar}.${animKey}.${variation}`; //console.debug(`creating ${key}`); this.anims.create({ key: key, - frames: this.anims.generateFrameNumbers( - avatar, { frames: anim[variation] }), + frames: this.anims.generateFrameNumbers(avatar, { + frames: anim[variation], + }), frameRate: anim.frameRate, repeat: -1, }); @@ -85,7 +90,7 @@ class DirectorScene extends Phaser.Scene { } // add child scenes - this.scene.add('main', MainScene, true); + this.scene.add("main", MainScene, true); this.scene.start(this.currentScene); } @@ -111,24 +116,25 @@ class DirectorScene extends Phaser.Scene { newAvatar.container.x = containerX; newAvatar.label.y = labelY; - } - else { + } else { this.onNewAvatar(username, key); } } /** new avatar event */ onNewAvatar(username, key = null, labelY = null) { - if (this.avatars.hasOwnProperty(username)) - return; + if (this.avatars.hasOwnProperty(username)) return; if (key === null || key == undefined) key = avatarKeys[Math.floor(Math.random() * avatarKeys.length)]; - else if (!avatarKeys.includes(key)) - return; + else if (!avatarKeys.includes(key)) return; const avatar = new avatarDefs[key].class( - this.game.scene.keys[this.currentScene], avatarDefs, username, key); + this.game.scene.keys[this.currentScene], + avatarDefs, + username, + key, + ); if (labelY !== null) { avatar.label.y = labelY; @@ -136,7 +142,7 @@ class DirectorScene extends Phaser.Scene { this.avatars[username] = avatar; // add new avatar to scenes - emitter.emit('register-avatar', this.avatars[username]); + emitter.emit("register-avatar", this.avatars[username]); } } diff --git a/html/overlay/scenes/main.js b/html/overlay/scenes/main.js index 10357c6..ca75a8d 100644 --- a/html/overlay/scenes/main.js +++ b/html/overlay/scenes/main.js @@ -1,20 +1,22 @@ -import constants from '../../constants.js'; -import { createMachine, interpret } - from 'https://unpkg.com/xstate@4/dist/xstate.web.js'; -import emitter from '../emitter.js'; -import { uuid } from '../../util.js'; +import constants from "../../constants.js"; +import { + createMachine, + interpret, +} from "https://unpkg.com/xstate@4/dist/xstate.web.js"; +import emitter from "../emitter.js"; +import { uuid } from "../../util.js"; /** main game scene */ class MainScene extends Phaser.Scene { constructor() { - super('main'); + super("main"); } create() { // events - this.events.on('pause', this.onPauseScene.bind(this)); - this.events.on('resume', this.onResumeScene.bind(this)); - emitter.on('register-avatar', this.onRegisterAvatar.bind(this)); + this.events.on("pause", this.onPauseScene.bind(this)); + this.events.on("resume", this.onResumeScene.bind(this)); + emitter.on("register-avatar", this.onRegisterAvatar.bind(this)); this.spriteGroup = this.physics.add.group({ bounceX: 1, @@ -30,14 +32,14 @@ class MainScene extends Phaser.Scene { update(time, delta) { for (let avatar of this.spriteGroup.children.entries) { // turn around if avatar hits the edge of the screen - if (avatar.stateMachine.currentState.value == 'walking' - && ( - (avatar.container.body.x <= 0 - && avatar.container.body.velocity.x < 0) - || (avatar.container.body.x >= - constants.SCREEN_WIDTH - avatar.displayWidth - && avatar.container.body.velocity.x > 0))) - { + if ( + avatar.stateMachine.currentState.value == "walking" && + ((avatar.container.body.x <= 0 && + avatar.container.body.velocity.x < 0) || + (avatar.container.body.x >= + constants.SCREEN_WIDTH - avatar.displayWidth && + avatar.container.body.velocity.x > 0)) + ) { avatar.changeFace(); avatar.play(`${avatar.key}.walking.${avatar.face}`); avatar.container.body.setVelocityX(-avatar.container.body.velocity.x); @@ -78,24 +80,27 @@ class MainScene extends Phaser.Scene { currentState: createMachine( { id: uuid(), - initial: 'idling', + initial: "idling", states: { deciding: { - entry: ['decide'], + entry: ["decide"], on: { DECIDED: [ - { target: 'walking', cond: (_, evt) => evt.next == 'walking' }, - { target: 'idling', cond: (_, evt) => evt.next == 'idling' }, + { + target: "walking", + cond: (_, evt) => evt.next == "walking", + }, + { target: "idling", cond: (_, evt) => evt.next == "idling" }, ], }, }, idling: { - entry: ['idle'], - on: { DECIDE: ['deciding'], }, + entry: ["idle"], + on: { DECIDE: ["deciding"] }, }, walking: { - entry: ['walk'], - on: { DECIDE: ['deciding'], }, + entry: ["walk"], + on: { DECIDE: ["deciding"] }, }, }, }, @@ -107,15 +112,14 @@ class MainScene extends Phaser.Scene { if (rand < constants.CHANCE_TO_WALK) { //console.debug('decided to walk'); - next = 'walking'; - } - else { + next = "walking"; + } else { //console.debug('decided to idle'); - next = 'idling'; + next = "idling"; } avatar.stateMachine.stateService.send({ - type: 'DECIDED', + type: "DECIDED", next: next, prev: avatar.stateMachine.currentState.value, }); @@ -125,48 +129,52 @@ class MainScene extends Phaser.Scene { avatar.container.body.setVelocityX(0); avatar.play(`${avatar.key}.idle.${avatar.face}`); setTimeout( - avatar.stateMachine.stateService.send.bind(this, 'DECIDE'), - Math.random() * constants.TIMEOUT_MAX); + avatar.stateMachine.stateService.send.bind(this, "DECIDE"), + Math.random() * constants.TIMEOUT_MAX, + ); }, walk: (context, event) => { //console.debug('walking'); - let swap = Math.random() < ( - event.prev == 'walking' + let swap = + Math.random() < + (event.prev == "walking" ? constants.CHANCE_TO_CHANGE_IF_WALKING : constants.CHANCE_TO_CHANGE); - if (swap) - avatar.changeFace(); + if (swap) avatar.changeFace(); - if (swap || event.prev != 'walking') { + if (swap || event.prev != "walking") { avatar.container.body.setVelocityX( - (constants.WALK_MIN_VELOCITY - + Math.random() - * (constants.WALK_MAX_VELOCITY - - constants.WALK_MIN_VELOCITY)) - * (avatar.face == constants.FACE_LEFT ? -1 : 1)); + (constants.WALK_MIN_VELOCITY + + Math.random() * + (constants.WALK_MAX_VELOCITY - + constants.WALK_MIN_VELOCITY)) * + (avatar.face == constants.FACE_LEFT ? -1 : 1), + ); avatar.play(`${avatar.key}.walking.${avatar.face}`); } setTimeout( - avatar.stateMachine.stateService.send.bind(this, 'DECIDE'), - Math.random() * constants.TIMEOUT_MAX); + avatar.stateMachine.stateService.send.bind(this, "DECIDE"), + Math.random() * constants.TIMEOUT_MAX, + ); }, }, - }), - }; + }, + ), + }; /** The service used for communicating with this avatar's state machine */ - avatar.stateMachine.stateService = - interpret(avatar.stateMachine.currentState); - avatar.stateMachine.stateService.onTransition(state => { - avatar.stateMachine.previousState = - avatar.stateMachine.currentState.name; + avatar.stateMachine.stateService = interpret( + avatar.stateMachine.currentState, + ); + avatar.stateMachine.stateService.onTransition((state) => { + avatar.stateMachine.previousState = avatar.stateMachine.currentState.name; avatar.stateMachine.currentState = state; }); avatar.stateMachine.stateService.start(); - avatar.stateMachine.stateService.send('idle'); + avatar.stateMachine.stateService.send("idle"); } } diff --git a/html/overlay/styles.css b/html/overlay/styles.css index 7720286..0f2c87b 100644 --- a/html/overlay/styles.css +++ b/html/overlay/styles.css @@ -1,4 +1,5 @@ -html, body { +html, +body { background-color: rgba(0, 0, 0, 0); height: 100%; margin: 0; diff --git a/html/overlay/twitch.js b/html/overlay/twitch.js index 2ea2d38..507af69 100644 --- a/html/overlay/twitch.js +++ b/html/overlay/twitch.js @@ -1,21 +1,18 @@ -import { hs } from '../util.js'; +import { hs } from "../util.js"; /** Twitch client */ -const twitchClient = () => new tmi.Client({ - channels: [hs.channel], - identity: { - username: hs.username, - password: `oauth:${hs.oauth}`, - }, -}); +const twitchClient = () => + new tmi.Client({ + channels: [hs.channel], + identity: { + username: hs.username, + password: `oauth:${hs.oauth}`, + }, + }); /** based on tags, is this user the broadcaster? */ -const isBroadcaster = tags => tags.badges.hasOwnProperty('broadcaster'); +const isBroadcaster = (tags) => tags.badges.hasOwnProperty("broadcaster"); /** based on tags, is this user a moderator? */ -const isModerator = tags => tags.mod; +const isModerator = (tags) => tags.mod; -export { - isBroadcaster, - isModerator, - twitchClient, -}; +export { isBroadcaster, isModerator, twitchClient }; diff --git a/html/overlay/webfontfile.js b/html/overlay/webfontfile.js index 9b8fba5..79b65ee 100644 --- a/html/overlay/webfontfile.js +++ b/html/overlay/webfontfile.js @@ -1,36 +1,32 @@ // https://blog.ourcade.co/posts/2020/phaser-3-google-fonts-webfontloader/ -class WebFontFile extends Phaser.Loader.File -{ +class WebFontFile extends Phaser.Loader.File { /** * @param {Phaser.Loader.LoaderPlugin} loader * @param {string | string[]} fontNames * @param {string} [service] */ - constructor(loader, fontNames, service = 'google') - { - super(loader, { type: 'webfont', key: fontNames.toString() }); + constructor(loader, fontNames, service = "google") { + super(loader, { type: "webfont", key: fontNames.toString() }); this.fontNames = Array.isArray(fontNames) ? fontNames : [fontNames]; this.service = service; } - load() - { + load() { const config = { active: () => this.loader.nextFile(this, true), }; - switch (this.service) - { - case 'google': - config['google'] = { + switch (this.service) { + case "google": + config["google"] = { families: this.fontNames, }; - break + break; default: - throw new Error('Unsupported font service'); + throw new Error("Unsupported font service"); } WebFont.load(config); diff --git a/html/util.js b/html/util.js index f34b3c0..f147597 100644 --- a/html/util.js +++ b/html/util.js @@ -1,6 +1,10 @@ /** parsed hash string (key-value pairs become object properties) */ const hs = Object.fromEntries( - window.location.hash.substring(1)?.split('&').map(v => v.split('=')) ?? []); + window.location.hash + .substring(1) + ?.split("&") + .map((v) => v.split("=")) ?? [], +); /** * Generates a UUID. @@ -8,12 +12,10 @@ const hs = Object.fromEntries( * @returns {string} The UUID */ const uuid = () => - 'xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx'.replace(/[^-]/g, c => - (c == 'x' + "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx".replace(/[^-]/g, (c) => + c == "x" ? (crypto.getRandomValues(new Uint8Array(1))[0] & 15).toString(16) - : (crypto.getRandomValues(new Uint8Array(1))[0] & 5).toString())); + : (crypto.getRandomValues(new Uint8Array(1))[0] & 5).toString(), + ); -export { - hs, - uuid, -}; +export { hs, uuid }; diff --git a/wsl.yml b/wsl.yml index ad92e35..30ffa72 100644 --- a/wsl.yml +++ b/wsl.yml @@ -1,4 +1,4 @@ -version: '3' +version: "3" services: traefik: