Spaces:
Running
Running
Manual changes saved
Browse files- index.html +71 -23
index.html
CHANGED
|
@@ -11,7 +11,7 @@
|
|
| 11 |
#canvasContainer { position:relative; flex:1; display:flex; justify-content:center; align-items:center; height:100vh; overflow:hidden; }
|
| 12 |
#gameCanvas { display:block; user-select:none; cursor:crosshair; box-shadow:0 0 20px rgba(0,0,0,.5); width:100%; height:100%; }
|
| 13 |
#stormWarning { z-index:10; }
|
| 14 |
-
#deathScreen { z-index:20; }
|
| 15 |
|
| 16 |
/* HUD */
|
| 17 |
#hudHealth { position:absolute; left:12px; bottom:12px; background:rgba(0,0,0,0.55); padding:6px 8px; border-radius:8px; font-weight:700; font-size:13px; display:flex; align-items:center; gap:8px; z-index:30; }
|
|
@@ -63,7 +63,7 @@
|
|
| 63 |
<li><strong>E</strong> — Use medkit in selected slot OR Interact / Loot</li>
|
| 64 |
<li><strong>Q</strong> — Build (costs 10 materials)</li>
|
| 65 |
<li><strong>R</strong> — Reload selected weapon</li>
|
| 66 |
-
<li><strong>1-5</strong> — Select inventory slot</li>
|
| 67 |
<li><strong>F</strong> — Equip / Unequip selected slot (pickaxe is the default)</li>
|
| 68 |
</ul>
|
| 69 |
</div>
|
|
@@ -98,6 +98,19 @@
|
|
| 98 |
</button>
|
| 99 |
</div>
|
| 100 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
<div id="hudHealth" class="hidden">
|
| 102 |
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#ffd86b" stroke-width="1.6"><path d="M20.8 8.6a5.5 5.5 0 0 0-7.8 0L12 10.6l-1-1a5.5 5.5 0 0 0-7.8 7.8l1 1L12 22l7.8-3.6 1-1a5.5 5.5 0 0 0 0-7.8z"></path></svg>
|
| 103 |
<span id="hudHealthText">100%</span>
|
|
@@ -124,6 +137,9 @@
|
|
| 124 |
const pickaxeSlot = document.getElementById('pickaxeSlot');
|
| 125 |
const stormWarning = document.getElementById('stormWarning');
|
| 126 |
const deathScreen = document.getElementById('deathScreen');
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
// World (bigger)
|
| 129 |
const WORLD = { width: 6000, height: 4000 };
|
|
@@ -151,32 +167,46 @@
|
|
| 151 |
const keys = { w:false,a:false,s:false,d:false,e:false,q:false,r:false,f:false };
|
| 152 |
const mouse = { canvasX:0, canvasY:0, worldX:0, worldY:0, down:false };
|
| 153 |
|
| 154 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
window.addEventListener('keydown',(e)=>{
|
| 156 |
const k = e.key.toLowerCase();
|
| 157 |
-
|
|
|
|
| 158 |
if (gameActive){
|
| 159 |
if (k in keys) keys[k] = true;
|
|
|
|
| 160 |
if (['1','2','3','4','5'].includes(k)) {
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
}
|
| 166 |
updateHUD();
|
| 167 |
}
|
|
|
|
| 168 |
if (k === 'f') {
|
| 169 |
-
// toggle between pickaxe
|
| 170 |
-
|
|
|
|
| 171 |
updateHUD();
|
| 172 |
}
|
|
|
|
| 173 |
if (k === 'r') keys.r = true;
|
| 174 |
} else {
|
| 175 |
-
// allow
|
| 176 |
-
if (['1','2','3','4','5'].includes(k)) {
|
| 177 |
-
|
|
|
|
|
|
|
| 178 |
}
|
| 179 |
});
|
|
|
|
| 180 |
window.addEventListener('keyup',(e)=>{
|
| 181 |
const k = e.key.toLowerCase();
|
| 182 |
if (k in keys) keys[k] = false;
|
|
@@ -193,7 +223,6 @@
|
|
| 193 |
player.angle = Math.atan2(mouse.worldY - player.y, mouse.worldX - player.x);
|
| 194 |
});
|
| 195 |
canvas.addEventListener('mousedown', (e)=>{
|
| 196 |
-
// update mouse coords immediately and set down
|
| 197 |
const rect = canvas.getBoundingClientRect();
|
| 198 |
mouse.canvasX = e.clientX - rect.left;
|
| 199 |
mouse.canvasY = e.clientY - rect.top;
|
|
@@ -281,16 +310,23 @@
|
|
| 281 |
slot.className = 'gear-slot';
|
| 282 |
slot.dataset.index = i;
|
| 283 |
slot.addEventListener('click', ()=> {
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
if
|
|
|
|
|
|
|
| 287 |
updateHUD();
|
| 288 |
});
|
| 289 |
hudGear.appendChild(slot);
|
| 290 |
}
|
| 291 |
// pickaxe click toggles equip (explicit: clicking pickaxe equips/unequips it)
|
| 292 |
pickaxeSlot.onclick = () => {
|
| 293 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 294 |
updateHUD();
|
| 295 |
};
|
| 296 |
updateHUD();
|
|
@@ -333,7 +369,6 @@
|
|
| 333 |
const angle = Math.atan2(targetY-originY, targetX-originX);
|
| 334 |
// decrement ammo if present
|
| 335 |
if (typeof weaponObj.ammoInMag === 'number') weaponObj.ammoInMag -= 1;
|
| 336 |
-
// weaponObj may be an inventory item (with .weapon) or weaponPickup (with .weapon) — both consistent
|
| 337 |
const dmg = weaponObj.weapon && weaponObj.weapon.dmg ? weaponObj.weapon.dmg : (weaponObj.dmg || 10);
|
| 338 |
const color = (weaponObj.weapon && weaponObj.weapon.color) || weaponObj.color || '#fff';
|
| 339 |
bullets.push({
|
|
@@ -378,7 +413,7 @@
|
|
| 378 |
}
|
| 379 |
|
| 380 |
// Interact: use medkit in selected slot OR loot chests / pickup items.
|
| 381 |
-
//
|
| 382 |
function interactNearby(){
|
| 383 |
// use medkit in selected slot
|
| 384 |
const sel = player.selectedSlot;
|
|
@@ -658,8 +693,16 @@
|
|
| 658 |
|
| 659 |
// Utility
|
| 660 |
function updatePlayerCount(){
|
| 661 |
-
const
|
| 662 |
-
document.getElementById('playerCount').textContent = `${1 +
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 663 |
}
|
| 664 |
|
| 665 |
// Drawing (chests no text; glow; pickups visible)
|
|
@@ -925,6 +968,9 @@
|
|
| 925 |
let gameActive = false;
|
| 926 |
|
| 927 |
function startGame(){
|
|
|
|
|
|
|
|
|
|
| 928 |
gameActive = true;
|
| 929 |
landingScreen.classList.add('hidden');
|
| 930 |
gameScreen.classList.remove('hidden');
|
|
@@ -968,6 +1014,8 @@
|
|
| 968 |
function playerDeath(){ gameActive = false; deathScreen.classList.remove('hidden'); }
|
| 969 |
|
| 970 |
document.getElementById('respawnBtn').addEventListener('click', ()=>{ deathScreen.classList.add('hidden'); landingScreen.classList.remove('hidden'); });
|
|
|
|
|
|
|
| 971 |
|
| 972 |
document.querySelectorAll('.biome-selector').forEach(el => el.addEventListener('click', ()=> startGame()));
|
| 973 |
|
|
|
|
| 11 |
#canvasContainer { position:relative; flex:1; display:flex; justify-content:center; align-items:center; height:100vh; overflow:hidden; }
|
| 12 |
#gameCanvas { display:block; user-select:none; cursor:crosshair; box-shadow:0 0 20px rgba(0,0,0,.5); width:100%; height:100%; }
|
| 13 |
#stormWarning { z-index:10; }
|
| 14 |
+
#deathScreen, #victoryScreen { z-index:20; }
|
| 15 |
|
| 16 |
/* HUD */
|
| 17 |
#hudHealth { position:absolute; left:12px; bottom:12px; background:rgba(0,0,0,0.55); padding:6px 8px; border-radius:8px; font-weight:700; font-size:13px; display:flex; align-items:center; gap:8px; z-index:30; }
|
|
|
|
| 63 |
<li><strong>E</strong> — Use medkit in selected slot OR Interact / Loot</li>
|
| 64 |
<li><strong>Q</strong> — Build (costs 10 materials)</li>
|
| 65 |
<li><strong>R</strong> — Reload selected weapon</li>
|
| 66 |
+
<li><strong>1-5</strong> — Select inventory slot (also equips it)</li>
|
| 67 |
<li><strong>F</strong> — Equip / Unequip selected slot (pickaxe is the default)</li>
|
| 68 |
</ul>
|
| 69 |
</div>
|
|
|
|
| 98 |
</button>
|
| 99 |
</div>
|
| 100 |
|
| 101 |
+
<div id="victoryScreen" class="hidden absolute inset-0 bg-black bg-opacity-80 flex flex-col items-center justify-center rounded-xl">
|
| 102 |
+
<h2 class="text-5xl font-bold text-green-400 mb-4">VICTORY</h2>
|
| 103 |
+
<p class="text-xl mb-6">You are the last player standing!</p>
|
| 104 |
+
<div class="flex gap-4">
|
| 105 |
+
<button id="goHomeBtn" class="bg-yellow-500 hover:bg-yellow-600 text-black font-bold py-3 px-6 rounded-lg flex items-center">
|
| 106 |
+
<i data-feather="home" class="mr-2"></i> Go to Home
|
| 107 |
+
</button>
|
| 108 |
+
<button id="continueBtn" class="bg-gray-700 hover:bg-gray-600 text-white font-bold py-3 px-6 rounded-lg flex items-center">
|
| 109 |
+
<i data-feather="repeat" class="mr-2"></i> Play Again
|
| 110 |
+
</button>
|
| 111 |
+
</div>
|
| 112 |
+
</div>
|
| 113 |
+
|
| 114 |
<div id="hudHealth" class="hidden">
|
| 115 |
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#ffd86b" stroke-width="1.6"><path d="M20.8 8.6a5.5 5.5 0 0 0-7.8 0L12 10.6l-1-1a5.5 5.5 0 0 0-7.8 7.8l1 1L12 22l7.8-3.6 1-1a5.5 5.5 0 0 0 0-7.8z"></path></svg>
|
| 116 |
<span id="hudHealthText">100%</span>
|
|
|
|
| 137 |
const pickaxeSlot = document.getElementById('pickaxeSlot');
|
| 138 |
const stormWarning = document.getElementById('stormWarning');
|
| 139 |
const deathScreen = document.getElementById('deathScreen');
|
| 140 |
+
const victoryScreen = document.getElementById('victoryScreen');
|
| 141 |
+
const goHomeBtn = document.getElementById('goHomeBtn');
|
| 142 |
+
const continueBtn = document.getElementById('continueBtn');
|
| 143 |
|
| 144 |
// World (bigger)
|
| 145 |
const WORLD = { width: 6000, height: 4000 };
|
|
|
|
| 167 |
const keys = { w:false,a:false,s:false,d:false,e:false,q:false,r:false,f:false };
|
| 168 |
const mouse = { canvasX:0, canvasY:0, worldX:0, worldY:0, down:false };
|
| 169 |
|
| 170 |
+
// Helper: equip slot index (or -1 for pickaxe)
|
| 171 |
+
function equipSlot(index){
|
| 172 |
+
// index: -1 for pickaxe, 0-4 for inventory
|
| 173 |
+
player.equippedIndex = index;
|
| 174 |
+
// if equipping a slot that is empty, still equip it (shows nothing but slot is 'equipped')
|
| 175 |
+
updateHUD();
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
window.addEventListener('keydown',(e)=>{
|
| 179 |
const k = e.key.toLowerCase();
|
| 180 |
+
|
| 181 |
+
// Movement & action keys only processed when game active
|
| 182 |
if (gameActive){
|
| 183 |
if (k in keys) keys[k] = true;
|
| 184 |
+
|
| 185 |
if (['1','2','3','4','5'].includes(k)) {
|
| 186 |
+
const idx = parseInt(k)-1;
|
| 187 |
+
player.selectedSlot = idx;
|
| 188 |
+
// Equip the pressed slot (this ensures weapons swap immediately)
|
| 189 |
+
equipSlot(idx);
|
|
|
|
| 190 |
updateHUD();
|
| 191 |
}
|
| 192 |
+
|
| 193 |
if (k === 'f') {
|
| 194 |
+
// toggle between pickaxe and selected slot
|
| 195 |
+
if (player.equippedIndex === player.selectedSlot) equipSlot(-1);
|
| 196 |
+
else equipSlot(player.selectedSlot);
|
| 197 |
updateHUD();
|
| 198 |
}
|
| 199 |
+
|
| 200 |
if (k === 'r') keys.r = true;
|
| 201 |
} else {
|
| 202 |
+
// allow selection on landing screen
|
| 203 |
+
if (['1','2','3','4','5'].includes(k)) {
|
| 204 |
+
player.selectedSlot = parseInt(k)-1;
|
| 205 |
+
updateHUD();
|
| 206 |
+
}
|
| 207 |
}
|
| 208 |
});
|
| 209 |
+
|
| 210 |
window.addEventListener('keyup',(e)=>{
|
| 211 |
const k = e.key.toLowerCase();
|
| 212 |
if (k in keys) keys[k] = false;
|
|
|
|
| 223 |
player.angle = Math.atan2(mouse.worldY - player.y, mouse.worldX - player.x);
|
| 224 |
});
|
| 225 |
canvas.addEventListener('mousedown', (e)=>{
|
|
|
|
| 226 |
const rect = canvas.getBoundingClientRect();
|
| 227 |
mouse.canvasX = e.clientX - rect.left;
|
| 228 |
mouse.canvasY = e.clientY - rect.top;
|
|
|
|
| 310 |
slot.className = 'gear-slot';
|
| 311 |
slot.dataset.index = i;
|
| 312 |
slot.addEventListener('click', ()=> {
|
| 313 |
+
const idx = parseInt(slot.dataset.index);
|
| 314 |
+
player.selectedSlot = idx;
|
| 315 |
+
// if clicking currently equipped slot -> toggle unequip to pickaxe
|
| 316 |
+
if (player.equippedIndex === idx) equipSlot(-1);
|
| 317 |
+
else equipSlot(idx);
|
| 318 |
updateHUD();
|
| 319 |
});
|
| 320 |
hudGear.appendChild(slot);
|
| 321 |
}
|
| 322 |
// pickaxe click toggles equip (explicit: clicking pickaxe equips/unequips it)
|
| 323 |
pickaxeSlot.onclick = () => {
|
| 324 |
+
if (player.equippedIndex === -1) {
|
| 325 |
+
// pickaxe already equipped -> equip selected slot instead
|
| 326 |
+
equipSlot(player.selectedSlot);
|
| 327 |
+
} else {
|
| 328 |
+
equipSlot(-1);
|
| 329 |
+
}
|
| 330 |
updateHUD();
|
| 331 |
};
|
| 332 |
updateHUD();
|
|
|
|
| 369 |
const angle = Math.atan2(targetY-originY, targetX-originX);
|
| 370 |
// decrement ammo if present
|
| 371 |
if (typeof weaponObj.ammoInMag === 'number') weaponObj.ammoInMag -= 1;
|
|
|
|
| 372 |
const dmg = weaponObj.weapon && weaponObj.weapon.dmg ? weaponObj.weapon.dmg : (weaponObj.dmg || 10);
|
| 373 |
const color = (weaponObj.weapon && weaponObj.weapon.color) || weaponObj.color || '#fff';
|
| 374 |
bullets.push({
|
|
|
|
| 413 |
}
|
| 414 |
|
| 415 |
// Interact: use medkit in selected slot OR loot chests / pickup items.
|
| 416 |
+
// Builds (objects) are NOT harvested via E anymore; only via pickaxe melee or shooting.
|
| 417 |
function interactNearby(){
|
| 418 |
// use medkit in selected slot
|
| 419 |
const sel = player.selectedSlot;
|
|
|
|
| 693 |
|
| 694 |
// Utility
|
| 695 |
function updatePlayerCount(){
|
| 696 |
+
const aliveEnemies = enemies.filter(e => e.health > 0).length;
|
| 697 |
+
document.getElementById('playerCount').textContent = `${1 + aliveEnemies}/20`;
|
| 698 |
+
|
| 699 |
+
// Victory check: if all enemies dead and player alive & game active -> show victory
|
| 700 |
+
if (gameActive && aliveEnemies === 0 && player.health > 0){
|
| 701 |
+
// stop game loop and show victory UI
|
| 702 |
+
gameActive = false;
|
| 703 |
+
victoryScreen.classList.remove('hidden');
|
| 704 |
+
clearInterval(timerInterval);
|
| 705 |
+
}
|
| 706 |
}
|
| 707 |
|
| 708 |
// Drawing (chests no text; glow; pickups visible)
|
|
|
|
| 968 |
let gameActive = false;
|
| 969 |
|
| 970 |
function startGame(){
|
| 971 |
+
victoryScreen.classList.add('hidden');
|
| 972 |
+
deathScreen.classList.add('hidden');
|
| 973 |
+
|
| 974 |
gameActive = true;
|
| 975 |
landingScreen.classList.add('hidden');
|
| 976 |
gameScreen.classList.remove('hidden');
|
|
|
|
| 1014 |
function playerDeath(){ gameActive = false; deathScreen.classList.remove('hidden'); }
|
| 1015 |
|
| 1016 |
document.getElementById('respawnBtn').addEventListener('click', ()=>{ deathScreen.classList.add('hidden'); landingScreen.classList.remove('hidden'); });
|
| 1017 |
+
goHomeBtn.addEventListener('click', ()=>{ victoryScreen.classList.add('hidden'); gameScreen.classList.add('hidden'); landingScreen.classList.remove('hidden'); });
|
| 1018 |
+
continueBtn.addEventListener('click', ()=>{ victoryScreen.classList.add('hidden'); startGame(); });
|
| 1019 |
|
| 1020 |
document.querySelectorAll('.biome-selector').forEach(el => el.addEventListener('click', ()=> startGame()));
|
| 1021 |
|