Spaces:
Running
Running
Bot commited on
Commit ·
31fcb83
1
Parent(s): 292ab5d
feat: increase fireball size and speed
Browse files- static/main.js +82 -52
static/main.js
CHANGED
|
@@ -121,6 +121,9 @@ let prevHands = null;
|
|
| 121 |
let chargeFrames = 0;
|
| 122 |
let throwCooldown = 0;
|
| 123 |
const fireballs = [];
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
// Heart State
|
| 126 |
const hearts = [];
|
|
@@ -331,31 +334,63 @@ function processFireball(hands) {
|
|
| 331 |
let chargeRadius = 0;
|
| 332 |
let chargePos = null;
|
| 333 |
|
|
|
|
|
|
|
|
|
|
| 334 |
if (hands.length === 2 && prevHands && prevHands.length === 2) {
|
| 335 |
const c1 = palmCenter(hands[0]), c2 = palmCenter(hands[1]);
|
| 336 |
const d = Math.hypot(c1.x - c2.x, c1.y - c2.y);
|
| 337 |
-
if (d < 0.
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 352 |
}
|
| 353 |
-
if (chargeFrames > 10 && !actionText) actionText = "🔥 CHARGING";
|
| 354 |
} else {
|
| 355 |
-
|
|
|
|
| 356 |
}
|
| 357 |
-
|
| 358 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 359 |
const c = palmCenter(hands[0]);
|
| 360 |
const v = getVelocity(c, palmCenter(prevHands[0]));
|
| 361 |
if (v.speed > 0.04 && throwCooldown === 0) {
|
|
@@ -364,9 +399,8 @@ function processFireball(hands) {
|
|
| 364 |
throwCooldown = 20;
|
| 365 |
actionText = "🔥 THROW";
|
| 366 |
}
|
| 367 |
-
} else {
|
| 368 |
-
chargeFrames = 0;
|
| 369 |
}
|
|
|
|
| 370 |
return { actionText, chargeRadius, chargePos };
|
| 371 |
}
|
| 372 |
|
|
@@ -401,11 +435,12 @@ function detectLoop() {
|
|
| 401 |
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
| 402 |
|
| 403 |
const hands = results.landmarks || [];
|
| 404 |
-
|
|
|
|
|
|
|
|
|
|
| 405 |
|
| 406 |
-
if (
|
| 407 |
-
// Mirror X coords
|
| 408 |
-
const mirroredHands = hands.map(hand => hand.map(lm => ({...lm, x: 1 - lm.x})));
|
| 409 |
|
| 410 |
// Draw Particles
|
| 411 |
for (let i = particles.length - 1; i >= 0; i--) {
|
|
@@ -446,33 +481,6 @@ function detectLoop() {
|
|
| 446 |
drawMultiverseConnections(mirroredHands[0], mirroredHands[1]);
|
| 447 |
}
|
| 448 |
|
| 449 |
-
// Stats
|
| 450 |
-
const fbData = processFireball(mirroredHands);
|
| 451 |
-
|
| 452 |
-
// Draw Charging Fireball
|
| 453 |
-
if (fbData.chargeRadius > 0 && fbData.chargePos) {
|
| 454 |
-
const r = fbData.chargeRadius;
|
| 455 |
-
ctx.save();
|
| 456 |
-
ctx.globalAlpha = 0.8 + Math.random() * 0.2; // Flicker
|
| 457 |
-
const g = ctx.createRadialGradient(fbData.chargePos.x, fbData.chargePos.y, 0, fbData.chargePos.x, fbData.chargePos.y, r);
|
| 458 |
-
g.addColorStop(0, '#fff');
|
| 459 |
-
g.addColorStop(0.3, '#ffaa00');
|
| 460 |
-
g.addColorStop(1, 'rgba(255,0,0,0)');
|
| 461 |
-
ctx.fillStyle = g;
|
| 462 |
-
ctx.beginPath();
|
| 463 |
-
ctx.arc(fbData.chargePos.x, fbData.chargePos.y, r, 0, Math.PI*2);
|
| 464 |
-
ctx.fill();
|
| 465 |
-
|
| 466 |
-
// Add some spark particles while charging
|
| 467 |
-
if (Math.random() > 0.5) {
|
| 468 |
-
ctx.fillStyle = `hsl(${20 + Math.random()*30}, 100%, 50%)`;
|
| 469 |
-
ctx.beginPath();
|
| 470 |
-
ctx.arc(fbData.chargePos.x + (Math.random()-0.5)*r, fbData.chargePos.y + (Math.random()-0.5)*r, 1.5, 0, Math.PI*2);
|
| 471 |
-
ctx.fill();
|
| 472 |
-
}
|
| 473 |
-
ctx.restore();
|
| 474 |
-
}
|
| 475 |
-
|
| 476 |
const g = fbData.actionText || detectGesture(mirroredHands[0]);
|
| 477 |
const s = calcSpread(mirroredHands[0]);
|
| 478 |
audio.updateSound(s, mirroredHands.length);
|
|
@@ -510,6 +518,28 @@ function detectLoop() {
|
|
| 510 |
}
|
| 511 |
}
|
| 512 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 513 |
animFrameId = requestAnimationFrame(detectLoop);
|
| 514 |
} catch (err) {
|
| 515 |
console.error("Error in detectLoop:", err);
|
|
|
|
| 121 |
let chargeFrames = 0;
|
| 122 |
let throwCooldown = 0;
|
| 123 |
const fireballs = [];
|
| 124 |
+
let chargeAmount = 0;
|
| 125 |
+
let fullGrownFrames = 0;
|
| 126 |
+
let pendingFireballPos = null;
|
| 127 |
|
| 128 |
// Heart State
|
| 129 |
const hearts = [];
|
|
|
|
| 334 |
let chargeRadius = 0;
|
| 335 |
let chargePos = null;
|
| 336 |
|
| 337 |
+
let isCharging = false;
|
| 338 |
+
let v1 = {vx:0, vy:0, speed:0}, v2 = {vx:0, vy:0, speed:0};
|
| 339 |
+
|
| 340 |
if (hands.length === 2 && prevHands && prevHands.length === 2) {
|
| 341 |
const c1 = palmCenter(hands[0]), c2 = palmCenter(hands[1]);
|
| 342 |
const d = Math.hypot(c1.x - c2.x, c1.y - c2.y);
|
| 343 |
+
if (d < 0.4) {
|
| 344 |
+
isCharging = true;
|
| 345 |
+
v1 = getVelocity(c1, palmCenter(prevHands[0]));
|
| 346 |
+
v2 = getVelocity(c2, palmCenter(prevHands[1]));
|
| 347 |
+
pendingFireballPos = { x: (c1.x + c2.x) / 2 * W, y: (c1.y + c2.y) / 2 * H };
|
| 348 |
+
}
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
if (isCharging || chargeAmount >= 500) {
|
| 352 |
+
if (isCharging) {
|
| 353 |
+
const rubSpeed = v1.speed + v2.speed;
|
| 354 |
+
chargeAmount += 1.0 + (rubSpeed * 30.0);
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
chargeRadius = Math.min(500, chargeAmount * 1.5);
|
| 358 |
+
chargePos = pendingFireballPos || { x: W/2, y: H/2 };
|
| 359 |
+
|
| 360 |
+
if (chargeRadius >= 500) {
|
| 361 |
+
fullGrownFrames++;
|
| 362 |
+
if (fullGrownFrames > 180) { // 3 seconds at 60fps
|
| 363 |
+
audio.play('fireball');
|
| 364 |
+
const angle = Math.random() * Math.PI * 2;
|
| 365 |
+
fireballs.push(new Fireball(chargePos.x, chargePos.y, Math.cos(angle)*15, Math.sin(angle)*15, chargeRadius));
|
| 366 |
+
chargeAmount = 0; fullGrownFrames = 0; pendingFireballPos = null; throwCooldown = 30; chargeRadius = 0;
|
| 367 |
+
actionText = "🔥 AUTO THROW";
|
| 368 |
+
} else {
|
| 369 |
+
actionText = "🔥 FULLY CHARGED";
|
| 370 |
}
|
|
|
|
| 371 |
} else {
|
| 372 |
+
fullGrownFrames = 0;
|
| 373 |
+
actionText = "🔥 CHARGING";
|
| 374 |
}
|
| 375 |
+
|
| 376 |
+
if (isCharging && chargeAmount > 30 && throwCooldown === 0) {
|
| 377 |
+
if (v1.speed > 0.05 && v2.speed > 0.05) {
|
| 378 |
+
audio.play('fireball');
|
| 379 |
+
let dx = (v1.vx + v2.vx) * W * 0.5;
|
| 380 |
+
let dy = (v1.vy + v2.vy) * H * 0.5;
|
| 381 |
+
if (Math.hypot(dx, dy) < 5) { dx = 0; dy = -15; }
|
| 382 |
+
fireballs.push(new Fireball(chargePos.x, chargePos.y, dx, dy, Math.max(40, chargeRadius)));
|
| 383 |
+
chargeAmount = 0; fullGrownFrames = 0; pendingFireballPos = null; throwCooldown = 30; chargeRadius = 0;
|
| 384 |
+
actionText = "🔥 MASSIVE FIREBALL";
|
| 385 |
+
}
|
| 386 |
+
}
|
| 387 |
+
} else {
|
| 388 |
+
chargeAmount = 0;
|
| 389 |
+
fullGrownFrames = 0;
|
| 390 |
+
pendingFireballPos = null;
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
if (chargeAmount === 0 && hands.length === 1 && prevHands && prevHands.length === 1) {
|
| 394 |
const c = palmCenter(hands[0]);
|
| 395 |
const v = getVelocity(c, palmCenter(prevHands[0]));
|
| 396 |
if (v.speed > 0.04 && throwCooldown === 0) {
|
|
|
|
| 399 |
throwCooldown = 20;
|
| 400 |
actionText = "🔥 THROW";
|
| 401 |
}
|
|
|
|
|
|
|
| 402 |
}
|
| 403 |
+
|
| 404 |
return { actionText, chargeRadius, chargePos };
|
| 405 |
}
|
| 406 |
|
|
|
|
| 435 |
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
| 436 |
|
| 437 |
const hands = results.landmarks || [];
|
| 438 |
+
const mirroredHands = hands.map(hand => hand.map(lm => ({...lm, x: 1 - lm.x})));
|
| 439 |
+
document.getElementById('handCount').textContent = mirroredHands.length;
|
| 440 |
+
|
| 441 |
+
const fbData = processFireball(mirroredHands);
|
| 442 |
|
| 443 |
+
if (mirroredHands.length > 0) {
|
|
|
|
|
|
|
| 444 |
|
| 445 |
// Draw Particles
|
| 446 |
for (let i = particles.length - 1; i >= 0; i--) {
|
|
|
|
| 481 |
drawMultiverseConnections(mirroredHands[0], mirroredHands[1]);
|
| 482 |
}
|
| 483 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 484 |
const g = fbData.actionText || detectGesture(mirroredHands[0]);
|
| 485 |
const s = calcSpread(mirroredHands[0]);
|
| 486 |
audio.updateSound(s, mirroredHands.length);
|
|
|
|
| 518 |
}
|
| 519 |
}
|
| 520 |
|
| 521 |
+
// Draw Charging Fireball (global so it persists if hands drop)
|
| 522 |
+
if (fbData.chargeRadius > 0 && fbData.chargePos) {
|
| 523 |
+
const r = fbData.chargeRadius;
|
| 524 |
+
ctx.save();
|
| 525 |
+
ctx.globalAlpha = 0.8 + Math.random() * 0.2;
|
| 526 |
+
const grad = ctx.createRadialGradient(fbData.chargePos.x, fbData.chargePos.y, 0, fbData.chargePos.x, fbData.chargePos.y, r);
|
| 527 |
+
grad.addColorStop(0, '#fff');
|
| 528 |
+
grad.addColorStop(0.3, '#ffaa00');
|
| 529 |
+
grad.addColorStop(1, 'rgba(255,0,0,0)');
|
| 530 |
+
ctx.fillStyle = grad;
|
| 531 |
+
ctx.beginPath();
|
| 532 |
+
ctx.arc(fbData.chargePos.x, fbData.chargePos.y, r, 0, Math.PI*2);
|
| 533 |
+
ctx.fill();
|
| 534 |
+
if (Math.random() > 0.5) {
|
| 535 |
+
ctx.fillStyle = `hsl(${20 + Math.random()*30}, 100%, 50%)`;
|
| 536 |
+
ctx.beginPath();
|
| 537 |
+
ctx.arc(fbData.chargePos.x + (Math.random()-0.5)*r, fbData.chargePos.y + (Math.random()-0.5)*r, 1.5, 0, Math.PI*2);
|
| 538 |
+
ctx.fill();
|
| 539 |
+
}
|
| 540 |
+
ctx.restore();
|
| 541 |
+
}
|
| 542 |
+
|
| 543 |
animFrameId = requestAnimationFrame(detectLoop);
|
| 544 |
} catch (err) {
|
| 545 |
console.error("Error in detectLoop:", err);
|