diff --git a/README.md b/README.md index f455f1c..28e9c06 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,89 @@ -# JavierExperiance -JavierGame is a web-based game built using JavaScript and HTML. +# Javier The Experience 🐵 -## Features -- **Dynamic Gameplay**: monkey. -- **Responsive Design**: Works fast on chromeos lol. -- **Lightweight**: its small. +A fast-paced 2D side-scrolling action game featuring Javier, a powerful monkey fighter. Battle waves of enemies, collect coins, unlock achievements, and survive as long as you can! + +## ✨ Features + +### Core Gameplay +- **Dynamic Combat System**: Punch enemies, build lightning charge, and unleash devastating attacks +- **Round-Based Progression**: Face increasingly difficult waves of enemies +- **Multiple Enemy Types**: Basic, Fast, Tank, Jumper, and Mini enemies with unique behaviors +- **Shop System**: Upgrade your strength, speed, and health with coins earned in battle + +### New Features +- **🎮 Mobile Support**: Full touch controls with virtual joystick and buttons +- **🔥 Combo System**: Chain kills for bonus points and multipliers +- **⚡ Power-Ups**: Collect speed, damage, and health boosts that spawn every 3 rounds +- **🏆 Achievement System**: Unlock achievements for various milestones +- **📊 Enhanced Stats Display**: Track kills, rounds, combos, and more +- **💾 Auto-Save**: Your progress is automatically saved +- **🎨 Modern UI**: Beautiful glassmorphism design with smooth animations + +### Quality of Life +- **Responsive Design**: Works perfectly on desktop, tablet, and mobile devices +- **Visual Feedback**: Damage indicators, combo displays, and achievement notifications +- **Performance Optimized**: Smooth 60fps gameplay +- **Accessibility**: Clear controls and visual indicators + +## 🎮 Controls + +### Desktop +- **A/D or Arrow Keys**: Move left/right +- **Space or Up Arrow**: Jump +- **J**: Punch attack +- **H**: Lightning strike (when charged) +- **S**: Open shop +- **Escape**: Pause + +### Mobile +- **Virtual Joystick**: Move and jump +- **👊 Button**: Punch +- **⚡ Button**: Lightning attack +- **⬆️ Button**: Jump + +## 🚀 Getting Started -## Installation 1. Clone the repository: ```bash git clone https://github.com/HenryTheAddict/JavierGame.git -2. Open the html. -3. wow + ``` + +2. Open `index.html` in your web browser + +3. Click "Start Experience" and begin your adventure! + +## 🎯 Gameplay Tips + +- Build combos by killing enemies quickly for bonus points +- Save your lightning attack for tough situations +- Collect power-ups that spawn every 3 rounds +- Upgrade your stats in the shop between rounds +- Health regenerates after 6 seconds without taking damage + +## 🏆 Achievements + +- **First Blood**: Kill 10 enemies +- **Slayer**: Kill 50 enemies +- **Massacre**: Kill 100 enemies +- **Combo Master**: Achieve a 10x combo +- **High Roller**: Score 1000 points +- **Survivor**: Reach round 5 + +## 🛠️ Technical Details + +- Built with vanilla JavaScript and HTML5 Canvas +- No external dependencies +- Responsive design with CSS media queries +- LocalStorage for save data +- Optimized for 60fps gameplay + +## 📱 Mobile Support + +The game is fully optimized for mobile devices with: +- Touch-friendly controls +- Responsive canvas sizing +- Virtual joystick for movement +- Large, easy-to-tap buttons +- Prevents accidental scrolling + +Enjoy the experience! 🎮 diff --git a/game.js b/game.js index acf122a..9349d61 100644 --- a/game.js +++ b/game.js @@ -13,10 +13,56 @@ document.addEventListener("DOMContentLoaded", function () { return; } + // Make canvas responsive + function resizeCanvas() { + const container = document.getElementById("gameContainer"); + if (!container) return; + + const maxWidth = Math.min(800, window.innerWidth - 20); + const maxHeight = Math.min(400, window.innerHeight - 200); + + const scale = Math.min(maxWidth / 800, maxHeight / 400); + canvas.style.width = (800 * scale) + "px"; + canvas.style.height = (400 * scale) + "px"; + + // Maintain aspect ratio + canvas.width = 800; + canvas.height = 400; + } + + // Initialize canvas size + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', resizeCanvas); + } else { + resizeCanvas(); + } + + window.addEventListener("resize", resizeCanvas); + window.addEventListener("orientationchange", () => { + setTimeout(resizeCanvas, 100); + }); + const scoreElement = document.getElementById("score"); const highScoreElement = document.getElementById("highScore"); const coinsElement = document.getElementById("coins"); const startRoundButton = document.getElementById("startRoundButton"); + const pauseButton = document.getElementById("pauseButton"); + const comboDisplay = document.getElementById("comboDisplay"); + const powerUpDisplay = document.getElementById("powerUpDisplay"); + const roundDisplay = document.getElementById("roundDisplay"); + const killsDisplay = document.getElementById("killsDisplay"); + const achievementNotification = document.getElementById("achievementNotification"); + const achievementText = document.getElementById("achievementText"); + + // Mobile controls + const joystick = document.getElementById("joystick"); + const joystickHandle = document.getElementById("joystickHandle"); + const mobileJump = document.getElementById("mobileJump"); + const mobilePunch = document.getElementById("mobilePunch"); + const mobileLightning = document.getElementById("mobileLightning"); + let joystickActive = false; + let joystickX = 0; + let joystickY = 0; // Game state let score = 0; @@ -26,6 +72,13 @@ document.addEventListener("DOMContentLoaded", function () { let isShopOpen = false; let gameStarted = false; // Track if game has started let lastDirection = 1; // 1 for right, -1 for left + let kills = 0; // Track total kills + let currentCombo = 0; // Combo counter + let comboTimer = 0; // Combo timer + const comboTimeout = 180; // 3 seconds at 60fps + let lastKillTime = 0; // Time since last kill + const powerUps = []; // Active power-ups + const achievements = new Set(); // Unlocked achievements // Health regeneration system let lastDamageTime = 0; @@ -182,6 +235,97 @@ document.addEventListener("DOMContentLoaded", function () { closeShopButton.addEventListener("click", toggleShop); startButton.addEventListener("click", startGame); shopButton.addEventListener("click", toggleShop); + if (pauseButton) { + pauseButton.addEventListener("click", togglePause); + } + + // Mobile controls setup + function setupMobileControls() { + if (!joystick || !joystickHandle) return; + + let joystickCenterX = 0; + let joystickCenterY = 0; + const joystickRadius = 35; + + function getJoystickPosition(e) { + const rect = joystick.getBoundingClientRect(); + return { + x: (e.touches ? e.touches[0].clientX : e.clientX) - rect.left - rect.width / 2, + y: (e.touches ? e.touches[0].clientY : e.clientY) - rect.top - rect.height / 2 + }; + } + + function updateJoystick(x, y) { + const distance = Math.sqrt(x * x + y * y); + if (distance > joystickRadius) { + x = (x / distance) * joystickRadius; + y = (y / distance) * joystickRadius; + } + joystickHandle.style.transform = `translate(calc(-50% + ${x}px), calc(-50% + ${y}px))`; + joystickX = x / joystickRadius; + joystickY = y / joystickRadius; + } + + joystick.addEventListener("touchstart", (e) => { + e.preventDefault(); + joystickActive = true; + const pos = getJoystickPosition(e); + updateJoystick(pos.x, pos.y); + }); + + joystick.addEventListener("touchmove", (e) => { + if (!joystickActive) return; + e.preventDefault(); + const pos = getJoystickPosition(e); + updateJoystick(pos.x, pos.y); + }); + + joystick.addEventListener("touchend", (e) => { + e.preventDefault(); + joystickActive = false; + joystickX = 0; + joystickY = 0; + joystickHandle.style.transform = "translate(-50%, -50%)"; + }); + + // Mobile button handlers + if (mobileJump) { + mobileJump.addEventListener("touchstart", (e) => { + e.preventDefault(); + keys[" "] = true; + keys["arrowup"] = true; + }); + mobileJump.addEventListener("touchend", (e) => { + e.preventDefault(); + keys[" "] = false; + keys["arrowup"] = false; + }); + } + + if (mobilePunch) { + mobilePunch.addEventListener("touchstart", (e) => { + e.preventDefault(); + if (!player.isPunching && !isPaused && !isShopOpen) { + player.isPunching = true; + setTimeout(() => { + player.isPunching = false; + }, 150); + checkPunchHits(); + } + }); + } + + if (mobileLightning) { + mobileLightning.addEventListener("touchstart", (e) => { + e.preventDefault(); + if (!isPaused && !isShopOpen && !player.isDead) { + performElectrocutionAttack(); + } + }); + } + } + + setupMobileControls(); // Remove the start round button functionality as we're auto-starting rounds startRoundButton.style.display = "none"; @@ -317,17 +461,206 @@ document.addEventListener("DOMContentLoaded", function () { } } + // Combo display function + function showCombo(combo) { + if (!comboDisplay) return; + comboDisplay.textContent = `${combo}x COMBO!`; + comboDisplay.classList.add("active"); + setTimeout(() => { + comboDisplay.classList.remove("active"); + }, 1000); + } + + // Update combo timer + function updateCombo() { + if (comboTimer > 0) { + comboTimer--; + if (comboTimer === 0 && currentCombo > 1) { + currentCombo = 0; + if (comboDisplay) comboDisplay.classList.remove("active"); + } + } + } + + // Power-up system + function spawnPowerUp(x, y) { + const powerUpTypes = [ + { type: "speed", icon: "⚡", color: "#f39c12", duration: 600 }, + { type: "damage", icon: "💪", color: "#e74c3c", duration: 600 }, + { type: "health", icon: "❤️", color: "#2ecc71", duration: 0 } + ]; + + const powerUp = powerUpTypes[Math.floor(Math.random() * powerUpTypes.length)]; + powerUps.push({ + x: x, + y: y, + width: 30, + height: 30, + type: powerUp.type, + icon: powerUp.icon, + color: powerUp.color, + rotation: 0, + collected: false + }); + } + + function collectPowerUp(powerUp) { + if (powerUp.collected) return; + powerUp.collected = true; + + const activePowerUp = { + type: powerUp.type, + timer: powerUp.type === "health" ? 0 : 600, + maxTime: 600 + }; + + switch (powerUp.type) { + case "speed": + player.speed *= 1.5; + break; + case "damage": + player.punchDamage *= 1.5; + break; + case "health": + player.health = Math.min(player.health + 30, player.maxHealth); + break; + } + + // Add to active power-ups list (check if already exists) + player.activePowerUps = player.activePowerUps || []; + const existingIndex = player.activePowerUps.findIndex(p => p.type === powerUp.type); + if (existingIndex > -1) { + // Reset timer if already exists + player.activePowerUps[existingIndex].timer = activePowerUp.timer; + } else { + player.activePowerUps.push(activePowerUp); + } + + // Remove power-up from world + const index = powerUps.indexOf(powerUp); + if (index > -1) powerUps.splice(index, 1); + } + + function updatePowerUps() { + // Update active power-ups + if (player.activePowerUps) { + for (let i = player.activePowerUps.length - 1; i >= 0; i--) { + const powerUp = player.activePowerUps[i]; + if (powerUp.timer > 0) { + powerUp.timer--; + if (powerUp.timer === 0) { + // Remove power-up effect + switch (powerUp.type) { + case "speed": + player.speed /= 1.5; + break; + case "damage": + player.punchDamage /= 1.5; + break; + } + player.activePowerUps.splice(i, 1); + } + } + } + } + + // Update power-up display + if (powerUpDisplay) { + powerUpDisplay.innerHTML = ""; + if (player.activePowerUps && player.activePowerUps.length > 0) { + player.activePowerUps.forEach(powerUp => { + const item = document.createElement("div"); + item.className = "power-up-item"; + const progress = powerUp.timer > 0 ? (powerUp.timer / powerUp.maxTime) * 100 : 100; + item.innerHTML = ` + +