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
|
||||
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
|
||||
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
|
||||
const hatRow = document.createElement('div');
|
||||
hatRow.style.display = 'flex';
|
||||
@ -323,6 +357,8 @@ export class ConfigUI extends SceneFeature {
|
||||
lasersEnabled: true,
|
||||
sideScreensEnabled: true,
|
||||
consoleRGBEnabled: true,
|
||||
consoleEnabled: true,
|
||||
guestCount: 150,
|
||||
djHat: 'None'
|
||||
};
|
||||
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.guestInput) this.guestInput.value = defaults.guestCount;
|
||||
if (this.hatSelect) this.hatSelect.value = defaults.djHat;
|
||||
this.updateStatus();
|
||||
};
|
||||
|
||||
@ -164,7 +164,7 @@ export class DJ extends SceneFeature {
|
||||
if (this.armTimer <= 0) {
|
||||
this.armState = Math.floor(Math.random() * 5);
|
||||
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;
|
||||
@ -176,7 +176,7 @@ export class DJ extends SceneFeature {
|
||||
let targetLeftX = 0;
|
||||
let targetRightX = 0;
|
||||
|
||||
if (this.armState === 4) {
|
||||
if (this.armState === 4 && state.config.consoleEnabled) {
|
||||
// Twiddling
|
||||
targetLeftZ = 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.currentRightAngleX = THREE.MathUtils.lerp(this.currentRightAngleX, targetRightX, deltaTime * 5);
|
||||
|
||||
if (this.armState === 4) {
|
||||
if (this.armState === 4 && state.config.consoleEnabled) {
|
||||
const t = time * 15;
|
||||
this.leftArm.rotation.z = -this.currentLeftAngle + Math.cos(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;
|
||||
if (this.moveTimer <= 0) {
|
||||
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') {
|
||||
const speed = 1.5;
|
||||
|
||||
@ -21,6 +21,8 @@ export class MusicConsole extends SceneFeature {
|
||||
// Position on stage, centered
|
||||
group.position.set(0, stageY, -16.5);
|
||||
state.scene.add(group);
|
||||
this.group = group;
|
||||
this.group.visible = state.config.consoleEnabled;
|
||||
|
||||
// 1. The Stand/Table Body
|
||||
const standGeo = new THREE.BoxGeometry(consoleWidth, consoleHeight, consoleDepth);
|
||||
|
||||
@ -7,7 +7,6 @@ import sceneFeatureManager from './SceneFeatureManager.js';
|
||||
const stageHeight = 1.5;
|
||||
const stageDepth = 5;
|
||||
const length = 25;
|
||||
const numGuests = 150;
|
||||
const moveSpeed = 0.8;
|
||||
const movementArea = { x: 15, z: length, y: 0, centerZ: -2 };
|
||||
const jumpChance = 0.01;
|
||||
@ -25,6 +24,7 @@ export class PartyGuests extends SceneFeature {
|
||||
constructor() {
|
||||
super();
|
||||
this.guests = [];
|
||||
this.guestPool = []; // Store all created guests to reuse them
|
||||
sceneFeatureManager.register(this);
|
||||
}
|
||||
|
||||
@ -36,96 +36,115 @@ export class PartyGuests extends SceneFeature {
|
||||
const headGeo = new THREE.SphereGeometry(0.25, 16, 16);
|
||||
// Arm: Ellipsoid (Scaled Sphere)
|
||||
const armGeo = new THREE.SphereGeometry(0.12, 16, 16);
|
||||
|
||||
this.geometries = { bodyGeo, headGeo, armGeo };
|
||||
|
||||
const createGuests = () => {
|
||||
for (let i = 0; i < numGuests; i++) {
|
||||
// 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,
|
||||
});
|
||||
// Initialize with config count
|
||||
this.setGuestCount(state.config.guestCount);
|
||||
}
|
||||
|
||||
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;
|
||||
group.scale.setScalar(scale);
|
||||
const group = new THREE.Group();
|
||||
|
||||
// Body
|
||||
const body = new THREE.Mesh(bodyGeo, material);
|
||||
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);
|
||||
const scale = 0.85 + Math.random() * 0.3;
|
||||
group.scale.setScalar(scale);
|
||||
|
||||
// Head
|
||||
const head = new THREE.Mesh(headGeo, material);
|
||||
head.position.y = 1.55; // Top of body
|
||||
head.castShadow = true;
|
||||
head.receiveShadow = true;
|
||||
group.add(head);
|
||||
// Body
|
||||
const body = new THREE.Mesh(this.geometries.bodyGeo, material);
|
||||
body.position.y = 0.7;
|
||||
body.castShadow = true;
|
||||
body.receiveShadow = true;
|
||||
group.add(body);
|
||||
|
||||
// Arms
|
||||
const createArm = (isLeft) => {
|
||||
const pivot = new THREE.Group();
|
||||
// Shoulder position
|
||||
pivot.position.set(isLeft ? -0.35 : 0.35, 1.3, 0);
|
||||
|
||||
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;
|
||||
};
|
||||
// Head
|
||||
const head = new THREE.Mesh(this.geometries.headGeo, material);
|
||||
head.position.y = 1.55;
|
||||
head.castShadow = true;
|
||||
head.receiveShadow = true;
|
||||
group.add(head);
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
// Arms
|
||||
const createArm = (isLeft) => {
|
||||
const pivot = new THREE.Group();
|
||||
// Shoulder position
|
||||
pivot.position.set(isLeft ? -0.35 : 0.35, 1.3, 0);
|
||||
|
||||
const arm = new THREE.Mesh(this.geometries.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;
|
||||
};
|
||||
|
||||
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) {
|
||||
@ -280,7 +299,7 @@ export class PartyGuests extends SceneFeature {
|
||||
onPartyStart() {
|
||||
const stageFrontZ = -40 / 2 + 5 + 5; // In front of the stage
|
||||
this.guests.forEach((guestObj, index) => {
|
||||
guestObj.mesh.visible = true;
|
||||
guestObj.mesh.visible = true; // Make sure active guests are visible
|
||||
// Rush to the stage
|
||||
guestObj.state = 'MOVING';
|
||||
if (index % 2 === 0) {
|
||||
|
||||
@ -8,6 +8,8 @@ export function initState() {
|
||||
lasersEnabled: true,
|
||||
sideScreensEnabled: true,
|
||||
consoleRGBEnabled: true,
|
||||
consoleEnabled: true,
|
||||
guestCount: 150,
|
||||
djHat: 'None' // 'None', 'Santa', 'Top Hat'
|
||||
};
|
||||
try {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user