JavaScript must be enabled to play.
Browser lacks capabilities required to play.
Upgrade or switch to another browser.
Loading…
<<nobr>> <<widget "loadItemsAmulets">> <<script>> window.setup = window.setup || {}; window.setup.items = window.setup.items || {}; window.setup.addItems({ amulet_of_defense: { id: "amulet_of_defense", name: "Amulet of Defense", type: "amulet", slot: "amulet", defense: 4, constitution: 2, levelReq: 2, sellValue: 25, buyValue: 75, icon: "🛡️📿", description: "DEF +4, CON +2" }, amulet_of_wisdom: { id: "amulet_of_wisdom", name: "Amulet of Wisdom", type: "amulet", slot: "amulet", intellect: 5, maxMP: 15, levelReq: 3, sellValue: 55, buyValue: 165, icon: "🧠📿", description: "INT +5, Max MP +15" }, amulet_of_the_magi: { id: "amulet_of_the_magi", name: "Amulet of the Magi", type: "amulet", slot: "amulet", intellect: 6, maxMP: 20, constitution: 2, levelReq: 4, sellValue: 90, buyValue: 270, icon: "🔮📿", description: "INT +6, Max MP +20, CON +2" }, amulet_of_hardiness: { id: "amulet_of_hardiness", name: "Amulet of Hardiness", type: "amulet", slot: "amulet", constitution: 5, strength: 2, levelReq: 3, sellValue: 50, buyValue: 150, icon: "💪📿", description: "CON +5, STR +2" }, amulet_of_the_warrior: { id: "amulet_of_the_warrior", name: "Amulet of the Warrior", type: "amulet", slot: "amulet", strength: 6, constitution: 3, defense: 2, levelReq: 4, sellValue: 100, buyValue: 300, icon: "⚔️📿", description: "STR +6, CON +3, DEF +2" }, amulet_of_the_ranger: { id: "amulet_of_the_ranger", name: "Amulet of the Ranger", type: "amulet", slot: "amulet", agility: 6, strength: 2, levelReq: 4, sellValue: 90, buyValue: 270, icon: "🏹📿", description: "AGI +6, STR +2" }, chipped_amulet: { id: "chipped_amulet", name: "Chipped Amulet", type: "amulet", slot: "amulet", intellect: 1, levelReq: 1, sellValue: 8, buyValue: 24, icon: "📿", description: "INT +1" }, silver_amulet: { id: "silver_amulet", name: "Silver Amulet", type: "amulet", slot: "amulet", intellect: 2, maxMP: 5, levelReq: 2, sellValue: 20, buyValue: 60, icon: "🥈📿", description: "INT +2, Max MP +5" }, golden_amulet: { id: "golden_amulet", name: "Golden Amulet", type: "amulet", slot: "amulet", intellect: 4, maxMP: 10, constitution: 1, levelReq: 3, sellValue: 45, buyValue: 135, icon: "🥇📿", description: "INT +4, Max MP +10, CON +1" }, eternal_amulet: { id: "eternal_amulet", name: "Eternal Amulet", type: "amulet", slot: "amulet", intellect: 10, maxMP: 35, constitution: 3, levelReq: 5, sellValue: 180, buyValue: 540, icon: "✨📿", description: "INT +10, Max MP +35, CON +3" } }); console.log("Merged Amulets module loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadItemsArmor">> <<script>> window.setup = window.setup || {}; window.setup.items = window.setup.items || {}; window.setup.addItems({ rags: { id: "rags", name: "Rags", type: "armor", slot: "armor", defense: 0, sellable: false, buyable: false, icon: "👕", description: "Threadbare clothing" }, cloth_tunic: { id: "cloth_tunic", name: "Cloth Tunic", type: "armor", slot: "armor", defense: 2, agility: 1, levelReq: 1, sellValue: 5, buyValue: 20, icon: "👚", description: "DEF +2, AGI +1" }, leather_armor: { id: "leather_armor", name: "Leather Armor", type: "armor", slot: "armor", defense: 5, agility: 1, constitution: 1, levelReq: 2, sellValue: 20, buyValue: 60, icon: "🧥", description: "DEF +5, AGI +1, CON +1" }, robe_of_the_mage: { id: "robe_of_the_mage", name: "Robe of the Mage", type: "armor", slot: "armor", defense: 4, intellect: 5, agility: 2, levelReq: 3, sellValue: 60, buyValue: 150, icon: "🥼", description: "DEF +4, INT +5, AGI +2" }, chainmail: { id: "chainmail", name: "Chainmail", type: "armor", slot: "armor", defense: 8, strength: 2, constitution: 2, agility: -2, levelReq: 3, sellValue: 40, buyValue: 120, icon: "🦺", description: "DEF +8, STR +2, CON +2, AGI -2" }, platemail: { id: "platemail", name: "Platemail", type: "armor", slot: "armor", defense: 12, strength: 4, constitution: 3, agility: -3, levelReq: 5, sellValue: 100, buyValue: 300, icon: "🛡️", description: "DEF +12, STR +4, CON +3, AGI -3" }, worn_chainmail: { id: "worn_chainmail", name: "Worn Chainmail", type: "armor", slot: "armor", defense: 4, constitution: 1, agility: -1, levelReq: 1, sellValue: 5, buyValue: 15, icon: "🦺", description: "DEF +4, CON +1, AGI -1" }, tattered_leather: { id: "tattered_leather", name: "Tattered Leather", type: "armor", slot: "armor", defense: 3, agility: 1, levelReq: 1, sellValue: 4, buyValue: 12, icon: "🧥", description: "DEF +3, AGI +1" }, frayed_robe: { id: "frayed_robe", name: "Frayed Robe", type: "armor", slot: "armor", defense: 2, intellect: 2, levelReq: 1, sellValue: 4, buyValue: 12, icon: "🥼", description: "DEF +2, INT +2" }, patchy_hide: { id: "patchy_hide", name: "Patchy Hide", type: "armor", slot: "armor", defense: 3, agility: 1, constitution: 1, levelReq: 1, sellValue: 5, buyValue: 15, icon: "🧥", description: "DEF +3, AGI +1, CON +1" }, studded_leather: { id: "studded_leather", name: "Studded Leather", type: "armor", slot: "armor", defense: 6, agility: 3, strength: 1, constitution: 1, levelReq: 3, sellValue: 35, buyValue: 105, icon: "🧥✨", description: "DEF +6, AGI +3, STR +1, CON +1" }, scale_mail: { id: "scale_mail", name: "Scale Mail", type: "armor", slot: "armor", defense: 9, strength: 3, constitution: 2, agility: -1, levelReq: 4, sellValue: 55, buyValue: 165, icon: "🦺", description: "DEF +9, STR +3, CON +2, AGI -1" }, worn_leather: { id: "worn_leather", name: "Worn Leather Armor", type: "armor", slot: "armor", defense: 3, agility: 1, levelReq: 1, sellValue: 8, buyValue: 24, icon: "🧥", description: "DEF +3, AGI +1" }, rangers_vest: { id: "rangers_vest", name: "Ranger's Vest", type: "armor", slot: "armor", defense: 7, agility: 3, constitution: 1, levelReq: 3, sellValue: 25, buyValue: 75, icon: "🧥🏹", description: "DEF +7, AGI +3, CON +1" }, shadowweave: { id: "shadowweave", name: "Shadowweave Armor", type: "armor", slot: "armor", defense: 9, agility: 5, intellect: 1, levelReq: 4, sellValue: 50, buyValue: 150, icon: "🌑🧥", description: "DEF +9, AGI +5, INT +1" }, dragonscale_armor: { id: "dragonscale_armor", name: "Dragonscale Armor", type: "armor", slot: "armor", defense: 12, strength: 2, agility: 3, constitution: 2, levelReq: 5, sellValue: 100, buyValue: 300, icon: "🐉🧥", description: "DEF +12, STR +2, AGI +3, CON +2" }, rusty_chainmail: { id: "rusty_chainmail", name: "Rusty Chainmail", type: "armor", slot: "armor", defense: 5, strength: 1, constitution: 1, agility: -1, levelReq: 1, sellValue: 10, buyValue: 30, icon: "🦺", description: "DEF +5, STR +1, CON +1, AGI -1" }, iron_platemail: { id: "iron_platemail", name: "Iron Platemail", type: "armor", slot: "armor", defense: 8, strength: 2, constitution: 2, agility: -1, levelReq: 2, sellValue: 20, buyValue: 60, icon: "🛡️", description: "DEF +8, STR +2, CON +2, AGI -1" }, knights_plate: { id: "knights_plate", name: "Knight's Plate", type: "armor", slot: "armor", defense: 11, strength: 3, constitution: 3, agility: -2, levelReq: 3, sellValue: 40, buyValue: 120, icon: "🛡️⚔️", description: "DEF +11, STR +3, CON +3, AGI -2" }, crimson_armor: { id: "crimson_armor", name: "Crimson Armor", type: "armor", slot: "armor", defense: 14, strength: 5, constitution: 4, agility: -2, levelReq: 4, sellValue: 80, buyValue: 240, icon: "🩸🛡️", description: "DEF +14, STR +5, CON +4, AGI -2" }, eternal_guard: { id: "eternal_guard", name: "Eternal Guard Armor", type: "armor", slot: "armor", defense: 18, strength: 7, constitution: 6, agility: -2, levelReq: 5, sellValue: 150, buyValue: 450, icon: "✨🛡️", description: "DEF +18, STR +7, CON +6, AGI -2" }, apprentice_robes: { id: "apprentice_robes", name: "Apprentice Robes", type: "armor", slot: "armor", defense: 3, intellect: 4, levelReq: 2, sellValue: 15, buyValue: 45, icon: "🥼🔮", description: "DEF +3, INT +4" }, archmage_robes: { id: "archmage_robes", name: "Archmage Robes", type: "armor", slot: "armor", defense: 5, intellect: 6, constitution: 1, levelReq: 3, sellValue: 35, buyValue: 105, icon: "✨🥼", description: "DEF +5, INT +6, CON +1" }, void_robes: { id: "void_robes", name: "Void Robes", type: "armor", slot: "armor", defense: 7, intellect: 8, agility: 2, levelReq: 4, sellValue: 70, buyValue: 210, icon: "🌌🥼", description: "DEF +7, INT +8, AGI +2" }, starlight_robes: { id: "starlight_robes", name: "Starlight Robes", type: "armor", slot: "armor", defense: 9, intellect: 11, constitution: 2, levelReq: 5, sellValue: 120, buyValue: 360, icon: "⭐🥼", description: "DEF +9, INT +11, CON +2" } }); console.log("Merged Armor module loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadItemsBoots">> <<script>> window.setup = window.setup || {}; window.setup.items = window.setup.items || {}; window.setup.addItems({ leather_boots: { id: "leather_boots", name: "Leather Boots", type: "boots", slot: "boots", defense: 2, agility: 1, levelReq: 1, sellValue: 6, buyValue: 18, icon: "👢", description: "DEF +2, AGI +1" }, iron_boots: { id: "iron_boots", name: "Iron Boots", type: "boots", slot: "boots", defense: 4, constitution: 1, agility: -1, levelReq: 2, sellValue: 15, buyValue: 45, icon: "👢⚔️", description: "DEF +4, CON +1, AGI -1" }, sorcerer_boots: { id: "sorcerer_boots", name: "Sorcerer Boots", type: "boots", slot: "boots", defense: 2, intellect: 3, agility: 1, levelReq: 3, sellValue: 20, buyValue: 60, icon: "🔮👢", description: "DEF +2, INT +3, AGI +1" }, boots_of_speed: { id: "boots_of_speed", name: "Boots of Speed", type: "boots", slot: "boots", defense: 2, agility: 6, levelReq: 4, sellValue: 55, buyValue: 165, icon: "⚡👢", description: "DEF +2, AGI +6" }, iron_toed_boots: { id: "iron_toed_boots", name: "Iron-toed Boots", type: "boots", slot: "boots", defense: 3, strength: 1, constitution: 1, levelReq: 1, sellValue: 8, buyValue: 24, icon: "👢", description: "DEF +3, STR +1, CON +1" }, soft_slippers: { id: "soft_slippers", name: "Soft Slippers", type: "boots", slot: "boots", defense: 0, agility: 2, levelReq: 1, sellValue: 3, buyValue: 9, icon: "👡", description: "AGI +2" }, threadbare_boots: { id: "threadbare_boots", name: "Threadbare Boots", type: "boots", slot: "boots", defense: 1, agility: 1, levelReq: 1, sellValue: 3, buyValue: 9, icon: "👢", description: "DEF +1, AGI +1" }, sturdy_boots: { id: "sturdy_boots", name: "Sturdy Boots", type: "boots", slot: "boots", defense: 3, constitution: 1, levelReq: 2, sellValue: 10, buyValue: 30, icon: "👢", description: "DEF +3, CON +1" }, ranger_boots: { id: "ranger_boots", name: "Ranger Boots", type: "boots", slot: "boots", defense: 3, agility: 3, levelReq: 3, sellValue: 25, buyValue: 75, icon: "👢🏹", description: "DEF +3, AGI +3" }, worn_boots: { id: "worn_boots", name: "Worn Boots", type: "boots", slot: "boots", defense: 1, agility: 1, levelReq: 1, sellValue: 4, buyValue: 12, icon: "👢", description: "DEF +1, AGI +1" }, shadow_striders: { id: "shadow_striders", name: "Shadow Striders", type: "boots", slot: "boots", defense: 5, agility: 5, intellect: 1, levelReq: 4, sellValue: 60, buyValue: 180, icon: "🌑👢", description: "DEF +5, AGI +5, INT +1" }, eternal_greaves: { id: "eternal_greaves", name: "Eternal Greaves", type: "boots", slot: "boots", defense: 8, strength: 2, agility: 4, constitution: 2, levelReq: 5, sellValue: 120, buyValue: 360, icon: "✨👢", description: "DEF +8, STR +2, AGI +4, CON +2" } }); console.log("Merged Boots module loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadItemsHelmets">> <<script>> window.setup = window.setup || {}; window.setup.items = window.setup.items || {}; window.setup.addItems({ leather_helm: { id: "leather_helm", name: "Leather Helm", type: "helmet", slot: "helmet", defense: 2, agility: 1, levelReq: 1, sellValue: 8, buyValue: 24, icon: "🪖", description: "DEF +2, AGI +1" }, iron_helm: { id: "iron_helm", name: "Iron Helm", type: "helmet", slot: "helmet", defense: 4, constitution: 2, levelReq: 2, sellValue: 15, buyValue: 45, icon: "🪖⚔️", description: "DEF +4, CON +2" }, wizard_hat: { id: "wizard_hat", name: "Wizard Hat", type: "helmet", slot: "helmet", defense: 1, intellect: 3, levelReq: 2, sellValue: 12, buyValue: 36, icon: "🧙🎩", description: "DEF +1, INT +3" }, arcane_circlet: { id: "arcane_circlet", name: "Arcane Circlet", type: "helmet", slot: "helmet", defense: 2, intellect: 5, maxMP: 10, levelReq: 4, sellValue: 50, buyValue: 150, icon: "👑✨", description: "DEF +2, INT +5, Max MP +10" }, great_helm: { id: "great_helm", name: "Great Helm", type: "helmet", slot: "helmet", defense: 8, strength: 2, constitution: 2, agility: -2, levelReq: 4, sellValue: 60, buyValue: 180, icon: "🪖🛡️", description: "DEF +8, STR +2, CON +2, AGI -2" }, rusty_helm: { id: "rusty_helm", name: "Rusty Helm", type: "helmet", slot: "helmet", defense: 2, constitution: 1, levelReq: 1, sellValue: 4, buyValue: 12, icon: "🪖", description: "DEF +2, CON +1" }, cloth_mask: { id: "cloth_mask", name: "Cloth Mask", type: "helmet", slot: "helmet", defense: 1, agility: 1, levelReq: 1, sellValue: 3, buyValue: 9, icon: "🎭", description: "DEF +1, AGI +1" }, worn_hood: { id: "worn_hood", name: "Worn Hood", type: "helmet", slot: "helmet", defense: 1, intellect: 1, agility: 1, levelReq: 1, sellValue: 4, buyValue: 12, icon: "🥷", description: "DEF +1, INT +1, AGI +1" }, leather_cap: { id: "leather_cap", name: "Leather Cap", type: "helmet", slot: "helmet", defense: 3, agility: 2, levelReq: 2, sellValue: 8, buyValue: 24, icon: "🧢", description: "DEF +3, AGI +2" }, ranger_hood: { id: "ranger_hood", name: "Ranger Hood", type: "helmet", slot: "helmet", defense: 4, agility: 3, intellect: 1, levelReq: 3, sellValue: 25, buyValue: 75, icon: "🏹🎭", description: "DEF +4, AGI +3, INT +1" }, crown_of_command: { id: "crown_of_command", name: "Crown of Command", type: "helmet", slot: "helmet", defense: 6, strength: 2, intellect: 2, levelReq: 5, sellValue: 80, buyValue: 240, icon: "👑", description: "DEF +6, STR +2, INT +2" }, knights_helm: { id: "knights_helm", name: "Knight's Helm", type: "helmet", slot: "helmet", defense: 6, strength: 1, constitution: 2, levelReq: 3, sellValue: 25, buyValue: 75, icon: "🪖✨", description: "DEF +6, STR +1, CON +2" }, eternal_crown: { id: "eternal_crown", name: "Eternal Crown", type: "helmet", slot: "helmet", defense: 10, strength: 3, constitution: 3, intellect: 2, levelReq: 5, sellValue: 150, buyValue: 450, icon: "✨👑", description: "DEF +10, STR +3, CON +3, INT +2" }, shadow_mask: { id: "shadow_mask", name: "Shadow Mask", type: "helmet", slot: "helmet", defense: 6, agility: 4, intellect: 2, levelReq: 4, sellValue: 50, buyValue: 150, icon: "🌑🎭", description: "DEF +6, AGI +4, INT +2" }, eternal_hood: { id: "eternal_hood", name: "Eternal Hood", type: "helmet", slot: "helmet", defense: 8, agility: 5, intellect: 3, levelReq: 5, sellValue: 110, buyValue: 330, icon: "✨🎭", description: "DEF +8, AGI +5, INT +3" } }); console.log("Merged Helmets module loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadItemsPotions">> <<script>> window.setup = window.setup || {}; window.setup.items = window.setup.items || {}; window.setup.addItems({ health_potion: { id: "health_potion", name: "Health Potion", type: "consumable", effect: "heal", value: 40, levelReq: 2, sellValue: 10, buyValue: 30, icon: "🧪❤️", description: "Restores 40 HP" }, mana_potion: { id: "mana_potion", name: "Mana Potion", type: "consumable", effect: "mana", value: 30, levelReq: 2, sellValue: 10, buyValue: 30, icon: "🧪🔷", description: "Restores 30 MP" }, minor_health_potion: { id: "minor_health_potion", name: "Minor Health Potion", type: "consumable", effect: "heal", value: 20, levelReq: 1, sellValue: 5, buyValue: 15, icon: "🧪💚", description: "Restores 20 HP" }, greater_health_potion: { id: "greater_health_potion", name: "Greater Health Potion", type: "consumable", effect: "heal", value: 70, levelReq: 3, sellValue: 20, buyValue: 60, icon: "🧪💖", description: "Restores 70 HP" }, superior_health_potion: { id: "superior_health_potion", name: "Superior Health Potion", type: "consumable", effect: "heal", value: 110, levelReq: 4, sellValue: 40, buyValue: 120, icon: "🧪❤️🔥", description: "Restores 110 HP" }, elixir_of_life: { id: "elixir_of_life", name: "Elixir of Life", type: "consumable", effect: "heal", value: 160, levelReq: 5, sellValue: 80, buyValue: 240, icon: "✨🧪", description: "Restores 160 HP" }, minor_mana_potion: { id: "minor_mana_potion", name: "Minor Mana Potion", type: "consumable", effect: "mana", value: 15, levelReq: 1, sellValue: 5, buyValue: 15, icon: "🧪💙", description: "Restores 15 MP" }, greater_mana_potion: { id: "greater_mana_potion", name: "Greater Mana Potion", type: "consumable", effect: "mana", value: 50, levelReq: 3, sellValue: 20, buyValue: 60, icon: "🧪💎", description: "Restores 50 MP" }, superior_mana_potion: { id: "superior_mana_potion", name: "Superior Mana Potion", type: "consumable", effect: "mana", value: 80, levelReq: 4, sellValue: 40, buyValue: 120, icon: "🧪🌀", description: "Restores 80 MP" }, elixir_of_mana: { id: "elixir_of_mana", name: "Elixir of Mana", type: "consumable", effect: "mana", value: 120, levelReq: 5, sellValue: 80, buyValue: 240, icon: "✨🧪💙", description: "Restores 120 MP" } }); console.log("✅ Potions loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadItemsRings">> <<script>> window.setup = window.setup || {}; window.setup.items = window.setup.items || {}; window.setup.addItems({ ring_of_strength: { id: "ring_of_strength", name: "Ring of Strength", type: "ring", slot: "ring", strength: 3, constitution: 1, levelReq: 2, sellValue: 25, buyValue: 75, icon: "💍⚔️", description: "STR +3, CON +1" }, ring_of_intellect: { id: "ring_of_intellect", name: "Ring of Intellect", type: "ring", slot: "ring", intellect: 3, levelReq: 2, sellValue: 25, buyValue: 75, icon: "🔮💍", description: "INT +3" }, ring_of_magic: { id: "ring_of_magic", name: "Ring of Magic", type: "ring", slot: "ring", intellect: 5, maxMP: 10, levelReq: 3, sellValue: 50, buyValue: 150, icon: "✨💍", description: "INT +5, Max MP +10" }, ring_of_vitality: { id: "ring_of_vitality", name: "Ring of Vitality", type: "ring", slot: "ring", constitution: 4, levelReq: 2, sellValue: 25, buyValue: 75, icon: "❤️💍", description: "CON +4" }, ring_of_protection: { id: "ring_of_protection", name: "Ring of Protection", type: "ring", slot: "ring", defense: 5, levelReq: 3, sellValue: 40, buyValue: 120, icon: "🛡️💍", description: "DEF +5" }, ring_of_agility: { id: "ring_of_agility", name: "Ring of Agility", type: "ring", slot: "ring", agility: 4, levelReq: 2, sellValue: 25, buyValue: 75, icon: "⚡💍", description: "AGI +4" }, minor_strength_ring: { id: "minor_strength_ring", name: "Ring of Minor Strength", type: "ring", slot: "ring", strength: 1, levelReq: 1, sellValue: 10, buyValue: 30, icon: "💍", description: "STR +1" }, warriors_ring: { id: "warriors_ring", name: "Warrior's Ring", type: "ring", slot: "ring", strength: 5, constitution: 2, levelReq: 3, sellValue: 50, buyValue: 150, icon: "⚔️💍", description: "STR +5, CON +2" }, kings_ring: { id: "kings_ring", name: "King's Ring", type: "ring", slot: "ring", strength: 7, constitution: 3, agility: 1, levelReq: 4, sellValue: 100, buyValue: 300, icon: "👑💍", description: "STR +7, CON +3, AGI +1" }, eternal_ring: { id: "eternal_ring", name: "Eternal Ring", type: "ring", slot: "ring", strength: 10, constitution: 4, agility: 2, levelReq: 5, sellValue: 200, buyValue: 600, icon: "✨💍", description: "STR +10, CON +4, AGI +2" } }); console.log("Merged Rings module loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadItemsSpellbooks">> <<script>> window.setup = window.setup || {}; window.setup.items = window.setup.items || {}; window.setup.addItems({ spellbook_fire: { id: "spellbook_fire", name: "Spellbook of Fire", type: "spellbook", slot: "none", intellect: 2, spells: ["fireball"], sellValue: 100, buyValue: 300, icon: "📖🔥", description: "Teaches Fireball. Intellect +2" }, spellbook_ice: { id: "spellbook_ice", name: "Spellbook of Ice", type: "spellbook", slot: "none", intellect: 2, spells: ["ice_shard"], sellValue: 100, buyValue: 300, icon: "📖❄️", description: "Teaches Ice Shard. Intellect +2" }, spellbook_lightning: { id: "spellbook_lightning", name: "Spellbook of Lightning", type: "spellbook", slot: "none", intellect: 2, spells: ["lightning"], sellValue: 100, buyValue: 300, icon: "📖⚡", description: "Teaches Lightning Bolt. Intellect +2" }, tome_of_the_archmage: { id: "tome_of_the_archmage", name: "Tome of the Archmage", type: "spellbook", slot: "none", intellect: 8, spells: ["fireball", "lightning", "ice_shard"], sellValue: 300, buyValue: 800, icon: "📖✨", description: "Teaches all spells. Intellect +8" } }); console.log("✅ Spellbooks module loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadItemsWeaponsBows">> <<script>> window.setup = window.setup || {}; window.setup.items = window.setup.items || {}; window.setup.addItems({ cracked_bow: { id: "cracked_bow", name: "Cracked Bow", type: "weapon", slot: "weapon", damage: 3, agility: 1, levelReq: 1, sellValue: 4, buyValue: 12, icon: "🏹", description: "DMG +3, AGI +1" }, hunting_bow: { id: "hunting_bow", name: "Hunting Bow", type: "weapon", slot: "weapon", damage: 4, agility: 2, levelReq: 2, sellValue: 10, buyValue: 30, icon: "🏹", description: "DMG +4, AGI +2" }, oak_longbow: { id: "oak_longbow", name: "Oak Longbow", type: "weapon", slot: "weapon", damage: 5, agility: 3, levelReq: 3, sellValue: 18, buyValue: 54, icon: "🏹", description: "DMG +5, AGI +3" }, reinforced_recurve: { id: "reinforced_recurve", name: "Reinforced Recurve", type: "weapon", slot: "weapon", damage: 7, agility: 4, levelReq: 4, sellValue: 35, buyValue: 105, icon: "🏹✨", description: "DMG +7, AGI +4" }, rangers_bow: { id: "rangers_bow", name: "Ranger's Bow", type: "weapon", slot: "weapon", damage: 8, agility: 5, levelReq: 5, sellValue: 50, buyValue: 150, icon: "🏹🌲", description: "DMG +8, AGI +5" }, ironwood_greatbow: { id: "ironwood_greatbow", name: "Ironwood Greatbow", type: "weapon", slot: "weapon", damage: 10, agility: 6, strength: 2, levelReq: 6, sellValue: 80, buyValue: 240, icon: "🏹💪", description: "DMG +10, AGI +6, STR +2" }, falconstrike_bow: { id: "falconstrike_bow", name: "Falconstrike Bow", type: "weapon", slot: "weapon", damage: 11, agility: 7, levelReq: 7, sellValue: 100, buyValue: 300, icon: "🏹🦅", description: "DMG +11, AGI +7" }, moonveil_bow: { id: "moonveil_bow", name: "Moonveil Bow", type: "weapon", slot: "weapon", damage: 14, agility: 8, intellect: 2, levelReq: 8, sellValue: 150, buyValue: 450, icon: "🏹🌙", description: "DMG +14, AGI +8, INT +2" }, stormpiercer_bow: { id: "stormpiercer_bow", name: "Stormpiercer Bow", type: "weapon", slot: "weapon", damage: 16, agility: 9, levelReq: 9, sellValue: 200, buyValue: 600, icon: "🏹⚡", description: "DMG +16, AGI +9" }, dragonwing_longbow: { id: "dragonwing_longbow", name: "Dragonwing Longbow", type: "weapon", slot: "weapon", damage: 20, agility: 12, strength: 3, levelReq: 10, sellValue: 350, buyValue: 1050, icon: "🏹🐉", description: "DMG +20, AGI +12, STR +3" } }); console.log("✅ Bows loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadItemsWeaponsDaggers">> <<script>> window.setup = window.setup || {}; window.setup.items = window.setup.items || {}; window.setup.addItems({ rusty_dagger: { id: "rusty_dagger", name: "Rusty Dagger", type: "weapon", slot: "weapon", damage: 3, agility: 1, levelReq: 1, sellValue: 3, buyValue: 10, icon: "🗡️", description: "DMG +3, AGI +1" }, old_dagger: { id: "old_dagger", name: "Old Dagger", type: "weapon", slot: "weapon", damage: 4, agility: 1, levelReq: 2, sellValue: 8, buyValue: 25, icon: "🗡️", description: "DMG +4, AGI +1" }, iron_knife: { id: "iron_knife", name: "Iron Knife", type: "weapon", slot: "weapon", damage: 5, agility: 2, levelReq: 3, sellValue: 15, buyValue: 45, icon: "🗡️", description: "DMG +5, AGI +2" }, steel_dagger: { id: "steel_dagger", name: "Steel Dagger", type: "weapon", slot: "weapon", damage: 7, agility: 3, levelReq: 4, sellValue: 30, buyValue: 90, icon: "🗡️✨", description: "DMG +7, AGI +3" }, fine_dirk: { id: "fine_dirk", name: "Fine Dirk", type: "weapon", slot: "weapon", damage: 8, agility: 4, levelReq: 5, sellValue: 45, buyValue: 135, icon: "🗡️✨", description: "DMG +8, AGI +4" }, assassins_fang: { id: "assassins_fang", name: "Assassin's Fang", type: "weapon", slot: "weapon", damage: 10, agility: 5, critical: 5, levelReq: 6, sellValue: 70, buyValue: 210, icon: "🗡️💀", description: "DMG +10, AGI +5, CRIT +5%" }, shadow_dirk: { id: "shadow_dirk", name: "Shadow Dirk", type: "weapon", slot: "weapon", damage: 11, agility: 6, levelReq: 7, sellValue: 90, buyValue: 270, icon: "🗡️🌑", description: "DMG +11, AGI +6" }, venomblade: { id: "venomblade", name: "Venomblade", type: "weapon", slot: "weapon", damage: 13, agility: 7, poison: 5, levelReq: 8, sellValue: 120, buyValue: 360, icon: "🗡️💚", description: "DMG +13, AGI +7, POISON: 5 dmg/turn" }, ghostfang_kris: { id: "ghostfang_kris", name: "Ghostfang Kris", type: "weapon", slot: "weapon", damage: 15, agility: 8, levelReq: 9, sellValue: 160, buyValue: 480, icon: "🗡️👻", description: "DMG +15, AGI +8" }, soulpiercer: { id: "soulpiercer", name: "Soulpiercer", type: "weapon", slot: "weapon", damage: 18, agility: 10, lifesteal: 10, levelReq: 10, sellValue: 250, buyValue: 750, icon: "🗡️💜", description: "DMG +18, AGI +10, LIFESTEAL: 10%" } }); console.log("✅ Daggers loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadItemsWeaponsStaffs">> <<script>> window.setup = window.setup || {}; window.setup.items = window.setup.items || {}; window.setup.addItems({ crooked_branch: { id: "crooked_branch", name: "Crooked Branch", type: "weapon", slot: "weapon", damage: 2, intellect: 2, levelReq: 1, sellValue: 4, buyValue: 12, icon: "🔮", description: "DMG +2, INT +2" }, apprentice_staff: { id: "apprentice_staff", name: "Apprentice Staff", type: "weapon", slot: "weapon", damage: 3, intellect: 3, levelReq: 2, sellValue: 10, buyValue: 30, icon: "🔮", description: "DMG +3, INT +3" }, worn_oak_staff: { id: "worn_oak_staff", name: "Worn Oak Staff", type: "weapon", slot: "weapon", damage: 4, intellect: 3, constitution: 1, levelReq: 3, sellValue: 18, buyValue: 54, icon: "🔮", description: "DMG +4, INT +3, CON +1" }, rune_carved_staff: { id: "rune_carved_staff", name: "Rune-Carved Staff", type: "weapon", slot: "weapon", damage: 5, intellect: 5, levelReq: 4, sellValue: 35, buyValue: 105, icon: "🔮✨", description: "DMG +5, INT +5" }, arcane_channeler: { id: "arcane_channeler", name: "Arcane Channeler", type: "weapon", slot: "weapon", damage: 6, intellect: 6, levelReq: 5, sellValue: 50, buyValue: 150, icon: "🔮✨", description: "DMG +6, INT +6" }, crystal_focus_staff: { id: "crystal_focus_staff", name: "Crystal Focus Staff", type: "weapon", slot: "weapon", damage: 8, intellect: 7, maxMP: 10, levelReq: 6, sellValue: 80, buyValue: 240, icon: "🔮💎", description: "DMG +8, INT +7, MAX MP +10" }, emberwood_staff: { id: "emberwood_staff", name: "Emberwood Staff", type: "weapon", slot: "weapon", damage: 9, intellect: 8, burn: 5, levelReq: 7, sellValue: 110, buyValue: 330, icon: "🔮🔥", description: "DMG +9, INT +8, BURN: 5 dmg/turn" }, voidcaller_staff: { id: "voidcaller_staff", name: "Voidcaller Staff", type: "weapon", slot: "weapon", damage: 12, intellect: 10, maxMP: 20, levelReq: 8, sellValue: 170, buyValue: 510, icon: "🔮🌌", description: "DMG +12, INT +10, MAX MP +20" }, archmages_scepter: { id: "archmages_scepter", name: "Archmage's Scepter", type: "weapon", slot: "weapon", damage: 15, intellect: 12, maxMP: 30, levelReq: 9, sellValue: 280, buyValue: 840, icon: "🔮👑", description: "DMG +15, INT +12, MAX MP +30" }, staff_of_eternal_stars: { id: "staff_of_eternal_stars", name: "Staff of Eternal Stars", type: "weapon", slot: "weapon", damage: 18, intellect: 15, maxMP: 40, levelReq: 10, sellValue: 500, buyValue: 1500, icon: "🔮✨🌟", description: "DMG +18, INT +15, MAX MP +40" } }); console.log("✅ Staffs loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadItemsWeaponsStarters">> <<script>> window.setup = window.setup || {}; window.setup.items = window.setup.items || {}; window.setup.addItems({ fists: { id: "fists", name: "Fists", type: "weapon", slot: "weapon", damage: 2, strength: 0, constitution: 0, defense: 0, agility: 0, intellect: 0, sellable: false, buyable: false, icon: "👊", description: "Your bare hands" }, broken_shortsword: { id: "broken_shortsword", name: "Broken Shortsword", type: "weapon", slot: "weapon", damage: 3, strength: 1, levelReq: 1, sellValue: 3, buyValue: 10, icon: "⚔️💔", description: "DMG +3, STR +1" }, tarnished_dagger: { id: "tarnished_dagger", name: "Tarnished Dagger", type: "weapon", slot: "weapon", damage: 3, agility: 2, levelReq: 1, sellValue: 3, buyValue: 10, icon: "🗡️", description: "DMG +3, AGI +2" }, gnarled_staff: { id: "gnarled_staff", name: "Gnarled Staff", type: "weapon", slot: "weapon", damage: 2, intellect: 3, levelReq: 1, sellValue: 3, buyValue: 10, icon: "🔮", description: "DMG +2, INT +3" }, makeshift_bow: { id: "makeshift_bow", name: "Makeshift Bow", type: "weapon", slot: "weapon", damage: 3, agility: 2, levelReq: 1, sellValue: 3, buyValue: 10, icon: "🏹", description: "DMG +3, AGI +2" } }); console.log("✅ Starter weapons loaded"); <</script>> <</widget>> [[Back to Item Manager|00_Items_Weapons_Daggers]] [[Back to Item Manager|00_Items_Weapons_Swords]] [[Back to Item Manager|00_Items_Weapons_Bows]] [[Back to Item Manager|00_Items_Weapons_Staffs]] <</nobr>>
<<nobr>> <<widget "loadItemsWeaponsSwords">> <<script>> window.setup = window.setup || {}; window.setup.items = window.setup.items || {}; window.setup.addItems({ rusted_sword: { id: "rusted_sword", name: "Rusted Sword", type: "weapon", slot: "weapon", damage: 4, strength: 1, levelReq: 1, sellValue: 5, buyValue: 15, icon: "⚔️", description: "DMG +4, STR +1" }, worn_shortsword: { id: "worn_shortsword", name: "Worn Shortsword", type: "weapon", slot: "weapon", damage: 5, strength: 2, levelReq: 2, sellValue: 12, buyValue: 36, icon: "⚔️", description: "DMG +5, STR +2" }, iron_longsword: { id: "iron_longsword", name: "Iron Longsword", type: "weapon", slot: "weapon", damage: 6, strength: 2, constitution: 1, levelReq: 3, sellValue: 20, buyValue: 60, icon: "⚔️", description: "DMG +6, STR +2, CON +1" }, steel_blade: { id: "steel_blade", name: "Steel Blade", type: "weapon", slot: "weapon", damage: 8, strength: 4, levelReq: 4, sellValue: 40, buyValue: 120, icon: "⚔️✨", description: "DMG +8, STR +4" }, knights_sword: { id: "knights_sword", name: "Knight's Sword", type: "weapon", slot: "weapon", damage: 10, strength: 4, constitution: 2, levelReq: 5, sellValue: 60, buyValue: 180, icon: "⚔️✨", description: "DMG +10, STR +4, CON +2" }, runed_longsword: { id: "runed_longsword", name: "Runed Longsword", type: "weapon", slot: "weapon", damage: 12, strength: 6, intellect: 2, levelReq: 6, sellValue: 90, buyValue: 270, icon: "⚔️🔮", description: "DMG +12, STR +6, INT +2" }, crimson_warblade: { id: "crimson_warblade", name: "Crimson Warblade", type: "weapon", slot: "weapon", damage: 13, strength: 7, constitution: 2, levelReq: 7, sellValue: 120, buyValue: 360, icon: "⚔️🩸", description: "DMG +13, STR +7, CON +2" }, dragonscale_greatsword: { id: "dragonscale_greatsword", name: "Dragonscale Greatsword", type: "weapon", slot: "weapon", damage: 16, strength: 9, constitution: 3, agility: -1, levelReq: 8, sellValue: 180, buyValue: 540, icon: "⚔️🐉", description: "DMG +16, STR +9, CON +3, AGI -1" }, kingslayer_blade: { id: "kingslayer_blade", name: "Kingslayer Blade", type: "weapon", slot: "weapon", damage: 18, strength: 10, agility: 2, levelReq: 9, sellValue: 240, buyValue: 720, icon: "⚔️👑", description: "DMG +18, STR +10, AGI +2" }, eternal_edge: { id: "eternal_edge", name: "Eternal Edge", type: "weapon", slot: "weapon", damage: 22, strength: 12, constitution: 4, levelReq: 10, sellValue: 400, buyValue: 1200, icon: "⚔️✨💫", description: "DMG +22, STR +12, CON +4" } }); console.log("✅ Swords loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadCombatDamage">> <<script>> window.setup = window.setup || {}; window.setup.combat = window.setup.combat || {}; window.setup.combat.applySpellEffects = function(enemy, effects) { let log = ""; for (let i = 0; i < effects.length; i++) { let effect = effects[i]; if (effect.type === "burn") { log += this.applyStatusToEnemy(enemy, "burn", effect.value, effect.turns); } if (effect.type === "poison") { log += this.applyStatusToEnemy(enemy, "poison", effect.value, effect.turns); } if (effect.type === "freeze") { log += this.applyStatusToEnemy(enemy, "freeze", effect.value, effect.turns); } if (effect.type === "slow") { log += this.applyStatusToEnemy(enemy, "slow", effect.value, effect.turns); } if (effect.type === "blind") { log += this.applyStatusToEnemy(enemy, "blind", effect.value, effect.turns); } if (effect.type === "stun") { log += this.applyStatusToEnemy(enemy, "stun", effect.value, effect.turns); } if (effect.type === "root") { log += this.applyStatusToEnemy(enemy, "root", effect.value, effect.turns); } if (effect.type === "weakness") { log += this.applyStatusToEnemy(enemy, "weakness", effect.value, effect.turns); } if (effect.type === "armor_break") { log += this.applyStatusToEnemy(enemy, "armor_break", effect.value, effect.turns); } } return log; }; window.setup.combat.calculatePlayerDamage = function(enemy, critMultiplier = 1) { let hitResult = this.checkHit(enemy); if (!hitResult.hit) { return { damage: 0, hit: false, hitChance: hitResult.chance, roll: hitResult.roll, evasionMessage: hitResult.evasionMessage }; } let weaponItem = window.setup.getItem(State.variables.equipment.weapon); let weaponDamage = weaponItem ? weaponItem.damage : 2; let baseDamage = Math.floor(Math.random() * 6) + 1 + State.variables.strength; let damage = Math.max(1, baseDamage + weaponDamage - enemy.defense); if (enemy.weakened) damage = Math.floor(damage * 0.7); let parryResult = window.setup.specialAbilities.applyParry(enemy, damage * critMultiplier); let weaponEffects = {}; if (weaponItem) { if (weaponItem.lifesteal) weaponEffects.lifesteal = weaponItem.lifesteal; if (weaponItem.poison) weaponEffects.poison = weaponItem.poison; if (weaponItem.burn) weaponEffects.burn = weaponItem.burn; if (weaponItem.freeze) weaponEffects.freeze = weaponItem.freeze; } return { damage: parryResult.damage, hit: true, hitChance: hitResult.chance, roll: hitResult.roll, evasionMessage: "", parryMessage: parryResult.message, weaponEffects: weaponEffects, rawDamage: damage * critMultiplier }; }; window.setup.combat.calculateEnemyDamage = function(enemy, allyCount = 0) { let enemyAGI = enemy.agility || 8; let enemyHitChance = 70 + ((enemyAGI - State.variables.agility) * 1.5); if (enemy.slowed) enemyHitChance = enemyHitChance * 0.6; if (enemy.blinded) enemyHitChance = enemyHitChance * 0.5; enemyHitChance = Math.min(85, Math.max(30, enemyHitChance)); let hitRoll = Math.random() * 100; if (hitRoll > enemyHitChance || enemy.stunned) { if (enemy.stunned) { return { damage: 0, isCrit: false, packMessage: '<span style="color:#ffaa44;">💫 ' + enemy.name + ' is stunned and cannot act!</span>', hit: false }; } return { damage: 0, isCrit: false, packMessage: '<span style="color:#88ff88;">' + enemy.name + ' attacks but misses you!</span>', hit: false }; } let armorItem = window.setup.getItem(State.variables.equipment.armor); let armorDefense = armorItem ? armorItem.defense : 0; let totalDefense = State.variables.defense + armorDefense; let armorBreak = 0; if (enemy.armorBroken) armorBreak = 8; let armorPenetration = 0; let armorPenMessage = ""; if (enemy.special === "armor_breaker" || enemy.strength >= 24 && (enemy.type === "humanoid" || enemy.type === "giant")) { armorPenetration = 5; armorPenMessage = '<span style="color:#ffaa44;">💪 ' + enemy.name + ' ignores ' + armorPenetration + ' armor!</span><br>'; } let effectiveDefense = Math.max(0, totalDefense - armorPenetration - armorBreak); let damage = Math.max(1, Math.floor(Math.random() * 8) + 3 + enemy.strength - effectiveDefense); let isCrit = false; if (enemy.special === "high_crit") { isCrit = Math.random() * 100 <= 25; if (isCrit) damage = Math.floor(damage * 1.8); } let packBonus = window.setup.specialAbilities.getPackBonus(enemy, allyCount); damage += packBonus.attackBonus; return { damage: Math.max(1, damage), isCrit: isCrit, packMessage: (armorPenMessage ? armorPenMessage : "") + (packBonus.message ? packBonus.message : ""), hit: true }; }; window.setup.combat.applyWeaponEffects = function(enemy, weaponEffects, damageDealt) { let result = { log: "", enemyHeal: 0 }; if (weaponEffects.lifesteal) { let healAmount = Math.floor(damageDealt * (weaponEffects.lifesteal / 100)); if (healAmount > 0) { State.variables.currentHP = Math.min(State.variables.maxHP, State.variables.currentHP + healAmount); result.log += '<span style="color:#88ff88;">💚 Lifesteal restores ' + healAmount + ' HP!</span><br>'; } } if (weaponEffects.poison && !enemy.poisoned) { result.log += this.applyStatusToEnemy(enemy, "poison", weaponEffects.poison, 2); } if (weaponEffects.burn && !enemy.burning) { result.log += this.applyStatusToEnemy(enemy, "burn", weaponEffects.burn, 2); } if (weaponEffects.freeze) { let freezeRoll = Math.random() * 100; if (freezeRoll <= weaponEffects.freeze && !enemy.frozen) { result.log += this.applyStatusToEnemy(enemy, "freeze", 0, 1); } } return result; }; console.log("✅ Combat Damage loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadCombatRound">> <<script>> window.setup = window.setup || {}; window.setup.combat = window.setup.combat || {}; window.setup.combat.processCombatRound = function(enemy, playerDamage, allyCount = 0, combatState = {}) { let summonedMinions = combatState.summonedMinions || []; let weaponEffects = combatState.weaponEffects || {}; let rawDamage = combatState.rawDamage || playerDamage; let spellEffects = combatState.spellEffects || []; enemy.hp -= playerDamage; let result = { playerDamage: playerDamage, enemyDamage: 0, enemyDied: enemy.hp <= 0, playerDied: false, combatLog: "", enemyHeal: 0, stunPlayer: false, additionalPlayerDamage: 0, explosionDamage: 0, summonedMinions: summonedMinions, summonTriggered: false }; if (Object.keys(weaponEffects).length > 0) { let effectResult = this.applyWeaponEffects(enemy, weaponEffects, rawDamage); result.combatLog += effectResult.log; } if (spellEffects.length > 0) { let spellEffectLog = this.applySpellEffects(enemy, spellEffects); result.combatLog += spellEffectLog; } if (enemy.special === "summon_adds" && !enemy.hasSummoned && enemy.hp < enemy.maxHp * 0.7) { enemy.hasSummoned = true; let summonCount = window.setup.specialAbilities.roll(1, 2); result.summonTriggered = true; result.combatLog += '<span style="color:#ffaa44;">🕯️ ' + enemy.name + ' summons ' + summonCount + ' minion(s)!🕯️</span><br>'; for (let i = 0; i < summonCount; i++) { result.summonedMinions.push({ name: enemy.name + "'s Minion", hp: Math.floor(enemy.maxHp * 0.4), maxHp: Math.floor(enemy.maxHp * 0.4), strength: Math.floor(enemy.strength * 0.6), constitution: Math.floor(enemy.constitution * 0.5), defense: Math.floor(enemy.defense * 0.7), agility: enemy.agility, exp: 0, gold: { min: 0, max: 0 }, type: enemy.type, description: "A lesser minion.", special: null, isMinion: true }); } } if (!result.enemyDied) { let dotResult = this.processDamageOverTime(enemy); result.combatLog += dotResult.log; if (dotResult.enemyDied) result.enemyDied = true; let buffExpireLog = this.processPlayerBuffExpiration(); result.combatLog += buffExpireLog; let debuffExpireLog = this.processEnemyStatusExpiration(enemy); result.combatLog += debuffExpireLog; if (enemy.frozen) { enemy.frozen = false; result.combatLog += '<span style="color:#88aaff;">❄️ ' + enemy.name + ' is frozen and cannot act!❄️</span><br>'; } else if (!result.enemyDied) { let turnStartResult = window.setup.specialAbilities.applyTurnStartAbility(enemy); result.combatLog += turnStartResult.log; let abilityResult = window.setup.specialAbilities.applyEnemyAbility(enemy, "on_damage"); result.combatLog += abilityResult.log; result.stunPlayer = abilityResult.stunPlayer; result.additionalPlayerDamage = abilityResult.additionalPlayerDamage; if (result.additionalPlayerDamage > 0) { if (State.variables.shield && State.variables.shieldValue > 0) { let absorbed = Math.min(State.variables.shieldValue, result.additionalPlayerDamage); State.variables.shieldValue -= absorbed; result.additionalPlayerDamage -= absorbed; result.combatLog += '<span style="color:#88aaff;">🛡️ Shield absorbs ' + absorbed + ' damage!</span><br>'; if (State.variables.shieldValue <= 0) { State.variables.shield = false; result.combatLog += '<span style="color:#88aaff;">🛡️ Shield shattered!</span><br>'; } } State.variables.currentHP -= result.additionalPlayerDamage; if (State.variables.currentHP <= 0) result.playerDied = true; } if (!result.stunPlayer) { let enemyDamageResult = this.calculateEnemyDamage(enemy, allyCount + result.summonedMinions.length); result.enemyDamage = enemyDamageResult.damage; if (State.variables.shield && State.variables.shieldValue > 0) { let absorbed = Math.min(State.variables.shieldValue, result.enemyDamage); State.variables.shieldValue -= absorbed; result.enemyDamage -= absorbed; result.combatLog += '<span style="color:#88aaff;">🛡️ Shield absorbs ' + absorbed + ' damage!</span><br>'; if (State.variables.shieldValue <= 0) { State.variables.shield = false; result.combatLog += '<span style="color:#88aaff;">🛡️ Shield shattered!</span><br>'; } } State.variables.currentHP -= result.enemyDamage; result.playerDied = State.variables.currentHP <= 0; if (!enemyDamageResult.hit) { result.combatLog += enemyDamageResult.packMessage + "<br>"; } else { if (enemyDamageResult.isCrit) result.combatLog += '<span style="color:#ff4444;">💀 ' + enemy.name + ' lands a CRITICAL HIT! 💀</span><br>'; if (enemyDamageResult.packMessage && !enemyDamageResult.packMessage.includes("misses")) result.combatLog += enemyDamageResult.packMessage; if (result.enemyDamage > 0) result.combatLog += enemy.name + ' hits you for ' + result.enemyDamage + ' damage!<br>'; } } else { result.combatLog += '<span style="color:#ff4444;">You are stunned and cannot act!</span><br>'; } } } if (result.enemyDied) { let deathResult = window.setup.specialAbilities.onDeathAbility(enemy); if (deathResult.playerDamage > 0) { result.explosionDamage = deathResult.playerDamage; State.variables.currentHP -= deathResult.playerDamage; result.combatLog += deathResult.log; if (State.variables.currentHP <= 0) result.playerDied = true; } } return result; }; window.setup.combat.processMultiCombatRound = function(enemies, playerDamageIndex = 0, allyCount = 0) { let results = [], totalLog = "", playerDied = false, updatedEnemies = enemies.slice(); if (updatedEnemies[playerDamageIndex]) { let targetEnemy = updatedEnemies[playerDamageIndex]; let combatState = { summonedMinions: [] }; let roundResult = this.processCombatRound(targetEnemy, playerDamage, allyCount, combatState); totalLog += roundResult.combatLog; playerDied = roundResult.playerDied; if (roundResult.summonedMinions.length > 0) updatedEnemies = updatedEnemies.concat(roundResult.summonedMinions); if (roundResult.enemyDied) updatedEnemies.splice(playerDamageIndex, 1); results.push(roundResult); } return { enemies: updatedEnemies, results: results, combatLog: totalLog, playerDied: playerDied }; }; window.setup.combat.awardVictory = function(enemy) { if (enemy.isMinion) return { gold: 0, exp: 0 }; let goldDropped = Math.floor(Math.random() * (enemy.gold.max - enemy.gold.min + 1) + enemy.gold.min); State.variables.gold += goldDropped; window.setup.exp.gain(enemy.exp); return { gold: goldDropped, exp: enemy.exp }; }; window.setup.combat.checkPlayerConfused = function() { if (State.variables.confused) { let confusedResult = window.setup.specialAbilities.checkConfused(true); if (confusedResult.confused) { State.variables.confused = false; return { canAct: false, message: confusedResult.message }; } State.variables.confused = false; } return { canAct: true, message: "" }; }; console.log("✅ Combat Round loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadCombatStatusEffects">> <<script>> window.setup = window.setup || {}; window.setup.combat = window.setup.combat || {}; window.setup.combat.applyStatusToEnemy = function(enemy, effectType, value, turns) { let log = ""; switch(effectType) { case "burn": if (!enemy.burning) { enemy.burning = true; enemy.burnDamage = value; enemy.burnTurns = turns; log += '<span style="color:#ff8844;">🔥 ' + enemy.name + ' is BURNING! (' + value + ' dmg/turn, ' + turns + ' turns)</span><br>'; } break; case "poison": if (!enemy.poisoned) { enemy.poisoned = true; enemy.poisonDamage = value; enemy.poisonTurns = turns; log += '<span style="color:#88ff88;">💚 ' + enemy.name + ' is POISONED! (' + value + ' dmg/turn, ' + turns + ' turns)</span><br>'; } break; case "freeze": if (!enemy.frozen) { enemy.frozen = true; enemy.frozenTurns = turns; log += '<span style="color:#88aaff;">❄️ ' + enemy.name + ' is FROZEN! (' + turns + ' turn)</span><br>'; } break; case "stun": if (!enemy.stunned) { enemy.stunned = true; enemy.stunTurns = turns; log += '<span style="color:#ffaa44;">💫 ' + enemy.name + ' is STUNNED! (' + turns + ' turn)</span><br>'; } break; case "slow": if (!enemy.slowed) { enemy.slowed = true; enemy.slowTurns = turns; log += '<span style="color:#aaffaa;">🐢 ' + enemy.name + ' is SLOWED! (' + turns + ' turns)</span><br>'; } break; case "blind": if (!enemy.blinded) { enemy.blinded = true; enemy.blindTurns = turns; log += '<span style="color:#aaaaff;">🌫️ ' + enemy.name + ' is BLINDED! (' + turns + ' turns)</span><br>'; } break; case "root": if (!enemy.rooted) { enemy.rooted = true; enemy.rootTurns = turns; log += '<span style="color:#88ff88;">🌿 ' + enemy.name + ' is ROOTED! (' + turns + ' turns)</span><br>'; } break; case "weakness": if (!enemy.weakened) { enemy.weakened = true; enemy.weaknessTurns = turns; enemy.originalStrength = enemy.strength; enemy.strength = Math.floor(enemy.strength * 0.7); log += '<span style="color:#ff8888;">💔 ' + enemy.name + ' is WEAKENED! (' + turns + ' turns)</span><br>'; } break; case "armor_break": if (!enemy.armorBroken) { enemy.armorBroken = true; enemy.armorBreakTurns = turns; log += '<span style="color:#ffaa44;">🛡️ ' + enemy.name + "'s ARMOR is SHATTERED!</span><br>"; } break; } return log; }; window.setup.combat.applyBuffToPlayer = function(buffType, value, turns) { let log = ""; switch(buffType) { case "regen": State.variables.regen = true; State.variables.regenValue = value; State.variables.regenTurns = turns; log += '<span style="color:#88ff88;">💚✨ Regen active! Restores ' + value + ' HP per turn (' + turns + ' turns)</span><br>'; break; case "haste": State.variables.haste = true; State.variables.hasteTurns = turns; log += '<span style="color:#ffaa88;">⚡ Haste active! Increased action speed (' + turns + ' turns)</span><br>'; break; case "shield": State.variables.shield = true; State.variables.shieldValue = value; State.variables.shieldTurns = turns; log += '<span style="color:#88aaff;">🛡️ Shield active! Absorbs ' + value + ' damage (' + turns + ' turns)</span><br>'; break; } return log; }; window.setup.combat.processEnemyStatusExpiration = function(enemy) { let log = ""; if (enemy.slowed) { enemy.slowTurns--; if (enemy.slowTurns <= 0) { enemy.slowed = false; log += '<span style="color:#aaffaa;">🐢 ' + enemy.name + ' is no longer slowed.</span><br>'; } } if (enemy.blinded) { enemy.blindTurns--; if (enemy.blindTurns <= 0) { enemy.blinded = false; log += '<span style="color:#aaaaff;">🌫️ ' + enemy.name + ' is no longer blinded.</span><br>'; } } if (enemy.stunned) { enemy.stunTurns--; if (enemy.stunTurns <= 0) { enemy.stunned = false; log += '<span style="color:#ffaa44;">💫 ' + enemy.name + ' recovers from stun.</span><br>'; } } if (enemy.rooted) { enemy.rootTurns--; if (enemy.rootTurns <= 0) { enemy.rooted = false; log += '<span style="color:#88ff88;">🌿 ' + enemy.name + ' breaks free from roots.</span><br>'; } } if (enemy.weakened) { enemy.weaknessTurns--; if (enemy.weaknessTurns <= 0) { enemy.weakened = false; if (enemy.originalStrength) { enemy.strength = enemy.originalStrength; } log += '<span style="color:#ff8888;">💔 ' + enemy.name + ' recovers from weakness.</span><br>'; } } if (enemy.frozen) { enemy.frozenTurns--; if (enemy.frozenTurns <= 0) { enemy.frozen = false; log += '<span style="color:#88aaff;">❄️ ' + enemy.name + ' thaws out!</span><br>'; } } if (enemy.armorBroken) { enemy.armorBreakTurns--; if (enemy.armorBreakTurns <= 0) { enemy.armorBroken = false; log += '<span style="color:#ffaa44;">🛡️ ' + enemy.name + "'s armor regenerates.</span><br>"; } } return log; }; window.setup.combat.processPlayerBuffExpiration = function() { let log = ""; if (State.variables.regen) { State.variables.regenTurns--; if (State.variables.regenTurns <= 0) { State.variables.regen = false; log += '<span style="color:#88ff88;">💚✨ Regen fades.</span><br>'; } } if (State.variables.haste) { State.variables.hasteTurns--; if (State.variables.hasteTurns <= 0) { State.variables.haste = false; log += '<span style="color:#ffaa88;">⚡ Haste fades.</span><br>'; } } if (State.variables.shield) { State.variables.shieldTurns--; if (State.variables.shieldTurns <= 0) { State.variables.shield = false; log += '<span style="color:#88aaff;">🛡️ Shield fades.</span><br>'; } } return log; }; window.setup.combat.processDamageOverTime = function(enemy) { let result = { log: "", damage: 0, enemyDied: false }; if (enemy.poisoned && enemy.poisonTurns > 0) { let poisonDamage = enemy.poisonDamage || 5; enemy.hp -= poisonDamage; enemy.poisonTurns--; result.damage += poisonDamage; result.log += '<span style="color:#88ff88;">💚 Poison deals ' + poisonDamage + ' damage! (' + enemy.poisonTurns + ' turns left)</span><br>'; if (enemy.poisonTurns <= 0) enemy.poisoned = false; } if (enemy.burning && enemy.burnTurns > 0) { let burnDamage = enemy.burnDamage || 5; enemy.hp -= burnDamage; enemy.burnTurns--; result.damage += burnDamage; result.log += '<span style="color:#ff8844;">🔥 Burn deals ' + burnDamage + ' damage! (' + enemy.burnTurns + ' turns left)</span><br>'; if (enemy.burnTurns <= 0) enemy.burning = false; } if (State.variables.regen && State.variables.regenTurns > 0) { let regenAmount = State.variables.regenValue || 5; let oldHP = State.variables.currentHP; State.variables.currentHP = Math.min(State.variables.maxHP, State.variables.currentHP + regenAmount); let actualHeal = State.variables.currentHP - oldHP; result.log += '<span style="color:#88ff88;">💚✨ Regen restores ' + actualHeal + ' HP! (' + State.variables.regenTurns + ' turns left)</span><br>'; } if (enemy.hp <= 0) result.enemyDied = true; return result; }; console.log("✅ Combat Status Effects loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesArctic">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ ice_wolf: { name: "Ice Wolf", tier: 1, type: "beast", description: "A white-furred wolf with frost-tipped fur.", special: "pack_hunter" }, ice_mephit: { name: "Ice Mephit", tier: 1, type: "elemental", description: "A small winged creature of living frost.", special: "explode" }, snow_goblin: { name: "Snow Goblin", tier: 2, type: "humanoid", description: "A white-furred goblin with ice-tipped arrows.", special: "trap_user" }, yeti: { name: "Yeti", tier: 2, type: "monstrosity", description: "A towering white-furred ape with icy claws.", special: "fearsome_bite" }, polar_owlbear: { name: "Polar Owlbear", tier: 3, type: "beast", description: "An owlbear with thick white fur and frozen beak.", special: "fury" }, winter_wolf: { name: "Winter Wolf", tier: 3, type: "beast", description: "A massive dire wolf with frost breath.", special: "breath_weapon" }, frost_salamander: { name: "Frost Salamander", tier: 4, type: "elemental", description: "A serpentine creature of living ice.", special: "breath_weapon" }, frost_giant: { name: "Frost Giant", tier: 4, type: "giant", description: "A massive blue-skinned giant with a frozen axe.", special: "fury" }, ancient_white_dragon: { name: "Ancient White Dragon", tier: 5, type: "dragon", description: "A massive icy dragon whose breath freezes souls.", special: "breath_weapon" } }); console.log("Arctic biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesCaverns">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ cave_bat: { name: "Cave Bat Swarm", tier: 1, type: "beast", description: "A screeching cloud of leathery wings.", special: "blind" }, giant_centipede: { name: "Giant Centipede", tier: 1, type: "beast", description: "A writhing, many-legged arthropod.", special: "poison_sting" }, duergar_scout: { name: "Duergar Scout", tier: 2, type: "humanoid", description: "A grey-skinned dwarf with cruel eyes.", special: "trap_user" }, ettercap: { name: "Ettercap", tier: 2, type: "monstrosity", description: "A twisted spider-like humanoid.", special: "poison_sting" }, carrion_crawler: { name: "Carrion Crawler", tier: 3, type: "monstrosity", description: "A giant worm-like creature with paralytic tentacles.", special: "paralyze" }, umber_hulk: { name: "Umber Hulk", tier: 3, type: "monstrosity", description: "A burrowing insectoid horror with confusing patterns.", special: "confuse" }, mind_flayer: { name: "Mind Flayer", tier: 4, type: "aberration", description: "A purple-skinned humanoid with tentacles.", special: "mind_blast" }, beholder: { name: "Beholder", tier: 4, type: "aberration", description: "A floating orb of eyes that never blinks.", special: "eye_rays" }, elder_brain: { name: "Elder Brain", tier: 5, type: "aberration", description: "A giant pulsating brain in a briny pool.", special: "mind_blast" } }); console.log("Caverns biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesCity">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ pickpocket: { name: "Pickpocket", tier: 1, type: "humanoid", description: "A hooded figure with quick hands.", special: "high_crit" }, street_ruffian: { name: "Street Ruffian", tier: 1, type: "humanoid", description: "A brute with a wooden club.", special: "pack_hunter" }, assassin_initiate: { name: "Assassin Initiate", tier: 2, type: "humanoid", description: "A silent figure in dark leather.", special: "poison_sting" }, corrupt_guard: { name: "Corrupt Guard", tier: 2, type: "humanoid", description: "A guard in dented armor who demands tribute.", special: "parry" }, wererat_boss: { name: "Wererat Crime Boss", tier: 3, type: "humanoid", description: "A rat-faced man with a silver-tipped cane.", special: "disease" }, mage_enforcer: { name: "Mage Enforcer", tier: 3, type: "humanoid", description: "A tattooed wizard working for the underworld.", special: "casts_spells" }, invisible_stalker: { name: "Invisible Stalker", tier: 4, type: "elemental", description: "An unseen force of vengeance.", special: "evasion" }, rakshasa_noble: { name: "Rakshasa Noble", tier: 4, type: "fiend", description: "A tiger-headed man in fine silks.", special: "casts_spells" }, beholder_crime_lord: { name: "Beholder Crime Lord", tier: 5, type: "aberration", description: "A floating eye tyrant controlling the underworld.", special: "eye_rays" } }); console.log("City biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesCoastal">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ reef_crab: { name: "Reef Crab", tier: 1, type: "beast", description: "A shelled crustacean with snapping claws.", special: "hard_shell" }, sahuagin_scout: { name: "Sahuagin Scout", tier: 1, type: "humanoid", description: "A scaled fish-man with webbed claws.", special: "pack_hunter" }, pirate_raider: { name: "Pirate Raider", tier: 2, type: "humanoid", description: "A scarred pirate with a cutlass.", special: "high_crit" }, giant_octopus: { name: "Giant Octopus", tier: 2, type: "beast", description: "A massive cephalopod with crushing tentacles.", special: "entangle" }, sea_hag: { name: "Sea Hag", tier: 3, type: "fey", description: "A hideous crone with seaweed hair.", special: "casts_spells" }, merrow: { name: "Merrow", tier: 3, type: "monstrosity", description: "A hulking aquatic ogre with a harpoon.", special: "fury" }, water_elemental: { name: "Water Elemental", tier: 4, type: "elemental", description: "A swirling vortex of seawater.", special: "entangle" }, kraken_priest: { name: "Kraken Priest", tier: 4, type: "humanoid", description: "A robed cultist preaching of the deep.", special: "casts_spells" }, dragon_turtle: { name: "Dragon Turtle", tier: 5, type: "dragon", description: "A colossal turtle with a dragon's head and steaming breath.", special: "breath_weapon" } }); console.log("Coastal biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesDeepOcean">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ quipper_swarm: { name: "Quipper Swarm", tier: 1, type: "beast", description: "A swirling school of razor-toothed fish.", special: "pack_hunter" }, hunter_shark: { name: "Hunter Shark", tier: 1, type: "beast", description: "A sleek predator sensing blood.", special: "blood_drain" }, giant_octopus_deep: { name: "Giant Octopus", tier: 2, type: "beast", description: "A massive cephalopod with crushing tentacles.", special: "entangle" }, chuul: { name: "Chuul", tier: 2, type: "monstrosity", description: "A lobster-like horror with grasping claws.", special: "paralyze" }, water_weird: { name: "Water Weird", tier: 3, type: "elemental", description: "A serpentine column of living water.", special: "entangle" }, aboleth_servitor: { name: "Aboleth Servitor", tier: 3, type: "aberration", description: "A slimy, tentacled servant.", special: "mind_blast" }, kraken_priest_deep: { name: "Kraken Priest", tier: 4, type: "humanoid", description: "A robed cultist preaching the coming of the deep.", special: "casts_spells" }, elder_leviathan: { name: "Elder Leviathan", tier: 4, type: "monstrosity", description: "A colossal sea serpent that drags ships to the depths.", special: "dragon_fear" }, true_kraken: { name: "True Kraken", tier: 5, type: "monstrosity", description: "The primordial kraken that sleeps in the abyss.", special: "mind_blast" } }); console.log("Deep Ocean biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesDesert">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ giant_scorpion: { name: "Giant Scorpion", tier: 1, type: "beast", description: "A massive arachnid with a venomous stinger.", special: "poison_sting" }, dust_mephit: { name: "Dust Mephit", tier: 1, type: "elemental", description: "A small winged creature of swirling sand.", special: "blind" }, gnoll_raider: { name: "Gnoll Raider", tier: 2, type: "humanoid", description: "A hyena-headed savage with a serrated blade.", special: "pack_hunter" }, mummy_servant: { name: "Mummy Servant", tier: 2, type: "undead", description: "A wrapped, desiccated corpse.", special: "cursed_touch" }, basilisk: { name: "Basilisk", tier: 3, type: "monstrosity", description: "An eight-legged lizard with a petrifying gaze.", special: "paralyze" }, lamia: { name: "Lamia", tier: 3, type: "monstrosity", description: "A woman's torso on a lion's body.", special: "casts_spells" }, mummy_lord: { name: "Mummy Lord", tier: 4, type: "undead", description: "An ancient priest-king entombed for millennia.", special: "casts_spells" }, sphinx: { name: "Sphinx", tier: 4, type: "monstrosity", description: "A majestic winged lion with a riddling mind.", special: "dragon_fear" }, ancient_blue_dragon: { name: "Ancient Blue Dragon", tier: 5, type: "dragon", description: "A massive dragon with lightning crackling between its horns.", special: "breath_weapon" } }); console.log("Desert biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesFey">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ pixie: { name: "Pixie", tier: 1, type: "fey", description: "A tiny glowing winged creature with a mischievous grin.", special: "blind" }, blink_dog: { name: "Blink Dog", tier: 1, type: "fey", description: "An intelligent hound that teleports.", special: "evasion" }, satyr: { name: "Satyr", tier: 2, type: "fey", description: "A horned goat-legged reveler with a panpipe.", special: "stun" }, dryad: { name: "Dryad", tier: 2, type: "fey", description: "A tree-spirit woman bound to her oak.", special: "entangle" }, unicorn_guardian: { name: "Unicorn Guardian", tier: 3, type: "celestial", description: "A white horse with a spiraling horn.", special: "regenerate" }, green_hag: { name: "Green Hag", tier: 3, type: "fey", description: "A warty, green-skinned crone.", special: "casts_spells" }, fey_knight: { name: "Fey Knight", tier: 4, type: "fey", description: "An armored elf lord with a shimmering blade.", special: "parry" }, archfey_emissary: { name: "Archfey Emissary", tier: 4, type: "fey", description: "A radiant, otherworldly being.", special: "casts_spells" }, jabberwock: { name: "Jabberwock", tier: 5, type: "dragon", description: "A chaotic dragon with eyes of flame.", special: "breath_weapon" } }); console.log("Fey Woods biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesForest">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ forest_wolf: { name: "Wolf", tier: 1, type: "beast", description: "A lean grey predator.", special: "pack_hunter" }, goblin_scout: { name: "Goblin Scout", tier: 1, type: "humanoid", description: "A sneaky goblin archer.", special: "trap_user" }, giant_spider: { name: "Giant Spider", tier: 1, type: "beast", description: "A massive arachnid with dripping fangs.", special: "poison_sting" }, hobgoblin_ranger: { name: "Hobgoblin Ranger", tier: 2, type: "humanoid", description: "A disciplined hobgoblin with a heavy crossbow.", special: "high_crit" }, owlbear: { name: "Owlbear", tier: 2, type: "beast", description: "A terrifying hybrid of owl and bear.", special: "fury" }, werewolf: { name: "Werewolf", tier: 2, type: "humanoid", description: "A hulking wolf-man with razor claws.", special: "fury" }, treant: { name: "Treant", tier: 3, type: "plant", description: "An ancient walking tree with massive branches.", special: "regenerate" }, green_hag_coven: { name: "Green Hag Coven", tier: 3, type: "fey", description: "Three cackling hags weaving dark magic.", special: "summon_adds" }, forest_hydra: { name: "Forest Hydra", tier: 4, type: "dragon", description: "A six-headed serpent that grows more heads when wounded.", special: "regenerate" }, ancient_green_dragon: { name: "Ancient Green Dragon", tier: 5, type: "dragon", description: "A colossal emerald dragon whose breath melts flesh from bone.", special: "dragon_fear" } }); console.log("Forest biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesGraveyard">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ skeleton: { name: "Skeleton", tier: 1, type: "undead", description: "Clattering bones animated by dark magic.", special: "vulnerable_blunt" }, zombie: { name: "Zombie", tier: 1, type: "undead", description: "A shambling corpse hungering for flesh.", special: "undead" }, ghoul: { name: "Ghoul", tier: 2, type: "undead", description: "A cannibalistic creature with paralyzing claws.", special: "paralyze" }, specter: { name: "Specter", tier: 2, type: "undead", description: "A wailing spirit that drains life force.", special: "cursed_touch" }, wight: { name: "Wight", tier: 2, type: "undead", description: "A grim warrior in rusted armor.", special: "cursed_touch" }, banshee: { name: "Banshee", tier: 3, type: "undead", description: "A weeping spirit whose scream heralds death.", special: "death_wail" }, vampire_spawn: { name: "Vampire Spawn", tier: 3, type: "undead", description: "A fanged creature bound to its master.", special: "blood_drain" }, death_knight: { name: "Death Knight", tier: 4, type: "undead", description: "A fallen paladin in cursed black armor.", special: "casts_spells" }, dracolich: { name: "Dracolich", tier: 4, type: "undead", description: "An undead dragon with glowing blue eyes.", special: "dragon_fear" }, lich: { name: "Lich", tier: 5, type: "undead", description: "An ancient spellcaster who transcended death.", special: "casts_spells" } }); console.log("Graveyard biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesHaunted">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ possessed_crow: { name: "Possessed Crow", tier: 1, type: "beast", description: "A black-eyed crow with unnatural intelligence.", special: "blind" }, cursed_villager: { name: "Cursed Villager", tier: 1, type: "humanoid", description: "A pale, hollow-eyed villager.", special: "cursed_touch" }, shadow: { name: "Shadow", tier: 2, type: "undead", description: "A living darkness that drains strength.", special: "cursed_touch" }, poltergeist: { name: "Poltergeist", tier: 2, type: "undead", description: "An invisible spirit that hurls objects.", special: "stun" }, witch: { name: "Witch", tier: 3, type: "humanoid", description: "A cackling woman with a cauldron of curses.", special: "casts_spells" }, doppelganger: { name: "Doppelganger", tier: 3, type: "monstrosity", description: "A shapeshifter wearing a stolen face.", special: "high_crit" }, headless_rider: { name: "Headless Rider", tier: 4, type: "undead", description: "A spectral horseman carrying his own skull.", special: "dragon_fear" }, night_hag: { name: "Night Hag", tier: 4, type: "fiend", description: "A grotesque hag who haunts nightmares.", special: "casts_spells" }, archdevil_avatar: { name: "Archdevil Avatar", tier: 5, type: "fiend", description: "A towering horned figure wreathed in hellfire.", special: "breath_weapon" } }); console.log("Haunted Village biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesHellscape">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ imp: { name: "Imp", tier: 1, type: "fiend", description: "A small winged devil with a barbed tail.", special: "poison_sting" }, lemure: { name: "Lemure", tier: 1, type: "fiend", description: "A blob-like tortured soul.", special: "undead" }, hell_hound: { name: "Hell Hound", tier: 2, type: "fiend", description: "A coal-black hound with fiery breath.", special: "breath_weapon" }, cult_fanatic: { name: "Cult Fanatic", tier: 2, type: "humanoid", description: "A zealot with infernal tattoos.", special: "casts_spells" }, barbed_devil: { name: "Barbed Devil", tier: 3, type: "fiend", description: "A spiky, reptilian devil.", special: "hard_shell" }, succubus: { name: "Succubus", tier: 3, type: "fiend", description: "A beautiful winged temptress.", special: "cursed_touch" }, horned_devil: { name: "Horned Devil", tier: 4, type: "fiend", description: "A massive winged devil with a barbed spear.", special: "fury" }, erinyes: { name: "Erinyes", tier: 4, type: "fiend", description: "A fallen angel with a flaming sword.", special: "high_crit" }, pit_fiend: { name: "Pit Fiend", tier: 5, type: "fiend", description: "A towering archdevil with crushing wings.", special: "breath_weapon" } }); console.log("Hellscape biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesJungle">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ poisonous_snake: { name: "Poisonous Snake", tier: 1, type: "beast", description: "A slender green viper hiding in the canopy.", special: "poison_sting" }, velociraptor: { name: "Velociraptor", tier: 1, type: "beast", description: "A feathered, clawed hunter.", special: "pack_hunter" }, giant_ape: { name: "Giant Ape", tier: 2, type: "beast", description: "A massive silverback gorilla.", special: "fury" }, yuan_ti_scout: { name: "Yuan-ti Scout", tier: 2, type: "humanoid", description: "A serpentine humanoid with slit eyes.", special: "poison_sting" }, displacer_beast: { name: "Displacer Beast", tier: 3, type: "monstrosity", description: "A six-legged panther with tentacles.", special: "evasion" }, assassin_vine: { name: "Assassin Vine", tier: 3, type: "plant", description: "A writhing mass of thorned vines.", special: "entangle" }, jungle_hydra: { name: "Jungle Hydra", tier: 4, type: "dragon", description: "A five-headed serpent that regrows heads.", special: "regenerate" }, yuan_ti_abomination: { name: "Yuan-ti Abomination", tier: 4, type: "monstrosity", description: "A serpentine hybrid with a snake's lower body.", special: "casts_spells" }, tyrannosaurus_rex: { name: "Tyrannosaurus Rex", tier: 5, type: "beast", description: "A colossal theropod with bone-crushing jaws.", special: "fearsome_bite" } }); console.log("Jungle biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesMountains">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ mountain_goat: { name: "Mountain Goat", tier: 1, type: "beast", description: "A sure-footed ram with sharp horns.", special: "pack_hunter" }, kobold_miner: { name: "Kobold Miner", tier: 1, type: "humanoid", description: "A scaly reptilian with a pickaxe.", special: "trap_user" }, harpy: { name: "Harpy", tier: 2, type: "monstrosity", description: "A foul bird-woman with a mesmerizing song.", special: "stun" }, bugbear: { name: "Bugbear", tier: 2, type: "humanoid", description: "A hulking, hairy goblinoid.", special: "fury" }, wyvern: { name: "Wyvern", tier: 3, type: "dragon", description: "A two-legged dragon with a venomous stinger.", special: "poison_sting" }, stone_giant: { name: "Stone Giant", tier: 3, type: "giant", description: "A mountainous grey-skinned giant.", special: "hard_shell" }, roc_mountain: { name: "Mountain Roc", tier: 4, type: "beast", description: "A colossal eagle that nests on the highest peaks.", special: "fearsome_bite" }, frost_giant_jarl: { name: "Frost Giant Jarl", tier: 4, type: "giant", description: "A frost giant chieftain with a frozen axe.", special: "fury" }, ancient_mountain_dragon: { name: "Ancient Red Dragon", tier: 5, type: "dragon", description: "A colossal crimson dragon ruling from the highest peak.", special: "breath_weapon" } }); console.log("Mountains biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesNoble">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ noble_duelist: { name: "Noble Duelist", tier: 1, type: "humanoid", description: "A foppish noble with a rapier.", special: "high_crit" }, spy: { name: "Court Spy", tier: 1, type: "humanoid", description: "A shadowy figure in the crowd.", special: "poison_sting" }, enchanted_servant: { name: "Enchanted Servant", tier: 2, type: "construct", description: "A magically animated butler.", special: "construct" }, court_mage: { name: "Court Mage", tier: 2, type: "humanoid", description: "The royal spellcaster.", special: "casts_spells" }, noble_vampire: { name: "Vampire Spawn", tier: 3, type: "undead", description: "A pale noble with an invite to dinner.", special: "blood_drain" }, succubus_diplomat: { name: "Succubus Diplomat", tier: 3, type: "fiend", description: "A beautiful diplomat with hidden motives.", special: "cursed_touch" }, noble_vampire_lord: { name: "Vampire Lord", tier: 4, type: "undead", description: "An ancient vampire who controls the nobility.", special: "summon_adds" }, rakshasa: { name: "Rakshasa", tier: 4, type: "fiend", description: "A tiger-headed noble with backwards hands.", special: "casts_spells" }, dragon_monarch: { name: "Dragon Monarch", tier: 5, type: "dragon", description: "A dragon in human form ruling the kingdom.", special: "breath_weapon" } }); console.log("Noble District biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesRoad">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ bandit: { name: "Bandit", tier: 1, type: "humanoid", description: "A highwayman with a rusty blade.", special: "uses_potions" }, wild_dog: { name: "Wild Dog", tier: 1, type: "beast", description: "A mangy feral canine.", special: "pack_hunter" }, highway_archer: { name: "Highway Archer", tier: 2, type: "humanoid", description: "A hooded bandit with a longbow.", special: "high_crit" }, orc_raider: { name: "Orc Raider", tier: 2, type: "humanoid", description: "A brutish orc with a jagged axe.", special: "fury" }, ogre_mercenary: { name: "Ogre Mercenary", tier: 3, type: "giant", description: "A club-wielding brute in scrap armor.", special: "fury" }, veteran_knight: { name: "Veteran Knight", tier: 3, type: "humanoid", description: "A scarred warrior in dented plate armor.", special: "parry" }, chimera: { name: "Chimera", tier: 3, type: "monstrosity", description: "A three-headed horror with lion, goat, and dragon heads.", special: "breath_weapon" }, war_mage: { name: "War Mage", tier: 4, type: "humanoid", description: "A robed spellcaster wreathed in arcane energy.", special: "casts_spells" }, roc: { name: "Roc", tier: 4, type: "beast", description: "A legendary bird whose shadow blots out the sun.", special: "fearsome_bite" }, adult_red_dragon: { name: "Adult Red Dragon", tier: 5, type: "dragon", description: "A crimson-scaled dragon that terrorizes the countryside.", special: "breath_weapon" } }); console.log("Road biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesRuins">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ giant_rat: { name: "Giant Rat", tier: 1, type: "beast", description: "A diseased rodent with yellowed fangs.", special: "disease" }, cultist: { name: "Cultist", tier: 1, type: "humanoid", description: "A robed fanatic muttering dark prayers.", special: "casts_spells" }, animated_armor: { name: "Animated Armor", tier: 2, type: "construct", description: "Self-moving armor that clanks ominously.", special: "construct" }, gargoyle: { name: "Gargoyle", tier: 2, type: "construct", description: "A stone creature that springs to life.", special: "hard_shell" }, dark_knight: { name: "Dark Knight", tier: 3, type: "humanoid", description: "A cursed warrior sworn to an ancient evil.", special: "cursed_touch" }, hell_hound: { name: "Hell Hound", tier: 3, type: "fiend", description: "A coal-black hound with smoldering eyes.", special: "breath_weapon" }, bone_golem: { name: "Bone Golem", tier: 4, type: "construct", description: "A towering construct of fused skeletons.", special: "fury" }, vampire_lord: { name: "Vampire Lord", tier: 4, type: "undead", description: "An ancient vampire who rules a dark domain.", special: "summon_adds" }, death_tyrant: { name: "Death Tyrant", tier: 5, type: "undead", description: "A floating beholder skull with deadly eye rays.", special: "eye_rays" } }); console.log("Ruins biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesSewers">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ sewer_rat: { name: "Giant Rat", tier: 1, type: "beast", description: "A mangy rodent with yellow fangs.", special: "disease" }, sewer_ooze: { name: "Sewer Ooze", tier: 1, type: "ooze", description: "A quivering blob of toxic waste.", special: "acid" }, wererat: { name: "Wererat", tier: 2, type: "humanoid", description: "A rat-like figure in tattered clothes.", special: "disease" }, sewer_ghoul: { name: "Sewer Ghoul", tier: 2, type: "undead", description: "A corpse-eater lurking in the filth.", special: "paralyze" }, otyugh: { name: "Otyugh", tier: 3, type: "aberration", description: "A three-legged horror with tentacles.", special: "disease" }, sewer_troll: { name: "Sewer Troll", tier: 3, type: "giant", description: "A regenerating brute that feeds on refuse.", special: "regenerate" }, sewer_hydra: { name: "Sewer Hydra", tier: 4, type: "dragon", description: "A multi-headed serpent living in the depths.", special: "regenerate" }, mind_flayer_hideout: { name: "Mind Flayer", tier: 4, type: "aberration", description: "A psychic horror in a hidden lair.", special: "mind_blast" }, elder_brain_colony: { name: "Elder Brain Colony", tier: 5, type: "aberration", description: "A massive pulsating brain controlling the underworld.", special: "mind_blast" } }); console.log("Sewers biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesShip">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ pirate_sailor: { name: "Pirate Sailor", tier: 1, type: "humanoid", description: "A sunburned sailor with a cutlass.", special: "pack_hunter" }, bandit_captain: { name: "Bandit Captain", tier: 1, type: "humanoid", description: "A scarred leader with a hook hand.", special: "uses_potions" }, swashbuckler: { name: "Swashbuckler", tier: 2, type: "humanoid", description: "A dashing duelist with a rapier.", special: "high_crit" }, berserker_raider: { name: "Berserker Raider", tier: 2, type: "humanoid", description: "A frothing warrior with twin axes.", special: "fury" }, war_priest: { name: "War Priest", tier: 3, type: "humanoid", description: "A holy warrior of the sea god.", special: "casts_spells" }, sea_mage: { name: "Sea Mage", tier: 3, type: "humanoid", description: "A spellcaster who commands storms.", special: "casts_spells" }, champion_corsair: { name: "Champion Corsair", tier: 4, type: "humanoid", description: "A legendary pirate with a magical cutlass.", special: "parry" }, dread_pirate: { name: "Dread Pirate", tier: 4, type: "undead", description: "A cursed captain with a skeletal crew.", special: "dragon_fear" }, pirate_king: { name: "Pirate King", tier: 5, type: "humanoid", description: "Ruler of the high seas with a fleet of ghost ships.", special: "breath_weapon" } }); console.log("Ship biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesSwamp">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ giant_frog: { name: "Giant Frog", tier: 1, type: "beast", description: "A bulbous amphibian with a sticky tongue.", special: null }, swamp_leech: { name: "Swamp Leech", tier: 1, type: "beast", description: "A blood-sucking worm that clings to flesh.", special: "blood_drain" }, lizardfolk_hunter: { name: "Lizardfolk Hunter", tier: 2, type: "humanoid", description: "A scaly reptilian with a bone spear.", special: "pack_hunter" }, crocodile: { name: "Crocodile", tier: 2, type: "beast", description: "A massive reptile with bone-crushing jaws.", special: "fury" }, ghoul_mirewalker: { name: "Ghoul Mirewalker", tier: 2, type: "undead", description: "A bloated corpse that rises from the murky waters.", special: "paralyze" }, swamp_troll: { name: "Swamp Troll", tier: 3, type: "giant", description: "A regenerating brute covered in algae and warts.", special: "regenerate" }, black_hag: { name: "Black Hag", tier: 3, type: "fey", description: "A withered crone who curses all who enter her domain.", special: "casts_spells" }, froghemoth: { name: "Froghemoth", tier: 4, type: "monstrosity", description: "A tentacled frog-beast the size of a cottage.", special: "fury" }, ancient_black_dragon: { name: "Ancient Black Dragon", tier: 5, type: "dragon", description: "A vile dragon with acid-dripping fangs and tattered wings.", special: "breath_weapon" } }); console.log("Swamp biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadEnemiesTemple">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies({ temple_snake: { name: "Temple Guardian Snake", tier: 1, type: "beast", description: "A golden-scaled serpent coiled around a pillar.", special: "poison_sting" }, acolyte: { name: "Acolyte", tier: 1, type: "humanoid", description: "A robed initiate burning incense.", special: "casts_spells" }, animated_statue: { name: "Animated Statue", tier: 2, type: "construct", description: "A stone warrior that springs to life.", special: "construct" }, shadow_priest: { name: "Shadow Priest", tier: 2, type: "humanoid", description: "A dark-robed cleric.", special: "casts_spells" }, minotaur: { name: "Minotaur", tier: 3, type: "monstrosity", description: "A bull-headed brute with a greataxe.", special: "charge" }, gorgon: { name: "Gorgon", tier: 3, type: "monstrosity", description: "A bull-like creature with iron scales.", special: "paralyze" }, medusa: { name: "Medusa", tier: 4, type: "monstrosity", description: "A woman with snakes for hair.", special: "paralyze" }, guardian_naga: { name: "Guardian Naga", tier: 4, type: "monstrosity", description: "A hooded serpent guardian.", special: "casts_spells" }, avatar_god: { name: "Avatar of a Forgotten God", tier: 5, type: "celestial", description: "A towering figure of starlight and ancient power.", special: "mind_blast" } }); console.log("Temple biome enemies loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadSpecialAbilities">> <<script>> window.setup = window.setup || {}; window.setup.specialAbilities = { roll: function(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }, applyEnemyAbility: function(enemy, trigger) { if (trigger === undefined) trigger = "on_damage"; let ability = enemy.special; let result = { log: "", enemyHeal: 0, stunPlayer: false, additionalPlayerDamage: 0, missNextTurn: false }; if (!ability) return result; switch(ability) { case "trap_user": if (this.roll(1, 100) <= 30) { result.additionalPlayerDamage = this.roll(8, 18); result.log += '<span style="color:#ffaa44;">⚠️ ' + enemy.name + ' throws a net! You take ' + result.additionalPlayerDamage + ' damage!⚠️</span><br>'; } break; case "uses_potions": if (enemy.hp < enemy.maxHp * 0.5 && this.roll(1, 100) <= 45) { let healAmount = this.roll(20, 40); enemy.hp = Math.min(enemy.maxHp, enemy.hp + healAmount); result.enemyHeal = healAmount; result.log += '<span style="color:#ff8888;">💊 ' + enemy.name + ' drinks a healing potion! Restored ' + healAmount + ' HP!💊</span><br>'; } break; case "high_crit": break; case "casts_spells": if (this.roll(1, 100) <= 35) { let playerLevel = State.variables.level || 1; let minDamage = 6 + Math.floor(playerLevel / 2); let maxDamage = 12 + Math.floor(playerLevel * 1.5); let spellDamage = this.roll(minDamage, maxDamage); result.additionalPlayerDamage = spellDamage; result.log += '<span style="color:#aa88ff;">🔮 ' + enemy.name + ' casts Shadow Bolt! Deals ' + spellDamage + ' damage!🔮</span><br>'; } break; case "parry": break; case "fury": if (enemy.hp < enemy.maxHp * 0.35 && !enemy.furyActive) { enemy.strength = Math.floor(enemy.strength * 1.6); enemy.furyActive = true; result.log += '<span style="color:#ff4444;">💢 ' + enemy.name + ' enters a FURY! Strength increased!💢</span><br>'; } break; case "enrage": if (enemy.hp < enemy.maxHp * 0.5 && !enemy.enraged) { enemy.strength += 12; enemy.defense += 4; enemy.enraged = true; result.log += '<span style="color:#ff4444;">😡 ' + enemy.name + ' ENRAGES! Strength and Defense surge!😡</span><br>'; } break; case "disease": if (this.roll(1, 100) <= 25) { result.log += '<span style="color:#88ff88;">🦠 ' + enemy.name + ' inflicts disease! You feel weaker!🦠</span><br>'; State.variables.strength = Math.max(5, State.variables.strength - 2); State.variables.agility = Math.max(5, State.variables.agility - 2); } break; case "pack_tactics": break; case "fearsome_bite": if (this.roll(1, 100) <= 30) { result.stunPlayer = true; result.log += '<span style="color:#ff4444;">🐺 ' + enemy.name + ' bites you! You lose your turn!🐺</span><br>'; } break; case "hard_shell": break; case "blood_drain": if (this.roll(1, 100) <= 35) { let drainAmount = this.roll(8, 20); result.additionalPlayerDamage = drainAmount; enemy.hp = Math.min(enemy.maxHp, enemy.hp + Math.floor(drainAmount / 2)); result.log += '<span style="color:#ff6666;">🩸 ' + enemy.name + ' drains your blood! Deals ' + drainAmount + ' damage!🩸</span><br>'; } break; case "poison_sting": if (this.roll(1, 100) <= 35) { let poisonDamage = this.roll(4, 12); result.additionalPlayerDamage = poisonDamage; State.variables.poisoned = true; State.variables.poisonValue = 5; State.variables.poisonTurns = 3; result.log += '<span style="color:#88ff88;">🐍 ' + enemy.name + ' stings you with venom! Deals ' + poisonDamage + ' damage and poisons you!</span><br>'; } break; case "venom": if (this.roll(1, 100) <= 30) { State.variables.poisoned = true; State.variables.poisonValue = 4; State.variables.poisonTurns = 3; result.log += '<span style="color:#88ff88;">🐍 Venom seeps into your veins! You take damage over time!🐍</span><br>'; } break; case "charge": if (!enemy.charged) { enemy.strength += 5; enemy.charged = true; result.log += '<span style="color:#ffaa44;">💨 ' + enemy.name + ' CHARGES! Next attack stronger!💨</span><br>'; } break; case "undead": break; case "vulnerable_blunt": break; case "incorporeal": if (this.roll(1, 100) <= 35) { result.log += '<span style="color:#aaaaff;">👻 ' + enemy.name + ' phases through your attack! Missed!👻</span><br>'; } break; case "paralyze": if (this.roll(1, 100) <= 25) { result.stunPlayer = true; result.log += '<span style="color:#ffaa44;">⚡ ' + enemy.name + ' paralyzes you! You are frozen!⚡</span><br>'; } break; case "camouflage": if (this.roll(1, 100) <= 30) { enemy.evasion = true; result.log += '<span style="color:#88ff88;">🌿 ' + enemy.name + ' blends in! Harder to hit!🌿</span><br>'; } break; case "entangle": if (this.roll(1, 100) <= 30) { result.stunPlayer = true; result.log += '<span style="color:#88ff88;">🌱 Vines entangle you! You are stuck!🌱</span><br>'; } break; case "spike_shot": if (this.roll(1, 100) <= 35) { let spikeDamage = this.roll(10, 22); result.additionalPlayerDamage = spikeDamage; result.log += '<span style="color:#ffaa44;">🌵 ' + enemy.name + ' fires needles! Deals ' + spikeDamage + ' damage!🌵</span><br>'; } break; case "explode": break; case "blind": if (this.roll(1, 100) <= 30) { result.missNextTurn = true; result.log += '<span style="color:#88ff88;">💨 Mud blinds you! Your accuracy drops!💨</span><br>'; } break; case "cloud": if (this.roll(1, 100) <= 30) { result.log += '<span style="color:#88aaff;">☁️ Smoke cloud obscures vision!☁️</span><br>'; } break; case "split": break; case "acid": let acidDamage = this.roll(8, 18); result.additionalPlayerDamage = acidDamage; result.log += '<span style="color:#88ff88;">💚 Acid splashes you! Deals ' + acidDamage + ' damage!💚</span><br>'; break; case "construct": break; case "terrifying": if (this.roll(1, 100) <= 30) { result.stunPlayer = true; result.log += '<span style="color:#ffaa44;">😨 ' + enemy.name + ' terrifies you! You hesitate!😨</span><br>'; } break; case "darkness": if (this.roll(1, 100) <= 30) { result.log += '<span style="color:#aa88ff;">🌑 Magical darkness surrounds ' + enemy.name + '!🌑</span><br>'; enemy.evasion = true; } break; case "adhesive": if (this.roll(1, 100) <= 35) { result.log += '<span style="color:#88aaff;">🫧 Your weapon sticks to ' + enemy.name + '!🫧</span><br>'; } break; case "regenerate": if (trigger === "on_turn_start" && enemy.hp < enemy.maxHp) { if (this.roll(1, 100) <= 40) { let healAmount = this.roll(8, 18); enemy.hp = Math.min(enemy.maxHp, enemy.hp + healAmount); result.enemyHeal = healAmount; result.log += '<span style="color:#88ff88;">🌿 ' + enemy.name + ' regenerates ' + healAmount + ' HP!🌿</span><br>'; } } break; case "dragon_fear": if (this.roll(1, 100) <= 30) { result.stunPlayer = true; result.log += '<span style="color:#ff4444;">🐉 ' + enemy.name + ' ROARS! You are frozen in fear!🐉</span><br>'; } break; case "breath_weapon": if (this.roll(1, 100) <= 35) { let playerLevel = State.variables.level || 1; let minDamage = 15 + Math.floor(playerLevel / 2); let maxDamage = 25 + Math.floor(playerLevel * 1.2); let breathDamage = this.roll(minDamage, maxDamage); result.additionalPlayerDamage = breathDamage; result.log += '<span style="color:#ff8844;">🐲 ' + enemy.name + ' unleashes BREATH WEAPON! Deals ' + breathDamage + ' damage!🐲</span><br>'; } break; case "eye_rays": if (this.roll(1, 100) <= 40) { let rayDamage = this.roll(8, 22); result.additionalPlayerDamage = rayDamage; result.log += '<span style="color:#aa44ff;">👁️ ' + enemy.name + ' fires eye rays! Deals ' + rayDamage + ' damage!👁️</span><br>'; } break; case "mind_blast": if (this.roll(1, 100) <= 30) { let psychicDamage = this.roll(12, 24); result.additionalPlayerDamage = psychicDamage; if (this.roll(1, 100) <= 25) { result.stunPlayer = true; result.log += '<span style="color:#aa88ff;">🧠 ' + enemy.name + ' blasts your mind for ' + psychicDamage + ' damage and STUNS you!🧠</span><br>'; } else { result.log += '<span style="color:#aa88ff;">🧠 ' + enemy.name + ' blasts your mind for ' + psychicDamage + ' damage!🧠</span><br>'; } } break; case "summon_adds": if (!enemy.hasSummoned && trigger === "on_damage" && enemy.hp < enemy.maxHp * 0.7) { enemy.hasSummoned = true; result.log += '<span style="color:#ffaa44;">🕯️ ' + enemy.name + ' summons minions to aid them!🕯️</span><br>'; } break; case "confuse": if (this.roll(1, 100) <= 25) { State.variables.playerConfused = true; result.log += '<span style="color:#aa88ff;">🌀 ' + enemy.name + ' confuses you! You may hurt yourself or waste your turn!🌀</span><br>'; } break; case "evasion": if (this.roll(1, 100) <= 25) { enemy.evasionActive = true; result.log += '<span style="color:#88aaff;">✨ ' + enemy.name + ' phases out of reality! Harder to hit!✨</span><br>'; } break; case "pack_hunter": break; case "cursed_touch": if (this.roll(1, 100) <= 30) { let curseDamage = this.roll(5, 15); result.additionalPlayerDamage = curseDamage; result.log += '<span style="color:#aa88ff;">👻 ' + enemy.name + ' touches you with a cursed hand! Deals ' + curseDamage + ' damage!</span><br>'; } break; case "death_wail": if (this.roll(1, 100) <= 25) { let wailDamage = this.roll(10, 25); result.additionalPlayerDamage = wailDamage; result.stunPlayer = true; result.log += '<span style="color:#ff44ff;">😭 ' + enemy.name + ' lets out a DEATH WAIL! Deals ' + wailDamage + ' damage and stuns you!</span><br>'; } break; } return result; }, onDeathAbility: function(enemy) { let ability = enemy.special; let result = { log: "", playerDamage: 0 }; if (ability === "explode") { let playerLevel = State.variables.level || 1; let minExplode = 8 + Math.floor(playerLevel / 2); let maxExplode = 18 + Math.floor(playerLevel * 1.5); let explosionDamage = this.roll(minExplode, maxExplode); result.playerDamage = explosionDamage; result.log += '<span style="color:#ff4444;">💥 ' + enemy.name + ' EXPLODES! You take ' + explosionDamage + ' damage!💥</span><br>'; } if (ability === "split" && !enemy.split) { enemy.split = true; result.log += '<span style="color:#88ff88;">🫧 ' + enemy.name + ' splits into two smaller slimes!🫧</span><br>'; } return result; }, applyTurnStartAbility: function(enemy) { let result = { log: "", enemyHeal: 0 }; if (enemy.special === "regenerate") { let regenResult = this.applyEnemyAbility(enemy, "on_turn_start"); result.log += regenResult.log; result.enemyHeal = regenResult.enemyHeal; } return result; }, getPackBonus: function(enemy, allyCount) { if (enemy.special === "pack_tactics" && allyCount > 0) { let bonus = 3 * Math.min(allyCount, 3); return { attackBonus: bonus, message: '🐺 Pack tactics! +' + bonus + ' damage!🐺' }; } if (enemy.special === "pack_hunter" && allyCount > 0) { let bonus = 2 * Math.min(allyCount, 5); return { attackBonus: bonus, message: '🐾 Pack hunter! +' + bonus + ' damage!🐾' }; } return { attackBonus: 0, message: "" }; }, applyParry: function(enemy, damage) { if (enemy.special === "parry" && this.roll(1, 100) <= 25) { let reducedDamage = Math.floor(damage * 0.6); return { damage: reducedDamage, message: '<span style="color:#88aaff;">🛡️ ' + enemy.name + ' parries! Damage reduced!🛡️</span><br>' }; } return { damage: damage, message: "" }; }, checkEvasion: function(enemy) { if (enemy.special === "evasion" && enemy.evasionActive) { if (this.roll(1, 100) <= 25) { return { dodged: true, message: '<span style="color:#88aaff;">✨ ' + enemy.name + ' phases through your attack!✨</span><br>' }; } } if (enemy.evasion === true) { if (this.roll(1, 100) <= 30) { return { dodged: true, message: '<span style="color:#88aaff;">🌫️ Your attack misses ' + enemy.name + '!🌫️</span><br>' }; } } return { dodged: false, message: "" }; }, checkConfused: function(playerConfused) { if (playerConfused && this.roll(1, 100) <= 50) { return { confused: true, message: '<span style="color:#aa88ff;">🌀 You are confused and stumble instead of attacking!🌀</span><br>' }; } return { confused: false, message: "" }; } }; console.log("✅ SpecialAbilities module loaded - COMPLETE with all enemy abilities"); <</script>> <</widget>> <</nobr>>
:: AddItem [widget] <<nobr>> <<widget "AddItem">> <!-- Add item to inventory (with safety) --> <<set $inventory[$args[0]] = ($inventory[$args[0]] || 0) + 1>> <!-- Optional: Show feedback message --> <<if $args[2]>> <<set $tempMessage = "You received: " + $args[0] + "!">> <</if>> <!-- Navigate to specified passage, or back, or stay --> <<if $args[1] && $args[1] != "">> <<goto $args[1]>> <<elseif $returnTo && $returnTo != "">> <<set $temp = $returnTo>> <<set $returnTo = "">> <<goto $temp>> <<else>> <<goto "GridCell">> <</if>> <</widget>> <</nobr>>
:: ApplyPremadeCharacter [widget] <<nobr>> <<widget "ApplyPremadeCharacter">> <<include $args[0]>> <!-- Apply class bonuses to base stats --> <<set $baseStrength = $baseStrength + ($classStrengthBonus || 0)>> <<set $baseConstitution = $baseConstitution + ($classConstitutionBonus || 0)>> <<set $baseDefense = $baseDefense + ($classDefenseBonus || 0)>> <<set $baseAgility = $baseAgility + ($classAgilityBonus || 0)>> <<set $baseIntellect = $baseIntellect + ($classIntellectBonus || 0)>> <!-- Calculate HP based on Constitution --> <<set $baseMaxHP = 30 + ($baseConstitution * 2)>> <!-- Apply final stats --> <<set $strength = $baseStrength>> <<set $constitution = $baseConstitution>> <<set $defense = $baseDefense>> <<set $agility = $baseAgility>> <<set $intellect = $baseIntellect>> <<set $maxHP = $baseMaxHP>> <<set $currentHP = $maxHP>> <<set $maxMP = 20 + Math.floor($intellect / 2)>> <<set $currentMP = $maxMP>> <!-- Store base values for equipment calculations --> <<set $baseStrength = $strength>> <<set $baseConstitution = $constitution>> <<set $baseDefense = $defense>> <<set $baseAgility = $agility>> <<set $baseIntellect = $intellect>> <<set $baseMaxHP = $maxHP>> <!-- Initialize inventory if needed --> <<if !$inventory>> <<set $inventory = {}>> <</if>> <!-- Add starting equipment --> <<if $startingWeapon>> <<set $inventory[$startingWeapon] = ($inventory[$startingWeapon] || 0) + 1>> <</if>> <<if $startingArmor>> <<set $inventory[$startingArmor] = ($inventory[$startingArmor] || 0) + 1>> <</if>> <<if $startingHelmet>> <<set $inventory[$startingHelmet] = ($inventory[$startingHelmet] || 0) + 1>> <</if>> <<if $startingBoots>> <<set $inventory[$startingBoots] = ($inventory[$startingBoots] || 0) + 1>> <</if>> <!-- Add gold --> <<set $gold = ($gold || 0) + ($startingGold || 0)>> <!-- Add potions --> <<if $startingPotions>> <<for $potion, $qty in $startingPotions>> <<set $inventory[$potion] = ($inventory[$potion] || 0) + $qty>> <</for>> <</if>> <!-- Set spellbook --> <<set $spellbook = $spellbook || []>> <!-- Reset equipment slots --> <<set $equipment = $equipment || {}>> <<set $equipment.weapon = "fists">> <<set $equipment.armor = "rags">> <<set $equipment.helmet = null>> <<set $equipment.boots = null>> <<set $equipment.ring = null>> <<set $equipment.amulet = null>> <!-- Clear combat state --> <<set $combatState = null>> <<set $inCombat = false>> <<set $enemyList = []>> <<set $currentEnemy = null>> <!-- Go to LoadingWorld (same as CharacterCreation) --> <<goto "LoadingWorld">> <</widget>> <</nobr>>
:: ConsoleStats [nobr] <<nobr>> <div class="fancy-header">📊 CONSOLE STATISTICS 📊</div> <div style="background: rgba(0,0,0,0.5); border-radius: 15px; padding: 20px; margin: 15px 0; text-align: center;"> <p>Click the button below to output biome statistics to the console (F12).</p> <p>This won't affect your game - just open the console to see the numbers.</p> <<link "📊 Output Stats to Console">> <<script>> var vars = State.variables; var stats = {}; var total = vars.gridExists.length; for (var i = 0; i < total; i++) { var key = vars.gridExists[i]; var type = vars.gridBiomes[key].type; stats[type] = (stats[type] || 0) + 1; } console.clear(); console.log("%c================== BIOME STATISTICS ==================", "color: #ffd700; font-size: 14px"); console.log("Total cells: " + total); console.log(""); var sorted = Object.keys(stats).sort(function(a, b) { return stats[b] - stats[a]; }); for (var s = 0; s < sorted.length; s++) { var type = sorted[s]; var count = stats[type]; var percent = (count / total * 100).toFixed(2); console.log(" " + type + ": " + count + " (" + percent + "%)"); } var waterCount = stats["water"] || 0; var waterPercent = (waterCount / total * 100).toFixed(2); console.log(""); console.log("%c💧 WATER: " + waterPercent + "% (" + waterCount + " cells)", "color: #88aaff"); if (waterPercent < 15) console.log("%c⚠️ Water is too low. Lower the noise1 threshold (0.5 → 0.4)", "color: #ff8888"); else if (waterPercent > 25) console.log("%c⚠️ Water is too high. Raise the noise1 threshold (0.5 → 0.6)", "color: #ff8888"); else console.log("%c✅ Water balance is good!", "color: #88ff88"); console.log("%c========================================================", "color: #ffd700"); <</script>> <span style="color: #88ff88;">✓ Check console (F12)</span> <</link>> <div style="margin-top: 20px;"> <<link "🗺️ Return to Map">> <<goto "GridCell">> <</link>> </div> </div> <</nobr>>
<<nobr>> <<widget "campMenu">> <div class="camp-menu"> <<link "⛺ Rest at Camp">> <<goto "Sleep Menu">> <</link>> </div> <</widget>> <</nobr>>
:: CharacterCreation [nobr] <<nobr>> <div class="fancy-header">✨ CREATE YOUR HERO ✨</div> <!-- Basic Info Card --> <div style="background: rgba(0,0,0,0.4); border-radius: 15px; padding: 20px; margin-bottom: 20px; border-left: 4px solid #d4af37;"> <div style="font-family: 'Cinzel', serif; color: #d4af37; font-size: 18px; margin-bottom: 15px;">📜 BASIC INFORMATION</div> <div style="display: flex; flex-wrap: wrap; gap: 20px; justify-content: space-between;"> <div style="flex: 1;"> <div style="margin-bottom: 10px;"> <span style="color: #aaa;">Name:</span><br> <<textbox "$playerName" $playerName "Enter name..." style="width: 100%; padding: 8px; background: #1a1a2e; border: 1px solid #d4af37; border-radius: 8px; color: #ffd700;">> </div> <div> <span style="color: #aaa;">Age:</span><br> <<textbox "$playerAge" $playerAge "Age" style="width: 100%; padding: 8px; background: #1a1a2e; border: 1px solid #d4af37; border-radius: 8px; color: #ffd700;">> </div> </div> <div style="flex: 1;"> <div style="margin-bottom: 10px;"> <span style="color: #aaa;">Sex:</span><br> <div style="display: flex; gap: 10px; margin-top: 5px;"> <<link "Male">><<set $playerSex = "Male">><<goto "CharacterCreation">><</link>> <<link "Female">><<set $playerSex = "Female">><<goto "CharacterCreation">><</link>> <<link "Non-binary">><<set $playerSex = "Non-binary">><<goto "CharacterCreation">><</link>> </div> <<if $playerSex>><div style="margin-top: 8px; color: #88ff88;">✓ Selected: $playerSex</div><</if>> </div> </div> </div> </div> <!-- Race Selection --> <div style="background: rgba(0,0,0,0.4); border-radius: 15px; padding: 20px; margin-bottom: 20px; border-left: 4px solid #88aaff;"> <div style="font-family: 'Cinzel', serif; color: #88aaff; font-size: 18px; margin-bottom: 15px;">🏔️ CHOOSE YOUR RACE</div> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px;"> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 12px; text-align: center; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.backgroundColor='rgba(136,170,255,0.2)'" onmouseout="this.style.backgroundColor='rgba(0,0,0,0.3)'"> <<link "👤 Human">><<include "Race_Human">><<goto "CharacterCreation">><</link>> <div style="font-size: 11px; color: #aaa; margin-top: 5px;">Balanced stats</div> <<if $playerRace == "Human">><div style="color: #ffd700; font-size: 12px; margin-top: 5px;">✓ Selected</div><</if>> </div> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 12px; text-align: center; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.backgroundColor='rgba(136,170,255,0.2)'" onmouseout="this.style.backgroundColor='rgba(0,0,0,0.3)'"> <<link "🧝 Elf">><<include "Race_Elf">><<goto "CharacterCreation">><</link>> <div style="font-size: 11px; color: #aaa; margin-top: 5px;">+2 AGI, +1 INT, -1 STR, -2 CON</div> <<if $playerRace == "Elf">><div style="color: #ffd700; font-size: 12px; margin-top: 5px;">✓ Selected</div><</if>> </div> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 12px; text-align: center; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.backgroundColor='rgba(136,170,255,0.2)'" onmouseout="this.style.backgroundColor='rgba(0,0,0,0.3)'"> <<link "⛏️ Dwarf">><<include "Race_Dwarf">><<goto "CharacterCreation">><</link>> <div style="font-size: 11px; color: #aaa; margin-top: 5px;">+1 STR, +2 CON, +1 DEF, -2 AGI</div> <<if $playerRace == "Dwarf">><div style="color: #ffd700; font-size: 12px; margin-top: 5px;">✓ Selected</div><</if>> </div> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 12px; text-align: center; cursor: pointer; transition: all 0.2s;" onmouseover="this.style.backgroundColor='rgba(136,170,255,0.2)'" onmouseout="this.style.backgroundColor='rgba(0,0,0,0.3)'"> <<link "💪 Orc">><<include "Race_Orc">><<goto "CharacterCreation">><</link>> <div style="font-size: 11px; color: #aaa; margin-top: 5px;">+2 STR, +1 CON, +1 DEF, -3 AGI, -2 INT</div> <<if $playerRace == "Orc">><div style="color: #ffd700; font-size: 12px; margin-top: 5px;">✓ Selected</div><</if>> </div> </div> </div> <!-- Class Selection --> <div style="background: rgba(0,0,0,0.4); border-radius: 15px; padding: 20px; margin-bottom: 20px; border-left: 4px solid #ffaa88;"> <div style="font-family: 'Cinzel', serif; color: #ffaa88; font-size: 18px; margin-bottom: 15px;">⚔️ CHOOSE YOUR CLASS</div> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 12px;"> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 12px;" onmouseover="this.style.backgroundColor='rgba(255,170,136,0.15)'" onmouseout="this.style.backgroundColor='rgba(0,0,0,0.3)'"> <<link "🛡️ Warrior">><<include "Class_Warrior">><<goto "CharacterCreation">><</link>> <div style="font-size: 11px; color: #aaa;">+2 STR, +3 CON, +2 DEF, +15 HP</div> <<if $playerClass == "Warrior">><div style="color: #ffd700; font-size: 12px; margin-top: 5px;">✓ Selected</div><</if>> </div> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 12px;" onmouseover="this.style.backgroundColor='rgba(255,170,136,0.15)'" onmouseout="this.style.backgroundColor='rgba(0,0,0,0.3)'"> <<link "🗡️ Rogue">><<include "Class_Rogue">><<goto "CharacterCreation">><</link>> <div style="font-size: 11px; color: #aaa;">+1 STR, +3 AGI, +1 INT, +8 HP, +50 gold</div> <<if $playerClass == "Rogue">><div style="color: #ffd700; font-size: 12px; margin-top: 5px;">✓ Selected</div><</if>> </div> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 12px;" onmouseover="this.style.backgroundColor='rgba(255,170,136,0.15)'" onmouseout="this.style.backgroundColor='rgba(0,0,0,0.3)'"> <<link "🔮 Mage">><<include "Class_Mage">><<goto "CharacterCreation">><</link>> <div style="font-size: 11px; color: #aaa;">+4 INT, +1 AGI, +1 CON, +5 HP, +10 MP, spells</div> <<if $playerClass == "Mage">><div style="color: #ffd700; font-size: 12px; margin-top: 5px;">✓ Selected</div><</if>> </div> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 12px;" onmouseover="this.style.backgroundColor='rgba(255,170,136,0.15)'" onmouseout="this.style.backgroundColor='rgba(0,0,0,0.3)'"> <<link "🏹 Ranger">><<include "Class_Ranger">><<goto "CharacterCreation">><</link>> <div style="font-size: 11px; color: #aaa;">+2 STR, +2 AGI, +2 CON, +1 DEF, +1 INT, +10 HP</div> <<if $playerClass == "Ranger">><div style="color: #ffd700; font-size: 12px; margin-top: 5px;">✓ Selected</div><</if>> </div> </div> </div> <!-- World Size Selection --> <div style="background: rgba(0,0,0,0.4); border-radius: 15px; padding: 20px; margin-bottom: 20px; border-left: 4px solid #88ff88;"> <div style="font-family: 'Cinzel', serif; color: #88ff88; font-size: 18px; margin-bottom: 15px;">🗺️ WORLD SIZE</div> <div style="margin-bottom: 10px; color: #aaa;">Choose the size of your world. Larger worlds have more to explore but may impact performance.</div> <div style="display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin: 15px 0;"> <<link "🟢 Small">> <<set $worldRadius = 20>> <<set $worldSizeName = "Small">> <<goto "CharacterCreation">> <</link>> <<link "🟡 Medium">> <<set $worldRadius = 30>> <<set $worldSizeName = "Medium">> <<goto "CharacterCreation">> <</link>> <<link "🟠 Large">> <<set $worldRadius = 45>> <<set $worldSizeName = "Large">> <<goto "CharacterCreation">> <</link>> <<link "🔴 Massive">> <<set $worldRadius = 60>> <<set $worldSizeName = "Massive">> <<goto "CharacterCreation">> <</link>> </div> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 12px;"> <div style="display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px; font-size: 12px;"> <span>🟢 Small: ~1,200 cells</span> <span>🟡 Medium: ~2,800 cells</span> <span>🟠 Large: ~6,300 cells</span> <span>🔴 Massive: ~11,300 cells</span> </div> <div style="margin-top: 10px; text-align: center;"> <span style="color: #ffd700;">Current: $worldSizeName (radius $worldRadius)</span> <<if $worldRadius == 60>> <div style="color: #ff8888; font-size: 11px; margin-top: 5px;">⚠️ Massive worlds may have slower performance</div> <</if>> </div> </div> </div> <!-- Character Preview --> <div style="background: rgba(0,0,0,0.5); border-radius: 15px; padding: 20px; margin-bottom: 20px; border: 1px solid #d4af37;"> <div style="text-align: center; font-family: 'Cinzel', serif; color: #d4af37; font-size: 18px; margin-bottom: 15px;">📊 CHARACTER PREVIEW</div> <div style="display: flex; justify-content: space-between; flex-wrap: wrap; gap: 20px;"> <div> <div><span style="color: #aaa;">Name:</span> <span style="color: #ffd700;"><<= $playerName || "???" >></span></div> <div><span style="color: #aaa;">Age:</span> <span style="color: #ffd700;"><<= $playerAge || "???" >></span></div> <div><span style="color: #aaa;">Sex:</span> <span style="color: #ffd700;"><<= $playerSex || "???" >></span></div> <div><span style="color: #aaa;">Race:</span> <span style="color: #ffd700;"><<= $playerRace || "???" >></span></div> <div><span style="color: #aaa;">Class:</span> <span style="color: #ffd700;"><<= $playerClass || "???" >></span></div> </div> <div> <div><span style="color: #aaa;">World Size:</span> <span style="color: #ffd700;"><<= $worldSizeName || "Medium" >> (radius <<= $worldRadius || 30 >>)</span></div> <div><span style="color: #aaa;">💪 STR:</span> <span style="color: #ffd700;"><<= $baseStrength + ($classStrengthBonus || 0) || "?" >></span></div> <div><span style="color: #aaa;">❤️ CON:</span> <span style="color: #ffd700;"><<= $baseConstitution + ($classConstitutionBonus || 0) || "?" >></span></div> <div><span style="color: #aaa;">🛡️ DEF:</span> <span style="color: #ffd700;"><<= $baseDefense + ($classDefenseBonus || 0) || "?" >></span></div> <div><span style="color: #aaa;">⚡ AGI:</span> <span style="color: #ffd700;"><<= $baseAgility + ($classAgilityBonus || 0) || "?" >></span></div> <div><span style="color: #aaa;">🧠 INT:</span> <span style="color: #ffd700;"><<= $baseIntellect + ($classIntellectBonus || 0) || "?" >></span></div> <div><span style="color: #aaa;">❤️ HP:</span> <span style="color: #ffd700;"><<= 30 + (($baseConstitution + ($classConstitutionBonus || 0)) * 2) || "?" >></span></div> </div> </div> </div> <!-- Start Button --> <div style="text-align: center;"> <<if $playerName && $playerName != "" && $playerAge && $playerAge != "" && $playerSex && $playerRace && $playerClass>> <<link "⚔️ BEGIN ADVENTURE ⚔️" style="font-size: 18px; padding: 12px 30px;">> // Apply Race + Class bonuses <<set $baseStrength = $baseStrength + ($classStrengthBonus || 0)>> <<set $baseConstitution = $baseConstitution + ($classConstitutionBonus || 0)>> <<set $baseDefense = $baseDefense + ($classDefenseBonus || 0)>> <<set $baseAgility = $baseAgility + ($classAgilityBonus || 0)>> <<set $baseIntellect = $baseIntellect + ($classIntellectBonus || 0)>> // Add starting equipment <<set $inventory[$startingWeapon] = $inventory[$startingWeapon] + 1>> <<set $inventory[$startingArmor] = $inventory[$startingArmor] + 1>> <<set $inventory[$startingHelmet] = $inventory[$startingHelmet] + 1>> <<set $inventory[$startingBoots] = $inventory[$startingBoots] + 1>> <<set $gold = $gold + $startingGold>> // Add starting potions <<for $potion, $qty in $startingPotions>> <<set $inventory[$potion] = $inventory[$potion] + $qty>> <</for>> // Calculate HP from CON <<set $baseMaxHP = 30 + ($baseConstitution * 2)>> // Apply final stats <<set $strength = $baseStrength>> <<set $constitution = $baseConstitution>> <<set $defense = $baseDefense>> <<set $agility = $baseAgility>> <<set $intellect = $baseIntellect>> <<set $maxHP = $baseMaxHP>> <<set $currentHP = $maxHP>> <<set $maxMP = 20 + Math.floor($intellect / 2)>> <<set $currentMP = $maxMP>> // Store base values <<set $baseStrength = $strength>> <<set $baseConstitution = $constitution>> <<set $baseDefense = $defense>> <<set $baseAgility = $agility>> <<set $baseIntellect = $intellect>> <<set $baseMaxHP = $maxHP>> // Reset equipment <<set $equipment.weapon = "fists">> <<set $equipment.armor = "rags">> <<set $equipment.helmet = null>> <<set $equipment.boots = null>> <<set $equipment.ring = null>> <<set $equipment.amulet = null>> <<goto "LoadingWorld">> <</link>> <<else>> <div style="background: rgba(255,100,100,0.2); border: 1px solid #ff6666; border-radius: 10px; padding: 15px;"> <span style="color: #ff8888;">⚠️ Please complete all fields before starting your adventure! ⚠️</span> </div> <</if>> </div> <</nobr>>
:: CharacterSheet [nobr] <<nobr>> <div class="fancy-header">📜 Character Sheet 📜</div> <div style="background: rgba(0,0,0,0.5); border-radius: 15px; padding: 20px; margin: 15px 0; text-align: center;"> <div style="font-size: 48px; margin-bottom: 10px;"> <<if $playerClass == "Warrior">>⚔️ <<elseif $playerClass == "Rogue">>🗡️ <<elseif $playerClass == "Mage">>🔮 <<elseif $playerClass == "Ranger">>🏹 <<else>>✨<</if>> </div> <div style="font-size: 28px; color: #ffd700; font-family: 'Cinzel', serif;"> $playerName </div> <div style="color: #aaa; margin-top: 5px;"> $playerRace · $playerClass · $playerSex · Age $playerAge </div> </div> <!-- Core Stats - Two Column Layout --> <div style="display: flex; flex-wrap: wrap; gap: 15px; margin-bottom: 15px;"> <!-- Left Column - Health & Mana --> <div style="flex: 1; background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px;"> <div style="font-family: 'Cinzel', serif; color: #88ff88; font-size: 16px; margin-bottom: 10px; border-bottom: 1px solid #88ff88; display: inline-block;">❤️ HEALTH</div> <div style="font-size: 24px; font-weight: bold; margin: 10px 0;">$currentHP / $maxHP</div> <div style="width: 100%; background: #333; border-radius: 10px; overflow: hidden;"> <div style="width: <<= ($currentHP / $maxHP) * 100>>%; background: linear-gradient(90deg, #ff4444, #ff8888); height: 8px;"></div> </div> <div style="font-family: 'Cinzel', serif; color: #88aaff; font-size: 16px; margin: 15px 0 10px 0; border-bottom: 1px solid #88aaff; display: inline-block;">💙 MANA</div> <div style="font-size: 24px; font-weight: bold; margin: 10px 0;">$currentMP / $maxMP</div> <div style="width: 100%; background: #333; border-radius: 10px; overflow: hidden;"> <div style="width: <<= ($currentMP / $maxMP) * 100>>%; background: linear-gradient(90deg, #4488ff, #88aaff); height: 8px;"></div> </div> </div> <!-- Right Column - Combat Stats --> <div style="flex: 1; background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px;"> <div style="font-family: 'Cinzel', serif; color: #ffaa88; font-size: 16px; margin-bottom: 10px; border-bottom: 1px solid #ffaa88; display: inline-block;">⚔️ COMBAT STATS</div> <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px;"> <div><span style="color: #ff8888;">💪 STR</span><br><span style="font-size: 20px; font-weight: bold;">$strength</span></div> <div><span style="color: #ff8888;">❤️ CON</span><br><span style="font-size: 20px; font-weight: bold;">$constitution</span></div> <div><span style="color: #88ff88;">🛡️ DEF</span><br><span style="font-size: 20px; font-weight: bold;">$defense</span></div> <div><span style="color: #ffff88;">⚡ AGI</span><br><span style="font-size: 20px; font-weight: bold;">$agility</span></div> <div><span style="color: #88aaff;">🧠 INT</span><br><span style="font-size: 20px; font-weight: bold;">$intellect</span></div> </div> </div> </div> <!-- Equipment Section - Dynamic --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #d4af37; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #d4af37; display: inline-block;">🗡️ EQUIPMENT</div> <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin-top: 10px;"> <!-- Weapon --> <<set $itemId = $equipment.weapon>> <<set $item = $itemId ? window.setup.items[$itemId] : null>> <<set $displayName = "None">> <<if !$itemId || $itemId == "fists">><<set $displayName = "Fists">> <<elseif $item>><<set $displayName = $item.name>><</if>> <div><span style="color: #ffaa88;">🗡️ Weapon:</span> $displayName</div> <!-- Armor --> <<set $itemId = $equipment.armor>> <<set $item = $itemId ? window.setup.items[$itemId] : null>> <<set $displayName = "None">> <<if !$itemId || $itemId == "rags">><<set $displayName = "Rags">> <<elseif $item>><<set $displayName = $item.name>><</if>> <div><span style="color: #ffaa88;">🛡️ Armor:</span> $displayName</div> <!-- Helmet --> <<set $itemId = $equipment.helmet>> <<set $item = $itemId ? window.setup.items[$itemId] : null>> <<set $displayName = $item ? $item.name : "None">> <div><span style="color: #ffaa88;">🪖 Helmet:</span> $displayName</div> <!-- Boots --> <<set $itemId = $equipment.boots>> <<set $item = $itemId ? window.setup.items[$itemId] : null>> <<set $displayName = $item ? $item.name : "None">> <div><span style="color: #ffaa88;">👢 Boots:</span> $displayName</div> <!-- Ring --> <<set $itemId = $equipment.ring>> <<set $item = $itemId ? window.setup.items[$itemId] : null>> <<set $displayName = $item ? $item.name : "None">> <div><span style="color: #ffaa88;">💍 Ring:</span> $displayName</div> <!-- Amulet --> <<set $itemId = $equipment.amulet>> <<set $item = $itemId ? window.setup.items[$itemId] : null>> <<set $displayName = $item ? $item.name : "None">> <div><span style="color: #ffaa88;">📿 Amulet:</span> $displayName</div> </div> </div> <!-- Equipment Stats Breakdown - Dynamic --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #ffaa88; font-size: 16px; margin-bottom: 10px; border-bottom: 1px solid #ffaa88; display: inline-block;">🔧 EQUIPMENT STATS</div> <div style="font-size: 12px; color: #aaa; margin-bottom: 10px;">Stats granted by equipped gear:</div> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 8px;"> <!-- Weapon --> <<set $itemId = $equipment.weapon>> <<set $item = $itemId ? window.setup.items[$itemId] : null>> <<if $item && $item.id != "fists">> <div style="background: rgba(0,0,0,0.2); border-radius: 10px; padding: 8px;"> <span style="color: #d4af37;">🗡️ $item.name</span><br> <span style="font-size: 11px;"> <<if $item.damage>>⚔️ DMG +$item.damage <</if>> <<if $item.strength>>💪 STR +$item.strength <</if>> <<if $item.agility>>⚡ AGI +$item.agility <</if>> <<if $item.intellect>>🧠 INT +$item.intellect <</if>> <<if $item.constitution>>❤️ CON +$item.constitution <</if>> <<if $item.defense>>🛡️ DEF +$item.defense <</if>> <<if $item.critical>>💀 CRIT +$item.critical% <</if>> <<if $item.lifesteal>>💚 Lifesteal $item.lifesteal% <</if>> <<if $item.poison>>💚 Poison $item.poison <</if>> <<if $item.burn>>🔥 Burn $item.burn <</if>> <<if $item.maxMP>>💙 Max MP +$item.maxMP <</if>> </span> </div> <</if>> <!-- Armor --> <<set $itemId = $equipment.armor>> <<set $item = $itemId ? window.setup.items[$itemId] : null>> <<if $item && $item.id != "rags">> <div style="background: rgba(0,0,0,0.2); border-radius: 10px; padding: 8px;"> <span style="color: #d4af37;">🛡️ $item.name</span><br> <span style="font-size: 11px;"> <<if $item.damage>>⚔️ DMG +$item.damage <</if>> <<if $item.strength>>💪 STR +$item.strength <</if>> <<if $item.agility>>⚡ AGI +$item.agility <</if>> <<if $item.intellect>>🧠 INT +$item.intellect <</if>> <<if $item.constitution>>❤️ CON +$item.constitution <</if>> <<if $item.defense>>🛡️ DEF +$item.defense <</if>> <<if $item.critical>>💀 CRIT +$item.critical% <</if>> <<if $item.lifesteal>>💚 Lifesteal $item.lifesteal% <</if>> <<if $item.poison>>💚 Poison $item.poison <</if>> <<if $item.burn>>🔥 Burn $item.burn <</if>> <<if $item.maxMP>>💙 Max MP +$item.maxMP <</if>> </span> </div> <</if>> <!-- Helmet --> <<set $itemId = $equipment.helmet>> <<set $item = $itemId ? window.setup.items[$itemId] : null>> <<if $item>> <div style="background: rgba(0,0,0,0.2); border-radius: 10px; padding: 8px;"> <span style="color: #d4af37;">🪖 $item.name</span><br> <span style="font-size: 11px;"> <<if $item.damage>>⚔️ DMG +$item.damage <</if>> <<if $item.strength>>💪 STR +$item.strength <</if>> <<if $item.agility>>⚡ AGI +$item.agility <</if>> <<if $item.intellect>>🧠 INT +$item.intellect <</if>> <<if $item.constitution>>❤️ CON +$item.constitution <</if>> <<if $item.defense>>🛡️ DEF +$item.defense <</if>> <<if $item.critical>>💀 CRIT +$item.critical% <</if>> <<if $item.lifesteal>>💚 Lifesteal $item.lifesteal% <</if>> <<if $item.poison>>💚 Poison $item.poison <</if>> <<if $item.burn>>🔥 Burn $item.burn <</if>> <<if $item.maxMP>>💙 Max MP +$item.maxMP <</if>> </span> </div> <</if>> <!-- Boots --> <<set $itemId = $equipment.boots>> <<set $item = $itemId ? window.setup.items[$itemId] : null>> <<if $item>> <div style="background: rgba(0,0,0,0.2); border-radius: 10px; padding: 8px;"> <span style="color: #d4af37;">👢 $item.name</span><br> <span style="font-size: 11px;"> <<if $item.damage>>⚔️ DMG +$item.damage <</if>> <<if $item.strength>>💪 STR +$item.strength <</if>> <<if $item.agility>>⚡ AGI +$item.agility <</if>> <<if $item.intellect>>🧠 INT +$item.intellect <</if>> <<if $item.constitution>>❤️ CON +$item.constitution <</if>> <<if $item.defense>>🛡️ DEF +$item.defense <</if>> <<if $item.critical>>💀 CRIT +$item.critical% <</if>> <<if $item.lifesteal>>💚 Lifesteal $item.lifesteal% <</if>> <<if $item.poison>>💚 Poison $item.poison <</if>> <<if $item.burn>>🔥 Burn $item.burn <</if>> <<if $item.maxMP>>💙 Max MP +$item.maxMP <</if>> </span> </div> <</if>> <!-- Ring --> <<set $itemId = $equipment.ring>> <<set $item = $itemId ? window.setup.items[$itemId] : null>> <<if $item>> <div style="background: rgba(0,0,0,0.2); border-radius: 10px; padding: 8px;"> <span style="color: #d4af37;">💍 $item.name</span><br> <span style="font-size: 11px;"> <<if $item.damage>>⚔️ DMG +$item.damage <</if>> <<if $item.strength>>💪 STR +$item.strength <</if>> <<if $item.agility>>⚡ AGI +$item.agility <</if>> <<if $item.intellect>>🧠 INT +$item.intellect <</if>> <<if $item.constitution>>❤️ CON +$item.constitution <</if>> <<if $item.defense>>🛡️ DEF +$item.defense <</if>> <<if $item.critical>>💀 CRIT +$item.critical% <</if>> <<if $item.lifesteal>>💚 Lifesteal $item.lifesteal% <</if>> <<if $item.poison>>💚 Poison $item.poison <</if>> <<if $item.burn>>🔥 Burn $item.burn <</if>> <<if $item.maxMP>>💙 Max MP +$item.maxMP <</if>> </span> </div> <</if>> <!-- Amulet --> <<set $itemId = $equipment.amulet>> <<set $item = $itemId ? window.setup.items[$itemId] : null>> <<if $item>> <div style="background: rgba(0,0,0,0.2); border-radius: 10px; padding: 8px;"> <span style="color: #d4af37;">📿 $item.name</span><br> <span style="font-size: 11px;"> <<if $item.damage>>⚔️ DMG +$item.damage <</if>> <<if $item.strength>>💪 STR +$item.strength <</if>> <<if $item.agility>>⚡ AGI +$item.agility <</if>> <<if $item.intellect>>🧠 INT +$item.intellect <</if>> <<if $item.constitution>>❤️ CON +$item.constitution <</if>> <<if $item.defense>>🛡️ DEF +$item.defense <</if>> <<if $item.critical>>💀 CRIT +$item.critical% <</if>> <<if $item.lifesteal>>💚 Lifesteal $item.lifesteal% <</if>> <<if $item.poison>>💚 Poison $item.poison <</if>> <<if $item.burn>>🔥 Burn $item.burn <</if>> <<if $item.maxMP>>💙 Max MP +$item.maxMP <</if>> </span> </div> <</if>> <<if (!$equipment.weapon || $equipment.weapon == "fists") && (!$equipment.armor || $equipment.armor == "rags") && !$equipment.helmet && !$equipment.boots && !$equipment.ring && !$equipment.amulet>> <div style="text-align: center; padding: 10px; color: #888;">No equipment with stats equipped</div> <</if>> </div> </div> <!-- Stat Breakdown - Dynamic --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #88aaff; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #88aaff; display: inline-block;">📊 STAT BREAKDOWN</div> <!-- Strength --> <<set $bonus = $strength - $baseStrength>> <div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.1);"> <div style="display: flex; align-items: center; gap: 10px;"> <span style="font-size: 20px;">💪</span> <span style="font-weight: bold;">Strength</span> </div> <div style="display: flex; gap: 20px;"> <span>Base: <span style="color: #aaa;">$baseStrength</span></span> <<if $bonus > 0>><span>+ Equipment: <span style="color: #88ff88;">+$bonus</span></span> <<elseif $bonus < 0>><span>+ Equipment: <span style="color: #ff8888;">$bonus</span></span> <<else>><span>+ Equipment: <span style="color: #aaa;">+0</span></span><</if>> <span style="font-weight: bold; min-width: 60px; text-align: right;">Total: $strength</span> </div> </div> <!-- Constitution --> <<set $bonus = $constitution - $baseConstitution>> <div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.1);"> <div style="display: flex; align-items: center; gap: 10px;"> <span style="font-size: 20px;">❤️</span> <span style="font-weight: bold;">Constitution</span> </div> <div style="display: flex; gap: 20px;"> <span>Base: <span style="color: #aaa;">$baseConstitution</span></span> <<if $bonus > 0>><span>+ Equipment: <span style="color: #88ff88;">+$bonus</span></span> <<elseif $bonus < 0>><span>+ Equipment: <span style="color: #ff8888;">$bonus</span></span> <<else>><span>+ Equipment: <span style="color: #aaa;">+0</span></span><</if>> <span style="font-weight: bold; min-width: 60px; text-align: right;">Total: $constitution</span> </div> </div> <!-- Defense --> <<set $bonus = $defense - $baseDefense>> <div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.1);"> <div style="display: flex; align-items: center; gap: 10px;"> <span style="font-size: 20px;">🛡️</span> <span style="font-weight: bold;">Defense</span> </div> <div style="display: flex; gap: 20px;"> <span>Base: <span style="color: #aaa;">$baseDefense</span></span> <<if $bonus > 0>><span>+ Equipment: <span style="color: #88ff88;">+$bonus</span></span> <<elseif $bonus < 0>><span>+ Equipment: <span style="color: #ff8888;">$bonus</span></span> <<else>><span>+ Equipment: <span style="color: #aaa;">+0</span></span><</if>> <span style="font-weight: bold; min-width: 60px; text-align: right;">Total: $defense</span> </div> </div> <!-- Agility --> <<set $bonus = $agility - $baseAgility>> <div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.1);"> <div style="display: flex; align-items: center; gap: 10px;"> <span style="font-size: 20px;">⚡</span> <span style="font-weight: bold;">Agility</span> </div> <div style="display: flex; gap: 20px;"> <span>Base: <span style="color: #aaa;">$baseAgility</span></span> <<if $bonus > 0>><span>+ Equipment: <span style="color: #88ff88;">+$bonus</span></span> <<elseif $bonus < 0>><span>+ Equipment: <span style="color: #ff8888;">$bonus</span></span> <<else>><span>+ Equipment: <span style="color: #aaa;">+0</span></span><</if>> <span style="font-weight: bold; min-width: 60px; text-align: right;">Total: $agility</span> </div> </div> <!-- Intellect --> <<set $bonus = $intellect - $baseIntellect>> <div style="display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.1);"> <div style="display: flex; align-items: center; gap: 10px;"> <span style="font-size: 20px;">🧠</span> <span style="font-weight: bold;">Intellect</span> </div> <div style="display: flex; gap: 20px;"> <span>Base: <span style="color: #aaa;">$baseIntellect</span></span> <<if $bonus > 0>><span>+ Equipment: <span style="color: #88ff88;">+$bonus</span></span> <<elseif $bonus < 0>><span>+ Equipment: <span style="color: #ff8888;">$bonus</span></span> <<else>><span>+ Equipment: <span style="color: #aaa;">+0</span></span><</if>> <span style="font-weight: bold; min-width: 60px; text-align: right;">Total: $intellect</span> </div> </div> </div> <!-- Progression Section --> <div style="display: flex; flex-wrap: wrap; gap: 15px; margin-bottom: 15px;"> <div style="flex: 1; background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px;"> <div style="font-family: 'Cinzel', serif; color: #ffd700; font-size: 16px; margin-bottom: 10px; border-bottom: 1px solid #ffd700; display: inline-block;">⭐ PROGRESSION</div> <div style="font-size: 20px; font-weight: bold; margin: 10px 0;">Level $level</div> <div>Experience: $exp / $nextLevelExp</div> <div style="width: 100%; background: #333; border-radius: 10px; overflow: hidden; margin-top: 5px;"> <div style="width: <<= ($exp / $nextLevelExp) * 100>>%; background: linear-gradient(90deg, #d4af37, #ffd700); height: 6px;"></div> </div> <div style="margin-top: 5px; font-size: 12px; color: #aaa;">Next Level: $nextLevelExp EXP needed</div> </div> <div style="flex: 1; background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px;"> <div style="font-family: 'Cinzel', serif; color: #ffd700; font-size: 16px; margin-bottom: 10px; border-bottom: 1px solid #ffd700; display: inline-block;">💰 FINANCES</div> <div style="font-size: 28px; font-weight: bold; margin: 10px 0; color: #ffd700;">$gold</div> <div style="font-size: 12px; color: #aaa;">gold pieces</div> </div> </div> <!-- Close Button --> <div style="text-align: center; margin-top: 20px;"> <<if $returnTo && $returnTo != "CharacterSheet">> <<link "❌ Close Character Sheet">> <<set $temp = $returnTo>> <<set $returnTo = "">> <<if $temp == "GridCell">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $coord = $gridX + "," + $gridY>> <</if>> <<goto $temp>> <</link>> <<else>> <<link "🗺️ Return to Map">> <<set $returnTo = "">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $coord = $gridX + "," + $gridY>> <<goto "GridCell">> <</link>> <</if>> </div> <</nobr>>
<<nobr>> <<set $playerClass = "Mage">> <<set $classChoice = "mage">> <<set $classStrengthBonus = 0>> <<set $classConstitutionBonus = 1>> <<set $classDefenseBonus = 0>> <<set $classAgilityBonus = 1>> <<set $classIntellectBonus = 4>> <<set $startingWeapon = "gnarled_staff">> <<set $startingArmor = "frayed_robe">> <<set $startingHelmet = "worn_hood">> <<set $startingBoots = "threadbare_boots">> <<set $startingGold = 25>> <<set $startingPotions = { minor_health_potion: 2, minor_mana_potion: 2 }>> <<set $spellbook = ["emberwake", "tide_lash", "stonegrasp", "gale_needle", "veillight", "soulkindle"]>> <</nobr>>
<<nobr>> <<set $playerClass = "Ranger">> <<set $classChoice = "ranger">> <<set $classStrengthBonus = 2>> <<set $classConstitutionBonus = 2>> <<set $classDefenseBonus = 1>> <<set $classAgilityBonus = 2>> <<set $classIntellectBonus = 1>> <<set $startingWeapon = "makeshift_bow">> <<set $startingArmor = "patchy_hide">> <<set $startingHelmet = "leather_cap">> <<set $startingBoots = "sturdy_boots">> <<set $startingGold = 25>> <<set $startingPotions = { minor_health_potion: 3 }>> <<set $spellbook = []>> <</nobr>>
<<nobr>> <<set $playerClass = "Rogue">> <<set $classChoice = "rogue">> <<set $classStrengthBonus = 1>> <<set $classConstitutionBonus = 1>> <<set $classDefenseBonus = 0>> <<set $classAgilityBonus = 3>> <<set $classIntellectBonus = 1>> <<set $startingWeapon = "tarnished_dagger">> <<set $startingArmor = "tattered_leather">> <<set $startingHelmet = "cloth_mask">> <<set $startingBoots = "soft_slippers">> <<set $startingGold = 50>> <<set $startingPotions = { minor_health_potion: 3 }>> <<set $spellbook = []>> <</nobr>>
<<nobr>> <<set $playerClass = "Warrior">> <<set $classChoice = "warrior">> <<set $classStrengthBonus = 2>> <<set $classConstitutionBonus = 3>> <<set $classDefenseBonus = 2>> <<set $classAgilityBonus = 0>> <<set $classIntellectBonus = 0>> <<set $startingWeapon = "broken_shortsword">> <<set $startingArmor = "worn_chainmail">> <<set $startingHelmet = "rusty_helm">> <<set $startingBoots = "iron_toed_boots">> <<set $startingGold = 0>> <<set $startingPotions = { minor_health_potion: 3 }>> <<set $spellbook = []>> <</nobr>>
:: Combat_Actions [nobr] <nobr> <div class="combat-actions"> <<if not $playerStunned>> <!-- Confusion Check - can still act but may fail --> <<if $playerConfused>> <<set _confusedRoll = random(1, 100)>> <<if _confusedRoll <= 40>> <!-- Confused and hurts self --> <<set $combatActions++>> <<set _selfDamage = random(5, 15)>> <<set $currentHP = $currentHP - _selfDamage>> <<set $combatLogText = $combatLogText + "<br><span style='color:#ffaa44;'>🌀 You are confused and hurt yourself for " + _selfDamage + " damage! 🌀</span><br>">> <<set $playerConfused = false>> <!-- Enemy still counterattacks --> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>> <<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">> <</if>> <<if $currentHP <= 0>> <<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 You have been defeated... 💀</span><br>">> <<set $combatState = "death">> <</if>> <<run window.setup.updateSidebar()>> <<goto "Combat_Display">> <<elseif _confusedRoll <= 70>> <!-- Confused and wastes turn --> <<set $combatActions++>> <<set $combatLogText = $combatLogText + "<br><span style='color:#ffaa44;'>🌀 You are confused and stumble around, wasting your turn! 🌀</span><br>">> <<set $playerConfused = false>> <!-- Enemy counterattacks --> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>> <<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">> <</if>> <<if $currentHP <= 0>> <<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 You have been defeated... 💀</span><br>">> <<set $combatState = "death">> <</if>> <<run window.setup.updateSidebar()>> <<goto "Combat_Display">> <<else>> <!-- Confused but shakes it off and can act normally --> <<set $combatLogText = $combatLogText + "<br><span style='color:#88ff88;'>🌀 You shake off the confusion! 🌀</span><br>">> <<set $playerConfused = false>> <</if>> <</if>> <!-- Normal Actions --> <<if not $playerConfused>> <<link "⚔️ Attack">> <<set $combatActions++>> <<set $critInfo = window.setup.combat.calculateCrit(15)>> <<set $attackResult = window.setup.combat.calculatePlayerDamage($currentEnemy, $critInfo.multiplier)>> <<set $playerDamage = $attackResult.damage>> <<set $playerRawDamage = $attackResult.rawDamage || $playerDamage>> <<set $playerWeaponEffects = $attackResult.weaponEffects || {}>> <<if not $attackResult.hit>> <<set $combatLogText = $combatLogText + "<br><span style='color:#ff8888;'>MISS! (" + $attackResult.roll + "% vs " + $attackResult.hitChance + "% chance)</span><br>">> <<if $attackResult.evasionMessage>><<set $combatLogText = $combatLogText + $attackResult.evasionMessage>><</if>> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>> <<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">> <</if>> <<if $currentHP <= 0>> <<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>You have been defeated...</span><br>">> <<set $combatState = "death">> <</if>> <<run window.setup.updateSidebar()>> <<goto "Combat_Display">> <<else>> <<if $attackResult.parryMessage>><<set $combatLogText = $combatLogText + $attackResult.parryMessage>><</if>> <<set $combatResult = window.setup.combat.processCombatRound($currentEnemy, $playerDamage, $allyCount, { weaponEffects: $playerWeaponEffects, rawDamage: $playerRawDamage })>> <<set $combatLogText = $combatLogText + "<br>" + ($critInfo.isCrit ? "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀 </span>" : "") + "You deal " + $combatResult.playerDamage + " damage! (" + $attackResult.roll + "% vs " + $attackResult.hitChance + "% chance)<br>">> <<set $combatLogText = $combatLogText + $combatResult.combatLog>> <<if $combatResult.enemyHeal > 0>> <<set $combatLogText = $combatLogText + "<span style='color:#88ff88;'>💚 " + $currentEnemy.name + " healed " + $combatResult.enemyHeal + " HP!</span><br>">> <</if>> <<if $combatResult.summonedMinions && $combatResult.summonedMinions.length > 0>> <<for _i=0; _i < $combatResult.summonedMinions.length; _i++>> <<set $enemyList.push($combatResult.summonedMinions[$_i])>> <</for>> <<set $combatLogText = $combatLogText + "<span style='color:#ffaa44;'>⚠️ " + $combatResult.summonedMinions.length + " minion(s) join the fight! ⚠️</span><br>">> <</if>> <<if $combatResult.enemyDied>> <<set $enemyList = $enemyList.filter((_, idx) => idx !== $currentEnemyIndex)>> <<if $enemyList.length > 0>> <<set $combatLogText = $combatLogText + "<span style='color:#88ff88;'>✅ " + $currentEnemy.name + " defeated! " + $enemyList.length + " enemy(s) remain.</span><br>">> <<set $currentEnemyIndex = 0>> <<set $currentEnemy = $enemyList[0]>> <<goto "Combat_Display">> <<else>> <<set $totalExp = 0>> <<set $totalGold = 0>> <<for _i=0; _i < $originalEnemyList.length; _i++>> <<if $originalEnemyList[$_i] && !$originalEnemyList[$_i].isMinion>> <<set $totalExp = $totalExp + ($originalEnemyList[$_i].exp || 0)>> <<set _goldMin = $originalEnemyList[$_i].gold ? $originalEnemyList[$_i].gold.min : 5>> <<set _goldMax = $originalEnemyList[$_i].gold ? $originalEnemyList[$_i].gold.max : 15>> <<set $totalGold = $totalGold + Math.floor(Math.random() * (_goldMax - _goldMin + 1) + _goldMin)>> <</if>> <</for>> <<if window.setup.exp && window.setup.exp.gain>> <<set $rewardsExpGain = window.setup.exp.gain($totalExp)>> <<else>> <<set $exp = $exp + $totalExp>> <<set $rewardsExpGain = false>> <</if>> <<set $gold = $gold + $totalGold>> <<set $combatLogText = $combatLogText + "<span style='color:#ffd700;'>🏆 VICTORY! You gain " + $totalExp + " exp and " + $totalGold + " gold! 🏆</span><br>">> <<set $combatState = "victory">> <<set $timeSpent = $combatActions * 0.25>> <<run window.setup.time.advance($timeSpent)>> <<set $combatLogText = $combatLogText + "<span style='color:#88ff88;'>⏰ Time passed: " + $timeSpent + " hours (" + $combatActions + " actions)</span><br>">> <</if>> <</if>> <<if $combatResult.playerDied>> <<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 You have been defeated... 💀</span><br>">> <<set $combatState = "death">> <</if>> <<set $playerStunned = $combatResult.stunPlayer>> <<set $playerConfused = $combatResult.confused || false>> <<run window.setup.updateSidebar()>> <<goto "Combat_Display">> <</if>> <</link>> <<link "💊 Use Potion">> <<set $selectingPotion = true>> <<goto "Combat_Display">> <</link>> <<link "🔮 Cast Spell">> <<set $castingSpell = "select">> <<goto "Combat_Display">> <</link>> <<link "🏃 Flee">> <<if $fleeCooldown > 0>> <<set $combatLogText = $combatLogText + "<br><span style='color:#ff8888;'>😰 Too exhausted to flee! Wait " + $fleeCooldown + " more turns. 😰</span><br>">> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>> <<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">> <</if>> <<if $currentHP <= 0>> <<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 You have been defeated... 💀</span><br>">> <<set $combatState = "death">> <</if>> <<set $fleeCooldown = $fleeCooldown - 1>> <<run window.setup.updateSidebar()>> <<goto "Combat_Display">> <<else>> <<set $combatActions++>> <<set $fleeRoll = random(1, 100)>> <<set $fleeChance = 50 + ($agility / 2)>> <<if $fleeRoll <= $fleeChance>> <<set $timeSpent = $combatActions * 0.25>> <<run window.setup.time.advance($timeSpent)>> <<set $combatLogText = $combatLogText + "<br><span style='color:#88ff88;'>🏃 You successfully flee! 🏃</span><br>">> <<set $combatState = 0>> <<set $combatLogText = "">> <<set $combatActions = 0>> <<set $castingSpell = null>> <<set $selectingPotion = null>> <<set $playerStunned = false>> <<set $playerConfused = false>> <<set $isTestBattle = false>> <<set $enemyList = []>> <<set $currentEnemy = null>> <<set $fleeCooldown = 0>> <<run window.setup.updateSidebar()>> <<goto "GridCell">> <<else>> <<set $fleeCooldown = $fleeCooldownDuration>> <<set $combatLogText = $combatLogText + "<br><span style='color:#ff8888;'>😰 Failed to flee! You're exhausted (" + $fleeCooldownDuration + " turn cooldown)! 😰</span><br>">> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>> <<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">> <</if>> <<if $currentHP <= 0>> <<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 You have been defeated... 💀</span><br>">> <<set $combatState = "death">> <</if>> <<run window.setup.updateSidebar()>> <<goto "Combat_Display">> <</if>> <</if>> <</link>> <</if>> <<else>> <div style="text-align: center; padding: 10px; color: #ffaa44;"> 😵 You are stunned and cannot act this turn! </div> <<link "⏩ End Turn">> <<set $combatActions++>> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<set $combatLogText = $combatLogText + "<br>" + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <<if $enemyDamageResult.isCrit>><<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">><</if>> <<if $enemyDamageResult.packMessage>><<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">><</if>> <<if $currentHP <= 0>> <<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 You have been defeated... 💀</span><br>">> <<set $combatState = "death">> <</if>> <<set $playerStunned = false>> <<run window.setup.updateSidebar()>> <<goto "Combat_Display">> <</link>> <</if>> </div> </nobr>
<<nobr>> <<widget "loadCombatCore">> <<script>> window.setup = window.setup || {}; window.setup.combat = window.setup.combat || {}; window.setup.combat.calculateHitChance = function(enemy) { let playerAGI = State.variables.agility; let enemyAGI = enemy.agility || 8; let chance = 70 + ((playerAGI - enemyAGI) * 1.5); if (enemy.blinded) chance = chance * 0.7; if (State.variables.haste) chance = chance * 1.2; return Math.min(90, Math.max(35, chance)); }; window.setup.combat.checkHit = function(enemy) { let evasionResult = window.setup.specialAbilities.checkEvasion(enemy); if (evasionResult.dodged) { return { hit: false, chance: 0, roll: 0, evasionMessage: evasionResult.message }; } let chance = this.calculateHitChance(enemy); let roll = Math.random() * 100; return { hit: roll <= chance, chance: Math.floor(chance), roll: Math.floor(roll), evasionMessage: "" }; }; window.setup.combat.calculateCrit = function(baseChance = 10) { let agiBonus = Math.floor(State.variables.agility / 12); let weaponBonus = 0; let weaponItem = window.setup.getItem(State.variables.equipment.weapon); if (weaponItem && weaponItem.critical) weaponBonus = weaponItem.critical; let critChance = Math.min(40, baseChance + agiBonus + weaponBonus); let isCrit = Math.random() * 100 <= critChance; return { isCrit: isCrit, multiplier: isCrit ? 1.5 : 1 }; }; console.log("✅ Combat Core loaded"); <</script>> <</widget>> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; margin-bottom: 20px;"> [[✨ Status Effects|03_Combat_StatusEffects]] [[💥 Damage|03_Combat_Damage]] [[🔄 Combat Round|03_Combat_Round]] </div> <</nobr>>
:: Combat_Death <<nobr>> <div id="death-screen" style="animation: fadeIn 1.5s ease-out;"> <div class="game-over">☠️ GAME OVER ☠️</div> <<set $deathPenaltyEnabled = false>> <<set $deathPenaltyPercent = 10>> <<set $deathPenaltyGoldPercent = 15>> <<set $deathQuips = [ "The darkness claims another soul...", "Victory was not meant to be.", "Your legend ends here.", "Even heroes fall sometimes.", "The adventure is over... for now.", "Death is only the beginning.", "Your journey has reached its end.", "The void welcomes you home.", "Fate had other plans.", "Rest now, weary traveler." ]>> <<set $randomQuip = $deathQuips[Math.floor(Math.random() * $deathQuips.length)]>> <div style="background: rgba(0,0,0,0.5); border-radius: 15px; padding: 20px; margin: 15px 0; text-align: center;"> <<if $currentEnemy && $currentEnemy.name>> <div style="font-size: 48px; margin-bottom: 10px; animation: shake 0.5s ease-out;"> <<if $currentEnemy.type == "humanoid">>⚔️ <<elseif $currentEnemy.type == "beast">>🐺 <<elseif $currentEnemy.type == "undead">>💀 <<elseif $currentEnemy.type == "plant">>🌿 <<elseif $currentEnemy.type == "elemental">>🔥 <<elseif $currentEnemy.type == "construct">>🛡️ <<else>>👹<</if>> </div> <div style="font-family: 'Cinzel', serif; color: #ff4444; font-size: 28px; margin-bottom: 10px; animation: slideDown 0.8s ease-out;"> You were slain by a <span style="color: #ff8888;">$currentEnemy.name</span>! </div> <div style="color: #aaa; font-style: italic; margin: 15px 0; padding: 10px; background: rgba(0,0,0,0.3); border-radius: 10px;"> "$currentEnemy.description" </div> <<else>> <div style="font-size: 48px; margin-bottom: 10px;">💀</div> <div style="font-family: 'Cinzel', serif; color: #ff4444; font-size: 28px; margin-bottom: 10px;"> Your journey has ended! </div> <</if>> <<if $deathPenaltyEnabled && ($level > 1 || $gold > 0)>> <<set $xpLoss = 0>> <<set $goldLoss = 0>> <<if $level > 1>> <<set $xpLoss = Math.floor($exp * ($deathPenaltyPercent / 100))>> <<set $exp = Math.max(0, $exp - $xpLoss)>> <</if>> <<set $goldLoss = Math.floor($gold * ($deathPenaltyGoldPercent / 100))>> <<set $gold = Math.max(0, $gold - $goldLoss)>> <<if $xpLoss > 0 || $goldLoss > 0>> <div style="background: rgba(0,0,0,0.4); border-radius: 10px; padding: 10px; margin: 15px 0; animation: pulse 0.5s ease-out;"> <div style="color: #ff8888; font-size: 16px; margin-bottom: 5px;">⚰️ DEATH PENALTY ⚰️</div> <<if $xpLoss > 0>><div>📖 Experience lost: <span style="color: #ff8888;">$xpLoss XP</span></div><</if>> <<if $goldLoss > 0>><div>💰 Gold lost: <span style="color: #ff8888;">$goldLoss gold</span></div><</if>> </div> <</if>> <</if>> <div style="font-style: italic; color: #88aaff; margin: 15px 0; padding: 10px; background: rgba(0,0,0,0.2); border-radius: 8px; animation: fadeInUp 1s ease-out;"> " $randomQuip " </div> <div style="border-top: 1px solid #d4af37; margin: 20px 0;"></div> <div style="font-family: 'Cinzel', serif; color: #d4af37; font-size: 18px; margin-bottom: 15px;">What would you like to do?</div> <div style="display: flex; flex-wrap: wrap; gap: 15px; justify-content: center;"> <<button "🔄 Start New Game">> <<run window.setup.resetGame()>> <<goto "Start">> <</button>> <<button "💾 Load Saved Game">> <<run UI.saves()>> <</button>> <<button "⚔️ Create New Character">> <<run window.setup.resetGame()>> <<goto "CharacterCreation">> <</button>> <<button "🏠 Return to Title">> <<run window.setup.resetGame()>> <<goto "Start">> <</button>> </div> <div style="margin-top: 20px; font-size: 12px; color: #666;"> Your adventure lasted $day days, reaching Level $level </div> </div> </div> <style> @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes fadeInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } @keyframes slideDown { from { opacity: 0; transform: translateY(-50px); } to { opacity: 1; transform: translateY(0); } } @keyframes shake { 0%, 100% { transform: translateX(0); } 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); } 20%, 40%, 60%, 80% { transform: translateX(5px); } } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.02); background: rgba(255,68,68,0.2); } 100% { transform: scale(1); } } </style> <</nobr>>
<<nobr>> <<if $combatState == "victory">> <<goto "Combat_Victory">> <</if>> <<if $combatState == "death">> <<goto "Combat_Death">> <</if>> <!-- Main combat display - uses full combat system --> <div class="combat-status"> <div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap;"> <div> <b>Current Enemy: $currentEnemy.name</b><br> ❤️ <span id="combat-enemy-hp">$currentEnemy.hp</span>/$currentEnemy.maxHp <<if $currentEnemy.special>> <br><span style="font-size: 10px; color: #ffaa88;">⚡ Ability: $currentEnemy.special</span> <</if>> <<if $currentEnemy.agility>> <br><span style="font-size: 10px; color: #aaa;">⚡ AGI: $currentEnemy.agility</span> <</if>> <div style="margin-top: 8px;"> <<if $currentEnemy.burning>><span style="color: #ff8844;">🔥 Burning</span> <</if>> <<if $currentEnemy.poisoned>><span style="color: #88ff88;">💚 Poisoned</span> <</if>> <<if $currentEnemy.frozen>><span style="color: #88aaff;">❄️ Frozen</span> <</if>> <<if $currentEnemy.stunned>><span style="color: #ffaa44;">💫 Stunned</span> <</if>> <<if $currentEnemy.slowed>><span style="color: #aaffaa;">🐢 Slowed</span> <</if>> <<if $currentEnemy.blinded>><span style="color: #aaaaff;">🌫️ Blinded</span> <</if>> <<if $currentEnemy.rooted>><span style="color: #88ff88;">🌿 Rooted</span> <</if>> <<if $currentEnemy.weakened>><span style="color: #ff8888;">💔 Weakened</span> <</if>> <<if $currentEnemy.armorBroken>><span style="color: #ffaa44;">🛡️ Armor Broken</span> <</if>> </div> </div> <<if $enemyList.length > 1>> <div> <b>Enemies Remaining:</b> $enemyList.length<br> <<for _i=0; _i < $enemyList.length; _i++>> <<if $enemyList[$_i]>> <<set _enemyName = $enemyList[$_i].name>> <<if $_i == $currentEnemyIndex>>🔴 <</if>><<if $_i != $currentEnemyIndex>>⚪ <</if>>$_enemyName<br> <</if>> <</for>> </div> <<else>> <div> <b>Enemies Remaining:</b> 1 </div> <</if>> <div style="text-align: right;"> <b>Your Stats</b><br> ❤️ $currentHP/$maxHP HP<br> 💙 $currentMP/$maxMP MP<br> ⚡ AGI: $agility <<if $allyCount > 0>>👥 Allies: $allyCount<br><</if>> <div style="margin-top: 5px; font-size: 11px;"> <<if $regen>><span style="color: #88ff88;">💚 Regen</span> <</if>> <<if $haste>><span style="color: #ffaa88;">⚡ Haste</span> <</if>> <<if $shield>><span style="color: #88aaff;">🛡️ Shield</span> <</if>> </div> </div> </div> </div> <div id="combatLog" style="height:150px; overflow:auto; border:1px solid #ccc; padding:5px; margin:10px 0; background:#0a0a0a; color:#ddd; font-family:monospace;"> $combatLogText </div> <!-- Include the full combat actions system --> <<include "Combat_Actions">> <!-- Include potion menu if selecting --> <<if $selectingPotion>> <<include "Combat_PotionMenu">> <</if>> <!-- Include spell menu if casting --> <<if $castingSpell == "select">> <<include "Combat_SpellMenu">> <</if>> <</nobr>>
<nobr> <div style="background: rgba(0,0,0,0.7); border-radius: 15px; padding: 15px; margin-top: 15px;"> <div style="text-align: center; font-family: 'Cinzel', serif; color: #d4af37; margin-bottom: 10px;">💊 SELECT A POTION</div> <div style="display: flex; flex-wrap: wrap; gap: 10px; justify-content: center;"> <<if $inventory.minor_health_potion > 0>> <<link "🧪 Minor Health Potion ($inventory.minor_health_potion) - Restores 20 HP">> <<set $combatActions++>> <<set $oldHP = $currentHP>> <<set $currentHP = Math.min($maxHP, $currentHP + 20)>> <<set $healed = $currentHP - $oldHP>> <<set $inventory.minor_health_potion = $inventory.minor_health_potion - 1>> <<set $combatLogText = $combatLogText + "<br>🧪 Used Minor Health Potion! Restored " + $healed + " HP!<br>">> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>><<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">><</if>> <<if $currentHP <= 0>><<set $combatState = "death">><</if>> <<set $selectingPotion = false>> <<run window.setup.updateSidebar()>> <<goto "MonsterColosseumBattle">> <<run window.setup.scrollToBottom("combatLog")>> <</link>> <</if>> <<if $inventory.health_potion > 0>> <<link "🧪 Health Potion ($inventory.health_potion) - Restores 40 HP">> <<set $combatActions++>> <<set $oldHP = $currentHP>> <<set $currentHP = Math.min($maxHP, $currentHP + 40)>> <<set $healed = $currentHP - $oldHP>> <<set $inventory.health_potion = $inventory.health_potion - 1>> <<set $combatLogText = $combatLogText + "<br>🧪 Used Health Potion! Restored " + $healed + " HP!<br>">> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>><<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">><</if>> <<if $currentHP <= 0>><<set $combatState = "death">><</if>> <<set $selectingPotion = false>> <<run window.setup.updateSidebar()>> <<goto "MonsterColosseumBattle">> <<run window.setup.scrollToBottom("combatLog")>> <</link>> <</if>> <<if $inventory.greater_health_potion > 0>> <<link "🧪✨ Greater Health Potion ($inventory.greater_health_potion) - Restores 70 HP">> <<set $combatActions++>> <<set $oldHP = $currentHP>> <<set $currentHP = Math.min($maxHP, $currentHP + 70)>> <<set $healed = $currentHP - $oldHP>> <<set $inventory.greater_health_potion = $inventory.greater_health_potion - 1>> <<set $combatLogText = $combatLogText + "<br>🧪✨ Used Greater Health Potion! Restored " + $healed + " HP!<br>">> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>><<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">><</if>> <<if $currentHP <= 0>><<set $combatState = "death">><</if>> <<set $selectingPotion = false>> <<run window.setup.updateSidebar()>> <<goto "MonsterColosseumBattle">> <<run window.setup.scrollToBottom("combatLog")>> <</link>> <</if>> <<if $inventory.superior_health_potion > 0>> <<link "🧪❤️🔥 Superior Health Potion ($inventory.superior_health_potion) - Restores 110 HP">> <<set $combatActions++>> <<set $oldHP = $currentHP>> <<set $currentHP = Math.min($maxHP, $currentHP + 110)>> <<set $healed = $currentHP - $oldHP>> <<set $inventory.superior_health_potion = $inventory.superior_health_potion - 1>> <<set $combatLogText = $combatLogText + "<br>🧪❤️🔥 Used Superior Health Potion! Restored " + $healed + " HP!<br>">> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>><<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">><</if>> <<if $currentHP <= 0>><<set $combatState = "death">><</if>> <<set $selectingPotion = false>> <<run window.setup.updateSidebar()>> <<goto "MonsterColosseumBattle">> <<run window.setup.scrollToBottom("combatLog")>> <</link>> <</if>> <<if $inventory.elixir_of_life > 0>> <<link "✨🧪 Elixir of Life ($inventory.elixir_of_life) - Restores 160 HP">> <<set $combatActions++>> <<set $oldHP = $currentHP>> <<set $currentHP = Math.min($maxHP, $currentHP + 160)>> <<set $healed = $currentHP - $oldHP>> <<set $inventory.elixir_of_life = $inventory.elixir_of_life - 1>> <<set $combatLogText = $combatLogText + "<br>✨🧪 Used Elixir of Life! Restored " + $healed + " HP!<br>">> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>><<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">><</if>> <<if $currentHP <= 0>><<set $combatState = "death">><</if>> <<set $selectingPotion = false>> <<run window.setup.updateSidebar()>> <<goto "MonsterColosseumBattle">> <<run window.setup.scrollToBottom("combatLog")>> <</link>> <</if>> <<if $inventory.minor_mana_potion > 0>> <<link "💙 Minor Mana Potion ($inventory.minor_mana_potion) - Restores 15 MP">> <<set $combatActions++>> <<set $oldMP = $currentMP>> <<set $currentMP = Math.min($maxMP, $currentMP + 15)>> <<set $restored = $currentMP - $oldMP>> <<set $inventory.minor_mana_potion = $inventory.minor_mana_potion - 1>> <<set $combatLogText = $combatLogText + "<br>💙 Used Minor Mana Potion! Restored " + $restored + " MP!<br>">> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>><<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">><</if>> <<if $currentHP <= 0>><<set $combatState = "death">><</if>> <<set $selectingPotion = false>> <<run window.setup.updateSidebar()>> <<goto "MonsterColosseumBattle">> <<run window.setup.scrollToBottom("combatLog")>> <</link>> <</if>> <<if $inventory.mana_potion > 0>> <<link "💙 Mana Potion ($inventory.mana_potion) - Restores 30 MP">> <<set $combatActions++>> <<set $oldMP = $currentMP>> <<set $currentMP = Math.min($maxMP, $currentMP + 30)>> <<set $restored = $currentMP - $oldMP>> <<set $inventory.mana_potion = $inventory.mana_potion - 1>> <<set $combatLogText = $combatLogText + "<br>💙 Used Mana Potion! Restored " + $restored + " MP!<br>">> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>><<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">><</if>> <<if $currentHP <= 0>><<set $combatState = "death">><</if>> <<set $selectingPotion = false>> <<run window.setup.updateSidebar()>> <<goto "MonsterColosseumBattle">> <<run window.setup.scrollToBottom("combatLog")>> <</link>> <</if>> <<if $inventory.greater_mana_potion > 0>> <<link "💙✨ Greater Mana Potion ($inventory.greater_mana_potion) - Restores 50 MP">> <<set $combatActions++>> <<set $oldMP = $currentMP>> <<set $currentMP = Math.min($maxMP, $currentMP + 50)>> <<set $restored = $currentMP - $oldMP>> <<set $inventory.greater_mana_potion = $inventory.greater_mana_potion - 1>> <<set $combatLogText = $combatLogText + "<br>💙✨ Used Greater Mana Potion! Restored " + $restored + " MP!<br>">> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>><<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">><</if>> <<if $currentHP <= 0>><<set $combatState = "death">><</if>> <<set $selectingPotion = false>> <<run window.setup.updateSidebar()>> <<goto "MonsterColosseumBattle">> <<run window.setup.scrollToBottom("combatLog")>> <</link>> <</if>> <<if $inventory.superior_mana_potion > 0>> <<link "💙🌀 Superior Mana Potion ($inventory.superior_mana_potion) - Restores 80 MP">> <<set $combatActions++>> <<set $oldMP = $currentMP>> <<set $currentMP = Math.min($maxMP, $currentMP + 80)>> <<set $restored = $currentMP - $oldMP>> <<set $inventory.superior_mana_potion = $inventory.superior_mana_potion - 1>> <<set $combatLogText = $combatLogText + "<br>💙🌀 Used Superior Mana Potion! Restored " + $restored + " MP!<br>">> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>><<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">><</if>> <<if $currentHP <= 0>><<set $combatState = "death">><</if>> <<set $selectingPotion = false>> <<run window.setup.updateSidebar()>> <<goto "MonsterColosseumBattle">> <<run window.setup.scrollToBottom("combatLog")>> <</link>> <</if>> <<if $inventory.elixir_of_mana > 0>> <<link "✨🧪💙 Elixir of Mana ($inventory.elixir_of_mana) - Restores 120 MP">> <<set $combatActions++>> <<set $oldMP = $currentMP>> <<set $currentMP = Math.min($maxMP, $currentMP + 120)>> <<set $restored = $currentMP - $oldMP>> <<set $inventory.elixir_of_mana = $inventory.elixir_of_mana - 1>> <<set $combatLogText = $combatLogText + "<br>✨🧪💙 Used Elixir of Mana! Restored " + $restored + " MP!<br>">> <<set $enemyDamageResult = window.setup.combat.calculateEnemyDamage($currentEnemy, $allyCount)>> <<set $currentHP = $currentHP - $enemyDamageResult.damage>> <<if $enemyDamageResult.packMessage>> <<set $combatLogText = $combatLogText + $enemyDamageResult.packMessage + "<br>">> <</if>> <<if $enemyDamageResult.damage > 0>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " hits you for " + $enemyDamageResult.damage + " damage!<br>">> <</if>> <<if $enemyDamageResult.isCrit>><<set $combatLogText = $combatLogText + "<span style='color:#ff4444;'>💀 CRITICAL HIT! 💀</span><br>">><</if>> <<if $currentHP <= 0>><<set $combatState = "death">><</if>> <<set $selectingPotion = false>> <<run window.setup.updateSidebar()>> <<goto "MonsterColosseumBattle">> <<run window.setup.scrollToBottom("combatLog")>> <</link>> <</if>> <<if $inventory.minor_health_potion == 0 && $inventory.health_potion == 0 && $inventory.greater_health_potion == 0 && $inventory.superior_health_potion == 0 && $inventory.elixir_of_life == 0 && $inventory.minor_mana_potion == 0 && $inventory.mana_potion == 0 && $inventory.greater_mana_potion == 0 && $inventory.superior_mana_potion == 0 && $inventory.elixir_of_mana == 0>> <div style="color: #ff8888; text-align: center; padding: 10px;">You have no potions!</div> <</if>> <<link "❌ Cancel">> <<set $selectingPotion = false>> <<goto "MonsterColosseumBattle">> <</link>> </div> </div> </nobr>
<!-- Spell Menu Selection --> <div style="background: rgba(0,0,0,0.7); border-radius: 15px; padding: 15px; margin-top: 15px;"> <div style="text-align: center; font-family: 'Cinzel', serif; color: #d4af37; margin-bottom: 10px;">📖 SELECT A SPELL (MP: $currentMP/$maxMP)</div> <<if !$spellbook || $spellbook.length == 0>> <div style="text-align: center; color: #ff8888; padding: 10px;"> You don't know any spells!<br> <span style="font-size: 12px; color: #aaa;">(Spellbook is empty. Learn spells from spellbooks found in the world.)</span> </div> <<else>> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px;"> <<for _i = 0; _i < $spellbook.length; _i++>> <<set $spellId = $spellbook[$_i]>> <<set $spell = window.setup.spells[$spellId]>> <<if $spell>> <<set $mpCost = $spell.mpCost>> <<set $canCast = ($currentMP >= $mpCost)>> <<if $canCast>> <<capture $spellId>> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 8px; text-align: center;"> <<link $spell.name + " (" + $mpCost + " MP)">> <<set $spellResult = window.setup.castSpell($spellId, $currentEnemy)>> <<set $combatLogText = $combatLogText + "<br>" + $spellResult.message + "<br>">> <<if $spellResult.success>> <<set $combatActions++>> <<set $currentMP = $currentMP - $mpCost>> <<if $spellResult.damage > 0>> <<set $currentEnemy.hp = $currentEnemy.hp - $spellResult.damage>> <</if>> <<if $currentEnemy.hp <= 0>> <<set $enemyList = $enemyList.slice(1)>> <<if $enemyList.length > 0>> <<set $currentEnemy = $enemyList[0]>> <<set $combatLogText = $combatLogText + "<span style='color:#88ff88;'>✅ " + $currentEnemy.name + " defeated! " + $enemyList.length + " enemy(s) remain.</span><br>">> <<set _enemyDamage = random(3, 10) + Math.floor($currentEnemy.strength / 2 || 0)>> <<set $currentHP = $currentHP - _enemyDamage>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " counterattacks for " + _enemyDamage + " damage!<br>">> <<else>> <<set $combatState = "victory">> <</if>> <<else>> <<set _enemyDamage = random(3, 10) + Math.floor($currentEnemy.strength / 2 || 0)>> <<set $currentHP = $currentHP - _enemyDamage>> <<set $combatLogText = $combatLogText + $currentEnemy.name + " counterattacks for " + _enemyDamage + " damage!<br>">> <</if>> <<if $currentHP <= 0>> <<set $combatState = "death">> <</if>> <</if>> <<set $castingSpell = false>> <<run window.setup.updateSidebar()>> <<goto "Combat_Display">> <</link>> <div style="font-size: 10px; color: #aaa;">$spell.description</div> </div> <</capture>> <<else>> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 8px; text-align: center; opacity: 0.5;"> <span>$spell.name ($mpCost MP)</span> <div style="font-size: 10px; color: #ff8888;">Not enough MP!</div> </div> <</if>> <</if>> <</for>> </div> <</if>> <div style="text-align: center; margin-top: 15px;"> <<link "❌ Cancel">> <<set $castingSpell = false>> <<goto "Combat_Display">> <</link>> </div> </div>
:: Combat_Victory <<nobr>> <div id="combatLog" style="height:200px; overflow:auto; border:1px solid #d4af37; padding:10px; margin:10px 0; background:rgba(0,0,0,0.5); color:#ddd; font-family:monospace;"> $combatLogText </div> <div style="text-align:center; margin:20px 0;"> <span style="color:#ffd700; font-size:28px; font-family:'Cinzel', serif;">🏆 VICTORY! 🏆</span> </div> <!-- Display XP and Gold gained --> <<if $testEnemy && $testEnemy.exp>> <div style="text-align:center; margin:10px 0;"> <<set $expGain = $testEnemy.exp>> <<set $exp = $exp + $expGain>> <span style="color:#88ff88;">✨ You gained $expGain XP!</span><br> <<set _goldGain = random($testEnemy.gold.min, $testEnemy.gold.max)>> <<set $gold = $gold + _goldGain>> <span style="color:#ffd700;">💰 You found _goldGain gold!</span> </div> <</if>> <!-- Return to appropriate destination --> <div style="text-align:center; margin:20px 0;"> <<link "🗺️ Continue">> <<set $combatState = null>> <<set $inCombat = false>> <<set $combatLogText = "">> <<set $combatActions = 0>> <<set $enemyList = []>> <<set $currentEnemy = null>> <<set $testEnemy = null>> <<set $selectingPotion = false>> <<set $castingSpell = false>> <<if $combatReturnAfterVictory>> <<set _returnDest = $combatReturnAfterVictory>> <<set $combatReturnAfterVictory = null>> <<goto _returnDest>> <<elseif $returnToGridX !== null && $returnToGridX !== undefined>> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $coord = $gridX + "," + $gridY>> <<set $returnToGridX = null>> <<set $returnToGridY = null>> <<goto "GridCell">> <<else>> <<goto "GridCell">> <</if>> <</link>> </div> <</nobr>>
:: EXP_Core [widget nobr] <<widget "loadExp">> <<script>> window.setup = window.setup || {}; window.setup.expTable = { 1: 0, 2: 300, 3: 900, 4: 2700, 5: 6500, 6: 14000, 7: 23000, 8: 34000, 9: 48000, 10: 64000, 11: 85000, 12: 100000, 13: 120000, 14: 140000, 15: 165000, 16: 195000, 17: 225000, 18: 265000, 19: 305000, 20: 355000 }; window.setup.exp = { getNextLevelExp: function(currentLevel) { if (currentLevel >= 20) return 999999; return window.setup.expTable[currentLevel + 1]; }, gain: function(amount) { if (!amount || amount <= 0) return false; let currentExp = State.variables.exp; let newExp = currentExp + amount; let currentLevel = State.variables.level; let leveledUp = false; let oldStats = { level: currentLevel, maxHP: State.variables.maxHP, maxMP: State.variables.maxMP, strength: State.variables.strength, constitution: State.variables.constitution, defense: State.variables.defense, agility: State.variables.agility, intellect: State.variables.intellect }; let newLevel = currentLevel; while (newLevel < 20 && newExp >= window.setup.expTable[newLevel + 1]) { newLevel++; leveledUp = true; } if (leveledUp) { for (let lvl = currentLevel + 1; lvl <= newLevel; lvl++) { let conBonus = Math.floor(State.variables.constitution / 6); State.variables.maxHP += 6 + conBonus; State.variables.maxMP += 4; State.variables.strength += 2; State.variables.constitution += 1; State.variables.defense += 1; State.variables.agility += 1; State.variables.intellect += 1; } State.variables.level = newLevel; State.variables.exp = newExp; State.variables.nextLevelExp = this.getNextLevelExp(newLevel); State.variables.currentHP = State.variables.maxHP; State.variables.currentMP = State.variables.maxMP; State.variables.baseStrength = State.variables.strength; State.variables.baseConstitution = State.variables.constitution; State.variables.baseDefense = State.variables.defense; State.variables.baseAgility = State.variables.agility; State.variables.baseIntellect = State.variables.intellect; State.variables.baseMaxHP = State.variables.maxHP; let newStats = { level: newLevel, maxHP: State.variables.maxHP, maxMP: State.variables.maxMP, strength: State.variables.strength, constitution: State.variables.constitution, defense: State.variables.defense, agility: State.variables.agility, intellect: State.variables.intellect }; if (window.setup.showLevelUp) { window.setup.showLevelUp(oldStats, newStats, newLevel - currentLevel); } } else { State.variables.exp = newExp; } return leveledUp; }, getXPProgress: function() { let level = State.variables.level; if (level >= 20) return 100; let currentXP = State.variables.exp; let nextXP = window.setup.expTable[level + 1]; let prevXP = window.setup.expTable[level]; let needed = nextXP - prevXP; let earned = currentXP - prevXP; return Math.floor((earned / needed) * 100); } }; window.setup.showLevelUp = function(oldStats, newStats, levelsGained = 1) { let existing = document.getElementById('levelup-notification'); if (existing) existing.remove(); let levelText = levelsGained > 1 ? `Levels Gained: +${levelsGained}` : `Level ${oldStats.level} → ${newStats.level}`; let notification = document.createElement('div'); notification.id = 'levelup-notification'; notification.className = 'levelup-notification'; notification.innerHTML = ` <h3>✨ LEVEL UP! ✨</h3> <div>${levelText}</div> <div>❤️ HP: ${oldStats.maxHP} → ${newStats.maxHP}</div> <div>💙 MP: ${oldStats.maxMP} → ${newStats.maxMP}</div> <div>💪 STR: ${oldStats.strength} → ${newStats.strength}</div> <div>❤️ CON: ${oldStats.constitution} → ${newStats.constitution}</div> <div>🛡️ DEF: ${oldStats.defense} → ${newStats.defense}</div> <div>⚡ AGI: ${oldStats.agility} → ${newStats.agility}</div> <div>🧠 INT: ${oldStats.intellect} → ${newStats.intellect}</div> `; document.body.appendChild(notification); setTimeout(() => notification.remove(), 5000); }; console.log("✅ Exp module loaded - D&D 20-level progression"); <</script>> <</widget>>
:: Encounter Return [widget] <<nobr>> <<widget "encounterReturn">> <<clearCombat>> <<set $inCombat = false>> <<if $returnAfterCombat>> <<goto $returnAfterCombat>> <<else>> <<returnToPassage>> <</if>> <</widget>> <</nobr>>
<<widget "loadEnemies">> <<script>> window.setup = window.setup || {}; window.setup.EnemyData = window.setup.EnemyData || {}; window.setup.addEnemies = function(enemyData) { Object.assign(window.setup.EnemyData, enemyData); console.log("Added", Object.keys(enemyData).length, "enemies to database"); }; window.setup.getEnemy = function(enemyId) { let result = window.setup.EnemyData[enemyId]; if (!result) { console.warn("Enemy not found:", enemyId); result = { name: "Debug Goblin", tier: 1, type: "humanoid", description: "A debug enemy. Something went wrong.", special: null }; } return JSON.parse(JSON.stringify(result)); }; window.setup.getRandomEnemy = function() { let keys = Object.keys(window.setup.EnemyData); let randomKey = keys[Math.floor(Math.random() * keys.length)]; return window.setup.getEnemy(randomKey); }; window.setup.enemyRanks = { common: { mult: 1.0, xpMult: 1.0, goldMult: 1.0, name: "Common", prefix: "", color: "#ffffff", chance: 75 }, variant: { mult: 1.3, xpMult: 1.5, goldMult: 1.3, name: "Variant", prefix: "Variant", color: "#88aaff", chance: 17.5 }, elite: { mult: 1.6, xpMult: 2.0, goldMult: 1.6, name: "Elite", prefix: "Elite", color: "#ffaa44", chance: 5 }, boss: { mult: 2.5, xpMult: 4.0, goldMult: 3.0, name: "Boss", prefix: "Boss", color: "#ff4444", chance: 2.5 }, legendary: { mult: 4.0, xpMult: 10.0, goldMult: 5.0, name: "Legendary", prefix: "Legendary", color: "#ffd700", chance: 0 } }; window.setup.tierPower = { 1: { hp: 25, str: 8, con: 5, def: 3, agi: 8, int: 5 }, 2: { hp: 45, str: 14, con: 9, def: 6, agi: 10, int: 8 }, 3: { hp: 80, str: 22, con: 14, def: 9, agi: 12, int: 12 }, 4: { hp: 140, str: 32, con: 20, def: 13, agi: 14, int: 18 }, 5: { hp: 220, str: 45, con: 28, def: 18, agi: 16, int: 25 } }; window.setup.getRandomRank = function() { let roll = Math.random() * 100; let cumulative = 0; for (let rank in window.setup.enemyRanks) { cumulative += window.setup.enemyRanks[rank].chance; if (roll <= cumulative) return rank; } return "common"; }; window.setup.calculateEnemyStats = function(baseEnemy, tier, rankKey) { let rank = window.setup.enemyRanks[rankKey]; let tierPower = window.setup.tierPower[tier] || window.setup.tierPower[1]; let baseHP = baseEnemy.baseHP || tierPower.hp; let baseSTR = baseEnemy.baseSTR || tierPower.str; let baseCON = baseEnemy.baseCON || tierPower.con; let baseDEF = baseEnemy.baseDEF || tierPower.def; let baseAGI = baseEnemy.baseAGI || tierPower.agi; let baseINT = baseEnemy.baseINT || tierPower.int; let variance = 0.85 + (Math.random() * 0.3); let totalMult = rank.mult * variance; return { maxHp: Math.max(5, Math.floor(baseHP * totalMult)), strength: Math.max(1, Math.floor(baseSTR * totalMult)), constitution: Math.max(1, Math.floor(baseCON * totalMult)), defense: Math.max(0, Math.floor(baseDEF * totalMult)), agility: Math.max(1, Math.floor(baseAGI * totalMult)), intellect: Math.max(1, Math.floor(baseINT * totalMult)) }; }; window.setup.calculateEnemyXP = function(tier, rankKey) { let baseXPTable = { 1: 30, 2: 60, 3: 120, 4: 240, 5: 480 }; let baseXP = baseXPTable[tier] || 30; let xpMult = window.setup.enemyRanks[rankKey].xpMult; let variance = 0.9 + (Math.random() * 0.2); return Math.floor(baseXP * xpMult * variance); }; window.setup.calculateEnemyGold = function(tier, rankKey) { let baseGoldTable = { 1: { min: 5, max: 15 }, 2: { min: 15, max: 35 }, 3: { min: 35, max: 70 }, 4: { min: 70, max: 150 }, 5: { min: 150, max: 300 } }; let baseGold = baseGoldTable[tier] || baseGoldTable[1]; let goldMult = window.setup.enemyRanks[rankKey].goldMult; return { min: Math.floor(baseGold.min * goldMult), max: Math.floor(baseGold.max * goldMult) }; }; window.setup.spawnEnemy = function(baseEnemyId, forceRank, forceTier) { if (forceRank === undefined) forceRank = null; if (forceTier === undefined) forceTier = null; let baseEnemy = window.setup.getEnemy(baseEnemyId); if (!baseEnemy) { console.error("Enemy not found:", baseEnemyId); return null; } let tier = forceTier || baseEnemy.tier || 1; let rankKey = forceRank || window.setup.getRandomRank(); let rank = window.setup.enemyRanks[rankKey]; let stats = window.setup.calculateEnemyStats(baseEnemy, tier, rankKey); let exp = window.setup.calculateEnemyXP(tier, rankKey); let gold = window.setup.calculateEnemyGold(tier, rankKey); let enemy = { id: baseEnemyId, name: rank.prefix ? rank.prefix + " " + baseEnemy.name : baseEnemy.name, originalName: baseEnemy.name, rank: rankKey, rankName: rank.name, tier: tier, hp: stats.maxHp, maxHp: stats.maxHp, strength: stats.strength, constitution: stats.constitution, defense: stats.defense, agility: stats.agility, intellect: stats.intellect, exp: exp, gold: gold, type: baseEnemy.type, description: baseEnemy.description, special: baseEnemy.special, isSpawned: true }; if (rankKey === "elite") { if (!enemy.special) enemy.special = "enrage"; enemy.strength = Math.floor(enemy.strength * 1.1); } if (rankKey === "boss") { if (!enemy.special) enemy.special = "enrage"; enemy.strength = Math.floor(enemy.strength * 1.2); enemy.defense = Math.floor(enemy.defense * 1.1); } if (rankKey === "legendary") { if (!enemy.special) enemy.special = "dragon_fear"; enemy.strength = Math.floor(enemy.strength * 1.3); enemy.defense = Math.floor(enemy.defense * 1.2); enemy.agility = Math.floor(enemy.agility * 1.1); } console.log("Spawned: " + rank.name + " T" + tier + " " + baseEnemy.name + " | HP: " + enemy.hp + " | XP: " + enemy.exp); return enemy; }; console.log("Enemy loader module loaded with rank system"); <</script>> <</widget>>
<<nobr>> <<widget "loadReset">> <<script>> window.setup = window.setup || {}; window.setup.resetGame = function() { State.variables.combatState = 0; State.variables.combatLogText = ""; State.variables.currentEnemy = 0; State.variables.combatActions = 0; State.variables.castingSpell = null; State.variables.selectingPotion = null; State.variables.playerStunned = false; State.variables.hour = 8; State.variables.minute = 0; State.variables.day = 1; State.variables.isNight = false; State.variables.gold = 50; State.variables.equipment = { weapon: "fists", armor: "rags", helmet: null, boots: null, ring: null, amulet: null }; State.variables.inventory = window.setup.initInventory(); State.variables.inventory.health_potion = 2; State.variables.inventory.rusty_dagger = 1; State.variables.inventory.cloth_tunic = 1; State.variables.inventory.mana_potion = 1; State.variables.spellbook = []; State.variables.baseStrength = 8; State.variables.baseConstitution = 8; State.variables.baseDefense = 7; State.variables.baseAgility = 8; State.variables.baseIntellect = 8; State.variables.baseMaxHP = 30; State.variables.strength = 8; State.variables.constitution = 8; State.variables.defense = 7; State.variables.agility = 8; State.variables.intellect = 8; State.variables.maxHP = 46; State.variables.currentHP = 46; State.variables.maxMP = 24; State.variables.currentMP = 24; State.variables.exp = 0; State.variables.level = 1; State.variables.nextLevelExp = 300; State.variables.returnTo = "TownSquare"; State.variables.goldAfterRest = false; State.variables.freeRest = false; State.variables.restMessage = null; State.variables.isTestBattle = false; }; window.setup.checkReset = function() { console.log("=== GAME RESET VERIFICATION ==="); console.log("Current HP:", State.variables.currentHP); console.log("Max HP:", State.variables.maxHP); console.log("CON:", State.variables.constitution); console.log("Level:", State.variables.level); }; console.log("✅ Reset module loaded via widget"); <</script>> <</widget>> <</nobr>>
:: GenerateGrid_Biomes [widget nobr] <<widget "GenerateGrid_Biomes">> <<script>> var vars = State.variables; var WORLD_RADIUS = vars.worldRadius || 60; var biomeTypes = { forest: { enemies: ["forest_wolf", "goblin_scout", "giant_spider", "hobgoblin_ranger", "owlbear", "werewolf"], icon: "🌲", namePrefix: ["Dark", "Silent", "Ancient"], nameSuffix: ["Forest", "Woods", "Grove"] }, plains: { enemies: ["bandit", "wild_dog", "highway_archer", "orc_raider"], icon: "🌾", namePrefix: ["Golden", "Rolling", "Open"], nameSuffix: ["Plains", "Fields", "Meadows"] }, mountain: { enemies: ["mountain_goat", "harpy", "bugbear", "wyvern", "stone_giant"], icon: "⛰️", namePrefix: ["High", "Frozen", "Jagged"], nameSuffix: ["Peaks", "Mountains", "Highlands"] }, water: { enemies: ["reef_crab", "sahuagin_scout", "giant_octopus", "sea_hag"], icon: "💧", namePrefix: ["Calm", "Still", "Peaceful"], nameSuffix: ["Lake", "Waters"] }, road: { enemies: ["bandit", "wild_dog", "highway_archer", "orc_raider", "ogre_mercenary", "veteran_knight"], icon: "🛣️", namePrefix: ["Old", "Winding", "Traveler's"], nameSuffix: ["Road", "Path", "Way"] }, swamp: { enemies: ["giant_frog", "swamp_leech", "lizardfolk_hunter", "crocodile", "ghoul_mirewalker", "swamp_troll"], icon: "🪷", namePrefix: ["Murky", "Black", "Rotting"], nameSuffix: ["Swamp", "Marsh", "Bog"] }, desert: { enemies: ["giant_scorpion", "dust_mephit", "gnoll_raider", "mummy_servant", "basilisk", "lamia"], icon: "🏜️", namePrefix: ["Endless", "Burning", "Scorched"], nameSuffix: ["Desert", "Dunes", "Waste"] }, tundra: { enemies: ["ice_wolf", "ice_mephit", "snow_goblin", "yeti", "polar_owlbear", "winter_wolf"], icon: "❄️", namePrefix: ["Frozen", "Bleak", "White"], nameSuffix: ["Tundra", "Wastes", "Barrens"] } }; function generateName(type, x, y) { if (type === "water") { var lakeNames = [ "Mirror Lake", "Stillwater Lake", "Misty Lake", "Crystal Lake", "Silver Lake", "Deepwater Lake", "Silent Lake", "Moon Lake", "Shadow Lake", "Blue Lake", "Emerald Lake", "Sapphire Lake", "Lake of Dreams", "Whispering Lake", "Tranquil Lake" ]; return lakeNames[Math.floor(Math.random() * lakeNames.length)]; } var biome = biomeTypes[type]; if (!biome) return type; var prefix = biome.namePrefix[Math.floor(Math.random() * biome.namePrefix.length)]; var suffix = biome.nameSuffix[Math.floor(Math.random() * biome.nameSuffix.length)]; return prefix + " " + suffix; } function generateDescription(type) { var descs = { forest: ["Tall trees block the sunlight.", "Wildlife scatters at your approach."], plains: ["Open grassland as far as the eye can see.", "Wildflowers dot the landscape."], mountain: ["Snow-capped peaks loom overhead.", "The air is thin and cold."], water: ["Clear waters reflect the sky above.", "The water is calm and peaceful."], swamp: ["Thick mist hangs over stagnant water.", "Strange sounds emerge from the murk."], desert: ["Endless dunes stretch to the horizon.", "The sun beats down mercilessly."], tundra: ["A bitter wind howls across the ice.", "Nothing grows in this frozen wasteland."], road: ["The path continues onward.", "A well-traveled route."] }; var list = descs[type] || ["An unexplored area."]; return list[Math.floor(Math.random() * list.length)]; } function pseudoRandom(x, y, seed) { var n = x * 374761393 + y * 668265263 + seed; n = (n ^ (n >> 13)) * 1274126177; n = (n ^ (n >> 15)) * 268582165; return ((n ^ (n >> 16)) % 1000) / 1000; } var seed = Math.random() * 100000; for (var i = 0; i < vars.gridExists.length; i++) { var key = vars.gridExists[i]; if (key === "0,0") continue; var coords = key.split(","); var x = parseInt(coords[0]); var y = parseInt(coords[1]); var dist = Math.sqrt(x*x + y*y); var noise1 = pseudoRandom(x, y, seed); var noise2 = pseudoRandom(x * 2, y * 2, seed + 1000); var noise3 = pseudoRandom(x * 3, y * 3, seed + 2000); var type = "plains"; if (noise1 > 0.59 && noise2 < 0.52) { type = "water"; } else if (noise2 > 0.85) { type = "mountain"; } else if (noise3 > 0.78 && Math.abs(y) < 35 && dist > 15) { type = "desert"; } else if (noise3 < 0.26 && Math.abs(y) > 28 && dist > 20) { type = "tundra"; } else if (noise1 > 0.70 && noise2 < 0.40 && dist > 8 && dist < 50) { type = "swamp"; } else if (noise1 > 0.25 && noise1 < 0.68 && noise2 > 0.25) { type = "forest"; } else { type = "plains"; } var biome = biomeTypes[type]; vars.gridBiomes[key] = { type: type, name: generateName(type, x, y), desc: generateDescription(type), enemies: biome ? biome.enemies.slice() : [], icon: biome ? biome.icon : "📍" }; } console.log("Biomes assigned for radius " + WORLD_RADIUS); <</script>> <</widget>>
:: GenerateGrid_BreakWater [widget] <<widget "GenerateGrid_BreakWater">> <<script>> var vars = State.variables; var gridExistsSet = new Set(vars.gridExists); var protectedTypes = ["town", "village", "enemy_camp", "shrine", "dungeon", "treasure", "merchant"]; // First pass: Identify and break up large water clusters for (var pass = 0; pass < 2; pass++) { for (var i = 0; i < vars.gridExists.length; i++) { var key = vars.gridExists[i]; var coords = key.split(","); var x = parseInt(coords[0]); var y = parseInt(coords[1]); if (vars.gridBiomes[key].type === "water") { var dist = Math.sqrt(x*x + y*y); // Count water neighbors in a 3x3 area var waterCount = 0; var landCount = 0; for (var dx = -1; dx <= 1; dx++) { for (var dy = -1; dy <= 1; dy++) { if (dx === 0 && dy === 0) continue; var nKey = (x + dx) + "," + (y + dy); if (gridExistsSet.has(nKey) && vars.gridBiomes[nKey]) { if (vars.gridBiomes[nKey].type === "water") { waterCount++; } else if (!protectedTypes.includes(vars.gridBiomes[nKey].type)) { landCount++; } } } } // Convert water to land if: // 1. Not at the far edge (dist < 48) - preserve ocean at edges // 2. Has at least 2 land neighbors (creates jagged coasts) // 3. Not completely surrounded by water if (dist < 48 && landCount >= 2 && waterCount < 7) { // 40% chance to convert to swamp, 30% to plains, 30% to forest var roll = Math.random(); var newType; if (roll < 0.4) { newType = "swamp"; } else if (roll < 0.7) { newType = "plains"; } else { newType = "forest"; } vars.gridBiomes[key].type = newType; if (newType === "swamp") { vars.gridBiomes[key].name = "Murky Swamp"; vars.gridBiomes[key].desc = "Thick mist hangs over stagnant water."; vars.gridBiomes[key].icon = "🪷"; vars.gridBiomes[key].enemies = ["bandit"]; } else if (newType === "plains") { vars.gridBiomes[key].name = "Golden Plains"; vars.gridBiomes[key].desc = "Open grassland as far as the eye can see."; vars.gridBiomes[key].icon = "🌾"; vars.gridBiomes[key].enemies = []; } else if (newType === "forest") { vars.gridBiomes[key].name = "Deep Forest"; vars.gridBiomes[key].desc = "Tall trees block the sunlight."; vars.gridBiomes[key].icon = "🌲"; vars.gridBiomes[key].enemies = ["forest_wolf"]; } } } } } // Second pass: Create islands by converting inland water to land for (var i = 0; i < vars.gridExists.length; i++) { var key = vars.gridExists[i]; var coords = key.split(","); var x = parseInt(coords[0]); var y = parseInt(coords[1]); var dist = Math.sqrt(x*x + y*y); if (vars.gridBiomes[key].type === "water" && dist < 45) { // Check if this water is surrounded by water on most sides (inland sea) var waterNeighbors = 0; for (var dx = -1; dx <= 1; dx++) { for (var dy = -1; dy <= 1; dy++) { if (dx === 0 && dy === 0) continue; var nKey = (x + dx) + "," + (y + dy); if (gridExistsSet.has(nKey) && vars.gridBiomes[nKey] && vars.gridBiomes[nKey].type === "water") { waterNeighbors++; } } } // If it's a small inland sea (6+ water neighbors), convert to land if (waterNeighbors >= 6) { var roll = Math.random(); var newType; if (roll < 0.5) { newType = "plains"; } else if (roll < 0.8) { newType = "forest"; } else { newType = "swamp"; } vars.gridBiomes[key].type = newType; if (newType === "plains") { vars.gridBiomes[key].name = "Golden Plains"; vars.gridBiomes[key].desc = "Open grassland as far as the eye can see."; vars.gridBiomes[key].icon = "🌾"; } else if (newType === "forest") { vars.gridBiomes[key].name = "Deep Forest"; vars.gridBiomes[key].desc = "Tall trees block the sunlight."; vars.gridBiomes[key].icon = "🌲"; } else if (newType === "swamp") { vars.gridBiomes[key].name = "Murky Swamp"; vars.gridBiomes[key].desc = "Thick mist hangs over stagnant water."; vars.gridBiomes[key].icon = "🪷"; vars.gridBiomes[key].enemies = ["bandit"]; } } } } console.log("Water broken up - inland seas converted to land"); <</script>> <</widget>>
:: GenerateGrid_Cells [widget] <<widget "GenerateGrid_Cells">> <<script>> var vars = State.variables; var WORLD_RADIUS = vars.worldRadius || 60; // Add town at center vars.gridExists.push("0,0"); vars.gridBiomes["0,0"] = { type: "town", name: "Central City", desc: "The bustling heart of the region.", enemies: [], icon: "🏰" }; // Generate all other cells for (var x = -WORLD_RADIUS; x <= WORLD_RADIUS; x++) { for (var y = -WORLD_RADIUS; y <= WORLD_RADIUS; y++) { if (x === 0 && y === 0) continue; if (Math.sqrt(x*x + y*y) > WORLD_RADIUS) continue; var key = x + "," + y; vars.gridExists.push(key); // Placeholder - will be filled in next step vars.gridBiomes[key] = { type: "plains", name: "Temp", desc: "Temp", enemies: [], icon: "🌾" }; } } console.log("Cells created for radius " + WORLD_RADIUS + ": " + vars.gridExists.length); <</script>> <</widget>>
:: GenerateGrid_ClusterRareBiomes [widget] <<widget "GenerateGrid_ClusterRareBiomes">> <<script>> var vars = State.variables; var gridExistsSet = new Set(vars.gridExists); var rareBiomes = ["desert", "tundra", "swamp"]; var minClusterSize = 50; var maxClusterSize = 75; // For each rare biome type for (var b = 0; b < rareBiomes.length; b++) { var biomeType = rareBiomes[b]; // Find all cells of this biome var biomeCells = []; for (var i = 0; i < vars.gridExists.length; i++) { var key = vars.gridExists[i]; if (vars.gridBiomes[key].type === biomeType) { biomeCells.push(key); } } // Group into clusters using flood fill var visited = new Set(); var clusters = []; for (var i = 0; i < biomeCells.length; i++) { var startKey = biomeCells[i]; if (visited.has(startKey)) continue; // Flood fill to find connected cluster var cluster = []; var queue = [startKey]; visited.add(startKey); while (queue.length > 0) { var currentKey = queue.shift(); cluster.push(currentKey); var coords = currentKey.split(","); var x = parseInt(coords[0]); var y = parseInt(coords[1]); // Check 4-directional neighbors (orthogonal only for better clustering) var neighbors = [ (x+1) + "," + y, (x-1) + "," + y, x + "," + (y+1), x + "," + (y-1) ]; for (var n = 0; n < neighbors.length; n++) { var nKey = neighbors[n]; if (!visited.has(nKey) && vars.gridBiomes[nKey] && vars.gridBiomes[nKey].type === biomeType) { visited.add(nKey); queue.push(nKey); } } } if (cluster.length > 0) { clusters.push(cluster); } } // Process each cluster - resize if needed for (var c = 0; c < clusters.length; c++) { var cluster = clusters[c]; var clusterSize = cluster.length; // If cluster is too small, expand it by converting neighboring cells if (clusterSize < minClusterSize) { var needed = minClusterSize - clusterSize; var candidates = []; // Find all adjacent cells to this cluster for (var i = 0; i < cluster.length; i++) { var coords = cluster[i].split(","); var x = parseInt(coords[0]); var y = parseInt(coords[1]); for (var dx = -1; dx <= 1; dx++) { for (var dy = -1; dy <= 1; dy++) { if (dx === 0 && dy === 0) continue; var nKey = (x + dx) + "," + (y + dy); if (gridExistsSet.has(nKey) && vars.gridBiomes[nKey] && vars.gridBiomes[nKey].type !== biomeType && vars.gridBiomes[nKey].type !== "town" && vars.gridBiomes[nKey].type !== "village" && vars.gridBiomes[nKey].type !== "water") { // Prefer plains and forest for conversion var priority = (vars.gridBiomes[nKey].type === "plains") ? 1 : (vars.gridBiomes[nKey].type === "forest") ? 2 : 3; candidates.push({key: nKey, priority: priority}); } } } } // Sort by priority (plains first) candidates.sort(function(a, b) { return a.priority - b.priority; }); // Expand cluster for (var i = 0; i < Math.min(needed, candidates.length); i++) { var convertKey = candidates[i].key; var coords = convertKey.split(","); var cx = parseInt(coords[0]); var cy = parseInt(coords[1]); vars.gridBiomes[convertKey].type = biomeType; // Update name and icon if (biomeType === "desert") { vars.gridBiomes[convertKey].name = "Endless Desert"; vars.gridBiomes[convertKey].desc = "The sun beats down mercilessly."; vars.gridBiomes[convertKey].icon = "🏜️"; } else if (biomeType === "tundra") { vars.gridBiomes[convertKey].name = "Frozen Tundra"; vars.gridBiomes[convertKey].desc = "A bitter wind howls across the ice."; vars.gridBiomes[convertKey].icon = "❄️"; } else if (biomeType === "swamp") { vars.gridBiomes[convertKey].name = "Murky Swamp"; vars.gridBiomes[convertKey].desc = "Thick mist hangs over stagnant water."; vars.gridBiomes[convertKey].icon = "🪷"; } } } // If cluster is too large, shrink it (convert edge cells back to plains/forest) if (clusterSize > maxClusterSize) { var toRemove = clusterSize - maxClusterSize; // Remove from the edges first (simplified: remove random cells from the cluster) for (var i = 0; i < toRemove && i < cluster.length; i++) { var removeKey = cluster[i]; var coords = removeKey.split(","); var rx = parseInt(coords[0]); var ry = parseInt(coords[1]); // Convert back to plains vars.gridBiomes[removeKey].type = "plains"; vars.gridBiomes[removeKey].name = "Golden Plains"; vars.gridBiomes[removeKey].desc = "Open grassland as far as the eye can see."; vars.gridBiomes[removeKey].icon = "🌾"; } } } } console.log("Rare biomes clustered to 50-75 cells each"); <</script>> <</widget>>
:: GenerateGrid_Finalize [widget] <<widget "GenerateGrid_Finalize">> <<script>> var vars = State.variables; // Place a few villages var villageCount = 0; var attempts = 0; while (villageCount < 8 && attempts < 200) { var randomIndex = Math.floor(Math.random() * vars.gridExists.length); var key = vars.gridExists[randomIndex]; if (key !== "0,0" && vars.gridBiomes[key].type !== "water" && vars.gridBiomes[key].type !== "mountain") { vars.gridBiomes[key] = { type: "village", name: "Riverside Village", desc: "A peaceful farming community.", enemies: [], icon: "🏘️", template: "farming" }; villageCount++; } attempts++; } // Place a few enemy camps var campCount = 0; attempts = 0; while (campCount < 6 && attempts < 200) { var randomIndex = Math.floor(Math.random() * vars.gridExists.length); var key = vars.gridExists[randomIndex]; if (key !== "0,0" && (vars.gridBiomes[key].type === "forest" || vars.gridBiomes[key].type === "plains")) { vars.gridBiomes[key] = { type: "enemy_camp", name: "Bandit Camp", desc: "Dangerous outlaws gather here.", enemies: ["bandit"], icon: "⚠️", isCamp: true }; campCount++; } attempts++; } console.log("Villages placed: " + villageCount + ", Camps: " + campCount); <</script>> <</widget>>
:: GenerateGrid_Init [widget] <<widget "GenerateGrid_Init">> <<script>> var vars = State.variables; vars.gridExists = []; vars.gridBiomes = {}; vars.clearedCamps = []; console.log("Grid init complete"); <</script>> <</widget>>
:: GenerateGrid_Smooth [widget nobr] <<widget "GenerateGrid_Smooth">> <<script>> var vars = State.variables; var gridExistsSet = new Set(vars.gridExists); var protectedTypes = ["town", "village", "enemy_camp", "shrine", "dungeon", "treasure", "merchant"]; var biomeTypes = { forest: { enemies: ["forest_wolf", "goblin_scout", "giant_spider"], icon: "🌲", namePrefix: ["Dark", "Silent", "Ancient"], nameSuffix: ["Forest", "Woods", "Grove"] }, plains: { enemies: ["bandit", "wild_dog"], icon: "🌾", namePrefix: ["Golden", "Rolling", "Open"], nameSuffix: ["Plains", "Fields", "Meadows"] }, mountain: { enemies: ["mountain_goat", "harpy"], icon: "⛰️", namePrefix: ["High", "Frozen", "Jagged"], nameSuffix: ["Peaks", "Mountains", "Highlands"] }, water: { enemies: ["water_serpent", "reef_crab"], icon: "💧", namePrefix: ["Deep", "Blue", "Still"], nameSuffix: ["Lake", "River", "Sea"] }, swamp: { enemies: ["giant_frog", "swamp_leech"], icon: "🪷", namePrefix: ["Murky", "Black", "Rotting"], nameSuffix: ["Swamp", "Marsh", "Bog"] }, desert: { enemies: ["giant_scorpion", "dust_mephit"], icon: "🏜️", namePrefix: ["Endless", "Burning", "Scorched"], nameSuffix: ["Desert", "Dunes", "Waste"] }, tundra: { enemies: ["ice_wolf", "ice_mephit"], icon: "❄️", namePrefix: ["Frozen", "Bleak", "White"], nameSuffix: ["Tundra", "Wastes", "Barrens"] }, road: { enemies: ["bandit", "highway_archer"], icon: "🛣️", namePrefix: ["Old", "Winding", "Traveler's"], nameSuffix: ["Road", "Path", "Way"] } }; function generateName(type, x, y) { var biome = biomeTypes[type]; if (!biome) return type; var prefix = biome.namePrefix[Math.floor(Math.random() * biome.namePrefix.length)]; var suffix = biome.nameSuffix[Math.floor(Math.random() * biome.nameSuffix.length)]; return prefix + " " + suffix; } function generateDescription(type) { var descs = { forest: ["Tall trees block the sunlight.", "Wildlife scatters at your approach."], plains: ["Open grassland as far as the eye can see.", "Wildflowers dot the landscape."], mountain: ["Snow-capped peaks loom overhead.", "The air is thin and cold."], water: ["Waves lap against the shore.", "The water is deep and dark."], swamp: ["Thick mist hangs over stagnant water.", "Strange sounds emerge from the murk."], desert: ["Endless dunes stretch to the horizon.", "The sun beats down mercilessly."], tundra: ["A bitter wind howls across the ice.", "Nothing grows in this frozen wasteland."], road: ["The path continues onward.", "A well-traveled route."] }; var list = descs[type] || ["An unexplored area."]; return list[Math.floor(Math.random() * list.length)]; } for (var pass = 0; pass < 3; pass++) { var newBiomes = JSON.parse(JSON.stringify(vars.gridBiomes)); for (var i = 0; i < vars.gridExists.length; i++) { var key = vars.gridExists[i]; var coords = key.split(","); var x = parseInt(coords[0]); var y = parseInt(coords[1]); var current = vars.gridBiomes[key].type; if (protectedTypes.includes(current)) continue; var neighborCounts = {}; for (var dx = -1; dx <= 1; dx++) { for (var dy = -1; dy <= 1; dy++) { if (dx === 0 && dy === 0) continue; var nKey = (x + dx) + "," + (y + dy); if (gridExistsSet.has(nKey) && vars.gridBiomes[nKey]) { var nType = vars.gridBiomes[nKey].type; if (!protectedTypes.includes(nType)) { neighborCounts[nType] = (neighborCounts[nType] || 0) + 1; } } } } var majority = current; var maxCount = 0; for (var b in neighborCounts) { if (neighborCounts[b] > maxCount) { maxCount = neighborCounts[b]; majority = b; } } if (maxCount >= 4 && majority !== current && majority !== undefined) { newBiomes[key].type = majority; newBiomes[key].name = generateName(majority, x, y); newBiomes[key].desc = generateDescription(majority); newBiomes[key].icon = biomeTypes[majority] ? biomeTypes[majority].icon : "📍"; // CRITICAL: Copy enemies array for the new biome type newBiomes[key].enemies = biomeTypes[majority] ? biomeTypes[majority].enemies.slice() : []; } } vars.gridBiomes = newBiomes; } console.log("Smoothing complete - biomes clustered"); <</script>> <</widget>>
:: GenerateGrid_SpecialNodes [widget] <<widget "GenerateGrid_SpecialNodes">> <<script>> var vars = State.variables; var WORLD_RADIUS = vars.worldRadius || 60; // Calculate scaling factor based on world size var baseRadius = 60; var scaleFactor = WORLD_RADIUS / baseRadius; // Dynamic node counts based on world size (LINEAR scaling for density) var counts = { village: 0, enemy_camp: 0, shrine: 0, dungeon: 0, treasure: 0, merchant: 0 }; var maxCounts = { village: Math.max(6, Math.floor(12 * scaleFactor)), enemy_camp: Math.max(8, Math.floor(15 * scaleFactor)), shrine: Math.max(4, Math.floor(8 * scaleFactor)), dungeon: Math.max(3, Math.floor(6 * scaleFactor)), treasure: Math.max(5, Math.floor(10 * scaleFactor)), merchant: Math.max(4, Math.floor(8 * scaleFactor)) }; // Scale MINIMUM DISTANCE requirements (prevents clustering in small worlds) // For small worlds, nodes need to be closer together; for large worlds, spread out var minVillageDist = Math.max(5, Math.floor(8 * scaleFactor)); var minCampDist = Math.max(4, Math.floor(6 * scaleFactor)); var minShrineDist = Math.max(5, Math.floor(8 * scaleFactor)); var minDungeonDist = Math.max(6, Math.floor(10 * scaleFactor)); var minTreasureDist = Math.max(4, Math.floor(6 * scaleFactor)); var minMerchantDist = Math.max(4, Math.floor(6 * scaleFactor)); var biomeTypes = { road: { enemies: ["bandit"], icon: "🛣️", namePrefix: ["Old", "Winding", "Traveler's"], nameSuffix: ["Road", "Path", "Way"] }, forest: { enemies: ["forest_wolf"], icon: "🌲", namePrefix: ["Silent", "Dark", "Ancient"], nameSuffix: ["Woods", "Forest", "Grove"] }, plains: { enemies: [], icon: "🌾", namePrefix: ["Golden", "Rolling", "Open"], nameSuffix: ["Plains", "Fields", "Meadows"] }, mountain: { enemies: ["bear"], icon: "⛰️", namePrefix: ["High", "Frozen", "Jagged"], nameSuffix: ["Peaks", "Mountains", "Highlands"] }, water: { enemies: [], icon: "💧", namePrefix: ["Deep", "Blue", "Still"], nameSuffix: ["Lake", "River", "Sea"] }, village: { enemies: [], icon: "🏘️", namePrefix: ["Oak", "River", "Green", "Mill", "Fair"], nameSuffix: ["Village", "Town", "Settlement"] }, enemy_camp: { enemies: ["bandit"], icon: "⚠️", namePrefix: ["Bandit", "Raider", "Blackwood"], nameSuffix: ["Camp", "Hideout", "Den"], isCamp: true }, shrine: { enemies: [], icon: "🛐", namePrefix: ["Moon", "Sun", "Ancient", "Eternal"], nameSuffix: ["Shrine", "Altar", "Sanctuary"] }, dungeon: { enemies: ["skeleton"], icon: "🏛️", namePrefix: ["Shadow", "Frozen", "Forgotten"], nameSuffix: ["Crypt", "Halls", "Ruins"] }, treasure: { enemies: ["bandit"], icon: "💰", namePrefix: ["Hidden", "Lost", "Ancient"], nameSuffix: ["Cache", "Hoard", "Trove"] }, merchant: { enemies: [], icon: "🏪", namePrefix: ["Traveling", "Wandering"], nameSuffix: ["Merchant", "Trader"] }, swamp: { enemies: ["bandit"], icon: "🪷", namePrefix: ["Murky", "Black", "Rotting"], nameSuffix: ["Swamp", "Marsh", "Bog"] }, desert: { enemies: ["bandit"], icon: "🏜️", namePrefix: ["Endless", "Burning", "Scorched"], nameSuffix: ["Desert", "Dunes", "Waste"] }, tundra: { enemies: ["bear"], icon: "❄️", namePrefix: ["Frozen", "Bleak", "White"], nameSuffix: ["Tundra", "Wastes", "Barrens"] } }; function generateName(type, x, y) { var biome = biomeTypes[type]; if (!biome) return type; var prefix = biome.namePrefix[Math.floor(Math.random() * biome.namePrefix.length)]; var suffix = biome.nameSuffix[Math.floor(Math.random() * biome.nameSuffix.length)]; return prefix + " " + suffix; } function generateDescription(type) { var descs = { village: ["A peaceful farming community.", "A small trading post."], shrine: ["A place of spiritual power.", "Ancient stones hum with energy."], dungeon: ["Darkness beckons from within.", "Adventurers seek treasure here."], enemy_camp: ["Dangerous outlaws gather here.", "Smoke rises from campfires."], forest: ["Tall trees block the sunlight.", "Wildlife scatters at your approach."], road: ["The path continues onward.", "A well-traveled route."], plains: ["Open grassland as far as the eye can see.", "Wildflowers dot the landscape."], mountain: ["Snow-capped peaks loom overhead.", "The air is thin and cold."], water: ["Waves lap against the shore.", "The water is deep and dark."], treasure: ["Something valuable may be hidden here.", "Glints of gold catch your eye."], town: ["The bustling heart of the region.", "Merchants call out their wares."], merchant: ["A tent with various wares on display.", "Exotic goods from distant lands."], swamp: ["Thick mist hangs over stagnant water.", "Strange sounds emerge from the murk."], desert: ["Endless dunes stretch to the horizon.", "The sun beats down mercilessly."], tundra: ["A bitter wind howls across the ice.", "Nothing grows in this frozen wasteland."] }; var list = descs[type] || ["An unexplored area."]; return list[Math.floor(Math.random() * list.length)]; } function getVillageTemplate(x, y, gridBiomes, gridExistsSet) { var nearbyWater = false; var nearbyForest = false; var nearbyMountain = false; var nearbyRoad = false; var nearbyEnemyCamp = false; var nearbyDungeon = false; for (var dx = -1; dx <= 1; dx++) { for (var dy = -1; dy <= 1; dy++) { if (dx === 0 && dy === 0) continue; var nKey = (x + dx) + "," + (y + dy); if (gridExistsSet.has(nKey) && gridBiomes[nKey]) { var neighborType = gridBiomes[nKey].type; if (neighborType === "water") nearbyWater = true; if (neighborType === "forest") nearbyForest = true; if (neighborType === "mountain") nearbyMountain = true; if (neighborType === "road") nearbyRoad = true; if (neighborType === "enemy_camp") nearbyEnemyCamp = true; if (neighborType === "dungeon") nearbyDungeon = true; } } } var ruinsChance = 8; if (nearbyEnemyCamp) ruinsChance += 12; if (nearbyDungeon) ruinsChance += 15; ruinsChance = Math.min(ruinsChance, 40); if (Math.random() * 100 < ruinsChance) { return "ruins"; } if (nearbyWater) return "fishing"; if (nearbyForest) return "logging"; if (nearbyMountain) return "mining"; if (nearbyRoad) return "trade"; var templates = ["farming", "farming", "farming", "hunting", "religious", "remote", "military", "magical"]; return templates[Math.floor(Math.random() * templates.length)]; } var gridExistsSet = new Set(vars.gridExists); // Create an array of candidate cells (excluding town and water) var candidates = []; for (var i = 0; i < vars.gridExists.length; i++) { var key = vars.gridExists[i]; if (key !== "0,0" && vars.gridBiomes[key].type !== "water") { candidates.push(key); } } // Shuffle candidates for (var i = candidates.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = candidates[i]; candidates[i] = candidates[j]; candidates[j] = temp; } // Track placed positions to enforce minimum distances between nodes var placedPositions = []; function isTooClose(x, y, minDist) { for (var p = 0; p < placedPositions.length; p++) { var dx = Math.abs(x - placedPositions[p].x); var dy = Math.abs(y - placedPositions[p].y); var dist = Math.sqrt(dx*dx + dy*dy); if (dist < minDist) { return true; } } return false; } // Place special nodes for (var i = 0; i < candidates.length && (counts.village < maxCounts.village || counts.enemy_camp < maxCounts.enemy_camp || counts.shrine < maxCounts.shrine || counts.dungeon < maxCounts.dungeon || counts.treasure < maxCounts.treasure || counts.merchant < maxCounts.merchant); i++) { var key = candidates[i]; var coords = key.split(","); var x = parseInt(coords[0]); var y = parseInt(coords[1]); var dist = Math.abs(x) + Math.abs(y); var currentType = vars.gridBiomes[key].type; var roll = Math.random(); // Skip if already a special node var specialTypes = ["village", "enemy_camp", "shrine", "dungeon", "treasure", "merchant"]; if (specialTypes.includes(currentType)) continue; // Villages - with distance check if (counts.village < maxCounts.village && dist >= minVillageDist && (currentType === "plains" || currentType === "forest") && roll < 0.03) { if (!isTooClose(x, y, minVillageDist)) { var template = getVillageTemplate(x, y, vars.gridBiomes, gridExistsSet); vars.gridBiomes[key] = { type: "village", name: generateName("village", x, y), desc: generateDescription("village"), enemies: [], icon: template === "ruins" ? "🏚️" : "🏘️", template: template }; counts.village++; placedPositions.push({x: x, y: y, type: "village"}); } } // Enemy camps else if (counts.enemy_camp < maxCounts.enemy_camp && dist >= minCampDist && (currentType === "forest" || currentType === "plains" || currentType === "mountain") && roll < 0.04) { if (!isTooClose(x, y, minCampDist)) { vars.gridBiomes[key] = { type: "enemy_camp", name: generateName("enemy_camp", x, y), desc: generateDescription("enemy_camp"), enemies: ["bandit"], icon: "⚠️", isCamp: true }; counts.enemy_camp++; placedPositions.push({x: x, y: y, type: "enemy_camp"}); } } // Shrines else if (counts.shrine < maxCounts.shrine && dist >= minShrineDist && (currentType === "forest" || currentType === "plains" || currentType === "mountain") && roll < 0.02) { if (!isTooClose(x, y, minShrineDist)) { vars.gridBiomes[key] = { type: "shrine", name: generateName("shrine", x, y), desc: generateDescription("shrine"), enemies: [], icon: "🛐" }; counts.shrine++; placedPositions.push({x: x, y: y, type: "shrine"}); } } // Dungeons else if (counts.dungeon < maxCounts.dungeon && dist >= minDungeonDist && (currentType === "mountain" || currentType === "swamp" || currentType === "desert") && roll < 0.02) { if (!isTooClose(x, y, minDungeonDist)) { vars.gridBiomes[key] = { type: "dungeon", name: generateName("dungeon", x, y), desc: generateDescription("dungeon"), enemies: ["skeleton"], icon: "🏛️" }; counts.dungeon++; placedPositions.push({x: x, y: y, type: "dungeon"}); } } // Treasure else if (counts.treasure < maxCounts.treasure && dist >= minTreasureDist && roll < 0.025) { if (!isTooClose(x, y, minTreasureDist)) { vars.gridBiomes[key] = { type: "treasure", name: generateName("treasure", x, y), desc: generateDescription("treasure"), enemies: ["bandit"], icon: "💰" }; counts.treasure++; placedPositions.push({x: x, y: y, type: "treasure"}); } } // Merchants else if (counts.merchant < maxCounts.merchant && dist >= minMerchantDist && (currentType === "road" || currentType === "plains") && roll < 0.02) { if (!isTooClose(x, y, minMerchantDist)) { vars.gridBiomes[key] = { type: "merchant", name: generateName("merchant", x, y), desc: generateDescription("merchant"), enemies: [], icon: "🏪" }; counts.merchant++; placedPositions.push({x: x, y: y, type: "merchant"}); } } } console.log("Dynamic special nodes placed for radius " + WORLD_RADIUS); console.log(" Villages: " + counts.village + " / " + maxCounts.village + " (min dist: " + minVillageDist + ")"); console.log(" Enemy Camps: " + counts.enemy_camp + " / " + maxCounts.enemy_camp + " (min dist: " + minCampDist + ")"); console.log(" Shrines: " + counts.shrine + " / " + maxCounts.shrine + " (min dist: " + minShrineDist + ")"); console.log(" Dungeons: " + counts.dungeon + " / " + maxCounts.dungeon + " (min dist: " + minDungeonDist + ")"); console.log(" Treasure: " + counts.treasure + " / " + maxCounts.treasure + " (min dist: " + minTreasureDist + ")"); console.log(" Merchants: " + counts.merchant + " / " + maxCounts.merchant + " (min dist: " + minMerchantDist + ")"); <</script>> <</widget>>
<<nobr>> <style> .game-container { display: flex; flex-direction: row; gap: 20px; flex-wrap: wrap; } .map-panel { flex: 1; min-width: 300px; background: rgba(0,0,0,0.3); border-radius: 15px; padding: 20px; border: 1px solid #d4af37; text-align: center; } .info-panel { flex: 1; min-width: 300px; background: rgba(0,0,0,0.3); border-radius: 15px; padding: 20px; border: 1px solid #d4af37; } @media (min-width: 901px) { .game-container { flex-wrap: nowrap; } } @media (max-width: 900px) { .game-container { flex-direction: column; } } .passage { max-width: 1200px !important; width: 95% !important; margin: 20px auto !important; } </style> <<if !$coord>> <<set $coord = "0,0">> <</if>> <<set $coords = $coord.split(",")>> <<set $gridX = Number($coords[0])>> <<set $gridY = Number($coords[1])>> <div class="game-container"> <div class="map-panel"> <<MiniMap>> <div style="margin-top: 15px;"> <<Movement_Compass>> </div> </div> <div class="info-panel"> <<MapLoc_Info>> </div> </div> <</nobr>>
<<nobr>> <div class="fancy-header">🏨 The Rusty Nail Inn 🏨</div> <div style="background: rgba(0,0,0,0.5); border-radius: 15px; padding: 20px; margin: 15px 0; text-align: center;"> <div style="font-size: 48px; margin-bottom: 10px;">🛏️</div> <div style="font-family: 'Cinzel', serif; color: #d4af37; font-size: 20px;">"Welcome, traveler!"</div> <div style="margin: 15px 0;">The innkeeper smiles warmly and shows you to a cozy room.</div> <!-- Show confirmation message if just rested --> <<if $restMessage>> <div class="inn-message"> $restMessage </div> <<link "Return to Map">> <<set $restMessage = null>> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $coord = $gridX + "," + $gridY>> <<goto "GridCell">> <</link>> <<else>> <!-- Rest Options --> <<if $gold >= 10>> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 15px; margin: 15px 0;"> <div style="font-size: 18px;">💰 Standard Room - <span style="color: #ffd700;">10 gold</span></div> <div style="font-size: 14px; color: #aaa;">Restores all HP and MP. Advances time by 8 hours.</div> <<link "💤 Rest for the night">> <<set $gold -= 10>> <<set $currentHP = $maxHP>> <<set $currentMP = $maxMP>> <<run window.setup.time.advance(8)>> <<set $hour12 = $hour % 12>> <<if $hour12 == 0>><<set $hour12 = 12>><</if>> <<set $ampm = $hour < 12 ? "AM" : "PM">> <<set $minuteStr = $minute < 10 ? "0" + $minute : $minute>> <<set $restMessage = "✨ You wake up refreshed! ✨<br>❤️ HP fully restored!<br>💙 MP fully restored!<br>💰 You paid 10 gold.<br>🕐 Time advances to $hour12:$minuteStr $ampm on Day $day">> <<goto "InnRest">> <</link>> </div> <</if>> <!-- Free rest option (always available, but shows differently based on gold) --> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 15px; margin: 15px 0;"> <div style="font-size: 18px;"> <<if $gold >= 10>>🪵<<else>>❤️<</if>> <<if $gold >= 10>>Hearth Rest - <span style="color: #88ff88;">Free</span><<else>>Free Hearth Rest<</if>> </div> <div style="font-size: 14px; color: #aaa;"> <<if $gold >= 10>> Rest by the hearth instead of paying for a room. Restores all HP and MP. Advances time by 8 hours. <<else>> The innkeeper takes pity on you and lets you rest by the hearth for free. Restores all HP and MP. Advances time by 8 hours. <</if>> </div> <<link "💤 Rest by the hearth">> <<set $currentHP = $maxHP>> <<set $currentMP = $maxMP>> <<run window.setup.time.advance(8)>> <<set $hour12 = $hour % 12>> <<if $hour12 == 0>><<set $hour12 = 12>><</if>> <<set $ampm = $hour < 12 ? "AM" : "PM">> <<set $minuteStr = $minute < 10 ? "0" + $minute : $minute>> <<set $restMessage = "✨ You wake up feeling rested! ✨<br>❤️ HP fully restored!<br>💙 MP fully restored!<br>🕐 Time advances to $hour12:$minuteStr $ampm on Day $day">> <<goto "InnRest">> <</link>> </div> <div style="margin-top: 20px;"> <div style="margin-top: 20px;"> <<link "🚪 Leave the inn">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $coord = $gridX + "," + $gridY>> <<goto "GridCell">> <</link>> </div> </div> <</if>> </div> <</nobr>>
:: Inventory [nobr] <<nobr>> <div class="fancy-header">🎒 Inventory 🎒</div> <div style="background: rgba(0,0,0,0.5); border-radius: 15px; padding: 15px; margin-bottom: 15px; text-align: center;"> 💰 Gold: <span style="color: #ffd700; font-weight: bold;">$gold</span> </div> <div id="inventory-container"></div> <<if $returnTo && $returnTo != "Inventory">> <<link "❌ Close">> <<set $temp = $returnTo>> <<set $returnTo = "">> <<if $temp == "GridCell">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $coord = $gridX + "," + $gridY>> <</if>> <<goto $temp>> <</link>> <<else>> <<link "🗺️ Return to Map">> <<set $returnTo = "">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $coord = $gridX + "," + $gridY>> <<goto "GridCell">> <</link>> <</if>> <<script>> setTimeout(function() { window.renderInventory(); }, 50); <</script>> <</nobr>>
<<nobr>> <<widget "loadInventory">> <<script>> window.setup = window.setup || {}; window.setup.inventory = { getItemData: function(itemId) { if (!itemId) return null; return window.setup.getItem(itemId); }, addItem: function(itemId, quantity = 1) { console.log("=== addItem DEBUG ==="); console.log("Item ID:", itemId); console.log("Quantity:", quantity); let itemData = this.getItemData(itemId); if (!itemData) { console.log("ERROR: Item not found -", itemId); return { success: false, message: "Item not found!" }; } console.log("Item found:", itemData.name); let current = State.variables.inventory[itemId] || 0; console.log("Current count:", current); State.variables.inventory[itemId] = current + quantity; console.log("New count:", State.variables.inventory[itemId]); console.log("=== addItem END ==="); return { success: true, message: `Added ${quantity}x ${itemData.name}!` }; }, removeItem: function(itemId, quantity = 1) { let current = State.variables.inventory[itemId] || 0; if (current < quantity) return { success: false, message: "Not enough items!" }; let itemData = this.getItemData(itemId); let newQuantity = current - quantity; if (newQuantity <= 0) { delete State.variables.inventory[itemId]; } else { State.variables.inventory[itemId] = newQuantity; } return { success: true, message: `Removed ${quantity}x ${itemData.name}!` }; }, useItem: function(itemId) { let quantity = State.variables.inventory[itemId] || 0; if (quantity === 0) return { success: false, message: "You don't have that item!" }; let itemData = this.getItemData(itemId); if (!itemData || itemData.type !== "consumable") return { success: false, message: "Cannot use that item!" }; let result = { success: true, message: "" }; if (itemData.effect === "heal") { let oldHP = State.variables.currentHP; State.variables.currentHP = Math.min(State.variables.maxHP, State.variables.currentHP + itemData.value); let healed = State.variables.currentHP - oldHP; result.message = `🧪 Used ${itemData.name}! Restored ${healed} HP!`; } else if (itemData.effect === "mana") { let oldMP = State.variables.currentMP; State.variables.currentMP = Math.min(State.variables.maxMP, State.variables.currentMP + itemData.value); let restored = State.variables.currentMP - oldMP; result.message = `💙 Used ${itemData.name}! Restored ${restored} MP!`; } this.removeItem(itemId); return result; }, equipItem: function(itemId) { let quantity = State.variables.inventory[itemId] || 0; if (quantity === 0) return { success: false, message: "You don't have that item!" }; let itemData = this.getItemData(itemId); if (!itemData) return { success: false, message: "Item not found!" }; let equippableTypes = ["weapon", "armor", "helmet", "boots", "ring", "amulet"]; if (!equippableTypes.includes(itemData.type)) return { success: false, message: "Cannot equip that item!" }; let slot = itemData.slot || itemData.type; let currentEquipped = State.variables.equipment[slot]; if (currentEquipped && currentEquipped !== "fists" && currentEquipped !== "rags") { let currentItemData = this.getItemData(currentEquipped); if (currentItemData) this.addItem(currentEquipped, 1); } State.variables.equipment[slot] = itemData.id; this.removeItem(itemId); this.recalculateStats(); return { success: true, message: `⚔️ Equipped ${itemData.name}!` }; }, unequipItem: function(slot) { let itemId = State.variables.equipment[slot]; if (!itemId) return { success: false, message: "Nothing equipped!" }; if (itemId === "fists" || itemId === "rags") return { success: false, message: "Cannot unequip your default equipment!" }; let itemData = this.getItemData(itemId); if (!itemData) return { success: false, message: "Item data not found!" }; this.addItem(itemId, 1); State.variables.equipment[slot] = null; this.recalculateStats(); return { success: true, message: `🔓 Unequipped ${itemData.name}!` }; }, recalculateStats: function() { let str = State.variables.baseStrength; let con = State.variables.baseConstitution; let def = State.variables.baseDefense; let agi = State.variables.baseAgility; let int = State.variables.baseIntellect; let mpBonus = 0; let w = this.getItemData(State.variables.equipment.weapon); if (w && w.id !== "fists") { str += w.strength || 0; con += w.constitution || 0; def += w.defense || 0; agi += w.agility || 0; int += w.intellect || 0; mpBonus += w.maxMP || 0; } let a = this.getItemData(State.variables.equipment.armor); if (a && a.id !== "rags") { str += a.strength || 0; con += a.constitution || 0; def += a.defense || 0; agi += a.agility || 0; int += a.intellect || 0; mpBonus += a.maxMP || 0; } let h = this.getItemData(State.variables.equipment.helmet); if (h) { str += h.strength || 0; con += h.constitution || 0; def += h.defense || 0; agi += h.agility || 0; int += h.intellect || 0; mpBonus += h.maxMP || 0; } let b = this.getItemData(State.variables.equipment.boots); if (b) { str += b.strength || 0; con += b.constitution || 0; def += b.defense || 0; agi += b.agility || 0; int += b.intellect || 0; mpBonus += b.maxMP || 0; } let r = this.getItemData(State.variables.equipment.ring); if (r) { str += r.strength || 0; con += r.constitution || 0; def += r.defense || 0; agi += r.agility || 0; int += r.intellect || 0; mpBonus += r.maxMP || 0; } let am = this.getItemData(State.variables.equipment.amulet); if (am) { str += am.strength || 0; con += am.constitution || 0; def += am.defense || 0; agi += am.agility || 0; int += am.intellect || 0; mpBonus += am.maxMP || 0; } State.variables.strength = str; State.variables.constitution = con; State.variables.defense = def; State.variables.agility = agi; State.variables.intellect = int; State.variables.maxHP = State.variables.baseMaxHP + (con - State.variables.baseConstitution) * 2; if (State.variables.currentHP > State.variables.maxHP) State.variables.currentHP = State.variables.maxHP; State.variables.maxMP = 20 + Math.floor(int / 2) + mpBonus; if (State.variables.currentMP > State.variables.maxMP) State.variables.currentMP = State.variables.maxMP; if (window.setup.updateSidebar) window.setup.updateSidebar(); }, getEquipComparison: function(itemId) { var item = window.setup.getItem(itemId); if (!item || !item.slot) return ""; var currentId = State.variables.equipment[item.slot]; var currentItem = currentId ? window.setup.getItem(currentId) : null; var comparisons = []; var stats = ["strength", "constitution", "defense", "agility", "intellect", "damage"]; var statNames = {"strength": "STR", "constitution": "CON", "defense": "DEF", "agility": "AGI", "intellect": "INT", "damage": "DMG"}; var statIcons = {"strength": "💪", "constitution": "❤️", "defense": "🛡️", "agility": "⚡", "intellect": "🧠", "damage": "⚔️"}; for (var i = 0; i < stats.length; i++) { var stat = stats[i]; var newVal = item[stat] || 0; var oldVal = currentItem ? (currentItem[stat] || 0) : 0; var diff = newVal - oldVal; if (diff !== 0) { var plusMinus = diff > 0 ? "+" : ""; var arrow = diff > 0 ? "▲" : (diff < 0 ? "▼" : ""); comparisons.push(statIcons[stat] + " " + statNames[stat] + ": " + oldVal + " → " + newVal + " (" + arrow + " " + plusMinus + diff + ")"); } } var specialStats = ["critical", "lifesteal", "poison", "burn", "maxMP"]; var specialNames = {"critical": "CRIT", "lifesteal": "LIFE", "poison": "POISON", "burn": "BURN", "maxMP": "MAX MP"}; var specialIcons = {"critical": "💀", "lifesteal": "💚", "poison": "💚", "burn": "🔥", "maxMP": "💙"}; for (var s = 0; s < specialStats.length; s++) { var spStat = specialStats[s]; var newSpVal = item[spStat] || 0; var oldSpVal = currentItem ? (currentItem[spStat] || 0) : 0; var spDiff = newSpVal - oldSpVal; if (spDiff !== 0 && (newSpVal > 0 || oldSpVal > 0)) { var spPlusMinus = spDiff > 0 ? "+" : ""; var spArrow = spDiff > 0 ? "▲" : (spDiff < 0 ? "▼" : ""); comparisons.push(specialIcons[spStat] + " " + specialNames[spStat] + ": " + oldSpVal + " → " + newSpVal + " (" + spArrow + " " + spPlusMinus + spDiff + ")"); } } if (comparisons.length === 0) return "No stat changes"; var result = "📊 Stat Changes:\n"; for (var c = 0; c < comparisons.length; c++) { result += "• " + comparisons[c] + "\n"; } return result; } }; console.log("✅ Inventory module loaded via widget"); <</script>> <</widget>> <</nobr>>
<<nobr>> <div class="fancy-header">📦 ITEM DATABASE MANAGER 📦</div> <div style="background: rgba(0,0,0,0.5); border-radius: 15px; padding: 20px; margin: 15px 0; text-align: center;"> <div style="font-family: 'Cinzel', serif; color: #d4af37; font-size: 20px;">Item Database Modules</div> <div style="margin: 10px 0; color: #aaa;">Click a category to create or edit its item data passage.</div> </div> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; margin-bottom: 20px;"> [[⚔️ Weapons|00_Items_Weapons_Starters]] [[🛡️ Armor|00_Items_Armor]] [[⛑️ Helmets|00_Items_Helmets]] [[👢 Boots|00_Items_Boots]] [[💍 Rings|00_Items_Rings]] [[📿 Amulets|00_Items_Amulets]] [[🧪 Potions|00_Items_Potions]] [[📖 Spellbooks|00_Items_Spellbooks]] </div> <<widget "loadItems">> <<script>> window.setup = window.setup || {}; window.setup.items = window.setup.items || {}; window.setup.addItems = function(itemData) { Object.assign(window.setup.items, itemData); console.log("✅ Added", Object.keys(itemData).length, "items to database"); }; window.setup.getItem = function(itemId) { let result = window.setup.items[itemId]; if (!result) { console.warn("Item not found:", itemId); return null; } return JSON.parse(JSON.stringify(result)); }; console.log("✅ Item loader module loaded"); <</script>> <</widget>> <</nobr>>
:: LoadingWorld [nobr] <<nobr>> <div style="text-align: center; padding: 50px;"> <div style="font-size: 64px; margin-bottom: 20px; animation: spin 2s infinite linear;">🗺️</div> <h2 style="color: #d4af37;">Generating Your World...</h2> <div style="width: 300px; height: 8px; background: #333; border-radius: 10px; margin: 20px auto; overflow: hidden;"> <div style="width: 100%; height: 100%; background: #d4af37; animation: loading 3s ease-out;"></div> </div> <div id="gen-status" style="margin-top: 20px; font-size: 12px; color: #888;">Creating world...</div> </div> <style> @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } @keyframes loading { 0% { width: 0%; } 100% { width: 100%; } } </style> <<GenerateGrid_Init>> <<GenerateGrid_Cells>> <<GenerateGrid_Biomes>> <<GenerateGrid_Smooth>> <<GenerateGrid_SpecialNodes>> <<GenerateGrid_Finalize>> <<set $gridExistsSet = new Set($gridExists)>> <<set $discoveredSet = new Set($discovered)>> <<set $gridX = 0>> <<set $gridY = 0>> <!-- FORCE DISCOVER 5x5 AREA AROUND TOWN (radius 2) --> <<for _dx = -2; _dx <= 2; _dx++>> <<for _dy = -2; _dy <= 2; _dy++>> <<set _key = (_dx) + "," + (_dy)>> <<if $gridExistsSet.has(_key) && !$discoveredSet.has(_key)>> <<set $discovered.push(_key)>> <<set $discoveredSet.add(_key)>> <</if>> <</for>> <</for>> <<set $returnToGridX = null>> <<set $returnToGridY = null>> <<set $inCombat = false>> <<set $coord = "0,0">> <<run setTimeout(function() { var statusDiv = document.getElementById('gen-status'); if (statusDiv) statusDiv.innerHTML = "World ready! " + State.variables.gridExists.length + " locations (radius " + State.variables.worldRadius + ")."; setTimeout(function() { SugarCube.Engine.play("GridCell"); }, 500); }, 2000)>> <</nobr>>
<<nobr>> <div class="fancy-header">🔮 The Arcane Emporium 🔮</div> <div style="background: rgba(0,0,0,0.5); border-radius: 15px; padding: 20px; margin: 15px 0; text-align: center;"> <div style="font-size: 48px; margin-bottom: 10px;">🧙</div> <div style="font-family: 'Cinzel', serif; color: #d4af37; font-size: 20px;">"Welcome, seeker of magical wonders!"</div> <div style="margin: 10px 0; color: #aaa;">The old mage gestures to shelves filled with glowing artifacts and shimmering potions.</div> <div style="margin: 10px 0;">💰 Your Gold: <span style="color: #ffd700; font-weight: bold;">$gold</span></div> </div> <!-- Mana Potions Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #88aaff; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #88aaff; display: inline-block;">💙 Mana Potions</div> <div class="shop-item"> <span class="shop-icon">💙</span> <span class="shop-name">Mana Potion</span> <span class="shop-price">💰 15 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("mana_potion", 1)>> <<goto "MagicShop">> <</link>> <span style="margin-left: auto;">Restores 20 MP</span> </div> <div class="shop-item"> <span class="shop-icon">💙✨</span> <span class="shop-name">Greater Mana Potion</span> <span class="shop-price">💰 40 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("greater_mana_potion", 1)>> <<goto "MagicShop">> <</link>> <span style="margin-left: auto;">Restores 50 MP</span> </div> </div> <!-- Armor Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #88ff88; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #88ff88; display: inline-block;">🛡️ Magical Armor</div> <div class="shop-item"> <span class="shop-icon">🥼</span> <span class="shop-name">Robe of the Mage</span> <span class="shop-price">💰 120 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("robe_of_the_mage", 1)>> <<goto "MagicShop">> <</link>> <span style="margin-left: auto;">DEF +4, INT +5, AGI +2</span> </div> </div> <!-- Helmets Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #d4af37; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #d4af37; display: inline-block;">🪖 Magical Hats</div> <div class="shop-item"> <span class="shop-icon">🧙</span> <span class="shop-name">Wizard's Hat</span> <span class="shop-price">💰 50 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("wizard_hat", 1)>> <<goto "MagicShop">> <</link>> <span style="margin-left: auto;">DEF +2, INT +4</span> </div> <div class="shop-item"> <span class="shop-icon">👑</span> <span class="shop-name">Arcane Circlet</span> <span class="shop-price">💰 80 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("arcane_circlet", 1)>> <<goto "MagicShop">> <</link>> <span style="margin-left: auto;">DEF +1, INT +6</span> </div> </div> <!-- Boots Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #88aaff; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #88aaff; display: inline-block;">👢 Magical Boots</div> <div class="shop-item"> <span class="shop-icon">👢</span> <span class="shop-name">Sorcerer's Boots</span> <span class="shop-price">💰 60 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("sorcerer_boots", 1)>> <<goto "MagicShop">> <</link>> <span style="margin-left: auto;">DEF +2, AGI +3, INT +2</span> </div> </div> <!-- Rings Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #ffd700; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #ffd700; display: inline-block;">💍 Magical Rings</div> <div class="shop-item"> <span class="shop-icon">💍</span> <span class="shop-name">Ring of Intellect</span> <span class="shop-price">💰 80 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("ring_of_intellect", 1)>> <<goto "MagicShop">> <</link>> <span style="margin-left: auto;">INT +5</span> </div> <div class="shop-item"> <span class="shop-icon">💍</span> <span class="shop-name">Ring of Magic</span> <span class="shop-price">💰 70 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("ring_of_magic", 1)>> <<goto "MagicShop">> <</link>> <span style="margin-left: auto;">INT +3, DEF +2</span> </div> </div> <!-- Amulets Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #ff88aa; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #ff88aa; display: inline-block;">📿 Magical Amulets</div> <div class="shop-item"> <span class="shop-icon">📿</span> <span class="shop-name">Amulet of Wisdom</span> <span class="shop-price">💰 90 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("amulet_of_wisdom", 1)>> <<goto "MagicShop">> <</link>> <span style="margin-left: auto;">INT +5, Max MP +15</span> </div> <div class="shop-item"> <span class="shop-icon">📿✨</span> <span class="shop-name">Amulet of the Magi</span> <span class="shop-price">💰 160 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("amulet_of_the_magi", 1)>> <<goto "MagicShop">> <</link>> <span style="margin-left: auto;">INT +8, DEF +3</span> </div> </div> <!-- Sell Items Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #ff8888; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #ff8888; display: inline-block;">💰 Sell Magical Items</div> <div style="font-size: 12px; color: #aaa; margin-bottom: 10px;">Sell your magical items for gold</div> <!-- Mana Potions --> <<if $inventory.mana_potion > 0>> <div class="shop-item"> <span>💙 Mana Potion (x$inventory.mana_potion)</span> <<link "💰 Sell (8 gold)">> <<set $result = window.setup.shop.sellItem("mana_potion", 1)>> <<goto "MagicShop">> <</link>> </div> <</if>> <<if $inventory.greater_mana_potion > 0>> <div class="shop-item"> <span>💙✨ Greater Mana Potion (x$inventory.greater_mana_potion)</span> <<link "💰 Sell (20 gold)">> <<set $result = window.setup.shop.sellItem("greater_mana_potion", 1)>> <<goto "MagicShop">> <</link>> </div> <</if>> <!-- Magical Armor --> <<if $inventory.robe_of_the_mage > 0>> <div class="shop-item"> <span>🥼 Robe of the Mage (x$inventory.robe_of_the_mage)</span> <<link "💰 Sell (60 gold)">> <<set $result = window.setup.shop.sellItem("robe_of_the_mage", 1)>> <<goto "MagicShop">> <</link>> </div> <</if>> <!-- Magical Helmets --> <<if $inventory.wizard_hat > 0>> <div class="shop-item"> <span>🧙 Wizard's Hat (x$inventory.wizard_hat)</span> <<link "💰 Sell (25 gold)">> <<set $result = window.setup.shop.sellItem("wizard_hat", 1)>> <<goto "MagicShop">> <</link>> </div> <</if>> <<if $inventory.arcane_circlet > 0>> <div class="shop-item"> <span>👑 Arcane Circlet (x$inventory.arcane_circlet)</span> <<link "💰 Sell (40 gold)">> <<set $result = window.setup.shop.sellItem("arcane_circlet", 1)>> <<goto "MagicShop">> <</link>> </div> <</if>> <!-- Magical Boots --> <<if $inventory.sorcerer_boots > 0>> <div class="shop-item"> <span>👢 Sorcerer's Boots (x$inventory.sorcerer_boots)</span> <<link "💰 Sell (30 gold)">> <<set $result = window.setup.shop.sellItem("sorcerer_boots", 1)>> <<goto "MagicShop">> <</link>> </div> <</if>> <!-- Magical Rings --> <<if $inventory.ring_of_intellect > 0>> <div class="shop-item"> <span>💍 Ring of Intellect (x$inventory.ring_of_intellect)</span> <<link "💰 Sell (40 gold)">> <<set $result = window.setup.shop.sellItem("ring_of_intellect", 1)>> <<goto "MagicShop">> <</link>> </div> <</if>> <<if $inventory.ring_of_magic > 0>> <div class="shop-item"> <span>💍 Ring of Magic (x$inventory.ring_of_magic)</span> <<link "💰 Sell (35 gold)">> <<set $result = window.setup.shop.sellItem("ring_of_magic", 1)>> <<goto "MagicShop">> <</link>> </div> <</if>> <!-- Magical Amulets --> <<if $inventory.amulet_of_wisdom > 0>> <div class="shop-item"> <span>📿 Amulet of Wisdom (x$inventory.amulet_of_wisdom)</span> <<link "💰 Sell (45 gold)">> <<set $result = window.setup.shop.sellItem("amulet_of_wisdom", 1)>> <<goto "MagicShop">> <</link>> </div> <</if>> <<if $inventory.amulet_of_the_magi > 0>> <div class="shop-item"> <span>📿✨ Amulet of the Magi (x$inventory.amulet_of_the_magi)</span> <<link "💰 Sell (80 gold)">> <<set $result = window.setup.shop.sellItem("amulet_of_the_magi", 1)>> <<goto "MagicShop">> <</link>> </div> <</if>> <<if $inventory.mana_potion == 0 && $inventory.greater_mana_potion == 0 && $inventory.robe_of_the_mage == 0 && $inventory.wizard_hat == 0 && $inventory.arcane_circlet == 0 && $inventory.sorcerer_boots == 0 && $inventory.ring_of_intellect == 0 && $inventory.ring_of_magic == 0 && $inventory.amulet_of_wisdom == 0 && $inventory.amulet_of_the_magi == 0>> <div style="text-align: center; padding: 10px; color: #888;">No magical items to sell</div> <</if>> </div> <div style="text-align: center; margin-top: 20px;"> <<link "🚪 Leave the Arcane Emporium">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $coord = $gridX + "," + $gridY>> <<goto "GridCell">> <</link>> </div> <!-- Message display --> <<if $result && $result.message>> <div style="position: fixed; top: 20px; left: 50%; transform: translateX(-50%); background: #1a1a2e; border: 2px solid #d4af37; border-radius: 10px; padding: 10px 20px; animation: fadeOutMsg 2s forwards; z-index: 1000;"> $result.message </div> <<set $result = null>> <</if>> <</nobr>>
<<widget "MapLoc_Info">> <<nobr>> <<set _key = $gridX + "," + $gridY>> <<set _data = $gridBiomes[_key]>> <!-- DEBUG PANEL - Shows encounter conditions --> <div style="background: #1a1a2a; border: 2px solid #ffaa44; border-radius: 8px; padding: 8px; margin: 8px; font-size: 11px; font-family: monospace; color: #ffaa44;"> <b>🔍 ENCOUNTER DEBUG</b><br> Biome type: <span style="color: #88ff88;">_data.type</span><br> Enemies count: <span style="color: #88ff88;">_data.enemies.length</span><br> Encounter Rate: <span style="color: #88ff88;">$encounterRates[_data.type]</span><br> Random Encounters Enabled: <span style="color: #88ff88;">$randomEncountersEnabled</span><br> Encounter Triggered flag: <span style="color: #88ff88;">$encounterTriggered</span><br> In Combat flag: <span style="color: #88ff88;">$inCombat</span><br> <br> <b>Condition Check:</b><br> $randomEncountersEnabled && !$encounterTriggered && _data.type != "town" && _data.type != "village" && _data.type != "shrine" && _data.type != "merchant"<br> = <span style="color: #ffaa88;">($randomEncountersEnabled) && (!$encounterTriggered) && (_data.type !== "town") && (_data.type !== "village") && (_data.type !== "shrine") && (_data.type !== "merchant")</span> </div> <!-- Random Encounter Check - happens ONCE per visit --> <<if $randomEncountersEnabled && !$encounterTriggered && _data.type != "town" && _data.type != "village" && _data.type != "shrine" && _data.type != "merchant">> <<set _encounterRate = $encounterRates[_data.type] || 5>> <<if random(1, 100) <= _encounterRate && _data.enemies && _data.enemies.length > 0>> <<set $encounterTriggered = true>> <<set $combatState = "active">> <<set $inCombat = true>> <<set $combatLogText = "⚔️ A wild enemy appears! ⚔️<br>">> <<set $combatActions = 0>> <<set $castingSpell = null>> <<set $selectingPotion = null>> <<set $playerStunned = false>> <<set $playerConfused = false>> <<set $isTestBattle = false>> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<set $combatReturnAfterVictory = null>> <<set _enemyId = _data.enemies[Math.floor(Math.random() * _data.enemies.length)]>> <<if !_enemyId>> <<set _enemyId = "bandit">> <</if>> <<set $enemyList = [window.setup.spawnEnemy(_enemyId)]>> <<set $originalEnemyList = $enemyList.slice()>> <<set $currentEnemyIndex = 0>> <<set $currentEnemy = $enemyList[0]>> <<set $testEnemy = $enemyList[0]>> <<goto "Combat_Display">> <<else>> <!-- Debug: Why no encounter this time? --> <div style="background: #2a1a1a; border: 1px solid #ff8888; border-radius: 5px; padding: 4px; margin: 4px; font-size: 10px; color: #ff8888;"> ⚠️ NO ENCOUNTER: Roll > rate OR no enemies list<br> Roll: <<= random(1, 100)>> | Rate: _encounterRate | Enemies exist: <<= _data.enemies && _data.enemies.length > 0>> </div> <</if>> <</if>> <!-- Reset encounter trigger when leaving the cell --> <<if $encounterTriggered && !$inCombat>> <<set $encounterTriggered = false>> <</if>> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 20px; border: 1px solid #d4af37;"> <div style="text-align: center;"> <div style="font-size: 48px;">_data.icon</div> <div style="font-size: 24px; font-family: 'Cinzel', serif; color: #d4af37;">_data.name</div> <div style="color: #aaa; margin: 10px 0;">_data.desc</div> <<if _data.type == "town">> <<if _key == "0,0">> <<link "🏰 Enter Central City">> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<goto "TownHub_Start">> <</link>> <<else>> <<link "🏰 Enter " + _data.name>> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<set $currentTownName = _data.name>> <<set $currentTownDesc = _data.desc>> <<goto "TownHub">> <</link>> <</if>> <<elseif _data.type == "village">> <<set _template = _data.template || "farming">> <<if _template == "ruins">> <<link "🏚️ Explore " + _data.name>> <<set $currentVillageTemplate = _template>> <<set $currentVillageName = _data.name>> <<set $currentVillageDesc = _data.desc>> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<goto "RuinsHub">> <</link>> <<else>> <<link "🏘️ Enter " + _data.name>> <<set $currentVillageTemplate = _template>> <<set $currentVillageName = _data.name>> <<set $currentVillageDesc = _data.desc>> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<goto "VillageHub">> <</link>> <</if>> <<elseif _data.type == "enemy_camp" && !_data.isCampCleared>> <<link "⚔️ Attack the Camp">> <<set $encounterTriggered = true>> <<set $combatState = "active">> <<set $inCombat = true>> <<set $combatLogText = "⚔️ You attack the camp! ⚔️<br>">> <<set $combatActions = 0>> <<set $castingSpell = null>> <<set $selectingPotion = null>> <<set $playerStunned = false>> <<set $playerConfused = false>> <<set $isTestBattle = false>> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<set $combatReturnAfterVictory = null>> <<set $enemyList = []>> <<set $enemyList.push(window.setup.spawnEnemy("bandit"))>> <<if random(1, 100) > 50>> <<set $enemyList.push(window.setup.spawnEnemy("bandit"))>> <</if>> <<set $originalEnemyList = $enemyList.slice()>> <<set $currentEnemyIndex = 0>> <<set $currentEnemy = $enemyList[0]>> <<set $testEnemy = $enemyList[0]>> <<goto "Combat_Display">> <</link>> <<elseif _data.type == "merchant" && !_data.visited>> <<set _data.visited = true>> <<link "💰 Talk to the Merchant">> <<goto "Shop">> <</link>> <<elseif _data.type == "shrine" && !_data.visited>> <<set _data.visited = true>> <<link "🛐 Pray at the Shrine">> <<set $currentHP = Math.min($maxHP, $currentHP + Math.floor($maxHP * 0.3))>> <<goto "GridCell">> <</link>> <<elseif _data.type == "treasure" && !_data.visited>> <<set _data.visited = true>> <<link "💰 Search for Treasure">> <<set _goldFound = random(20, 100)>> <<set $gold = $gold + _goldFound>> <<goto "GridCell">> <</link>> <<elseif _data.type == "dungeon" && !_data.visited>> <<set _data.visited = true>> <<link "🏛️ Enter " + _data.name>> <<goto "DungeonEntrance">> <</link>> <</if>> <<if _data.type != "town" && _data.type != "village">> <<link "⛺ Camp">> <<goto "CampMenu">> <</link>> <</if>> </div> </div> <</nobr>> <</widget>>
<<widget "nodeInteraction">> <<nobr>> <<set _key = $gridX + "," + $gridY>> <<set _data = $gridBiomes[_key]>> <div style="text-align: center; margin-top: 15px;"> <<if _data && _data.type == "town">> <<if _key == "0,0">> <<link "🏰 Enter Central City">> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<goto "TownHub_Start">> <</link>> <<else>> <<link "🏰 Enter " + _data.name>> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<set $currentTownName = _data.name>> <<set $currentTownDesc = _data.desc>> <<goto "TownHub">> <</link>> <</if>> <<elseif _data && _data.type == "village">> <<set _template = _data.template || "farming">> <<if _template == "ruins">> <<link "🏚️ Explore " + _data.name>> <<set $currentVillageTemplate = _template>> <<set $currentVillageName = _data.name>> <<set $currentVillageDesc = _data.desc>> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<goto "RuinsHub">> <</link>> <<else>> <<link "🏘️ Enter " + _data.name>> <<set $currentVillageTemplate = _template>> <<set $currentVillageName = _data.name>> <<set $currentVillageDesc = _data.desc>> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<goto "VillageHub">> <</link>> <</if>> <<elseif _data && _data.type == "merchant" && !_data.visited>> <<set _data.visited = true>> <<link "💰 Talk to the Merchant">> <<goto "ShopGeneric">> <</link>> <<elseif _data && _data.type == "shrine" && !_data.visited>> <<set _data.visited = true>> <<link "🛐 Pray at the Shrine">> <<set $currentHP = Math.min($maxHP, $currentHP + Math.floor($maxHP * 0.3))>> <<goto "GridCell">> <</link>> <<elseif _data && _data.type == "treasure" && !_data.visited>> <<set _data.visited = true>> <<link "💰 Search for Treasure">> <<set _goldFound = random(20, 100)>> <<set $gold = $gold + _goldFound>> <<goto "GridCell">> <</link>> <<elseif _data && _data.type == "enemy_camp" && !_data.isCampCleared>> <<set _data.isCampCleared = true>> <<link "⚔️ Attack the Camp">> <<set $combatState = "active">> <<set $inCombat = true>> <<set $combatLogText = "⚔️ You attack the bandit camp! ⚔️<br>">> <<set $combatActions = 0>> <<set $castingSpell = null>> <<set $selectingPotion = null>> <<set $playerStunned = false>> <<set $isTestBattle = false>> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<set $combatReturnAfterVictory = null>> <<set $enemyList = [window.setup.spawnEnemy("bandit")]>> <<set $enemyList.push(window.setup.spawnEnemy("bandit"))>> <<set $originalEnemyList = $enemyList.slice()>> <<set $currentEnemyIndex = 0>> <<set $currentEnemy = $enemyList[0]>> <<set $testEnemy = $enemyList[0]>> <<goto "Combat_Display">> <</link>> <<elseif _data && _data.type == "dungeon" && !_data.visited>> <<set _data.visited = true>> <<link "🏛️ Enter " + _data.name>> <<goto "DungeonEntrance">> <</link>> <</if>> </div> <</nobr>> <</widget>>
<<widget "MiniMap">> <<nobr>> <<set _x = $gridX>> <<set _y = $gridY>> <<set _cacheKey = _x + "," + _y>> <<if $minimapCache && $minimapCache[_cacheKey]>> <<= $minimapCache[_cacheKey]>> <<else>> <<capture _cacheKey>> <<set _output = '<div style="display: flex; justify-content: center; width: 100%;"><div style="display: inline-block; background: #0a0a1a; padding: 8px; border-radius: 12px; border: 2px solid #d4af37;">'>> <<for _dy = 4; _dy >= -4; _dy-->> <<set _output = _output + '<div style="display: flex; justify-content: center;">'>> <<for _dx = -4; _dx <= 4; _dx++>> <<set _checkX = _x + _dx>> <<set _checkY = _y + _dy>> <<set _key = _checkX + "," + _checkY>> <<if _dx == 0 && _dy == 0>> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #cc3333; border: 1px solid #ffd700; box-shadow: 0 0 4px #ff4444; text-align: center; line-height: 22px; font-size: 10px;">⚔️</div>'>> <<elseif $discoveredSet.has(_key)>> <<set _biome = $gridBiomes[_key]>> <<if _biome && _biome.type == "town">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #4a6a8a; border: 1px solid #d4af37; text-align: center; line-height: 22px; font-size: 10px;">🏰</div>'>> <<elseif _biome && _biome.type == "village">> <<if _biome.template == "ruins">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #5a3a3a; border: 1px solid #d4af37; text-align: center; line-height: 22px; font-size: 10px;">🏚️</div>'>> <<else>> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #4a6a8a; border: 1px solid #d4af37; text-align: center; line-height: 22px; font-size: 10px;">🏘️</div>'>> <</if>> <<elseif _biome && _biome.type == "dungeon">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #5a3a5a; border: 1px solid #d4af37; text-align: center; line-height: 22px; font-size: 10px;">🏛️</div>'>> <<elseif _biome && _biome.type == "enemy_camp">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #8a3a3a; border: 1px solid #d4af37; text-align: center; line-height: 22px; font-size: 10px;">⚠️</div>'>> <<elseif _biome && _biome.type == "shrine">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #8a7a3a; border: 1px solid #d4af37; text-align: center; line-height: 22px; font-size: 10px;">🛐</div>'>> <<elseif _biome && _biome.type == "treasure">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #8a7a3a; border: 1px solid #d4af37; text-align: center; line-height: 22px; font-size: 10px;">💰</div>'>> <<elseif _biome && _biome.type == "merchant">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #6a5a3a; border: 1px solid #d4af37; text-align: center; line-height: 22px; font-size: 10px;">🏪</div>'>> <<elseif _biome && _biome.type == "forest">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #2a5a2a; border: 1px solid #d4af37;"></div>'>> <<elseif _biome && _biome.type == "mountain">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #6a6a7a; border: 1px solid #d4af37;"></div>'>> <<elseif _biome && _biome.type == "water">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #2a4a6a; border: 1px solid #d4af37;"></div>'>> <<elseif _biome && _biome.type == "plains">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #5a6a3a; border: 1px solid #d4af37;"></div>'>> <<elseif _biome && _biome.type == "road">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #4a4a4a; border: 1px solid #d4af37;"></div>'>> <<elseif _biome && _biome.type == "swamp">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #3a5a2a; border: 1px solid #d4af37;"></div>'>> <<elseif _biome && _biome.type == "desert">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #aa8844; border: 1px solid #d4af37;"></div>'>> <<elseif _biome && _biome.type == "tundra">> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #8aaccc; border: 1px solid #d4af37;"></div>'>> <<else>> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #555; border: 1px solid #d4af37;"></div>'>> <</if>> <<elseif $gridExistsSet.has(_key)>> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #1a1a2a; border: 1px solid #333;"></div>'>> <<else>> <<set _output = _output + '<div style="width: 24px; height: 24px; margin: 1px; border-radius: 4px; background: #0a0a12; border: 1px solid #1a1a2a;"></div>'>> <</if>> <</for>> <<set _output = _output + '</div>'>> <</for>> <<set _output = _output + '</div></div><div style="text-align: center; margin-top: 5px; font-size: 10px; color: #aaa;">🗺️ ' + $discovered.length + '/' + $gridExists.length + '</div>'>> <<if !$minimapCache>><<set $minimapCache = {}>><</if>> <<set $minimapCache[_cacheKey] = _output>> <<= _output>> <</capture>> <</if>> <</nobr>> <</widget>>
<<widget "Movement_Compass">> <<nobr>> <<set _x = $gridX>> <<set _y = $gridY>> <<if $inHubView>> <div style="text-align: center; padding: 10px; background: rgba(0,0,0,0.3); border-radius: 10px;"> <span style="color: #ff8888;">🚪 You are inside a settlement. Exit to travel.</span> </div> <<else>> <div class="travel-buttons" style="display: flex; flex-direction: column; align-items: center; gap: 8px;"> <div> <<if $gridExistsSet.has(_x + "," + (_y + 1))>> <<capture _x _y>> <<link "⬆️ North" style="min-width: 80px;">> <<set $gridY = $gridY + 1>> <<set $coord = $gridX + "," + $gridY>> <<set _newKey = $gridX + "," + $gridY>> <<if !$discoveredSet.has(_newKey)>> <<set $discovered.push(_newKey)>> <<set $discoveredSet.add(_newKey)>> <</if>> <<set $minimapCache = {}>> <<goto "GridCell">> <</link>> <</capture>> <<else>> <span style="opacity: 0.3; padding: 5px 12px; min-width: 80px; display: inline-block;">⬆️ North</span> <</if>> </div> <div style="display: flex; gap: 20px;"> <<if $gridExistsSet.has((_x - 1) + "," + _y)>> <<capture _x _y>> <<link "⬅️ West" style="min-width: 80px;">> <<set $gridX = $gridX - 1>> <<set $coord = $gridX + "," + $gridY>> <<set _newKey = $gridX + "," + $gridY>> <<if !$discoveredSet.has(_newKey)>> <<set $discovered.push(_newKey)>> <<set $discoveredSet.add(_newKey)>> <</if>> <<set $minimapCache = {}>> <<goto "GridCell">> <</link>> <</capture>> <<else>> <span style="opacity: 0.3; padding: 5px 12px; min-width: 80px; display: inline-block;">⬅️ West</span> <</if>> <<if $gridExistsSet.has((_x + 1) + "," + _y)>> <<capture _x _y>> <<link "➡️ East" style="min-width: 80px;">> <<set $gridX = $gridX + 1>> <<set $coord = $gridX + "," + $gridY>> <<set _newKey = $gridX + "," + $gridY>> <<if !$discoveredSet.has(_newKey)>> <<set $discovered.push(_newKey)>> <<set $discoveredSet.add(_newKey)>> <</if>> <<set $minimapCache = {}>> <<goto "GridCell">> <</link>> <</capture>> <<else>> <span style="opacity: 0.3; padding: 5px 12px; min-width: 80px; display: inline-block;">➡️ East</span> <</if>> </div> <div> <<if $gridExistsSet.has(_x + "," + (_y - 1))>> <<capture _x _y>> <<link "⬇️ South" style="min-width: 80px;">> <<set $gridY = $gridY - 1>> <<set $coord = $gridX + "," + $gridY>> <<set _newKey = $gridX + "," + $gridY>> <<if !$discoveredSet.has(_newKey)>> <<set $discovered.push(_newKey)>> <<set $discoveredSet.add(_newKey)>> <</if>> <<set $minimapCache = {}>> <<goto "GridCell">> <</link>> <</capture>> <<else>> <span style="opacity: 0.3; padding: 5px 12px; min-width: 80px; display: inline-block;">⬇️ South</span> <</if>> </div> </div> <</if>> <</nobr>> <</widget>>
:: Premade_Morgana [nobr] <<nobr>> <!-- RAW DATA ONLY - No calculations --> <<set $playerName = "Morgana">> <<set $playerAge = 28>> <<set $playerSex = "Female">> <<set $playerRace = "Human">> <<set $playerClass = "Mage">> <<set $classChoice = "mage">> <!-- Race stats (Human) --> <<set $baseStrength = 8>> <<set $baseConstitution = 8>> <<set $baseDefense = 7>> <<set $baseAgility = 8>> <<set $baseIntellect = 8>> <!-- Class bonuses (Mage) --> <<set $classStrengthBonus = 0>> <<set $classConstitutionBonus = 1>> <<set $classDefenseBonus = 0>> <<set $classAgilityBonus = 1>> <<set $classIntellectBonus = 4>> <!-- Starting equipment --> <<set $startingWeapon = "gnarled_staff">> <<set $startingArmor = "frayed_robe">> <<set $startingHelmet = "worn_hood">> <<set $startingBoots = "threadbare_boots">> <<set $startingGold = 25>> <<set $startingPotions = { minor_health_potion: 2, minor_mana_potion: 2 }>> <!-- Spells --> <<set $spellbook = ["emberwake", "tide_lash", "stonegrasp", "gale_needle", "veillight", "soulkindle"]>> <</nobr>>
:: Premade_Sylvanas [nobr] <<nobr>> <!-- RAW DATA ONLY - No calculations --> <<set $playerName = "Sylvanas">> <<set $playerAge = 26>> <<set $playerSex = "Female">> <<set $playerRace = "Elf">> <<set $playerClass = "Ranger">> <<set $classChoice = "ranger">> <!-- Race stats (Elf) --> <<set $baseStrength = 7>> <<set $baseConstitution = 6>> <<set $baseDefense = 6>> <<set $baseAgility = 10>> <<set $baseIntellect = 9>> <!-- Class bonuses (Ranger) --> <<set $classStrengthBonus = 2>> <<set $classConstitutionBonus = 2>> <<set $classDefenseBonus = 1>> <<set $classAgilityBonus = 2>> <<set $classIntellectBonus = 1>> <!-- Starting equipment --> <<set $startingWeapon = "makeshift_bow">> <<set $startingArmor = "patchy_hide">> <<set $startingHelmet = "leather_cap">> <<set $startingBoots = "sturdy_boots">> <<set $startingGold = 25>> <<set $startingPotions = { minor_health_potion: 3 }>> <!-- Spells --> <<set $spellbook = []>> <</nobr>>
:: Premade_Kaelen [nobr] <<nobr>> <!-- RAW DATA ONLY - No calculations --> <<set $playerName = "Kaelen">> <<set $playerAge = 22>> <<set $playerSex = "Male">> <<set $playerRace = "Elf">> <<set $playerClass = "Rogue">> <<set $classChoice = "rogue">> <!-- Race stats (Elf) --> <<set $baseStrength = 7>> <<set $baseConstitution = 6>> <<set $baseDefense = 6>> <<set $baseAgility = 10>> <<set $baseIntellect = 9>> <!-- Class bonuses (Rogue) --> <<set $classStrengthBonus = 1>> <<set $classConstitutionBonus = 1>> <<set $classDefenseBonus = 0>> <<set $classAgilityBonus = 3>> <<set $classIntellectBonus = 1>> <!-- Starting equipment --> <<set $startingWeapon = "tarnished_dagger">> <<set $startingArmor = "tattered_leather">> <<set $startingHelmet = "cloth_mask">> <<set $startingBoots = "soft_slippers">> <<set $startingGold = 50>> <<set $startingPotions = { minor_health_potion: 3 }>> <!-- Spells --> <<set $spellbook = []>> <</nobr>>
:: Premade_Bjorn [nobr] <<nobr>> <!-- RAW DATA ONLY - No calculations --> <<set $playerName = "Bjorn">> <<set $playerAge = 30>> <<set $playerSex = "Male">> <<set $playerRace = "Dwarf">> <<set $playerClass = "Warrior">> <<set $classChoice = "warrior">> <!-- Race stats (Dwarf) --> <<set $baseStrength = 9>> <<set $baseConstitution = 10>> <<set $baseDefense = 8>> <<set $baseAgility = 6>> <<set $baseIntellect = 7>> <!-- Class bonuses (Warrior) --> <<set $classStrengthBonus = 2>> <<set $classConstitutionBonus = 3>> <<set $classDefenseBonus = 2>> <<set $classAgilityBonus = 0>> <<set $classIntellectBonus = 0>> <!-- Starting equipment --> <<set $startingWeapon = "broken_shortsword">> <<set $startingArmor = "worn_chainmail">> <<set $startingHelmet = "rusty_helm">> <<set $startingBoots = "iron_toed_boots">> <<set $startingGold = 0>> <<set $startingPotions = { minor_health_potion: 3 }>> <!-- Spells --> <<set $spellbook = []>> <</nobr>>
<<nobr>> <<set $playerRace = "Dwarf">> <<set $baseStrength = 9>> <<set $baseConstitution = 10>> <<set $baseDefense = 8>> <<set $baseAgility = 6>> <<set $baseMaxHP = 30>> <<set $baseIntellect = 7>> <</nobr>>
<<nobr>> <<set $playerRace = "Elf">> <<set $baseStrength = 7>> <<set $baseConstitution = 6>> <<set $baseDefense = 6>> <<set $baseAgility = 10>> <<set $baseMaxHP = 30>> <<set $baseIntellect = 9>> <</nobr>>
<<nobr>> <<set $playerRace = "Human">> <<set $baseStrength = 8>> <<set $baseConstitution = 8>> <<set $baseDefense = 7>> <<set $baseAgility = 8>> <<set $baseMaxHP = 30>> <<set $baseIntellect = 8>> <</nobr>>
<<nobr>> <<set $playerRace = "Orc">> <<set $baseStrength = 10>> <<set $baseConstitution = 9>> <<set $baseDefense = 8>> <<set $baseAgility = 5>> <<set $baseMaxHP = 30>> <<set $baseIntellect = 6>> <</nobr>>
:: RuinsHub [nobr] <<nobr>> <<set _key = $gridX + "," + $gridY>> <<set _data = $gridBiomes[_key]>> <<set _isCleared = $ruinsCleared.indexOf(_key) !== -1>> <<set _isLooted = $ruinsLooted.indexOf(_key) !== -1>> <<set _daysSinceClear = $day - ($ruinsLastCleared[_key] || 0)>> <<set _canRespawn = _daysSinceClear >= 5>> <div style="text-align: center; background: linear-gradient(135deg, #2a2a2a, #1a1a1a); border: 2px solid #8a3a3a; border-radius: 16px; padding: 20px;"> <h2>🏚️ <<= _data.name >> (Ruins)</h2> <p><i><<= _data.desc >></i></p> <p>Whatever happened here was recent... or recurring. The buildings are crumbling, and an eerie silence hangs in the air.</p> <hr> <!-- COMBAT OPTION --> <<if !_isCleared || (_isCleared && _canRespawn)>> <<if _isCleared && _canRespawn>> <<set _index = $ruinsCleared.indexOf(_key)>> <<if _index !== -1>><<set $ruinsCleared.deleteAt(_index)>><</if>> <<set _isCleared = false>> <</if>> <div style="background: #3a2a2a; padding: 15px; border-radius: 8px; margin: 10px 0;"> <<link "⚔️ Investigate the noise" style="font-size: 1.2em;">> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<set $combatState = "active">> <<set $inCombat = true>> <<set $combatLogText = "⚔️ Something moves in the shadows of " + _data.name + "... ⚔️<br>">> <<set $combatActions = 0>> <<set $castingSpell = null>> <<set $selectingPotion = null>> <<set $playerStunned = false>> <<set $isTestBattle = false>> <<set _enemies = [window.setup.spawnEnemy("ghoul")]>> <<if random(1, 100) > 65>> <<set _enemies.push(window.setup.spawnEnemy("skeleton"))>> <</if>> <<if random(1, 100) > 85>> <<set _enemies.push(window.setup.spawnEnemy("ghoul"))>> <</if>> <<set $enemyList = _enemies>> <<set $originalEnemyList = $enemyList.slice()>> <<set $currentEnemyIndex = 0>> <<set $currentEnemy = $enemyList[0]>> <<set $testEnemy = $enemyList[0]>> <<set $combatReturnAfterVictory = "RuinsHub_AfterCombat">> <<goto "Combat_Display">> <</link>> </div> <<else>> <div style="background: #2a2a2a; padding: 15px; border-radius: 8px; margin: 10px 0; opacity: 0.6;"> ⚔️ The ruins are quiet for now... (will repopulate in 5 days) </div> <</if>> <!-- TREASURE OPTION --> <<if !_isLooted>> <div style="background: #2a3a2a; padding: 15px; border-radius: 8px; margin: 10px 0;"> <<link "🔍 Search for treasure" style="font-size: 1.2em;">> <<set _lootRoll = random(1, 100)>> <<set _lootGold = 0>> <<set _lootItem = false>> <<if _lootRoll > 85>> <<set _lootGold = 100>> <<set $inventory.push({name: "Healing Potion", type: "potion", effect: "restore 20 HP"})>> <<set _lootItem = true>> <<set $combatLogText = "You find 100 gold and a Healing Potion!">> <<elseif _lootRoll > 60>> <<set _lootGold = 50>> <<set $combatLogText = "You find 50 gold among the rubble.">> <<elseif _lootRoll > 30>> <<set _lootGold = 25>> <<set $combatLogText = "You find 25 gold in an old chest.">> <<elseif _lootRoll > 10>> <<set _lootGold = 10>> <<set $combatLogText = "You find a few scattered coins.">> <<else>> <<set $combatLogText = "You find nothing but bones and rubble.">> <</if>> <<set $gold = $gold + _lootGold>> <<set $ruinsLooted.push(_key)>> <<goto "GridCell">> <</link>> </div> <<else>> <div style="background: #2a2a2a; padding: 15px; border-radius: 8px; margin: 10px 0; opacity: 0.6;"> 🔍 The ruins have been picked clean. </div> <</if>> <hr> <div style="margin-top: 20px;"> <<link "🗺️ Leave the ruins">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $returnToGridX = null>> <<set $returnToGridY = null>> <<goto "GridCell">> <</link>> </div> </div> <</nobr>>
:: RuinsHub_AfterCombat [nobr] <<nobr>> <<set _key = $gridX + "," + $gridY>> <<if $ruinsCleared.indexOf(_key) === -1>> <<set $ruinsCleared.push(_key)>> <</if>> <<set $ruinsLastCleared[_key] = $day>> <div style="text-align: center; background: linear-gradient(135deg, #2a3a2a, #1a2a1a); border: 2px solid #6aaa4a; border-radius: 16px; padding: 20px;"> <h2>⚔️ Victory</h2> <p>The ruins fall silent. The threat has been eliminated... for now.</p> <p><i>You sense the darkness may return in time.</i></p> <div style="margin-top: 20px;"> <<link "Continue exploring the ruins">> <<goto "RuinsHub">> <</link>> </div> </div> <</nobr>>
<<nobr>> <div class="fancy-header">⚙️ SETTINGS ⚙️</div> <div style="background: rgba(0,0,0,0.5); border-radius: 15px; padding: 20px; margin: 15px 0;"> <!-- Difficulty Settings --> <div style="margin-bottom: 25px;"> <div style="font-family: 'Cinzel', serif; color: #d4af37; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #d4af37;">🎮 DIFFICULTY</div> <div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; padding: 8px 0;"> <span>Current Difficulty:</span> <<if $difficulty == "easy">><span style="color: #88ff88;">Easy</span> <<elseif $difficulty == "normal">><span style="color: #ffd700;">Normal</span> <<elseif $difficulty == "hard">><span style="color: #ff8888;">Hard</span> <<else>><span style="color: #ffd700;">Normal</span><</if>> <div style="display: flex; gap: 10px;"> <<link "🌿 Easy">> <<set $difficulty = "easy">> <<set $deathPenaltyPercent = 5>> <<set $deathPenaltyGoldPercent = 5>> <<goto "Settings">> <</link>> <<link "⚔️ Normal">> <<set $difficulty = "normal">> <<set $deathPenaltyPercent = 10>> <<set $deathPenaltyGoldPercent = 10>> <<goto "Settings">> <</link>> <<link "🔥 Hard">> <<set $difficulty = "hard">> <<set $deathPenaltyPercent = 20>> <<set $deathPenaltyGoldPercent = 20>> <<goto "Settings">> <</link>> </div> </div> <div style="font-size: 12px; color: #aaa; margin-top: 5px;"> Easy: 5% penalty | Normal: 10% penalty | Hard: 20% penalty </div> </div> <!-- Death Penalty Toggle --> <div style="margin-bottom: 25px;"> <div style="font-family: 'Cinzel', serif; color: #ff8888; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #ff8888;">⚰️ DEATH PENALTY</div> <div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; padding: 8px 0;"> <span>Death Penalty:</span> <<if $deathPenaltyEnabled>> <span style="color: #ff8888;">ENABLED</span> <<link "Disable">> <<set $deathPenaltyEnabled = false>> <<goto "Settings">> <</link>> <<else>> <span style="color: #88ff88;">DISABLED</span> <<link "Enable">> <<set $deathPenaltyEnabled = true>> <<goto "Settings">> <</link>> <</if>> </div> <div style="font-size: 12px; color: #aaa; margin-top: 5px;"> When enabled, death costs XP and gold. Disabled for casual play. </div> </div> <!-- Combat Settings --> <div style="margin-bottom: 25px;"> <div style="font-family: 'Cinzel', serif; color: #88aaff; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #88aaff;">⚔️ COMBAT</div> <div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; padding: 8px 0;"> <span>Flee Cooldown Turns:</span> <<if $fleeCooldownDuration == 3>> <span>3 turns (Default)</span> <<else>> <span>$fleeCooldownDuration turns</span> <</if>> <div style="display: flex; gap: 10px;"> <<link "2 Turns">> <<set $fleeCooldownDuration = 2>> <<goto "Settings">> <</link>> <<link "3 Turns">> <<set $fleeCooldownDuration = 3>> <<goto "Settings">> <</link>> <<link "5 Turns">> <<set $fleeCooldownDuration = 5>> <<goto "Settings">> <</link>> </div> </div> <!-- Random Encounters Toggle --> <div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; padding: 8px 0; margin-top: 10px; border-top: 1px solid #444;"> <span>Random Encounters:</span> <<if $randomEncountersEnabled>> <span style="color: #88ff88;">ENABLED</span> <<link "Disable">> <<set $randomEncountersEnabled = false>> <<goto "Settings">> <</link>> <<else>> <span style="color: #ff8888;">DISABLED</span> <<link "Enable">> <<set $randomEncountersEnabled = true>> <<goto "Settings">> <</link>> <</if>> </div> <div style="font-size: 12px; color: #aaa; margin-top: 5px;"> When disabled, you will never encounter random enemies while exploring. Boss fights and scripted battles still occur. </div> </div> <!-- Map Settings --> <div style="margin-bottom: 25px;"> <div style="font-family: 'Cinzel', serif; color: #88ff88; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #88ff88;">🗺️ MAP SETTINGS</div> <div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; padding: 8px 0;"> <span>Reveal Entire Map:</span> <<link "🔓 Reveal All Locations">> <<set $discovered = $gridExists.slice()>> <<set $discoveredSet = new Set($gridExists)>> <<set $minimapCache = {}>> <<goto "Settings">> <</link>> </div> <div style="font-size: 12px; color: #aaa; margin-top: 5px;"> Reveals every location on the world map. Great for testing or exploration! </div> </div> <!-- Save/Load Section --> <div style="margin-bottom: 25px;"> <div style="font-family: 'Cinzel', serif; color: #ffd700; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #ffd700;">💾 SAVE / LOAD</div> <div style="display: flex; flex-wrap: wrap; gap: 15px; justify-content: center; padding: 10px 0;"> <<button "💾 Save Game">> <<run UI.saves()>> <</button>> <<button "📂 Load Game">> <<run UI.saves()>> <</button>> <<button "🗑️ Clear Saves">> <<run UI.saves.delete()>> <<goto "Settings">> <</button>> </div> </div> <!-- Quick Save/Load Info --> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 10px; text-align: center;"> <div style="font-size: 12px; color: #aaa;"> 💡 <strong>Quick Tips:</strong><br> • Press <strong>F5</strong> to Quick Save<br> • Press <strong>F9</strong> to Quick Load<br> • Death penalty settings require a new game to take full effect </div> </div> </div> <div style="text-align: center; margin-top: 20px;"> <<link "❌ Close Settings">> <<set $returnTo = "">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $coord = $gridX + "," + $gridY>> <<goto "GridCell">> <</link>> </div> <</nobr>>
<<nobr>> <div class="fancy-header">🏪 The General Store 🏪</div> <div style="background: rgba(0,0,0,0.5); border-radius: 15px; padding: 20px; margin: 15px 0; text-align: center;"> <div style="font-size: 48px; margin-bottom: 10px;">🏪</div> <div style="font-family: 'Cinzel', serif; color: #d4af37; font-size: 20px;">"Welcome, adventurer!"</div> <div style="margin: 10px 0; color: #aaa;">The merchant smiles and shows you his wares.</div> <div style="margin: 10px 0;">💰 Your Gold: <span style="color: #ffd700; font-weight: bold;">$gold</span></div> </div> <!-- Health Potions Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #88ff88; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #88ff88; display: inline-block;">🧪 Potions</div> <div class="shop-item"> <span class="shop-icon">🧪</span> <span class="shop-name">Health Potion</span> <span class="shop-price">💰 10 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("health_potion", 1)>> <<goto "Shop">> <</link>> <span style="margin-left: auto;">Restores 20 HP</span> </div> <div class="shop-item"> <span class="shop-icon">🧪✨</span> <span class="shop-name">Greater Health Potion</span> <span class="shop-price">💰 30 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("greater_health_potion", 1)>> <<goto "Shop">> <</link>> <span style="margin-left: auto;">Restores 50 HP</span> </div> </div> <!-- Weapons Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #ffaa88; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #ffaa88; display: inline-block;">⚔️ Weapons</div> <div class="shop-item"> <span class="shop-icon">🗡️</span> <span class="shop-name">Rusty Dagger</span> <span class="shop-price">💰 10 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("rusty_dagger", 1)>> <<goto "Shop">> <</link>> <span style="margin-left: auto;">DMG +5, STR +2</span> </div> </div> <!-- Armor Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #88ff88; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #88ff88; display: inline-block;">🛡️ Armor</div> <div class="shop-item"> <span class="shop-icon">👚</span> <span class="shop-name">Cloth Tunic</span> <span class="shop-price">💰 15 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("cloth_tunic", 1)>> <<goto "Shop">> <</link>> <span style="margin-left: auto;">DEF +2, AGI +1</span> </div> <div class="shop-item"> <span class="shop-icon">🧥</span> <span class="shop-name">Leather Armor</span> <span class="shop-price">💰 40 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("leather_armor", 1)>> <<goto "Shop">> <</link>> <span style="margin-left: auto;">DEF +5</span> </div> </div> <!-- Helmets Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #d4af37; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #d4af37; display: inline-block;">🪖 Helmets</div> <div class="shop-item"> <span class="shop-icon">⛑️</span> <span class="shop-name">Leather Helm</span> <span class="shop-price">💰 15 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("leather_helm", 1)>> <<goto "Shop">> <</link>> <span style="margin-left: auto;">DEF +2, AGI +1</span> </div> <div class="shop-item"> <span class="shop-icon">🪖</span> <span class="shop-name">Iron Helm</span> <span class="shop-price">💰 30 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("iron_helm", 1)>> <<goto "Shop">> <</link>> <span style="margin-left: auto;">DEF +4, STR +1, AGI -1</span> </div> </div> <!-- Boots Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #88aaff; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #88aaff; display: inline-block;">👢 Boots</div> <div class="shop-item"> <span class="shop-icon">👢</span> <span class="shop-name">Leather Boots</span> <span class="shop-price">💰 12 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("leather_boots", 1)>> <<goto "Shop">> <</link>> <span style="margin-left: auto;">DEF +1, AGI +2</span> </div> <div class="shop-item"> <span class="shop-icon">🥾</span> <span class="shop-name">Iron Boots</span> <span class="shop-price">💰 25 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("iron_boots", 1)>> <<goto "Shop">> <</link>> <span style="margin-left: auto;">DEF +3</span> </div> </div> <!-- Rings Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #ffd700; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #ffd700; display: inline-block;">💍 Rings</div> <div class="shop-item"> <span class="shop-icon">💍</span> <span class="shop-name">Ring of Strength</span> <span class="shop-price">💰 60 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("ring_of_strength", 1)>> <<goto "Shop">> <</link>> <span style="margin-left: auto;">STR +3</span> </div> </div> <!-- Amulets Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #ff88aa; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #ff88aa; display: inline-block;">📿 Amulets</div> <div class="shop-item"> <span class="shop-icon">📿</span> <span class="shop-name">Amulet of Defense</span> <span class="shop-price">💰 60 gold</span> <<link "🛒 Buy">> <<set $result = window.setup.shop.buyItem("amulet_of_defense", 1)>> <<goto "Shop">> <</link>> <span style="margin-left: auto;">DEF +3</span> </div> </div> <!-- Sell Items Section --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #ff8888; font-size: 18px; margin-bottom: 10px; border-bottom: 1px solid #ff8888; display: inline-block;">💰 Sell Items</div> <div style="font-size: 12px; color: #aaa; margin-bottom: 10px;">Sell your items for gold</div> <!-- Health Potions --> <<if $inventory.health_potion > 0>> <div class="shop-item"> <span>🧪 Health Potion (x$inventory.health_potion)</span> <<link "💰 Sell (5 gold)">> <<set $result = window.setup.shop.sellItem("health_potion", 1)>> <<goto "Shop">> <</link>> </div> <</if>> <<if $inventory.greater_health_potion > 0>> <div class="shop-item"> <span>🧪✨ Greater Health Potion (x$inventory.greater_health_potion)</span> <<link "💰 Sell (15 gold)">> <<set $result = window.setup.shop.sellItem("greater_health_potion", 1)>> <<goto "Shop">> <</link>> </div> <</if>> <!-- Weapons --> <<if $inventory.rusty_dagger > 0>> <div class="shop-item"> <span>🗡️ Rusty Dagger (x$inventory.rusty_dagger)</span> <<link "💰 Sell (5 gold)">> <<set $result = window.setup.shop.sellItem("rusty_dagger", 1)>> <<goto "Shop">> <</link>> </div> <</if>> <!-- Armor --> <<if $inventory.cloth_tunic > 0>> <div class="shop-item"> <span>👚 Cloth Tunic (x$inventory.cloth_tunic)</span> <<link "💰 Sell (5 gold)">> <<set $result = window.setup.shop.sellItem("cloth_tunic", 1)>> <<goto "Shop">> <</link>> </div> <</if>> <<if $inventory.leather_armor > 0>> <div class="shop-item"> <span>🧥 Leather Armor (x$inventory.leather_armor)</span> <<link "💰 Sell (20 gold)">> <<set $result = window.setup.shop.sellItem("leather_armor", 1)>> <<goto "Shop">> <</link>> </div> <</if>> <!-- Helmets --> <<if $inventory.leather_helm > 0>> <div class="shop-item"> <span>⛑️ Leather Helm (x$inventory.leather_helm)</span> <<link "💰 Sell (8 gold)">> <<set $result = window.setup.shop.sellItem("leather_helm", 1)>> <<goto "Shop">> <</link>> </div> <</if>> <<if $inventory.iron_helm > 0>> <div class="shop-item"> <span>🪖 Iron Helm (x$inventory.iron_helm)</span> <<link "💰 Sell (15 gold)">> <<set $result = window.setup.shop.sellItem("iron_helm", 1)>> <<goto "Shop">> <</link>> </div> <</if>> <!-- Boots --> <<if $inventory.leather_boots > 0>> <div class="shop-item"> <span>👢 Leather Boots (x$inventory.leather_boots)</span> <<link "💰 Sell (6 gold)">> <<set $result = window.setup.shop.sellItem("leather_boots", 1)>> <<goto "Shop">> <</link>> </div> <</if>> <<if $inventory.iron_boots > 0>> <div class="shop-item"> <span>🥾 Iron Boots (x$inventory.iron_boots)</span> <<link "💰 Sell (12 gold)">> <<set $result = window.setup.shop.sellItem("iron_boots", 1)>> <<goto "Shop">> <</link>> </div> <</if>> <!-- Rings --> <<if $inventory.ring_of_strength > 0>> <div class="shop-item"> <span>💍 Ring of Strength (x$inventory.ring_of_strength)</span> <<link "💰 Sell (30 gold)">> <<set $result = window.setup.shop.sellItem("ring_of_strength", 1)>> <<goto "Shop">> <</link>> </div> <</if>> <!-- Amulets --> <<if $inventory.amulet_of_defense > 0>> <div class="shop-item"> <span>📿 Amulet of Defense (x$inventory.amulet_of_defense)</span> <<link "💰 Sell (30 gold)">> <<set $result = window.setup.shop.sellItem("amulet_of_defense", 1)>> <<goto "Shop">> <</link>> </div> <</if>> <<if $inventory.health_potion == 0 && $inventory.greater_health_potion == 0 && $inventory.rusty_dagger == 0 && $inventory.cloth_tunic == 0 && $inventory.leather_armor == 0 && $inventory.leather_helm == 0 && $inventory.iron_helm == 0 && $inventory.leather_boots == 0 && $inventory.iron_boots == 0 && $inventory.ring_of_strength == 0 && $inventory.amulet_of_defense == 0>> <div style="text-align: center; padding: 10px; color: #888;">No sellable items in inventory</div> <</if>> </div> <div style="text-align: center; margin-top: 20px;"> <<link "🚪 Leave the General Store">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $coord = $gridX + "," + $gridY>> <<goto "GridCell">> <</link>> </div> <!-- Message display --> <<if $result && $result.message>> <div style="position: fixed; top: 20px; left: 50%; transform: translateX(-50%); background: #1a1a2e; border: 2px solid #d4af37; border-radius: 10px; padding: 10px 20px; animation: fadeOutMsg 2s forwards; z-index: 1000;"> $result.message </div> <<set $result = null>> <</if>> <</nobr>>
<<nobr>> <<widget "loadShops">> <<script>> window.setup = window.setup || {}; window.setup.shop = { buyItem: function(itemId, quantity = 1) { let itemData = window.setup.getItem(itemId); if (!itemData) return { success: false, message: "Item not found!" }; let totalCost = itemData.buyValue * quantity; if (State.variables.gold < totalCost) return { success: false, message: `Need ${totalCost} gold!` }; State.variables.gold -= totalCost; window.setup.inventory.addItem(itemId, quantity); return { success: true, message: `Bought ${quantity}x ${itemData.name}!` }; }, sellItem: function(itemId, quantity = 1) { let currentQty = State.variables.inventory[itemId] || 0; if (currentQty < quantity) return { success: false, message: "Not enough items!" }; let itemData = window.setup.getItem(itemId); if (!itemData) return { success: false, message: "Item not found!" }; let totalValue = itemData.sellValue * quantity; State.variables.gold += totalValue; window.setup.inventory.removeItem(itemId, quantity); return { success: true, message: `Sold ${quantity}x ${itemData.name} for ${totalValue} gold!` }; } }; console.log("✅ Shops module loaded via widget"); <</script>> <</widget>> <</nobr>>
<<nobr>> <<widget "loadSidebar">> <<script>> window.setup.updateSidebar = function() { var hp = document.getElementById('sidebar-hp-text'); if (hp) hp.innerHTML = State.variables.currentHP + '/' + State.variables.maxHP; var mp = document.getElementById('sidebar-mp-text'); if (mp) mp.innerHTML = State.variables.currentMP + '/' + State.variables.maxMP; var str = document.getElementById('sidebar-str'); if (str) str.innerHTML = State.variables.strength; var con = document.getElementById('sidebar-con'); if (con) con.innerHTML = State.variables.constitution; var def = document.getElementById('sidebar-def'); if (def) def.innerHTML = State.variables.defense; var agi = document.getElementById('sidebar-agi'); if (agi) agi.innerHTML = State.variables.agility; var intl = document.getElementById('sidebar-int'); if (intl) intl.innerHTML = State.variables.intellect; var lvl = document.getElementById('sidebar-level'); if (lvl) lvl.innerHTML = State.variables.level; var exp = document.getElementById('sidebar-exp-text'); if (exp) exp.innerHTML = State.variables.exp + '/' + State.variables.nextLevelExp; var gold = document.getElementById('sidebar-gold'); if (gold) gold.innerHTML = State.variables.gold; if (window.setup.updateTimeTheme) { window.setup.updateTimeTheme(); } }; window.setup.scrollToBottom = function(id) { setTimeout(function() { var el = document.getElementById(id); if (el) el.scrollTop = el.scrollHeight; }, 50); }; $(document).on(':passagerender', function() { setTimeout(window.setup.updateSidebar, 50); }); console.log("✅ Sidebar loaded"); <</script>> <</widget>> <</nobr>>
:: Spellbook [nobr] <<nobr>> <div class="fancy-header">📖 SPELLBOOK 📖</div> <div style="background: rgba(0,0,0,0.5); border-radius: 15px; padding: 20px; margin: 15px 0; text-align: center;"> <div style="font-family: 'Cinzel', serif; color: #d4af37; font-size: 18px;">"Knowledge is the truest power."</div> <div style="margin: 10px 0;">You know <span id="spellbook-count"><<= $spellbook.length >></span> spells.</div> </div> <!-- Spellbook content loads here dynamically --> <div id="spellbook-container"></div> <!-- Close Button --> <div style="text-align: center; margin-top: 20px;"> <<if $returnTo && $returnTo != "Spellbook">> <<link "❌ Close Spellbook">> <<set $temp = $returnTo>> <<set $returnTo = "">> <<if $temp == "GridCell">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $coord = $gridX + "," + $gridY>> <</if>> <<goto $temp>> <</link>> <<else>> <<link "🗺️ Return to Map">> <<set $returnTo = "">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $coord = $gridX + "," + $gridY>> <<goto "GridCell">> <</link>> <</if>> </div> <<script>> window.renderSpellbook(); <</script>> <</nobr>>
<<nobr>> <<widget "loadSpells">> <<script>> window.setup = window.setup || {}; window.setup.spells = window.setup.spells || {}; console.log("🔥 Registering spells..."); <</script>> <!-- FIRE SPELLS --> <<script>> window.setup.spells.emberwake = { id: "emberwake", name: "🔥 Emberwake", element: "fire", tier: "common", description: "Scorching embers that linger on the target", mpCost: 6, damageMin: 8, damageMax: 14, scaling: 1.5, type: "damage", target: "single", effect: "burn", effectValue: 3, effectTurns: 2 }; window.setup.spells.scorchbrand = { id: "scorchbrand", name: "🔥 Scorchbrand", element: "fire", tier: "common", description: "Brands the enemy with searing heat", mpCost: 7, damageMin: 10, damageMax: 16, scaling: 1.5, type: "damage", target: "single", effect: "burn", effectValue: 4, effectTurns: 2 }; window.setup.spells.blazethorn = { id: "blazethorn", name: "🔥 Blazethorn", element: "fire", tier: "common", description: "Thorns of flame pierce the target", mpCost: 8, damageMin: 12, damageMax: 18, scaling: 1.8, type: "damage", target: "single" }; window.setup.spells.wildkindle = { id: "wildkindle", name: "🔥 Wildkindle", element: "fire", tier: "common", description: "Uncontrolled flames that spread to all enemies", mpCost: 9, damageMin: 10, damageMax: 20, scaling: 1.8, type: "damage", target: "all" }; console.log("✅ Fire common (4)"); <</script>> <<script>> window.setup.spells.inferno_veil = { id: "inferno_veil", name: "🔥 Inferno Veil", element: "fire", tier: "mid", description: "Wraps the caster in a veil of fire, burning attackers", mpCost: 12, damageMin: 0, damageMax: 0, scaling: 2, type: "buff", target: "self", effect: "fire_shield", effectValue: 8, effectTurns: 3 }; window.setup.spells.crimson_furnace = { id: "crimson_furnace", name: "🔥 Crimson Furnace", element: "fire", tier: "mid", description: "Opens a furnace of crimson flames beneath the enemy", mpCost: 14, damageMin: 18, damageMax: 28, scaling: 2.2, type: "damage", target: "single", effect: "burn", effectValue: 6, effectTurns: 3 }; window.setup.spells.ashen_tempest = { id: "ashen_tempest", name: "🔥 Ashen Tempest", element: "fire", tier: "mid", description: "A storm of ash and cinders consumes all enemies", mpCost: 16, damageMin: 15, damageMax: 25, scaling: 2, type: "damage", target: "all", effect: "blind", effectValue: 20, effectTurns: 2 }; window.setup.spells.molten_spear = { id: "molten_spear", name: "🔥 Molten Spear", element: "fire", tier: "mid", description: "Hurls a spear of molten rock that pierces armor", mpCost: 15, damageMin: 22, damageMax: 32, scaling: 2.5, type: "damage", target: "single", armorPenetration: 10 }; console.log("✅ Fire mid (4)"); <</script>> <<script>> window.setup.spells.crown_of_cinders = { id: "crown_of_cinders", name: "🔥 Crown of Cinders", element: "fire", tier: "legendary", description: "Don the Crown of Cinders - all fire spells gain bonus damage", mpCost: 20, damageMin: 0, damageMax: 0, scaling: 3, type: "buff", target: "self", effect: "crown_buff", effectValue: "fire", effectTurns: 5 }; window.setup.spells.phoenix_reckoning = { id: "phoenix_reckoning", name: "🔥 Phoenix Reckoning", element: "fire", tier: "legendary", description: "Summons a phoenix to deal massive damage and heal the caster", mpCost: 25, damageMin: 35, damageMax: 50, scaling: 3.5, type: "damage", target: "single", lifesteal: 50 }; window.setup.spells.worldfire_invocation = { id: "worldfire_invocation", name: "🔥 Worldfire Invocation", element: "fire", tier: "legendary", description: "Calls upon primordial fire to burn all enemies", mpCost: 30, damageMin: 30, damageMax: 45, scaling: 3.2, type: "damage", target: "all", effect: "burn", effectValue: 10, effectTurns: 4 }; window.setup.spells.tyrant_sun = { id: "tyrant_sun", name: "🔥 Tyrant Sun", element: "fire", tier: "legendary", description: "Manifests a miniature sun that annihilates a single target", mpCost: 35, damageMin: 50, damageMax: 70, scaling: 4, type: "damage", target: "single" }; console.log("✅ Fire legendary (4) - TOTAL 12 FIRE SPELLS"); <</script>> <!-- WATER SPELLS --> <<script>> window.setup.spells.tide_lash = { id: "tide_lash", name: "💧 Tide Lash", element: "water", tier: "common", description: "Lashes the enemy with pressurized water", mpCost: 6, damageMin: 7, damageMax: 15, scaling: 1.5, type: "damage", target: "single" }; window.setup.spells.mistweave = { id: "mistweave", name: "💧 Mistweave", element: "water", tier: "common", description: "Conjures mist that reduces enemy accuracy", mpCost: 7, damageMin: 0, damageMax: 0, scaling: 1.5, type: "debuff", target: "single", effect: "blind", effectValue: 15, effectTurns: 3 }; window.setup.spells.frostwake = { id: "frostwake", name: "💧 Frostwake", element: "water", tier: "common", description: "Chilling frost that slows the enemy", mpCost: 7, damageMin: 6, damageMax: 14, scaling: 1.5, type: "damage", target: "single", effect: "slow", effectValue: 25, effectTurns: 2 }; window.setup.spells.deepcurrent = { id: "deepcurrent", name: "💧 Deepcurrent", element: "water", tier: "common", description: "Crushing deep water pressure", mpCost: 8, damageMin: 9, damageMax: 17, scaling: 1.8, type: "damage", target: "single" }; console.log("✅ Water common (4)"); <</script>> <<script>> window.setup.spells.glacier_bloom = { id: "glacier_bloom", name: "💧 Glacier Bloom", element: "water", tier: "mid", description: "Ice crystals bloom from the ground, freezing the enemy", mpCost: 13, damageMin: 15, damageMax: 25, scaling: 2.2, type: "damage", target: "single", effect: "freeze", effectValue: 50, effectTurns: 1 }; window.setup.spells.abyssal_current = { id: "abyssal_current", name: "💧 Abyssal Current", element: "water", tier: "mid", description: "Pulls the enemy into crushing depths", mpCost: 14, damageMin: 18, damageMax: 28, scaling: 2.3, type: "damage", target: "single", armorPenetration: 8 }; window.setup.spells.tempest_tide = { id: "tempest_tide", name: "💧 Tempest Tide", element: "water", tier: "mid", description: "A tidal wave crashes into all enemies", mpCost: 16, damageMin: 14, damageMax: 24, scaling: 2, type: "damage", target: "all" }; window.setup.spells.leviathan_pulse = { id: "leviathan_pulse", name: "💧 Leviathan Pulse", element: "water", tier: "mid", description: "Pulses with the power of the deep", mpCost: 15, damageMin: 20, damageMax: 30, scaling: 2.5, type: "damage", target: "single", effect: "slow", effectValue: 30, effectTurns: 3 }; console.log("✅ Water mid (4)"); <</script>> <<script>> window.setup.spells.throne_of_tides = { id: "throne_of_tides", name: "💧 Throne of Tides", element: "water", tier: "legendary", description: "Ascend the Throne of Tides - immense water mastery", mpCost: 22, damageMin: 0, damageMax: 0, scaling: 3, type: "buff", target: "self", effect: "throne_buff", effectValue: "water", effectTurns: 5 }; window.setup.spells.eternal_monsoon = { id: "eternal_monsoon", name: "💧 Eternal Monsoon", element: "water", tier: "legendary", description: "An eternal storm floods the battlefield", mpCost: 28, damageMin: 25, damageMax: 40, scaling: 3, type: "damage", target: "all", effect: "slow", effectValue: 40, effectTurns: 3 }; window.setup.spells.worldfrost = { id: "worldfrost", name: "💧 Worldfrost", element: "water", tier: "legendary", description: "Freezes the world solid", mpCost: 32, damageMin: 35, damageMax: 55, scaling: 3.5, type: "damage", target: "all", effect: "freeze", effectValue: 75, effectTurns: 1 }; window.setup.spells.leviathans_judgment = { id: "leviathans_judgment", name: "💧 Leviathan's Judgment", element: "water", tier: "legendary", description: "The Leviathan itself judges the target", mpCost: 38, damageMin: 55, damageMax: 80, scaling: 4.2, type: "damage", target: "single" }; console.log("✅ Water legendary (4) - TOTAL 12 WATER SPELLS"); <</script>> <!-- EARTH SPELLS --> <<script>> window.setup.spells.stonegrasp = { id: "stonegrasp", name: "🪨 Stonegrasp", element: "earth", tier: "common", description: "Stone hands grasp the enemy, rooting them", mpCost: 6, damageMin: 0, damageMax: 0, scaling: 1.2, type: "debuff", target: "single", effect: "root", effectValue: 100, effectTurns: 2 }; window.setup.spells.rootsnare = { id: "rootsnare", name: "🪨 Rootsnare", element: "earth", tier: "common", description: "Roots burst from the ground to ensnare the enemy", mpCost: 7, damageMin: 0, damageMax: 0, scaling: 1.3, type: "debuff", target: "single", effect: "slow", effectValue: 30, effectTurns: 3 }; window.setup.spells.ironroot = { id: "ironroot", name: "🪨 Ironroot", element: "earth", tier: "common", description: "Hardens the caster's skin with iron-like bark", mpCost: 8, damageMin: 0, damageMax: 0, scaling: 1.5, type: "buff", target: "self", effect: "armor_up", effectValue: 5, effectTurns: 3 }; window.setup.spells.cragburst = { id: "cragburst", name: "🪨 Cragburst", element: "earth", tier: "common", description: "Shards of rock explode from the ground", mpCost: 8, damageMin: 8, damageMax: 16, scaling: 1.6, type: "damage", target: "single" }; console.log("✅ Earth common (4)"); <</script>> <<script>> window.setup.spells.titans_spine = { id: "titans_spine", name: "🪨 Titan's Spine", element: "earth", tier: "mid", description: "A massive stone spine erupts beneath the enemy", mpCost: 14, damageMin: 16, damageMax: 26, scaling: 2.2, type: "damage", target: "single" }; window.setup.spells.obsidian_lance = { id: "obsidian_lance", name: "🪨 Obsidian Lance", element: "earth", tier: "mid", description: "Hurls a razor-sharp obsidian lance", mpCost: 15, damageMin: 20, damageMax: 30, scaling: 2.4, type: "damage", target: "single", armorPenetration: 12 }; window.setup.spells.tremor_pulse = { id: "tremor_pulse", name: "🪨 Tremor Pulse", element: "earth", tier: "mid", description: "Sends shockwaves through the ground", mpCost: 16, damageMin: 12, damageMax: 22, scaling: 2, type: "damage", target: "all" }; window.setup.spells.shatterstone = { id: "shatterstone", name: "🪨 Shatterstone", element: "earth", tier: "mid", description: "Shatters an enemy's armor and deals damage", mpCost: 13, damageMin: 14, damageMax: 24, scaling: 2, type: "damage", target: "single", effect: "armor_break", effectValue: 8, effectTurns: 3 }; console.log("✅ Earth mid (4)"); <</script>> <<script>> window.setup.spells.worldbreaker = { id: "worldbreaker", name: "🪨 Worldbreaker", element: "earth", tier: "legendary", description: "Cracks the earth beneath all enemies", mpCost: 30, damageMin: 28, damageMax: 42, scaling: 3, type: "damage", target: "all", armorPenetration: 15 }; window.setup.spells.pillars_of_creation = { id: "pillars_of_creation", name: "🪨 Pillars of Creation", element: "earth", tier: "legendary", description: "Raises massive pillars from the earth", mpCost: 28, damageMin: 30, damageMax: 45, scaling: 3.2, type: "damage", target: "single", effect: "stun", effectValue: 100, effectTurns: 1 }; window.setup.spells.the_sleeping_mountain = { id: "the_sleeping_mountain", name: "🪨 The Sleeping Mountain", element: "earth", tier: "legendary", description: "Awakens a dormant mountain to crush enemies", mpCost: 35, damageMin: 40, damageMax: 60, scaling: 3.8, type: "damage", target: "all" }; window.setup.spells.primordial_rupture = { id: "primordial_rupture", name: "🪨 Primordial Rupture", element: "earth", tier: "legendary", description: "Tears open the earth to primordial depths", mpCost: 40, damageMin: 60, damageMax: 85, scaling: 4.5, type: "damage", target: "single" }; console.log("✅ Earth legendary (4) - TOTAL 12 EARTH SPELLS"); <</script>> <!-- AIR SPELLS --> <<script>> window.setup.spells.gale_needle = { id: "gale_needle", name: "💨 Gale Needle", element: "air", tier: "common", description: "A needle-thin gust of wind pierces the enemy", mpCost: 6, damageMin: 7, damageMax: 15, scaling: 1.5, type: "damage", target: "single", armorPenetration: 5 }; window.setup.spells.whisperstep = { id: "whisperstep", name: "💨 Whisperstep", element: "air", tier: "common", description: "Grants the caster swift, silent movement", mpCost: 8, damageMin: 0, damageMax: 0, scaling: 1.5, type: "buff", target: "self", effect: "haste", effectValue: 20, effectTurns: 3 }; window.setup.spells.windlash = { id: "windlash", name: "💨 Windlash", element: "air", tier: "common", description: "Lashes the enemy with a whip of wind", mpCost: 7, damageMin: 6, damageMax: 14, scaling: 1.5, type: "damage", target: "single" }; window.setup.spells.static_bloom = { id: "static_bloom", name: "💨 Static Bloom", element: "air", tier: "common", description: "Static electricity builds up and discharges", mpCost: 8, damageMin: 9, damageMax: 17, scaling: 1.8, type: "damage", target: "single", effect: "shock", effectValue: 5, effectTurns: 2 }; console.log("✅ Air common (4)"); <</script>> <<script>> window.setup.spells.lightning = { id: "lightning", name: "⚡ Lightning Bolt", element: "air", tier: "mid", description: "Strikes the enemy with lightning", mpCost: 10, damageMin: 10, damageMax: 30, scaling: 2, type: "damage", target: "single" }; window.setup.spells.tempest_crown = { id: "tempest_crown", name: "💨 Tempest Crown", element: "air", tier: "mid", description: "Crowns the caster with swirling storms", mpCost: 15, damageMin: 0, damageMax: 0, scaling: 2, type: "buff", target: "self", effect: "tempest_buff", effectValue: 25, effectTurns: 4 }; window.setup.spells.razorwind = { id: "razorwind", name: "💨 Razorwind", element: "air", tier: "mid", description: "A tornado of razor-sharp wind", mpCost: 14, damageMin: 18, damageMax: 28, scaling: 2.3, type: "damage", target: "all" }; window.setup.spells.thunderwake = { id: "thunderwake", name: "💨 Thunderwake", element: "air", tier: "mid", description: "Calls thunder that echoes through enemies", mpCost: 16, damageMin: 20, damageMax: 32, scaling: 2.5, type: "damage", target: "all" }; window.setup.spells.stormveil = { id: "stormveil", name: "💨 Stormveil", element: "air", tier: "mid", description: "Wraps the caster in a veil of storms", mpCost: 13, damageMin: 0, damageMax: 0, scaling: 1.8, type: "buff", target: "self", effect: "evasion", effectValue: 25, effectTurns: 3 }; console.log("✅ Air mid (5)"); <</script>> <<script>> window.setup.spells.voice_of_the_storm = { id: "voice_of_the_storm", name: "💨 Voice of the Storm", element: "air", tier: "legendary", description: "Speak with the voice of the storm itself", mpCost: 25, damageMin: 32, damageMax: 48, scaling: 3.2, type: "damage", target: "all" }; window.setup.spells.heavenrend = { id: "heavenrend", name: "💨 Heavenrend", element: "air", tier: "legendary", description: "Rends the heavens themselves", mpCost: 30, damageMin: 40, damageMax: 60, scaling: 3.8, type: "damage", target: "single", armorPenetration: 20 }; window.setup.spells.crown_of_thunder = { id: "crown_of_thunder", name: "💨 Crown of Thunder", element: "air", tier: "legendary", description: "Dons the Crown of Thunder, gaining immense power", mpCost: 28, damageMin: 0, damageMax: 0, scaling: 3, type: "buff", target: "self", effect: "thunder_crown", effectValue: 35, effectTurns: 5 }; window.setup.spells.the_endless_gale = { id: "the_endless_gale", name: "💨 The Endless Gale", element: "air", tier: "legendary", description: "Summons an endless gale that never stops", mpCost: 40, damageMin: 45, damageMax: 70, scaling: 4.2, type: "damage", target: "all" }; console.log("✅ Air legendary (4) - TOTAL 13 AIR SPELLS"); <</script>> <!-- SPIRIT SPELLS --> <<script>> window.setup.spells.heal = { id: "heal", name: "💚 Heal", element: "spirit", tier: "common", description: "Restores health to the caster", mpCost: 6, healMin: 15, healMax: 30, scaling: 3, type: "heal", target: "self" }; window.setup.spells.soulkindle = { id: "soulkindle", name: "💚 Soulkindle", element: "spirit", tier: "common", description: "Kindles the soul, restoring health over time", mpCost: 8, healMin: 8, healMax: 15, scaling: 2, type: "heal", target: "self", effect: "regen", effectValue: 5, effectTurns: 3 }; window.setup.spells.veillight = { id: "veillight", name: "💚 Veillight", element: "spirit", tier: "common", description: "Pierces the veil between life and death", mpCost: 7, damageMin: 0, damageMax: 0, scaling: 1.5, type: "damage", target: "single", effect: "spirit_burn", effectValue: 4, effectTurns: 3 }; window.setup.spells.lifebloom = { id: "lifebloom", name: "💚 Lifebloom", element: "spirit", tier: "common", description: "Blooms with life energy, healing the caster", mpCost: 9, healMin: 10, healMax: 20, scaling: 2.5, type: "heal", target: "self" }; window.setup.spells.ghostveil = { id: "ghostveil", name: "💚 Ghostveil", element: "spirit", tier: "common", description: "Wraps the caster in a veil of spirits", mpCost: 8, damageMin: 0, damageMax: 0, scaling: 1.5, type: "buff", target: "self", effect: "ghost_veil", effectValue: 15, effectTurns: 3 }; console.log("✅ Spirit common (5)"); <</script>> <<script>> window.setup.spells.astral_binding = { id: "astral_binding", name: "💚 Astral Binding", element: "spirit", tier: "mid", description: "Binds the enemy's spirit, preventing actions", mpCost: 14, damageMin: 0, damageMax: 0, scaling: 2, type: "debuff", target: "single", effect: "stun", effectValue: 100, effectTurns: 1 }; window.setup.spells.spirit_bloom = { id: "spirit_bloom", name: "💚 Spirit Bloom", element: "spirit", tier: "mid", description: "Blooms with pure spirit energy, healing and damaging", mpCost: 15, damageMin: 12, damageMax: 22, healMin: 12, healMax: 22, scaling: 2.2, type: "dual", target: "single" }; window.setup.spells.soulwarden = { id: "soulwarden", name: "💚 Soulwarden", element: "spirit", tier: "mid", description: "Becomes a warden of souls, protecting the caster", mpCost: 16, damageMin: 0, damageMax: 0, scaling: 2, type: "buff", target: "self", effect: "soul_protect", effectValue: 20, effectTurns: 4 }; window.setup.spells.ethereal_pulse = { id: "ethereal_pulse", name: "💚 Ethereal Pulse", element: "spirit", tier: "mid", description: "Pulses with ethereal energy", mpCost: 13, damageMin: 15, damageMax: 25, scaling: 2.3, type: "damage", target: "single" }; console.log("✅ Spirit mid (4)"); <</script>> <<script>> window.setup.spells.resurrection_hymn = { id: "resurrection_hymn", name: "💚 Resurrection Hymn", element: "spirit", tier: "legendary", description: "Sings a hymn that can restore massive health", mpCost: 40, healMin: 100, healMax: 200, scaling: 5, type: "heal", target: "self" }; window.setup.spells.gate_of_spirits = { id: "gate_of_spirits", name: "💚 Gate of Spirits", element: "spirit", tier: "legendary", description: "Opens a gate to the spirit world", mpCost: 35, damageMin: 30, damageMax: 50, scaling: 3.5, type: "damage", target: "all", effect: "spirit_drain", effectValue: 20 }; window.setup.spells.crown_of_eternity = { id: "crown_of_eternity", name: "💚 Crown of Eternity", element: "spirit", tier: "legendary", description: "Dons the Crown of Eternity, transcending mortality", mpCost: 30, damageMin: 0, damageMax: 0, scaling: 3, type: "buff", target: "self", effect: "eternal_crown", effectValue: 50, effectTurns: 5 }; window.setup.spells.the_final_mercy = { id: "the_final_mercy", name: "💚 The Final Mercy", element: "spirit", tier: "legendary", description: "Offers the enemy final mercy... or final judgment", mpCost: 45, damageMin: 50, damageMax: 100, scaling: 5, type: "damage", target: "single" }; console.log("✅ Spirit legendary (4) - TOTAL 13 SPIRIT SPELLS"); console.log("✅✅✅ ALL 62 SPELLS REGISTERED! ✅✅✅"); <</script>> <<script>> window.setup.castSpell = function(spellId, enemy) { var spell = window.setup.spells[spellId]; if (!spell) return { success: false, message: "Spell not found!" }; var mpCost = spell.mpCost; var currentMP = State.variables.currentMP; var intellect = State.variables.intellect; if (currentMP < mpCost) { return { success: false, message: "Not enough MP! Need " + mpCost + " MP." }; } var result = { success: true, message: "", damage: 0, heal: 0, effects: [] }; if (spell.type === "damage") { var baseDamage = Math.floor(Math.random() * (spell.damageMax - spell.damageMin + 1) + spell.damageMin); var intBonus = intellect * spell.scaling; var totalDamage = baseDamage + intBonus; if (spell.armorPenetration) { var armorReduction = Math.min(spell.armorPenetration, (enemy.defense || 0)); totalDamage = totalDamage + armorReduction; result.message = "🧙 Cast " + spell.name + "! Dealt " + totalDamage + " damage! (Ignores " + spell.armorPenetration + " armor)"; } else { result.message = "🧙 Cast " + spell.name + "! Dealt " + totalDamage + " damage!"; } result.damage = totalDamage; if (spell.effect === "burn") { result.effects.push({ type: "burn", value: spell.effectValue, turns: spell.effectTurns }); result.message += " 🔥 Enemy is burning!"; } if (spell.effect === "poison") { result.effects.push({ type: "poison", value: spell.effectValue, turns: spell.effectTurns }); result.message += " 💚 Enemy is poisoned!"; } if (spell.effect === "freeze") { result.effects.push({ type: "freeze", value: spell.effectValue, turns: spell.effectTurns }); result.message += " ❄️ Enemy is frozen!"; } if (spell.effect === "blind") { result.effects.push({ type: "blind", value: spell.effectValue, turns: spell.effectTurns }); result.message += " 🌫️ Enemy is blinded!"; } if (spell.lifesteal) { result.lifesteal = spell.lifesteal; } } else if (spell.type === "heal") { var baseHeal = Math.floor(Math.random() * (spell.healMax - spell.healMin + 1) + spell.healMin); var intBonus = intellect * spell.scaling; var totalHeal = baseHeal + intBonus; var oldHP = State.variables.currentHP; State.variables.currentHP = Math.min(State.variables.maxHP, State.variables.currentHP + totalHeal); var actualHeal = State.variables.currentHP - oldHP; result.heal = actualHeal; result.message = "🧙 Cast " + spell.name + "! Restored " + actualHeal + " HP!"; } else if (spell.type === "buff") { result.message = "🧙 Cast " + spell.name + "!"; if (spell.effect === "fire_shield") { State.variables.fireShield = { active: true, value: spell.effectValue, turns: spell.effectTurns }; result.message += " 🔥 Fire Shield active! (" + spell.effectValue + " damage to attackers, " + spell.effectTurns + " turns)"; } if (spell.effect === "haste") { State.variables.haste = { active: true, turns: spell.effectTurns }; result.message += " ⚡ Haste active! (Extra attacks, " + spell.effectTurns + " turns)"; } if (spell.effect === "arcane_shield") { State.variables.arcaneShield = { active: true, value: spell.effectValue, turns: spell.effectTurns }; result.message += " 🛡️ Arcane Shield active! (Absorbs " + spell.effectValue + " damage, " + spell.effectTurns + " turns)"; } } else if (spell.type === "debuff") { result.message = "🧙 Cast " + spell.name + "!"; if (spell.effect === "slow") { result.effects.push({ type: "slow", value: spell.effectValue, turns: spell.effectTurns }); result.message += " 🐢 Enemy slowed!"; } if (spell.effect === "weakness") { result.effects.push({ type: "weakness", value: spell.effectValue, turns: spell.effectTurns }); result.message += " 💔 Enemy weakened!"; } } State.variables.currentMP -= mpCost; return result; }; console.log("✅ Spells casting system loaded"); <</script>> <</widget>> <</nobr>>
:: Start [nobr] <<nobr>> <div class="fancy-header">⚔️ Hilt: Sword Reborn 3.0.4 ⚔️</div> <div style="text-align: center; margin: 30px 0;"> A grand adventure awaits... </div> <div style="text-align: center;"> <!-- Custom Creation --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <<link "✨ Create Character ✨">> <<goto "CharacterCreation">> <</link>> <div style="font-size: 12px; color: #aaa;">Customize your own hero</div> </div> <!-- Premade Characters --> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 15px;"> <div style="font-family: 'Cinzel', serif; color: #d4af37; margin-bottom: 10px;">⚔️ PREMADE CHARACTERS ⚔️</div> <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px;"> <<link "🛡️ Bjorn - Warrior">> <<ApplyPremadeCharacter "Premade_Warrior">> <</link>> <<link "🗡️ Kaelen - Rogue">> <<ApplyPremadeCharacter "Premade_Rogue">> <</link>> <<link "🔮 Morgana - Mage">> <<ApplyPremadeCharacter "Premade_Mage">> <</link>> <<link "🏹 Sylvanas - Ranger">> <<ApplyPremadeCharacter "Premade_Ranger">> <</link>> </div> <div style="font-size: 11px; color: #aaa; margin-top: 10px;">Choose a ready-made hero and begin instantly!</div> </div> </div> <</nobr>>
:: StoryInit <!-- ============================================ --> <!-- 1. CORE SYSTEM LOADERS (WIDGETS) --> <!-- ============================================ --> <!-- Item System --> <<loadItems>> <<loadItemsWeaponsStarters>> <<loadItemsWeaponsDaggers>> <<loadItemsWeaponsSwords>> <<loadItemsWeaponsBows>> <<loadItemsWeaponsStaffs>> <<loadItemsArmor>> <<loadItemsHelmets>> <<loadItemsBoots>> <<loadItemsRings>> <<loadItemsAmulets>> <<loadItemsPotions>> <<loadItemsSpellbooks>> <!-- Spell System --> <<loadSpells>> <!-- Combat System --> <<loadSpecialAbilities>> <<loadCombatCore>> <<loadCombatStatusEffects>> <<loadCombatDamage>> <<loadCombatRound>> <!-- Enemy System --> <<loadEnemies>> <<loadEnemiesForest>> <<loadEnemiesSwamp>> <<loadEnemiesRoad>> <<loadEnemiesGraveyard>> <<loadEnemiesRuins>> <<loadEnemiesMountains>> <<loadEnemiesDesert>> <<loadEnemiesArctic>> <<loadEnemiesCaverns>> <<loadEnemiesCoastal>> <<loadEnemiesHaunted>> <<loadEnemiesJungle>> <<loadEnemiesFey>> <<loadEnemiesHellscape>> <<loadEnemiesTemple>> <<loadEnemiesCity>> <<loadEnemiesDeepOcean>> <<loadEnemiesShip>> <<loadEnemiesNoble>> <<loadEnemiesSewers>> <!-- Utility Systems --> <<loadInventory>> <<loadShops>> <<loadExp>> <<loadTime>> <<loadSidebar>> <<loadReset>> <!-- World Map Settings --> <<set $worldMapZoom = 8>> <<set $worldMapCenterX = 0>> <<set $worldMapCenterY = 0>> <<set $worldRadius = 30>> <<set $worldSizeName = "Medium">> <!-- Hub view tracking --> <<set $inHubView = false>> <<set $hubType = null>> <<set $hubName = "">> <<set $hubDesc = "">> <<set $hubIcon = "">> <!-- ============================================ --> <!-- 2. INITIALIZE DYNAMIC SYSTEMS --> <!-- ============================================ --> <<set $inventory = window.setup.initInventory()>> <<run window.setup.updateTimeTheme()>> <!-- ============================================ --> <!-- 3. PLAYER STATS & PROGRESSION --> <!-- ============================================ --> <!-- Core Stats (Base values before equipment) --> <<set $baseStrength = 8>> <<set $baseConstitution = 8>> <<set $baseDefense = 7>> <<set $baseAgility = 8>> <<set $baseIntellect = 8>> <<set $baseMaxHP = 30>> <!-- Current Stats (After equipment bonuses) --> <<set $strength = 8>> <<set $constitution = 8>> <<set $defense = 7>> <<set $agility = 8>> <<set $intellect = 8>> <<set $maxHP = 46>> <<set $currentHP = 46>> <<set $maxMP = 24>> <<set $currentMP = 24>> <!-- Leveling --> <<set $level = 1>> <<set $exp = 0>> <<set $nextLevelExp = 300>> <!-- Equipment --> <<set $equipment = { weapon: "fists", armor: "rags", helmet: null, boots: null, ring: null, amulet: null }>> <!-- Spells Known --> <<set $spellbook = []>> <!-- ============================================ --> <!-- 4. CHARACTER CREATION --> <!-- ============================================ --> <<set $playerName = "">> <<set $playerAge = 0>> <<set $playerRace = "">> <<set $playerSex = "">> <<set $playerClass = "">> <<set $classChoice = "">> <!-- ============================================ --> <!-- 5. COMBAT STATE --> <!-- ============================================ --> <<set $combatState = 0>> <<set $combatLogText = "">> <<set $combatActions = 0>> <<set $playerStunned = false>> <<set $playerConfused = false>> <<set $returnTo = "TownSquare">> <!-- Combat Temporaries --> <<set $currentEnemy = 0>> <<set $enemyList = []>> <<set $originalEnemyList = []>> <<set $currentEnemyIndex = 0>> <<set $allyCount = 0>> <<set $testEnemy = null>> <<set $selectedBiome = "forest">> <<set $critInfo = null>> <<set $attackResult = null>> <<set $playerDamage = 0>> <<set $playerRawDamage = 0>> <<set $playerWeaponEffects = null>> <<set $combatResult = null>> <<set $result = null>> <<set $spellResult = null>> <<set $castingSpell = null>> <<set $selectingPotion = null>> <<set $isTestBattle = false>> <!-- ============================================ --> <!-- 6. PLAYER STATUS EFFECTS --> <!-- ============================================ --> <<set $regen = false>> <<set $regenValue = 0>> <<set $regenTurns = 0>> <<set $haste = false>> <<set $hasteTurns = 0>> <<set $shield = false>> <<set $shieldValue = 0>> <<set $shieldTurns = 0>> <<set $poisoned = false>> <<set $poisonValue = 0>> <<set $poisonTurns = 0>> <<set $burning = false>> <<set $burnValue = 0>> <<set $burnTurns = 0>> <<set $confused = false>> <!-- ============================================ --> <!-- 7. ECONOMY & TIME --> <!-- ============================================ --> <<set $gold = 0>> <<set $temp = null>> <!-- Time System --> <<set $hour = 8>> <<set $minute = 0>> <<set $day = 1>> <<set $isNight = false>> <!-- Inn Rest --> <<set $goldAfterRest = false>> <<set $freeRest = false>> <<set $restMessage = null>> <!-- ============================================ --> <!-- 8. SETTINGS & CONFIGURATION --> <!-- ============================================ --> <!-- Difficulty --> <<set $difficulty = "normal">> <<set $randomEncountersEnabled = true>> <<if $randomEncountersEnabled === undefined>><<set $randomEncountersEnabled = true>><</if>> <<set $encounterTriggered = false>> <!-- Death Penalty --> <<set $deathPenaltyEnabled = false>> <<set $deathPenaltyPercent = 10>> <<set $deathPenaltyGoldPercent = 15>> <!-- Combat Settings --> <<set $fleeCooldownDuration = 3>> <!-- Debug --> <<set $debugMode = true>> <!-- ============================================ --> <!-- 9. GRID WORLD SYSTEM --> <!-- ============================================ --> <!-- Position Tracking --> <<set $gridX = 0>> <<set $gridY = 0>> <<set $discovered = ["0,0"]>> <<set $discoveredSet = new Set(["0,0"])>> <<set $clearedCamps = []>> <<set $returnToGridX = null>> <<set $returnToGridY = null>> <<set $inCombat = false>> <!-- Performance Optimizations --> <<set $moveCooldown = false>> <<set $minimapCache = {}>> <!-- Grid arrays (will be populated by GenerateGrid widget) --> <<set $gridExists = []>> <<set $gridExistsSet = new Set()>> <<set $gridBiomes = {}>> <<set $encounterRates = { town: 0, village: 2, road: 12, forest: 15, plains: 8, mountain: 18, water: 10, shrine: 0, dungeon: 0, treasure: 20, enemy_camp: 0, merchant: 0, swamp: 16, desert: 14, tundra: 17 }>> <!-- Village & Town Hub Tracking --> <<set $currentVillageTemplate = "farming">> <<set $currentVillageName = "">> <<set $currentVillageDesc = "">> <<set $currentTownName = "">> <<set $currentTownDesc = "">> <!-- Ruins Tracking --> <<set $ruinsCleared = []>> <<set $ruinsLooted = []>> <<set $ruinsLastCleared = {}>> <!-- Combat Return Routing --> <<set $combatReturnAfterVictory = null>> <!-- ============================================ --> <!-- 10. INITIALIZATION COMPLETE --> <!-- ============================================ --> <<script>> console.log("✅ StoryInit complete - All systems loaded"); <</script>>
<<nobr>> <<set $hour12 = $hour % 12>> <<if $hour12 == 0>> <<set $hour12 = 12>> <</if>> <<set $ampm = $hour < 12 ? "AM" : "PM">> <<set $minuteStr = $minute < 10 ? "0" + $minute : $minute>> <div style="padding: 15px;"> <div style="text-align: center; font-family: 'Pirata One', serif; font-size: 14px; color: #d4af37;"> Hilt: Sword Reborn 3.0.4 </div> <div style="text-align: center; font-size: 18px; font-weight: bold; background: rgba(0,0,0,0.3); border-radius: 10px; padding: 5px; margin: 10px 0;"> 📅 Day $day | <span id="sidebar-time">$hour12:$minuteStr $ampm</span> </div> <div style="font-size: 20px; font-weight: bold; text-align: center; background: rgba(0,0,0,0.3); border-radius: 10px; padding: 8px; margin: 10px 0;"> ❤️ <span id="sidebar-hp-text">$currentHP/$maxHP</span><br> 💙 <span id="sidebar-mp-text">$currentMP/$maxMP</span> </div> <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 5px; margin: 10px 0;"> <div>💪 STR: <span id="sidebar-str">$strength</span></div> <div>❤️ CON: <span id="sidebar-con">$constitution</span></div> <div>🛡️ DEF: <span id="sidebar-def">$defense</span></div> <div>⚡ AGI: <span id="sidebar-agi">$agility</span></div> <div>🧠 INT: <span id="sidebar-int">$intellect</span></div> <div>⭐ LVL: <span id="sidebar-level">$level</span></div> </div> <div>📖 EXP: <span id="sidebar-exp-text">$exp/$nextLevelExp</span></div> <div>💰 GOLD: <span id="sidebar-gold">$gold</span></div> <div style="text-align: center; border-bottom: 2px solid #d4af37; margin: 15px 0 10px 0;"> 🎒 ACTIONS </div> <<link "🧰 Inventory">> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<set $returnTo = passage()>> <<goto "Inventory">> <</link>> <<link "📖 Spellbook">> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<set $returnTo = passage()>> <<goto "Spellbook">> <</link>> <<link "📜 Character Sheet">> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<set $returnTo = passage()>> <<goto "CharacterSheet">> <</link>> <<link "🗺️ World Map">> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<set $worldMapCenterX = $gridX>> <<set $worldMapCenterY = $gridY>> <<goto "WorldOverview">> <</link>> <<link "⚙️ Settings">> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<set $returnTo = passage()>> <<goto "Settings">> <</link>> <<link "📊 Biome Stats">> <<set $returnToGridX = $gridX>> <<set $returnToGridY = $gridY>> <<goto "BiomeStats">> <</link>> <<script>> setTimeout(function() { if (window.setup.updateSidebar) window.setup.updateSidebar(); }, 50); $(document).on(':passagerender', function() { setTimeout(function() { if (window.setup.updateSidebar) window.setup.updateSidebar(); }, 50); }); <</script>> <</nobr>>
<<nobr>> <<widget "loadTime">> <<script>> // Time module is now in Story JavaScript // This widget just ensures compatibility console.log("✅ Time module widget loaded"); <</script>> <</widget>> <</nobr>>
<<nobr>> <div style="text-align: center; background: linear-gradient(135deg, #2a3a4a, #1a2a3a); border: 2px solid #d4af37; border-radius: 16px; padding: 20px;"> <h2>🏰 <<= $currentTownName >></h2> <p><i><<= $currentTownDesc >></i></p> <div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 15px; margin: 20px 0;"> <<link "🏨 Rest at Inn">> <<set $returnTo = "TownHub">> <<goto "InnRest">> <</link>> <<link "🏪 General Store">> <<set $returnTo = "TownHub">> <<goto "Shop">> <</link>> <<link "🔮 Magic Shop">> <<set $returnTo = "TownHub">> <<goto "MagicShop">> <</link>> <<link "⚔️ Blacksmith">> <<set $returnTo = "TownHub">> <<goto "BlacksmithShop">> <</link>> <<link "📜 Quest Board">> <<goto "QuestBoard">> <</link>> </div> <hr> <div style="margin-top: 20px;"> <<link "🗺️ Return to Map">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $returnToGridX = null>> <<set $returnToGridY = null>> <<goto "GridCell">> <</link>> </div> </div> <</nobr>>
<<nobr>> <div style="text-align: center; background: linear-gradient(135deg, #3a3a5a, #2a2a4a); border: 3px solid #ffd700; border-radius: 20px; padding: 25px;"> <h1 style="color: #ffd700; font-family: 'Cinzel', serif;">🏰 CENTRAL CITY 🏰</h1> <p><i>The bustling heart of the realm. From here, adventurers set forth to explore the unknown.</i></p> <div style="background: rgba(0,0,0,0.3); border-radius: 10px; padding: 10px; margin: 15px 0;"> <span style="color: #aaa;">⚔️ The city never sleeps. Merchants hawk their wares, guards patrol the walls, and rumors of adventure fill every tavern. ⚔️</span> </div> <div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 15px; margin: 25px 0;"> <<link "🏨 Rest at Inn">> <<set $returnTo = "TownHub_Start">> <<goto "InnRest">> <</link>> <<link "🏪 General Store">> <<set $returnTo = "TownHub_Start">> <<goto "Shop">> <</link>> <<link "🔮 Arcane Emporium">> <<set $returnTo = "TownHub_Start">> <<goto "MagicShop">> <</link>> <<link "⚔️ Blacksmith">> <<set $returnTo = "TownHub_Start">> <<goto "BlacksmithShop">> <</link>> <<link "🏟️ Monster Colosseum">> <<goto "MonsterColosseum">> <</link>> <<link "📜 Quest Board">> <<goto "QuestBoard">> <</link>> </div> <hr style="border-color: #ffd700;"> <div style="margin-top: 20px;"> <<link "🚪 Exit to World Map">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $returnToGridX = null>> <<set $returnToGridY = null>> <<goto "GridCell">> <</link>> </div> <div style="margin-top: 15px; font-size: 11px; color: #666;"> Current location: Central City (0,0) </div> </div> <</nobr>>
<<nobr>> <<set _template = $currentVillageTemplate>> <<set _name = $currentVillageName>> <<set _desc = $currentVillageDesc>> <<if _template == "fishing">> <div style="text-align: center; background: linear-gradient(135deg, #0a2a3a, #0a1a2a); border: 2px solid #4a8aaa; border-radius: 16px; padding: 20px;"> <h2>🎣 <<= _name >></h2> <p><i><<= _desc >></i></p> <p>A fishing village perched on the water's edge. Nets dry in the sun and boats bob gently in the harbor.</p> <div style="background: #1a4a6a; padding: 10px; border-radius: 8px; margin: 10px 0;"> <span style="color: #88ccff;">🐟 Fresh fish daily! 🐟</span> </div> </div> <<elseif _template == "logging">> <div style="text-align: center; background: linear-gradient(135deg, #2a3a1a, #1a2a0a); border: 2px solid #6a8a3a; border-radius: 16px; padding: 20px;"> <h2>🪵 <<= _name >></h2> <p><i><<= _desc >></i></p> <p>The sound of axes and saws echoes through the forest. Timber stacks line the main road.</p> <div style="background: #3a5a2a; padding: 10px; border-radius: 8px; margin: 10px 0;"> <span style="color: #aaff88;">🪓 Premium lumber! 🪓</span> </div> </div> <<elseif _template == "mining">> <div style="text-align: center; background: linear-gradient(135deg, #3a3a2a, #2a2a1a); border: 2px solid #aa8844; border-radius: 16px; padding: 20px;"> <h2>⛏️ <<= _name >></h2> <p><i><<= _desc >></i></p> <p>Stone buildings and dust-covered miners. The ringing of picks against rock never ceases.</p> <div style="background: #5a4a2a; padding: 10px; border-radius: 8px; margin: 10px 0;"> <span style="color: #ffcc66;">⛏️ Ore and gems! ⛏️</span> </div> </div> <<elseif _template == "trade">> <div style="text-align: center; background: linear-gradient(135deg, #4a3a2a, #3a2a1a); border: 2px solid #d4af37; border-radius: 16px; padding: 20px;"> <h2>🚂 <<= _name >></h2> <p><i><<= _desc >></i></p> <p>A bustling crossroads town. Caravans come and go, bringing exotic goods from distant lands.</p> <div style="background: #6a4a2a; padding: 10px; border-radius: 8px; margin: 10px 0;"> <span style="color: #ffdd88;">💰 Rare goods available! 💰</span> </div> </div> <<elseif _template == "farming">> <div style="text-align: center; background: linear-gradient(135deg, #3a5a2a, #2a4a1a); border: 2px solid #8aba4a; border-radius: 16px; padding: 20px;"> <h2>🌾 <<= _name >></h2> <p><i><<= _desc >></i></p> <p>Golden wheat fields sway in the breeze. Simple folk live simple lives here.</p> <div style="background: #4a6a2a; padding: 10px; border-radius: 8px; margin: 10px 0;"> <span style="color: #ccff88;">🌽 Fresh produce! 🌽</span> </div> </div> <<elseif _template == "hunting">> <div style="text-align: center; background: linear-gradient(135deg, #2a4a2a, #1a3a1a); border: 2px solid #6aaa4a; border-radius: 16px; padding: 20px;"> <h2>🏹 <<= _name >></h2> <p><i><<= _desc >></i></p> <p>Tanned hides stretch between buildings. Hunters return with today's quarry.</p> <div style="background: #3a5a2a; padding: 10px; border-radius: 8px; margin: 10px 0;"> <span style="color: #aaffaa;">🏹 Quality furs and leather! 🏹</span> </div> </div> <<elseif _template == "religious">> <div style="text-align: center; background: linear-gradient(135deg, #4a4a6a, #3a3a5a); border: 2px solid #c4aaf0; border-radius: 16px; padding: 20px;"> <h2>🛐 <<= _name >></h2> <p><i><<= _desc >></i></p> <p>A serene village built around an ancient shrine. Pilgrims visit for blessings.</p> <div style="background: #5a5a7a; padding: 10px; border-radius: 8px; margin: 10px 0;"> <span style="color: #ddaaff;">✨ Healing and blessings! ✨</span> </div> </div> <<elseif _template == "remote">> <div style="text-align: center; background: linear-gradient(135deg, #3a3a3a, #2a2a2a); border: 2px solid #888888; border-radius: 16px; padding: 20px;"> <h2>🏔️ <<= _name >></h2> <p><i><<= _desc >></i></p> <p>A lonely hamlet at the edge of civilization. They don't get many visitors.</p> <div style="background: #4a4a4a; padding: 10px; border-radius: 8px; margin: 10px 0;"> <span style="color: #aaaaaa;">🏔️ Basic supplies only 🏔️</span> </div> </div> <<elseif _template == "military">> <div style="text-align: center; background: linear-gradient(135deg, #6a3a3a, #5a2a2a); border: 2px solid #cc6644; border-radius: 16px; padding: 20px;"> <h2>⚔️ <<= _name >></h2> <p><i><<= _desc >></i></p> <p>Soldiers patrol the walls. The clang of steel on steel rings from the training grounds.</p> <div style="background: #7a4a4a; padding: 10px; border-radius: 8px; margin: 10px 0;"> <span style="color: #ffaa88;">⚔️ Weapons and training! ⚔️</span> </div> </div> <<elseif _template == "magical">> <div style="text-align: center; background: linear-gradient(135deg, #4a2a5a, #3a1a4a); border: 2px solid #aa66ff; border-radius: 16px; padding: 20px;"> <h2>🔮 <<= _name >></h2> <p><i><<= _desc >></i></p> <p>Arcane energy crackles in the air. Wizards and sages study ancient tomes here.</p> <div style="background: #5a3a6a; padding: 10px; border-radius: 8px; margin: 10px 0;"> <span style="color: #cc88ff;">🔮 Spell scrolls and artifacts! 🔮</span> </div> </div> <</if>> <!-- COMMON VILLAGE SERVICES --> <div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 15px; margin: 20px 0;"> <<link "🏨 Rest at Inn">> <<set $returnTo = "VillageHub">> <<goto "InnRest">> <</link>> <<link "🏪 General Store">> <<set $returnTo = "VillageHub">> <<goto "Shop">> <</link>> <<if _template == "trade" || _template == "magical">> <<link "🔮 Magic Shop">> <<set $returnTo = "VillageHub">> <<goto "MagicShop">> <</link>> <</if>> <<if _template == "mining" || _template == "military">> <<link "⚔️ Blacksmith">> <<set $returnTo = "VillageHub">> <<goto "BlacksmithShop">> <</link>> <</if>> <<if _template == "fishing">> <<link "🐟 Fish Market">> <<set $returnTo = "VillageHub">> <<goto "FishMarket">> <</link>> <</if>> <<if _template == "logging">> <<link "🪵 Lumber Trader">> <<set $returnTo = "VillageHub">> <<goto "LumberTrader">> <</link>> <</if>> <<if _template == "hunting">> <<link "🏹 Fur Trader">> <<set $returnTo = "VillageHub">> <<goto "FurTrader">> <</link>> <</if>> <<if _template == "religious">> <<link "🛐 Visit Shrine">> <<set $currentHP = Math.min($maxHP, $currentHP + Math.floor($maxHP * 0.2))>> <<set $combatLogText = "You feel blessed by the shrine's energy!<br>">> <<goto "VillageHub">> <</link>> <</if>> </div> <div style="text-align: center; margin-top: 20px;"> <<link "🗺️ Return to Map">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $returnToGridX = null>> <<set $returnToGridY = null>> <<goto "GridCell">> <</link>> </div> <</nobr>>
:: WorldOverview [nobr] <<nobr>> <style> .world-overview { text-align: center; padding: 10px; } .world-overview h2 { font-family: 'Cinzel', serif; color: #d4af37; margin-bottom: 15px; } .world-overview-container { display: inline-block; background: #0a0a1a; padding: 10px; border-radius: 16px; border: 2px solid #d4af37; overflow: auto; max-width: 98vw; max-height: 80vh; } .world-overview-row { display: flex; justify-content: center; line-height: 0; } .world-cell { margin: 0px; display: inline-block; } </style> <div class="world-overview"> <h2>🗺️ WORLD BIOME OVERVIEW 🗺️</h2> <div style="text-align: center; margin-bottom: 15px;"> <div style="margin-bottom: 10px;"> <span style="color: #d4af37;">Cell Size:</span> <<link "Tiny (3px)">><<set $overviewCellSize = 3>><<goto "WorldOverview">><</link>> <<link "Small (4px)">><<set $overviewCellSize = 4>><<goto "WorldOverview">><</link>> <<link "Medium (5px)">><<set $overviewCellSize = 5>><<goto "WorldOverview">><</link>> <<link "Large (6px)">><<set $overviewCellSize = 6>><<goto "WorldOverview">><</link>> <<link "X-Large (7px)">><<set $overviewCellSize = 7>><<goto "WorldOverview">><</link>> </div> <<link "🔄 Refresh">> <<goto "WorldOverview">> <</link>> | <<link "❌ Return to Map">> <<goto "GridCell">> <</link>> </div> <div class="world-overview-container"> <<if !$overviewCellSize>> <<set $overviewCellSize = 5>> <</if>> <<set _cellSize = $overviewCellSize>> <!-- Build map using pure Twine macros --> <<set _output = "">> <<for _dy = 60; _dy >= -60; _dy-->> <<set _output = _output + '<div class="world-overview-row">'>> <<for _dx = -60; _dx <= 60; _dx++>> <<set _key = _dx + "," + _dy>> <<set _color = "#0a0a12">> <<if $gridExistsSet.has(_key) && $discoveredSet.has(_key)>> <<set _biome = $gridBiomes[_key]>> <<if _biome && _biome.type == "town">> <<set _color = "#4a6a8a">> <<elseif _biome && _biome.type == "village">> <<set _color = "#4a6a8a">> <<elseif _biome && _biome.type == "forest">> <<set _color = "#2a6a2a">> <<elseif _biome && _biome.type == "plains">> <<set _color = "#6a8a4a">> <<elseif _biome && _biome.type == "mountain">> <<set _color = "#8a8a9a">> <<elseif _biome && _biome.type == "water">> <<set _color = "#2a5a8a">> <<elseif _biome && _biome.type == "swamp">> <<set _color = "#4a7a3a">> <<elseif _biome && _biome.type == "desert">> <<set _color = "#caa860">> <<elseif _biome && _biome.type == "tundra">> <<set _color = "#9abccc">> <<elseif _biome && _biome.type == "road">> <<set _color = "#5a5a5a">> <<elseif _biome && _biome.type == "enemy_camp">> <<set _color = "#8a3a3a">> <<elseif _biome && _biome.type == "shrine">> <<set _color = "#8a7a3a">> <<elseif _biome && _biome.type == "dungeon">> <<set _color = "#5a3a5a">> <<elseif _biome && _biome.type == "treasure">> <<set _color = "#8a7a3a">> <<elseif _biome && _biome.type == "merchant">> <<set _color = "#6a5a3a">> <</if>> <<elseif $gridExistsSet.has(_key)>> <<set _color = "#1a1a2a">> <</if>> <<if _dx == $gridX && _dy == $gridY>> <<set _color = "#ff4444">> <</if>> <<set _output = _output + '<div class="world-cell" style="width: ' + _cellSize + 'px; height: ' + _cellSize + 'px; background: ' + _color + ';"></div>'>> <</for>> <<set _output = _output + '</div>'>> <</for>> <<= _output >> </div> <div style="margin-top: 15px; font-size: 11px; color: #aaa;"> <div>🎨 Full World Overview - 121x121 cells at <<= _cellSize >>px each</div> <div>🗺️ Discovered: $discovered.length / $gridExists.length locations</div> <div style="margin-top: 8px; display: flex; flex-wrap: wrap; justify-content: center; gap: 12px;"> <span><span style="background: #2a6a2a; display: inline-block; width: 14px; height: 14px;"></span> Forest</span> <span><span style="background: #6a8a4a; display: inline-block; width: 14px; height: 14px;"></span> Plains</span> <span><span style="background: #8a8a9a; display: inline-block; width: 14px; height: 14px;"></span> Mountain</span> <span><span style="background: #2a5a8a; display: inline-block; width: 14px; height: 14px;"></span> Water</span> <span><span style="background: #caa860; display: inline-block; width: 14px; height: 14px;"></span> Desert</span> <span><span style="background: #9abccc; display: inline-block; width: 14px; height: 14px;"></span> Tundra</span> <span><span style="background: #4a7a3a; display: inline-block; width: 14px; height: 14px;"></span> Swamp</span> <span><span style="background: #4a6a8a; display: inline-block; width: 14px; height: 14px;"></span> Town/Village</span> <span><span style="background: #ff4444; display: inline-block; width: 14px; height: 14px;"></span> You</span> </div> </div> <div style="text-align: center; margin-top: 20px;"> <<link "🗺️ Return to Game">> <<set $gridX = $returnToGridX>> <<set $gridY = $returnToGridY>> <<set $coord = $gridX + "," + $gridY>> <<goto "GridCell">> <</link>> </div> <</nobr>>
:: WorldSizeSelect [nobr] <<nobr>> <div class="fancy-header">🗺️ WORLD SIZE SELECTION 🗺️</div> <div style="background: rgba(0,0,0,0.5); border-radius: 15px; padding: 20px; margin: 15px 0; text-align: center;"> <div style="font-size: 48px; margin-bottom: 10px;">🌍</div> <div style="font-family: 'Cinzel', serif; color: #d4af37; font-size: 20px;">"How vast shall your adventure be?"</div> <div style="margin: 10px 0; color: #aaa;">Choose the size of your world. This cannot be changed after creation.</div> </div> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 20px;"> <div style="display: flex; flex-wrap: wrap; gap: 15px; justify-content: center; margin: 15px 0;"> <<link "🟢 Small World">> <<set $worldRadius = 20>> <<set $worldSizeName = "Small">> <<goto "CharacterCreation">> <</link>> <<if $worldRadius == 30>> <div style="margin-top: 10px; font-size: 12px; color: #88ff88;">✨ Recommended for most players ✨</div> <</if>> <<link "🟡 Medium World">> <<set $worldRadius = 30>> <<set $worldSizeName = "Medium">> <<goto "CharacterCreation">> <</link>> <<link "🟠 Large World">> <<set $worldRadius = 45>> <<set $worldSizeName = "Large">> <<goto "CharacterCreation">> <</link>> <<link "🔴 Massive World">> <<set $worldRadius = 60>> <<set $worldSizeName = "Massive">> <<goto "CharacterCreation">> <</link>> </div> </div> <div style="background: rgba(0,0,0,0.3); border-radius: 15px; padding: 15px; margin-bottom: 20px;"> <table style="width: 100%; border-collapse: collapse;"> <tr style="border-bottom: 1px solid #d4af37;"> <th style="text-align: left; padding: 8px;">Size</th> <th style="text-align: right; padding: 8px;">Radius</th> <th style="text-align: right; padding: 8px;">Cells</th> <th style="text-align: left; padding: 8px;">Performance</th> </tr> <tr style="border-bottom: 1px solid #333;"> <td style="padding: 8px;">🟢 Small</td> <td style="text-align: right; padding: 8px;">20</td> <td style="text-align: right; padding: 8px;">~1,200</td> <td style="padding: 8px;">Very Fast</td> </tr> <tr style="border-bottom: 1px solid #333;"> <td style="padding: 8px;">🟡 Medium</td> <td style="text-align: right; padding: 8px;">30</td> <td style="text-align: right; padding: 8px;">~2,800</td> <td style="padding: 8px;">Fast</td> </tr> <tr style="border-bottom: 1px solid #333;"> <td style="padding: 8px;">🟠 Large</td> <td style="text-align: right; padding: 8px;">45</td> <td style="text-align: right; padding: 8px;">~6,300</td> <td style="padding: 8px;">Good</td> </tr> <tr> <td style="padding: 8px;">🔴 Massive</td> <td style="text-align: right; padding: 8px;">60</td> <td style="text-align: right; padding: 8px;">~11,300</td> <td style="padding: 8px;">Slower (Current)</td> </tr> </table> <<if $worldRadius == 60>> <div style="background: rgba(255,100,100,0.2); border: 1px solid #ff6666; border-radius: 10px; padding: 10px; margin-top: 15px;"> <span style="color: #ff8888;">⚠️ CAUTION: Massive worlds push the limits of what Twine can handle. Recommended for powerful computers only. ⚠️</span> </div> <</if>> </div> <div style="text-align: center; margin-top: 20px;"> <<link "↩️ Back to Start">> <<goto "Start">> <</link>> </div> <</nobr>>