Files
PROFERIS - Mi³osz Stocki dd8f6b2277 Idle animations. Closes #14
2026-07-09 14:01:00 +02:00

147 lines
3.8 KiB
JavaScript

import IK from './kinematics.js';
import Motion from './motion.js';
let activeMode = 'none'; // 'none', 'circles', 'erratic', 'park-bounce'
let parkPosition = { x: 0, y: 80 };
let anchorPos = { x: 0, y: 80 };
let timerId = null;
let currentAngle = 0;
let bounceState = 'to-park'; // 'to-park' or 'to-anchor'
const IDLE_WAIT_MS = 5000;
const CONTINUOUS_WAIT_MS = 500; // 0.5s pause between idle movements
const RADIUS = 20;
export const IdleEffects = {
setMode(mode) {
activeMode = mode || 'none';
localStorage.setItem('wiji_idle_mode', activeMode);
// Update all dropdowns in UI to keep them in sync
document.querySelectorAll('.idle-mode-select').forEach(el => {
if (el.value !== activeMode) el.value = activeMode;
});
if (activeMode === 'none') {
this.stopTimer();
} else {
if (!Motion.isMoving && localStorage.getItem('wiji_homed') === 'true') {
this.updateAnchorPos();
this.startTimer(IDLE_WAIT_MS);
}
}
},
getMode() {
return activeMode;
},
setParkPosition(x, y) {
parkPosition = { x, y };
},
updateAnchorPos() {
const { steps1, steps2 } = Motion.getSteps();
const pos = IK.forward(IK.stepsToRad(steps1), IK.stepsToRad(steps2));
if (pos.valid) {
anchorPos = { x: pos.endX, y: pos.endY };
currentAngle = 0;
bounceState = 'to-park';
}
},
startTimer(ms) {
this.stopTimer();
if (activeMode === 'none') return;
timerId = setTimeout(() => {
this.triggerNextMove();
}, ms);
},
stopTimer() {
if (timerId) {
clearTimeout(timerId);
timerId = null;
}
},
async triggerNextMove() {
if (activeMode === 'none' || localStorage.getItem('wiji_homed') !== 'true') return;
let targetX = anchorPos.x;
let targetY = anchorPos.y;
let motionMode = 'direct';
if (activeMode === 'circles') {
currentAngle += Math.PI / 4; // 45 degrees
targetX = anchorPos.x + RADIUS * Math.cos(currentAngle);
targetY = anchorPos.y + RADIUS * Math.sin(currentAngle);
motionMode = 'direct';
} else if (activeMode === 'erratic') {
const randAngle = Math.random() * Math.PI * 2;
const randR = Math.random() * RADIUS;
targetX = anchorPos.x + randR * Math.cos(randAngle);
targetY = anchorPos.y + randR * Math.sin(randAngle);
motionMode = 'erratic';
} else if (activeMode === 'park-bounce') {
if (bounceState === 'to-park') {
targetX = parkPosition.x;
targetY = parkPosition.y;
bounceState = 'to-anchor';
} else {
targetX = anchorPos.x;
targetY = anchorPos.y;
bounceState = 'to-park';
}
motionMode = 'direct';
}
// Workspace check
const check = IK.checkWorkspace(targetX, targetY);
if (!check.ok) {
// If out of bounds, skip this move and wait for next
this.startTimer(CONTINUOUS_WAIT_MS);
return;
}
// Fire the idle move!
try {
await Motion.goto(targetX, targetY, motionMode, true);
} catch(e) {
// If aborted, it's fine.
}
},
init() {
const saved = localStorage.getItem('wiji_idle_mode');
if (saved) activeMode = saved;
document.addEventListener('wiji:motion-start', (e) => {
const isIdleMove = e.detail && e.detail.isIdleMove;
if (!isIdleMove) {
this.stopTimer();
}
});
document.addEventListener('wiji:motion-end', (e) => {
if (activeMode === 'none' || localStorage.getItem('wiji_homed') !== 'true') return;
const isIdleMove = e.detail && e.detail.isIdleMove;
if (!isIdleMove) {
this.updateAnchorPos();
this.startTimer(IDLE_WAIT_MS);
} else {
this.startTimer(CONTINUOUS_WAIT_MS);
}
});
document.addEventListener('wiji:unhomed', () => {
this.setMode('none');
});
}
};
// Auto-init globally
IdleEffects.init();