GuangyuanSD commited on
Commit
68fc303
·
verified ·
1 Parent(s): 9ef1468

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +614 -279
index.html CHANGED
@@ -372,303 +372,468 @@
372
  </audio>
373
 
374
  <script>
375
- // 更新游戏状态对象
376
- const state = {
377
- score: 0,
378
- hits: 0,
379
- health: 10,
380
- targets: [],
381
- projectiles: [],
382
- stars: [],
383
- currentWeapon: 'basic',
384
- weapons: {
385
- basic: { price: 0, fireRate: 500, projectileCount: 1, spread: 0, bouncy: false },
386
- rapid: { price: 50, fireRate: 250, projectileCount: 1, spread: 0, bouncy: false },
387
- double: { price: 150, fireRate: 250, projectileCount: 2, spread: 10, bouncy: false },
388
- shotgun: { price: 300, fireRate: 500, projectileCount: 5, spread: 20, bouncy: false },
389
- bouncy: {
390
- price: 500,
391
- fireRate: 250,
392
- projectileCount: 8,
393
- spread: 360,
394
- bouncy: true
395
- }
396
- },
397
- lastShot: 0,
398
- colors: ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff'],
399
- currentColorIndex: 0,
400
- targetSpeed: 2,
401
- targetCount: 1,
402
- gameOver: false,
403
- centerRadius: 300,
404
- targetSpawnBuffer: 100
405
- };
406
-
407
- // 初始化游戏
408
- function initGame() {
409
- initHealthBar();
410
- createTargets(state.targetCount);
411
- setupCursor();
412
- gameLoop();
413
- }
414
-
415
- // 游戏主循环
416
- function gameLoop() {
417
- if (!state.gameOver) {
418
- updateTargets();
419
- updateProjectiles();
420
- checkCollisions();
421
- checkCenterCollision();
422
- requestAnimationFrame(gameLoop);
423
- }
424
- }
425
-
426
- // 创建目标
427
- function createTargets(count) {
428
- const targetsContainer = document.getElementById('targets-container');
429
- targetsContainer.innerHTML = '';
430
- state.targets = [];
431
-
432
- for (let i = 0; i < count; i++) {
433
- createTarget(i);
434
- }
435
- }
436
-
437
- function createTarget(index) {
438
- const target = document.createElement('div');
439
- target.className = 'target new-target-flash';
440
- target.style.backgroundImage = 'url("https://huggingface.co/front/assets/huggingface_logo-noborder.svg")';
441
-
442
- const centerX = window.innerWidth / 2;
443
- const centerY = window.innerHeight / 2;
444
-
445
- let x, y;
446
- let attempts = 0;
447
- const maxAttempts = 10;
448
-
449
- // 确保目标不会生成在中心区域附近
450
- do {
451
- x = Math.random() * (window.innerWidth - 250);
452
- y = Math.random() * (window.innerHeight - 250);
453
- attempts++;
454
-
455
- const dx = x - centerX;
456
- const dy = y - centerY;
457
- const distance = Math.sqrt(dx * dx + dy * dy);
458
-
459
- if (distance > state.centerRadius || attempts >= maxAttempts) {
460
- break;
461
- }
462
- } while (true);
463
-
464
- target.style.left = `${x}px`;
465
- target.style.top = `${y}px`;
466
-
467
- // 移除闪烁动画
468
- setTimeout(() => {
469
- target.classList.remove('new-target-flash');
470
- }, 500);
471
-
472
- document.getElementById('targets-container').appendChild(target);
473
-
474
- state.targets.push({
475
- element: target,
476
- x: x,
477
- y: y,
478
- vx: (Math.random() - 0.5) * state.targetSpeed,
479
- vy: (Math.random() - 0.5) * state.targetSpeed,
480
- index: index
481
- });
482
- }
483
-
484
- // 更新目标位置
485
- function updateTargets() {
486
- state.targets.forEach(target => {
487
- target.x += target.vx;
488
- target.y += target.vy;
489
-
490
- // 边界反弹
491
- if (target.x <= 0 || target.x >= window.innerWidth - 250) {
492
- target.vx = -target.vx;
493
- }
494
-
495
- if (target.y <= 0 || target.y >= window.innerHeight - 250) {
496
- target.vy = -target.vy;
497
- }
498
-
499
- target.element.style.left = `${target.x}px`;
500
- target.element.style.top = `${target.y}px`;
501
- });
502
- }
503
-
504
- // 射击处理
505
- function handleShoot(e) {
506
- if (state.gameOver) return;
507
-
508
- const now = Date.now();
509
- const weapon = state.weapons[state.currentWeapon];
510
-
511
- if (now - state.lastShot < weapon.fireRate) return;
512
-
513
- state.lastShot = now;
514
-
515
- for (let i = 0; i < weapon.projectileCount; i++) {
516
- createProjectile(e.clientX, e.clientY, i);
517
- }
518
- }
519
-
520
- // 创建子弹
521
- function createProjectile(mouseX, mouseY, index) {
522
- const projectile = document.createElement('div');
523
- projectile.className = 'projectile';
524
-
525
- const centerX = window.innerWidth / 2;
526
- const centerY = window.innerHeight / 2;
527
-
528
- let dx, dy;
529
- const weapon = state.weapons[state.currentWeapon];
530
-
531
- if (state.currentWeapon === 'bouncy') {
532
- const angle = (index / weapon.projectileCount) * Math.PI * 2;
533
- dx = Math.cos(angle);
534
- dy = Math.sin(angle);
535
- } else {
536
- dx = mouseX - centerX;
537
- dy = mouseY - centerY;
538
-
539
- const distance = Math.sqrt(dx * dx + dy * dy);
540
- dx /= distance;
541
- dy /= distance;
542
-
543
- const spreadAngle = (index - (weapon.projectileCount - 1) / 2) * weapon.spread * (Math.PI / 180);
544
- const cos = Math.cos(spreadAngle);
545
- const sin = Math.sin(spreadAngle);
546
- const tx = dx * cos - dy * sin;
547
- const ty = dx * sin + dy * cos;
548
-
549
- dx = tx;
550
- dy = ty;
551
- }
552
-
553
- projectile.style.backgroundColor = state.colors[state.currentColorIndex];
554
- projectile.style.left = `${centerX}px`;
555
- projectile.style.top = `${centerY}px`;
556
-
557
- document.getElementById('projectiles-container').appendChild(projectile);
558
-
559
- state.projectiles.push({
560
- element: projectile,
561
- x: centerX,
562
- y: centerY,
563
- vx: dx * 15,
564
- vy: dy * 15,
565
- bounces: 0,
566
- maxBounces: weapon.bouncy ? 3 : 0
567
- });
568
- }
569
-
570
- // 更新子弹位置
571
- function updateProjectiles() {
572
- state.projectiles.forEach((projectile, index) => {
573
- projectile.x += projectile.vx;
574
- projectile.y += projectile.vy;
575
-
576
- projectile.element.style.left = `${projectile.x}px`;
577
- projectile.element.style.top = `${projectile.y}px`;
578
-
579
- // 边界反弹
580
- if (projectile.bounces < projectile.maxBounces) {
581
- if (projectile.x <= 0 || projectile.x >= window.innerWidth) {
582
- projectile.vx = -projectile.vx;
583
- projectile.bounces++;
584
  }
585
 
586
- if (projectile.y <= 0 || projectile.y >= window.innerHeight) {
587
- projectile.vy = -projectile.vy;
588
- projectile.bounces++;
 
 
 
 
 
 
 
589
  }
590
- }
591
-
592
- // 移除超出边界的子弹
593
- if (projectile.x < -50 || projectile.x > window.innerWidth + 50 ||
594
- projectile.y < -50 || projectile.y > window.innerHeight + 50) {
595
- projectile.element.remove();
596
- state.projectiles.splice(index, 1);
597
- }
598
- });
599
- }
600
-
601
- // 碰撞检测
602
- function checkCollisions() {
603
- state.projectiles.forEach((projectile, pIndex) => {
604
- state.targets.forEach((target, tIndex) => {
605
- if (isColliding(projectile, target)) {
606
- handleHit(target, projectile);
607
- projectile.element.remove();
608
- state.projectiles.splice(pIndex, 1);
609
  }
610
- });
611
- });
612
- }
613
-
614
- // 击中处理
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615
  function handleHit(target, projectile) {
 
 
 
 
616
  // 添加击中闪烁效果
617
  target.element.classList.add('hit-flash');
618
  setTimeout(() => {
619
  target.element.classList.remove('hit-flash');
620
  }, 300);
621
 
622
- // 增强击退效果
623
- const dx = target.x - projectile.x;
624
- const dy = target.y - projectile.y;
625
- const distance = Math.sqrt(dx * dx + dy * dy);
626
-
627
- const force = 15;
628
- target.vx = (dx / distance) * force;
629
- target.vy = (dy / distance) * force;
630
-
631
- state.score += 100;
632
- state.hits++;
633
- updateScore();
634
- }
635
-
636
- // 中心区域碰撞检测
637
- function checkCenterCollision() {
638
- const centerX = window.innerWidth / 2;
639
- const centerY = window.innerHeight / 2;
640
- const centerRadius = 50;
641
 
642
- state.targets.forEach(target => {
643
- const targetCenterX = target.x + 125;
644
- const targetCenterY = target.y + 125;
645
-
646
- const dx = targetCenterX - centerX;
647
- const dy = targetCenterY - centerY;
648
- const distance = Math.sqrt(dx * dx + dy * dy);
649
-
650
- if (distance < centerRadius) {
651
- handleCenterHit();
652
- }
653
- });
654
  }
