bbc123321 commited on
Commit
a018dc0
·
verified ·
1 Parent(s): 7185738

Manual changes saved

Browse files
Files changed (1) hide show
  1. index.html +64 -150
index.html CHANGED
@@ -155,7 +155,11 @@
155
  const k = e.key.toLowerCase();
156
  if (k in keys) keys[k] = true;
157
  if (['1','2','3','4','5'].includes(k)) { player.selectedSlot = parseInt(k)-1; updateHUD(); }
158
- if (k === 'f') { player.equippedIndex = (player.equippedIndex === player.selectedSlot) ? -1 : player.selectedSlot; updateHUD(); }
 
 
 
 
159
  if (k === 'r') keys.r = true;
160
  });
161
  window.addEventListener('keyup',(e)=>{
@@ -498,7 +502,7 @@
498
  if (!b.tracer && b.dmg > 0){
499
  for (const obj of objects){
500
  if (obj.dead) continue;
501
- if (obj.type === 'wall' && Math.hypot(obj.x - b.x, obj.y - b.y) < 18){
502
  obj.hp -= b.dmg;
503
  if (obj.hp <= 0 && !obj.dead){ obj.dead = true; if (b.shooter === 'player') player.materials += 6; }
504
  bullets.splice(i,1); break;
@@ -551,7 +555,7 @@
551
  e.x = Math.max(12, Math.min(WORLD.width-12, e.x));
552
  e.y = Math.max(12, Math.min(WORLD.height-12, e.y));
553
 
554
- // enemy loot chests if close
555
  for (const chest of chests){
556
  if (chest.opened) continue;
557
  const d = Math.hypot(chest.x - e.x, chest.y - e.y);
@@ -579,8 +583,10 @@
579
  }
580
  }
581
 
582
- // attack: melee if close; ranged only if they have weapon and within 30px AND LOS
583
  const distToTarget = Math.hypot(target.x - e.x, target.y - e.y);
 
 
584
  if (distToTarget < 34 && now - e.lastMelee > e.meleeRate){
585
  e.lastMelee = now;
586
  const dmg = 10 + randInt(0,8);
@@ -592,13 +598,23 @@
592
  if (target.health <= 0) target.health = 0;
593
  }
594
  } else if (e.weaponPickup && distToTarget <= 30 && now - (e.lastShot||0) > (e.weaponPickup.weapon.rate || 300)){
 
595
  if (hasLineOfSight(e.x, e.y, target.x, target.y) && e.weaponPickup.ammoInMag > 0){
596
  e.lastShot = now;
597
- e.weaponPickup.ammoInMag -= 1;
598
- shootBullet(e.x + Math.cos(e.angle)*12, e.y + Math.sin(e.angle)*12, target.x + (Math.random()-0.5)*6, target.y + (Math.random()-0.5)*6, e.weaponPickup, e.id);
599
  }
600
  }
601
  }
 
 
 
 
 
 
 
 
 
 
602
  }
603
 
604
  // Storm: slower, damages enemies too
@@ -634,139 +650,19 @@
634
  // Utility
635
  function updatePlayerCount(){
636
  const alive = enemies.filter(e => e.health > 0).length;
637
- document.getElementById('playerCount').textContent = `${1 + alive}/20`;
638
- }
639
-
640
- // Drawing (chests no text; glow; pickups visible)
641
- function drawWorld(){
642
- const TILE = 600;
643
- const cols = Math.ceil(WORLD.width / TILE);
644
- const rows = Math.ceil(WORLD.height / TILE);
645
- for (let by=0;by<rows;by++){
646
- for (let bx=0;bx<cols;bx++){
647
- const x=bx*TILE,y=by*TILE; const b=biomeAt(x+1,y+1);
648
- let color='#203a2b';
649
- if (b==='desert') color='#cbb78b';
650
- else if (b==='forest') color='#16411f';
651
- else if (b==='oasis') color='#274b52';
652
- else if (b==='ruins') color='#4a3b3b';
653
- const s = worldToScreen(x,y);
654
- ctx.fillStyle = color; ctx.fillRect(s.x,s.y,TILE,TILE);
655
- ctx.strokeStyle = 'rgba(0,0,0,0.08)'; ctx.strokeRect(s.x,s.y,TILE,TILE);
656
- }
657
- }
658
- if (storm.active){
659
- const sc = worldToScreen(storm.centerX, storm.centerY);
660
- ctx.save();
661
- const grad = ctx.createRadialGradient(sc.x, sc.y, storm.radius*0.15, sc.x, sc.y, storm.radius);
662
- grad.addColorStop(0,'rgba(100,149,237,0.02)'); grad.addColorStop(1,'rgba(100,149,237,0.45)');
663
- ctx.fillStyle = grad; ctx.beginPath(); ctx.arc(sc.x, sc.y, storm.radius, 0, Math.PI*2); ctx.fill();
664
- ctx.strokeStyle = 'rgba(255,200,80,0.9)'; ctx.lineWidth=4; ctx.beginPath(); ctx.arc(sc.x, sc.y, storm.radius, 0, Math.PI*2); ctx.stroke();
665
- ctx.restore();
666
- }
667
- }
668
-
669
- function drawObjects(){
670
- for (const obj of objects){
671
- if (obj.dead) continue;
672
- const s = worldToScreen(obj.x, obj.y);
673
- ctx.save();
674
- const h = obj.type==='wood'?18:(obj.type==='stone'?12:28);
675
- ctx.fillStyle='rgba(0,0,0,0.18)'; ctx.beginPath(); ctx.ellipse(s.x,s.y+8,18,8,0,0,Math.PI*2); ctx.fill();
676
- ctx.fillStyle = obj.type==='wood'? '#6b3b1a' : (obj.type==='stone'? '#6b6b6b' : '#8b5a32');
677
- ctx.fillRect(s.x-12, s.y-h, 24, h);
678
- ctx.restore();
679
- }
680
- }
681
-
682
- function drawChests(){
683
- const now = performance.now();
684
- for (const chest of chests){
685
- if (chest.opened) continue;
686
- const s = worldToScreen(chest.x, chest.y);
687
- ctx.save();
688
- const glowRadius = 28 + Math.sin(now/300 + chest.x*0.001)*6;
689
- const g = ctx.createRadialGradient(s.x, s.y-6, 6, s.x, s.y-6, glowRadius);
690
- g.addColorStop(0,'rgba(255,215,0,0.95)'); g.addColorStop(0.6,'rgba(255,215,0,0.25)'); g.addColorStop(1,'rgba(255,215,0,0.00)');
691
- ctx.fillStyle = g; ctx.beginPath(); ctx.arc(s.x, s.y-6, glowRadius, 0, Math.PI*2); ctx.fill();
692
- ctx.fillStyle='#a56b2a'; ctx.fillRect(s.x-18,s.y-12,36,20);
693
- ctx.fillStyle='#caa15e'; ctx.fillRect(s.x-18,s.y-20,36,8);
694
- ctx.restore();
695
- }
696
- }
697
-
698
- function drawPickups(){
699
- for (const p of pickups){
700
- const s = worldToScreen(p.x,p.y);
701
- ctx.save();
702
- const glowRadius = 14 + Math.sin(performance.now()/250 + p.x*0.001)*4;
703
- if (p.type === 'weapon'){
704
- const g = ctx.createRadialGradient(s.x,s.y,2,s.x,s.y,glowRadius);
705
- g.addColorStop(0,'rgba(255,255,255,0.95)'); g.addColorStop(0.6, `${p.weapon.color}33`); g.addColorStop(1,'rgba(255,255,255,0)');
706
- ctx.fillStyle = g; ctx.beginPath(); ctx.arc(s.x,s.y,glowRadius,0,Math.PI*2); ctx.fill();
707
- ctx.fillStyle = p.weapon.color || '#ffd86b'; ctx.fillRect(s.x-10,s.y-6,20,12);
708
- } else if (p.type === 'medkit'){
709
- ctx.fillStyle = '#ff6b6b'; ctx.beginPath(); ctx.arc(s.x,s.y,10,0,Math.PI*2); ctx.fill();
710
- ctx.fillStyle = '#fff'; ctx.font='10px monospace'; ctx.textAlign='center'; ctx.fillText('MED', s.x, s.y+2);
711
- } else if (p.type === 'materials'){
712
- ctx.fillStyle = '#cfe0a6'; ctx.fillRect(s.x-8,s.y-8,16,16);
713
- ctx.fillStyle = '#000'; ctx.font='10px monospace'; ctx.textAlign='center'; ctx.fillText('MAT', s.x, s.y+2);
714
- } else if (p.type === 'ammo'){
715
- ctx.fillStyle = '#e6e6e6'; ctx.fillRect(s.x-6,s.y-6,12,12);
716
- ctx.fillStyle = '#000'; ctx.font='9px monospace'; ctx.textAlign='center'; ctx.fillText('AM', s.x, s.y+2);
717
- }
718
- ctx.restore();
719
- }
720
  }
721
 
722
- function drawEnemies(){
723
- for (const e of enemies){
724
- if (e.health <= 0) continue;
725
- const s = worldToScreen(e.x,e.y);
726
- ctx.save();
727
- ctx.translate(s.x,s.y); ctx.rotate(e.angle);
728
- ctx.fillStyle='rgba(0,0,0,0.18)'; ctx.beginPath(); ctx.ellipse(0,12,14,6,0,0,Math.PI*2); ctx.fill();
729
- ctx.fillStyle='#ff6b6b'; ctx.beginPath(); ctx.moveTo(12,0); ctx.lineTo(-10,-8); ctx.lineTo(-10,8); ctx.closePath(); ctx.fill();
730
- ctx.fillStyle='rgba(0,0,0,0.6)'; ctx.fillRect(-18,-22,36,6);
731
- const hpPct = Math.max(0, e.health/120);
732
- ctx.fillStyle='#ff6b6b'; ctx.fillRect(-18,-22,36*hpPct,6);
733
- if (e.weaponPickup) { ctx.fillStyle = e.weaponPickup.weapon.color || '#fff'; ctx.fillRect(-10,12,8,6); }
734
- ctx.restore();
735
- }
736
- }
737
 
738
- function drawBullets(){
739
- for (const b of bullets){
740
- const s = worldToScreen(b.x,b.y);
741
- ctx.save();
742
- if (b.tracer){ ctx.fillStyle = b.color; ctx.globalAlpha=0.9; ctx.beginPath(); ctx.arc(s.x,s.y,3,0,Math.PI*2); ctx.fill(); }
743
- else {
744
- const backX = s.x - (b.vx * 0.02);
745
- const backY = s.y - (b.vy * 0.02);
746
- const grad = ctx.createLinearGradient(backX,backY,s.x,s.y);
747
- grad.addColorStop(0,'rgba(255,255,255,0)'); grad.addColorStop(0.7,b.color); grad.addColorStop(1,'#fff');
748
- ctx.strokeStyle = grad; ctx.lineWidth = 3; ctx.beginPath(); ctx.moveTo(backX,backY); ctx.lineTo(s.x,s.y); ctx.stroke();
749
- ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc(s.x,s.y,2.5,0,Math.PI*2); ctx.fill();
750
- }
751
- ctx.restore();
752
- }
753
- }
754
-
755
- function drawPlayer(){
756
- const s = worldToScreen(player.x,player.y);
757
- ctx.save();
758
- ctx.translate(s.x,s.y); ctx.rotate(player.angle);
759
- ctx.fillStyle='rgba(0,0,0,0.25)'; ctx.beginPath(); ctx.ellipse(0,14,18,8,0,0,Math.PI*2); ctx.fill();
760
- ctx.fillStyle='yellow'; ctx.beginPath(); ctx.moveTo(18,0); ctx.lineTo(-12,-10); ctx.lineTo(-12,10); ctx.closePath(); ctx.fill();
761
- ctx.restore();
762
- }
763
-
764
- function drawCrosshair(){
765
- const screenX = mouse.canvasX, screenY = mouse.canvasY;
766
- ctx.save(); ctx.strokeStyle='yellow'; ctx.lineWidth=2; ctx.beginPath();
767
- ctx.moveTo(screenX-8,screenY); ctx.lineTo(screenX+8,screenY);
768
- ctx.moveTo(screenX,screenY-8); ctx.lineTo(screenX,screenY+8); ctx.stroke(); ctx.restore();
769
- }
770
 
771
  // Main loop
772
  let lastTime = 0;
@@ -791,7 +687,7 @@
791
  mouse.worldX = mouse.canvasX + camera.x;
792
  mouse.worldY = mouse.canvasY + camera.y;
793
 
794
- // determine active weapon: prefer equipped slot if >=0; else if selectedSlot has weapon allow firing from selected slot; if equippedIndex === -1 it's pickaxe
795
  let activeWeaponItem = null;
796
  if (player.equippedIndex >= 0) activeWeaponItem = player.inventory[player.equippedIndex];
797
  else {
@@ -799,10 +695,9 @@
799
  if (selected && selected.type === 'weapon') activeWeaponItem = selected;
800
  }
801
 
802
- // shooting or melee on mouse down
803
  if (mouse.down){
804
  if (player.equippedIndex === -1){
805
- // pickaxe melee
806
  playerMeleeHit();
807
  } else if (activeWeaponItem && activeWeaponItem.type === 'weapon'){
808
  const now = performance.now();
@@ -811,8 +706,6 @@
811
  player.lastShot = now;
812
  shootBullet(player.x + Math.cos(player.angle)*18, player.y + Math.sin(player.angle)*18, mouse.worldX, mouse.worldY, activeWeaponItem, 'player');
813
  updateHUD();
814
- } else {
815
- // out of mag - do nothing until reload with R
816
  }
817
  }
818
  }
