From 47b7645046b74ec38817660aa19dac48ae6e9a4b Mon Sep 17 00:00:00 2001 From: Dejvino Date: Sun, 4 Jan 2026 06:47:21 +0000 Subject: [PATCH] Feature: DJ has a gameboy music controller --- party-stage/src/scene/config-ui.js | 4 ++ party-stage/src/scene/dj.js | 69 +++++++++++++++++++++++++++++- party-stage/src/state.js | 1 + 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/party-stage/src/scene/config-ui.js b/party-stage/src/scene/config-ui.js index 6c74f74..ef6136a 100644 --- a/party-stage/src/scene/config-ui.js +++ b/party-stage/src/scene/config-ui.js @@ -123,6 +123,9 @@ export class ConfigUI extends SceneFeature { // Console RGB Toggle createToggle('Console RGB Panel', 'consoleRGBEnabled'); + // Gameboy Toggle + createToggle('Gameboy', 'gameboyEnabled'); + // Guest Count Input const guestRow = document.createElement('div'); guestRow.style.display = 'flex'; @@ -358,6 +361,7 @@ export class ConfigUI extends SceneFeature { sideScreensEnabled: true, consoleRGBEnabled: true, consoleEnabled: true, + gameboyEnabled: false, guestCount: 150, djHat: 'None' }; diff --git a/party-stage/src/scene/dj.js b/party-stage/src/scene/dj.js index 954162f..91636cf 100644 --- a/party-stage/src/scene/dj.js +++ b/party-stage/src/scene/dj.js @@ -142,6 +142,42 @@ export class DJ extends SceneFeature { topHatGroup.visible = false; this.head.add(topHatGroup); this.hats['Top Hat'] = topHatGroup; + + // --- Gameboy / Portable Console --- + const gbGroup = new THREE.Group(); + // Body + const gbGeo = new THREE.BoxGeometry(0.2, 0.3, 0.05); + const gbMat = new THREE.MeshStandardMaterial({ color: 0x222222, roughness: 0.4 }); + const gbBody = new THREE.Mesh(gbGeo, gbMat); + gbGroup.add(gbBody); + + // Screen (Emissive) + const screenGeo = new THREE.PlaneGeometry(0.16, 0.12); + this.gbScreenMat = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); + const screen = new THREE.Mesh(screenGeo, this.gbScreenMat); + screen.position.set(0, 0.05, 0.026); + gbGroup.add(screen); + + // Knobs/Buttons + const knobGeo = new THREE.CylinderGeometry(0.02, 0.02, 0.02, 8); + const knobMat = new THREE.MeshStandardMaterial({ color: 0xcccccc }); + + const k1 = new THREE.Mesh(knobGeo, knobMat); + k1.rotation.x = Math.PI/2; + k1.position.set(-0.05, -0.08, 0.026); + gbGroup.add(k1); + + const k2 = new THREE.Mesh(knobGeo, knobMat); + k2.rotation.x = Math.PI/2; + k2.position.set(0.05, -0.08, 0.026); + gbGroup.add(k2); + + // Attach to Left Arm (Hand position) + gbGroup.position.set(0, -0.9, 0.1); + gbGroup.rotation.x = -Math.PI / 4; // Tilted up + gbGroup.rotation.z = Math.PI; // Upside down + this.leftArm.add(gbGroup); + this.gameboy = gbGroup; } update(deltaTime) { @@ -162,9 +198,16 @@ export class DJ extends SceneFeature { // Update Arm State this.armTimer -= deltaTime; if (this.armTimer <= 0) { - this.armState = Math.floor(Math.random() * 5); + this.armState = Math.floor(Math.random() * 4); // 0-3: Dance moves this.armTimer = 2 + Math.random() * 4; - if (Math.random() < 0.3 && state.config.consoleEnabled) this.armState = 4; // Twiddling some more + + const rand = Math.random(); + // Gameboy Fiddling + if (state.config.gameboyEnabled && rand < 0.35) { + this.armState = 5; + } else if (state.config.consoleEnabled && rand < 0.65) { + this.armState = 4; // Console Twiddling + } } const upAngle = Math.PI * 0.85; @@ -182,6 +225,13 @@ export class DJ extends SceneFeature { targetRightZ = 0.2; targetLeftX = twiddleX; targetRightX = twiddleX; + } else if (this.armState === 5 && state.config.gameboyEnabled) { + targetLeftZ = -0.3; + targetRightZ = -0.5; // Cross inwards + const timeRotX = Math.cos(time * 0.5) * 0.3; + const consoleRotX = state.config.consoleEnabled ? -0.5 : 0; + targetLeftX = -1.0 + timeRotX + consoleRotX; // Lift up + targetRightX = -1.1 + timeRotX + consoleRotX; // Lift up } else { targetLeftZ = (this.armState === 1 || this.armState === 3) ? upAngle : downAngle; targetRightZ = (this.armState === 2 || this.armState === 3) ? upAngle : downAngle; @@ -198,6 +248,12 @@ export class DJ extends SceneFeature { this.rightArm.rotation.z = this.currentRightAngle + Math.sin(t) * 0.05; this.leftArm.rotation.x = this.currentLeftAngleX + Math.sin(t) * 0.1; this.rightArm.rotation.x = this.currentRightAngleX + Math.cos(t) * 0.1; + } else if (this.armState === 5 && state.config.gameboyEnabled) { + const t = time * 20; + this.leftArm.rotation.z = -this.currentLeftAngle + Math.sin(t * 0.2) * 0.01; + this.rightArm.rotation.z = this.currentRightAngle + Math.sin(t) * 0.05; + this.leftArm.rotation.x = this.currentLeftAngleX + Math.cos(t * 0.2) * 0.01; + this.rightArm.rotation.x = this.currentRightAngleX + Math.cos(t) * 0.05; } else { const wave = Math.sin(time * 8) * 0.1; const beatBounce = beatIntensity * 0.2; @@ -233,6 +289,15 @@ export class DJ extends SceneFeature { for (const [name, mesh] of Object.entries(this.hats)) { mesh.visible = (state.config.djHat === name); } + + // Update Gameboy + if (this.gameboy) { + this.gameboy.visible = state.config.gameboyEnabled; + if (this.gameboy.visible && state.music) { + const hue = (time * 0.5) % 1; + this.gbScreenMat.color.setHSL(hue, 1.0, 0.5); + } + } } onPartyStart() { diff --git a/party-stage/src/state.js b/party-stage/src/state.js index 77fa8d0..84c3ef2 100644 --- a/party-stage/src/state.js +++ b/party-stage/src/state.js @@ -9,6 +9,7 @@ export function initState() { sideScreensEnabled: true, consoleRGBEnabled: true, consoleEnabled: true, + gameboyEnabled: false, guestCount: 150, djHat: 'None' // 'None', 'Santa', 'Top Hat' };