Compare commits
2 Commits
412baeb46d
...
3df8f29a4c
Author | SHA1 | Date | |
---|---|---|---|
3df8f29a4c | |||
09574294a4 |
350
database.sql
Normal file
350
database.sql
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
DROP TABLE IF EXISTS babble;
|
||||||
|
CREATE TABLE babble (
|
||||||
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
`posted` INTEGER NOT NULL DEFAULT (unixepoch()),
|
||||||
|
`author` INTEGER NOT NULL,
|
||||||
|
`babble` TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS drops;
|
||||||
|
CREATE TABLE drops (
|
||||||
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
`name` TEXT NOT NULL,
|
||||||
|
`level` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`type` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`att` TEXT NOT NULL DEFAULT '',
|
||||||
|
);
|
||||||
|
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');
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS 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 DEFAULT '',
|
||||||
|
`content` TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS items;
|
||||||
|
CREATE TABLE items (
|
||||||
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
`type` INTEGER NOT NULL DEFAULT 1,
|
||||||
|
`name` TEXT NOT NULL,
|
||||||
|
`value` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`att` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`special` TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
|
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');
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS classes;
|
||||||
|
CREATE TABLE classes (
|
||||||
|
'id' INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
'name' TEXT NOT NULL,
|
||||||
|
'lore' TEXT NOT NULL,
|
||||||
|
'exp_rate' INTEGER NOT NULL DEFAULT 3,
|
||||||
|
'base_hp' INTEGER NOT NULL DEFAULT 15,
|
||||||
|
'base_mp' INTEGER NOT NULL DEFAULT 10,
|
||||||
|
'base_str' INTEGER NOT NULL DEFAULT 1,
|
||||||
|
'base_dex' INTEGER NOT NULL DEFAULT 1,
|
||||||
|
'hp_rate' INTEGER NOT NULL DEFAULT 2,
|
||||||
|
'mp_rate' INTEGER NOT NULL DEFAULT 2,
|
||||||
|
'str_rate' INTEGER NOT NULL DEFAULT 2,
|
||||||
|
'dex_rate' INTEGER NOT NULL DEFAULT 2,
|
||||||
|
);
|
||||||
|
INSERT INTO classes VALUES
|
||||||
|
(1, 'Adventurer', '', 3, 15, 10, 4, 4, 2, 2, 2, 2),
|
||||||
|
(2, 'Mage', '', 1, 10, 15, 1, 7, 1, 3, 1, 2),
|
||||||
|
(3, 'Warrior', '', 2, 20, 5, 7, 1, 3, 1, 3, 1),
|
||||||
|
(4, 'Paladin', '', 5, 15, 15, 5, 5, 2, 2, 2, 2);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS monsters;
|
||||||
|
CREATE TABLE monsters (
|
||||||
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
`name` TEXT NOT NULL,
|
||||||
|
`max_hp` INTEGER NOT NULL DEFAULT 1,
|
||||||
|
`max_dmg` INTEGER NOT NULL DEFAULT 1,
|
||||||
|
`armor` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`level` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`max_exp` INTEGER NOT NULL DEFAULT 1,
|
||||||
|
`max_gold` INTEGER NOT NULL DEFAULT 1,
|
||||||
|
`immune` INTEGER NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
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);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS news;
|
||||||
|
CREATE TABLE news (
|
||||||
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
`author` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`posted` INTEGER NOT NULL DEFAULT (unixepoch()),
|
||||||
|
`content` TEXT NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO news (content) VALUES ('This is the first news post. Please use the admin control panel to add another one and make this one go away.');
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS 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
|
||||||
|
);
|
||||||
|
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);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS 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
|
||||||
|
);
|
||||||
|
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');
|
@ -1,16 +1,16 @@
|
|||||||
package control
|
package control
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
nigiri "git.sharkk.net/Sharkk/Nigiri"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
store *nigiri.BaseStore[Control]
|
global *Control
|
||||||
global *Control
|
mu sync.RWMutex
|
||||||
mu sync.RWMutex
|
filename string
|
||||||
)
|
)
|
||||||
|
|
||||||
// Control represents the game control settings
|
// Control represents the game control settings
|
||||||
@ -24,39 +24,72 @@ type Control struct {
|
|||||||
Class3Name string `json:"class_3_name"`
|
Class3Name string `json:"class_3_name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init sets up the Nigiri store for control settings
|
// Init loads control settings from the specified JSON file
|
||||||
func Init(collection *nigiri.Collection) {
|
func Init(jsonFile string) error {
|
||||||
store = nigiri.NewBaseStore[Control]()
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
// Load or create the singleton control instance
|
filename = jsonFile
|
||||||
all := store.GetAll()
|
|
||||||
if len(all) == 0 {
|
// Try to load from file
|
||||||
// Create default control settings
|
if data, err := os.ReadFile(filename); err == nil {
|
||||||
global = New()
|
var ctrl Control
|
||||||
global.ID = 1
|
if err := json.Unmarshal(data, &ctrl); err != nil {
|
||||||
store.Add(1, global)
|
return fmt.Errorf("failed to parse JSON: %w", err)
|
||||||
} else {
|
|
||||||
// Use the first (and only) control entry
|
|
||||||
for _, ctrl := range all {
|
|
||||||
global = ctrl
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply defaults for any missing fields
|
// Apply defaults for any missing fields
|
||||||
defaults := New()
|
defaults := New()
|
||||||
if global.WorldSize == 0 {
|
if ctrl.WorldSize == 0 {
|
||||||
global.WorldSize = defaults.WorldSize
|
ctrl.WorldSize = defaults.WorldSize
|
||||||
}
|
}
|
||||||
if global.Class1Name == "" {
|
if ctrl.Class1Name == "" {
|
||||||
global.Class1Name = defaults.Class1Name
|
ctrl.Class1Name = defaults.Class1Name
|
||||||
}
|
}
|
||||||
if global.Class2Name == "" {
|
if ctrl.Class2Name == "" {
|
||||||
global.Class2Name = defaults.Class2Name
|
ctrl.Class2Name = defaults.Class2Name
|
||||||
}
|
}
|
||||||
if global.Class3Name == "" {
|
if ctrl.Class3Name == "" {
|
||||||
global.Class3Name = defaults.Class3Name
|
ctrl.Class3Name = defaults.Class3Name
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl.ID = 1 // Ensure singleton ID
|
||||||
|
global = &ctrl
|
||||||
|
} else {
|
||||||
|
// Create default control settings if file doesn't exist
|
||||||
|
global = New()
|
||||||
|
if err := save(); err != nil {
|
||||||
|
return fmt.Errorf("failed to create default config file: %w", err)
|
||||||
}
|
}
|
||||||
store.Update(global.ID, global)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return global.Validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// save writes the current control settings to the JSON file (internal use)
|
||||||
|
func save() error {
|
||||||
|
if filename == "" {
|
||||||
|
return fmt.Errorf("no filename set")
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.MarshalIndent(global, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal JSON: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.WriteFile(filename, data, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save writes the current control settings to the JSON file (public)
|
||||||
|
func Save() error {
|
||||||
|
mu.RLock()
|
||||||
|
defer mu.RUnlock()
|
||||||
|
|
||||||
|
if global == nil {
|
||||||
|
return fmt.Errorf("control not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
return save()
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Control with sensible defaults
|
// New creates a new Control with sensible defaults
|
||||||
@ -77,12 +110,12 @@ func Get() *Control {
|
|||||||
mu.RLock()
|
mu.RLock()
|
||||||
defer mu.RUnlock()
|
defer mu.RUnlock()
|
||||||
if global == nil {
|
if global == nil {
|
||||||
panic("control not initialized - call Initialize first")
|
panic("control not initialized - call Init first")
|
||||||
}
|
}
|
||||||
return global
|
return global
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set updates the global control instance (thread-safe)
|
// Set updates the global control instance and saves to file (thread-safe)
|
||||||
func Set(control *Control) error {
|
func Set(control *Control) error {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
@ -92,15 +125,11 @@ func Set(control *Control) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := store.Update(1, control); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
global = control
|
global = control
|
||||||
return nil
|
return save()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update updates specific fields of the control settings
|
// Update updates specific fields of the control settings and saves to file
|
||||||
func Update(updater func(*Control)) error {
|
func Update(updater func(*Control)) error {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
@ -113,7 +142,20 @@ func Update(updater func(*Control)) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := store.Update(1, &updated); err != nil {
|
global = &updated
|
||||||
|
return save()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateNoSave updates specific fields without saving (useful for batch updates)
|
||||||
|
func UpdateNoSave(updater func(*Control)) error {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
// Create a copy to work with
|
||||||
|
updated := *global
|
||||||
|
updater(&updated)
|
||||||
|
|
||||||
|
if err := updated.Validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,14 +276,3 @@ func (c *Control) GetWorldBounds() (minX, minY, maxX, maxY int) {
|
|||||||
radius := c.GetWorldRadius()
|
radius := c.GetWorldRadius()
|
||||||
return -radius, -radius, radius, radius
|
return -radius, -radius, radius, radius
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy compatibility functions (will be removed later)
|
|
||||||
func Load(filename string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Save() error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -2,19 +2,27 @@ package babble
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
nigiri "git.sharkk.net/Sharkk/Nigiri"
|
"dk/internal/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Babble represents a global chat message in the game
|
// Babble represents a global chat message in the game
|
||||||
type Babble struct {
|
type Babble struct {
|
||||||
ID int `json:"id"`
|
ID int
|
||||||
Posted int64 `json:"posted"`
|
Posted int64
|
||||||
Author string `json:"author" db:"index"`
|
Author int
|
||||||
Babble string `json:"babble"`
|
Babble string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Babble with sensible defaults
|
||||||
|
func New() *Babble {
|
||||||
|
return &Babble{
|
||||||
|
Posted: time.Now().Unix(),
|
||||||
|
Author: 0,
|
||||||
|
Babble: "",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks if babble has valid values
|
// Validate checks if babble has valid values
|
||||||
@ -22,8 +30,8 @@ func (b *Babble) Validate() error {
|
|||||||
if b.Posted <= 0 {
|
if b.Posted <= 0 {
|
||||||
return fmt.Errorf("babble Posted timestamp must be positive")
|
return fmt.Errorf("babble Posted timestamp must be positive")
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(b.Author) == "" {
|
if b.Author <= 0 {
|
||||||
return fmt.Errorf("babble Author cannot be empty")
|
return fmt.Errorf("babble Author must be a valid user ID")
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(b.Babble) == "" {
|
if strings.TrimSpace(b.Babble) == "" {
|
||||||
return fmt.Errorf("babble message cannot be empty")
|
return fmt.Errorf("babble message cannot be empty")
|
||||||
@ -31,139 +39,71 @@ func (b *Babble) Validate() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global store with singleton pattern
|
|
||||||
var store *nigiri.BaseStore[Babble]
|
|
||||||
|
|
||||||
// Init sets up the Nigiri store and indices
|
|
||||||
func Init(collection *nigiri.Collection) {
|
|
||||||
store = nigiri.NewBaseStore[Babble]()
|
|
||||||
|
|
||||||
// Register custom indices
|
|
||||||
store.RegisterIndex("byAuthor", nigiri.BuildStringGroupIndex(func(b *Babble) string {
|
|
||||||
return strings.ToLower(b.Author)
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("allByPosted", nigiri.BuildSortedListIndex(func(a, b *Babble) bool {
|
|
||||||
if a.Posted != b.Posted {
|
|
||||||
return a.Posted > b.Posted // DESC
|
|
||||||
}
|
|
||||||
return a.ID > b.ID // DESC
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RebuildIndices()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStore returns the babble store
|
|
||||||
func GetStore() *nigiri.BaseStore[Babble] {
|
|
||||||
if store == nil {
|
|
||||||
panic("babble store not initialized - call Initialize first")
|
|
||||||
}
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new Babble with sensible defaults
|
|
||||||
func New() *Babble {
|
|
||||||
return &Babble{
|
|
||||||
Posted: time.Now().Unix(),
|
|
||||||
Author: "",
|
|
||||||
Babble: "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CRUD operations
|
|
||||||
func (b *Babble) Save() error {
|
|
||||||
if b.ID == 0 {
|
|
||||||
id, err := store.Create(b)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
b.ID = id
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return store.Update(b.ID, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Babble) Delete() error {
|
func (b *Babble) Delete() error {
|
||||||
store.Remove(b.ID)
|
return database.Exec("DELETE FROM babble WHERE id = %d", b.ID)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert with ID assignment
|
|
||||||
func (b *Babble) Insert() error {
|
func (b *Babble) Insert() error {
|
||||||
id, err := store.Create(b)
|
id, err := database.Insert("babble", b, "ID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b.ID = id
|
b.ID = int(id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query functions
|
|
||||||
func Find(id int) (*Babble, error) {
|
func Find(id int) (*Babble, error) {
|
||||||
babble, exists := store.Find(id)
|
var babble Babble
|
||||||
if !exists {
|
err := database.Get(&babble, "SELECT * FROM babble WHERE id = %d", id)
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("babble with ID %d not found", id)
|
return nil, fmt.Errorf("babble with ID %d not found", id)
|
||||||
}
|
}
|
||||||
return babble, nil
|
return &babble, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func All() ([]*Babble, error) {
|
func All() ([]*Babble, error) {
|
||||||
return store.AllSorted("allByPosted"), nil
|
var babbles []*Babble
|
||||||
|
err := database.Select(&babbles, "SELECT * FROM babble ORDER BY posted DESC, id DESC")
|
||||||
|
return babbles, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByAuthor(author string) ([]*Babble, error) {
|
func ByAuthor(authorID int) ([]*Babble, error) {
|
||||||
messages := store.GroupByIndex("byAuthor", strings.ToLower(author))
|
var babbles []*Babble
|
||||||
|
err := database.Select(&babbles, "SELECT * FROM babble WHERE author = %d ORDER BY posted DESC, id DESC", authorID)
|
||||||
// Sort by posted DESC, then ID DESC
|
return babbles, err
|
||||||
sort.Slice(messages, func(i, j int) bool {
|
|
||||||
if messages[i].Posted != messages[j].Posted {
|
|
||||||
return messages[i].Posted > messages[j].Posted // DESC
|
|
||||||
}
|
|
||||||
return messages[i].ID > messages[j].ID // DESC
|
|
||||||
})
|
|
||||||
|
|
||||||
return messages, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Recent(limit int) ([]*Babble, error) {
|
func Recent(limit int) ([]*Babble, error) {
|
||||||
all := store.AllSorted("allByPosted")
|
var babbles []*Babble
|
||||||
if limit > len(all) {
|
err := database.Select(&babbles, "SELECT * FROM babble ORDER BY posted DESC, id DESC LIMIT %d", limit)
|
||||||
limit = len(all)
|
return babbles, err
|
||||||
}
|
|
||||||
return all[:limit], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Since(since int64) ([]*Babble, error) {
|
func Since(since int64) ([]*Babble, error) {
|
||||||
return store.FilterByIndex("allByPosted", func(b *Babble) bool {
|
var babbles []*Babble
|
||||||
return b.Posted >= since
|
err := database.Select(&babbles, "SELECT * FROM babble WHERE posted >= %d ORDER BY posted DESC, id DESC", since)
|
||||||
}), nil
|
return babbles, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Between(start, end int64) ([]*Babble, error) {
|
func Between(start, end int64) ([]*Babble, error) {
|
||||||
return store.FilterByIndex("allByPosted", func(b *Babble) bool {
|
var babbles []*Babble
|
||||||
return b.Posted >= start && b.Posted <= end
|
err := database.Select(&babbles, "SELECT * FROM babble WHERE posted >= %d AND posted <= %d ORDER BY posted DESC, id DESC", start, end)
|
||||||
}), nil
|
return babbles, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Search(term string) ([]*Babble, error) {
|
func Search(term string) ([]*Babble, error) {
|
||||||
lowerTerm := strings.ToLower(term)
|
var babbles []*Babble
|
||||||
return store.FilterByIndex("allByPosted", func(b *Babble) bool {
|
searchTerm := "%" + term + "%"
|
||||||
return strings.Contains(strings.ToLower(b.Babble), lowerTerm)
|
err := database.Select(&babbles, "SELECT * FROM babble WHERE babble LIKE %s ORDER BY posted DESC, id DESC", searchTerm)
|
||||||
}), nil
|
return babbles, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func RecentByAuthor(author string, limit int) ([]*Babble, error) {
|
func RecentByAuthor(authorID int, limit int) ([]*Babble, error) {
|
||||||
messages, err := ByAuthor(author)
|
var babbles []*Babble
|
||||||
if err != nil {
|
err := database.Select(&babbles, "SELECT * FROM babble ORDER BY posted DESC, id DESC LIMIT %d", authorID, limit)
|
||||||
return nil, err
|
return babbles, err
|
||||||
}
|
|
||||||
if limit > len(messages) {
|
|
||||||
limit = len(messages)
|
|
||||||
}
|
|
||||||
return messages[:limit], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods
|
|
||||||
func (b *Babble) PostedTime() time.Time {
|
func (b *Babble) PostedTime() time.Time {
|
||||||
return time.Unix(b.Posted, 0)
|
return time.Unix(b.Posted, 0)
|
||||||
}
|
}
|
||||||
@ -180,8 +120,8 @@ func (b *Babble) Age() time.Duration {
|
|||||||
return time.Since(b.PostedTime())
|
return time.Since(b.PostedTime())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Babble) IsAuthor(username string) bool {
|
func (b *Babble) IsAuthor(userID int) bool {
|
||||||
return strings.EqualFold(b.Author, username)
|
return b.Author == userID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Babble) Preview(maxLength int) string {
|
func (b *Babble) Preview(maxLength int) string {
|
||||||
@ -265,14 +205,3 @@ func (b *Babble) HasMention(username string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy compatibility functions (will be removed later)
|
|
||||||
func LoadData(dataPath string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveData(dataPath string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -3,16 +3,16 @@ package drops
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
nigiri "git.sharkk.net/Sharkk/Nigiri"
|
"dk/internal/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Drop represents a drop item in the game
|
// Drop represents a drop item in the game
|
||||||
type Drop struct {
|
type Drop struct {
|
||||||
ID int `json:"id"`
|
ID int
|
||||||
Name string `json:"name" db:"required"`
|
Name string
|
||||||
Level int `json:"level" db:"index"`
|
Level int
|
||||||
Type int `json:"type" db:"index"`
|
Type int
|
||||||
Att string `json:"att"`
|
Att string
|
||||||
}
|
}
|
||||||
|
|
||||||
// DropType constants for drop types
|
// DropType constants for drop types
|
||||||
@ -20,43 +20,12 @@ const (
|
|||||||
TypeConsumable = 1
|
TypeConsumable = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
// Global store
|
// New creates a new Drop with sensible defaults
|
||||||
var store *nigiri.BaseStore[Drop]
|
|
||||||
|
|
||||||
// Init sets up the Nigiri store and indices
|
|
||||||
func Init(collection *nigiri.Collection) {
|
|
||||||
store = nigiri.NewBaseStore[Drop]()
|
|
||||||
|
|
||||||
// Register custom indices
|
|
||||||
store.RegisterIndex("byLevel", nigiri.BuildIntGroupIndex(func(d *Drop) int {
|
|
||||||
return d.Level
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("byType", nigiri.BuildIntGroupIndex(func(d *Drop) int {
|
|
||||||
return d.Type
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("allByID", nigiri.BuildSortedListIndex(func(a, b *Drop) bool {
|
|
||||||
return a.ID < b.ID
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RebuildIndices()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStore returns the drops store
|
|
||||||
func GetStore() *nigiri.BaseStore[Drop] {
|
|
||||||
if store == nil {
|
|
||||||
panic("drops store not initialized - call Initialize first")
|
|
||||||
}
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new Drop with sensible defaults
|
|
||||||
func New() *Drop {
|
func New() *Drop {
|
||||||
return &Drop{
|
return &Drop{
|
||||||
Name: "",
|
Name: "",
|
||||||
Level: 1, // Default minimum level
|
Level: 1,
|
||||||
Type: TypeConsumable, // Default to consumable
|
Type: TypeConsumable,
|
||||||
Att: "",
|
Att: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,54 +45,45 @@ func (d *Drop) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CRUD operations
|
// CRUD operations
|
||||||
func (d *Drop) Save() error {
|
|
||||||
if d.ID == 0 {
|
|
||||||
id, err := store.Create(d)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.ID = id
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return store.Update(d.ID, d)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Drop) Delete() error {
|
func (d *Drop) Delete() error {
|
||||||
store.Remove(d.ID)
|
return database.Exec("DELETE FROM drops WHERE id = %d", d.ID)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert with ID assignment
|
|
||||||
func (d *Drop) Insert() error {
|
func (d *Drop) Insert() error {
|
||||||
id, err := store.Create(d)
|
id, err := database.Insert("drops", d, "ID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
d.ID = id
|
d.ID = int(id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query functions
|
// Query functions
|
||||||
func Find(id int) (*Drop, error) {
|
func Find(id int) (*Drop, error) {
|
||||||
drop, exists := store.Find(id)
|
var drop Drop
|
||||||
if !exists {
|
err := database.Get(&drop, "SELECT * FROM drops WHERE id = %d", id)
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("drop with ID %d not found", id)
|
return nil, fmt.Errorf("drop with ID %d not found", id)
|
||||||
}
|
}
|
||||||
return drop, nil
|
return &drop, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func All() ([]*Drop, error) {
|
func All() ([]*Drop, error) {
|
||||||
return store.AllSorted("allByID"), nil
|
var drops []*Drop
|
||||||
|
err := database.Select(&drops, "SELECT * FROM drops ORDER BY id ASC")
|
||||||
|
return drops, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByLevel(minLevel int) ([]*Drop, error) {
|
func ByLevel(minLevel int) ([]*Drop, error) {
|
||||||
return store.FilterByIndex("allByID", func(d *Drop) bool {
|
var drops []*Drop
|
||||||
return d.Level <= minLevel
|
err := database.Select(&drops, "SELECT * FROM drops WHERE level <= %d ORDER BY id ASC", minLevel)
|
||||||
}), nil
|
return drops, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByType(dropType int) ([]*Drop, error) {
|
func ByType(dropType int) ([]*Drop, error) {
|
||||||
return store.GroupByIndex("byType", dropType), nil
|
var drops []*Drop
|
||||||
|
err := database.Select(&drops, "SELECT * FROM drops WHERE type = %d ORDER BY id ASC", dropType)
|
||||||
|
return drops, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
@ -139,14 +99,3 @@ func (d *Drop) TypeName() string {
|
|||||||
return "Unknown"
|
return "Unknown"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy compatibility functions (will be removed later)
|
|
||||||
func LoadData(dataPath string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveData(dataPath string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
263
internal/models/fightlogs/fightlogs.go
Normal file
263
internal/models/fightlogs/fightlogs.go
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
package fightlogs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"dk/internal/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FightLog represents a single action in a fight
|
||||||
|
type FightLog struct {
|
||||||
|
ID int
|
||||||
|
FightID int
|
||||||
|
Type int
|
||||||
|
Data int
|
||||||
|
Name string
|
||||||
|
Created int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action type constants
|
||||||
|
const (
|
||||||
|
ActionAttackHit = 1
|
||||||
|
ActionAttackMiss = 2
|
||||||
|
ActionSpellHeal = 3
|
||||||
|
ActionSpellHurt = 4
|
||||||
|
ActionRunSuccess = 5
|
||||||
|
ActionRunFail = 6
|
||||||
|
ActionGeneric = 7
|
||||||
|
ActionMonsterAttack = 8
|
||||||
|
ActionMonsterMiss = 9
|
||||||
|
ActionMonsterSpell = 10
|
||||||
|
ActionMonsterDeath = 11
|
||||||
|
)
|
||||||
|
|
||||||
|
// New creates a new FightLog with sensible defaults
|
||||||
|
func New(fightID int) *FightLog {
|
||||||
|
return &FightLog{
|
||||||
|
FightID: fightID,
|
||||||
|
Type: ActionGeneric,
|
||||||
|
Data: 0,
|
||||||
|
Name: "",
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checks if fight log has valid values
|
||||||
|
func (fl *FightLog) Validate() error {
|
||||||
|
if fl.FightID <= 0 {
|
||||||
|
return fmt.Errorf("fight log FightID must be positive")
|
||||||
|
}
|
||||||
|
if fl.Created <= 0 {
|
||||||
|
return fmt.Errorf("fight log Created timestamp must be positive")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRUD operations
|
||||||
|
func (fl *FightLog) Delete() error {
|
||||||
|
return database.Exec("DELETE FROM fight_logs WHERE id = %d", fl.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fl *FightLog) Insert() error {
|
||||||
|
id, err := database.Insert("fight_logs", fl, "ID")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fl.ID = int(id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query functions
|
||||||
|
func Find(id int) (*FightLog, error) {
|
||||||
|
var log FightLog
|
||||||
|
err := database.Get(&log, "SELECT * FROM fight_logs WHERE id = %d", id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("fight log with ID %d not found", id)
|
||||||
|
}
|
||||||
|
return &log, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ByFightID(fightID int) ([]*FightLog, error) {
|
||||||
|
var logs []*FightLog
|
||||||
|
err := database.Select(&logs, "SELECT * FROM fight_logs WHERE fight_id = %d ORDER BY created ASC, id ASC", fightID)
|
||||||
|
return logs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteByFightID(fightID int) error {
|
||||||
|
return database.Exec("DELETE FROM fight_logs WHERE fight_id = %d", fightID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions for adding different types of actions
|
||||||
|
func AddAction(fightID int, action string) error {
|
||||||
|
log := &FightLog{
|
||||||
|
FightID: fightID,
|
||||||
|
Type: ActionGeneric,
|
||||||
|
Name: action,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
return log.Insert()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddAttackHit(fightID, damage int) error {
|
||||||
|
log := &FightLog{
|
||||||
|
FightID: fightID,
|
||||||
|
Type: ActionAttackHit,
|
||||||
|
Data: damage,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
return log.Insert()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddAttackMiss(fightID int) error {
|
||||||
|
log := &FightLog{
|
||||||
|
FightID: fightID,
|
||||||
|
Type: ActionAttackMiss,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
return log.Insert()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddSpellHeal(fightID int, spellName string, healAmount int) error {
|
||||||
|
log := &FightLog{
|
||||||
|
FightID: fightID,
|
||||||
|
Type: ActionSpellHeal,
|
||||||
|
Data: healAmount,
|
||||||
|
Name: spellName,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
return log.Insert()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddSpellHurt(fightID int, spellName string, damage int) error {
|
||||||
|
log := &FightLog{
|
||||||
|
FightID: fightID,
|
||||||
|
Type: ActionSpellHurt,
|
||||||
|
Data: damage,
|
||||||
|
Name: spellName,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
return log.Insert()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddRunSuccess(fightID int) error {
|
||||||
|
log := &FightLog{
|
||||||
|
FightID: fightID,
|
||||||
|
Type: ActionRunSuccess,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
return log.Insert()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddRunFail(fightID int) error {
|
||||||
|
log := &FightLog{
|
||||||
|
FightID: fightID,
|
||||||
|
Type: ActionRunFail,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
return log.Insert()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddMonsterAttack(fightID int, monsterName string, damage int) error {
|
||||||
|
log := &FightLog{
|
||||||
|
FightID: fightID,
|
||||||
|
Type: ActionMonsterAttack,
|
||||||
|
Data: damage,
|
||||||
|
Name: monsterName,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
return log.Insert()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddMonsterMiss(fightID int, monsterName string) error {
|
||||||
|
log := &FightLog{
|
||||||
|
FightID: fightID,
|
||||||
|
Type: ActionMonsterMiss,
|
||||||
|
Name: monsterName,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
return log.Insert()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddMonsterSpell(fightID int, monsterName, spellName string, damage int) error {
|
||||||
|
log := &FightLog{
|
||||||
|
FightID: fightID,
|
||||||
|
Type: ActionMonsterSpell,
|
||||||
|
Data: damage,
|
||||||
|
Name: monsterName + "|" + spellName,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
return log.Insert()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddMonsterDeath(fightID int, monsterName string) error {
|
||||||
|
log := &FightLog{
|
||||||
|
FightID: fightID,
|
||||||
|
Type: ActionMonsterDeath,
|
||||||
|
Name: monsterName,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
return log.Insert()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert logs to human-readable strings
|
||||||
|
func GetActions(fightID int) ([]string, error) {
|
||||||
|
logs, err := ByFightID(fightID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]string, len(logs))
|
||||||
|
for i, log := range logs {
|
||||||
|
result[i] = log.ToString()
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLastAction(fightID int) (string, error) {
|
||||||
|
var log FightLog
|
||||||
|
err := database.Get(&log, "SELECT * FROM fight_logs WHERE fight_id = %d ORDER BY created DESC, id DESC LIMIT 1", fightID)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil // No logs found
|
||||||
|
}
|
||||||
|
return log.ToString(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper methods
|
||||||
|
func (fl *FightLog) CreatedTime() time.Time {
|
||||||
|
return time.Unix(fl.Created, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fl *FightLog) ToString() string {
|
||||||
|
switch fl.Type {
|
||||||
|
case ActionAttackHit:
|
||||||
|
return fmt.Sprintf("You attacked for %d damage!", fl.Data)
|
||||||
|
case ActionAttackMiss:
|
||||||
|
return "You missed your attack!"
|
||||||
|
case ActionSpellHeal:
|
||||||
|
return fmt.Sprintf("You cast %s and healed %d HP!", fl.Name, fl.Data)
|
||||||
|
case ActionSpellHurt:
|
||||||
|
return fmt.Sprintf("You cast %s and dealt %d damage!", fl.Name, fl.Data)
|
||||||
|
case ActionRunSuccess:
|
||||||
|
return "You successfully ran away!"
|
||||||
|
case ActionRunFail:
|
||||||
|
return "You failed to run away!"
|
||||||
|
case ActionGeneric:
|
||||||
|
return fl.Name
|
||||||
|
case ActionMonsterAttack:
|
||||||
|
return fmt.Sprintf("%s attacks for %d damage!", fl.Name, fl.Data)
|
||||||
|
case ActionMonsterMiss:
|
||||||
|
return fmt.Sprintf("%s missed its attack!", fl.Name)
|
||||||
|
case ActionMonsterSpell:
|
||||||
|
parts := strings.Split(fl.Name, "|")
|
||||||
|
if len(parts) == 2 {
|
||||||
|
return fmt.Sprintf("%s casts %s for %d damage!", parts[0], parts[1], fl.Data)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s casts a spell for %d damage!", fl.Name, fl.Data)
|
||||||
|
case ActionMonsterDeath:
|
||||||
|
return fmt.Sprintf("%s has been defeated!", fl.Name)
|
||||||
|
default:
|
||||||
|
return "Unknown action"
|
||||||
|
}
|
||||||
|
}
|
@ -1,140 +0,0 @@
|
|||||||
package fights
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ActionEntry represents a compacted fight action log. This allows us to store more logs
|
|
||||||
// in the same space as a single string.
|
|
||||||
type ActionEntry struct {
|
|
||||||
Type int `json:"t"`
|
|
||||||
Data int `json:"d,omitempty"`
|
|
||||||
Name string `json:"n,omitempty"` // For spell names
|
|
||||||
}
|
|
||||||
|
|
||||||
// Action type constants
|
|
||||||
const (
|
|
||||||
ActionAttackHit = 1
|
|
||||||
ActionAttackMiss = 2
|
|
||||||
ActionSpellHeal = 3
|
|
||||||
ActionSpellHurt = 4
|
|
||||||
ActionRunSuccess = 5
|
|
||||||
ActionRunFail = 6
|
|
||||||
ActionGeneric = 7
|
|
||||||
ActionMonsterAttack = 8
|
|
||||||
ActionMonsterMiss = 9
|
|
||||||
ActionMonsterSpell = 10
|
|
||||||
ActionMonsterDeath = 11
|
|
||||||
)
|
|
||||||
|
|
||||||
func (f *Fight) AddAction(action string) {
|
|
||||||
f.Actions = append(f.Actions, ActionEntry{Type: ActionGeneric, Name: action})
|
|
||||||
f.Updated = time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fight) AddActionAttackHit(damage int) {
|
|
||||||
f.Actions = append(f.Actions, ActionEntry{Type: ActionAttackHit, Data: damage})
|
|
||||||
f.Updated = time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fight) AddActionAttackMiss() {
|
|
||||||
f.Actions = append(f.Actions, ActionEntry{Type: ActionAttackMiss})
|
|
||||||
f.Updated = time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fight) AddActionSpellHeal(spellName string, healAmount int) {
|
|
||||||
f.Actions = append(f.Actions, ActionEntry{Type: ActionSpellHeal, Data: healAmount, Name: spellName})
|
|
||||||
f.Updated = time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fight) AddActionSpellHurt(spellName string, damage int) {
|
|
||||||
f.Actions = append(f.Actions, ActionEntry{Type: ActionSpellHurt, Data: damage, Name: spellName})
|
|
||||||
f.Updated = time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fight) AddActionRunSuccess() {
|
|
||||||
f.Actions = append(f.Actions, ActionEntry{Type: ActionRunSuccess})
|
|
||||||
f.Updated = time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fight) AddActionRunFail() {
|
|
||||||
f.Actions = append(f.Actions, ActionEntry{Type: ActionRunFail})
|
|
||||||
f.Updated = time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert actions to human-readable strings
|
|
||||||
func (f *Fight) GetActions() []string {
|
|
||||||
result := make([]string, len(f.Actions))
|
|
||||||
for i, action := range f.Actions {
|
|
||||||
result[i] = f.actionToString(action)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fight) GetLastAction() string {
|
|
||||||
if len(f.Actions) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return f.actionToString(f.Actions[len(f.Actions)-1])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fight) ClearActions() {
|
|
||||||
f.Actions = make([]ActionEntry, 0)
|
|
||||||
f.Updated = time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fight) AddActionMonsterAttack(monsterName string, damage int) {
|
|
||||||
f.Actions = append(f.Actions, ActionEntry{Type: ActionMonsterAttack, Data: damage, Name: monsterName})
|
|
||||||
f.Updated = time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fight) AddActionMonsterMiss(monsterName string) {
|
|
||||||
f.Actions = append(f.Actions, ActionEntry{Type: ActionMonsterMiss, Name: monsterName})
|
|
||||||
f.Updated = time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fight) AddActionMonsterSpell(monsterName, spellName string, damage int) {
|
|
||||||
f.Actions = append(f.Actions, ActionEntry{Type: ActionMonsterSpell, Data: damage, Name: monsterName + "|" + spellName})
|
|
||||||
f.Updated = time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fight) AddActionMonsterDeath(monsterName string) {
|
|
||||||
f.Actions = append(f.Actions, ActionEntry{Type: ActionMonsterDeath, Name: monsterName})
|
|
||||||
f.Updated = time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update actionToString method - add these cases
|
|
||||||
func (f *Fight) actionToString(action ActionEntry) string {
|
|
||||||
switch action.Type {
|
|
||||||
case ActionAttackHit:
|
|
||||||
return fmt.Sprintf("You attacked for %d damage!", action.Data)
|
|
||||||
case ActionAttackMiss:
|
|
||||||
return "You missed your attack!"
|
|
||||||
case ActionSpellHeal:
|
|
||||||
return fmt.Sprintf("You cast %s and healed %d HP!", action.Name, action.Data)
|
|
||||||
case ActionSpellHurt:
|
|
||||||
return fmt.Sprintf("You cast %s and dealt %d damage!", action.Name, action.Data)
|
|
||||||
case ActionRunSuccess:
|
|
||||||
return "You successfully ran away!"
|
|
||||||
case ActionRunFail:
|
|
||||||
return "You failed to run away!"
|
|
||||||
case ActionGeneric:
|
|
||||||
return action.Name
|
|
||||||
case ActionMonsterAttack:
|
|
||||||
return fmt.Sprintf("%s attacks for %d damage!", action.Name, action.Data)
|
|
||||||
case ActionMonsterMiss:
|
|
||||||
return fmt.Sprintf("%s missed its attack!", action.Name)
|
|
||||||
case ActionMonsterSpell:
|
|
||||||
parts := strings.Split(action.Name, "|")
|
|
||||||
if len(parts) == 2 {
|
|
||||||
return fmt.Sprintf("%s casts %s for %d damage!", parts[0], parts[1], action.Data)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s casts a spell for %d damage!", action.Name, action.Data)
|
|
||||||
case ActionMonsterDeath:
|
|
||||||
return fmt.Sprintf("%s has been defeated!", action.Name)
|
|
||||||
default:
|
|
||||||
return "Unknown action"
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,80 +4,29 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
nigiri "git.sharkk.net/Sharkk/Nigiri"
|
"dk/internal/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fight represents a fight, past or present
|
// Fight represents a fight, past or present
|
||||||
type Fight struct {
|
type Fight struct {
|
||||||
ID int `json:"id"`
|
ID int
|
||||||
UserID int `json:"user_id" db:"index"`
|
UserID int
|
||||||
MonsterID int `json:"monster_id" db:"index"`
|
MonsterID int
|
||||||
MonsterHP int `json:"monster_hp"`
|
MonsterHP int
|
||||||
MonsterMaxHP int `json:"monster_max_hp"`
|
MonsterMaxHP int
|
||||||
MonsterSleep int `json:"monster_sleep"`
|
MonsterSleep int
|
||||||
MonsterImmune int `json:"monster_immune"`
|
MonsterImmune int
|
||||||
UberDamage int `json:"uber_damage"`
|
UberDamage int
|
||||||
UberDefense int `json:"uber_defense"`
|
UberDefense int
|
||||||
FirstStrike bool `json:"first_strike"`
|
FirstStrike bool
|
||||||
Turn int `json:"turn"`
|
Turn int
|
||||||
RanAway bool `json:"ran_away"`
|
RanAway bool
|
||||||
Victory bool `json:"victory"`
|
Victory bool
|
||||||
Won bool `json:"won"`
|
Won bool
|
||||||
RewardGold int `json:"reward_gold"`
|
RewardGold int
|
||||||
RewardExp int `json:"reward_exp"`
|
RewardExp int
|
||||||
Actions []ActionEntry `json:"actions"`
|
Created int64
|
||||||
Created int64 `json:"created"`
|
Updated int64
|
||||||
Updated int64 `json:"updated"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Global store
|
|
||||||
var store *nigiri.BaseStore[Fight]
|
|
||||||
|
|
||||||
// Init sets up the Nigiri store and indices
|
|
||||||
func Init(collection *nigiri.Collection) {
|
|
||||||
store = nigiri.NewBaseStore[Fight]()
|
|
||||||
|
|
||||||
// Register custom indices
|
|
||||||
store.RegisterIndex("byUserID", nigiri.BuildIntGroupIndex(func(f *Fight) int {
|
|
||||||
return f.UserID
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("byMonsterID", nigiri.BuildIntGroupIndex(func(f *Fight) int {
|
|
||||||
return f.MonsterID
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("activeFights", nigiri.BuildFilteredIntGroupIndex(
|
|
||||||
func(f *Fight) bool {
|
|
||||||
return !f.RanAway && !f.Victory
|
|
||||||
},
|
|
||||||
func(f *Fight) int {
|
|
||||||
return f.UserID
|
|
||||||
},
|
|
||||||
))
|
|
||||||
|
|
||||||
store.RegisterIndex("allByCreated", nigiri.BuildSortedListIndex(func(a, b *Fight) bool {
|
|
||||||
if a.Created != b.Created {
|
|
||||||
return a.Created > b.Created // DESC
|
|
||||||
}
|
|
||||||
return a.ID > b.ID // DESC
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("allByUpdated", nigiri.BuildSortedListIndex(func(a, b *Fight) bool {
|
|
||||||
if a.Updated != b.Updated {
|
|
||||||
return a.Updated > b.Updated // DESC
|
|
||||||
}
|
|
||||||
return a.ID > b.ID // DESC
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RebuildIndices()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStore returns the fights store
|
|
||||||
func GetStore() *nigiri.BaseStore[Fight] {
|
|
||||||
if store == nil {
|
|
||||||
panic("fights store not initialized - call Initialize first")
|
|
||||||
}
|
|
||||||
return store
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Fight with sensible defaults
|
// New creates a new Fight with sensible defaults
|
||||||
@ -99,7 +48,6 @@ func New(userID, monsterID int) *Fight {
|
|||||||
Won: false,
|
Won: false,
|
||||||
RewardGold: 0,
|
RewardGold: 0,
|
||||||
RewardExp: 0,
|
RewardExp: 0,
|
||||||
Actions: make([]ActionEntry, 0),
|
|
||||||
Created: now,
|
Created: now,
|
||||||
Updated: now,
|
Updated: now,
|
||||||
}
|
}
|
||||||
@ -129,75 +77,88 @@ func (f *Fight) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CRUD operations
|
// CRUD operations
|
||||||
func (f *Fight) Save() error {
|
|
||||||
f.Updated = time.Now().Unix()
|
|
||||||
if f.ID == 0 {
|
|
||||||
id, err := store.Create(f)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
f.ID = id
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return store.Update(f.ID, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Fight) Delete() error {
|
func (f *Fight) Delete() error {
|
||||||
store.Remove(f.ID)
|
return database.Exec("DELETE FROM fights WHERE id = %d", f.ID)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert with ID assignment
|
|
||||||
func (f *Fight) Insert() error {
|
func (f *Fight) Insert() error {
|
||||||
f.Updated = time.Now().Unix()
|
f.Updated = time.Now().Unix()
|
||||||
id, err := store.Create(f)
|
id, err := database.Insert("fights", f, "ID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.ID = id
|
f.ID = int(id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Fight) Update() error {
|
||||||
|
f.Updated = time.Now().Unix()
|
||||||
|
return database.Update("fights", map[string]any{
|
||||||
|
"user_id": f.UserID,
|
||||||
|
"monster_id": f.MonsterID,
|
||||||
|
"monster_hp": f.MonsterHP,
|
||||||
|
"monster_max_hp": f.MonsterMaxHP,
|
||||||
|
"monster_sleep": f.MonsterSleep,
|
||||||
|
"monster_immune": f.MonsterImmune,
|
||||||
|
"uber_damage": f.UberDamage,
|
||||||
|
"uber_defense": f.UberDefense,
|
||||||
|
"first_strike": f.FirstStrike,
|
||||||
|
"turn": f.Turn,
|
||||||
|
"ran_away": f.RanAway,
|
||||||
|
"victory": f.Victory,
|
||||||
|
"won": f.Won,
|
||||||
|
"reward_gold": f.RewardGold,
|
||||||
|
"reward_exp": f.RewardExp,
|
||||||
|
"created": f.Created,
|
||||||
|
"updated": f.Updated,
|
||||||
|
}, "id", f.ID)
|
||||||
|
}
|
||||||
|
|
||||||
// Query functions
|
// Query functions
|
||||||
func Find(id int) (*Fight, error) {
|
func Find(id int) (*Fight, error) {
|
||||||
fight, exists := store.Find(id)
|
var fight Fight
|
||||||
if !exists {
|
err := database.Get(&fight, "SELECT * FROM fights WHERE id = %d", id)
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fight with ID %d not found", id)
|
return nil, fmt.Errorf("fight with ID %d not found", id)
|
||||||
}
|
}
|
||||||
return fight, nil
|
return &fight, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func All() ([]*Fight, error) {
|
func All() ([]*Fight, error) {
|
||||||
return store.AllSorted("allByCreated"), nil
|
var fights []*Fight
|
||||||
|
err := database.Select(&fights, "SELECT * FROM fights ORDER BY created DESC, id DESC")
|
||||||
|
return fights, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByUserID(userID int) ([]*Fight, error) {
|
func ByUserID(userID int) ([]*Fight, error) {
|
||||||
return store.GroupByIndex("byUserID", userID), nil
|
var fights []*Fight
|
||||||
|
err := database.Select(&fights, "SELECT * FROM fights WHERE user_id = %d ORDER BY created DESC, id DESC", userID)
|
||||||
|
return fights, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByMonsterID(monsterID int) ([]*Fight, error) {
|
func ByMonsterID(monsterID int) ([]*Fight, error) {
|
||||||
return store.GroupByIndex("byMonsterID", monsterID), nil
|
var fights []*Fight
|
||||||
|
err := database.Select(&fights, "SELECT * FROM fights WHERE monster_id = %d ORDER BY created DESC, id DESC", monsterID)
|
||||||
|
return fights, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ActiveByUserID(userID int) ([]*Fight, error) {
|
func ActiveByUserID(userID int) ([]*Fight, error) {
|
||||||
return store.GroupByIndex("activeFights", userID), nil
|
var fights []*Fight
|
||||||
|
err := database.Select(&fights, "SELECT * FROM fights WHERE user_id = %d AND ran_away = 0 AND victory = 0 ORDER BY created DESC, id DESC", userID)
|
||||||
|
return fights, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Active() ([]*Fight, error) {
|
func Active() ([]*Fight, error) {
|
||||||
result := store.FilterByIndex("allByCreated", func(f *Fight) bool {
|
var fights []*Fight
|
||||||
return !f.RanAway && !f.Victory
|
err := database.Select(&fights, "SELECT * FROM fights WHERE ran_away = 0 AND victory = 0 ORDER BY created DESC, id DESC")
|
||||||
})
|
return fights, err
|
||||||
return result, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Recent(within time.Duration) ([]*Fight, error) {
|
func Recent(within time.Duration) ([]*Fight, error) {
|
||||||
cutoff := time.Now().Add(-within).Unix()
|
cutoff := time.Now().Add(-within).Unix()
|
||||||
|
var fights []*Fight
|
||||||
result := store.FilterByIndex("allByCreated", func(f *Fight) bool {
|
err := database.Select(&fights, "SELECT * FROM fights WHERE created >= %d ORDER BY created DESC, id DESC", cutoff)
|
||||||
return f.Created >= cutoff
|
return fights, err
|
||||||
})
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
|
@ -2,67 +2,32 @@ package forum
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
nigiri "git.sharkk.net/Sharkk/Nigiri"
|
"dk/internal/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Forum represents a forum post or thread in the game
|
// Forum represents a forum post or thread in the game
|
||||||
type Forum struct {
|
type Forum struct {
|
||||||
ID int `json:"id"`
|
ID int
|
||||||
Posted int64 `json:"posted"`
|
Posted int64
|
||||||
LastPost int64 `json:"last_post"`
|
LastPost int64
|
||||||
Author int `json:"author" db:"index"`
|
Author int
|
||||||
Parent int `json:"parent" db:"index"`
|
Parent int
|
||||||
Replies int `json:"replies"`
|
Replies int
|
||||||
Title string `json:"title" db:"required"`
|
Title string
|
||||||
Content string `json:"content" db:"required"`
|
Content string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global store
|
// New creates a new Forum with sensible defaults
|
||||||
var store *nigiri.BaseStore[Forum]
|
|
||||||
|
|
||||||
// Init sets up the Nigiri store and indices
|
|
||||||
func Init(collection *nigiri.Collection) {
|
|
||||||
store = nigiri.NewBaseStore[Forum]()
|
|
||||||
|
|
||||||
// Register custom indices
|
|
||||||
store.RegisterIndex("byParent", nigiri.BuildIntGroupIndex(func(f *Forum) int {
|
|
||||||
return f.Parent
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("byAuthor", nigiri.BuildIntGroupIndex(func(f *Forum) int {
|
|
||||||
return f.Author
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("allByLastPost", nigiri.BuildSortedListIndex(func(a, b *Forum) bool {
|
|
||||||
if a.LastPost != b.LastPost {
|
|
||||||
return a.LastPost > b.LastPost // DESC
|
|
||||||
}
|
|
||||||
return a.ID > b.ID // DESC
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RebuildIndices()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStore returns the forum store
|
|
||||||
func GetStore() *nigiri.BaseStore[Forum] {
|
|
||||||
if store == nil {
|
|
||||||
panic("forum store not initialized - call Initialize first")
|
|
||||||
}
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new Forum with sensible defaults
|
|
||||||
func New() *Forum {
|
func New() *Forum {
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
return &Forum{
|
return &Forum{
|
||||||
Posted: now,
|
Posted: now,
|
||||||
LastPost: now,
|
LastPost: now,
|
||||||
Author: 0,
|
Author: 0,
|
||||||
Parent: 0, // Default to thread (not reply)
|
Parent: 0,
|
||||||
Replies: 0,
|
Replies: 0,
|
||||||
Title: "",
|
Title: "",
|
||||||
Content: "",
|
Content: "",
|
||||||
@ -93,102 +58,77 @@ func (f *Forum) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CRUD operations
|
// CRUD operations
|
||||||
func (f *Forum) Save() error {
|
|
||||||
if f.ID == 0 {
|
|
||||||
id, err := store.Create(f)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
f.ID = id
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return store.Update(f.ID, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Forum) Delete() error {
|
func (f *Forum) Delete() error {
|
||||||
store.Remove(f.ID)
|
return database.Exec("DELETE FROM forum WHERE id = %d", f.ID)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert with ID assignment
|
|
||||||
func (f *Forum) Insert() error {
|
func (f *Forum) Insert() error {
|
||||||
id, err := store.Create(f)
|
id, err := database.Insert("forum", f, "ID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.ID = id
|
f.ID = int(id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query functions
|
// Query functions
|
||||||
func Find(id int) (*Forum, error) {
|
func Find(id int) (*Forum, error) {
|
||||||
forum, exists := store.Find(id)
|
var forum Forum
|
||||||
if !exists {
|
err := database.Get(&forum, "SELECT * FROM forum WHERE id = %d", id)
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("forum post with ID %d not found", id)
|
return nil, fmt.Errorf("forum post with ID %d not found", id)
|
||||||
}
|
}
|
||||||
return forum, nil
|
return &forum, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func All() ([]*Forum, error) {
|
func All() ([]*Forum, error) {
|
||||||
return store.AllSorted("allByLastPost"), nil
|
var forums []*Forum
|
||||||
|
err := database.Select(&forums, "SELECT * FROM forum ORDER BY newpostdate DESC, id DESC")
|
||||||
|
return forums, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Threads() ([]*Forum, error) {
|
func Threads() ([]*Forum, error) {
|
||||||
return store.FilterByIndex("allByLastPost", func(f *Forum) bool {
|
var forums []*Forum
|
||||||
return f.Parent == 0
|
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = 0 ORDER BY newpostdate DESC, id DESC")
|
||||||
}), nil
|
return forums, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByParent(parentID int) ([]*Forum, error) {
|
func ByParent(parentID int) ([]*Forum, error) {
|
||||||
replies := store.GroupByIndex("byParent", parentID)
|
var forums []*Forum
|
||||||
|
if parentID > 0 {
|
||||||
// Sort replies chronologically (posted ASC, then ID ASC)
|
// Replies sorted chronologically
|
||||||
if parentID > 0 && len(replies) > 1 {
|
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = %d ORDER BY postdate ASC, id ASC", parentID)
|
||||||
sort.Slice(replies, func(i, j int) bool {
|
return forums, err
|
||||||
if replies[i].Posted != replies[j].Posted {
|
} else {
|
||||||
return replies[i].Posted < replies[j].Posted // ASC
|
// Threads sorted by last activity
|
||||||
}
|
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = %d ORDER BY newpostdate DESC, id DESC", parentID)
|
||||||
return replies[i].ID < replies[j].ID // ASC
|
return forums, err
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return replies, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByAuthor(authorID int) ([]*Forum, error) {
|
func ByAuthor(authorID int) ([]*Forum, error) {
|
||||||
posts := store.GroupByIndex("byAuthor", authorID)
|
var forums []*Forum
|
||||||
|
err := database.Select(&forums, "SELECT * FROM forum WHERE author = %d ORDER BY postdate DESC, id DESC", authorID)
|
||||||
// Sort by posted DESC, then ID DESC
|
return forums, err
|
||||||
sort.Slice(posts, func(i, j int) bool {
|
|
||||||
if posts[i].Posted != posts[j].Posted {
|
|
||||||
return posts[i].Posted > posts[j].Posted // DESC
|
|
||||||
}
|
|
||||||
return posts[i].ID > posts[j].ID // DESC
|
|
||||||
})
|
|
||||||
|
|
||||||
return posts, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Recent(limit int) ([]*Forum, error) {
|
func Recent(limit int) ([]*Forum, error) {
|
||||||
all := store.AllSorted("allByLastPost")
|
var forums []*Forum
|
||||||
if limit > len(all) {
|
err := database.Select(&forums, "SELECT * FROM forum ORDER BY newpostdate DESC, id DESC LIMIT %d", limit)
|
||||||
limit = len(all)
|
return forums, err
|
||||||
}
|
|
||||||
return all[:limit], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Search(term string) ([]*Forum, error) {
|
func Search(term string) ([]*Forum, error) {
|
||||||
lowerTerm := strings.ToLower(term)
|
var forums []*Forum
|
||||||
return store.FilterByIndex("allByLastPost", func(f *Forum) bool {
|
searchTerm := "%" + term + "%"
|
||||||
return strings.Contains(strings.ToLower(f.Title), lowerTerm) ||
|
err := database.Select(&forums, "SELECT * FROM forum WHERE title LIKE %s OR content LIKE %s ORDER BY newpostdate DESC, id DESC", searchTerm, searchTerm)
|
||||||
strings.Contains(strings.ToLower(f.Content), lowerTerm)
|
return forums, err
|
||||||
}), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Since(since int64) ([]*Forum, error) {
|
func Since(since int64) ([]*Forum, error) {
|
||||||
return store.FilterByIndex("allByLastPost", func(f *Forum) bool {
|
var forums []*Forum
|
||||||
return f.LastPost >= since
|
err := database.Select(&forums, "SELECT * FROM forum WHERE newpostdate >= %d ORDER BY newpostdate DESC, id DESC", since)
|
||||||
}), nil
|
return forums, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
@ -309,14 +249,3 @@ func (f *Forum) GetThread() (*Forum, error) {
|
|||||||
}
|
}
|
||||||
return Find(f.Parent)
|
return Find(f.Parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy compatibility functions (will be removed later)
|
|
||||||
func LoadData(dataPath string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveData(dataPath string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -3,17 +3,17 @@ package items
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
nigiri "git.sharkk.net/Sharkk/Nigiri"
|
"dk/internal/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Item represents an item in the game
|
// Item represents an item in the game
|
||||||
type Item struct {
|
type Item struct {
|
||||||
ID int `json:"id"`
|
ID int
|
||||||
Type int `json:"type" db:"index"`
|
Type int
|
||||||
Name string `json:"name" db:"required"`
|
Name string
|
||||||
Value int `json:"value"`
|
Value int
|
||||||
Att int `json:"att"`
|
Att int
|
||||||
Special string `json:"special"`
|
Special string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ItemType constants for item types
|
// ItemType constants for item types
|
||||||
@ -23,37 +23,10 @@ const (
|
|||||||
TypeShield = 3
|
TypeShield = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
// Global store
|
// New creates a new Item with sensible defaults
|
||||||
var store *nigiri.BaseStore[Item]
|
|
||||||
|
|
||||||
// Init sets up the Nigiri store and indices
|
|
||||||
func Init(collection *nigiri.Collection) {
|
|
||||||
store = nigiri.NewBaseStore[Item]()
|
|
||||||
|
|
||||||
// Register custom indices
|
|
||||||
store.RegisterIndex("byType", nigiri.BuildIntGroupIndex(func(i *Item) int {
|
|
||||||
return i.Type
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("allByID", nigiri.BuildSortedListIndex(func(a, b *Item) bool {
|
|
||||||
return a.ID < b.ID
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RebuildIndices()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStore returns the items store
|
|
||||||
func GetStore() *nigiri.BaseStore[Item] {
|
|
||||||
if store == nil {
|
|
||||||
panic("items store not initialized - call Initialize first")
|
|
||||||
}
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new Item with sensible defaults
|
|
||||||
func New() *Item {
|
func New() *Item {
|
||||||
return &Item{
|
return &Item{
|
||||||
Type: TypeWeapon, // Default to weapon
|
Type: TypeWeapon,
|
||||||
Name: "",
|
Name: "",
|
||||||
Value: 0,
|
Value: 0,
|
||||||
Att: 0,
|
Att: 0,
|
||||||
@ -79,48 +52,39 @@ func (i *Item) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CRUD operations
|
// CRUD operations
|
||||||
func (i *Item) Save() error {
|
|
||||||
if i.ID == 0 {
|
|
||||||
id, err := store.Create(i)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
i.ID = id
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return store.Update(i.ID, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Item) Delete() error {
|
func (i *Item) Delete() error {
|
||||||
store.Remove(i.ID)
|
return database.Exec("DELETE FROM items WHERE id = %d", i.ID)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert with ID assignment
|
|
||||||
func (i *Item) Insert() error {
|
func (i *Item) Insert() error {
|
||||||
id, err := store.Create(i)
|
id, err := database.Insert("items", i, "ID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
i.ID = id
|
i.ID = int(id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query functions
|
// Query functions
|
||||||
func Find(id int) (*Item, error) {
|
func Find(id int) (*Item, error) {
|
||||||
item, exists := store.Find(id)
|
var item Item
|
||||||
if !exists {
|
err := database.Get(&item, "SELECT * FROM items WHERE id = %d", id)
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("item with ID %d not found", id)
|
return nil, fmt.Errorf("item with ID %d not found", id)
|
||||||
}
|
}
|
||||||
return item, nil
|
return &item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func All() ([]*Item, error) {
|
func All() ([]*Item, error) {
|
||||||
return store.AllSorted("allByID"), nil
|
var items []*Item
|
||||||
|
err := database.Select(&items, "SELECT * FROM items ORDER BY id ASC")
|
||||||
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByType(itemType int) ([]*Item, error) {
|
func ByType(itemType int) ([]*Item, error) {
|
||||||
return store.GroupByIndex("byType", itemType), nil
|
var items []*Item
|
||||||
|
err := database.Select(&items, "SELECT * FROM items WHERE type = %d ORDER BY id ASC", itemType)
|
||||||
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
@ -156,14 +120,3 @@ func (i *Item) HasSpecial() bool {
|
|||||||
func (i *Item) IsEquippable() bool {
|
func (i *Item) IsEquippable() bool {
|
||||||
return i.Type == TypeWeapon || i.Type == TypeArmor || i.Type == TypeShield
|
return i.Type == TypeWeapon || i.Type == TypeArmor || i.Type == TypeShield
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy compatibility functions (will be removed later)
|
|
||||||
func LoadData(dataPath string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveData(dataPath string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -3,20 +3,20 @@ package monsters
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
nigiri "git.sharkk.net/Sharkk/Nigiri"
|
"dk/internal/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Monster represents a monster in the game
|
// Monster represents a monster in the game
|
||||||
type Monster struct {
|
type Monster struct {
|
||||||
ID int `json:"id"`
|
ID int
|
||||||
Name string `json:"name" db:"required"`
|
Name string
|
||||||
MaxHP int `json:"max_hp"`
|
MaxHP int
|
||||||
MaxDmg int `json:"max_dmg"`
|
MaxDmg int
|
||||||
Armor int `json:"armor"`
|
Armor int
|
||||||
Level int `json:"level" db:"index"`
|
Level int
|
||||||
MaxExp int `json:"max_exp"`
|
MaxExp int
|
||||||
MaxGold int `json:"max_gold"`
|
MaxGold int
|
||||||
Immune int `json:"immune" db:"index"`
|
Immune int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Immunity constants
|
// Immunity constants
|
||||||
@ -26,41 +26,7 @@ const (
|
|||||||
ImmuneSleep = 2
|
ImmuneSleep = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// Global store
|
// New creates a new Monster with sensible defaults
|
||||||
var store *nigiri.BaseStore[Monster]
|
|
||||||
|
|
||||||
// Init sets up the Nigiri store and indices
|
|
||||||
func Init(collection *nigiri.Collection) {
|
|
||||||
store = nigiri.NewBaseStore[Monster]()
|
|
||||||
|
|
||||||
// Register custom indices
|
|
||||||
store.RegisterIndex("byLevel", nigiri.BuildIntGroupIndex(func(m *Monster) int {
|
|
||||||
return m.Level
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("byImmunity", nigiri.BuildIntGroupIndex(func(m *Monster) int {
|
|
||||||
return m.Immune
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("allByLevel", nigiri.BuildSortedListIndex(func(a, b *Monster) bool {
|
|
||||||
if a.Level == b.Level {
|
|
||||||
return a.ID < b.ID
|
|
||||||
}
|
|
||||||
return a.Level < b.Level
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RebuildIndices()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStore returns the monsters store
|
|
||||||
func GetStore() *nigiri.BaseStore[Monster] {
|
|
||||||
if store == nil {
|
|
||||||
panic("monsters store not initialized - call Initialize first")
|
|
||||||
}
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new Monster with sensible defaults
|
|
||||||
func New() *Monster {
|
func New() *Monster {
|
||||||
return &Monster{
|
return &Monster{
|
||||||
Name: "",
|
Name: "",
|
||||||
@ -92,61 +58,51 @@ func (m *Monster) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CRUD operations
|
// CRUD operations
|
||||||
func (m *Monster) Save() error {
|
|
||||||
if m.ID == 0 {
|
|
||||||
id, err := store.Create(m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m.ID = id
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return store.Update(m.ID, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Monster) Delete() error {
|
func (m *Monster) Delete() error {
|
||||||
store.Remove(m.ID)
|
return database.Exec("DELETE FROM monsters WHERE id = %d", m.ID)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert with ID assignment
|
|
||||||
func (m *Monster) Insert() error {
|
func (m *Monster) Insert() error {
|
||||||
id, err := store.Create(m)
|
id, err := database.Insert("monsters", m, "ID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m.ID = id
|
m.ID = int(id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query functions
|
// Query functions
|
||||||
func Find(id int) (*Monster, error) {
|
func Find(id int) (*Monster, error) {
|
||||||
monster, exists := store.Find(id)
|
var monster Monster
|
||||||
if !exists {
|
err := database.Get(&monster, "SELECT * FROM monsters WHERE id = %d", id)
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("monster with ID %d not found", id)
|
return nil, fmt.Errorf("monster with ID %d not found", id)
|
||||||
}
|
}
|
||||||
return monster, nil
|
return &monster, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func All() ([]*Monster, error) {
|
func All() ([]*Monster, error) {
|
||||||
return store.AllSorted("allByLevel"), nil
|
var monsters []*Monster
|
||||||
|
err := database.Select(&monsters, "SELECT * FROM monsters ORDER BY level ASC, id ASC")
|
||||||
|
return monsters, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByLevel(level int) ([]*Monster, error) {
|
func ByLevel(level int) ([]*Monster, error) {
|
||||||
return store.GroupByIndex("byLevel", level), nil
|
var monsters []*Monster
|
||||||
|
err := database.Select(&monsters, "SELECT * FROM monsters WHERE level = %d ORDER BY id ASC", level)
|
||||||
|
return monsters, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByLevelRange(minLevel, maxLevel int) ([]*Monster, error) {
|
func ByLevelRange(minLevel, maxLevel int) ([]*Monster, error) {
|
||||||
var result []*Monster
|
var monsters []*Monster
|
||||||
for level := minLevel; level <= maxLevel; level++ {
|
err := database.Select(&monsters, "SELECT * FROM monsters WHERE level >= %d AND level <= %d ORDER BY level ASC, id ASC", minLevel, maxLevel)
|
||||||
monsters := store.GroupByIndex("byLevel", level)
|
return monsters, err
|
||||||
result = append(result, monsters...)
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByImmunity(immunityType int) ([]*Monster, error) {
|
func ByImmunity(immunityType int) ([]*Monster, error) {
|
||||||
return store.GroupByIndex("byImmunity", immunityType), nil
|
var monsters []*Monster
|
||||||
|
err := database.Select(&monsters, "SELECT * FROM monsters WHERE immune = %d ORDER BY level ASC, id ASC", immunityType)
|
||||||
|
return monsters, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
@ -195,14 +151,3 @@ func (m *Monster) GoldPerHP() float64 {
|
|||||||
}
|
}
|
||||||
return float64(m.MaxGold) / float64(m.MaxHP)
|
return float64(m.MaxGold) / float64(m.MaxHP)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy compatibility functions (will be removed later)
|
|
||||||
func LoadData(dataPath string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveData(dataPath string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -5,53 +5,23 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
nigiri "git.sharkk.net/Sharkk/Nigiri"
|
"dk/internal/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
// News represents a news post in the game
|
// News represents a news post in the game
|
||||||
type News struct {
|
type News struct {
|
||||||
ID int `json:"id"`
|
ID int
|
||||||
Author int `json:"author" db:"index"`
|
Author int
|
||||||
Posted int64 `json:"posted"`
|
Posted int64
|
||||||
Content string `json:"content" db:"required"`
|
Content string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global store
|
// New creates a new News with sensible defaults
|
||||||
var store *nigiri.BaseStore[News]
|
|
||||||
|
|
||||||
// Init sets up the Nigiri store and indices
|
|
||||||
func Init(collection *nigiri.Collection) {
|
|
||||||
store = nigiri.NewBaseStore[News]()
|
|
||||||
|
|
||||||
// Register custom indices
|
|
||||||
store.RegisterIndex("byAuthor", nigiri.BuildIntGroupIndex(func(n *News) int {
|
|
||||||
return n.Author
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("allByPosted", nigiri.BuildSortedListIndex(func(a, b *News) bool {
|
|
||||||
if a.Posted != b.Posted {
|
|
||||||
return a.Posted > b.Posted // DESC
|
|
||||||
}
|
|
||||||
return a.ID > b.ID // DESC
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RebuildIndices()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStore returns the news store
|
|
||||||
func GetStore() *nigiri.BaseStore[News] {
|
|
||||||
if store == nil {
|
|
||||||
panic("news store not initialized - call Init first")
|
|
||||||
}
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new News with sensible defaults
|
|
||||||
func New() *News {
|
func New() *News {
|
||||||
return &News{
|
return &News{
|
||||||
Author: 0, // No author by default
|
Author: 0,
|
||||||
Posted: time.Now().Unix(), // Current time
|
Posted: time.Now().Unix(),
|
||||||
Content: "", // Empty content
|
Content: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,75 +37,64 @@ func (n *News) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CRUD operations
|
// CRUD operations
|
||||||
func (n *News) Save() error {
|
|
||||||
if n.ID == 0 {
|
|
||||||
id, err := store.Create(n)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
n.ID = id
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return store.Update(n.ID, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *News) Delete() error {
|
func (n *News) Delete() error {
|
||||||
store.Remove(n.ID)
|
return database.Exec("DELETE FROM news WHERE id = %d", n.ID)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert with ID assignment
|
|
||||||
func (n *News) Insert() error {
|
func (n *News) Insert() error {
|
||||||
id, err := store.Create(n)
|
id, err := database.Insert("news", n, "ID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
n.ID = id
|
n.ID = int(id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query functions
|
// Query functions
|
||||||
func Find(id int) (*News, error) {
|
func Find(id int) (*News, error) {
|
||||||
news, exists := store.Find(id)
|
var news News
|
||||||
if !exists {
|
err := database.Get(&news, "SELECT * FROM news WHERE id = %d", id)
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("news with ID %d not found", id)
|
return nil, fmt.Errorf("news with ID %d not found", id)
|
||||||
}
|
}
|
||||||
return news, nil
|
return &news, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func All() ([]*News, error) {
|
func All() ([]*News, error) {
|
||||||
return store.AllSorted("allByPosted"), nil
|
var news []*News
|
||||||
|
err := database.Select(&news, "SELECT * FROM news ORDER BY posted DESC, id DESC")
|
||||||
|
return news, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByAuthor(authorID int) ([]*News, error) {
|
func ByAuthor(authorID int) ([]*News, error) {
|
||||||
return store.GroupByIndex("byAuthor", authorID), nil
|
var news []*News
|
||||||
|
err := database.Select(&news, "SELECT * FROM news WHERE author = %d ORDER BY posted DESC, id DESC", authorID)
|
||||||
|
return news, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Recent(limit int) ([]*News, error) {
|
func Recent(limit int) ([]*News, error) {
|
||||||
all := store.AllSorted("allByPosted")
|
var news []*News
|
||||||
if limit > len(all) {
|
err := database.Select(&news, "SELECT * FROM news ORDER BY posted DESC, id DESC LIMIT %d", limit)
|
||||||
limit = len(all)
|
return news, err
|
||||||
}
|
|
||||||
return all[:limit], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Since(since int64) ([]*News, error) {
|
func Since(since int64) ([]*News, error) {
|
||||||
return store.FilterByIndex("allByPosted", func(n *News) bool {
|
var news []*News
|
||||||
return n.Posted >= since
|
err := database.Select(&news, "SELECT * FROM news WHERE posted >= %d ORDER BY posted DESC, id DESC", since)
|
||||||
}), nil
|
return news, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Between(start, end int64) ([]*News, error) {
|
func Between(start, end int64) ([]*News, error) {
|
||||||
return store.FilterByIndex("allByPosted", func(n *News) bool {
|
var news []*News
|
||||||
return n.Posted >= start && n.Posted <= end
|
err := database.Select(&news, "SELECT * FROM news WHERE posted >= %d AND posted <= %d ORDER BY posted DESC, id DESC", start, end)
|
||||||
}), nil
|
return news, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Search(term string) ([]*News, error) {
|
func Search(term string) ([]*News, error) {
|
||||||
lowerTerm := strings.ToLower(term)
|
var news []*News
|
||||||
return store.FilterByIndex("allByPosted", func(n *News) bool {
|
searchTerm := "%" + term + "%"
|
||||||
return strings.Contains(strings.ToLower(n.Content), lowerTerm)
|
err := database.Select(&news, "SELECT * FROM news WHERE content LIKE %s ORDER BY posted DESC, id DESC", searchTerm)
|
||||||
}), nil
|
return news, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
@ -213,14 +172,3 @@ func (n *News) Contains(term string) bool {
|
|||||||
func (n *News) IsEmpty() bool {
|
func (n *News) IsEmpty() bool {
|
||||||
return strings.TrimSpace(n.Content) == ""
|
return strings.TrimSpace(n.Content) == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy compatibility functions (will be removed later)
|
|
||||||
func LoadData(dataPath string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveData(dataPath string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -2,18 +2,17 @@ package spells
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
nigiri "git.sharkk.net/Sharkk/Nigiri"
|
"dk/internal/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Spell represents a spell in the game
|
// Spell represents a spell in the game
|
||||||
type Spell struct {
|
type Spell struct {
|
||||||
ID int `json:"id"`
|
ID int
|
||||||
Name string `json:"name" db:"required,unique"`
|
Name string
|
||||||
MP int `json:"mp" db:"index"`
|
MP int
|
||||||
Attribute int `json:"attribute"`
|
Attribute int
|
||||||
Type int `json:"type" db:"index"`
|
Type int
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpellType constants for spell types
|
// SpellType constants for spell types
|
||||||
@ -25,54 +24,13 @@ const (
|
|||||||
TypeDefenseBoost = 5
|
TypeDefenseBoost = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
// Global store
|
// New creates a new Spell with sensible defaults
|
||||||
var store *nigiri.BaseStore[Spell]
|
|
||||||
|
|
||||||
// Init sets up the Nigiri store and indices
|
|
||||||
func Init(collection *nigiri.Collection) {
|
|
||||||
store = nigiri.NewBaseStore[Spell]()
|
|
||||||
|
|
||||||
// Register custom indices
|
|
||||||
store.RegisterIndex("byType", nigiri.BuildIntGroupIndex(func(s *Spell) int {
|
|
||||||
return s.Type
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("byName", nigiri.BuildCaseInsensitiveLookupIndex(func(s *Spell) string {
|
|
||||||
return s.Name
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("byMP", nigiri.BuildIntGroupIndex(func(s *Spell) int {
|
|
||||||
return s.MP
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RegisterIndex("allByTypeMP", nigiri.BuildSortedListIndex(func(a, b *Spell) bool {
|
|
||||||
if a.Type != b.Type {
|
|
||||||
return a.Type < b.Type
|
|
||||||
}
|
|
||||||
if a.MP != b.MP {
|
|
||||||
return a.MP < b.MP
|
|
||||||
}
|
|
||||||
return a.ID < b.ID
|
|
||||||
}))
|
|
||||||
|
|
||||||
store.RebuildIndices()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStore returns the spells store
|
|
||||||
func GetStore() *nigiri.BaseStore[Spell] {
|
|
||||||
if store == nil {
|
|
||||||
panic("spells store not initialized - call Initialize first")
|
|
||||||
}
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new Spell with sensible defaults
|
|
||||||
func New() *Spell {
|
func New() *Spell {
|
||||||
return &Spell{
|
return &Spell{
|
||||||
Name: "",
|
Name: "",
|
||||||
MP: 5, // Default MP cost
|
MP: 5,
|
||||||
Attribute: 10, // Default attribute value
|
Attribute: 10,
|
||||||
Type: TypeHealing, // Default to healing spell
|
Type: TypeHealing,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,68 +52,60 @@ func (s *Spell) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CRUD operations
|
// CRUD operations
|
||||||
func (s *Spell) Save() error {
|
|
||||||
if s.ID == 0 {
|
|
||||||
id, err := store.Create(s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.ID = id
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return store.Update(s.ID, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Spell) Delete() error {
|
func (s *Spell) Delete() error {
|
||||||
store.Remove(s.ID)
|
return database.Exec("DELETE FROM spells WHERE id = %d", s.ID)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert with ID assignment
|
|
||||||
func (s *Spell) Insert() error {
|
func (s *Spell) Insert() error {
|
||||||
id, err := store.Create(s)
|
id, err := database.Insert("spells", s, "ID")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.ID = id
|
s.ID = int(id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query functions
|
// Query functions
|
||||||
func Find(id int) (*Spell, error) {
|
func Find(id int) (*Spell, error) {
|
||||||
spell, exists := store.Find(id)
|
var spell Spell
|
||||||
if !exists {
|
err := database.Get(&spell, "SELECT * FROM spells WHERE id = %d", id)
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("spell with ID %d not found", id)
|
return nil, fmt.Errorf("spell with ID %d not found", id)
|
||||||
}
|
}
|
||||||
return spell, nil
|
return &spell, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func All() ([]*Spell, error) {
|
func All() ([]*Spell, error) {
|
||||||
return store.AllSorted("allByTypeMP"), nil
|
var spells []*Spell
|
||||||
|
err := database.Select(&spells, "SELECT * FROM spells ORDER BY type ASC, mp ASC, id ASC")
|
||||||
|
return spells, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByType(spellType int) ([]*Spell, error) {
|
func ByType(spellType int) ([]*Spell, error) {
|
||||||
return store.GroupByIndex("byType", spellType), nil
|
var spells []*Spell
|
||||||
|
err := database.Select(&spells, "SELECT * FROM spells WHERE type = %d ORDER BY mp ASC, id ASC", spellType)
|
||||||
|
return spells, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByMaxMP(maxMP int) ([]*Spell, error) {
|
func ByMaxMP(maxMP int) ([]*Spell, error) {
|
||||||
return store.FilterByIndex("allByTypeMP", func(s *Spell) bool {
|
var spells []*Spell
|
||||||
return s.MP <= maxMP
|
err := database.Select(&spells, "SELECT * FROM spells WHERE mp <= %d ORDER BY type ASC, mp ASC, id ASC", maxMP)
|
||||||
}), nil
|
return spells, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByTypeAndMaxMP(spellType, maxMP int) ([]*Spell, error) {
|
func ByTypeAndMaxMP(spellType, maxMP int) ([]*Spell, error) {
|
||||||
return store.FilterByIndex("allByTypeMP", func(s *Spell) bool {
|
var spells []*Spell
|
||||||
return s.Type == spellType && s.MP <= maxMP
|
err := database.Select(&spells, "SELECT * FROM spells WHERE type = %d AND mp <= %d ORDER BY mp ASC, id ASC", spellType, maxMP)
|
||||||
}), nil
|
return spells, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByName(name string) (*Spell, error) {
|
func ByName(name string) (*Spell, error) {
|
||||||
spell, exists := store.LookupByIndex("byName", strings.ToLower(name))
|
var spell Spell
|
||||||
if !exists {
|
err := database.Get(&spell, "SELECT * FROM spells WHERE name = %s COLLATE NOCASE", name)
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("spell with name '%s' not found", name)
|
return nil, fmt.Errorf("spell with name '%s' not found", name)
|
||||||
}
|
}
|
||||||
return spell, nil
|
return &spell, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
@ -214,14 +164,3 @@ func (s *Spell) IsOffensive() bool {
|
|||||||
func (s *Spell) IsSupport() bool {
|
func (s *Spell) IsSupport() bool {
|
||||||
return s.Type == TypeHealing || s.Type == TypeAttackBoost || s.Type == TypeDefenseBoost
|
return s.Type == TypeHealing || s.Type == TypeAttackBoost || s.Type == TypeDefenseBoost
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy compatibility functions (will be removed later)
|
|
||||||
func LoadData(dataPath string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveData(dataPath string) error {
|
|
||||||
// No longer needed - Nigiri handles this
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
48
main.go
48
main.go
@ -9,21 +9,10 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"dk/internal/models/babble"
|
|
||||||
"dk/internal/models/control"
|
|
||||||
"dk/internal/models/drops"
|
|
||||||
"dk/internal/models/fights"
|
|
||||||
"dk/internal/models/forum"
|
|
||||||
"dk/internal/models/items"
|
|
||||||
"dk/internal/models/monsters"
|
|
||||||
"dk/internal/models/news"
|
|
||||||
"dk/internal/models/spells"
|
|
||||||
"dk/internal/models/towns"
|
|
||||||
"dk/internal/models/users"
|
"dk/internal/models/users"
|
||||||
"dk/internal/routes"
|
"dk/internal/routes"
|
||||||
"dk/internal/template"
|
"dk/internal/template"
|
||||||
|
|
||||||
nigiri "git.sharkk.net/Sharkk/Nigiri"
|
|
||||||
sushi "git.sharkk.net/Sharkk/Sushi"
|
sushi "git.sharkk.net/Sharkk/Sushi"
|
||||||
"git.sharkk.net/Sharkk/Sushi/auth"
|
"git.sharkk.net/Sharkk/Sushi/auth"
|
||||||
"git.sharkk.net/Sharkk/Sushi/csrf"
|
"git.sharkk.net/Sharkk/Sushi/csrf"
|
||||||
@ -68,11 +57,6 @@ func start(port string) error {
|
|||||||
|
|
||||||
template.InitializeCache(cwd)
|
template.InitializeCache(cwd)
|
||||||
|
|
||||||
db := nigiri.NewCollection(filepath.Join(cwd, "data"))
|
|
||||||
if err := setupStores(db); err != nil {
|
|
||||||
return fmt.Errorf("failed to setup Nigiri stores: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
app := sushi.New()
|
app := sushi.New()
|
||||||
sushi.InitSessions(filepath.Join(cwd, "data/_sessions.json"))
|
sushi.InitSessions(filepath.Join(cwd, "data/_sessions.json"))
|
||||||
|
|
||||||
@ -108,11 +92,6 @@ func start(port string) error {
|
|||||||
<-c
|
<-c
|
||||||
log.Println("\nReceived shutdown signal, shutting down gracefully...")
|
log.Println("\nReceived shutdown signal, shutting down gracefully...")
|
||||||
|
|
||||||
log.Println("Saving database...")
|
|
||||||
if err := db.Save(); err != nil {
|
|
||||||
log.Printf("Error saving database: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Saving sessions...")
|
log.Println("Saving sessions...")
|
||||||
sushi.SaveSessions()
|
sushi.SaveSessions()
|
||||||
|
|
||||||
@ -120,33 +99,6 @@ func start(port string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupStores(db *nigiri.Collection) error {
|
|
||||||
users.Init(db)
|
|
||||||
towns.Init(db)
|
|
||||||
spells.Init(db)
|
|
||||||
news.Init(db)
|
|
||||||
monsters.Init(db)
|
|
||||||
items.Init(db)
|
|
||||||
forum.Init(db)
|
|
||||||
drops.Init(db)
|
|
||||||
babble.Init(db)
|
|
||||||
fights.Init(db)
|
|
||||||
control.Init(db)
|
|
||||||
|
|
||||||
db.Add("users", users.GetStore())
|
|
||||||
db.Add("towns", towns.GetStore())
|
|
||||||
db.Add("spells", spells.GetStore())
|
|
||||||
db.Add("news", news.GetStore())
|
|
||||||
db.Add("monsters", monsters.GetStore())
|
|
||||||
db.Add("items", items.GetStore())
|
|
||||||
db.Add("forum", forum.GetStore())
|
|
||||||
db.Add("drops", drops.GetStore())
|
|
||||||
db.Add("babble", babble.GetStore())
|
|
||||||
db.Add("fights", fights.GetStore())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUserByID(userID int) any {
|
func getUserByID(userID int) any {
|
||||||
return users.GetByID(userID)
|
return users.GetByID(userID)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user