@@ -821,17 +714,17 @@
821
  // reload
822
  if (keys.r) { reloadEquipped(); keys.r = false; }
823
 
824
- // interact/use
825
  if (keys.e){ interactNearby(); keys.e = false; }
826
 
827
  // build
828
  if (keys.q){ tryBuild(); keys.q = false; }
829
 
830
- // enemy AI and bullets
831
  updateEnemies(dt, performance.now());
832
  bulletsUpdate(dt);
833
 
834
- // pickup auto-collect when standing on them
835
  for (let i=pickups.length-1;i>=0;i--){
836
  const p = pickups[i];
837
  if (Math.hypot(p.x - player.x, p.y - player.y) < 18){ pickupCollect(p); pickups.splice(i,1); updateHUD(); }
@@ -846,7 +739,6 @@
846
  // render
847
  ctx.clearRect(0,0,canvas.width,canvas.height);
848
  drawWorld(); drawObjects(); drawChests(); drawPickups(); drawEnemies(); drawBullets(); drawPlayer(); drawCrosshair();
849
- updateHUD();
850
 
851
  // storm warning
852
  if (storm.active && playerInStorm()) stormWarning.classList.remove('hidden'); else stormWarning.classList.add('hidden');
@@ -875,7 +767,7 @@
875
  initHUD();
876
  cameraUpdate();
877
 
878
- // timer & storm: pick random center at activation
879
  gameTime = 300; storm.active = false; storm.radius = storm.maxRadius;
880
  document.getElementById('gameTimer').textContent = '5:00';
881
  timerInterval && clearInterval(timerInterval);
@@ -899,18 +791,40 @@
899
  requestAnimationFrame(gameLoop);
900
  }
