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

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +272 -515
index.html CHANGED
@@ -373,467 +373,303 @@
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,16 +678,13 @@ function handleCenterHit() {
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,7 +692,6 @@ function handleCenterHit() {
859
  });
860
 
861
  if (state.health <= 0) {
862
- // 延迟游戏结束,让玩家看到所有目标被弹出的效果
863
  setTimeout(() => {
864
  gameOver();
865
  }, 500);
@@ -867,81 +699,6 @@ function handleCenterHit() {
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;
 
373
 
374
  <script>
375
  document.addEventListener('DOMContentLoaded', () => {
376
+ // 更新游戏状态对象
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: 250, projectileCount: 2, spread: 10, bouncy: false },
389
+ shotgun: { price: 300, fireRate: 500, projectileCount: 5, spread: 20, bouncy: false },
390
+ bouncy: {
391
+ price: 500,
392
+ fireRate: 250,
393
+ projectileCount: 8,
394
+ spread: 360,
395
+ bouncy: true
396
+ }
397
+ },
398
+ lastShot: 0,
399
+ colors: ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff'],
400
+ currentColorIndex: 0,
401
+ targetSpeed: 2,
402
+ targetCount: 1,
403
+ gameOver: false,
404
+ centerRadius: 300,
405
+ targetSpawnBuffer: 100
406
+ };
407
+
408
+ // 初始化游戏
409
+ function initGame() {
410
+ initHealthBar();
411
+ createTargets(state.targetCount);
412
+ setupCursor();
413
+ gameLoop();
414
+ }
415
+
416
+ // 游戏主循环
417
+ function gameLoop() {
418
+ if (!state.gameOver) {
419
+ updateTargets();
420
+ updateProjectiles();
421
+ checkCollisions();
422
+ checkCenterCollision();
423
+ requestAnimationFrame(gameLoop);
424
+ }
425
+ }
426
+
427
+ // 创建目标
428
+ function createTargets(count) {
429
+ const targetsContainer = document.getElementById('targets-container');
430
+ targetsContainer.innerHTML = '';
431
+ state.targets = [];
432
+
433
+ for (let i = 0; i < count; i++) {
434
+ createTarget(i);
435
+ }
436
+ }
437
+
438
+ function createTarget(index) {
439
+ const target = document.createElement('div');
440
+ target.className = 'target new-target-flash';
441
+ target.style.backgroundImage = 'url("https://huggingface.co/front/assets/huggingface_logo-noborder.svg")';
442
+
443
+ const centerX = window.innerWidth / 2;
444
+ const centerY = window.innerHeight / 2;
445
+
446
+ let x, y;
447
+ let attempts = 0;
448
+ const maxAttempts = 10;
449
+
450
+ // 确保目标不会生成在中心区域附近
451
+ do {
452
+ x = Math.random() * (window.innerWidth - 250);
453
+ y = Math.random() * (window.innerHeight - 250);
454
+ attempts++;
455
+
456
+ const dx = x - centerX;
457
+ const dy = y - centerY;
458
+ const distance = Math.sqrt(dx * dx + dy * dy);
459
+
460
+ if (distance > state.centerRadius || attempts >= maxAttempts) {
461
+ break;
462
+ }
463
+ } while (true);
464
+
465
+ target.style.left = `${x}px`;
466
+ target.style.top = `${y}px`;
467
+
468
+ // 移除闪烁动画
469
+ setTimeout(() => {
470
+ target.classList.remove('new-target-flash');
471
+ }, 500);
472
+
473
+ document.getElementById('targets-container').appendChild(target);
474
+
475
+ state.targets.push({
476
+ element: target,
477
+ x: x,
478
+ y: y,
479
+ vx: (Math.random() - 0.5) * state.targetSpeed,
480
+ vy: (Math.random() - 0.5) * state.targetSpeed,
481
+ index: index
482
+ });
483
+ }
484
+
485
+ // 更新目标位置
486
+ function updateTargets() {
487
+ state.targets.forEach(target => {
488
+ target.x += target.vx;
489
+ target.y += target.vy;
490
+
491
+ // 边界反弹
492
+ if (target.x <= 0 || target.x >= window.innerWidth - 250) {
493
+ target.vx = -target.vx;
494
+ }
495
+
496
+ if (target.y <= 0 || target.y >= window.innerHeight - 250) {
497
+ target.vy = -target.vy;
498
+ }
499
+
500
+ target.element.style.left = `${target.x}px`;
501
+ target.element.style.top = `${target.y}px`;
502
+ });
503
+ }
504
+
505
+ // 射击处理
506
+ function handleShoot(e) {
507
+ if (state.gameOver) return;
508
+
509
+ const now = Date.now();
510
+ const weapon = state.weapons[state.currentWeapon];
511
+
512
+ if (now - state.lastShot < weapon.fireRate) return;
513
+
514
+ state.lastShot = now;
515
+
516
+ for (let i = 0; i < weapon.projectileCount; i++) {
517
+ createProjectile(e.clientX, e.clientY, i);
518
+ }
519
+ }
520
+
521
+ // 创建子弹
522
+ function createProjectile(mouseX, mouseY, index) {
523
+ const projectile = document.createElement('div');
524
+ projectile.className = 'projectile';
525
+
526
+ const centerX = window.innerWidth / 2;
527
+ const centerY = window.innerHeight / 2;
528
+
529
+ let dx, dy;
530
+ const weapon = state.weapons[state.currentWeapon];
531
+
532
+ if (state.currentWeapon === 'bouncy') {
533
+ const angle = (index / weapon.projectileCount) * Math.PI * 2;
534
+ dx = Math.cos(angle);
535
+ dy = Math.sin(angle);
536
+ } else {
537
+ dx = mouseX - centerX;
538
+ dy = mouseY - centerY;
539
+
540
+ const distance = Math.sqrt(dx * dx + dy * dy);
541
+ dx /= distance;
542
+ dy /= distance;
543
+
544
+ const spreadAngle = (index - (weapon.projectileCount - 1) / 2) * weapon.spread * (Math.PI / 180);
545
+ const cos = Math.cos(spreadAngle);
546
+ const sin = Math.sin(spreadAngle);
547
+ const tx = dx * cos - dy * sin;
548
+ const ty = dx * sin + dy * cos;
549
+
550
+ dx = tx;
551
+ dy = ty;
552
+ }
553
+
554
+ projectile.style.backgroundColor = state.colors[state.currentColorIndex];
555
+ projectile.style.left = `${centerX}px`;
556
+ projectile.style.top = `${centerY}px`;
557
+
558
+ document.getElementById('projectiles-container').appendChild(projectile);
559
+
560
+ state.projectiles.push({
561
+ element: projectile,
562
+ x: centerX,
563
+ y: centerY,
564
+ vx: dx * 15,
565
+ vy: dy * 15,
566
+ bounces: 0,
567
+ maxBounces: weapon.bouncy ? 3 : 0
568
+ });
569
+ }
570
+
571
+ // 更新子弹位置
572
+ function updateProjectiles() {
573
+ state.projectiles.forEach((projectile, index) => {
574
+ projectile.x += projectile.vx;
575
+ projectile.y += projectile.vy;
576
+
577
+ projectile.element.style.left = `${projectile.x}px`;
578
+ projectile.element.style.top = `${projectile.y}px`;
579
+
580
+ // 边界反弹
581
+ if (projectile.bounces < projectile.maxBounces) {
582
+ if (projectile.x <= 0 || projectile.x >= window.innerWidth) {
583
+ projectile.vx = -projectile.vx;
584
+ projectile.bounces++;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
585
  }
586
 
587
+ if (projectile.y <= 0 || projectile.y >= window.innerHeight) {
588
+ projectile.vy = -projectile.vy;
589
+ projectile.bounces++;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
590
  }
591
+ }
592
+
593
+ // 移除超出边界的子弹
594
+ if (projectile.x < -50 || projectile.x > window.innerWidth + 50 ||
595
+ projectile.y < -50 || projectile.y > window.innerHeight + 50) {
596
+ projectile.element.remove();
597
+ state.projectiles.splice(index, 1);
598
+ }
599
+ });
600
+ }
601
+
602
+ // 碰撞检测
603
+ function checkCollisions() {
604
+ state.projectiles.forEach((projectile, pIndex) => {
605
+ state.targets.forEach((target, tIndex) => {
606
+ if (isColliding(projectile, target)) {
607
+ handleHit(target, projectile);
608
+ projectile.element.remove();
609
+ state.projectiles.splice(pIndex, 1);
 
 
 
610
  }
611
+ });
612
+ });
613
+ }
614
+
615
+ // 击中处理
616
  function handleHit(target, projectile) {
 
 
 
 
617
  // 添加击中闪烁效果
618
  target.element.classList.add('hit-flash');
619
  setTimeout(() => {
620
  target.element.classList.remove('hit-flash');
621
  }, 300);
622
 
623
+ // 增强击退效果
624
+ const dx = target.x - projectile.x;
625
+ const dy = target.y - projectile.y;
626
+ const distance = Math.sqrt(dx * dx + dy * dy);
627
+
628
+ const force = 15;
629
+ target.vx = (dx / distance) * force;
630
+ target.vy = (dy / distance) * force;
631
 
632
+ state.score += 100;
633
+ state.hits++;
634
+ updateScore();
635
+ }
636
+
637
+ // 中心区域碰撞检测
638
+ function checkCenterCollision() {
639
+ const centerX = window.innerWidth / 2;
640
+ const centerY = window.innerHeight / 2;
641
+ const centerRadius = 50;
642
+
643
+ state.targets.forEach(target => {
644
+ const targetCenterX = target.x + 125;
645
+ const targetCenterY = target.y + 125;
646
+
647
+ const dx = targetCenterX - centerX;
648
+ const dy = targetCenterY - centerY;
649
+ const distance = Math.sqrt(dx * dx + dy * dy);
650
+
651
+ if (distance < centerRadius) {
652
+ handleCenterHit();
653
+ }
654
+ });
655
  }
656
 
 
657
  function handleCenterHit() {
658
  if (state.health > 0) {
659
  state.health--;
660
  updateHealth();
661
 
662
  // 全屏红色闪烁
663
+ const screenFlash = document.createElement('div');
664
+ screenFlash.className = 'screen-flash active';
665
+ document.body.appendChild(screenFlash);
666
+
667
  setTimeout(() => {
668
+ screenFlash.remove();
669
  }, 500);
670
 
671
  // 将所有目标弹出到画面边缘
672
  state.targets.forEach(target => {
 
673
  const centerX = window.innerWidth / 2;
674
  const centerY = window.innerHeight / 2;
675
  const targetCenterX = target.x + 125;
 
678
  let dx = targetCenterX - centerX;
679
  let dy = targetCenterY - centerY;
680
 
 
681
  const distance = Math.sqrt(dx * dx + dy * dy);
682
  dx /= distance;
683
  dy /= distance;
684
 
 
685
  target.vx = dx * 20;
686
  target.vy = dy * 20;
687
 
 
688
  target.element.classList.add('hit-flash');
689
  setTimeout(() => {
690
  target.element.classList.remove('hit-flash');
 
692
  });
693
 
694
  if (state.health <= 0) {
 
695
  setTimeout(() => {
696
  gameOver();
697
  }, 500);
 
699
  }
700
  }
701
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
702
  // Game over
703
  function gameOver() {
704
  state.gameOver = true;