655
 
 
656
  function handleCenterHit() {
657
  if (state.health > 0) {
658
  state.health--;
659
  updateHealth();
660
 
661
  // 全屏红色闪烁
662
- const screenFlash = document.createElement('div');
663
- screenFlash.className = 'screen-flash active';
664
- document.body.appendChild(screenFlash);
665
-
666
  setTimeout(() => {
667
- screenFlash.remove();
668
  }, 500);
669
 
670
  // 将所有目标弹出到画面边缘
671
  state.targets.forEach(target => {
 
672
  const centerX = window.innerWidth / 2;
673
  const centerY = window.innerHeight / 2;
674
  const targetCenterX = target.x + 125;
@@ -677,13 +842,16 @@ function handleCenterHit() {
677
  let dx = targetCenterX - centerX;
678
  let dy = targetCenterY - centerY;
679
 
 
680
  const distance = Math.sqrt(dx * dx + dy * dy);
681
  dx /= distance;
682
  dy /= distance;
683
 
 
684
  target.vx = dx * 20;
685
  target.vy = dy * 20;
686
 
 
687
  target.element.classList.add('hit-flash');
688
  setTimeout(() => {
689
  target.element.classList.remove('hit-flash');
@@ -691,19 +859,186 @@ function handleCenterHit() {
691
  });
692
 
693
  if (state.health <= 0) {
 
694
  setTimeout(() => {
695
  gameOver();
696
  }, 500);
697
  }
698
  }
699
  }
700
-
701
- // 初始化游戏
702
- document.addEventListener('DOMContentLoaded', () => {
703
- document.getElementById('game-container').addEventListener('click', handleShoot);
704
- initGame();
705
- });
706
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
707
  </script>
708
  <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=GuangyuanSD/ejectionface" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
709
  </html>
 
372
  </audio>
373
 
374
  <script>
375
+ document.addEventListener('DOMContentLoaded', () => {
376
+ // Game state
377
+ const state = {
378
+ score: 0,
379
+ hits: 0,
380
+ health: 10,
381
+ targets: [],
382
+ projectiles: [],
383
+ stars: [],
384
+ currentWeapon: 'basic',
385
+ weapons: {
386
+ basic: { price: 0, fireRate: 500, projectileCount: 1, spread: 0, bouncy: false },
387
+ rapid: { price: 50, fireRate: 250, projectileCount: 1, spread: 0, bouncy: false },
388
+ double: { price: 150, fireRate: 500, projectileCount: 2, spread: 10, bouncy: false },
389
+ shotgun: { price: 300, fireRate: 800, projectileCount: 5, spread: 20, bouncy: false },
390
+ bouncy: { price: 500, fireRate: 400, projectileCount: 1, spread: 0, bouncy: true }
391
+ },
392
+ lastShot: 0,
393
+ colors: ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff'],
394
+ currentColorIndex: 0,
395
+ targetSpeed: 2,
396
+ targetCount: 1,
397
+ gameOver: false
398
+ };
399
+
400
+ // DOM elements
401
+ const gameContainer = document.getElementById('game-container');
402
+ const starfield = document.getElementById('starfield');
403
+ const targetsContainer = document.getElementById('targets-container');
404
+ const projectilesContainer = document.getElementById('projectiles-container');
405
+ const hitMarkersContainer = document.getElementById('hit-markers-container');
406
+ const scoreDisplay = document.getElementById('score-display');
407
+ const hitCountElement = document.getElementById('hit-count');
408
+ const scoreElement = document.getElementById('score');
409
+ const customCursor = document.getElementById('custom-cursor');
410
+ const hitSound = document.getElementById('hit-sound');
411
+ const shootSound = document.getElementById('shoot-sound');
412
+ const bgMusic = document.getElementById('bg-music');
413
+ const weaponElements = document.querySelectorAll('.weapon');
414
+ const healthBar = document.getElementById('health-bar');
415
+ const gameOverScreen = document.getElementById('game-over');
416
+ const gameOverText = document.getElementById('game-over-text');
417
+ const retryButton = document.getElementById('retry-button');
418
+
419
+ // Initialize game
420
+ initHealthBar();
421
+ initStarfield();
422
+ createTargets(state.targetCount);
423
+ setupCursor();
424
+ setupWeapons();
425
+ playMusic();
426
+
427
+ // Event listeners
428
+ gameContainer.addEventListener('mousemove', handleMouseMove);
429
+ gameContainer.addEventListener('click', handleShoot);
430
+ retryButton.addEventListener('click', resetGame);
431
+
432
+ // Game loop
433
+ function gameLoop() {
434
+ if (!state.gameOver) {
435
+ updateTargets();
436
+ updateProjectiles();
437
+ checkCollisions();
438
+ checkCenterCollision();
439
+ requestAnimationFrame(gameLoop);
440
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441
  }
442
 
443
+ gameLoop();
444
+
445
+ // Initialize health bar
446
+ function initHealthBar() {
447
+ healthBar.innerHTML = '';
448
+ for (let i = 0; i < 10; i++) {
449
+ const healthPoint = document.createElement('div');
450
+ healthPoint.className = 'health-point';
451
+ healthBar.appendChild(healthPoint);
452
+ }
453
  }
454
+
455
+ // Update health display
456
+ function updateHealth() {
457
+ const healthPoints = document.querySelectorAll('.health-point');
458
+ healthPoints.forEach((point, index) => {
459
+ if (index < state.health) {
460
+ point.classList.remove('lost');
461
+ } else {
462
+ point.classList.add('lost');
463
+ }
464
+ });
 
 
 
 
 
 
 
 
465
  }
466
+
467
+ // Initialize starfield
468
+ function initStarfield() {
469
+ const starCount = Math.floor(window.innerWidth * window.innerHeight / 1000);
470
+
471
+ for (let i = 0; i < starCount; i++) {
472
+ createStar();
473
+ }
474
+ }
475
+
476
+ function createStar() {
477
+ const star = document.createElement('div');
478
+ star.className = 'star';
479
+
480
+ const x = Math.random() * window.innerWidth;
481
+ const y = Math.random() * window.innerHeight;
482
+ const size = Math.random() * 3;
483
+ const opacity = Math.random();
484
+
485
+ star.style.left = `${x}px`;
486
+ star.style.top = `${y}px`;
487
+ star.style.width = `${size}px`;
488
+ star.style.height = `${size}px`;
489
+ star.style.opacity = opacity;
490
+
491
+ starfield.appendChild(star);
492
+
493
+ // Animate star (parallax effect)
494
+ const speed = 0.5 + Math.random() * 2;
495
+
496
+ function animateStar() {
497
+ const currentX = parseFloat(star.style.left);
498
+ const currentY = parseFloat(star.style.top);
499
+
500
+ // Move star outward from center
501
+ const centerX = window.innerWidth / 2;
502
+ const centerY = window.innerHeight / 2;
503
+
504
+ let dx = currentX - centerX;
505
+ let dy = currentY - centerY;
506
+
507
+ // Normalize direction vector
508
+ const distance = Math.sqrt(dx * dx + dy * dy);
509
+ dx /= distance;
510
+ dy /= distance;
511
+
512
+ // Move star
513
+ star.style.left = `${currentX + dx * speed}px`;
514
+ star.style.top = `${currentY + dy * speed}px`;
515
+
516
+ // Reset star if it goes out of bounds
517
+ if (parseFloat(star.style.left) < 0 || parseFloat(star.style.left) > window.innerWidth ||
518
+ parseFloat(star.style.top) < 0 || parseFloat(star.style.top) > window.innerHeight) {
519
+ star.style.left = `${Math.random() * window.innerWidth}px`;
520
+ star.style.top = `${Math.random() * window.innerHeight}px`;
521
+ }
522
+
523
+ requestAnimationFrame(animateStar);
524
+ }
525
+
526
+ animateStar();
527
+ }
528
+
529
+ // Create targets
530
+ function createTargets(count) {
531
+ targetsContainer.innerHTML = '';
532
+ state.targets = [];
533
+
534
+ for (let i = 0; i < count; i++) {
535
+ createTarget(i);
536
+ }
537
+ }
538
+
539
+ function createTarget(index) {
540
+ const target = document.createElement('div');
541
+ target.className = 'target';
542
+
543
+ // Use huggingface emoji as target
544
+ target.style.backgroundImage = 'url("https://huggingface.co/front/assets/huggingface_logo-noborder.svg")';
545
+
546
+ // Position randomly but ensure it's fully visible
547
+ const maxX = window.innerWidth - 250;
548
+ const maxY = window.innerHeight - 250;
549
+
550
+ const x = Math.max(0, Math.min(maxX, Math.random() * maxX));
551
+ const y = Math.max(0, Math.min(maxY, Math.random() * maxY));
552
+
553
+ target.style.left = `${x}px`;
554
+ target.style.top = `${y}px`;
555
+
556
+ // Add hit effect
557
+ const hitEffect = document.createElement('div');
558
+ hitEffect.className = 'hit-effect';
559
+ hitEffect.style.backgroundImage = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 100 100\'><circle cx=\'50\' cy=\'50\' r=\'40\' fill=\'' + state.colors[state.currentColorIndex] + '\' opacity=\'0.7\'/></svg>")';
560
+
561
+ target.appendChild(hitEffect);
562
+ targetsContainer.appendChild(target);
563
+
564
+ // Store target data
565
+ state.targets.push({
566
+ element: target,
567
+ hitEffect: hitEffect,
568
+ x: x,
569
+ y: y,
570
+ vx: (Math.random() - 0.5) * state.targetSpeed,
571
+ vy: (Math.random() - 0.5) * state.targetSpeed,
572
+ index: index
573
+ });
574
+ }
575
+
576
+ // Update targets position
577
+ function updateTargets() {
578
+ state.targets.forEach(target => {
579
+ // Update position
580
+ target.x += target.vx;
581
+ target.y += target.vy;
582
+
583
+ // Bounce off edges
584
+ if (target.x <= 0 || target.x >= window.innerWidth - 250) {
585
+ target.vx = -target.vx * (0.9 + Math.random() * 0.2);
586
+ }
587
+
588
+ if (target.y <= 0 || target.y >= window.innerHeight - 250) {
589
+ target.vy = -target.vy * (0.9 + Math.random() * 0.2);
590
+ }
591
+
592
+ // Random direction changes
593
+ if (Math.random() < 0.02) {
594
+ target.vx += (Math.random() - 0.5) * 0.5;
595
+ target.vy += (Math.random() - 0.5) * 0.5;
596
+
597
+ // Limit speed
598
+ const speed = Math.sqrt(target.vx * target.vx + target.vy * target.vy);
599
+ const maxSpeed = state.targetSpeed * 1.5;
600
+
601
+ if (speed > maxSpeed) {
602
+ target.vx = (target.vx / speed) * maxSpeed;
603
+ target.vy = (target.vy / speed) * maxSpeed;
604
+ }
605
+ }
606
+
607
+ // Apply position
608
+ target.element.style.left = `${target.x}px`;
609
+ target.element.style.top = `${target.y}px`;
610
+ });
611
+ }
612
+
613
+ // Setup custom cursor
614
+ function setupCursor() {
615
+ customCursor.style.backgroundImage = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 100 100\'><path d=\'M50 10 L50 90 M10 50 L90 50\' stroke=\'' + state.colors[state.currentColorIndex] + '\' stroke-width=\'5\' stroke-linecap=\'round\'/></svg>")';
616
+ customCursor.style.display = 'block';
617
+ }
618
+
619
+ function handleMouseMove(e) {
620
+ customCursor.style.left = `${e.clientX - 20}px`;
621
+ customCursor.style.top = `${e.clientY - 20}px`;
622
+
623
+ // Rotate cursor slightly for visual feedback
624
+ const rotation = Math.sin(Date.now() / 200) * 5;
625
+ customCursor.style.transform = `rotate(${rotation}deg)`;
626
+ }
627
+
628
+ // Handle shooting
629
+ function handleShoot(e) {
630
+ if (state.gameOver) return;
631
+
632
+ const now = Date.now();
633
+ const weapon = state.weapons[state.currentWeapon];
634
+
635
+ if (now - state.lastShot < weapon.fireRate) return;
636
+
637
+ state.lastShot = now;
638
+
639
+ // Play shoot sound
640
+ shootSound.currentTime = 0;
641
+ shootSound.play();
642
+
643
+ // Create projectiles based on weapon type
644
+ for (let i = 0; i < weapon.projectileCount; i++) {
645
+ createProjectile(e.clientX, e.clientY, i);
646
+ }
647
+
648
+ // Animate cursor
649
+ customCursor.style.transform = 'scale(0.8)';
650
+ setTimeout(() => {
651
+ customCursor.style.transform = 'scale(1)';
652
+ }, 100);
653
+ }
654
+
655
+ function createProjectile(mouseX, mouseY, index) {
656
+ const projectile = document.createElement('div');
657
+ projectile.className = 'projectile';
658
+
659
+ // Calculate direction from center to mouse
660
+ const centerX = window.innerWidth / 2;
661
+ const centerY = window.innerHeight / 2;
662
+
663
+ let dx = mouseX - centerX;
664
+ let dy = mouseY - centerY;
665
+
666
+ // Normalize direction vector
667
+ const distance = Math.sqrt(dx * dx + dy * dy);
668
+ dx /= distance;
669
+ dy /= distance;
670
+
671
+ // Apply weapon spread
672
+ const weapon = state.weapons[state.currentWeapon];
673
+ const spreadAngle = (index - (weapon.projectileCount - 1) / 2) * weapon.spread * (Math.PI / 180);
674
+
675
+ // Rotate direction by spread angle
676
+ const cos = Math.cos(spreadAngle);
677
+ const sin = Math.sin(spreadAngle);
678
+ const tx = dx * cos - dy * sin;
679
+ const ty = dx * sin + dy * cos;
680
+
681
+ dx = tx;
682
+ dy = ty;
683
+
684
+ // Set projectile color
685
+ projectile.style.backgroundColor = state.colors[state.currentColorIndex];
686
+
687
+ // Set initial position at center
688
+ projectile.style.left = `${centerX}px`;
689
+ projectile.style.top = `${centerY}px`;
690
+
691
+ projectilesContainer.appendChild(projectile);
692
+
693
+ // Store projectile data
694
+ const projectileData = {
695
+ element: projectile,
696
+ x: centerX,
697
+ y: centerY,
698
+ vx: dx * 10,
699
+ vy: dy * 10,
700
+ bounces: 0,
701
+ maxBounces: weapon.bouncy ? 3 : 0
702
+ };
703
+
704
+ state.projectiles.push(projectileData);
705
+ }
706
+
707
+ // Update projectiles position
708
+ function updateProjectiles() {
709
+ state.projectiles.forEach((projectile, index) => {
710
+ // Update position
711
+ projectile.x += projectile.vx;
712
+ projectile.y += projectile.vy;
713
+
714
+ // Bounce off edges if bouncy
715
+ if (projectile.maxBounces > 0) {
716
+ if (projectile.x <= 0 || projectile.x >= window.innerWidth) {
717
+ projectile.vx = -projectile.vx;
718
+ projectile.bounces++;
719
+ projectile.x = Math.max(0, Math.min(window.innerWidth, projectile.x));
720
+ }
721
+
722
+ if (projectile.y <= 0 || projectile.y >= window.innerHeight) {
723
+ projectile.vy = -projectile.vy;
724
+ projectile.bounces++;
725
+ projectile.y = Math.max(0, Math.min(window.innerHeight, projectile.y));
726
+ }
727
+ }
728
+
729
+ // Apply position
730
+ projectile.element.style.left = `${projectile.x}px`;
731
+ projectile.element.style.top = `${projectile.y}px`;
732
+
733
+ // Remove if out of bounds or max bounces reached
734
+ if (projectile.x < 0 || projectile.x > window.innerWidth ||
735
+ projectile.y < 0 || projectile.y > window.innerHeight ||
736
+ projectile.bounces > projectile.maxBounces) {
737
+ projectile.element.remove();
738
+ state.projectiles.splice(index, 1);
739
+ }
740
+ });
741
+ }
742
+
743
+ // Check for collisions with targets
744
+ function checkCollisions() {
745
+ state.projectiles.forEach((projectile, pIndex) => {
746
+ state.targets.forEach(target => {
747
+ // Simple circle-rectangle collision detection
748
+ const targetRect = {
749
+ x: target.x,
750
+ y: target.y,
751
+ width: 250,
752
+ height: 250
753
+ };
754
+
755
+ // Find closest point on rectangle to circle
756
+ const closestX = Math.max(targetRect.x, Math.min(projectile.x, targetRect.x + targetRect.width));
757
+ const closestY = Math.max(targetRect.y, Math.min(projectile.y, targetRect.y + targetRect.height));
758
+
759
+ // Calculate distance
760
+ const distanceX = projectile.x - closestX;
761
+ const distanceY = projectile.y - closestY;
762
+ const distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
763
+
764
+ if (distance < 10) { // Projectile radius
765
+ // Hit!
766
+ handleHit(target, projectile);
767
+
768
+ // Remove projectile
769
+ projectile.element.remove();
770
+ state.projectiles.splice(pIndex, 1);
771
+ }
772
+ });
773
+ });
774
+ }
775
+
776
+ // Check for collisions with center
777
+ function checkCenterCollision() {
778
+ if (state.gameOver) return;
779
+
780
+ const centerX = window.innerWidth / 2;
781
+ const centerY = window.innerHeight / 2;
782
+ const centerRadius = 10;
783
+
784
+ state.targets.forEach(target => {
785
+ // Check if any part of target is within center radius
786
+ const targetCenterX = target.x + 125;
787
+ const targetCenterY = target.y + 125;
788
+ const distance = Math.sqrt(
789
+ Math.pow(targetCenterX - centerX, 2) +
790
+ Math.pow(targetCenterY - centerY, 2)
791
+ );
792
+
793
+ if (distance < centerRadius + 125) {
794
+ handleCenterHit();
795
+ }
796
+ });
797
+ }
798
+
799
+ // 修改handleCenterHit函数
800
  function handleHit(target, projectile) {
801
+ // Play hit sound
802
+ hitSound.currentTime = 0;
803
+ hitSound.play();
804
+
805
  // 添加击中闪烁效果
806
  target.element.classList.add('hit-flash');
807
  setTimeout(() => {
808
  target.element.classList.remove('hit-flash');
809
  }, 300);
810
 
811
+ // 其他原有代码保持不变...
812
+ // Show hit effect
813
+ target.hitEffect.classList.add('active');
814
+ setTimeout(() => {
815
+ target.hitEffect.classList.remove('active');
816
+ }, 500);
 
 
 
 
 
 
 
 
 
 
 
 
 
817
 
818
+ // ...其余原有代码
 
 
 
 
 
 
 
 
 
 
 
819
  }
820
 
821
+ // 修改handleCenterHit函数,添加全屏红色闪烁
822
  function handleCenterHit() {
823
  if (state.health > 0) {
824
  state.health--;
825
  updateHealth();
826
 
827
  // 全屏红色闪烁
828
+ const screenFlash = document.getElementById('screen-flash');
829
+ screenFlash.classList.add('active');
 
 
830
  setTimeout(() => {
831
+ screenFlash.classList.remove('active');
832
  }, 500);
833
 
834
  // 将所有目标弹出到画面边缘
835
  state.targets.forEach(target => {
836
+ // 计算从中心到目标的向量
837
  const centerX = window.innerWidth / 2;
838
  const centerY = window.innerHeight / 2;
839
  const targetCenterX = target.x + 125;
 
842
  let dx = targetCenterX - centerX;
843
  let dy = targetCenterY - centerY;
844
 
845
+ // 标准化向量并放大(弹出效果)
846
  const distance = Math.sqrt(dx * dx + dy * dy);
847
  dx /= distance;
848
  dy /= distance;
849
 
850
+ // 设置很大的速度向边缘移动
851
  target.vx = dx * 20;
852
  target.vy = dy * 20;
853
 
854
+ // 添加闪烁效果
855
  target.element.classList.add('hit-flash');
856
  setTimeout(() => {
857
  target.element.classList.remove('hit-flash');
 
859
  });
860
 
861
  if (state.health <= 0) {
862
+ // 延迟游戏结束,让玩家看到所有目标被弹出的效果
863
  setTimeout(() => {
864
  gameOver();
865
  }, 500);
866
  }
867
  }
868
  }
869
+
870
+ function handleHit(target, projectile) {
871
+ // Play hit sound
872
+ hitSound.currentTime = 0;
873
+ hitSound.play();
874
+
875
+ // Show hit effect
876
+ target.hitEffect.classList.add('active');
877
+ setTimeout(() => {
878
+ target.hitEffect.classList.remove('active');
879
+ }, 500);
880
+
881
+ // Create hit marker
882
+ const hitMarker = document.createElement('div');
883
+ hitMarker.className = 'hit-marker';
884
+ hitMarker.style.left = `${projectile.x - 15}px`;
885
+ hitMarker.style.top = `${projectile.y - 15}px`;
886
+ hitMarkersContainer.appendChild(hitMarker);
887
+
888
+ setTimeout(() => {
889
+ hitMarker.remove();
890
+ }, 1000);
891
+
892
+ // Update score
893
+ state.hits++;
894
+ state.score += 10;
895
+
896
+ hitCountElement.textContent = state.hits;
897
+ scoreElement.textContent = state.score;
898
+
899
+ // Show "Ejection FACE!" text
900
+ const ejectionText = document.createElement('div');
901
+ ejectionText.className = 'ejection-text';
902
+ ejectionText.textContent = 'Ejection FACE!';
903
+ ejectionText.style.left = `${window.innerWidth / 2 - 150}px`;
904
+ ejectionText.style.top = `${window.innerHeight / 2 - 50}px`;
905
+
906
+ gameContainer.appendChild(ejectionText);
907
+ setTimeout(() => {
908
+ ejectionText.remove();
909
+ }, 1500);
910
+
911
+ // Change target direction on hit
912
+ target.vx = (Math.random() - 0.5) * state.targetSpeed * 2;
913
+ target.vy = (Math.random() - 0.5) * state.targetSpeed * 2;
914
+
915
+ // Increase difficulty every 10 hits
916
+ if (state.hits % 10 === 0) {
917
+ state.targetSpeed += 0.2;
918
+
919
+ // Make targets change direction more often
920
+ state.targets.forEach(t => {
921
+ t.vx *= 1.1;
922
+ t.vy *= 1.1;
923
+ });
924
+ }
925
+
926
+ // Add new target every 10 hits
927
+ if (state.hits % 10 === 0) {
928
+ state.targetCount++;
929
+ state.currentColorIndex = (state.currentColorIndex + 1) % state.colors.length;
930
+ setupCursor();
931
+ createTargets(state.targetCount);
932
+ }
933
+
934
+ // Unlock weapons if enough score
935
+ weaponElements.forEach(weapon => {
936
+ const weaponType = weapon.dataset.weapon;
937
+ const price = parseInt(weapon.dataset.price);
938
+
939
+ if (state.score >= price) {
940
+ weapon.classList.remove('locked');
941
+ }
942
+ });
943
+ }
944
+
945
+ // Game over
946
+ function gameOver() {
947
+ state.gameOver = true;
948
+
949
+ // Make targets dance
950
+ state.targets.forEach(target => {
951
+ target.element.classList.add('dancing');
952
+ });
953
+
954
+ // Show game over screen
955
+ gameOverScreen.style.display = 'flex';
956
+ }
957
+
958
+ // Reset game
959
+ function resetGame() {
960
+ // Reset game state
961
+ state.score = 0;
962
+ state.hits = 0;
963
+ state.health = 10;
964
+ state.targets = [];
965
+ state.projectiles = [];
966
+ state.currentWeapon = 'basic';
967
+ state.currentColorIndex = 0;
968
+ state.targetSpeed = 2;
969
+ state.targetCount = 1;
970
+ state.gameOver = false;
971
+
972
+ // Reset UI
973
+ hitCountElement.textContent = '0';
974
+ scoreElement.textContent = '0';
975
+ gameOverScreen.style.display = 'none';
976
+
977
+ // Clear containers
978
+ targetsContainer.innerHTML = '';
979
+ projectilesContainer.innerHTML = '';
980
+ hitMarkersContainer.innerHTML = '';
981
+
982
+ // Reset weapons
983
+ weaponElements.forEach(weapon => {
984
+ if (weapon.dataset.weapon !== 'basic') {
985
+ weapon.classList.add('locked');
986
+ }
987
+ weapon.classList.remove('selected');
988
+ });
989
+ document.querySelector('.weapon[data-weapon="basic"]').classList.add('selected');
990
+
991
+ // Reset health
992
+ initHealthBar();
993
+
994
+ // Start new game
995
+ createTargets(state.targetCount);
996
+ gameLoop();
997
+ }
998
+
999
+ // Setup weapon selection
1000
+ function setupWeapons() {
1001
+ weaponElements.forEach(weapon => {
1002
+ const weaponType = weapon.dataset.weapon;
1003
+
1004
+ // Set weapon icons
1005
+ switch (weaponType) {
1006
+ case 'basic':
1007
+ weapon.style.backgroundImage = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 100 100\'><path d=\'M50 10 L50 90 M10 50 L90 50\' stroke=\'' + state.colors[0] + '\' stroke-width=\'5\' stroke-linecap=\'round\'/></svg>")';
1008
+ break;
1009
+ case 'rapid':
1010
+ weapon.style.backgroundImage = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 100 100\'><path d=\'M50 10 L50 90 M10 50 L90 50\' stroke=\'' + state.colors[1] + '\' stroke-width=\'5\' stroke-linecap=\'round\'/><circle cx=\'50\' cy=\'50\' r=\'15\' fill=\'none\' stroke=\'' + state.colors[1] + '\' stroke-width=\'3\'/></svg>")';
1011
+ break;
1012
+ case 'double':
1013
+ weapon.style.backgroundImage = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 100 100\'><path d=\'M50 10 L50 90 M10 50 L90 50\' stroke=\'' + state.colors[2] + '\' stroke-width=\'5\' stroke-linecap=\'round\'/><path d=\'M30 30 L70 70 M30 70 L70 30\' stroke=\'' + state.colors[2] + '\' stroke-width=\'3\'/></svg>")';
1014
+ break;
1015
+ case 'shotgun':
1016
+ weapon.style.backgroundImage = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 100 100\'><path d=\'M50 10 L50 90 M10 50 L90 50\' stroke=\'' + state.colors[3] + '\' stroke-width=\'5\' stroke-linecap=\'round\'/><path d=\'M50 30 L30 50 M50 30 L70 50 M50 70 L30 50 M50 70 L70 50\' stroke=\'' + state.colors[3] + '\' stroke-width=\'3\'/></svg>")';
1017
+ break;
1018
+ case 'bouncy':
1019
+ weapon.style.backgroundImage = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 100 100\'><path d=\'M50 10 L50 90 M10 50 L90 50\' stroke=\'' + state.colors[4] + '\' stroke-width=\'5\' stroke-linecap=\'round\'/><path d=\'M20 20 Q50 30 80 20 Q85 50 80 80 Q50 70 20 80 Q15 50 20 20\' fill=\'none\' stroke=\'' + state.colors[4] + '\' stroke-width=\'3\'/></svg>")';
1020
+ break;
1021
+ }
1022
+
1023
+ weapon.addEventListener('click', () => {
1024
+ if (weapon.classList.contains('locked')) return;
1025
+
1026
+ // Select weapon
1027
+ weaponElements.forEach(w => w.classList.remove('selected'));
1028
+ weapon.classList.add('selected');
1029
+
1030
+ state.currentWeapon = weaponType;
1031
+ setupCursor();
1032
+ });
1033
+ });
1034
+ }
1035
+
1036
+ // Play background music
1037
+ function playMusic() {
1038
+ bgMusic.volume = 0.3;
1039
+ bgMusic.play();
1040
+ }
1041
+ });
1042
  </script>
1043
  <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=GuangyuanSD/ejectionface" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
1044
  </html>