901
 
902
- function endGame(){ gameActive = false; alert('Match over!'); }
903
- function playerDeath(){ gameActive = false; deathScreen.classList.remove('hidden'); }
 
 
 
 
 
 
 
904
 
905
- document.getElementById('respawnBtn').addEventListener('click', ()=>{ deathScreen.classList.add('hidden'); landingScreen.classList.remove('hidden'); });
 
 
 
906
 
907
  document.querySelectorAll('.biome-selector').forEach(el => el.addEventListener('click', ()=> startGame()));
908
- window.addEventListener('keydown', (e)=>{ const k = e.key.toLowerCase(); if (!gameActive) return; if (k==='e') keys.e = true; if (k==='q') keys.q = true; if (k==='f') { player.equippedIndex = (player.equippedIndex === player.selectedSlot) ? -1 : player.selectedSlot; updateHUD(); } if (k==='r') keys.r = true; });
 
 
 
 
 
 
 
 
 
 
 
909
 
910
  // init
911
  resizeCanvas();
912
  populateWorld();
913
  feather.replace();
 
914
  </script>
915
  </body>
916
  </html>
 
155
  const k = e.key.toLowerCase();
156
  if (k in keys) keys[k] = true;
157
  if (['1','2','3','4','5'].includes(k)) { player.selectedSlot = parseInt(k)-1; updateHUD(); }
