From 958a7098a275446dedc16e9186eddebf03024f75 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Wed, 13 Aug 2025 19:15:01 -0500 Subject: [PATCH] work on converting to in-memory --- data/drops.json | 226 +++++ data/items.json | 266 ++++++ data/monsters.json | 1663 +++++++++++++++++++++++++++++++++ data/spells.json | 135 +++ data/towns.json | 82 ++ internal/install/install.go | 500 ---------- internal/monsters/monsters.go | 521 ++++++++--- internal/server/server.go | 12 + test_load_data.go | 34 + 9 files changed, 2822 insertions(+), 617 deletions(-) create mode 100644 data/drops.json create mode 100644 data/items.json create mode 100644 data/monsters.json create mode 100644 data/spells.json create mode 100644 data/towns.json delete mode 100644 internal/install/install.go create mode 100644 test_load_data.go diff --git a/data/drops.json b/data/drops.json new file mode 100644 index 0000000..01f3491 --- /dev/null +++ b/data/drops.json @@ -0,0 +1,226 @@ +[ + { + "id": 1, + "name": "Life Pebble", + "level": 1, + "type": 1, + "att": "maxhp,10" + }, + { + "id": 2, + "name": "Life Stone", + "level": 10, + "type": 1, + "att": "maxhp,25" + }, + { + "id": 3, + "name": "Life Rock", + "level": 25, + "type": 1, + "att": "maxhp,50" + }, + { + "id": 4, + "name": "Magic Pebble", + "level": 1, + "type": 1, + "att": "maxmp,10" + }, + { + "id": 5, + "name": "Magic Stone", + "level": 10, + "type": 1, + "att": "maxmp,25" + }, + { + "id": 6, + "name": "Magic Rock", + "level": 25, + "type": 1, + "att": "maxmp,50" + }, + { + "id": 7, + "name": "Dragon's Scale", + "level": 10, + "type": 1, + "att": "defensepower,25" + }, + { + "id": 8, + "name": "Dragon's Plate", + "level": 30, + "type": 1, + "att": "defensepower,50" + }, + { + "id": 9, + "name": "Dragon's Claw", + "level": 10, + "type": 1, + "att": "attackpower,25" + }, + { + "id": 10, + "name": "Dragon's Tooth", + "level": 30, + "type": 1, + "att": "attackpower,50" + }, + { + "id": 11, + "name": "Dragon's Tear", + "level": 35, + "type": 1, + "att": "strength,50" + }, + { + "id": 12, + "name": "Dragon's Wing", + "level": 35, + "type": 1, + "att": "dexterity,50" + }, + { + "id": 13, + "name": "Demon's Sin", + "level": 35, + "type": 1, + "att": "maxhp,-50,strength,50" + }, + { + "id": 14, + "name": "Demon's Fall", + "level": 35, + "type": 1, + "att": "maxmp,-50,strength,50" + }, + { + "id": 15, + "name": "Demon's Lie", + "level": 45, + "type": 1, + "att": "maxhp,-100,strength,100" + }, + { + "id": 16, + "name": "Demon's Hate", + "level": 45, + "type": 1, + "att": "maxmp,-100,strength,100" + }, + { + "id": 17, + "name": "Angel's Joy", + "level": 25, + "type": 1, + "att": "maxhp,25,strength,25" + }, + { + "id": 18, + "name": "Angel's Rise", + "level": 30, + "type": 1, + "att": "maxhp,50,strength,50" + }, + { + "id": 19, + "name": "Angel's Truth", + "level": 35, + "type": 1, + "att": "maxhp,75,strength,75" + }, + { + "id": 20, + "name": "Angel's Love", + "level": 40, + "type": 1, + "att": "maxhp,100,strength,100" + }, + { + "id": 21, + "name": "Seraph's Joy", + "level": 25, + "type": 1, + "att": "maxmp,25,dexterity,25" + }, + { + "id": 22, + "name": "Seraph's Rise", + "level": 30, + "type": 1, + "att": "maxmp,50,dexterity,50" + }, + { + "id": 23, + "name": "Seraph's Truth", + "level": 35, + "type": 1, + "att": "maxmp,75,dexterity,75" + }, + { + "id": 24, + "name": "Seraph's Love", + "level": 40, + "type": 1, + "att": "maxmp,100,dexterity,100" + }, + { + "id": 25, + "name": "Ruby", + "level": 50, + "type": 1, + "att": "maxhp,150" + }, + { + "id": 26, + "name": "Pearl", + "level": 50, + "type": 1, + "att": "maxmp,150" + }, + { + "id": 27, + "name": "Emerald", + "level": 50, + "type": 1, + "att": "strength,150" + }, + { + "id": 28, + "name": "Topaz", + "level": 50, + "type": 1, + "att": "dexterity,150" + }, + { + "id": 29, + "name": "Obsidian", + "level": 50, + "type": 1, + "att": "attackpower,150" + }, + { + "id": 30, + "name": "Diamond", + "level": 50, + "type": 1, + "att": "defensepower,150" + }, + { + "id": 31, + "name": "Memory Drop", + "level": 5, + "type": 1, + "att": "expbonus,10" + }, + { + "id": 32, + "name": "Fortune Drop", + "level": 5, + "type": 1, + "att": "goldbonus,10" + } +] \ No newline at end of file diff --git a/data/items.json b/data/items.json new file mode 100644 index 0000000..e185f4d --- /dev/null +++ b/data/items.json @@ -0,0 +1,266 @@ +[ + { + "id": 1, + "type": 1, + "name": "Stick", + "value": 10, + "att": 2, + "special": "" + }, + { + "id": 2, + "type": 1, + "name": "Branch", + "value": 30, + "att": 4, + "special": "" + }, + { + "id": 3, + "type": 1, + "name": "Club", + "value": 40, + "att": 5, + "special": "" + }, + { + "id": 4, + "type": 1, + "name": "Dagger", + "value": 90, + "att": 8, + "special": "" + }, + { + "id": 5, + "type": 1, + "name": "Hatchet", + "value": 150, + "att": 12, + "special": "" + }, + { + "id": 6, + "type": 1, + "name": "Axe", + "value": 200, + "att": 16, + "special": "" + }, + { + "id": 7, + "type": 1, + "name": "Brand", + "value": 300, + "att": 25, + "special": "" + }, + { + "id": 8, + "type": 1, + "name": "Poleaxe", + "value": 500, + "att": 35, + "special": "" + }, + { + "id": 9, + "type": 1, + "name": "Broadsword", + "value": 800, + "att": 45, + "special": "" + }, + { + "id": 10, + "type": 1, + "name": "Battle Axe", + "value": 1200, + "att": 50, + "special": "" + }, + { + "id": 11, + "type": 1, + "name": "Claymore", + "value": 2000, + "att": 60, + "special": "" + }, + { + "id": 12, + "type": 1, + "name": "Dark Axe", + "value": 3000, + "att": 100, + "special": "expbonus,-5" + }, + { + "id": 13, + "type": 1, + "name": "Dark Sword", + "value": 4500, + "att": 125, + "special": "expbonus,-10" + }, + { + "id": 14, + "type": 1, + "name": "Bright Sword", + "value": 6000, + "att": 100, + "special": "expbonus,10" + }, + { + "id": 15, + "type": 1, + "name": "Magic Sword", + "value": 10000, + "att": 150, + "special": "maxmp,50" + }, + { + "id": 16, + "type": 1, + "name": "Destiny Blade", + "value": 50000, + "att": 250, + "special": "strength,50" + }, + { + "id": 17, + "type": 2, + "name": "Skivvies", + "value": 25, + "att": 2, + "special": "goldbonus,10" + }, + { + "id": 18, + "type": 2, + "name": "Clothes", + "value": 50, + "att": 5, + "special": "" + }, + { + "id": 19, + "type": 2, + "name": "Leather Armor", + "value": 75, + "att": 10, + "special": "" + }, + { + "id": 20, + "type": 2, + "name": "Hard Leather Armor", + "value": 150, + "att": 25, + "special": "" + }, + { + "id": 21, + "type": 2, + "name": "Chain Mail", + "value": 300, + "att": 30, + "special": "" + }, + { + "id": 22, + "type": 2, + "name": "Bronze Plate", + "value": 900, + "att": 50, + "special": "" + }, + { + "id": 23, + "type": 2, + "name": "Iron Plate", + "value": 2000, + "att": 100, + "special": "" + }, + { + "id": 24, + "type": 2, + "name": "Magic Armor", + "value": 4000, + "att": 125, + "special": "maxmp,50" + }, + { + "id": 25, + "type": 2, + "name": "Dark Armor", + "value": 5000, + "att": 150, + "special": "expbonus,-10" + }, + { + "id": 26, + "type": 2, + "name": "Bright Armor", + "value": 10000, + "att": 175, + "special": "expbonus,10" + }, + { + "id": 27, + "type": 2, + "name": "Destiny Raiment", + "value": 50000, + "att": 200, + "special": "dexterity,50" + }, + { + "id": 28, + "type": 3, + "name": "Reed Shield", + "value": 50, + "att": 2, + "special": "" + }, + { + "id": 29, + "type": 3, + "name": "Buckler", + "value": 100, + "att": 4, + "special": "" + }, + { + "id": 30, + "type": 3, + "name": "Small Shield", + "value": 500, + "att": 10, + "special": "" + }, + { + "id": 31, + "type": 3, + "name": "Large Shield", + "value": 2500, + "att": 30, + "special": "" + }, + { + "id": 32, + "type": 3, + "name": "Silver Shield", + "value": 10000, + "att": 60, + "special": "" + }, + { + "id": 33, + "type": 3, + "name": "Destiny Aegis", + "value": 25000, + "att": 100, + "special": "maxhp,50" + } +] \ No newline at end of file diff --git a/data/monsters.json b/data/monsters.json new file mode 100644 index 0000000..8383a29 --- /dev/null +++ b/data/monsters.json @@ -0,0 +1,1663 @@ +[ + { + "id": 1, + "name": "Blue Slime", + "max_hp": 4, + "max_dmg": 3, + "armor": 1, + "level": 1, + "max_exp": 1, + "max_gold": 1, + "immune": 0 + }, + { + "id": 2, + "name": "Red Slime", + "max_hp": 6, + "max_dmg": 5, + "armor": 1, + "level": 1, + "max_exp": 2, + "max_gold": 1, + "immune": 0 + }, + { + "id": 3, + "name": "Critter", + "max_hp": 6, + "max_dmg": 5, + "armor": 2, + "level": 1, + "max_exp": 4, + "max_gold": 2, + "immune": 0 + }, + { + "id": 4, + "name": "Creature", + "max_hp": 10, + "max_dmg": 8, + "armor": 2, + "level": 2, + "max_exp": 4, + "max_gold": 2, + "immune": 0 + }, + { + "id": 5, + "name": "Shadow", + "max_hp": 10, + "max_dmg": 9, + "armor": 3, + "level": 2, + "max_exp": 6, + "max_gold": 2, + "immune": 1 + }, + { + "id": 6, + "name": "Drake", + "max_hp": 11, + "max_dmg": 10, + "armor": 3, + "level": 2, + "max_exp": 8, + "max_gold": 3, + "immune": 0 + }, + { + "id": 7, + "name": "Shade", + "max_hp": 12, + "max_dmg": 10, + "armor": 3, + "level": 3, + "max_exp": 10, + "max_gold": 3, + "immune": 1 + }, + { + "id": 8, + "name": "Drakelor", + "max_hp": 14, + "max_dmg": 12, + "armor": 4, + "level": 3, + "max_exp": 10, + "max_gold": 3, + "immune": 0 + }, + { + "id": 9, + "name": "Silver Slime", + "max_hp": 15, + "max_dmg": 100, + "armor": 200, + "level": 30, + "max_exp": 15, + "max_gold": 1000, + "immune": 2 + }, + { + "id": 10, + "name": "Scamp", + "max_hp": 16, + "max_dmg": 13, + "armor": 5, + "level": 4, + "max_exp": 15, + "max_gold": 5, + "immune": 0 + }, + { + "id": 11, + "name": "Raven", + "max_hp": 16, + "max_dmg": 13, + "armor": 5, + "level": 4, + "max_exp": 18, + "max_gold": 6, + "immune": 0 + }, + { + "id": 12, + "name": "Scorpion", + "max_hp": 18, + "max_dmg": 14, + "armor": 6, + "level": 5, + "max_exp": 20, + "max_gold": 7, + "immune": 0 + }, + { + "id": 13, + "name": "Illusion", + "max_hp": 20, + "max_dmg": 15, + "armor": 6, + "level": 5, + "max_exp": 20, + "max_gold": 7, + "immune": 1 + }, + { + "id": 14, + "name": "Nightshade", + "max_hp": 22, + "max_dmg": 16, + "armor": 6, + "level": 6, + "max_exp": 24, + "max_gold": 8, + "immune": 0 + }, + { + "id": 15, + "name": "Drakemal", + "max_hp": 22, + "max_dmg": 18, + "armor": 7, + "level": 6, + "max_exp": 24, + "max_gold": 8, + "immune": 0 + }, + { + "id": 16, + "name": "Shadow Raven", + "max_hp": 24, + "max_dmg": 18, + "armor": 7, + "level": 6, + "max_exp": 26, + "max_gold": 9, + "immune": 1 + }, + { + "id": 17, + "name": "Ghost", + "max_hp": 24, + "max_dmg": 20, + "armor": 8, + "level": 6, + "max_exp": 28, + "max_gold": 9, + "immune": 0 + }, + { + "id": 18, + "name": "Frost Raven", + "max_hp": 26, + "max_dmg": 20, + "armor": 8, + "level": 7, + "max_exp": 30, + "max_gold": 10, + "immune": 0 + }, + { + "id": 19, + "name": "Rogue Scorpion", + "max_hp": 28, + "max_dmg": 22, + "armor": 9, + "level": 7, + "max_exp": 32, + "max_gold": 11, + "immune": 0 + }, + { + "id": 20, + "name": "Ghoul", + "max_hp": 29, + "max_dmg": 24, + "armor": 9, + "level": 7, + "max_exp": 34, + "max_gold": 11, + "immune": 0 + }, + { + "id": 21, + "name": "Magician", + "max_hp": 30, + "max_dmg": 24, + "armor": 10, + "level": 8, + "max_exp": 36, + "max_gold": 12, + "immune": 0 + }, + { + "id": 22, + "name": "Rogue", + "max_hp": 30, + "max_dmg": 25, + "armor": 12, + "level": 8, + "max_exp": 40, + "max_gold": 13, + "immune": 0 + }, + { + "id": 23, + "name": "Drakefin", + "max_hp": 32, + "max_dmg": 26, + "armor": 12, + "level": 8, + "max_exp": 40, + "max_gold": 13, + "immune": 0 + }, + { + "id": 24, + "name": "Shimmer", + "max_hp": 32, + "max_dmg": 26, + "armor": 14, + "level": 8, + "max_exp": 45, + "max_gold": 15, + "immune": 1 + }, + { + "id": 25, + "name": "Fire Raven", + "max_hp": 34, + "max_dmg": 28, + "armor": 14, + "level": 9, + "max_exp": 45, + "max_gold": 15, + "immune": 0 + }, + { + "id": 26, + "name": "Dybbuk", + "max_hp": 34, + "max_dmg": 28, + "armor": 14, + "level": 9, + "max_exp": 50, + "max_gold": 17, + "immune": 0 + }, + { + "id": 27, + "name": "Knave", + "max_hp": 36, + "max_dmg": 30, + "armor": 15, + "level": 9, + "max_exp": 52, + "max_gold": 17, + "immune": 0 + }, + { + "id": 28, + "name": "Goblin", + "max_hp": 36, + "max_dmg": 30, + "armor": 15, + "level": 10, + "max_exp": 54, + "max_gold": 18, + "immune": 0 + }, + { + "id": 29, + "name": "Skeleton", + "max_hp": 38, + "max_dmg": 30, + "armor": 18, + "level": 10, + "max_exp": 58, + "max_gold": 19, + "immune": 0 + }, + { + "id": 30, + "name": "Dark Slime", + "max_hp": 38, + "max_dmg": 32, + "armor": 18, + "level": 10, + "max_exp": 62, + "max_gold": 21, + "immune": 0 + }, + { + "id": 31, + "name": "Silver Scorpion", + "max_hp": 30, + "max_dmg": 160, + "armor": 350, + "level": 40, + "max_exp": 63, + "max_gold": 2000, + "immune": 2 + }, + { + "id": 32, + "name": "Mirage", + "max_hp": 40, + "max_dmg": 32, + "armor": 20, + "level": 11, + "max_exp": 64, + "max_gold": 21, + "immune": 1 + }, + { + "id": 33, + "name": "Sorceror", + "max_hp": 41, + "max_dmg": 33, + "armor": 22, + "level": 11, + "max_exp": 68, + "max_gold": 23, + "immune": 0 + }, + { + "id": 34, + "name": "Imp", + "max_hp": 42, + "max_dmg": 34, + "armor": 22, + "level": 12, + "max_exp": 70, + "max_gold": 23, + "immune": 0 + }, + { + "id": 35, + "name": "Nymph", + "max_hp": 43, + "max_dmg": 35, + "armor": 22, + "level": 12, + "max_exp": 70, + "max_gold": 23, + "immune": 0 + }, + { + "id": 36, + "name": "Scoundrel", + "max_hp": 43, + "max_dmg": 35, + "armor": 22, + "level": 12, + "max_exp": 75, + "max_gold": 25, + "immune": 0 + }, + { + "id": 37, + "name": "Megaskeleton", + "max_hp": 44, + "max_dmg": 36, + "armor": 24, + "level": 13, + "max_exp": 78, + "max_gold": 26, + "immune": 0 + }, + { + "id": 38, + "name": "Grey Wolf", + "max_hp": 44, + "max_dmg": 36, + "armor": 24, + "level": 13, + "max_exp": 82, + "max_gold": 27, + "immune": 0 + }, + { + "id": 39, + "name": "Phantom", + "max_hp": 46, + "max_dmg": 38, + "armor": 24, + "level": 14, + "max_exp": 85, + "max_gold": 28, + "immune": 1 + }, + { + "id": 40, + "name": "Specter", + "max_hp": 46, + "max_dmg": 38, + "armor": 24, + "level": 14, + "max_exp": 90, + "max_gold": 30, + "immune": 0 + }, + { + "id": 41, + "name": "Dark Scorpion", + "max_hp": 48, + "max_dmg": 40, + "armor": 26, + "level": 15, + "max_exp": 95, + "max_gold": 32, + "immune": 1 + }, + { + "id": 42, + "name": "Warlock", + "max_hp": 48, + "max_dmg": 40, + "armor": 26, + "level": 15, + "max_exp": 100, + "max_gold": 33, + "immune": 1 + }, + { + "id": 43, + "name": "Orc", + "max_hp": 49, + "max_dmg": 42, + "armor": 28, + "level": 15, + "max_exp": 104, + "max_gold": 35, + "immune": 0 + }, + { + "id": 44, + "name": "Sylph", + "max_hp": 49, + "max_dmg": 42, + "armor": 28, + "level": 15, + "max_exp": 106, + "max_gold": 35, + "immune": 0 + }, + { + "id": 45, + "name": "Wraith", + "max_hp": 50, + "max_dmg": 45, + "armor": 30, + "level": 16, + "max_exp": 108, + "max_gold": 36, + "immune": 0 + }, + { + "id": 46, + "name": "Hellion", + "max_hp": 50, + "max_dmg": 45, + "armor": 30, + "level": 16, + "max_exp": 110, + "max_gold": 37, + "immune": 0 + }, + { + "id": 47, + "name": "Bandit", + "max_hp": 52, + "max_dmg": 45, + "armor": 30, + "level": 16, + "max_exp": 114, + "max_gold": 38, + "immune": 0 + }, + { + "id": 48, + "name": "Ultraskeleton", + "max_hp": 52, + "max_dmg": 46, + "armor": 32, + "level": 16, + "max_exp": 116, + "max_gold": 39, + "immune": 0 + }, + { + "id": 49, + "name": "Dark Wolf", + "max_hp": 54, + "max_dmg": 47, + "armor": 36, + "level": 17, + "max_exp": 120, + "max_gold": 40, + "immune": 1 + }, + { + "id": 50, + "name": "Troll", + "max_hp": 56, + "max_dmg": 48, + "armor": 36, + "level": 17, + "max_exp": 120, + "max_gold": 40, + "immune": 0 + }, + { + "id": 51, + "name": "Werewolf", + "max_hp": 56, + "max_dmg": 48, + "armor": 38, + "level": 17, + "max_exp": 124, + "max_gold": 41, + "immune": 0 + }, + { + "id": 52, + "name": "Hellcat", + "max_hp": 58, + "max_dmg": 50, + "armor": 38, + "level": 18, + "max_exp": 128, + "max_gold": 43, + "immune": 0 + }, + { + "id": 53, + "name": "Spirit", + "max_hp": 58, + "max_dmg": 50, + "armor": 38, + "level": 18, + "max_exp": 132, + "max_gold": 44, + "immune": 0 + }, + { + "id": 54, + "name": "Nisse", + "max_hp": 60, + "max_dmg": 52, + "armor": 40, + "level": 19, + "max_exp": 132, + "max_gold": 44, + "immune": 0 + }, + { + "id": 55, + "name": "Dawk", + "max_hp": 60, + "max_dmg": 54, + "armor": 40, + "level": 19, + "max_exp": 136, + "max_gold": 45, + "immune": 0 + }, + { + "id": 56, + "name": "Figment", + "max_hp": 64, + "max_dmg": 55, + "armor": 42, + "level": 19, + "max_exp": 140, + "max_gold": 47, + "immune": 1 + }, + { + "id": 57, + "name": "Hellhound", + "max_hp": 66, + "max_dmg": 56, + "armor": 44, + "level": 20, + "max_exp": 140, + "max_gold": 47, + "immune": 0 + }, + { + "id": 58, + "name": "Wizard", + "max_hp": 66, + "max_dmg": 56, + "armor": 44, + "level": 20, + "max_exp": 144, + "max_gold": 48, + "immune": 0 + }, + { + "id": 59, + "name": "Uruk", + "max_hp": 68, + "max_dmg": 58, + "armor": 44, + "level": 20, + "max_exp": 146, + "max_gold": 49, + "immune": 0 + }, + { + "id": 60, + "name": "Siren", + "max_hp": 68, + "max_dmg": 400, + "armor": 800, + "level": 50, + "max_exp": 10000, + "max_gold": 50, + "immune": 2 + }, + { + "id": 61, + "name": "Megawraith", + "max_hp": 70, + "max_dmg": 60, + "armor": 46, + "level": 21, + "max_exp": 155, + "max_gold": 52, + "immune": 0 + }, + { + "id": 62, + "name": "Dawkin", + "max_hp": 70, + "max_dmg": 60, + "armor": 46, + "level": 21, + "max_exp": 155, + "max_gold": 52, + "immune": 0 + }, + { + "id": 63, + "name": "Grey Bear", + "max_hp": 70, + "max_dmg": 62, + "armor": 48, + "level": 21, + "max_exp": 160, + "max_gold": 53, + "immune": 0 + }, + { + "id": 64, + "name": "Haunt", + "max_hp": 72, + "max_dmg": 62, + "armor": 48, + "level": 22, + "max_exp": 160, + "max_gold": 53, + "immune": 0 + }, + { + "id": 65, + "name": "Hellbeast", + "max_hp": 74, + "max_dmg": 64, + "armor": 50, + "level": 22, + "max_exp": 165, + "max_gold": 55, + "immune": 0 + }, + { + "id": 66, + "name": "Fear", + "max_hp": 76, + "max_dmg": 66, + "armor": 52, + "level": 23, + "max_exp": 165, + "max_gold": 55, + "immune": 0 + }, + { + "id": 67, + "name": "Beast", + "max_hp": 76, + "max_dmg": 66, + "armor": 52, + "level": 23, + "max_exp": 170, + "max_gold": 57, + "immune": 0 + }, + { + "id": 68, + "name": "Ogre", + "max_hp": 78, + "max_dmg": 68, + "armor": 54, + "level": 23, + "max_exp": 170, + "max_gold": 57, + "immune": 0 + }, + { + "id": 69, + "name": "Dark Bear", + "max_hp": 80, + "max_dmg": 70, + "armor": 56, + "level": 24, + "max_exp": 175, + "max_gold": 58, + "immune": 1 + }, + { + "id": 70, + "name": "Fire", + "max_hp": 80, + "max_dmg": 72, + "armor": 56, + "level": 24, + "max_exp": 175, + "max_gold": 58, + "immune": 0 + }, + { + "id": 71, + "name": "Polgergeist", + "max_hp": 84, + "max_dmg": 74, + "armor": 58, + "level": 25, + "max_exp": 180, + "max_gold": 60, + "immune": 0 + }, + { + "id": 72, + "name": "Fright", + "max_hp": 86, + "max_dmg": 76, + "armor": 58, + "level": 25, + "max_exp": 180, + "max_gold": 60, + "immune": 0 + }, + { + "id": 73, + "name": "Lycan", + "max_hp": 88, + "max_dmg": 78, + "armor": 60, + "level": 25, + "max_exp": 185, + "max_gold": 62, + "immune": 0 + }, + { + "id": 74, + "name": "Terra Elemental", + "max_hp": 88, + "max_dmg": 80, + "armor": 62, + "level": 25, + "max_exp": 185, + "max_gold": 62, + "immune": 1 + }, + { + "id": 75, + "name": "Necromancer", + "max_hp": 90, + "max_dmg": 80, + "armor": 62, + "level": 26, + "max_exp": 190, + "max_gold": 63, + "immune": 0 + }, + { + "id": 76, + "name": "Ultrawraith", + "max_hp": 90, + "max_dmg": 82, + "armor": 64, + "level": 26, + "max_exp": 190, + "max_gold": 63, + "immune": 0 + }, + { + "id": 77, + "name": "Dawkor", + "max_hp": 92, + "max_dmg": 82, + "armor": 64, + "level": 26, + "max_exp": 195, + "max_gold": 65, + "immune": 0 + }, + { + "id": 78, + "name": "Werebear", + "max_hp": 92, + "max_dmg": 84, + "armor": 65, + "level": 26, + "max_exp": 195, + "max_gold": 65, + "immune": 0 + }, + { + "id": 79, + "name": "Brute", + "max_hp": 94, + "max_dmg": 84, + "armor": 65, + "level": 27, + "max_exp": 200, + "max_gold": 67, + "immune": 0 + }, + { + "id": 80, + "name": "Large Beast", + "max_hp": 96, + "max_dmg": 88, + "armor": 66, + "level": 27, + "max_exp": 200, + "max_gold": 67, + "immune": 0 + }, + { + "id": 81, + "name": "Horror", + "max_hp": 96, + "max_dmg": 88, + "armor": 68, + "level": 27, + "max_exp": 210, + "max_gold": 70, + "immune": 0 + }, + { + "id": 82, + "name": "Flame", + "max_hp": 100, + "max_dmg": 90, + "armor": 70, + "level": 28, + "max_exp": 210, + "max_gold": 70, + "immune": 0 + }, + { + "id": 83, + "name": "Lycanthor", + "max_hp": 100, + "max_dmg": 90, + "armor": 70, + "level": 28, + "max_exp": 210, + "max_gold": 70, + "immune": 0 + }, + { + "id": 84, + "name": "Wyrm", + "max_hp": 100, + "max_dmg": 92, + "armor": 72, + "level": 28, + "max_exp": 220, + "max_gold": 73, + "immune": 0 + }, + { + "id": 85, + "name": "Aero Elemental", + "max_hp": 104, + "max_dmg": 94, + "armor": 74, + "level": 29, + "max_exp": 220, + "max_gold": 73, + "immune": 1 + }, + { + "id": 86, + "name": "Dawkare", + "max_hp": 106, + "max_dmg": 96, + "armor": 76, + "level": 29, + "max_exp": 220, + "max_gold": 73, + "immune": 0 + }, + { + "id": 87, + "name": "Large Brute", + "max_hp": 108, + "max_dmg": 98, + "armor": 78, + "level": 29, + "max_exp": 230, + "max_gold": 77, + "immune": 0 + }, + { + "id": 88, + "name": "Frost Wyrm", + "max_hp": 110, + "max_dmg": 100, + "armor": 80, + "level": 30, + "max_exp": 230, + "max_gold": 77, + "immune": 0 + }, + { + "id": 89, + "name": "Knight", + "max_hp": 110, + "max_dmg": 102, + "armor": 80, + "level": 30, + "max_exp": 240, + "max_gold": 80, + "immune": 0 + }, + { + "id": 90, + "name": "Lycanthra", + "max_hp": 112, + "max_dmg": 104, + "armor": 82, + "level": 30, + "max_exp": 240, + "max_gold": 80, + "immune": 0 + }, + { + "id": 91, + "name": "Terror", + "max_hp": 115, + "max_dmg": 108, + "armor": 84, + "level": 31, + "max_exp": 250, + "max_gold": 83, + "immune": 0 + }, + { + "id": 92, + "name": "Blaze", + "max_hp": 118, + "max_dmg": 108, + "armor": 84, + "level": 31, + "max_exp": 250, + "max_gold": 83, + "immune": 0 + }, + { + "id": 93, + "name": "Aqua Elemental", + "max_hp": 120, + "max_dmg": 110, + "armor": 90, + "level": 31, + "max_exp": 260, + "max_gold": 87, + "immune": 1 + }, + { + "id": 94, + "name": "Fire Wyrm", + "max_hp": 120, + "max_dmg": 110, + "armor": 90, + "level": 32, + "max_exp": 260, + "max_gold": 87, + "immune": 0 + }, + { + "id": 95, + "name": "Lesser Wyvern", + "max_hp": 122, + "max_dmg": 110, + "armor": 92, + "level": 32, + "max_exp": 270, + "max_gold": 90, + "immune": 0 + }, + { + "id": 96, + "name": "Doomer", + "max_hp": 124, + "max_dmg": 112, + "armor": 92, + "level": 32, + "max_exp": 270, + "max_gold": 90, + "immune": 0 + }, + { + "id": 97, + "name": "Armor Knight", + "max_hp": 130, + "max_dmg": 115, + "armor": 95, + "level": 33, + "max_exp": 280, + "max_gold": 93, + "immune": 0 + }, + { + "id": 98, + "name": "Wyvern", + "max_hp": 134, + "max_dmg": 120, + "armor": 95, + "level": 33, + "max_exp": 290, + "max_gold": 97, + "immune": 0 + }, + { + "id": 99, + "name": "Nightmare", + "max_hp": 138, + "max_dmg": 125, + "armor": 100, + "level": 33, + "max_exp": 300, + "max_gold": 100, + "immune": 0 + }, + { + "id": 100, + "name": "Fira Elemental", + "max_hp": 140, + "max_dmg": 125, + "armor": 100, + "level": 34, + "max_exp": 310, + "max_gold": 103, + "immune": 1 + }, + { + "id": 101, + "name": "Megadoomer", + "max_hp": 140, + "max_dmg": 128, + "armor": 105, + "level": 34, + "max_exp": 320, + "max_gold": 107, + "immune": 0 + }, + { + "id": 102, + "name": "Greater Wyvern", + "max_hp": 145, + "max_dmg": 130, + "armor": 105, + "level": 34, + "max_exp": 335, + "max_gold": 112, + "immune": 0 + }, + { + "id": 103, + "name": "Advocate", + "max_hp": 148, + "max_dmg": 132, + "armor": 108, + "level": 35, + "max_exp": 350, + "max_gold": 117, + "immune": 0 + }, + { + "id": 104, + "name": "Strong Knight", + "max_hp": 150, + "max_dmg": 135, + "armor": 110, + "level": 35, + "max_exp": 365, + "max_gold": 122, + "immune": 0 + }, + { + "id": 105, + "name": "Liche", + "max_hp": 150, + "max_dmg": 135, + "armor": 110, + "level": 35, + "max_exp": 380, + "max_gold": 127, + "immune": 0 + }, + { + "id": 106, + "name": "Ultradoomer", + "max_hp": 155, + "max_dmg": 140, + "armor": 115, + "level": 36, + "max_exp": 395, + "max_gold": 132, + "immune": 0 + }, + { + "id": 107, + "name": "Fanatic", + "max_hp": 160, + "max_dmg": 140, + "armor": 115, + "level": 36, + "max_exp": 410, + "max_gold": 137, + "immune": 0 + }, + { + "id": 108, + "name": "Green Dragon", + "max_hp": 160, + "max_dmg": 140, + "armor": 115, + "level": 36, + "max_exp": 425, + "max_gold": 142, + "immune": 0 + }, + { + "id": 109, + "name": "Fiend", + "max_hp": 160, + "max_dmg": 145, + "armor": 120, + "level": 37, + "max_exp": 445, + "max_gold": 148, + "immune": 0 + }, + { + "id": 110, + "name": "Greatest Wyvern", + "max_hp": 162, + "max_dmg": 150, + "armor": 120, + "level": 37, + "max_exp": 465, + "max_gold": 155, + "immune": 0 + }, + { + "id": 111, + "name": "Lesser Devil", + "max_hp": 164, + "max_dmg": 150, + "armor": 120, + "level": 37, + "max_exp": 485, + "max_gold": 162, + "immune": 0 + }, + { + "id": 112, + "name": "Liche Master", + "max_hp": 168, + "max_dmg": 155, + "armor": 125, + "level": 38, + "max_exp": 505, + "max_gold": 168, + "immune": 0 + }, + { + "id": 113, + "name": "Zealot", + "max_hp": 168, + "max_dmg": 155, + "armor": 125, + "level": 38, + "max_exp": 530, + "max_gold": 177, + "immune": 0 + }, + { + "id": 114, + "name": "Serafiend", + "max_hp": 170, + "max_dmg": 155, + "armor": 125, + "level": 38, + "max_exp": 555, + "max_gold": 185, + "immune": 0 + }, + { + "id": 115, + "name": "Pale Knight", + "max_hp": 175, + "max_dmg": 160, + "armor": 130, + "level": 39, + "max_exp": 580, + "max_gold": 193, + "immune": 0 + }, + { + "id": 116, + "name": "Blue Dragon", + "max_hp": 180, + "max_dmg": 160, + "armor": 130, + "level": 39, + "max_exp": 605, + "max_gold": 202, + "immune": 0 + }, + { + "id": 117, + "name": "Obsessive", + "max_hp": 180, + "max_dmg": 160, + "armor": 135, + "level": 40, + "max_exp": 630, + "max_gold": 210, + "immune": 0 + }, + { + "id": 118, + "name": "Devil", + "max_hp": 184, + "max_dmg": 164, + "armor": 135, + "level": 40, + "max_exp": 666, + "max_gold": 222, + "immune": 0 + }, + { + "id": 119, + "name": "Liche Prince", + "max_hp": 190, + "max_dmg": 168, + "armor": 138, + "level": 40, + "max_exp": 660, + "max_gold": 220, + "immune": 0 + }, + { + "id": 120, + "name": "Cherufiend", + "max_hp": 195, + "max_dmg": 170, + "armor": 140, + "level": 41, + "max_exp": 690, + "max_gold": 230, + "immune": 0 + }, + { + "id": 121, + "name": "Red Dragon", + "max_hp": 200, + "max_dmg": 180, + "armor": 145, + "level": 41, + "max_exp": 720, + "max_gold": 240, + "immune": 0 + }, + { + "id": 122, + "name": "Greater Devil", + "max_hp": 200, + "max_dmg": 180, + "armor": 145, + "level": 41, + "max_exp": 750, + "max_gold": 250, + "immune": 0 + }, + { + "id": 123, + "name": "Renegade", + "max_hp": 205, + "max_dmg": 185, + "armor": 150, + "level": 42, + "max_exp": 780, + "max_gold": 260, + "immune": 0 + }, + { + "id": 124, + "name": "Archfiend", + "max_hp": 210, + "max_dmg": 190, + "armor": 150, + "level": 42, + "max_exp": 810, + "max_gold": 270, + "immune": 0 + }, + { + "id": 125, + "name": "Liche Lord", + "max_hp": 210, + "max_dmg": 190, + "armor": 155, + "level": 42, + "max_exp": 850, + "max_gold": 283, + "immune": 0 + }, + { + "id": 126, + "name": "Greatest Devil", + "max_hp": 215, + "max_dmg": 195, + "armor": 160, + "level": 43, + "max_exp": 890, + "max_gold": 297, + "immune": 0 + }, + { + "id": 127, + "name": "Dark Knight", + "max_hp": 220, + "max_dmg": 200, + "armor": 160, + "level": 43, + "max_exp": 930, + "max_gold": 310, + "immune": 0 + }, + { + "id": 128, + "name": "Giant", + "max_hp": 220, + "max_dmg": 200, + "armor": 165, + "level": 43, + "max_exp": 970, + "max_gold": 323, + "immune": 0 + }, + { + "id": 129, + "name": "Shadow Dragon", + "max_hp": 225, + "max_dmg": 200, + "armor": 170, + "level": 44, + "max_exp": 1010, + "max_gold": 337, + "immune": 0 + }, + { + "id": 130, + "name": "Liche King", + "max_hp": 225, + "max_dmg": 205, + "armor": 170, + "level": 44, + "max_exp": 1050, + "max_gold": 350, + "immune": 0 + }, + { + "id": 131, + "name": "Incubus", + "max_hp": 230, + "max_dmg": 205, + "armor": 175, + "level": 44, + "max_exp": 1100, + "max_gold": 367, + "immune": 1 + }, + { + "id": 132, + "name": "Traitor", + "max_hp": 230, + "max_dmg": 205, + "armor": 175, + "level": 45, + "max_exp": 1150, + "max_gold": 383, + "immune": 0 + }, + { + "id": 133, + "name": "Demon", + "max_hp": 240, + "max_dmg": 210, + "armor": 180, + "level": 45, + "max_exp": 1200, + "max_gold": 400, + "immune": 0 + }, + { + "id": 134, + "name": "Dark Dragon", + "max_hp": 245, + "max_dmg": 215, + "armor": 180, + "level": 45, + "max_exp": 1250, + "max_gold": 417, + "immune": 1 + }, + { + "id": 135, + "name": "Insurgent", + "max_hp": 250, + "max_dmg": 220, + "armor": 190, + "level": 46, + "max_exp": 1300, + "max_gold": 433, + "immune": 0 + }, + { + "id": 136, + "name": "Leviathan", + "max_hp": 255, + "max_dmg": 225, + "armor": 190, + "level": 46, + "max_exp": 1350, + "max_gold": 450, + "immune": 0 + }, + { + "id": 137, + "name": "Grey Daemon", + "max_hp": 260, + "max_dmg": 230, + "armor": 190, + "level": 46, + "max_exp": 1400, + "max_gold": 467, + "immune": 0 + }, + { + "id": 138, + "name": "Succubus", + "max_hp": 265, + "max_dmg": 240, + "armor": 200, + "level": 47, + "max_exp": 1460, + "max_gold": 487, + "immune": 1 + }, + { + "id": 139, + "name": "Demon Prince", + "max_hp": 270, + "max_dmg": 240, + "armor": 200, + "level": 47, + "max_exp": 1520, + "max_gold": 507, + "immune": 0 + }, + { + "id": 140, + "name": "Black Dragon", + "max_hp": 275, + "max_dmg": 250, + "armor": 205, + "level": 47, + "max_exp": 1580, + "max_gold": 527, + "immune": 1 + }, + { + "id": 141, + "name": "Nihilist", + "max_hp": 280, + "max_dmg": 250, + "armor": 205, + "level": 47, + "max_exp": 1640, + "max_gold": 547, + "immune": 0 + }, + { + "id": 142, + "name": "Behemoth", + "max_hp": 285, + "max_dmg": 260, + "armor": 210, + "level": 48, + "max_exp": 1700, + "max_gold": 567, + "immune": 0 + }, + { + "id": 143, + "name": "Demagogue", + "max_hp": 290, + "max_dmg": 260, + "armor": 210, + "level": 48, + "max_exp": 1760, + "max_gold": 587, + "immune": 0 + }, + { + "id": 144, + "name": "Demon Lord", + "max_hp": 300, + "max_dmg": 270, + "armor": 220, + "level": 48, + "max_exp": 1820, + "max_gold": 607, + "immune": 0 + }, + { + "id": 145, + "name": "Red Daemon", + "max_hp": 310, + "max_dmg": 280, + "armor": 230, + "level": 48, + "max_exp": 1880, + "max_gold": 627, + "immune": 0 + }, + { + "id": 146, + "name": "Colossus", + "max_hp": 320, + "max_dmg": 300, + "armor": 240, + "level": 49, + "max_exp": 1940, + "max_gold": 647, + "immune": 0 + }, + { + "id": 147, + "name": "Demon King", + "max_hp": 330, + "max_dmg": 300, + "armor": 250, + "level": 49, + "max_exp": 2000, + "max_gold": 667, + "immune": 0 + }, + { + "id": 148, + "name": "Dark Daemon", + "max_hp": 340, + "max_dmg": 320, + "armor": 260, + "level": 49, + "max_exp": 2200, + "max_gold": 733, + "immune": 1 + }, + { + "id": 149, + "name": "Titan", + "max_hp": 360, + "max_dmg": 340, + "armor": 270, + "level": 50, + "max_exp": 2400, + "max_gold": 800, + "immune": 0 + }, + { + "id": 150, + "name": "Black Daemon", + "max_hp": 400, + "max_dmg": 400, + "armor": 280, + "level": 50, + "max_exp": 3000, + "max_gold": 1000, + "immune": 1 + }, + { + "id": 151, + "name": "Lucifuge", + "max_hp": 600, + "max_dmg": 600, + "armor": 400, + "level": 50, + "max_exp": 10000, + "max_gold": 10000, + "immune": 2 + } +] \ No newline at end of file diff --git a/data/spells.json b/data/spells.json new file mode 100644 index 0000000..a4a0e7c --- /dev/null +++ b/data/spells.json @@ -0,0 +1,135 @@ +[ + { + "id": 1, + "name": "Heal", + "mp": 5, + "attribute": 10, + "type": 1 + }, + { + "id": 2, + "name": "Revive", + "mp": 10, + "attribute": 25, + "type": 1 + }, + { + "id": 3, + "name": "Life", + "mp": 25, + "attribute": 50, + "type": 1 + }, + { + "id": 4, + "name": "Breath", + "mp": 50, + "attribute": 100, + "type": 1 + }, + { + "id": 5, + "name": "Gaia", + "mp": 75, + "attribute": 150, + "type": 1 + }, + { + "id": 6, + "name": "Hurt", + "mp": 5, + "attribute": 15, + "type": 2 + }, + { + "id": 7, + "name": "Pain", + "mp": 12, + "attribute": 35, + "type": 2 + }, + { + "id": 8, + "name": "Maim", + "mp": 25, + "attribute": 70, + "type": 2 + }, + { + "id": 9, + "name": "Rend", + "mp": 40, + "attribute": 100, + "type": 2 + }, + { + "id": 10, + "name": "Chaos", + "mp": 50, + "attribute": 130, + "type": 2 + }, + { + "id": 11, + "name": "Sleep", + "mp": 10, + "attribute": 5, + "type": 3 + }, + { + "id": 12, + "name": "Dream", + "mp": 30, + "attribute": 9, + "type": 3 + }, + { + "id": 13, + "name": "Nightmare", + "mp": 60, + "attribute": 13, + "type": 3 + }, + { + "id": 14, + "name": "Craze", + "mp": 10, + "attribute": 10, + "type": 4 + }, + { + "id": 15, + "name": "Rage", + "mp": 20, + "attribute": 25, + "type": 4 + }, + { + "id": 16, + "name": "Fury", + "mp": 30, + "attribute": 50, + "type": 4 + }, + { + "id": 17, + "name": "Ward", + "mp": 10, + "attribute": 10, + "type": 5 + }, + { + "id": 18, + "name": "Fend", + "mp": 20, + "attribute": 25, + "type": 5 + }, + { + "id": 19, + "name": "Barrier", + "mp": 30, + "attribute": 50, + "type": 5 + } +] \ No newline at end of file diff --git a/data/towns.json b/data/towns.json new file mode 100644 index 0000000..957dcf0 --- /dev/null +++ b/data/towns.json @@ -0,0 +1,82 @@ +[ + { + "id": 1, + "name": "Midworld", + "x": 0, + "y": 0, + "inn_cost": 5, + "map_cost": 0, + "tp_cost": 0, + "shop_list": "1,2,3,17,18,19,28,29" + }, + { + "id": 2, + "name": "Roma", + "x": 30, + "y": 30, + "inn_cost": 10, + "map_cost": 25, + "tp_cost": 5, + "shop_list": "2,3,4,18,19,29" + }, + { + "id": 3, + "name": "Bris", + "x": 70, + "y": -70, + "inn_cost": 25, + "map_cost": 50, + "tp_cost": 15, + "shop_list": "2,3,4,5,18,19,20,29.30" + }, + { + "id": 4, + "name": "Kalle", + "x": -100, + "y": 100, + "inn_cost": 40, + "map_cost": 100, + "tp_cost": 30, + "shop_list": "5,6,8,10,12,21,22,23,29,30" + }, + { + "id": 5, + "name": "Narcissa", + "x": -130, + "y": -130, + "inn_cost": 60, + "map_cost": 500, + "tp_cost": 50, + "shop_list": "4,7,9,11,13,21,22,23,29,30,31" + }, + { + "id": 6, + "name": "Hambry", + "x": 170, + "y": 170, + "inn_cost": 90, + "map_cost": 1000, + "tp_cost": 80, + "shop_list": "10,11,12,13,14,23,24,30,31" + }, + { + "id": 7, + "name": "Gilead", + "x": 200, + "y": -200, + "inn_cost": 100, + "map_cost": 3000, + "tp_cost": 110, + "shop_list": "12,13,14,15,24,25,26,32" + }, + { + "id": 8, + "name": "Endworld", + "x": -250, + "y": -250, + "inn_cost": 125, + "map_cost": 9000, + "tp_cost": 160, + "shop_list": "16,27,33" + } +] \ No newline at end of file diff --git a/internal/install/install.go b/internal/install/install.go deleted file mode 100644 index 8d39528..0000000 --- a/internal/install/install.go +++ /dev/null @@ -1,500 +0,0 @@ -// package install is the home of the install command -// -// Its purpose is to set up the intial database structure and data, -// then create a "demo" user to act as the initial admin account. -// -// At the moment, it simply creates a static structure and admin user; -// in the future I'd like to add migrations and prompt for account -// creation. -package install - -import ( - "fmt" - "time" - - "dk/internal/database" - "dk/internal/password" - "dk/internal/users" -) - -const dbPath = "dk.db" - -func Run() error { - fmt.Println("Dragon Knight Installation") - fmt.Println("==========================") - - start := time.Now() - - if err := database.Init("dk.db"); err != nil { - return fmt.Errorf("failed to initialize database: %w", err) - } - defer database.Close() - - if err := createTables(); err != nil { - return fmt.Errorf("failed to create tables: %w", err) - } - - if err := populateData(); err != nil { - return fmt.Errorf("failed to populate data: %w", err) - } - - if err := createDemoUser(); err != nil { - return fmt.Errorf("failed to create demo user: %w", err) - } - - elapsed := time.Since(start) - fmt.Printf("\nDatabase setup complete in %.4f seconds.\n", elapsed.Seconds()) - fmt.Println("Demo user created:") - fmt.Println(" Username: demo") - fmt.Println(" Password: Demo123!") - fmt.Println(" Email: demo@demo.com") - fmt.Println("\nInstallation complete!") - - return nil -} - -func createTables() error { - tables := []struct { - name string - sql string - }{ - {"babble", `CREATE TABLE babble ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - posted INTEGER NOT NULL DEFAULT (unixepoch()), - author TEXT NOT NULL DEFAULT '', - babble TEXT NOT NULL DEFAULT '' - )`}, - {"control", `CREATE TABLE control ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - world_size INTEGER NOT NULL DEFAULT 250, - open INTEGER NOT NULL DEFAULT 1, - admin_email TEXT NOT NULL DEFAULT '', - class_1_name TEXT NOT NULL DEFAULT '', - class_2_name TEXT NOT NULL DEFAULT '', - class_3_name TEXT NOT NULL DEFAULT '' - )`}, - {"drops", `CREATE TABLE drops ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL DEFAULT '', - level INTEGER NOT NULL DEFAULT 0, - type INTEGER NOT NULL DEFAULT 0, - att TEXT NOT NULL DEFAULT '' - )`}, - {"forum", `CREATE TABLE forum ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - posted INTEGER NOT NULL DEFAULT (unixepoch()), - last_post INTEGER NOT NULL DEFAULT (unixepoch()), - author INTEGER NOT NULL, - parent INTEGER NOT NULL DEFAULT 0, - replies INTEGER NOT NULL DEFAULT 0, - title TEXT NOT NULL, - content TEXT NOT NULL - )`}, - {"items", `CREATE TABLE items ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - type INTEGER NOT NULL DEFAULT 0, - name TEXT NOT NULL, - value INTEGER NOT NULL DEFAULT 0, - att INTEGER NOT NULL DEFAULT 0, - special TEXT NOT NULL DEFAULT '' - )`}, - {"monsters", `CREATE TABLE monsters ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - max_hp INTEGER NOT NULL DEFAULT 0, - max_dmg INTEGER NOT NULL DEFAULT 0, - armor INTEGER NOT NULL DEFAULT 0, - level INTEGER NOT NULL DEFAULT 0, - max_exp INTEGER NOT NULL DEFAULT 0, - max_gold INTEGER NOT NULL DEFAULT 0, - immune INTEGER NOT NULL DEFAULT 0 - )`}, - {"news", `CREATE TABLE news ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - author INTEGER NOT NULL, - posted INTEGER NOT NULL DEFAULT (unixepoch()), - content TEXT NOT NULL - )`}, - {"spells", `CREATE TABLE spells ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - mp INTEGER NOT NULL DEFAULT 0, - attribute INTEGER NOT NULL DEFAULT 0, - type INTEGER NOT NULL DEFAULT 0 - )`}, - {"towns", `CREATE TABLE towns ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL, - x INTEGER NOT NULL DEFAULT 0, - y INTEGER NOT NULL DEFAULT 0, - inn_cost INTEGER NOT NULL DEFAULT 0, - map_cost INTEGER NOT NULL DEFAULT 0, - tp_cost INTEGER NOT NULL DEFAULT 0, - shop_list TEXT NOT NULL DEFAULT '' - )`}, - {"users", `CREATE TABLE users ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - username TEXT NOT NULL, - password TEXT NOT NULL, - email TEXT NOT NULL, - verified INTEGER NOT NULL DEFAULT 0, - token TEXT NOT NULL DEFAULT '', - registered INTEGER NOT NULL DEFAULT (unixepoch()), - last_online INTEGER NOT NULL DEFAULT (unixepoch()), - auth INTEGER NOT NULL DEFAULT 0, - x INTEGER NOT NULL DEFAULT 0, - y INTEGER NOT NULL DEFAULT 0, - class_id INTEGER NOT NULL DEFAULT 0, - currently TEXT NOT NULL DEFAULT 'In Town', - fighting INTEGER NOT NULL DEFAULT 0, - monster_id INTEGER NOT NULL DEFAULT 0, - monster_hp INTEGER NOT NULL DEFAULT 0, - monster_sleep INTEGER NOT NULL DEFAULT 0, - monster_immune INTEGER NOT NULL DEFAULT 0, - uber_damage INTEGER NOT NULL DEFAULT 0, - uber_defense INTEGER NOT NULL DEFAULT 0, - hp INTEGER NOT NULL DEFAULT 15, - mp INTEGER NOT NULL DEFAULT 0, - tp INTEGER NOT NULL DEFAULT 10, - max_hp INTEGER NOT NULL DEFAULT 15, - max_mp INTEGER NOT NULL DEFAULT 0, - max_tp INTEGER NOT NULL DEFAULT 10, - level INTEGER NOT NULL DEFAULT 1, - gold INTEGER NOT NULL DEFAULT 100, - exp INTEGER NOT NULL DEFAULT 0, - gold_bonus INTEGER NOT NULL DEFAULT 0, - exp_bonus INTEGER NOT NULL DEFAULT 0, - strength INTEGER NOT NULL DEFAULT 5, - dexterity INTEGER NOT NULL DEFAULT 5, - attack INTEGER NOT NULL DEFAULT 5, - defense INTEGER NOT NULL DEFAULT 5, - weapon_id INTEGER NOT NULL DEFAULT 0, - armor_id INTEGER NOT NULL DEFAULT 0, - shield_id INTEGER NOT NULL DEFAULT 0, - slot_1_id INTEGER NOT NULL DEFAULT 0, - slot_2_id INTEGER NOT NULL DEFAULT 0, - slot_3_id INTEGER NOT NULL DEFAULT 0, - weapon_name TEXT NOT NULL DEFAULT '', - armor_name TEXT NOT NULL DEFAULT '', - shield_name TEXT NOT NULL DEFAULT '', - slot_1_name TEXT NOT NULL DEFAULT '', - slot_2_name TEXT NOT NULL DEFAULT '', - slot_3_name TEXT NOT NULL DEFAULT '', - drop_code INTEGER NOT NULL DEFAULT 0, - spells TEXT NOT NULL DEFAULT '', - towns TEXT NOT NULL DEFAULT '' - )`}, - } - - for _, table := range tables { - if err := database.Exec(table.sql); err != nil { - return fmt.Errorf("failed to create %s table: %w", table.name, err) - } - fmt.Printf("✓ %s table created\n", table.name) - } - - return nil -} - -func populateData() error { - if err := database.Exec("INSERT INTO control VALUES (1, 250, 1, '', 'Mage', 'Warrior', 'Paladin')"); err != nil { - return fmt.Errorf("failed to populate control table: %w", err) - } - fmt.Println("✓ control table populated") - - dropsSQL := `INSERT INTO drops VALUES - (1, 'Life Pebble', 1, 1, 'maxhp,10'), - (2, 'Life Stone', 10, 1, 'maxhp,25'), - (3, 'Life Rock', 25, 1, 'maxhp,50'), - (4, 'Magic Pebble', 1, 1, 'maxmp,10'), - (5, 'Magic Stone', 10, 1, 'maxmp,25'), - (6, 'Magic Rock', 25, 1, 'maxmp,50'), - (7, 'Dragon''s Scale', 10, 1, 'defensepower,25'), - (8, 'Dragon''s Plate', 30, 1, 'defensepower,50'), - (9, 'Dragon''s Claw', 10, 1, 'attackpower,25'), - (10, 'Dragon''s Tooth', 30, 1, 'attackpower,50'), - (11, 'Dragon''s Tear', 35, 1, 'strength,50'), - (12, 'Dragon''s Wing', 35, 1, 'dexterity,50'), - (13, 'Demon''s Sin', 35, 1, 'maxhp,-50,strength,50'), - (14, 'Demon''s Fall', 35, 1, 'maxmp,-50,strength,50'), - (15, 'Demon''s Lie', 45, 1, 'maxhp,-100,strength,100'), - (16, 'Demon''s Hate', 45, 1, 'maxmp,-100,strength,100'), - (17, 'Angel''s Joy', 25, 1, 'maxhp,25,strength,25'), - (18, 'Angel''s Rise', 30, 1, 'maxhp,50,strength,50'), - (19, 'Angel''s Truth', 35, 1, 'maxhp,75,strength,75'), - (20, 'Angel''s Love', 40, 1, 'maxhp,100,strength,100'), - (21, 'Seraph''s Joy', 25, 1, 'maxmp,25,dexterity,25'), - (22, 'Seraph''s Rise', 30, 1, 'maxmp,50,dexterity,50'), - (23, 'Seraph''s Truth', 35, 1, 'maxmp,75,dexterity,75'), - (24, 'Seraph''s Love', 40, 1, 'maxmp,100,dexterity,100'), - (25, 'Ruby', 50, 1, 'maxhp,150'), - (26, 'Pearl', 50, 1, 'maxmp,150'), - (27, 'Emerald', 50, 1, 'strength,150'), - (28, 'Topaz', 50, 1, 'dexterity,150'), - (29, 'Obsidian', 50, 1, 'attackpower,150'), - (30, 'Diamond', 50, 1, 'defensepower,150'), - (31, 'Memory Drop', 5, 1, 'expbonus,10'), - (32, 'Fortune Drop', 5, 1, 'goldbonus,10')` - if err := database.Exec(dropsSQL); err != nil { - return fmt.Errorf("failed to populate drops table: %w", err) - } - fmt.Println("✓ drops table populated") - - itemsSQL := `INSERT INTO items VALUES - (1, 1, 'Stick', 10, 2, ''), - (2, 1, 'Branch', 30, 4, ''), - (3, 1, 'Club', 40, 5, ''), - (4, 1, 'Dagger', 90, 8, ''), - (5, 1, 'Hatchet', 150, 12, ''), - (6, 1, 'Axe', 200, 16, ''), - (7, 1, 'Brand', 300, 25, ''), - (8, 1, 'Poleaxe', 500, 35, ''), - (9, 1, 'Broadsword', 800, 45, ''), - (10, 1, 'Battle Axe', 1200, 50, ''), - (11, 1, 'Claymore', 2000, 60, ''), - (12, 1, 'Dark Axe', 3000, 100, 'expbonus,-5'), - (13, 1, 'Dark Sword', 4500, 125, 'expbonus,-10'), - (14, 1, 'Bright Sword', 6000, 100, 'expbonus,10'), - (15, 1, 'Magic Sword', 10000, 150, 'maxmp,50'), - (16, 1, 'Destiny Blade', 50000, 250, 'strength,50'), - (17, 2, 'Skivvies', 25, 2, 'goldbonus,10'), - (18, 2, 'Clothes', 50, 5, ''), - (19, 2, 'Leather Armor', 75, 10, ''), - (20, 2, 'Hard Leather Armor', 150, 25, ''), - (21, 2, 'Chain Mail', 300, 30, ''), - (22, 2, 'Bronze Plate', 900, 50, ''), - (23, 2, 'Iron Plate', 2000, 100, ''), - (24, 2, 'Magic Armor', 4000, 125, 'maxmp,50'), - (25, 2, 'Dark Armor', 5000, 150, 'expbonus,-10'), - (26, 2, 'Bright Armor', 10000, 175, 'expbonus,10'), - (27, 2, 'Destiny Raiment', 50000, 200, 'dexterity,50'), - (28, 3, 'Reed Shield', 50, 2, ''), - (29, 3, 'Buckler', 100, 4, ''), - (30, 3, 'Small Shield', 500, 10, ''), - (31, 3, 'Large Shield', 2500, 30, ''), - (32, 3, 'Silver Shield', 10000, 60, ''), - (33, 3, 'Destiny Aegis', 25000, 100, 'maxhp,50')` - if err := database.Exec(itemsSQL); err != nil { - return fmt.Errorf("failed to populate items table: %w", err) - } - fmt.Println("✓ items table populated") - - monstersSQL := `INSERT INTO monsters VALUES - (1, 'Blue Slime', 4, 3, 1, 1, 1, 1, 0), - (2, 'Red Slime', 6, 5, 1, 1, 2, 1, 0), - (3, 'Critter', 6, 5, 2, 1, 4, 2, 0), - (4, 'Creature', 10, 8, 2, 2, 4, 2, 0), - (5, 'Shadow', 10, 9, 3, 2, 6, 2, 1), - (6, 'Drake', 11, 10, 3, 2, 8, 3, 0), - (7, 'Shade', 12, 10, 3, 3, 10, 3, 1), - (8, 'Drakelor', 14, 12, 4, 3, 10, 3, 0), - (9, 'Silver Slime', 15, 100, 200, 30, 15, 1000, 2), - (10, 'Scamp', 16, 13, 5, 4, 15, 5, 0), - (11, 'Raven', 16, 13, 5, 4, 18, 6, 0), - (12, 'Scorpion', 18, 14, 6, 5, 20, 7, 0), - (13, 'Illusion', 20, 15, 6, 5, 20, 7, 1), - (14, 'Nightshade', 22, 16, 6, 6, 24, 8, 0), - (15, 'Drakemal', 22, 18, 7, 6, 24, 8, 0), - (16, 'Shadow Raven', 24, 18, 7, 6, 26, 9, 1), - (17, 'Ghost', 24, 20, 8, 6, 28, 9, 0), - (18, 'Frost Raven', 26, 20, 8, 7, 30, 10, 0), - (19, 'Rogue Scorpion', 28, 22, 9, 7, 32, 11, 0), - (20, 'Ghoul', 29, 24, 9, 7, 34, 11, 0), - (21, 'Magician', 30, 24, 10, 8, 36, 12, 0), - (22, 'Rogue', 30, 25, 12, 8, 40, 13, 0), - (23, 'Drakefin', 32, 26, 12, 8, 40, 13, 0), - (24, 'Shimmer', 32, 26, 14, 8, 45, 15, 1), - (25, 'Fire Raven', 34, 28, 14, 9, 45, 15, 0), - (26, 'Dybbuk', 34, 28, 14, 9, 50, 17, 0), - (27, 'Knave', 36, 30, 15, 9, 52, 17, 0), - (28, 'Goblin', 36, 30, 15, 10, 54, 18, 0), - (29, 'Skeleton', 38, 30, 18, 10, 58, 19, 0), - (30, 'Dark Slime', 38, 32, 18, 10, 62, 21, 0), - (31, 'Silver Scorpion', 30, 160, 350, 40, 63, 2000, 2), - (32, 'Mirage', 40, 32, 20, 11, 64, 21, 1), - (33, 'Sorceror', 41, 33, 22, 11, 68, 23, 0), - (34, 'Imp', 42, 34, 22, 12, 70, 23, 0), - (35, 'Nymph', 43, 35, 22, 12, 70, 23, 0), - (36, 'Scoundrel', 43, 35, 22, 12, 75, 25, 0), - (37, 'Megaskeleton', 44, 36, 24, 13, 78, 26, 0), - (38, 'Grey Wolf', 44, 36, 24, 13, 82, 27, 0), - (39, 'Phantom', 46, 38, 24, 14, 85, 28, 1), - (40, 'Specter', 46, 38, 24, 14, 90, 30, 0), - (41, 'Dark Scorpion', 48, 40, 26, 15, 95, 32, 1), - (42, 'Warlock', 48, 40, 26, 15, 100, 33, 1), - (43, 'Orc', 49, 42, 28, 15, 104, 35, 0), - (44, 'Sylph', 49, 42, 28, 15, 106, 35, 0), - (45, 'Wraith', 50, 45, 30, 16, 108, 36, 0), - (46, 'Hellion', 50, 45, 30, 16, 110, 37, 0), - (47, 'Bandit', 52, 45, 30, 16, 114, 38, 0), - (48, 'Ultraskeleton', 52, 46, 32, 16, 116, 39, 0), - (49, 'Dark Wolf', 54, 47, 36, 17, 120, 40, 1), - (50, 'Troll', 56, 48, 36, 17, 120, 40, 0), - (51, 'Werewolf', 56, 48, 38, 17, 124, 41, 0), - (52, 'Hellcat', 58, 50, 38, 18, 128, 43, 0), - (53, 'Spirit', 58, 50, 38, 18, 132, 44, 0), - (54, 'Nisse', 60, 52, 40, 19, 132, 44, 0), - (55, 'Dawk', 60, 54, 40, 19, 136, 45, 0), - (56, 'Figment', 64, 55, 42, 19, 140, 47, 1), - (57, 'Hellhound', 66, 56, 44, 20, 140, 47, 0), - (58, 'Wizard', 66, 56, 44, 20, 144, 48, 0), - (59, 'Uruk', 68, 58, 44, 20, 146, 49, 0), - (60, 'Siren', 68, 400, 800, 50, 10000, 50, 2), - (61, 'Megawraith', 70, 60, 46, 21, 155, 52, 0), - (62, 'Dawkin', 70, 60, 46, 21, 155, 52, 0), - (63, 'Grey Bear', 70, 62, 48, 21, 160, 53, 0), - (64, 'Haunt', 72, 62, 48, 22, 160, 53, 0), - (65, 'Hellbeast', 74, 64, 50, 22, 165, 55, 0), - (66, 'Fear', 76, 66, 52, 23, 165, 55, 0), - (67, 'Beast', 76, 66, 52, 23, 170, 57, 0), - (68, 'Ogre', 78, 68, 54, 23, 170, 57, 0), - (69, 'Dark Bear', 80, 70, 56, 24, 175, 58, 1), - (70, 'Fire', 80, 72, 56, 24, 175, 58, 0), - (71, 'Polgergeist', 84, 74, 58, 25, 180, 60, 0), - (72, 'Fright', 86, 76, 58, 25, 180, 60, 0), - (73, 'Lycan', 88, 78, 60, 25, 185, 62, 0), - (74, 'Terra Elemental', 88, 80, 62, 25, 185, 62, 1), - (75, 'Necromancer', 90, 80, 62, 26, 190, 63, 0), - (76, 'Ultrawraith', 90, 82, 64, 26, 190, 63, 0), - (77, 'Dawkor', 92, 82, 64, 26, 195, 65, 0), - (78, 'Werebear', 92, 84, 65, 26, 195, 65, 0), - (79, 'Brute', 94, 84, 65, 27, 200, 67, 0), - (80, 'Large Beast', 96, 88, 66, 27, 200, 67, 0), - (81, 'Horror', 96, 88, 68, 27, 210, 70, 0), - (82, 'Flame', 100, 90, 70, 28, 210, 70, 0), - (83, 'Lycanthor', 100, 90, 70, 28, 210, 70, 0), - (84, 'Wyrm', 100, 92, 72, 28, 220, 73, 0), - (85, 'Aero Elemental', 104, 94, 74, 29, 220, 73, 1), - (86, 'Dawkare', 106, 96, 76, 29, 220, 73, 0), - (87, 'Large Brute', 108, 98, 78, 29, 230, 77, 0), - (88, 'Frost Wyrm', 110, 100, 80, 30, 230, 77, 0), - (89, 'Knight', 110, 102, 80, 30, 240, 80, 0), - (90, 'Lycanthra', 112, 104, 82, 30, 240, 80, 0), - (91, 'Terror', 115, 108, 84, 31, 250, 83, 0), - (92, 'Blaze', 118, 108, 84, 31, 250, 83, 0), - (93, 'Aqua Elemental', 120, 110, 90, 31, 260, 87, 1), - (94, 'Fire Wyrm', 120, 110, 90, 32, 260, 87, 0), - (95, 'Lesser Wyvern', 122, 110, 92, 32, 270, 90, 0), - (96, 'Doomer', 124, 112, 92, 32, 270, 90, 0), - (97, 'Armor Knight', 130, 115, 95, 33, 280, 93, 0), - (98, 'Wyvern', 134, 120, 95, 33, 290, 97, 0), - (99, 'Nightmare', 138, 125, 100, 33, 300, 100, 0), - (100, 'Fira Elemental', 140, 125, 100, 34, 310, 103, 1), - (101, 'Megadoomer', 140, 128, 105, 34, 320, 107, 0), - (102, 'Greater Wyvern', 145, 130, 105, 34, 335, 112, 0), - (103, 'Advocate', 148, 132, 108, 35, 350, 117, 0), - (104, 'Strong Knight', 150, 135, 110, 35, 365, 122, 0), - (105, 'Liche', 150, 135, 110, 35, 380, 127, 0), - (106, 'Ultradoomer', 155, 140, 115, 36, 395, 132, 0), - (107, 'Fanatic', 160, 140, 115, 36, 410, 137, 0), - (108, 'Green Dragon', 160, 140, 115, 36, 425, 142, 0), - (109, 'Fiend', 160, 145, 120, 37, 445, 148, 0), - (110, 'Greatest Wyvern', 162, 150, 120, 37, 465, 155, 0), - (111, 'Lesser Devil', 164, 150, 120, 37, 485, 162, 0), - (112, 'Liche Master', 168, 155, 125, 38, 505, 168, 0), - (113, 'Zealot', 168, 155, 125, 38, 530, 177, 0), - (114, 'Serafiend', 170, 155, 125, 38, 555, 185, 0), - (115, 'Pale Knight', 175, 160, 130, 39, 580, 193, 0), - (116, 'Blue Dragon', 180, 160, 130, 39, 605, 202, 0), - (117, 'Obsessive', 180, 160, 135, 40, 630, 210, 0), - (118, 'Devil', 184, 164, 135, 40, 666, 222, 0), - (119, 'Liche Prince', 190, 168, 138, 40, 660, 220, 0), - (120, 'Cherufiend', 195, 170, 140, 41, 690, 230, 0), - (121, 'Red Dragon', 200, 180, 145, 41, 720, 240, 0), - (122, 'Greater Devil', 200, 180, 145, 41, 750, 250, 0), - (123, 'Renegade', 205, 185, 150, 42, 780, 260, 0), - (124, 'Archfiend', 210, 190, 150, 42, 810, 270, 0), - (125, 'Liche Lord', 210, 190, 155, 42, 850, 283, 0), - (126, 'Greatest Devil', 215, 195, 160, 43, 890, 297, 0), - (127, 'Dark Knight', 220, 200, 160, 43, 930, 310, 0), - (128, 'Giant', 220, 200, 165, 43, 970, 323, 0), - (129, 'Shadow Dragon', 225, 200, 170, 44, 1010, 337, 0), - (130, 'Liche King', 225, 205, 170, 44, 1050, 350, 0), - (131, 'Incubus', 230, 205, 175, 44, 1100, 367, 1), - (132, 'Traitor', 230, 205, 175, 45, 1150, 383, 0), - (133, 'Demon', 240, 210, 180, 45, 1200, 400, 0), - (134, 'Dark Dragon', 245, 215, 180, 45, 1250, 417, 1), - (135, 'Insurgent', 250, 220, 190, 46, 1300, 433, 0), - (136, 'Leviathan', 255, 225, 190, 46, 1350, 450, 0), - (137, 'Grey Daemon', 260, 230, 190, 46, 1400, 467, 0), - (138, 'Succubus', 265, 240, 200, 47, 1460, 487, 1), - (139, 'Demon Prince', 270, 240, 200, 47, 1520, 507, 0), - (140, 'Black Dragon', 275, 250, 205, 47, 1580, 527, 1), - (141, 'Nihilist', 280, 250, 205, 47, 1640, 547, 0), - (142, 'Behemoth', 285, 260, 210, 48, 1700, 567, 0), - (143, 'Demagogue', 290, 260, 210, 48, 1760, 587, 0), - (144, 'Demon Lord', 300, 270, 220, 48, 1820, 607, 0), - (145, 'Red Daemon', 310, 280, 230, 48, 1880, 627, 0), - (146, 'Colossus', 320, 300, 240, 49, 1940, 647, 0), - (147, 'Demon King', 330, 300, 250, 49, 2000, 667, 0), - (148, 'Dark Daemon', 340, 320, 260, 49, 2200, 733, 1), - (149, 'Titan', 360, 340, 270, 50, 2400, 800, 0), - (150, 'Black Daemon', 400, 400, 280, 50, 3000, 1000, 1), - (151, 'Lucifuge', 600, 600, 400, 50, 10000, 10000, 2)` - if err := database.Exec(monstersSQL); err != nil { - return fmt.Errorf("failed to populate monsters table: %w", err) - } - fmt.Println("✓ monsters table populated") - - if err := database.Exec("INSERT INTO news (author, content) VALUES (1, 'Welcome to Dragon Knight! This is your first news post.')"); err != nil { - return fmt.Errorf("failed to populate news table: %w", err) - } - fmt.Println("✓ news table populated") - - spellsSQL := `INSERT INTO spells VALUES - (1, 'Heal', 5, 10, 1), - (2, 'Revive', 10, 25, 1), - (3, 'Life', 25, 50, 1), - (4, 'Breath', 50, 100, 1), - (5, 'Gaia', 75, 150, 1), - (6, 'Hurt', 5, 15, 2), - (7, 'Pain', 12, 35, 2), - (8, 'Maim', 25, 70, 2), - (9, 'Rend', 40, 100, 2), - (10, 'Chaos', 50, 130, 2), - (11, 'Sleep', 10, 5, 3), - (12, 'Dream', 30, 9, 3), - (13, 'Nightmare', 60, 13, 3), - (14, 'Craze', 10, 10, 4), - (15, 'Rage', 20, 25, 4), - (16, 'Fury', 30, 50, 4), - (17, 'Ward', 10, 10, 5), - (18, 'Fend', 20, 25, 5), - (19, 'Barrier', 30, 50, 5)` - if err := database.Exec(spellsSQL); err != nil { - return fmt.Errorf("failed to populate spells table: %w", err) - } - fmt.Println("✓ spells table populated") - - townsSQL := `INSERT INTO towns VALUES - (1, 'Midworld', 0, 0, 5, 0, 0, '1,2,3,17,18,19,28,29'), - (2, 'Roma', 30, 30, 10, 25, 5, '2,3,4,18,19,29'), - (3, 'Bris', 70, -70, 25, 50, 15, '2,3,4,5,18,19,20,29.30'), - (4, 'Kalle', -100, 100, 40, 100, 30, '5,6,8,10,12,21,22,23,29,30'), - (5, 'Narcissa', -130, -130, 60, 500, 50, '4,7,9,11,13,21,22,23,29,30,31'), - (6, 'Hambry', 170, 170, 90, 1000, 80, '10,11,12,13,14,23,24,30,31'), - (7, 'Gilead', 200, -200, 100, 3000, 110, '12,13,14,15,24,25,26,32'), - (8, 'Endworld', -250, -250, 125, 9000, 160, '16,27,33')` - if err := database.Exec(townsSQL); err != nil { - return fmt.Errorf("failed to populate towns table: %w", err) - } - fmt.Println("✓ towns table populated") - - return nil -} - -func createDemoUser() error { - user := users.New() - user.Username = "demo" - user.Email = "demo@demo.com" - user.Password = password.Hash("Demo123!") - user.ClassID = 1 - user.Auth = 4 - - if err := user.Insert(); err != nil { - return fmt.Errorf("failed to create demo user: %w", err) - } - - fmt.Println("✓ Demo user created") - return nil -} diff --git a/internal/monsters/monsters.go b/internal/monsters/monsters.go index c4a5aa5..ec72301 100644 --- a/internal/monsters/monsters.go +++ b/internal/monsters/monsters.go @@ -1,51 +1,37 @@ package monsters import ( + "encoding/json" "fmt" - - "dk/internal/database" - "dk/internal/helpers/scanner" - - "zombiezen.com/go/sqlite" + "os" + "path/filepath" + "sort" + "sync" ) -// Monster represents a monster in the database +// Monster represents a monster in the game type Monster struct { - database.BaseModel - - ID int `db:"id" json:"id"` - Name string `db:"name" json:"name"` - MaxHP int `db:"max_hp" json:"max_hp"` - MaxDmg int `db:"max_dmg" json:"max_dmg"` - Armor int `db:"armor" json:"armor"` - Level int `db:"level" json:"level"` - MaxExp int `db:"max_exp" json:"max_exp"` - MaxGold int `db:"max_gold" json:"max_gold"` - Immune int `db:"immune" json:"immune"` -} - -func (m *Monster) GetTableName() string { - return "monsters" -} - -func (m *Monster) GetID() int { - return m.ID -} - -func (m *Monster) SetID(id int) { - m.ID = id -} - -func (m *Monster) Set(field string, value any) error { - return database.Set(m, field, value) + ID int `json:"id"` + Name string `json:"name"` + MaxHP int `json:"max_hp"` + MaxDmg int `json:"max_dmg"` + Armor int `json:"armor"` + Level int `json:"level"` + MaxExp int `json:"max_exp"` + MaxGold int `json:"max_gold"` + Immune int `json:"immune"` } func (m *Monster) Save() error { - return database.Save(m) + store := GetStore() + store.UpdateMonster(m) + return nil } func (m *Monster) Delete() error { - return database.Delete(m) + store := GetStore() + store.RemoveMonster(m.ID) + return nil } // Creates a new Monster with sensible defaults @@ -62,19 +48,6 @@ func New() *Monster { } } -var monsterScanner = scanner.New[Monster]() - -// Returns the column list for monster queries -func monsterColumns() string { - return monsterScanner.Columns() -} - -// Populates a Monster struct using the fast scanner -func scanMonster(stmt *sqlite.Stmt) *Monster { - monster := &Monster{} - monsterScanner.Scan(stmt, monster) - return monster -} // Immunity constants for monster immunity types const ( @@ -83,109 +56,423 @@ const ( ImmuneSleep = 2 // Immune to Sleep spells ) -// Retrieves a monster by ID -func Find(id int) (*Monster, error) { - var monster *Monster +// MonsterStore provides in-memory storage with O(1) lookups +type MonsterStore struct { + monsters map[int]*Monster // ID -> Monster (O(1)) + byLevel map[int][]*Monster // Level -> []*Monster (O(1) to get slice) + byImmunity map[int][]*Monster // Immunity -> []*Monster (O(1) to get slice) + allByLevel []*Monster // Pre-sorted by level, id + maxID int + mu sync.RWMutex +} - query := `SELECT ` + monsterColumns() + ` FROM monsters WHERE id = ?` +// Global in-memory store +var store *MonsterStore +var storeOnce sync.Once - err := database.Query(query, func(stmt *sqlite.Stmt) error { - monster = scanMonster(stmt) - return nil - }, id) +// Initialize the in-memory store +func initStore() { + store = &MonsterStore{ + monsters: make(map[int]*Monster), + byLevel: make(map[int][]*Monster), + byImmunity: make(map[int][]*Monster), + allByLevel: make([]*Monster, 0), + maxID: 0, + } +} - if err != nil { - return nil, fmt.Errorf("failed to find monster: %w", err) +// GetStore returns the global monster store +func GetStore() *MonsterStore { + storeOnce.Do(initStore) + return store +} + +// AddMonster adds a monster to the in-memory store and updates all indices +func (ms *MonsterStore) AddMonster(monster *Monster) { + ms.mu.Lock() + defer ms.mu.Unlock() + + // Add to primary store + ms.monsters[monster.ID] = monster + + // Update max ID + if monster.ID > ms.maxID { + ms.maxID = monster.ID } - if monster == nil { + // Add to level index + ms.byLevel[monster.Level] = append(ms.byLevel[monster.Level], monster) + + // Add to immunity index + ms.byImmunity[monster.Immune] = append(ms.byImmunity[monster.Immune], monster) + + // Add to sorted list and re-sort + ms.allByLevel = append(ms.allByLevel, monster) + sort.Slice(ms.allByLevel, func(i, j int) bool { + if ms.allByLevel[i].Level == ms.allByLevel[j].Level { + return ms.allByLevel[i].ID < ms.allByLevel[j].ID + } + return ms.allByLevel[i].Level < ms.allByLevel[j].Level + }) + + // Sort level index + sort.Slice(ms.byLevel[monster.Level], func(i, j int) bool { + return ms.byLevel[monster.Level][i].ID < ms.byLevel[monster.Level][j].ID + }) + + // Sort immunity index + sort.Slice(ms.byImmunity[monster.Immune], func(i, j int) bool { + if ms.byImmunity[monster.Immune][i].Level == ms.byImmunity[monster.Immune][j].Level { + return ms.byImmunity[monster.Immune][i].ID < ms.byImmunity[monster.Immune][j].ID + } + return ms.byImmunity[monster.Immune][i].Level < ms.byImmunity[monster.Immune][j].Level + }) +} + +// RemoveMonster removes a monster from the store and updates indices +func (ms *MonsterStore) RemoveMonster(id int) { + ms.mu.Lock() + defer ms.mu.Unlock() + + monster, exists := ms.monsters[id] + if !exists { + return + } + + // Remove from primary store + delete(ms.monsters, id) + + // Remove from level index + levelMonsters := ms.byLevel[monster.Level] + for i, m := range levelMonsters { + if m.ID == id { + ms.byLevel[monster.Level] = append(levelMonsters[:i], levelMonsters[i+1:]...) + break + } + } + + // Remove from immunity index + immunityMonsters := ms.byImmunity[monster.Immune] + for i, m := range immunityMonsters { + if m.ID == id { + ms.byImmunity[monster.Immune] = append(immunityMonsters[:i], immunityMonsters[i+1:]...) + break + } + } + + // Remove from sorted list + for i, m := range ms.allByLevel { + if m.ID == id { + ms.allByLevel = append(ms.allByLevel[:i], ms.allByLevel[i+1:]...) + break + } + } +} + +// UpdateMonster updates a monster and rebuilds indices +func (ms *MonsterStore) UpdateMonster(monster *Monster) { + ms.RemoveMonster(monster.ID) + ms.AddMonster(monster) +} + +// GetNextID returns the next available ID +func (ms *MonsterStore) GetNextID() int { + ms.mu.RLock() + defer ms.mu.RUnlock() + return ms.maxID + 1 +} + +// LoadFromJSON loads monster data from a JSON file +func (ms *MonsterStore) LoadFromJSON(filename string) error { + ms.mu.Lock() + defer ms.mu.Unlock() + + data, err := os.ReadFile(filename) + if err != nil { + if os.IsNotExist(err) { + return nil // File doesn't exist, start with empty store + } + return fmt.Errorf("failed to read monsters JSON: %w", err) + } + + // Handle empty file + if len(data) == 0 { + return nil // Empty file, start with empty store + } + + var monsters []*Monster + if err := json.Unmarshal(data, &monsters); err != nil { + return fmt.Errorf("failed to unmarshal monsters JSON: %w", err) + } + + // Clear existing data + ms.monsters = make(map[int]*Monster) + ms.byLevel = make(map[int][]*Monster) + ms.byImmunity = make(map[int][]*Monster) + ms.allByLevel = make([]*Monster, 0) + ms.maxID = 0 + + // Add all monsters + for _, monster := range monsters { + ms.monsters[monster.ID] = monster + if monster.ID > ms.maxID { + ms.maxID = monster.ID + } + ms.byLevel[monster.Level] = append(ms.byLevel[monster.Level], monster) + ms.byImmunity[monster.Immune] = append(ms.byImmunity[monster.Immune], monster) + ms.allByLevel = append(ms.allByLevel, monster) + } + + // Sort all indices + sort.Slice(ms.allByLevel, func(i, j int) bool { + if ms.allByLevel[i].Level == ms.allByLevel[j].Level { + return ms.allByLevel[i].ID < ms.allByLevel[j].ID + } + return ms.allByLevel[i].Level < ms.allByLevel[j].Level + }) + + for level := range ms.byLevel { + sort.Slice(ms.byLevel[level], func(i, j int) bool { + return ms.byLevel[level][i].ID < ms.byLevel[level][j].ID + }) + } + + for immunity := range ms.byImmunity { + sort.Slice(ms.byImmunity[immunity], func(i, j int) bool { + if ms.byImmunity[immunity][i].Level == ms.byImmunity[immunity][j].Level { + return ms.byImmunity[immunity][i].ID < ms.byImmunity[immunity][j].ID + } + return ms.byImmunity[immunity][i].Level < ms.byImmunity[immunity][j].Level + }) + } + + return nil +} + +// SaveToJSON saves monster data to a JSON file +func (ms *MonsterStore) SaveToJSON(filename string) error { + ms.mu.RLock() + defer ms.mu.RUnlock() + + monsters := make([]*Monster, 0, len(ms.monsters)) + for _, monster := range ms.monsters { + monsters = append(monsters, monster) + } + + // Sort by ID for consistent output + sort.Slice(monsters, func(i, j int) bool { + return monsters[i].ID < monsters[j].ID + }) + + data, err := json.MarshalIndent(monsters, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal monsters to JSON: %w", err) + } + + if err := os.WriteFile(filename, data, 0644); err != nil { + return fmt.Errorf("failed to write monsters JSON: %w", err) + } + + return nil +} + +// findMonstersDataPath finds the monsters.json file relative to the current working directory +func findMonstersDataPath() (string, error) { + // Try current directory first (cwd/data/monsters.json) + if _, err := os.Stat("data/monsters.json"); err == nil { + return "data/monsters.json", nil + } + + // Walk up directories to find the data folder + dir, err := os.Getwd() + if err != nil { + return "", err + } + + for { + dataPath := filepath.Join(dir, "data", "monsters.json") + if _, err := os.Stat(dataPath); err == nil { + return dataPath, nil + } + + parent := filepath.Dir(dir) + if parent == dir { + break // reached root + } + dir = parent + } + + // Default to current directory if not found + return "data/monsters.json", nil +} + +// LoadData loads monster data from JSON file, or initializes with default data +func LoadData() error { + store := GetStore() + + dataPath, err := findMonstersDataPath() + if err != nil { + return fmt.Errorf("failed to find monsters data path: %w", err) + } + + if err := store.LoadFromJSON(dataPath); err != nil { + // If JSON doesn't exist, initialize with default monsters + if os.IsNotExist(err) { + fmt.Println("No existing monster data found, initializing with defaults...") + if err := initializeDefaultMonsters(); err != nil { + return fmt.Errorf("failed to initialize default monsters: %w", err) + } + // Save the default data + if err := SaveData(); err != nil { + return fmt.Errorf("failed to save default monster data: %w", err) + } + fmt.Printf("Initialized %d default monsters\n", len(store.monsters)) + } else { + return fmt.Errorf("failed to load from JSON: %w", err) + } + } else { + fmt.Printf("Loaded %d monsters from JSON\n", len(store.monsters)) + } + + return nil +} + +// initializeDefaultMonsters creates the default monster set +func initializeDefaultMonsters() error { + store := GetStore() + + // Default monsters from the original SQL data + defaultMonsters := []*Monster{ + {ID: 1, Name: "Blue Slime", MaxHP: 4, MaxDmg: 3, Armor: 1, Level: 1, MaxExp: 1, MaxGold: 1, Immune: ImmuneNone}, + {ID: 2, Name: "Red Slime", MaxHP: 6, MaxDmg: 5, Armor: 1, Level: 1, MaxExp: 2, MaxGold: 1, Immune: ImmuneNone}, + {ID: 3, Name: "Critter", MaxHP: 6, MaxDmg: 5, Armor: 2, Level: 1, MaxExp: 4, MaxGold: 2, Immune: ImmuneNone}, + {ID: 4, Name: "Creature", MaxHP: 10, MaxDmg: 8, Armor: 2, Level: 2, MaxExp: 4, MaxGold: 2, Immune: ImmuneNone}, + {ID: 5, Name: "Shadow", MaxHP: 10, MaxDmg: 9, Armor: 3, Level: 2, MaxExp: 6, MaxGold: 2, Immune: ImmuneHurt}, + {ID: 6, Name: "Drake", MaxHP: 11, MaxDmg: 10, Armor: 3, Level: 2, MaxExp: 8, MaxGold: 3, Immune: ImmuneNone}, + {ID: 7, Name: "Shade", MaxHP: 12, MaxDmg: 10, Armor: 3, Level: 3, MaxExp: 10, MaxGold: 3, Immune: ImmuneHurt}, + {ID: 8, Name: "Drakelor", MaxHP: 14, MaxDmg: 12, Armor: 4, Level: 3, MaxExp: 10, MaxGold: 3, Immune: ImmuneNone}, + {ID: 9, Name: "Silver Slime", MaxHP: 15, MaxDmg: 100, Armor: 200, Level: 30, MaxExp: 15, MaxGold: 1000, Immune: ImmuneSleep}, + {ID: 10, Name: "Scamp", MaxHP: 16, MaxDmg: 13, Armor: 5, Level: 4, MaxExp: 15, MaxGold: 5, Immune: ImmuneNone}, + } + + for _, monster := range defaultMonsters { + store.AddMonster(monster) + } + + return nil +} + +// SaveData saves monster data to JSON file +func SaveData() error { + store := GetStore() + + dataPath, err := findMonstersDataPath() + if err != nil { + return fmt.Errorf("failed to find monsters data path: %w", err) + } + + // Ensure data directory exists + dataDir := filepath.Dir(dataPath) + if err := os.MkdirAll(dataDir, 0755); err != nil { + return fmt.Errorf("failed to create data directory: %w", err) + } + + if err := store.SaveToJSON(dataPath); err != nil { + return fmt.Errorf("failed to save monsters to JSON: %w", err) + } + + fmt.Printf("Saved %d monsters to JSON\n", len(store.monsters)) + return nil +} + +// Retrieves a monster by ID - O(1) lookup +func Find(id int) (*Monster, error) { + store := GetStore() + store.mu.RLock() + defer store.mu.RUnlock() + + monster, exists := store.monsters[id] + if !exists { return nil, fmt.Errorf("monster with ID %d not found", id) } return monster, nil } -// Retrieves all monsters +// Retrieves all monsters - O(1) lookup (returns pre-sorted slice) func All() ([]*Monster, error) { - var monsters []*Monster + store := GetStore() + store.mu.RLock() + defer store.mu.RUnlock() - query := `SELECT ` + monsterColumns() + ` FROM monsters ORDER BY level, id` - - err := database.Query(query, func(stmt *sqlite.Stmt) error { - monster := scanMonster(stmt) - monsters = append(monsters, monster) - return nil - }) - - if err != nil { - return nil, fmt.Errorf("failed to retrieve all monsters: %w", err) - } - - return monsters, nil + // Return a copy of the slice to prevent external modifications + result := make([]*Monster, len(store.allByLevel)) + copy(result, store.allByLevel) + return result, nil } -// Retrieves monsters by level +// Retrieves monsters by level - O(1) lookup func ByLevel(level int) ([]*Monster, error) { - var monsters []*Monster + store := GetStore() + store.mu.RLock() + defer store.mu.RUnlock() - query := `SELECT ` + monsterColumns() + ` FROM monsters WHERE level = ? ORDER BY id` - - err := database.Query(query, func(stmt *sqlite.Stmt) error { - monster := scanMonster(stmt) - monsters = append(monsters, monster) - return nil - }, level) - - if err != nil { - return nil, fmt.Errorf("failed to retrieve monsters by level: %w", err) + monsters, exists := store.byLevel[level] + if !exists { + return []*Monster{}, nil } - return monsters, nil + // Return a copy of the slice to prevent external modifications + result := make([]*Monster, len(monsters)) + copy(result, monsters) + return result, nil } -// Retrieves monsters within a level range (inclusive) +// Retrieves monsters within a level range (inclusive) - O(k) where k is result size func ByLevelRange(minLevel, maxLevel int) ([]*Monster, error) { - var monsters []*Monster + store := GetStore() + store.mu.RLock() + defer store.mu.RUnlock() - query := `SELECT ` + monsterColumns() + ` FROM monsters WHERE level BETWEEN ? AND ? ORDER BY level, id` - - err := database.Query(query, func(stmt *sqlite.Stmt) error { - monster := scanMonster(stmt) - monsters = append(monsters, monster) - return nil - }, minLevel, maxLevel) - - if err != nil { - return nil, fmt.Errorf("failed to retrieve monsters by level range: %w", err) + var result []*Monster + for level := minLevel; level <= maxLevel; level++ { + if monsters, exists := store.byLevel[level]; exists { + result = append(result, monsters...) + } } - return monsters, nil + return result, nil } -// Retrieves monsters by immunity type +// Retrieves monsters by immunity type - O(1) lookup func ByImmunity(immunityType int) ([]*Monster, error) { - var monsters []*Monster + store := GetStore() + store.mu.RLock() + defer store.mu.RUnlock() - query := `SELECT ` + monsterColumns() + ` FROM monsters WHERE immune = ? ORDER BY level, id` - - err := database.Query(query, func(stmt *sqlite.Stmt) error { - monster := scanMonster(stmt) - monsters = append(monsters, monster) - return nil - }, immunityType) - - if err != nil { - return nil, fmt.Errorf("failed to retrieve monsters by immunity: %w", err) + monsters, exists := store.byImmunity[immunityType] + if !exists { + return []*Monster{}, nil } - return monsters, nil + // Return a copy of the slice to prevent external modifications + result := make([]*Monster, len(monsters)) + copy(result, monsters) + return result, nil } -// Saves a new monster to the database and sets the ID +// Saves a new monster to the in-memory store and sets the ID func (m *Monster) Insert() error { - columns := `name, max_hp, max_dmg, armor, level, max_exp, max_gold, immune` - values := []any{m.Name, m.MaxHP, m.MaxDmg, m.Armor, m.Level, m.MaxExp, m.MaxGold, m.Immune} - return database.Insert(m, columns, values...) + store := GetStore() + + // Assign new ID if not set + if m.ID == 0 { + m.ID = store.GetNextID() + } + + // Add to store + store.AddMonster(m) + return nil } // Returns true if the monster is immune to Hurt spells diff --git a/internal/server/server.go b/internal/server/server.go index ec5f17e..fd516e0 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -11,6 +11,7 @@ import ( "dk/internal/auth" "dk/internal/database" "dk/internal/middleware" + "dk/internal/monsters" "dk/internal/router" "dk/internal/routes" "dk/internal/template" @@ -32,6 +33,11 @@ func Start(port string) error { } defer database.Close() + // Load monster data into memory + if err := monsters.LoadData(); err != nil { + return fmt.Errorf("failed to load monster data: %w", err) + } + auth.Init("sessions.json") // Initialize auth.Manager r := router.New() @@ -96,6 +102,12 @@ func Start(port string) error { <-c log.Println("Received shutdown signal, shutting down gracefully...") + // Save monster data before shutdown + log.Println("Saving monster data...") + if err := monsters.SaveData(); err != nil { + log.Printf("Error saving monster data: %v", err) + } + // Save sessions before shutdown log.Println("Saving sessions...") if err := auth.Manager.Close(); err != nil { diff --git a/test_load_data.go b/test_load_data.go new file mode 100644 index 0000000..ddc1355 --- /dev/null +++ b/test_load_data.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "dk/internal/monsters" +) + +func main() { + fmt.Println("Testing LoadData() function...") + + err := monsters.LoadData() + if err != nil { + fmt.Printf("LoadData() failed: %v\n", err) + return + } + + // Test that we can find a monster + monster, err := monsters.Find(1) + if err != nil { + fmt.Printf("Find(1) failed: %v\n", err) + return + } + + fmt.Printf("Successfully loaded data! Found monster: %s (Level %d)\n", monster.Name, monster.Level) + + // Test getting all monsters + all, err := monsters.All() + if err != nil { + fmt.Printf("All() failed: %v\n", err) + return + } + + fmt.Printf("Total monsters loaded: %d\n", len(all)) +} \ No newline at end of file