diff options
-rw-r--r-- | html/plains/game.js | 88 |
1 files changed, 76 insertions, 12 deletions
diff --git a/html/plains/game.js b/html/plains/game.js index ad7b8c3..43b4446 100644 --- a/html/plains/game.js +++ b/html/plains/game.js @@ -152,6 +152,15 @@ const CONFIG = { startDelay: 1500, // Start idle animation after 1.5 seconds lookSpeed: 0.001, // Speed of the looking animation lookRadius: 0.4 // How far to look around (in radians) + }, + equipment: { + swordUnlockCount: 3, // Number of rescues needed to unlock sword + unlockAnimation: { + duration: 6000, // Duration of unlock animation in ms + glowColor: 'rgba(255, 215, 0, 0.6)', // Golden glow + messageText: 'Sword Unlocked!', + messageColor: '#FFD700' // Gold color for text + } } }, sword: { @@ -304,7 +313,7 @@ const createInitialState = () => ({ direction: { x: 0, y: -1 }, swordAngle: 0, isSwinging: false, - equipment: 'sword', + equipment: 'unarmed', // Start without sword bubbles: [], bubbleParticles: [], lastBubbleTime: 0, @@ -313,7 +322,9 @@ const createInitialState = () => ({ dashExhausted: false, // Is dash on cooldown? lastInputTime: 0, // Track when the last input occurred baseDirection: { x: 0, y: -1 }, - lastDashEnd: 0 + lastDashEnd: 0, + swordUnlocked: false, + rescuedCount: 0 }, particles: [], footprints: [], @@ -395,15 +406,18 @@ const inputHandlers = { handleAttack: (state, animationTime) => { if (state.player.isDefending) return state; - if (state.player.equipment === 'sword' && !state.player.isSwinging) { - return { - ...state, - player: { - ...state.player, - isSwinging: true, - swordAngle: Math.atan2(state.player.direction.y, state.player.direction.x) - Math.PI / 2 - } - }; + if (state.player.equipment === 'sword') { + if (!state.player.swordUnlocked) return state; // Prevent sword usage if not unlocked + if (!state.player.isSwinging) { + return { + ...state, + player: { + ...state.player, + isSwinging: true, + swordAngle: Math.atan2(state.player.direction.y, state.player.direction.x) - Math.PI / 2 + } + }; + } } else if (state.player.equipment === 'unarmed') { return createBubbleAttack(state, animationTime); } @@ -411,6 +425,8 @@ const inputHandlers = { }, handleEquipmentSwitch: (state) => { + if (!state.player.swordUnlocked) return state; // Can't switch if sword isn't unlocked + const equipment = ['sword', 'unarmed']; const currentIndex = equipment.indexOf(state.player.equipment); return { @@ -1393,10 +1409,18 @@ const updatePlayer = () => { if (distance < (CONFIG.player.size + CONFIG.world.villagers.size) / 2) { villager.status = 'rescued'; + state.player.rescuedCount++; + + // Check for sword unlock + if (state.player.rescuedCount >= CONFIG.player.equipment.swordUnlockCount && !state.player.swordUnlocked) { + state.player.swordUnlocked = true; + state.player.equipment = 'sword'; // Auto-equip sword + showSwordUnlockAnimation(); + } // Assign new position in village when rescued const villageSize = CONFIG.world.village.size * CONFIG.display.grid.size; - const margin = CONFIG.world.villagers.size; // Keep villagers away from village edges + const margin = CONFIG.world.villagers.size; villager.x = margin + Math.random() * (villageSize - margin * 2); villager.y = margin + Math.random() * (villageSize - margin * 2); @@ -1632,4 +1656,44 @@ const getCellInfo = (x, y) => { grass: !hasTree && !hasMushrooms && !hasFlowers && hasGrass } }; +}; + +const showSwordUnlockAnimation = () => { + // Create container for animation + const container = document.createElement('div'); + container.style.position = 'fixed'; + container.style.top = '50%'; + container.style.left = '50%'; + container.style.transform = 'translate(-50%, -50%)'; + container.style.padding = '20px'; + container.style.backgroundColor = 'rgba(0, 0, 0, 0.8)'; + container.style.borderRadius = '10px'; + container.style.animation = 'glow 2s ease-in-out'; + container.style.zIndex = '1000'; + + // Add CSS animation + const style = document.createElement('style'); + style.textContent = ` + @keyframes glow { + 0% { box-shadow: 0 0 0 0 ${CONFIG.player.equipment.unlockAnimation.glowColor}; opacity: 0; } + 50% { box-shadow: 0 0 30px 10px ${CONFIG.player.equipment.unlockAnimation.glowColor}; opacity: 1; } + 100% { box-shadow: 0 0 0 0 ${CONFIG.player.equipment.unlockAnimation.glowColor}; opacity: 0; } + } + `; + document.head.appendChild(style); + + // Add message text + const message = document.createElement('div'); + message.textContent = CONFIG.player.equipment.unlockAnimation.messageText; + message.style.color = CONFIG.player.equipment.unlockAnimation.messageColor; + message.style.fontSize = '24px'; + message.style.textAlign = 'center'; + container.appendChild(message); + + // Add to document and remove after animation + document.body.appendChild(container); + setTimeout(() => { + document.body.removeChild(container); + document.head.removeChild(style); + }, CONFIG.player.equipment.unlockAnimation.duration); }; \ No newline at end of file |