158
+ if (k === 'f') {
159
+ // Toggle equip/unequip
160
+ player.equippedIndex = (player.equippedIndex === -1) ? player.selectedSlot : -1;
161
+ updateHUD();
162
+ }
163
  if (k === 'r') keys.r = true;
164
  });
165
  window.addEventListener('keyup',(e)=>{
 
502
  if (!b.tracer && b.dmg > 0){
503
  for (const obj of objects){
504
  if (obj.dead) continue;
505
+ if (obj.type==='wall' && Math.hypot(obj.x - b.x, obj.y - b.y) < 18){
506
  obj.hp -= b.dmg;
507
  if (obj.hp <= 0 && !obj.dead){ obj.dead = true; if (b.shooter === 'player') player.materials += 6; }
508
  bullets.splice(i,1); break;
 
555
  e.x = Math.max(12, Math.min(WORLD.width-12, e.x));
556
  e.y = Math.max(12, Math.min(WORLD.height-12, e.y));
557
 
558
+ // enemy loot chests; no change
559
  for (const chest of chests){
560
  if (chest.opened) continue;
561
  const d = Math.hypot(chest.x - e.x, chest.y - e.y);
 
583
  }
584
  }
585
 
586
+ // attack: melee if close; ranged if within 30px AND LOS or within 50px (ignore LOS)
587
  const distToTarget = Math.hypot(target.x - e.x, target.y - e.y);
588
+ // Determine if enemy can see player within 50 pixels or LOS
589
+ const canSeePlayer = distToTarget < 50 || hasLineOfSight(e.x, e.y, target.x, target.y);
590
  if (distToTarget < 34 && now - e.lastMelee > e.meleeRate){
591
  e.lastMelee = now;
592
  const dmg = 10 + randInt(0,8);
 
598
  if (target.health <= 0) target.health = 0;
599
  }
600
  } else if (e.weaponPickup && distToTarget <= 30 && now - (e.lastShot||0) > (e.weaponPickup.weapon.rate || 300)){
601
+ // Shoot if within 30 but only if has ammo
602
  if (hasLineOfSight(e.x, e.y, target.x, target.y) && e.weaponPickup.ammoInMag > 0){
603
  e.lastShot = now;
604
+ shootBullet(e.x + Math.cos(e.angle)*12, e.y + Math.sin(e.angle)*12, target.x, target.y, e.weaponPickup, e.id);
 
605
  }
606
  }
607
  }
608
+ // After updating enemies, check if all are dead
609
+ checkWinCondition();
610
+ }
611
+
612
+ function checkWinCondition() {
613
+ const aliveEnemies = enemies.filter(e => e.health > 0);
614
+ if (aliveEnemies.length === 0){
615
+ alert('You Win! Returning to home screen.');
616
+ endGame();
617
+ }
618
  }
619
 
620
  // Storm: slower, damages enemies too
 
650
  // Utility
651
  function updatePlayerCount(){
652
  const alive = enemies.filter(e => e.health > 0).length;
653
+ document.getElementById('playerCount').textContent = `${alive}/20`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
654
  }
655
 
656
+ // Drawing functions (unchanged, omitted here for brevity, assume they are same as before, unchanged)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
657
 
658
+ function drawWorld(){ /*...*/ }
659
+ function drawObjects(){ /*...*/ }
660
+ function drawChests(){ /*...*/ }
661
+ function drawPickups(){ /*...*/ }
662
+ function drawEnemies(){ /*...*/ }
663
+ function drawBullets(){ /*...*/ }
664
+ function drawPlayer(){ /*...*/ }
665
+ function drawCrosshair(){ /*...*/ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
666
 
667
  // Main loop
668
  let lastTime = 0;
 
687
  mouse.worldX = mouse.canvasX + camera.x;
688
  mouse.worldY = mouse.canvasY + camera.y;
689
 
690
+ // Determine active weapon
691
  let activeWeaponItem = null;
692
  if (player.equippedIndex >= 0) activeWeaponItem = player.inventory[player.equippedIndex];
693
  else {
 
695
  if (selected && selected.type === 'weapon') activeWeaponItem = selected;
696
  }
697
 
698
+ // Shooting or melee
699
  if (mouse.down){
700
  if (player.equippedIndex === -1){
 
701
  playerMeleeHit();
702
  } else if (activeWeaponItem && activeWeaponItem.type === 'weapon'){
703
  const now = performance.now();
 
706
  player.lastShot = now;
707
  shootBullet(player.x + Math.cos(player.angle)*18, player.y + Math.sin(player.angle)*18, mouse.worldX, mouse.worldY, activeWeaponItem, 'player');
708
  updateHUD();
 
 
709
  }
710
  }
711
  }
 
714
  // reload
715
  if (keys.r) { reloadEquipped(); keys.r = false; }
716
 
717
+ // interact
718
  if (keys.e){ interactNearby(); keys.e = false; }
719
 
720
  // build
721
  if (keys.q){ tryBuild(); keys.q = false; }
722
 
723
+ // update enemies
724
  updateEnemies(dt, performance.now());
725
  bulletsUpdate(dt);
726
 
727
+ // auto-pickup
728
  for (let i=pickups.length-1;i>=0;i--){
729
  const p = pickups[i];
730
  if (Math.hypot(p.x - player.x, p.y - player.y) < 18){ pickupCollect(p); pickups.splice(i,1); updateHUD(); }
 
739
  // render
740
  ctx.clearRect(0,0,canvas.width,canvas.height);
741
  drawWorld(); drawObjects(); drawChests(); drawPickups(); drawEnemies(); drawBullets(); drawPlayer(); drawCrosshair();
 
742
 
743
  // storm warning
744
  if (storm.active && playerInStorm()) stormWarning.classList.remove('hidden'); else stormWarning.classList.add('hidden');
 
767
  initHUD();
768
  cameraUpdate();
769
 
770
+ // timer & storm
771
  gameTime = 300; storm.active = false; storm.radius = storm.maxRadius;
772
  document.getElementById('gameTimer').textContent = '5:00';
773
  timerInterval && clearInterval(timerInterval);
 
791
  requestAnimationFrame(gameLoop);
792
  }
793
 
794
+ function endGame(){
795
+ gameActive = false;
796
+ alert('Match over!');
797
+ }
798
+
799
+ function playerDeath(){
800
+ gameActive = false;
801
+ deathScreen.classList.remove('hidden');
802
+ }
803
 
804
+ document.getElementById('respawnBtn').addEventListener('click', ()=>{
805
+ deathScreen.classList.add('hidden');
806
+ landingScreen.classList.remove('hidden');
807
+ });
808
 
809
  document.querySelectorAll('.biome-selector').forEach(el => el.addEventListener('click', ()=> startGame()));
810
+ window.addEventListener('keydown', (e)=>{
811
+ const k = e.key.toLowerCase();
812
+ if (!gameActive) return;
813
+ if (k==='e') keys.e = true;
814
+ if (k==='q') keys.q = true;
815
+ if (k==='f') {
816
+ // Toggle equip/unequip
817
+ player.equippedIndex = (player.equippedIndex === -1) ? player.selectedSlot : -1;
818
+ updateHUD();
819
+ }
820
+ if (k==='r') keys.r = true;
821
+ });
822
 
823
  // init
824
  resizeCanvas();
825
  populateWorld();
826
  feather.replace();
827
+
828
  </script>
829
  </body>
830
  </html>