diff --git a/Games/Medieval_RPG/README.md b/Games/Medieval_RPG/README.md new file mode 100644 index 000000000..c788b97f8 --- /dev/null +++ b/Games/Medieval_RPG/README.md @@ -0,0 +1,39 @@ +# **Medieval RPG - Kingdom Quest** โš”๏ธ + +## **Description ๐Ÿ“ƒ** + +**Medieval RPG - Kingdom Quest** is a complete 2D action RPG game built with HTML5 Canvas, CSS3, and JavaScript. Experience an immersive medieval fantasy world with combat, automatic inventory management, and progressive character development. + +## **Functionalities ๐ŸŽฎ** + +- **๐Ÿ—บ๏ธ Multi-Map World System**: 4 explorable areas (Village Outskirts, Dark Forest, Ancient Ruins, Dragon's Lair) +- **โš”๏ธ Keyboard Combat System**: Pure keyboard-based combat with automatic targeting +- **๐ŸŽ’ Automatic Inventory**: Items are automatically collected when walking over them +- **๐Ÿ—ก๏ธ Progressive Weapon System**: 5 swords from Rusty to Legendary with increasing power +- **โœจ Attack Animations**: Particle effects, damage numbers, and critical hit animations +- **๐Ÿ“œ Dynamic Quest System**: Progressive quests with completion rewards +- ** Portal Travel**: Fast travel between different map areas + +## **How to play? ๐Ÿ•น๏ธ** + +### **Controls:** + +- **W A S D / Arrow Keys**: Move your character +- **TAB**: Attack nearby enemies +- **F**: Use equipped weapon's special attack +- **Q**: Quick heal using available potions +- **SPACE**: Interact with NPCs and shops +- **M**: Open map selection to travel between areas +- **ESC**: Close any open modals + +### **Gameplay:** + +- **Village Outskirts** (โญ): Starting area with basic enemies +- **Dark Forest** (โญโญ): Tougher enemies and enhanced weapons +- **Ancient Ruins** (โญโญโญ): Rare artifacts and challenging combat +- **Dragon's Lair** (โญโญโญโญโญ): End-game content with legendary equipment + +## **Screenshots ๐Ÿ“ธ** + +Medieval RPG Game Screenshot +Medieval RPG Game Screenshot diff --git a/Games/Medieval_RPG/game.js b/Games/Medieval_RPG/game.js new file mode 100644 index 000000000..b095ab33a --- /dev/null +++ b/Games/Medieval_RPG/game.js @@ -0,0 +1,2090 @@ +// Medieval RPG - Complete Game with Keyboard Combat and Swords +class MedievalRPG { + constructor() { + this.canvas = document.getElementById("gameCanvas"); + this.ctx = this.canvas.getContext("2d"); + this.gameWidth = this.canvas.width; + this.gameHeight = this.canvas.height; + + // Game state + this.gameRunning = true; + this.lastTime = 0; + this.deltaTime = 0; + this.inCombat = false; + this.combatCooldown = 0; + + // Player object + this.player = { + x: 400, + y: 300, + width: 32, + height: 32, + speed: 180, + level: 1, + experience: 0, + experienceToNext: 100, + hp: 100, + maxHp: 100, + gold: 50, + score: 0, + inventory: [], + maxInventory: 20, + equipment: { + weapon: null, + armor: null, + accessory: null, + }, + stats: { + strength: 10, + defense: 5, + agility: 8, + magic: 5, + }, + isAttacking: false, + attackCooldown: 0, + lastAttackTime: 0, + attackAnimation: { + active: false, + duration: 0, + type: "normal", // 'normal', 'special', 'critical' + particles: [], + }, + }; + + // Quests tracking + this.quests = { + swordFound: false, + treasuresCollected: 0, + monstersDefeated: 0, + completedQuests: [], + availableQuests: [ + { + id: "sword_quest", + title: "๐Ÿ—ก๏ธ Find the Lost Sword", + description: + "Search the ancient ruins for the legendary blade", + requirement: "swordFound", + target: 1, + completed: false, + reward: { gold: 200, xp: 100, item: "Health Potion" }, + }, + { + id: "treasure_quest", + title: "๐Ÿ’ฐ Treasure Hunter", + description: "Collect 10 treasures", + requirement: "treasuresCollected", + target: 10, + completed: false, + reward: { gold: 500, xp: 250, item: "Magic Ring" }, + }, + { + id: "monster_quest", + title: "โš”๏ธ Monster Slayer", + description: "Defeat 5 monsters", + requirement: "monstersDefeated", + target: 5, + completed: false, + reward: { gold: 300, xp: 150, item: "Iron Armor" }, + }, + ], + }; + + // Current map and map system + this.currentMap = 0; + this.maps = [ + { + name: "Village Outskirts", + width: 1600, + height: 1200, + theme: "grass", + emoji: "๐Ÿ˜๏ธ", + difficulty: 1, + description: + "The peaceful village outskirts where your adventure begins", + rewards: "Basic equipment and starting resources", + }, + { + name: "Dark Forest", + width: 2000, + height: 1600, + theme: "forest", + emoji: "๐ŸŒฒ", + difficulty: 2, + description: + "A mysterious forest filled with dangerous creatures", + rewards: "Enhanced weapons and forest treasures", + }, + { + name: "Ancient Ruins", + width: 1800, + height: 1400, + theme: "ruins", + emoji: "๐Ÿ›๏ธ", + difficulty: 3, + description: "Crumbling ruins that hold ancient secrets", + rewards: "Rare artifacts and enchanted items", + }, + { + name: "Dragon's Lair", + width: 2400, + height: 1800, + theme: "cave", + emoji: "๐Ÿ‰", + difficulty: 5, + description: "The final challenge - face the ancient dragon", + rewards: "Legendary equipment and ultimate glory", + }, + ]; + + // Animation particles system + this.particles = []; + this.damageNumbers = []; + + // Input handling + this.keys = {}; + this.setupInput(); + + // Game objects + this.treasures = []; + this.enemies = []; + this.npcs = []; + this.swords = []; + this.items = []; + + // Game map + this.map = { + width: this.maps[this.currentMap].width, + height: this.maps[this.currentMap].height, + tiles: [], + }; + + // Map portals for travel + this.portals = []; + + // Camera + this.camera = { + x: 0, + y: 0, + }; + + // Initialize game + this.initializeGame(); + this.gameLoop(); + } + + setupInput() { + document.addEventListener("keydown", (e) => { + this.keys[e.key.toLowerCase()] = true; + + // Combat controls + if (e.key.toLowerCase() === "tab") { + e.preventDefault(); + this.performAttack(); + } + if (e.key.toLowerCase() === "f") { + this.useSpecialAttack(); + } + if (e.key.toLowerCase() === "q") { + this.usePotion(); + } + if (e.key === " ") { + this.handleInteraction(); + } + if (e.key.toLowerCase() === "m") { + this.showMapSelection(); + } + }); + + document.addEventListener("keyup", (e) => { + this.keys[e.key.toLowerCase()] = false; + }); + } + + initializeGame() { + // Generate map + this.generateMap(); + + // Create swords (special items) + this.createSwords(); + + // Create treasures + this.createTreasures(); + + // Create enemies + this.createEnemies(); + + // Create NPCs + this.createNPCs(); + + // Create items and potions + this.createItems(); + + // Create portals between maps + this.createPortals(); + + // Give player starting equipment + this.giveStartingEquipment(); + + // Initialize inventory UI + this.updateInventoryUI(); + this.updateUI(); + + // Welcome message + this.showMessage( + "๐Ÿฐ Welcome to Kingdom Quest! Find swords, defeat monsters, and collect treasures!", + "success" + ); + this.showMessage( + "โš”๏ธ Press TAB to attack enemies, F for special attacks, Q to use potions", + "info" + ); + } + + giveStartingEquipment() { + // Give player a basic sword to start + const basicSword = { + name: "Rusty Sword", + icon: "๐Ÿ—ก๏ธ", + type: "weapon", + damage: 15, + special: "Quick Strike", + value: 25, + rarity: "common", + }; + + this.addToInventory(basicSword); + this.player.equipment.weapon = basicSword; + this.showMessage( + "๐Ÿ—ก๏ธ You start with a Rusty Sword! Press TAB to attack enemies.", + "success" + ); + } + + generateMap() { + const currentMapData = this.maps[this.currentMap]; + const tileSize = 32; + const cols = Math.ceil(currentMapData.width / tileSize); + const rows = Math.ceil(currentMapData.height / tileSize); + + this.map.width = currentMapData.width; + this.map.height = currentMapData.height; + this.map.tiles = []; + + for (let y = 0; y < rows; y++) { + this.map.tiles[y] = []; + for (let x = 0; x < cols; x++) { + let tile = this.getMapThemeTile(currentMapData.theme); + this.map.tiles[y][x] = tile; + } + } + } + + getMapThemeTile(theme) { + const random = Math.random(); + + switch (theme) { + case "grass": + if (random < 0.1) return "stone"; + else if (random < 0.15) return "tree"; + else if (random < 0.18) return "water"; + return "grass"; + + case "forest": + if (random < 0.4) return "tree"; + else if (random < 0.5) return "dark_tree"; + else if (random < 0.6) return "stone"; + return "grass"; + + case "ruins": + if (random < 0.3) return "stone"; + else if (random < 0.4) return "ruins_wall"; + else if (random < 0.5) return "ruins_floor"; + return "grass"; + + case "cave": + if (random < 0.5) return "cave_wall"; + else if (random < 0.6) return "cave_floor"; + else if (random < 0.7) return "lava"; + return "stone"; + + default: + return "grass"; + } + } + + createPortals() { + this.portals = []; + + // Only create portals if not on the last map + if (this.currentMap < this.maps.length - 1) { + // Portal to next map + this.portals.push({ + x: this.map.width - 100, + y: this.map.height / 2, + width: 40, + height: 40, + targetMap: this.currentMap + 1, + icon: "๐ŸŒ€", + name: `Portal to ${this.maps[this.currentMap + 1].name}`, + }); + } + + // Portal to previous map (if not on first map) + if (this.currentMap > 0) { + this.portals.push({ + x: 50, + y: this.map.height / 2, + width: 40, + height: 40, + targetMap: this.currentMap - 1, + icon: "๐ŸŒ€", + name: `Portal to ${this.maps[this.currentMap - 1].name}`, + }); + } + } + + createSwords() { + const swordTypes = [ + { + name: "Iron Sword", + icon: "โš”๏ธ", + damage: 25, + special: "Power Strike", + value: 100, + rarity: "uncommon", + points: 500, + }, + { + name: "Silver Blade", + icon: "๐Ÿ—ก๏ธ", + damage: 35, + special: "Lightning Cut", + value: 200, + rarity: "rare", + points: 800, + }, + { + name: "Enchanted Sword", + icon: "โœจ", + damage: 50, + special: "Magic Slash", + value: 350, + rarity: "epic", + points: 1200, + }, + { + name: "Dragon Slayer", + icon: "๐Ÿฒ", + damage: 75, + special: "Dragon's Wrath", + value: 500, + rarity: "legendary", + points: 2000, + }, + { + name: "Excalibur", + icon: "๐Ÿ‘‘", + damage: 100, + special: "Divine Strike", + value: 1000, + rarity: "mythic", + points: 5000, + }, + ]; + + // Place swords strategically around the map + swordTypes.forEach((swordType, index) => { + const sword = { + x: 200 + index * 300 + Math.random() * 200, + y: 200 + index * 200 + Math.random() * 200, + width: 24, + height: 24, + type: swordType, + collected: false, + glowing: true, + }; + this.swords.push(sword); + }); + } + + createTreasures() { + const treasureTypes = [ + { + name: "Gold Coin", + icon: "๐Ÿช™", + value: 10, + rarity: "common", + points: 50, + }, + { + name: "Silver Chalice", + icon: "๐Ÿ†", + value: 50, + rarity: "uncommon", + points: 200, + }, + { + name: "Ancient Scroll", + icon: "๐Ÿ“œ", + value: 75, + rarity: "rare", + points: 300, + }, + { + name: "Magic Crystal", + icon: "๐Ÿ’Ž", + value: 100, + rarity: "epic", + points: 500, + }, + { + name: "Dragon Egg", + icon: "๐Ÿฅš", + value: 200, + rarity: "legendary", + points: 1000, + }, + ]; + + // Place treasures randomly + for (let i = 0; i < 40; i++) { + const treasureType = + treasureTypes[Math.floor(Math.random() * treasureTypes.length)]; + const treasure = { + x: Math.random() * (this.map.width - 50) + 25, + y: Math.random() * (this.map.height - 50) + 25, + width: 20, + height: 20, + type: treasureType, + collected: false, + }; + this.treasures.push(treasure); + } + } + + createEnemies() { + const enemyTypes = [ + { + name: "Goblin", + icon: "๐Ÿ‘น", + hp: 30, + maxHp: 30, + damage: 8, + xp: 25, + gold: 15, + speed: 50, + }, + { + name: "Orc Warrior", + icon: "๐Ÿ‘บ", + hp: 50, + maxHp: 50, + damage: 12, + xp: 40, + gold: 25, + speed: 40, + }, + { + name: "Skeleton", + icon: "๐Ÿ’€", + hp: 35, + maxHp: 35, + damage: 10, + xp: 30, + gold: 20, + speed: 45, + }, + { + name: "Dark Knight", + icon: "๐Ÿ–ค", + hp: 80, + maxHp: 80, + damage: 18, + xp: 60, + gold: 50, + speed: 35, + }, + ]; + + // Place enemies around the map + for (let i = 0; i < 25; i++) { + const enemyType = + enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; + const enemy = { + x: Math.random() * (this.map.width - 100) + 50, + y: Math.random() * (this.map.height - 100) + 50, + width: 32, + height: 32, + type: enemyType, + hp: enemyType.hp, + maxHp: enemyType.maxHp, + isAlive: true, + lastAttackTime: 0, + moveDirection: Math.random() * Math.PI * 2, + changeDirectionTime: 0, + }; + this.enemies.push(enemy); + } + } + + createNPCs() { + const npcTypes = [ + { + name: "Village Merchant", + icon: "๐Ÿง™โ€โ™‚๏ธ", + type: "shop", + dialogue: "Welcome! I sell weapons, armor, and potions!", + }, + { + name: "Wise Elder", + icon: "๐Ÿ‘ด", + type: "quest", + dialogue: "Find the legendary swords to save our kingdom!", + }, + { + name: "Healer", + icon: "๐Ÿง™โ€โ™€๏ธ", + type: "healer", + dialogue: "I can restore your health for gold.", + }, + ]; + + npcTypes.forEach((npcType, index) => { + const npc = { + x: 100 + index * 400, + y: 100, + width: 32, + height: 32, + type: npcType, + }; + this.npcs.push(npc); + }); + } + + createItems() { + const itemTypes = [ + { + name: "Health Potion", + icon: "๐Ÿงช", + type: "potion", + effect: "heal", + value: 50, + price: 30, + }, + { + name: "Iron Armor", + icon: "๐Ÿ›ก๏ธ", + type: "armor", + defense: 10, + value: 80, + price: 100, + }, + { + name: "Magic Ring", + icon: "๐Ÿ’", + type: "accessory", + magic: 5, + value: 120, + price: 150, + }, + ]; + + // Place some items around the map + for (let i = 0; i < 15; i++) { + const itemType = + itemTypes[Math.floor(Math.random() * itemTypes.length)]; + const item = { + x: Math.random() * (this.map.width - 50) + 25, + y: Math.random() * (this.map.height - 50) + 25, + width: 20, + height: 20, + type: itemType, + collected: false, + }; + this.items.push(item); + } + } + + gameLoop(currentTime = 0) { + this.deltaTime = (currentTime - this.lastTime) / 1000; + this.lastTime = currentTime; + + if (this.gameRunning) { + this.update(); + this.render(); + } + + requestAnimationFrame((time) => this.gameLoop(time)); + } + + update() { + // Update timers + if (this.combatCooldown > 0) { + this.combatCooldown -= this.deltaTime; + } + if (this.player.attackCooldown > 0) { + this.player.attackCooldown -= this.deltaTime; + } + + // Update attack animations + this.updateAttackAnimations(); + + // Update particles + this.updateParticles(); + + // Update damage numbers + this.updateDamageNumbers(); + + // Update player + this.updatePlayer(); + + // Update camera + this.updateCamera(); + + // Update enemies + this.updateEnemies(); + + // Check collisions + this.checkCollisions(); + + // Update combat status + this.updateCombatStatus(); + + // Check quest completion + this.checkQuestCompletion(); + } + + updatePlayer() { + let moveX = 0; + let moveY = 0; + + // Handle movement input + if (this.keys["w"] || this.keys["arrowup"]) moveY = -1; + if (this.keys["s"] || this.keys["arrowdown"]) moveY = 1; + if (this.keys["a"] || this.keys["arrowleft"]) moveX = -1; + if (this.keys["d"] || this.keys["arrowright"]) moveX = 1; + + // Normalize diagonal movement + if (moveX !== 0 && moveY !== 0) { + moveX *= 0.707; + moveY *= 0.707; + } + + // Apply movement + const newX = this.player.x + moveX * this.player.speed * this.deltaTime; + const newY = this.player.y + moveY * this.player.speed * this.deltaTime; + + // Check bounds + if (newX >= 0 && newX <= this.map.width - this.player.width) { + this.player.x = newX; + } + if (newY >= 0 && newY <= this.map.height - this.player.height) { + this.player.y = newY; + } + } + + updateCamera() { + this.camera.x = this.player.x - this.gameWidth / 2; + this.camera.y = this.player.y - this.gameHeight / 2; + + this.camera.x = Math.max( + 0, + Math.min(this.camera.x, this.map.width - this.gameWidth) + ); + this.camera.y = Math.max( + 0, + Math.min(this.camera.y, this.map.height - this.gameHeight) + ); + } + + updateEnemies() { + this.enemies.forEach((enemy) => { + if (!enemy.isAlive) return; + + // Simple AI: move randomly and attack player if nearby + enemy.changeDirectionTime += this.deltaTime; + if (enemy.changeDirectionTime > 2) { + enemy.moveDirection = Math.random() * Math.PI * 2; + enemy.changeDirectionTime = 0; + } + + // Move enemy + const moveX = + Math.cos(enemy.moveDirection) * + enemy.type.speed * + this.deltaTime; + const moveY = + Math.sin(enemy.moveDirection) * + enemy.type.speed * + this.deltaTime; + + enemy.x += moveX; + enemy.y += moveY; + + // Keep enemies in bounds + enemy.x = Math.max( + 0, + Math.min(enemy.x, this.map.width - enemy.width) + ); + enemy.y = Math.max( + 0, + Math.min(enemy.y, this.map.height - enemy.height) + ); + + // Check if enemy can attack player + const distance = this.getDistance(this.player, enemy); + if (distance < 50 && Date.now() - enemy.lastAttackTime > 2000) { + this.enemyAttackPlayer(enemy); + enemy.lastAttackTime = Date.now(); + } + }); + } + + checkCollisions() { + // Check sword collection + this.swords.forEach((sword) => { + if (!sword.collected && this.isColliding(this.player, sword)) { + this.collectSword(sword); + } + }); + + // Check treasure collection + this.treasures.forEach((treasure) => { + if ( + !treasure.collected && + this.isColliding(this.player, treasure) + ) { + this.collectTreasure(treasure); + } + }); + + // Check item collection + this.items.forEach((item) => { + if (!item.collected && this.isColliding(this.player, item)) { + this.collectItem(item); + } + }); + + // Check portal interactions + this.portals.forEach((portal) => { + if (this.isColliding(this.player, portal)) { + this.showPortalPrompt(portal); + } + }); + } + + updateAttackAnimations() { + if (this.player.attackAnimation.active) { + this.player.attackAnimation.duration -= this.deltaTime; + + if (this.player.attackAnimation.duration <= 0) { + this.player.attackAnimation.active = false; + this.player.attackAnimation.particles = []; + } else { + // Update attack particles + this.player.attackAnimation.particles.forEach((particle) => { + particle.x += particle.vx * this.deltaTime; + particle.y += particle.vy * this.deltaTime; + particle.life -= this.deltaTime; + particle.alpha = particle.life / particle.maxLife; + }); + + // Remove dead particles + this.player.attackAnimation.particles = + this.player.attackAnimation.particles.filter( + (p) => p.life > 0 + ); + } + } + } + + updateParticles() { + this.particles.forEach((particle) => { + particle.x += particle.vx * this.deltaTime; + particle.y += particle.vy * this.deltaTime; + particle.life -= this.deltaTime; + particle.alpha = particle.life / particle.maxLife; + }); + + this.particles = this.particles.filter((p) => p.life > 0); + } + + updateDamageNumbers() { + this.damageNumbers.forEach((dmg) => { + dmg.y -= 50 * this.deltaTime; + dmg.life -= this.deltaTime; + dmg.alpha = dmg.life / dmg.maxLife; + }); + + this.damageNumbers = this.damageNumbers.filter((d) => d.life > 0); + } + + checkQuestCompletion() { + this.quests.availableQuests.forEach((quest) => { + if (!quest.completed) { + let progress = 0; + + switch (quest.requirement) { + case "swordFound": + progress = this.quests.swordFound ? 1 : 0; + break; + case "treasuresCollected": + progress = this.quests.treasuresCollected; + break; + case "monstersDefeated": + progress = this.quests.monstersDefeated; + break; + } + + if (progress >= quest.target && !quest.completed) { + this.completeQuest(quest); + } + } + }); + } + + completeQuest(quest) { + quest.completed = true; + this.quests.completedQuests.push(quest.id); + + // Give rewards + this.player.gold += quest.reward.gold; + this.addExperience(quest.reward.xp); + + // Add reward item to inventory + if (quest.reward.item) { + const rewardItem = this.createRewardItem(quest.reward.item); + this.addToInventory(rewardItem); + } + + this.showMessage(`๐ŸŽ‰ Quest Completed: ${quest.title}!`, "success"); + this.showMessage( + `๐Ÿ’ฐ Received: ${quest.reward.gold} gold, ${quest.reward.xp} XP`, + "success" + ); + if (quest.reward.item) { + this.showMessage(`๐Ÿ“ฆ Received: ${quest.reward.item}!`, "success"); + } + + this.updateQuestUI(); + this.updateUI(); + } + + createRewardItem(itemName) { + const rewardItems = { + "Health Potion": { + name: "Health Potion", + icon: "๐Ÿงช", + type: "potion", + effect: "heal", + value: 50, + }, + "Magic Ring": { + name: "Magic Ring", + icon: "๐Ÿ’", + type: "accessory", + magic: 10, + value: 200, + }, + "Iron Armor": { + name: "Iron Armor", + icon: "๐Ÿ›ก๏ธ", + type: "armor", + defense: 15, + value: 150, + }, + }; + + return rewardItems[itemName] || rewardItems["Health Potion"]; + } + + showPortalPrompt(portal) { + if (!this.portalPromptShown) { + this.showMessage( + `๐ŸŒ€ Press SPACE to travel to ${portal.name}`, + "info" + ); + this.portalPromptShown = true; + this.nearPortal = portal; + + setTimeout(() => { + this.portalPromptShown = false; + }, 3000); + } + } + + travelToMap(mapIndex) { + if (mapIndex >= 0 && mapIndex < this.maps.length) { + this.currentMap = mapIndex; + + // Reset map and objects + this.map.width = this.maps[this.currentMap].width; + this.map.height = this.maps[this.currentMap].height; + + // Regenerate everything for new map + this.generateMap(); + this.createSwords(); + this.createTreasures(); + this.createEnemies(); + this.createNPCs(); + this.createItems(); + this.createPortals(); + + // Reset player position + this.player.x = 100; + this.player.y = 100; + + // Update current map display + const currentMapElement = + document.getElementById("current-map-name"); + if (currentMapElement) { + currentMapElement.textContent = this.maps[this.currentMap].name; + } + + this.showMessage( + `๐Ÿ—บ๏ธ Traveled to ${this.maps[this.currentMap].name}!`, + "success" + ); + this.showMessage( + `๐Ÿ“œ ${this.maps[this.currentMap].description}`, + "info" + ); + } + } + + isColliding(obj1, obj2) { + return ( + obj1.x < obj2.x + obj2.width && + obj1.x + obj1.width > obj2.x && + obj1.y < obj2.y + obj2.height && + obj1.y + obj1.height > obj2.y + ); + } + + getDistance(obj1, obj2) { + const dx = obj1.x + obj1.width / 2 - (obj2.x + obj2.width / 2); + const dy = obj1.y + obj1.height / 2 - (obj2.y + obj2.height / 2); + return Math.sqrt(dx * dx + dy * dy); + } + + collectSword(sword) { + sword.collected = true; + + const swordItem = { + name: sword.type.name, + icon: sword.type.icon, + type: "weapon", + damage: sword.type.damage, + special: sword.type.special, + value: sword.type.value, + rarity: sword.type.rarity, + }; + + this.addToInventory(swordItem); + this.player.score += sword.type.points; + this.player.gold += sword.type.value; + + // Auto-equip if better than current weapon + if ( + !this.player.equipment.weapon || + sword.type.damage > this.player.equipment.weapon.damage + ) { + this.player.equipment.weapon = swordItem; + this.showMessage(`โš”๏ธ Equipped ${sword.type.name}!`, "success"); + } + + // Update quest progress + this.quests.swordFound = true; + this.updateQuestUI(); + + this.showMessage( + `๐Ÿ—ก๏ธ Found ${sword.type.name}! +${sword.type.points} points`, + "success" + ); + this.updateUI(); + this.updateInventoryUI(); + } + + collectTreasure(treasure) { + treasure.collected = true; + + const treasureItem = { + name: treasure.type.name, + icon: treasure.type.icon, + type: "treasure", + value: treasure.type.value, + rarity: treasure.type.rarity, + }; + + this.addToInventory(treasureItem); + this.player.gold += treasure.type.value; + this.player.score += treasure.type.points; + this.addExperience(treasure.type.points / 10); + + // Update quest progress + this.quests.treasuresCollected++; + this.updateQuestUI(); + + this.showMessage( + `๐Ÿ’ฐ Found ${treasure.type.name}! +${treasure.type.points} points`, + "success" + ); + this.updateUI(); + this.updateInventoryUI(); + } + + collectItem(item) { + item.collected = true; + + this.addToInventory({ + name: item.type.name, + icon: item.type.icon, + type: item.type.type, + effect: item.type.effect, + value: item.type.value, + defense: item.type.defense, + magic: item.type.magic, + }); + + this.showMessage(`๐Ÿ“ฆ Found ${item.type.name}!`, "success"); + this.updateInventoryUI(); + } + + addToInventory(item) { + if (this.player.inventory.length < this.player.maxInventory) { + this.player.inventory.push(item); + return true; + } else { + this.showMessage("โŒ Inventory full!", "error"); + return false; + } + } + + performAttack() { + if (this.player.attackCooldown > 0) return; + + // Find nearest enemy within attack range + let nearestEnemy = null; + let nearestDistance = Infinity; + + this.enemies.forEach((enemy) => { + if (!enemy.isAlive) return; + + const distance = this.getDistance(this.player, enemy); + if (distance < 60 && distance < nearestDistance) { + nearestEnemy = enemy; + nearestDistance = distance; + } + }); + + if (nearestEnemy) { + this.attackEnemy(nearestEnemy); + } else { + this.showMessage("โš”๏ธ No enemies in range!", "warning"); + } + } + + attackEnemy(enemy) { + const weapon = this.player.equipment.weapon; + const baseDamage = weapon ? weapon.damage : 10; + const criticalChance = 0.2; // 20% critical hit chance + const isCritical = Math.random() < criticalChance; + const totalDamage = Math.floor( + (baseDamage + this.player.stats.strength) * (isCritical ? 1.5 : 1) + ); + + enemy.hp -= totalDamage; + this.player.attackCooldown = 1.0; // 1 second cooldown + + // Create attack animation + this.createAttackAnimation(enemy, isCritical ? "critical" : "normal"); + + // Create damage number + this.createDamageNumber(enemy.x, enemy.y, totalDamage, isCritical); + + // Create hit particles + this.createHitParticles( + enemy.x + enemy.width / 2, + enemy.y + enemy.height / 2 + ); + + const hitMessage = isCritical + ? `๐Ÿ’ฅ CRITICAL HIT! ${enemy.type.name} for ${totalDamage} damage!` + : `โš”๏ธ Hit ${enemy.type.name} for ${totalDamage} damage!`; + + this.showMessage(hitMessage, isCritical ? "warning" : "success"); + + if (enemy.hp <= 0) { + this.enemyDefeated(enemy); + } + + this.updateCombatStatus(); + } + + createAttackAnimation(target, type) { + this.player.attackAnimation.active = true; + this.player.attackAnimation.duration = 0.5; + this.player.attackAnimation.type = type; + this.player.attackAnimation.particles = []; + + // Create attack particles based on weapon type + const weapon = this.player.equipment.weapon; + const particleCount = type === "critical" ? 15 : 8; + + for (let i = 0; i < particleCount; i++) { + const angle = (Math.PI * 2 * i) / particleCount; + const speed = 100 + Math.random() * 50; + + this.player.attackAnimation.particles.push({ + x: this.player.x + this.player.width / 2, + y: this.player.y + this.player.height / 2, + vx: Math.cos(angle) * speed, + vy: Math.sin(angle) * speed, + life: 0.3, + maxLife: 0.3, + alpha: 1, + color: + type === "critical" + ? "#ff4444" + : weapon?.icon === "๐Ÿ—ก๏ธ" + ? "#ffd700" + : "#ffffff", + }); + } + } + + createDamageNumber(x, y, damage, isCritical) { + this.damageNumbers.push({ + x: x + Math.random() * 20 - 10, + y: y - 10, + damage: damage, + life: 2.0, + maxLife: 2.0, + alpha: 1, + isCritical: isCritical, + }); + } + + createHitParticles(x, y) { + for (let i = 0; i < 8; i++) { + const angle = Math.random() * Math.PI * 2; + const speed = 50 + Math.random() * 100; + + this.particles.push({ + x: x, + y: y, + vx: Math.cos(angle) * speed, + vy: Math.sin(angle) * speed, + life: 0.5, + maxLife: 0.5, + alpha: 1, + color: "#ff6b6b", + }); + } + } + + useSpecialAttack() { + const weapon = this.player.equipment.weapon; + if (!weapon || !weapon.special) { + this.showMessage("โŒ No special attack available!", "error"); + return; + } + + if (this.player.attackCooldown > 0) return; + + // Find all enemies in range for special attack + const enemiesInRange = this.enemies.filter( + (enemy) => + enemy.isAlive && this.getDistance(this.player, enemy) < 80 + ); + + if (enemiesInRange.length === 0) { + this.showMessage( + "โš”๏ธ No enemies in range for special attack!", + "warning" + ); + return; + } + + const specialDamage = weapon.damage * 1.5 + this.player.stats.strength; + + enemiesInRange.forEach((enemy) => { + enemy.hp -= specialDamage; + if (enemy.hp <= 0) { + this.enemyDefeated(enemy); + } + }); + + this.player.attackCooldown = 3.0; // Longer cooldown for special + this.showMessage( + `โœจ ${weapon.special}! Hit ${enemiesInRange.length} enemies!`, + "success" + ); + } + + usePotion() { + const potion = this.player.inventory.find( + (item) => item.type === "potion" && item.effect === "heal" + ); + + if (!potion) { + this.showMessage("โŒ No healing potions available!", "error"); + return; + } + + if (this.player.hp >= this.player.maxHp) { + this.showMessage("โŒ Already at full health!", "warning"); + return; + } + + // Remove potion from inventory + const index = this.player.inventory.indexOf(potion); + this.player.inventory.splice(index, 1); + + // Heal player + this.player.hp = Math.min( + this.player.maxHp, + this.player.hp + potion.value + ); + + this.showMessage( + `๐Ÿงช Used ${potion.name}! Restored ${potion.value} HP`, + "success" + ); + this.updateUI(); + this.updateInventoryUI(); + } + + enemyAttackPlayer(enemy) { + const damage = Math.max( + 1, + enemy.type.damage - this.player.stats.defense + ); + this.player.hp -= damage; + + this.showMessage( + `๐Ÿ’ฅ ${enemy.type.name} attacks for ${damage} damage!`, + "error" + ); + + if (this.player.hp <= 0) { + this.gameOver(); + } + + this.updateUI(); + } + + enemyDefeated(enemy) { + enemy.isAlive = false; + + // Rewards + this.player.gold += enemy.type.gold; + this.player.score += enemy.type.xp * 10; + this.addExperience(enemy.type.xp); + + // Update quest progress + this.quests.monstersDefeated++; + this.updateQuestUI(); + + this.showMessage( + `๐Ÿ’€ Defeated ${enemy.type.name}! +${enemy.type.xp} XP, +${enemy.type.gold} gold`, + "success" + ); + this.updateUI(); + } + + addExperience(amount) { + this.player.experience += amount; + + while (this.player.experience >= this.player.experienceToNext) { + this.levelUp(); + } + + this.updateUI(); + } + + levelUp() { + this.player.experience -= this.player.experienceToNext; + this.player.level++; + this.player.experienceToNext = Math.floor( + this.player.experienceToNext * 1.2 + ); + + // Increase stats + this.player.maxHp += 20; + this.player.hp = this.player.maxHp; + this.player.stats.strength += 3; + this.player.stats.defense += 2; + this.player.stats.agility += 1; + this.player.stats.magic += 1; + + this.showMessage( + `๐Ÿ†™ Level Up! Now level ${this.player.level}!`, + "success" + ); + this.updateUI(); + } + + handleInteraction() { + // Check for nearby NPCs + this.npcs.forEach((npc) => { + const distance = this.getDistance(this.player, npc); + if (distance < 50) { + this.interactWithNPC(npc); + return; + } + }); + + // Check for nearby portals + if (this.nearPortal) { + this.travelToMap(this.nearPortal.targetMap); + this.nearPortal = null; + } + } + + interactWithNPC(npc) { + if (npc.type.type === "shop") { + this.showShopModal(); + } else if (npc.type.type === "healer") { + this.healPlayer(); + } else { + this.showMessage(npc.type.dialogue, "info"); + } + } + + healPlayer() { + const healCost = 30; + if ( + this.player.gold >= healCost && + this.player.hp < this.player.maxHp + ) { + this.player.gold -= healCost; + this.player.hp = this.player.maxHp; + this.showMessage("โœจ Fully healed! -30 gold", "success"); + this.updateUI(); + } else if (this.player.hp >= this.player.maxHp) { + this.showMessage("โŒ Already at full health!", "warning"); + } else { + this.showMessage("โŒ Not enough gold for healing!", "error"); + } + } + + updateCombatStatus() { + const nearbyEnemies = this.enemies.filter( + (enemy) => + enemy.isAlive && this.getDistance(this.player, enemy) < 80 + ).length; + + const statusElement = document.getElementById("combat-status"); + if (nearbyEnemies > 0) { + statusElement.textContent = `โš”๏ธ ${nearbyEnemies} enemies nearby - Ready to fight!`; + statusElement.style.color = "#ff6b6b"; + } else { + statusElement.textContent = "โœ… Safe area - No enemies nearby"; + statusElement.style.color = "#90ee90"; + } + } + + render() { + // Clear canvas + this.ctx.fillStyle = "#2d5a2d"; + this.ctx.fillRect(0, 0, this.gameWidth, this.gameHeight); + + // Draw map + this.drawMap(); + + // Draw items + this.drawItems(); + + // Draw treasures + this.drawTreasures(); + + // Draw swords + this.drawSwords(); + + // Draw enemies + this.drawEnemies(); + + // Draw NPCs + this.drawNPCs(); + + // Draw player + this.drawPlayer(); + + // Draw UI overlays + this.drawUIOverlays(); + } + + drawMap() { + const tileSize = 32; + const startX = Math.floor(this.camera.x / tileSize); + const startY = Math.floor(this.camera.y / tileSize); + const endX = Math.min( + startX + Math.ceil(this.gameWidth / tileSize) + 1, + this.map.tiles[0].length + ); + const endY = Math.min( + startY + Math.ceil(this.gameHeight / tileSize) + 1, + this.map.tiles.length + ); + + for (let y = startY; y < endY; y++) { + for (let x = startX; x < endX; x++) { + const tile = this.map.tiles[y][x]; + const screenX = x * tileSize - this.camera.x; + const screenY = y * tileSize - this.camera.y; + + switch (tile) { + case "grass": + this.ctx.fillStyle = "#4a7c59"; + break; + case "stone": + this.ctx.fillStyle = "#8b7355"; + break; + case "tree": + this.ctx.fillStyle = "#2d4016"; + break; + case "dark_tree": + this.ctx.fillStyle = "#1a2010"; + break; + case "water": + this.ctx.fillStyle = "#4682b4"; + break; + case "ruins_wall": + this.ctx.fillStyle = "#6b5b73"; + break; + case "ruins_floor": + this.ctx.fillStyle = "#9b8b93"; + break; + case "cave_wall": + this.ctx.fillStyle = "#3b3b3b"; + break; + case "cave_floor": + this.ctx.fillStyle = "#5b5b5b"; + break; + case "lava": + this.ctx.fillStyle = "#ff4500"; + break; + default: + this.ctx.fillStyle = "#4a7c59"; + } + + this.ctx.fillRect(screenX, screenY, tileSize, tileSize); + } + } + + // Draw portals + this.drawPortals(); + } + + drawSwords() { + this.swords.forEach((sword) => { + if (sword.collected) return; + + const screenX = sword.x - this.camera.x; + const screenY = sword.y - this.camera.y; + + // Draw glowing effect + if (sword.glowing) { + this.ctx.shadowColor = "#ffd700"; + this.ctx.shadowBlur = 15; + } + + this.ctx.font = "24px Arial"; + this.ctx.fillText(sword.type.icon, screenX, screenY + 20); + + this.ctx.shadowBlur = 0; + + // Draw sword name + this.ctx.fillStyle = "#ffd700"; + this.ctx.font = "10px Arial"; + this.ctx.textAlign = "center"; + this.ctx.fillText(sword.type.name, screenX + 12, screenY - 5); + this.ctx.textAlign = "left"; + }); + } + + drawTreasures() { + this.treasures.forEach((treasure) => { + if (treasure.collected) return; + + const screenX = treasure.x - this.camera.x; + const screenY = treasure.y - this.camera.y; + + this.ctx.font = "20px Arial"; + this.ctx.fillText(treasure.type.icon, screenX, screenY + 16); + }); + } + + drawItems() { + this.items.forEach((item) => { + if (item.collected) return; + + const screenX = item.x - this.camera.x; + const screenY = item.y - this.camera.y; + + this.ctx.font = "18px Arial"; + this.ctx.fillText(item.type.icon, screenX, screenY + 16); + }); + } + + drawEnemies() { + this.enemies.forEach((enemy) => { + if (!enemy.isAlive) return; + + const screenX = enemy.x - this.camera.x; + const screenY = enemy.y - this.camera.y; + + // Draw enemy + this.ctx.font = "30px Arial"; + this.ctx.fillText(enemy.type.icon, screenX, screenY + 24); + + // Draw health bar + const barWidth = 32; + const barHeight = 4; + const healthPercent = enemy.hp / enemy.maxHp; + + this.ctx.fillStyle = "#ff0000"; + this.ctx.fillRect(screenX, screenY - 8, barWidth, barHeight); + this.ctx.fillStyle = "#00ff00"; + this.ctx.fillRect( + screenX, + screenY - 8, + barWidth * healthPercent, + barHeight + ); + + // Draw enemy name + this.ctx.fillStyle = "#ff6b6b"; + this.ctx.font = "10px Arial"; + this.ctx.textAlign = "center"; + this.ctx.fillText(enemy.type.name, screenX + 16, screenY - 12); + this.ctx.textAlign = "left"; + }); + } + + drawNPCs() { + this.npcs.forEach((npc) => { + const screenX = npc.x - this.camera.x; + const screenY = npc.y - this.camera.y; + + this.ctx.font = "30px Arial"; + this.ctx.fillText(npc.type.icon, screenX, screenY + 24); + + // Draw NPC name + this.ctx.fillStyle = "#90ee90"; + this.ctx.font = "10px Arial"; + this.ctx.textAlign = "center"; + this.ctx.fillText(npc.type.name, screenX + 16, screenY - 5); + this.ctx.textAlign = "left"; + }); + } + + drawPlayer() { + const screenX = this.player.x - this.camera.x; + const screenY = this.player.y - this.camera.y; + + // Draw attack effect if attacking + if (this.player.attackCooldown > 0.5) { + this.ctx.strokeStyle = "#ffd700"; + this.ctx.lineWidth = 3; + this.ctx.beginPath(); + this.ctx.arc(screenX + 16, screenY + 16, 40, 0, Math.PI * 2); + this.ctx.stroke(); + } + + // Draw player + this.ctx.font = "30px Arial"; + this.ctx.fillText("๐Ÿคด", screenX, screenY + 24); + + // Draw equipped weapon indicator + if (this.player.equipment.weapon) { + this.ctx.font = "16px Arial"; + this.ctx.fillText( + this.player.equipment.weapon.icon, + screenX + 25, + screenY + 10 + ); + } + + // Draw player name and level + this.ctx.fillStyle = "#ffd700"; + this.ctx.font = "12px Arial"; + this.ctx.textAlign = "center"; + this.ctx.fillText( + `Level ${this.player.level} Hero`, + screenX + 16, + screenY - 5 + ); + this.ctx.textAlign = "left"; + + // Draw attack animations + this.drawAttackAnimations(); + + // Draw damage numbers + this.drawDamageNumbers(); + } + + drawPortals() { + this.portals.forEach((portal) => { + const screenX = portal.x - this.camera.x; + const screenY = portal.y - this.camera.y; + + // Draw swirling animation + const time = Date.now() * 0.005; + this.ctx.save(); + this.ctx.translate( + screenX + portal.width / 2, + screenY + portal.height / 2 + ); + this.ctx.rotate(time); + + // Draw portal effect + this.ctx.shadowColor = "#00ffff"; + this.ctx.shadowBlur = 15; + this.ctx.font = "40px Arial"; + this.ctx.fillText(portal.icon, -20, 15); + + this.ctx.restore(); + this.ctx.shadowBlur = 0; + + // Draw portal name + this.ctx.fillStyle = "#00ffff"; + this.ctx.font = "10px Arial"; + this.ctx.textAlign = "center"; + this.ctx.fillText( + portal.name, + screenX + portal.width / 2, + screenY - 5 + ); + this.ctx.textAlign = "left"; + }); + } + + drawAttackAnimations() { + // Draw player attack animation + if (this.player.attackAnimation.active) { + this.player.attackAnimation.particles.forEach((particle) => { + this.ctx.save(); + this.ctx.globalAlpha = particle.alpha; + this.ctx.fillStyle = particle.color; + this.ctx.beginPath(); + this.ctx.arc( + particle.x - this.camera.x, + particle.y - this.camera.y, + 3, + 0, + Math.PI * 2 + ); + this.ctx.fill(); + this.ctx.restore(); + }); + } + + // Draw general particles + this.particles.forEach((particle) => { + this.ctx.save(); + this.ctx.globalAlpha = particle.alpha; + this.ctx.fillStyle = particle.color; + this.ctx.beginPath(); + this.ctx.arc( + particle.x - this.camera.x, + particle.y - this.camera.y, + 2, + 0, + Math.PI * 2 + ); + this.ctx.fill(); + this.ctx.restore(); + }); + } + + drawDamageNumbers() { + this.damageNumbers.forEach((dmg) => { + this.ctx.save(); + this.ctx.globalAlpha = dmg.alpha; + this.ctx.fillStyle = dmg.isCritical ? "#ff4444" : "#ffffff"; + this.ctx.font = dmg.isCritical ? "bold 16px Arial" : "14px Arial"; + this.ctx.strokeStyle = "#000000"; + this.ctx.lineWidth = 2; + this.ctx.textAlign = "center"; + + const screenX = dmg.x - this.camera.x; + const screenY = dmg.y - this.camera.y; + + this.ctx.strokeText(dmg.damage.toString(), screenX, screenY); + this.ctx.fillText(dmg.damage.toString(), screenX, screenY); + this.ctx.restore(); + }); + } + + drawUIOverlays() { + // Draw minimap + this.drawMinimap(); + + // Draw attack range indicator when near enemies + const nearbyEnemies = this.enemies.filter( + (enemy) => + enemy.isAlive && this.getDistance(this.player, enemy) < 80 + ); + + if (nearbyEnemies.length > 0) { + const screenX = this.player.x - this.camera.x; + const screenY = this.player.y - this.camera.y; + + this.ctx.strokeStyle = "rgba(255, 215, 0, 0.3)"; + this.ctx.lineWidth = 2; + this.ctx.setLineDash([5, 5]); + this.ctx.beginPath(); + this.ctx.arc(screenX + 16, screenY + 16, 60, 0, Math.PI * 2); + this.ctx.stroke(); + this.ctx.setLineDash([]); + } + } + + drawMinimap() { + const minimapSize = 120; + const minimapX = this.gameWidth - minimapSize - 10; + const minimapY = 10; + const scale = minimapSize / Math.max(this.map.width, this.map.height); + + // Minimap background + this.ctx.fillStyle = "rgba(0, 0, 0, 0.7)"; + this.ctx.fillRect(minimapX, minimapY, minimapSize, minimapSize); + this.ctx.strokeStyle = "#d4af37"; + this.ctx.lineWidth = 2; + this.ctx.strokeRect(minimapX, minimapY, minimapSize, minimapSize); + + // Draw swords on minimap + this.swords.forEach((sword) => { + if (sword.collected) return; + const x = minimapX + sword.x * scale; + const y = minimapY + sword.y * scale; + this.ctx.fillStyle = "#ffd700"; + this.ctx.fillRect(x - 2, y - 2, 4, 4); + }); + + // Draw treasures on minimap + this.treasures.forEach((treasure) => { + if (treasure.collected) return; + const x = minimapX + treasure.x * scale; + const y = minimapY + treasure.y * scale; + this.ctx.fillStyle = "#ff6600"; + this.ctx.fillRect(x - 1, y - 1, 2, 2); + }); + + // Draw enemies on minimap + this.enemies.forEach((enemy) => { + if (!enemy.isAlive) return; + const x = minimapX + enemy.x * scale; + const y = minimapY + enemy.y * scale; + this.ctx.fillStyle = "#ff0000"; + this.ctx.fillRect(x - 1, y - 1, 2, 2); + }); + + // Draw player on minimap + const playerX = minimapX + this.player.x * scale; + const playerY = minimapY + this.player.y * scale; + this.ctx.fillStyle = "#00ff00"; + this.ctx.fillRect(playerX - 2, playerY - 2, 4, 4); + } + + // UI Management + updateUI() { + document.getElementById("player-level").textContent = this.player.level; + document.getElementById("player-hp").textContent = this.player.hp; + document.getElementById("player-max-hp").textContent = + this.player.maxHp; + document.getElementById("player-strength").textContent = + this.player.stats.strength; + document.getElementById("player-defense").textContent = + this.player.stats.defense; + document.getElementById("player-score").textContent = this.player.score; + document.getElementById("player-gold").textContent = this.player.gold; + + // Update experience bar + const expPercent = + (this.player.experience / this.player.experienceToNext) * 100; + document.getElementById("exp-bar").style.width = expPercent + "%"; + document.getElementById( + "exp-text" + ).textContent = `${this.player.experience}/${this.player.experienceToNext} XP`; + } + + updateQuestUI() { + document.getElementById("sword-progress").textContent = `Progress: ${ + this.quests.swordFound ? 1 : 0 + }/1`; + document.getElementById( + "treasure-progress" + ).textContent = `Progress: ${this.quests.treasuresCollected}/10`; + document.getElementById( + "monster-progress" + ).textContent = `Progress: ${this.quests.monstersDefeated}/5`; + } + + updateInventoryUI() { + const inventoryGrid = document.getElementById("inventory-grid"); + const inventoryCount = document.getElementById("inventory-count"); + + inventoryGrid.innerHTML = ""; + inventoryCount.textContent = this.player.inventory.length; + + // Create inventory slots + for (let i = 0; i < this.player.maxInventory; i++) { + const slot = document.createElement("div"); + slot.className = "inventory-slot"; + + if (i < this.player.inventory.length) { + const item = this.player.inventory[i]; + slot.className += " occupied"; + slot.innerHTML = `${item.icon}`; + slot.title = `${item.name} (${item.type})`; + } + + inventoryGrid.appendChild(slot); + } + } + + showMessage(text, type = "info") { + const messageLog = document.getElementById("message-log"); + const message = document.createElement("div"); + message.className = `message ${type}`; + message.textContent = text; + + messageLog.appendChild(message); + + // Auto-remove after 5 seconds + setTimeout(() => { + if (message.parentNode) { + message.parentNode.removeChild(message); + } + }, 5000); + } + + showShopModal() { + const shopItems = [ + { + name: "Iron Sword", + icon: "โš”๏ธ", + price: 150, + type: "weapon", + damage: 25, + }, + { + name: "Steel Armor", + icon: "๐Ÿ›ก๏ธ", + price: 200, + type: "armor", + defense: 15, + }, + { + name: "Health Potion", + icon: "๐Ÿงช", + price: 30, + type: "potion", + effect: "heal", + value: 50, + }, + { + name: "Mana Potion", + icon: "๐Ÿ’™", + price: 40, + type: "potion", + effect: "mana", + value: 30, + }, + ]; + + const shopItemsContainer = document.getElementById("shop-items"); + shopItemsContainer.innerHTML = ""; + + shopItems.forEach((item) => { + const itemDiv = document.createElement("div"); + itemDiv.className = "shop-item"; + itemDiv.innerHTML = ` +
${item.icon} ${item.name}
+
+ ${ + item.type === "weapon" + ? `Damage: ${item.damage}` + : item.type === "armor" + ? `Defense: ${item.defense}` + : item.type === "potion" + ? `Restores ${item.value} ${item.effect}` + : "" + } +
+
๐Ÿ’ฐ ${item.price} gold
+ + `; + shopItemsContainer.appendChild(itemDiv); + }); + + document.getElementById("shop-modal").classList.remove("hidden"); + } + + showMapSelection() { + const mapModal = document.getElementById("map-modal"); + const mapSelection = document.getElementById("map-selection"); + + // Clear previous content + mapSelection.innerHTML = ""; + + // Create map options + this.maps.forEach((map, index) => { + const mapOption = document.createElement("div"); + mapOption.className = "map-option"; + + // Add current/locked classes + if (index === this.currentMap) { + mapOption.classList.add("current"); + } + + // For now, all maps are unlocked. You can add unlock conditions later + const isUnlocked = true; + if (!isUnlocked) { + mapOption.classList.add("locked"); + } + + // Create difficulty stars + const stars = + "โ˜…".repeat(map.difficulty || 1) + + "โ˜†".repeat(5 - (map.difficulty || 1)); + + mapOption.innerHTML = ` +
${map.emoji || "๐Ÿ—บ๏ธ"} ${map.name}
+
${map.description}
+
+ ${Array.from(stars) + .map( + (star) => + `${star}` + ) + .join("")} +
+
${ + map.rewards || "Explore to discover rewards!" + }
+ `; + + // Add click handler if not current map and unlocked + if (index !== this.currentMap && isUnlocked) { + mapOption.addEventListener("click", () => { + this.travelToMap(index); + this.closeMapModal(); + }); + } + + mapSelection.appendChild(mapOption); + }); + + mapModal.classList.remove("hidden"); + } + + closeMapModal() { + document.getElementById("map-modal").classList.add("hidden"); + } + + gameOver() { + this.gameRunning = false; + this.showMessage("๐Ÿ’€ Game Over! You have been defeated.", "error"); + + setTimeout(() => { + if (confirm("Game Over! Would you like to restart?")) { + location.reload(); + } + }, 2000); + } +} + +// Global functions for modal interactions +function closeShopModal() { + document.getElementById("shop-modal").classList.add("hidden"); +} + +function closeMapModal() { + document.getElementById("map-modal").classList.add("hidden"); +} + +function buyItem(itemName, price, itemDataJson) { + const itemData = JSON.parse(itemDataJson.replace(/"/g, '"')); + + if (game.player.gold >= price) { + game.player.gold -= price; + + const item = { + name: itemData.name, + icon: itemData.icon, + type: itemData.type, + damage: itemData.damage, + defense: itemData.defense, + effect: itemData.effect, + value: itemData.value, + }; + + if (game.addToInventory(item)) { + game.showMessage(`โœ… Purchased ${itemName}!`, "success"); + game.updateUI(); + game.updateInventoryUI(); + } + } else { + game.showMessage("โŒ Not enough gold!", "error"); + } +} + +// Initialize game when page loads +let game; +window.addEventListener("load", () => { + game = new MedievalRPG(); + + // Setup modal event listeners + setupModalEventListeners(); +}); + +// Setup modal event listeners +function setupModalEventListeners() { + // Close modals when clicking outside + document.getElementById("shop-modal").addEventListener("click", (e) => { + if (e.target.id === "shop-modal") { + closeShopModal(); + } + }); + + document.getElementById("map-modal").addEventListener("click", (e) => { + if (e.target.id === "map-modal") { + closeMapModal(); + } + }); + + // Close modals with Escape key + document.addEventListener("keydown", (e) => { + if (e.key === "Escape") { + const shopModal = document.getElementById("shop-modal"); + const mapModal = document.getElementById("map-modal"); + + if (!shopModal.classList.contains("hidden")) { + closeShopModal(); + } + if (!mapModal.classList.contains("hidden")) { + closeMapModal(); + } + } + }); +} + +// Handle window resize +window.addEventListener("resize", () => { + if (game) { + // Handle canvas resize if needed + } +}); diff --git a/Games/Medieval_RPG/index.html b/Games/Medieval_RPG/index.html new file mode 100644 index 000000000..d9f913269 --- /dev/null +++ b/Games/Medieval_RPG/index.html @@ -0,0 +1,166 @@ + + + + + + Medieval RPG - Kingdom Quest + + + +
+ +
+
+

๐Ÿฐ Kingdom Quest

+
+
+ Level: + 1 +
+
+ HP: + 100/100 +
+
+ Strength: + 10 +
+
+ Defense: + 5 +
+
+ Score: + 0 +
+
+ Gold: + 50 +
+
+
+
+ 0/100 XP +
+
+ +
+

+ ๐ŸŽ’ Inventory (0/20) +

+
+ +
+
+ +
+

๐Ÿ“œ Active Quests

+
+
+
+ ๐Ÿ—ก๏ธ Find the Lost Sword +
+
+ Search the ancient ruins for the legendary blade +
+
+ Progress: 0/1 +
+
+
+
๐Ÿ’ฐ Treasure Hunter
+
+ Collect 10 treasures +
+
+ Progress: 0/10 +
+
+
+
โš”๏ธ Monster Slayer
+
+ Defeat 5 monsters +
+
+ Progress: 0/5 +
+
+
+
+ +
+

โš”๏ธ Combat Status

+
Ready for battle!
+
+ Controls:
+ TAB - Attack nearby enemy
+ F - Use equipped weapon special
+ Q - Quick heal (if potions available) +
+
+
+ + +
+ +
+
+ WASD/Arrows: Move โ€ข TAB: Attack โ€ข F: Special Attack โ€ข Q: + Heal โ€ข SPACE: Interact โ€ข M: Select Map +
+
+
+ Current Map: + Village Outskirts +
+
+ Available: Village โ€ข Forest โ€ข Ruins โ€ข Dragon's Lair +
+
+
+
+ + +
+ + + + + + +
+ + + + diff --git a/Games/Medieval_RPG/styles.css b/Games/Medieval_RPG/styles.css new file mode 100644 index 000000000..3ace6068b --- /dev/null +++ b/Games/Medieval_RPG/styles.css @@ -0,0 +1,743 @@ +/* Medieval RPG Styles */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; + background: linear-gradient(135deg, #2c1810, #8b4513); + color: #f4e4bc; + overflow: hidden; +} + +.game-container { + display: flex; + height: 100vh; + background: url('data:image/svg+xml,') + repeat; +} + +/* Game UI Panel */ +.game-ui { + width: 300px; + background: rgba(0, 0, 0, 0.9); + border-right: 3px solid #d4af37; + padding: 20px; + overflow-y: auto; + box-shadow: 5px 0 15px rgba(0, 0, 0, 0.5); +} + +.game-ui h3 { + text-align: center; + color: #d4af37; + margin-bottom: 20px; + font-size: 1.4em; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7); +} + +.game-ui h4 { + color: #f4e4bc; + margin: 20px 0 10px 0; + border-bottom: 2px solid #654321; + padding-bottom: 5px; +} + +/* Stats Panel */ +.stats-panel { + margin-bottom: 25px; + background: rgba(100, 67, 33, 0.3); + padding: 15px; + border-radius: 8px; + border: 1px solid #8b4513; +} + +.player-stats .stat { + display: flex; + justify-content: space-between; + margin: 8px 0; + padding: 5px; + background: rgba(0, 0, 0, 0.3); + border-radius: 4px; +} + +.stat-label { + font-weight: bold; + color: #d4af37; +} + +.experience-bar { + position: relative; + width: 100%; + height: 20px; + background: #333; + border-radius: 10px; + overflow: hidden; + margin-top: 10px; + border: 1px solid #8b4513; +} + +.exp-fill { + height: 100%; + background: linear-gradient(90deg, #4caf50, #8bc34a); + width: 0%; + transition: width 0.5s ease; +} + +.exp-text { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 12px; + font-weight: bold; + color: white; + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.7); +} + +/* Inventory Panel */ +.inventory-panel { + margin-bottom: 25px; + background: rgba(100, 67, 33, 0.3); + padding: 15px; + border-radius: 8px; + border: 1px solid #8b4513; +} + +.inventory-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 5px; + margin-top: 10px; +} + +.inventory-slot { + width: 50px; + height: 50px; + border: 2px solid #654321; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + border-radius: 4px; + transition: all 0.3s ease; +} + +.inventory-slot:hover { + border-color: #d4af37; + background: rgba(212, 175, 55, 0.2); +} + +.inventory-slot.occupied { + background: rgba(212, 175, 55, 0.3); + border-color: #d4af37; +} + +.item-icon { + font-size: 24px; +} + +/* Quest Panel */ +.quest-panel { + background: rgba(100, 67, 33, 0.3); + padding: 15px; + border-radius: 8px; + border: 1px solid #8b4513; +} + +.quest-item { + background: rgba(0, 0, 0, 0.4); + padding: 10px; + margin: 8px 0; + border-radius: 6px; + border-left: 4px solid #654321; + transition: all 0.3s ease; +} + +.quest-item.active { + border-left-color: #d4af37; + background: rgba(212, 175, 55, 0.1); +} + +.quest-title { + font-weight: bold; + color: #d4af37; + margin-bottom: 5px; +} + +.quest-description { + font-size: 0.9em; + color: #cccccc; + margin-bottom: 5px; +} + +.quest-progress { + font-size: 0.8em; + color: #90ee90; +} + +/* Combat Info Panel */ +.combat-info { + background: rgba(100, 67, 33, 0.3); + padding: 15px; + border-radius: 8px; + border: 1px solid #8b4513; + margin-top: 20px; +} + +#combat-status { + padding: 8px; + background: rgba(0, 0, 0, 0.3); + border-radius: 4px; + margin: 10px 0; + color: #90ee90; + font-weight: bold; +} + +.combat-help { + font-size: 0.85em; + color: #cccccc; + margin-top: 10px; + padding: 8px; + background: rgba(0, 0, 0, 0.2); + border-radius: 4px; + border-left: 3px solid #d4af37; +} + +/* Health Bar for Player */ +.player-health-bar { + position: relative; + width: 100%; + height: 15px; + background: #333; + border-radius: 8px; + overflow: hidden; + margin: 5px 0; + border: 1px solid #654321; +} + +.player-hp-fill { + height: 100%; + background: linear-gradient(90deg, #dc3545, #ff6b6b); + transition: width 0.3s ease; +} + +/* Equipment Slots */ +.equipment-panel { + background: rgba(100, 67, 33, 0.3); + padding: 15px; + border-radius: 8px; + border: 1px solid #8b4513; + margin-bottom: 20px; +} + +.equipment-slots { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 10px; + margin-top: 10px; +} + +.equipment-slot { + width: 60px; + height: 60px; + border: 2px solid #654321; + background: rgba(0, 0, 0, 0.5); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + border-radius: 8px; + transition: all 0.3s ease; +} + +.equipment-slot:hover { + border-color: #d4af37; + background: rgba(212, 175, 55, 0.2); +} + +.equipment-slot.equipped { + background: rgba(212, 175, 55, 0.3); + border-color: #d4af37; + box-shadow: 0 0 10px rgba(212, 175, 55, 0.5); +} + +.equipment-label { + font-size: 9px; + color: #cccccc; + margin-top: 2px; +} + +/* Game World */ +.game-world { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; +} + +#gameCanvas { + border: 3px solid #d4af37; + background: #2d5a2d; + box-shadow: 0 0 20px rgba(212, 175, 55, 0.3); + border-radius: 8px; +} + +.game-controls { + margin-top: 15px; + padding: 10px 20px; + background: rgba(0, 0, 0, 0.8); + border-radius: 25px; + border: 2px solid #654321; +} + +.control-hint { + font-size: 0.9em; + color: #f4e4bc; + text-align: center; + margin-bottom: 10px; +} + +.map-info { + text-align: center; + font-size: 0.85em; + color: #d4af37; +} + +.current-map { + font-weight: bold; + margin-bottom: 5px; +} + +.available-maps { + color: #a0a0a0; + font-style: italic; +} + +/* Message Log */ +#message-log { + position: fixed; + top: 20px; + right: 20px; + width: 300px; + max-height: 200px; + overflow-y: auto; + z-index: 1000; +} + +.message { + background: rgba(0, 0, 0, 0.9); + padding: 10px 15px; + margin: 5px 0; + border-radius: 6px; + border-left: 4px solid #d4af37; + animation: slideIn 0.5s ease; +} + +.message.success { + border-left-color: #4caf50; + background: rgba(76, 175, 80, 0.2); +} + +.message.warning { + border-left-color: #ff9800; + background: rgba(255, 152, 0, 0.2); +} + +.message.error { + border-left-color: #f44336; + background: rgba(244, 67, 54, 0.2); +} + +@keyframes slideIn { + from { + transform: translateX(100%); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +/* Modal Styles */ +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.8); + display: flex; + align-items: center; + justify-content: center; + z-index: 2000; +} + +.modal.hidden { + display: none; +} + +.modal-content { + background: linear-gradient(135deg, #2c1810, #3d2817); + border: 3px solid #d4af37; + border-radius: 12px; + padding: 0; + min-width: 400px; + max-width: 600px; + max-height: 80vh; + overflow-y: auto; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.7); +} + +.modal-header { + background: rgba(212, 175, 55, 0.2); + padding: 15px 20px; + border-bottom: 2px solid #654321; + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal-header h3 { + color: #d4af37; + margin: 0; +} + +.close-btn { + background: none; + border: none; + color: #d4af37; + font-size: 24px; + cursor: pointer; + padding: 0; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + transition: all 0.3s ease; +} + +.close-btn:hover { + background: rgba(212, 175, 55, 0.3); +} + +.modal-body { + padding: 20px; +} + +/* Buttons */ +.btn { + background: linear-gradient(135deg, #654321, #8b4513); + color: #f4e4bc; + border: 2px solid #d4af37; + padding: 10px 20px; + border-radius: 6px; + cursor: pointer; + font-weight: bold; + transition: all 0.3s ease; + margin: 5px; +} + +.btn:hover { + background: linear-gradient(135deg, #8b4513, #a0522d); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + transform: translateY(-2px); +} + +.btn-primary { + background: linear-gradient(135deg, #d4af37, #ffcc00); + color: #2c1810; +} + +.btn-attack { + background: linear-gradient(135deg, #dc3545, #c82333); +} + +.btn-defend { + background: linear-gradient(135deg, #007bff, #0056b3); +} + +.btn-flee { + background: linear-gradient(135deg, #6c757d, #545b62); +} + +/* Shop Styles */ +.shop-items { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 15px; +} + +.shop-item { + background: rgba(100, 67, 33, 0.3); + border: 2px solid #654321; + border-radius: 8px; + padding: 15px; + transition: all 0.3s ease; +} + +.shop-item:hover { + border-color: #d4af37; + background: rgba(212, 175, 55, 0.2); +} + +.shop-item-name { + font-weight: bold; + color: #d4af37; + margin-bottom: 5px; +} + +.shop-item-description { + font-size: 0.9em; + color: #cccccc; + margin-bottom: 10px; +} + +.shop-item-price { + color: #ffcc00; + font-weight: bold; +} + +/* Combat Styles */ +.combat-area { + text-align: center; +} + +.enemy-info { + margin-bottom: 20px; +} + +.enemy-hp-bar { + position: relative; + width: 100%; + height: 25px; + background: #333; + border-radius: 12px; + overflow: hidden; + margin-top: 10px; + border: 2px solid #654321; +} + +.hp-fill { + height: 100%; + background: linear-gradient(90deg, #dc3545, #ff6b6b); + transition: width 0.5s ease; +} + +#enemy-hp-text { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-weight: bold; + color: white; + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.7); +} + +.combat-actions { + margin: 20px 0; +} + +#combat-log { + background: rgba(0, 0, 0, 0.5); + border-radius: 8px; + padding: 15px; + max-height: 150px; + overflow-y: auto; + text-align: left; + font-family: monospace; + border: 1px solid #654321; +} + +.combat-message { + margin: 5px 0; + padding: 2px 0; +} + +.player-action { + color: #4caf50; +} + +.enemy-action { + color: #f44336; +} + +.system-message { + color: #ffcc00; +} + +/* Responsive Design */ +@media (max-width: 1200px) { + .game-ui { + width: 250px; + } + + #gameCanvas { + width: 600px; + height: 450px; + } +} + +@media (max-width: 900px) { + .game-container { + flex-direction: column; + } + + .game-ui { + width: 100%; + height: 200px; + order: 2; + } + + .game-world { + order: 1; + } + + #gameCanvas { + width: 100%; + max-width: 800px; + height: 400px; + } +} + +/* Animations */ +@keyframes bounce { + 0%, + 20%, + 50%, + 80%, + 100% { + transform: translateY(0); + } + 40% { + transform: translateY(-10px); + } + 60% { + transform: translateY(-5px); + } +} + +.bounce { + animation: bounce 1s; +} + +/* Map Selection Modal */ +.map-selection { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 15px; + padding: 10px; +} + +.map-option { + background: linear-gradient(135deg, #654321, #8b4513); + border: 2px solid #d4af37; + border-radius: 10px; + padding: 15px; + cursor: pointer; + transition: all 0.3s ease; + text-align: center; + position: relative; + overflow: hidden; +} + +.map-option:hover { + transform: translateY(-3px); + box-shadow: 0 5px 15px rgba(212, 175, 55, 0.3); + border-color: #ffd700; +} + +.map-option.current { + background: linear-gradient(135deg, #2d4a22, #4a7c33); + border-color: #90ee90; +} + +.map-option.current::before { + content: "๐Ÿ“ CURRENT LOCATION"; + position: absolute; + top: 5px; + right: 5px; + font-size: 0.7em; + color: #90ee90; + font-weight: bold; +} + +.map-option.locked { + background: linear-gradient(135deg, #444, #666); + border-color: #888; + cursor: not-allowed; + opacity: 0.6; +} + +.map-option.locked::after { + content: "๐Ÿ”’ LOCKED"; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 0.8em; + color: #aaa; + font-weight: bold; + background: rgba(0, 0, 0, 0.8); + padding: 5px 10px; + border-radius: 5px; +} + +.map-title { + font-size: 1.1em; + font-weight: bold; + color: #d4af37; + margin-bottom: 8px; +} + +.map-description { + font-size: 0.9em; + color: #f4e4bc; + margin-bottom: 10px; + line-height: 1.3; +} + +.map-difficulty { + display: flex; + justify-content: center; + gap: 2px; + margin-bottom: 8px; +} + +.difficulty-star { + color: #ffd700; + font-size: 0.9em; +} + +.difficulty-star.empty { + color: #666; +} + +.map-rewards { + font-size: 0.8em; + color: #90ee90; + font-style: italic; +} + +@keyframes glow { + 0% { + box-shadow: 0 0 5px rgba(212, 175, 55, 0.5); + } + 50% { + box-shadow: 0 0 20px rgba(212, 175, 55, 0.8); + } + 100% { + box-shadow: 0 0 5px rgba(212, 175, 55, 0.5); + } +} + +.glow { + animation: glow 2s infinite; +} diff --git a/README.md b/README.md index 7c75d9bc0..10a0ae5f4 100644 --- a/README.md +++ b/README.md @@ -1428,6 +1428,7 @@ This repository also provides one such platforms where contributers come over an | [MrFakeGame](https://github.com/kunjgit/GameZone/tree/main/Games/MrFakeGame) | [Checkers](https://github.com/kunjgit/GameZone/tree/main/Games/Checkers) | [Roulette](https://github.com/kunjgit/GameZone/tree/main/Games/Roulette) | [Aero Acrobat](https://github.com/kunjgit/GameZone/tree/main/Games/Aero_Acrobat) | [Adventure_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Adventure_Game) | | [Pumpkin_Pursuit](https://github.com/kunjgit/GameZone/tree/main/Games/Pumpkin_Pursuit) | [Corona Shooter](https://github.com/kunjgit/GameZone/tree/main/Games/Corona_Shooter) | [Pokemon Ball Finder](https://github.com/kunjgit/GameZone/tree/main/Games/Pokemon_Ball_Finder) | [Basketball](https://github.com/kunjgit/GameZone/tree/main/Games/Basketball) | [Wault_master](https://github.com/kunjgit/GameZone/tree/main/Games/Wault_master) | | [Reaction TIme](https://github.com/kunjgit/GameZone/tree/main/Games/Reaction_Time) | [Flag Guess Game](https://github.com/kunjgit/GameZone/tree/main/Games/Flag_Guess_Game) | [Cross_The_Road](https://github.com/kunjgit/GameZone/tree/main/Games/Cross_The_Road) | [Highway Race - Barrel Dodge](https://github.com/kunjgit/GameZone/tree/main/Games/Highway_Race) | [Bit_Maze_Platformer_Maze](https://github.com/kunjgit/GameZone/tree/main/Games/Bit_Maze_Platformer_Maze) | +| [Medieval RPG - Kingdom Quest](https://github.com/kunjgit/GameZone/tree/main/Games/Medieval_RPG) | | [Math Game](https://github.com/kunjgit/GameZone/tree/main/Games/Math_Game) | [Space Drifter](https://github.com/kunjgit/GameZone/tree/main/Games/space_drifter) | [Observe the Cloud](https://github.com/kunjgit/GameZone/tree/main/Games/Observe%20the%20Cloud) | [Cosmic_Coin_Blaster](https://github.com/kunjgit/GameZone/tree/main/Games/Cosmic_Coin_Blaster) | [Circus Charly](https://github.com/kunjgit/GameZone/tree/main/Games/Circus_Charly) | | [Pikachu_Volleyball](https://github.com/kunjgit/GameZone/tree/main/Games/Pikachu_Volleyball) | [Trex_Run](https://github.com/akankshachanana1/GameZone/tree/Added/Games/Trex_Run) | [Crack_The_Code](https://github.com/kunjgit/GameZone/tree/main/Games/Crack_The_Code) | [Skeleathon](https://github.com/kunjgit/GameZone/tree/main/Games/Skeleathon) | [Shadow_PokeGuess](https://github.com/kunjgit/GameZone/tree/main/Games/Shadow_PokeGuess) | | [Brain Color Mastermind](https://github.com/kunjgit/GameZone/tree/main/Games/Brain_Color_Mastermind) | [Lizard Spock Game](https://github.com/kunjgit/GameZone/tree/main/Games/Lizard_Spock_Game) | [Angry Boars](https://github.com/kunjgit/GameZone/tree/main/Games/Angry_Boars) | [Alphabet Learning Game](https://github.com/kunjgit/GameZone/tree/main/Games/Alphabet_Learning_Game) | [Country_Guesser_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Country_Guesser_Game) | diff --git a/assets/images/Medieval_RPG.png b/assets/images/Medieval_RPG.png new file mode 100644 index 000000000..fb98773e4 Binary files /dev/null and b/assets/images/Medieval_RPG.png differ diff --git a/assets/images/Medieval_RPG_MAP2.png b/assets/images/Medieval_RPG_MAP2.png new file mode 100644 index 000000000..7340449e2 Binary files /dev/null and b/assets/images/Medieval_RPG_MAP2.png differ diff --git a/assets/js/gamesData.json b/assets/js/gamesData.json index 07dfe9fdd..40e70b90b 100644 --- a/assets/js/gamesData.json +++ b/assets/js/gamesData.json @@ -3250,9 +3250,14 @@ "gameUrl": "Random_Choice_Picker", "thumbnailUrl": "Drop_Dash_Game.png" }, - "648":{ + "650":{ "gameTitle" : "Drummer Kit", "gameUrl": "Drummer_Kit", "thumbnailUrl": "Drummer_Kit.png" + }, + "651":{ + "gameTitle" : " - Kingdom Quest", + "gameUrl": "Medieval_RPG", + "thumbnailUrl": "Medieval_RPG.png" } }