Feature: configurable toggle for music console and count of guests
This commit is contained in:
parent
dcf12771d6
commit
6b292b32ad
@ -114,9 +114,43 @@ export class ConfigUI extends SceneFeature {
|
|||||||
// Side Screens Toggle
|
// Side Screens Toggle
|
||||||
createToggle('Side Screens', 'sideScreensEnabled');
|
createToggle('Side Screens', 'sideScreensEnabled');
|
||||||
|
|
||||||
|
// Music Console Toggle
|
||||||
|
createToggle('Music Console', 'consoleEnabled', (enabled) => {
|
||||||
|
const consoleFeature = sceneFeatureManager.features.find(f => f.constructor.name === 'MusicConsole');
|
||||||
|
if (consoleFeature && consoleFeature.group) consoleFeature.group.visible = enabled;
|
||||||
|
});
|
||||||
|
|
||||||
// Console RGB Toggle
|
// Console RGB Toggle
|
||||||
createToggle('Console RGB Panel', 'consoleRGBEnabled');
|
createToggle('Console RGB Panel', 'consoleRGBEnabled');
|
||||||
|
|
||||||
|
// Guest Count Input
|
||||||
|
const guestRow = document.createElement('div');
|
||||||
|
guestRow.style.display = 'flex';
|
||||||
|
guestRow.style.alignItems = 'center';
|
||||||
|
guestRow.style.justifyContent = 'space-between';
|
||||||
|
|
||||||
|
const guestLabel = document.createElement('label');
|
||||||
|
guestLabel.innerText = 'Guest Count';
|
||||||
|
|
||||||
|
const guestInput = document.createElement('input');
|
||||||
|
guestInput.type = 'number';
|
||||||
|
guestInput.min = '0';
|
||||||
|
guestInput.max = '500';
|
||||||
|
guestInput.value = state.config.guestCount;
|
||||||
|
guestInput.style.width = '60px';
|
||||||
|
guestInput.onchange = (e) => {
|
||||||
|
const val = parseInt(e.target.value, 10);
|
||||||
|
state.config.guestCount = val;
|
||||||
|
saveConfig();
|
||||||
|
const guestsFeature = sceneFeatureManager.features.find(f => f.constructor.name === 'PartyGuests');
|
||||||
|
if (guestsFeature) guestsFeature.setGuestCount(val);
|
||||||
|
};
|
||||||
|
this.guestInput = guestInput;
|
||||||
|
|
||||||
|
guestRow.appendChild(guestLabel);
|
||||||
|
guestRow.appendChild(guestInput);
|
||||||
|
rightContainer.appendChild(guestRow);
|
||||||
|
|
||||||
// DJ Hat Selector
|
// DJ Hat Selector
|
||||||
const hatRow = document.createElement('div');
|
const hatRow = document.createElement('div');
|
||||||
hatRow.style.display = 'flex';
|
hatRow.style.display = 'flex';
|
||||||
@ -323,6 +357,8 @@ export class ConfigUI extends SceneFeature {
|
|||||||
lasersEnabled: true,
|
lasersEnabled: true,
|
||||||
sideScreensEnabled: true,
|
sideScreensEnabled: true,
|
||||||
consoleRGBEnabled: true,
|
consoleRGBEnabled: true,
|
||||||
|
consoleEnabled: true,
|
||||||
|
guestCount: 150,
|
||||||
djHat: 'None'
|
djHat: 'None'
|
||||||
};
|
};
|
||||||
for (const key in defaults) {
|
for (const key in defaults) {
|
||||||
@ -332,6 +368,7 @@ export class ConfigUI extends SceneFeature {
|
|||||||
if (this.toggles[key].callback) this.toggles[key].callback(defaults[key]);
|
if (this.toggles[key].callback) this.toggles[key].callback(defaults[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.guestInput) this.guestInput.value = defaults.guestCount;
|
||||||
if (this.hatSelect) this.hatSelect.value = defaults.djHat;
|
if (this.hatSelect) this.hatSelect.value = defaults.djHat;
|
||||||
this.updateStatus();
|
this.updateStatus();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -164,7 +164,7 @@ export class DJ extends SceneFeature {
|
|||||||
if (this.armTimer <= 0) {
|
if (this.armTimer <= 0) {
|
||||||
this.armState = Math.floor(Math.random() * 5);
|
this.armState = Math.floor(Math.random() * 5);
|
||||||
this.armTimer = 2 + Math.random() * 4;
|
this.armTimer = 2 + Math.random() * 4;
|
||||||
if (Math.random() < 0.3) this.armState = 4; // Twiddling some more
|
if (Math.random() < 0.3 && state.config.consoleEnabled) this.armState = 4; // Twiddling some more
|
||||||
}
|
}
|
||||||
|
|
||||||
const upAngle = Math.PI * 0.85;
|
const upAngle = Math.PI * 0.85;
|
||||||
@ -176,7 +176,7 @@ export class DJ extends SceneFeature {
|
|||||||
let targetLeftX = 0;
|
let targetLeftX = 0;
|
||||||
let targetRightX = 0;
|
let targetRightX = 0;
|
||||||
|
|
||||||
if (this.armState === 4) {
|
if (this.armState === 4 && state.config.consoleEnabled) {
|
||||||
// Twiddling
|
// Twiddling
|
||||||
targetLeftZ = 0.2;
|
targetLeftZ = 0.2;
|
||||||
targetRightZ = 0.2;
|
targetRightZ = 0.2;
|
||||||
@ -192,7 +192,7 @@ export class DJ extends SceneFeature {
|
|||||||
this.currentLeftAngleX = THREE.MathUtils.lerp(this.currentLeftAngleX, targetLeftX, deltaTime * 5);
|
this.currentLeftAngleX = THREE.MathUtils.lerp(this.currentLeftAngleX, targetLeftX, deltaTime * 5);
|
||||||
this.currentRightAngleX = THREE.MathUtils.lerp(this.currentRightAngleX, targetRightX, deltaTime * 5);
|
this.currentRightAngleX = THREE.MathUtils.lerp(this.currentRightAngleX, targetRightX, deltaTime * 5);
|
||||||
|
|
||||||
if (this.armState === 4) {
|
if (this.armState === 4 && state.config.consoleEnabled) {
|
||||||
const t = time * 15;
|
const t = time * 15;
|
||||||
this.leftArm.rotation.z = -this.currentLeftAngle + Math.cos(t) * 0.05;
|
this.leftArm.rotation.z = -this.currentLeftAngle + Math.cos(t) * 0.05;
|
||||||
this.rightArm.rotation.z = this.currentRightAngle + Math.sin(t) * 0.05;
|
this.rightArm.rotation.z = this.currentRightAngle + Math.sin(t) * 0.05;
|
||||||
@ -215,7 +215,8 @@ export class DJ extends SceneFeature {
|
|||||||
this.moveTimer -= deltaTime;
|
this.moveTimer -= deltaTime;
|
||||||
if (this.moveTimer <= 0) {
|
if (this.moveTimer <= 0) {
|
||||||
this.state = 'MOVING';
|
this.state = 'MOVING';
|
||||||
this.targetX = (Math.random() - 0.5) * 2.5;
|
const range = state.config.consoleEnabled ? 2.5 : 10.0;
|
||||||
|
this.targetX = (Math.random() - 0.5) * range;
|
||||||
}
|
}
|
||||||
} else if (this.state === 'MOVING') {
|
} else if (this.state === 'MOVING') {
|
||||||
const speed = 1.5;
|
const speed = 1.5;
|
||||||
|
|||||||
@ -21,6 +21,8 @@ export class MusicConsole extends SceneFeature {
|
|||||||
// Position on stage, centered
|
// Position on stage, centered
|
||||||
group.position.set(0, stageY, -16.5);
|
group.position.set(0, stageY, -16.5);
|
||||||
state.scene.add(group);
|
state.scene.add(group);
|
||||||
|
this.group = group;
|
||||||
|
this.group.visible = state.config.consoleEnabled;
|
||||||
|
|
||||||
// 1. The Stand/Table Body
|
// 1. The Stand/Table Body
|
||||||
const standGeo = new THREE.BoxGeometry(consoleWidth, consoleHeight, consoleDepth);
|
const standGeo = new THREE.BoxGeometry(consoleWidth, consoleHeight, consoleDepth);
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import sceneFeatureManager from './SceneFeatureManager.js';
|
|||||||
const stageHeight = 1.5;
|
const stageHeight = 1.5;
|
||||||
const stageDepth = 5;
|
const stageDepth = 5;
|
||||||
const length = 25;
|
const length = 25;
|
||||||
const numGuests = 150;
|
|
||||||
const moveSpeed = 0.8;
|
const moveSpeed = 0.8;
|
||||||
const movementArea = { x: 15, z: length, y: 0, centerZ: -2 };
|
const movementArea = { x: 15, z: length, y: 0, centerZ: -2 };
|
||||||
const jumpChance = 0.01;
|
const jumpChance = 0.01;
|
||||||
@ -25,6 +24,7 @@ export class PartyGuests extends SceneFeature {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.guests = [];
|
this.guests = [];
|
||||||
|
this.guestPool = []; // Store all created guests to reuse them
|
||||||
sceneFeatureManager.register(this);
|
sceneFeatureManager.register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,96 +36,115 @@ export class PartyGuests extends SceneFeature {
|
|||||||
const headGeo = new THREE.SphereGeometry(0.25, 16, 16);
|
const headGeo = new THREE.SphereGeometry(0.25, 16, 16);
|
||||||
// Arm: Ellipsoid (Scaled Sphere)
|
// Arm: Ellipsoid (Scaled Sphere)
|
||||||
const armGeo = new THREE.SphereGeometry(0.12, 16, 16);
|
const armGeo = new THREE.SphereGeometry(0.12, 16, 16);
|
||||||
|
|
||||||
|
this.geometries = { bodyGeo, headGeo, armGeo };
|
||||||
|
|
||||||
const createGuests = () => {
|
// Initialize with config count
|
||||||
for (let i = 0; i < numGuests; i++) {
|
this.setGuestCount(state.config.guestCount);
|
||||||
// Random Color
|
}
|
||||||
// Dark gray-blue shades
|
|
||||||
const color = new THREE.Color().setHSL(
|
|
||||||
0.6 + (Math.random() * 0.1 - 0.05), // Hue around 0.6 (blue)
|
|
||||||
0.1 + Math.random() * 0.1, // Low saturation
|
|
||||||
0.01 + Math.random() * 0.05 // Much darker lightness
|
|
||||||
);
|
|
||||||
const material = new THREE.MeshStandardMaterial({
|
|
||||||
color: color,
|
|
||||||
roughness: 0.6,
|
|
||||||
metalness: 0.1,
|
|
||||||
});
|
|
||||||
|
|
||||||
const group = new THREE.Group();
|
createGuest() {
|
||||||
|
// Random Color
|
||||||
|
// Dark gray-blue shades
|
||||||
|
const color = new THREE.Color().setHSL(
|
||||||
|
0.6 + (Math.random() * 0.1 - 0.05), // Hue around 0.6 (blue)
|
||||||
|
0.1 + Math.random() * 0.1, // Low saturation
|
||||||
|
0.01 + Math.random() * 0.05 // Much darker lightness
|
||||||
|
);
|
||||||
|
const material = new THREE.MeshStandardMaterial({
|
||||||
|
color: color,
|
||||||
|
roughness: 0.6,
|
||||||
|
metalness: 0.1,
|
||||||
|
});
|
||||||
|
|
||||||
const scale = 0.85 + Math.random() * 0.3;
|
const group = new THREE.Group();
|
||||||
group.scale.setScalar(scale);
|
|
||||||
|
|
||||||
// Body
|
const scale = 0.85 + Math.random() * 0.3;
|
||||||
const body = new THREE.Mesh(bodyGeo, material);
|
group.scale.setScalar(scale);
|
||||||
body.position.y = 0.7; // Center of capsule (0.8 length + 0.3*2 radius = 1.4 total height. Center at 0.7)
|
|
||||||
body.castShadow = true;
|
|
||||||
body.receiveShadow = true;
|
|
||||||
group.add(body);
|
|
||||||
|
|
||||||
// Head
|
// Body
|
||||||
const head = new THREE.Mesh(headGeo, material);
|
const body = new THREE.Mesh(this.geometries.bodyGeo, material);
|
||||||
head.position.y = 1.55; // Top of body
|
body.position.y = 0.7;
|
||||||
head.castShadow = true;
|
body.castShadow = true;
|
||||||
head.receiveShadow = true;
|
body.receiveShadow = true;
|
||||||
group.add(head);
|
group.add(body);
|
||||||
|
|
||||||
// Arms
|
// Head
|
||||||
const createArm = (isLeft) => {
|
const head = new THREE.Mesh(this.geometries.headGeo, material);
|
||||||
const pivot = new THREE.Group();
|
head.position.y = 1.55;
|
||||||
// Shoulder position
|
head.castShadow = true;
|
||||||
pivot.position.set(isLeft ? -0.35 : 0.35, 1.3, 0);
|
head.receiveShadow = true;
|
||||||
|
group.add(head);
|
||||||
const arm = new THREE.Mesh(armGeo, material);
|
|
||||||
arm.scale.set(1, 3.5, 1); // Ellipsoid
|
|
||||||
arm.position.y = -0.4; // Hang down from pivot
|
|
||||||
arm.castShadow = true;
|
|
||||||
arm.receiveShadow = true;
|
|
||||||
|
|
||||||
pivot.add(arm);
|
|
||||||
return pivot;
|
|
||||||
};
|
|
||||||
|
|
||||||
const leftArm = createArm(true);
|
// Arms
|
||||||
const rightArm = createArm(false);
|
const createArm = (isLeft) => {
|
||||||
|
const pivot = new THREE.Group();
|
||||||
group.add(leftArm);
|
// Shoulder position
|
||||||
group.add(rightArm);
|
pivot.position.set(isLeft ? -0.35 : 0.35, 1.3, 0);
|
||||||
|
|
||||||
// Position
|
const arm = new THREE.Mesh(this.geometries.armGeo, material);
|
||||||
const pos = new THREE.Vector3(
|
arm.scale.set(1, 3.5, 1); // Ellipsoid
|
||||||
(Math.random() - 0.5) * movementArea.x,
|
arm.position.y = -0.4; // Hang down from pivot
|
||||||
0,
|
arm.castShadow = true;
|
||||||
movementArea.centerZ + ( rushIn
|
arm.receiveShadow = true;
|
||||||
? (((Math.random()-0.5) * length * 0.6) - length * 0.3)
|
|
||||||
: ((Math.random()-0.5) * length))
|
pivot.add(arm);
|
||||||
);
|
return pivot;
|
||||||
|
|
||||||
group.position.copy(pos);
|
|
||||||
group.visible = false;
|
|
||||||
state.scene.add(group);
|
|
||||||
|
|
||||||
this.guests.push({
|
|
||||||
mesh: group,
|
|
||||||
leftArm,
|
|
||||||
rightArm,
|
|
||||||
state: 'WAITING',
|
|
||||||
targetPosition: pos.clone(),
|
|
||||||
waitStartTime: 0,
|
|
||||||
waitTime: 3 + Math.random() * 4, // Wait longer: 3-7 seconds
|
|
||||||
isJumping: false,
|
|
||||||
jumpStartTime: 0,
|
|
||||||
jumpHeight: 0,
|
|
||||||
shouldRaiseArms: false,
|
|
||||||
handsUpTimer: 0,
|
|
||||||
handsRaisedType: 'BOTH',
|
|
||||||
randomOffset: Math.random() * 100
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
createGuests();
|
const leftArm = createArm(true);
|
||||||
|
const rightArm = createArm(false);
|
||||||
|
|
||||||
|
group.add(leftArm);
|
||||||
|
group.add(rightArm);
|
||||||
|
|
||||||
|
// Position
|
||||||
|
const pos = new THREE.Vector3(
|
||||||
|
(Math.random() - 0.5) * movementArea.x,
|
||||||
|
0,
|
||||||
|
movementArea.centerZ + ( rushIn
|
||||||
|
? (((Math.random()-0.5) * length * 0.6) - length * 0.3)
|
||||||
|
: ((Math.random()-0.5) * length))
|
||||||
|
);
|
||||||
|
|
||||||
|
group.position.copy(pos);
|
||||||
|
group.visible = false;
|
||||||
|
state.scene.add(group);
|
||||||
|
|
||||||
|
return {
|
||||||
|
mesh: group,
|
||||||
|
leftArm,
|
||||||
|
rightArm,
|
||||||
|
state: 'WAITING',
|
||||||
|
targetPosition: pos.clone(),
|
||||||
|
waitStartTime: 0,
|
||||||
|
waitTime: 3 + Math.random() * 4,
|
||||||
|
isJumping: false,
|
||||||
|
jumpStartTime: 0,
|
||||||
|
jumpHeight: 0,
|
||||||
|
shouldRaiseArms: false,
|
||||||
|
handsUpTimer: 0,
|
||||||
|
handsRaisedType: 'BOTH',
|
||||||
|
randomOffset: Math.random() * 100
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
setGuestCount(count) {
|
||||||
|
// Ensure we have enough guests in the pool
|
||||||
|
while (this.guestPool.length < count) {
|
||||||
|
this.guestPool.push(this.createGuest());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update visibility and active list
|
||||||
|
this.guests = [];
|
||||||
|
this.guestPool.forEach((guest, index) => {
|
||||||
|
if (index < count) {
|
||||||
|
guest.mesh.visible = state.partyStarted; // Only visible if party started
|
||||||
|
this.guests.push(guest);
|
||||||
|
} else {
|
||||||
|
guest.mesh.visible = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
update(deltaTime) {
|
update(deltaTime) {
|
||||||
@ -280,7 +299,7 @@ export class PartyGuests extends SceneFeature {
|
|||||||
onPartyStart() {
|
onPartyStart() {
|
||||||
const stageFrontZ = -40 / 2 + 5 + 5; // In front of the stage
|
const stageFrontZ = -40 / 2 + 5 + 5; // In front of the stage
|
||||||
this.guests.forEach((guestObj, index) => {
|
this.guests.forEach((guestObj, index) => {
|
||||||
guestObj.mesh.visible = true;
|
guestObj.mesh.visible = true; // Make sure active guests are visible
|
||||||
// Rush to the stage
|
// Rush to the stage
|
||||||
guestObj.state = 'MOVING';
|
guestObj.state = 'MOVING';
|
||||||
if (index % 2 === 0) {
|
if (index % 2 === 0) {
|
||||||
|
|||||||
@ -8,6 +8,8 @@ export function initState() {
|
|||||||
lasersEnabled: true,
|
lasersEnabled: true,
|
||||||
sideScreensEnabled: true,
|
sideScreensEnabled: true,
|
||||||
consoleRGBEnabled: true,
|
consoleRGBEnabled: true,
|
||||||
|
consoleEnabled: true,
|
||||||
|
guestCount: 150,
|
||||||
djHat: 'None' // 'None', 'Santa', 'Top Hat'
|
djHat: 'None' // 'None', 'Santa', 'Top Hat'
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user