finish migration back to sqlite, update routes/actions et cetera
This commit is contained in:
parent
3df8f29a4c
commit
75a1927d3a
@ -1 +0,0 @@
|
||||
[]
|
@ -1,11 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"world_size": 200,
|
||||
"open": 1,
|
||||
"admin_email": "",
|
||||
"class_1_name": "Mage",
|
||||
"class_2_name": "Warrior",
|
||||
"class_3_name": "Paladin"
|
||||
}
|
||||
]
|
BIN
data/dk.db
Normal file
BIN
data/dk.db
Normal file
Binary file not shown.
226
data/drops.json
226
data/drops.json
@ -1,226 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Life Pebble",
|
||||
"level": 1,
|
||||
"type": 1,
|
||||
"att": "maxhp,10"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Life Stone",
|
||||
"level": 10,
|
||||
"type": 1,
|
||||
"att": "maxhp,25"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Life Rock",
|
||||
"level": 25,
|
||||
"type": 1,
|
||||
"att": "maxhp,50"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "Magic Pebble",
|
||||
"level": 1,
|
||||
"type": 1,
|
||||
"att": "maxmp,10"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Magic Stone",
|
||||
"level": 10,
|
||||
"type": 1,
|
||||
"att": "maxmp,25"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "Magic Rock",
|
||||
"level": 25,
|
||||
"type": 1,
|
||||
"att": "maxmp,50"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "Dragon's Scale",
|
||||
"level": 10,
|
||||
"type": 1,
|
||||
"att": "defensepower,25"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "Dragon's Plate",
|
||||
"level": 30,
|
||||
"type": 1,
|
||||
"att": "defensepower,50"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": "Dragon's Claw",
|
||||
"level": 10,
|
||||
"type": 1,
|
||||
"att": "attackpower,25"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"name": "Dragon's Tooth",
|
||||
"level": 30,
|
||||
"type": 1,
|
||||
"att": "attackpower,50"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "Dragon's Tear",
|
||||
"level": 35,
|
||||
"type": 1,
|
||||
"att": "strength,50"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"name": "Dragon's Wing",
|
||||
"level": 35,
|
||||
"type": 1,
|
||||
"att": "dexterity,50"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"name": "Demon's Sin",
|
||||
"level": 35,
|
||||
"type": 1,
|
||||
"att": "maxhp,-50,strength,50"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"name": "Demon's Fall",
|
||||
"level": 35,
|
||||
"type": 1,
|
||||
"att": "maxmp,-50,strength,50"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"name": "Demon's Lie",
|
||||
"level": 45,
|
||||
"type": 1,
|
||||
"att": "maxhp,-100,strength,100"
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"name": "Demon's Hate",
|
||||
"level": 45,
|
||||
"type": 1,
|
||||
"att": "maxmp,-100,strength,100"
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"name": "Angel's Joy",
|
||||
"level": 25,
|
||||
"type": 1,
|
||||
"att": "maxhp,25,strength,25"
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"name": "Angel's Rise",
|
||||
"level": 30,
|
||||
"type": 1,
|
||||
"att": "maxhp,50,strength,50"
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"name": "Angel's Truth",
|
||||
"level": 35,
|
||||
"type": 1,
|
||||
"att": "maxhp,75,strength,75"
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"name": "Angel's Love",
|
||||
"level": 40,
|
||||
"type": 1,
|
||||
"att": "maxhp,100,strength,100"
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"name": "Seraph's Joy",
|
||||
"level": 25,
|
||||
"type": 1,
|
||||
"att": "maxmp,25,dexterity,25"
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"name": "Seraph's Rise",
|
||||
"level": 30,
|
||||
"type": 1,
|
||||
"att": "maxmp,50,dexterity,50"
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"name": "Seraph's Truth",
|
||||
"level": 35,
|
||||
"type": 1,
|
||||
"att": "maxmp,75,dexterity,75"
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"name": "Seraph's Love",
|
||||
"level": 40,
|
||||
"type": 1,
|
||||
"att": "maxmp,100,dexterity,100"
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"name": "Ruby",
|
||||
"level": 50,
|
||||
"type": 1,
|
||||
"att": "maxhp,150"
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"name": "Pearl",
|
||||
"level": 50,
|
||||
"type": 1,
|
||||
"att": "maxmp,150"
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"name": "Emerald",
|
||||
"level": 50,
|
||||
"type": 1,
|
||||
"att": "strength,150"
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"name": "Topaz",
|
||||
"level": 50,
|
||||
"type": 1,
|
||||
"att": "dexterity,150"
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"name": "Obsidian",
|
||||
"level": 50,
|
||||
"type": 1,
|
||||
"att": "attackpower,150"
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
"name": "Diamond",
|
||||
"level": 50,
|
||||
"type": 1,
|
||||
"att": "defensepower,150"
|
||||
},
|
||||
{
|
||||
"id": 31,
|
||||
"name": "Memory Drop",
|
||||
"level": 5,
|
||||
"type": 1,
|
||||
"att": "expbonus,10"
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
"name": "Fortune Drop",
|
||||
"level": 5,
|
||||
"type": 1,
|
||||
"att": "goldbonus,10"
|
||||
}
|
||||
]
|
1108
data/fights.json
1108
data/fights.json
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
||||
[]
|
266
data/items.json
266
data/items.json
@ -1,266 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"type": 1,
|
||||
"name": "Stick",
|
||||
"value": 10,
|
||||
"att": 2,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": 1,
|
||||
"name": "Branch",
|
||||
"value": 30,
|
||||
"att": 4,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"type": 1,
|
||||
"name": "Club",
|
||||
"value": 40,
|
||||
"att": 5,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": 1,
|
||||
"name": "Dagger",
|
||||
"value": 90,
|
||||
"att": 8,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": 1,
|
||||
"name": "Hatchet",
|
||||
"value": 150,
|
||||
"att": 12,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": 1,
|
||||
"name": "Axe",
|
||||
"value": 200,
|
||||
"att": 16,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": 1,
|
||||
"name": "Brand",
|
||||
"value": 300,
|
||||
"att": 25,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": 1,
|
||||
"name": "Poleaxe",
|
||||
"value": 500,
|
||||
"att": 35,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"type": 1,
|
||||
"name": "Broadsword",
|
||||
"value": 800,
|
||||
"att": 45,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"type": 1,
|
||||
"name": "Battle Axe",
|
||||
"value": 1200,
|
||||
"att": 50,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"type": 1,
|
||||
"name": "Claymore",
|
||||
"value": 2000,
|
||||
"att": 60,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"type": 1,
|
||||
"name": "Dark Axe",
|
||||
"value": 3000,
|
||||
"att": 100,
|
||||
"special": "expbonus,-5"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"type": 1,
|
||||
"name": "Dark Sword",
|
||||
"value": 4500,
|
||||
"att": 125,
|
||||
"special": "expbonus,-10"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"type": 1,
|
||||
"name": "Bright Sword",
|
||||
"value": 6000,
|
||||
"att": 100,
|
||||
"special": "expbonus,10"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"type": 1,
|
||||
"name": "Magic Sword",
|
||||
"value": 10000,
|
||||
"att": 150,
|
||||
"special": "maxmp,50"
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"type": 1,
|
||||
"name": "Destiny Blade",
|
||||
"value": 50000,
|
||||
"att": 250,
|
||||
"special": "strength,50"
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"type": 2,
|
||||
"name": "Skivvies",
|
||||
"value": 25,
|
||||
"att": 2,
|
||||
"special": "goldbonus,10"
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"type": 2,
|
||||
"name": "Clothes",
|
||||
"value": 50,
|
||||
"att": 5,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"type": 2,
|
||||
"name": "Leather Armor",
|
||||
"value": 75,
|
||||
"att": 10,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"type": 2,
|
||||
"name": "Hard Leather Armor",
|
||||
"value": 150,
|
||||
"att": 25,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"type": 2,
|
||||
"name": "Chain Mail",
|
||||
"value": 300,
|
||||
"att": 30,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"type": 2,
|
||||
"name": "Bronze Plate",
|
||||
"value": 900,
|
||||
"att": 50,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"type": 2,
|
||||
"name": "Iron Plate",
|
||||
"value": 2000,
|
||||
"att": 100,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"type": 2,
|
||||
"name": "Magic Armor",
|
||||
"value": 4000,
|
||||
"att": 125,
|
||||
"special": "maxmp,50"
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"type": 2,
|
||||
"name": "Dark Armor",
|
||||
"value": 5000,
|
||||
"att": 150,
|
||||
"special": "expbonus,-10"
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"type": 2,
|
||||
"name": "Bright Armor",
|
||||
"value": 10000,
|
||||
"att": 175,
|
||||
"special": "expbonus,10"
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"type": 2,
|
||||
"name": "Destiny Raiment",
|
||||
"value": 50000,
|
||||
"att": 200,
|
||||
"special": "dexterity,50"
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"type": 3,
|
||||
"name": "Reed Shield",
|
||||
"value": 50,
|
||||
"att": 2,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"type": 3,
|
||||
"name": "Buckler",
|
||||
"value": 100,
|
||||
"att": 4,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
"type": 3,
|
||||
"name": "Small Shield",
|
||||
"value": 500,
|
||||
"att": 10,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 31,
|
||||
"type": 3,
|
||||
"name": "Large Shield",
|
||||
"value": 2500,
|
||||
"att": 30,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
"type": 3,
|
||||
"name": "Silver Shield",
|
||||
"value": 10000,
|
||||
"att": 60,
|
||||
"special": ""
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
"type": 3,
|
||||
"name": "Destiny Aegis",
|
||||
"value": 25000,
|
||||
"att": 100,
|
||||
"special": "maxhp,50"
|
||||
}
|
||||
]
|
1663
data/monsters.json
1663
data/monsters.json
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
||||
[]
|
135
data/spells.json
135
data/spells.json
@ -1,135 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Heal",
|
||||
"mp": 5,
|
||||
"attribute": 10,
|
||||
"type": 1
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Revive",
|
||||
"mp": 10,
|
||||
"attribute": 25,
|
||||
"type": 1
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Life",
|
||||
"mp": 25,
|
||||
"attribute": 50,
|
||||
"type": 1
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "Breath",
|
||||
"mp": 50,
|
||||
"attribute": 100,
|
||||
"type": 1
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Gaia",
|
||||
"mp": 75,
|
||||
"attribute": 150,
|
||||
"type": 1
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "Hurt",
|
||||
"mp": 5,
|
||||
"attribute": 15,
|
||||
"type": 2
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "Pain",
|
||||
"mp": 12,
|
||||
"attribute": 35,
|
||||
"type": 2
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "Maim",
|
||||
"mp": 25,
|
||||
"attribute": 70,
|
||||
"type": 2
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": "Rend",
|
||||
"mp": 40,
|
||||
"attribute": 100,
|
||||
"type": 2
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"name": "Chaos",
|
||||
"mp": 50,
|
||||
"attribute": 130,
|
||||
"type": 2
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "Sleep",
|
||||
"mp": 10,
|
||||
"attribute": 5,
|
||||
"type": 3
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"name": "Dream",
|
||||
"mp": 30,
|
||||
"attribute": 9,
|
||||
"type": 3
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"name": "Nightmare",
|
||||
"mp": 60,
|
||||
"attribute": 13,
|
||||
"type": 3
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"name": "Craze",
|
||||
"mp": 10,
|
||||
"attribute": 10,
|
||||
"type": 4
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"name": "Rage",
|
||||
"mp": 20,
|
||||
"attribute": 25,
|
||||
"type": 4
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"name": "Fury",
|
||||
"mp": 30,
|
||||
"attribute": 50,
|
||||
"type": 4
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"name": "Ward",
|
||||
"mp": 10,
|
||||
"attribute": 10,
|
||||
"type": 5
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"name": "Fend",
|
||||
"mp": 20,
|
||||
"attribute": 25,
|
||||
"type": 5
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"name": "Barrier",
|
||||
"mp": 30,
|
||||
"attribute": 50,
|
||||
"type": 5
|
||||
}
|
||||
]
|
@ -1,82 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Midworld",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"inn_cost": 1,
|
||||
"map_cost": 0,
|
||||
"tp_cost": 0,
|
||||
"shop_list": "1,2,3,17,18,19,28,29"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Roma",
|
||||
"x": 30,
|
||||
"y": 30,
|
||||
"inn_cost": 10,
|
||||
"map_cost": 25,
|
||||
"tp_cost": 5,
|
||||
"shop_list": "2,3,4,18,19,29"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Bris",
|
||||
"x": 70,
|
||||
"y": -70,
|
||||
"inn_cost": 25,
|
||||
"map_cost": 50,
|
||||
"tp_cost": 15,
|
||||
"shop_list": "2,3,4,5,18,19,20,29.30"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "Kalle",
|
||||
"x": -100,
|
||||
"y": 100,
|
||||
"inn_cost": 40,
|
||||
"map_cost": 100,
|
||||
"tp_cost": 30,
|
||||
"shop_list": "5,6,8,10,12,21,22,23,29,30"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Narcissa",
|
||||
"x": -130,
|
||||
"y": -130,
|
||||
"inn_cost": 60,
|
||||
"map_cost": 500,
|
||||
"tp_cost": 50,
|
||||
"shop_list": "4,7,9,11,13,21,22,23,29,30,31"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "Hambry",
|
||||
"x": 170,
|
||||
"y": 170,
|
||||
"inn_cost": 90,
|
||||
"map_cost": 1000,
|
||||
"tp_cost": 80,
|
||||
"shop_list": "10,11,12,13,14,23,24,30,31"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "Gilead",
|
||||
"x": 200,
|
||||
"y": -200,
|
||||
"inn_cost": 100,
|
||||
"map_cost": 3000,
|
||||
"tp_cost": 110,
|
||||
"shop_list": "12,13,14,15,24,25,26,32"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "Endworld",
|
||||
"x": -250,
|
||||
"y": -250,
|
||||
"inn_cost": 125,
|
||||
"map_cost": 9000,
|
||||
"tp_cost": 160,
|
||||
"shop_list": "16,27,33"
|
||||
}
|
||||
]
|
85
database.sql
85
database.sql
@ -12,7 +12,7 @@ CREATE TABLE drops (
|
||||
`name` TEXT NOT NULL,
|
||||
`level` INTEGER NOT NULL DEFAULT 0,
|
||||
`type` INTEGER NOT NULL DEFAULT 0,
|
||||
`att` TEXT NOT NULL DEFAULT '',
|
||||
`att` TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
INSERT INTO drops VALUES
|
||||
(1, 'Life Pebble', 1, 1, 'maxhp,10'),
|
||||
@ -117,7 +117,7 @@ CREATE TABLE classes (
|
||||
'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,
|
||||
'dex_rate' INTEGER NOT NULL DEFAULT 2
|
||||
);
|
||||
INSERT INTO classes VALUES
|
||||
(1, 'Adventurer', '', 3, 15, 10, 4, 4, 2, 2, 2, 2),
|
||||
@ -347,4 +347,83 @@ INSERT INTO towns VALUES
|
||||
(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');
|
||||
(8, 'Endworld', -250, -250, 125, 9000, 160, '16,27,33');
|
||||
|
||||
DROP TABLE IF EXISTS users;
|
||||
CREATE TABLE users (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`username` TEXT NOT NULL,
|
||||
`password` TEXT NOT NULL,
|
||||
`email` TEXT NOT NULL,
|
||||
`verified` INTEGER NOT NULL DEFAULT 0,
|
||||
`token` TEXT NOT NULL DEFAULT '',
|
||||
`registered` INTEGER NOT NULL DEFAULT (unixepoch()),
|
||||
`last_online` INTEGER NOT NULL DEFAULT (unixepoch()),
|
||||
`auth` INTEGER NOT NULL DEFAULT 0,
|
||||
`x` INTEGER NOT NULL DEFAULT 0,
|
||||
`y` INTEGER NOT NULL DEFAULT 0,
|
||||
`class_id` INTEGER NOT NULL DEFAULT 1,
|
||||
`currently` TEXT NOT NULL DEFAULT 'In Town',
|
||||
`fight_id` INTEGER NOT NULL DEFAULT 0,
|
||||
`hp` INTEGER NOT NULL DEFAULT 10,
|
||||
`mp` INTEGER NOT NULL DEFAULT 10,
|
||||
`tp` INTEGER NOT NULL DEFAULT 10,
|
||||
`max_hp` INTEGER NOT NULL DEFAULT 10,
|
||||
`max_mp` INTEGER NOT NULL DEFAULT 10,
|
||||
`max_tp` INTEGER NOT NULL DEFAULT 10,
|
||||
`level` INTEGER NOT NULL DEFAULT 1,
|
||||
`gold` INTEGER NOT NULL DEFAULT 100,
|
||||
`exp` INTEGER NOT NULL DEFAULT 0,
|
||||
`gold_bonus` INTEGER NOT NULL DEFAULT 0,
|
||||
`exp_bonus` INTEGER NOT NULL DEFAULT 0,
|
||||
`strength` INTEGER NOT NULL DEFAULT 0,
|
||||
`dexterity` INTEGER NOT NULL DEFAULT 0,
|
||||
`attack` INTEGER NOT NULL DEFAULT 0,
|
||||
`defense` INTEGER NOT NULL DEFAULT 0,
|
||||
`weapon_id` INTEGER NOT NULL DEFAULT 0,
|
||||
`armor_id` INTEGER NOT NULL DEFAULT 0,
|
||||
`shield_id` INTEGER NOT NULL DEFAULT 0,
|
||||
`slot_1_id` INTEGER NOT NULL DEFAULT 0,
|
||||
`slot_2_id` INTEGER NOT NULL DEFAULT 0,
|
||||
`slot_3_id` INTEGER NOT NULL DEFAULT 0,
|
||||
`weapon_name` TEXT NOT NULL DEFAULT '',
|
||||
`armor_name` TEXT NOT NULL DEFAULT '',
|
||||
`shield_name` TEXT NOT NULL DEFAULT '',
|
||||
`slot_1_name` TEXT NOT NULL DEFAULT '',
|
||||
`slot_2_name` TEXT NOT NULL DEFAULT '',
|
||||
`slot_3_name` TEXT NOT NULL DEFAULT '',
|
||||
`spells` TEXT NOT NULL DEFAULT '',
|
||||
`towns` TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS fights;
|
||||
CREATE TABLE fights (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`user_id` INTEGER NOT NULL,
|
||||
`monster_id` INTEGER NOT NULL,
|
||||
`monster_hp` INTEGER NOT NULL DEFAULT 0,
|
||||
`monster_max_hp` INTEGER NOT NULL DEFAULT 0,
|
||||
`monster_sleep` INTEGER NOT NULL DEFAULT 0,
|
||||
`monster_immune` INTEGER NOT NULL DEFAULT 0,
|
||||
`uber_damage` INTEGER NOT NULL DEFAULT 0,
|
||||
`uber_defense` INTEGER NOT NULL DEFAULT 0,
|
||||
`first_strike` INTEGER NOT NULL DEFAULT 0,
|
||||
`turn` INTEGER NOT NULL DEFAULT 0,
|
||||
`ran_away` INTEGER NOT NULL DEFAULT 0,
|
||||
`victory` INTEGER NOT NULL DEFAULT 0,
|
||||
`won` INTEGER NOT NULL DEFAULT 0,
|
||||
`reward_gold` INTEGER NOT NULL DEFAULT 0,
|
||||
`reward_exp` INTEGER NOT NULL DEFAULT 0,
|
||||
`created` INTEGER NOT NULL DEFAULT (unixepoch()),
|
||||
`updated` INTEGER NOT NULL DEFAULT (unixepoch())
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS fight_logs;
|
||||
CREATE TABLE fight_logs (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`fight_id` INTEGER NOT NULL,
|
||||
`type` INTEGER NOT NULL DEFAULT 7,
|
||||
`data` INTEGER NOT NULL DEFAULT 0,
|
||||
`name` TEXT NOT NULL DEFAULT '',
|
||||
`created` INTEGER NOT NULL DEFAULT (unixepoch())
|
||||
);
|
||||
|
3
go.mod
3
go.mod
@ -3,9 +3,9 @@ module dk
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
git.sharkk.net/Sharkk/Nigiri v1.0.0
|
||||
git.sharkk.net/Sharkk/Sushi v1.1.1
|
||||
github.com/valyala/fasthttp v1.65.0
|
||||
zombiezen.com/go/sqlite v1.4.2
|
||||
)
|
||||
|
||||
require (
|
||||
@ -24,5 +24,4 @@ require (
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.37.1 // indirect
|
||||
zombiezen.com/go/sqlite v1.4.2 // indirect
|
||||
)
|
||||
|
28
go.sum
28
go.sum
@ -1,11 +1,11 @@
|
||||
git.sharkk.net/Sharkk/Nigiri v1.0.0 h1:N0MvWOoX54iXjR8D1LqGIFrtMAPdaoj/32n13Ou/p90=
|
||||
git.sharkk.net/Sharkk/Nigiri v1.0.0/go.mod h1:HWpMtXaodPXE7dZXQ6tbZNL0DRV9PT65D0DOV0NAwsM=
|
||||
git.sharkk.net/Sharkk/Sushi v1.1.1 h1:ynU16l6vAhY/JUwHlI4zMQiPuL9lcs88W/mAGZsL4Rw=
|
||||
git.sharkk.net/Sharkk/Sushi v1.1.1/go.mod h1:S84ACGkuZ+BKzBO4lb5WQnm5aw9+l7VSO2T1bjzxL3o=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
@ -26,16 +26,40 @@ golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
|
||||
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
||||
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
|
||||
modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8=
|
||||
modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/libc v1.65.7 h1:Ia9Z4yzZtWNtUIuiPuQ7Qf7kxYrxP1/jeHZzG8bFu00=
|
||||
modernc.org/libc v1.65.7/go.mod h1:011EQibzzio/VX3ygj1qGFt5kMjP0lHb0qCW5/D/pQU=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.37.1 h1:EgHJK/FPoqC+q2YBXg7fUmES37pCHFc97sI7zSayBEs=
|
||||
modernc.org/sqlite v1.37.1/go.mod h1:XwdRtsE1MpiBcL54+MbKcaDvcuej+IYSMfLN6gSKV8g=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
zombiezen.com/go/sqlite v1.4.2 h1:KZXLrBuJ7tKNEm+VJcApLMeQbhmAUOKA5VWS93DfFRo=
|
||||
zombiezen.com/go/sqlite v1.4.2/go.mod h1:5Kd4taTAD4MkBzT25mQ9uaAlLjyR0rFhsR6iINO70jc=
|
||||
|
@ -1,6 +1,9 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"dk/internal/database"
|
||||
"dk/internal/helpers/exp"
|
||||
"dk/internal/models/fightlogs"
|
||||
"dk/internal/models/fights"
|
||||
"dk/internal/models/monsters"
|
||||
"dk/internal/models/spells"
|
||||
@ -8,210 +11,273 @@ import (
|
||||
"dk/internal/models/users"
|
||||
"math"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func HandleAttack(fight *fights.Fight, user *users.User) {
|
||||
// Load monster data to get armor
|
||||
type FightResult struct {
|
||||
FightUpdates map[string]any
|
||||
UserUpdates map[string]any
|
||||
LogAction func() error
|
||||
Ended bool
|
||||
Victory bool
|
||||
Won bool
|
||||
}
|
||||
|
||||
func HandleAttack(fight *fights.Fight, user *users.User) *FightResult {
|
||||
monster, err := monsters.Find(fight.MonsterID)
|
||||
if err != nil {
|
||||
fight.AddAction("Monster not found!")
|
||||
return
|
||||
return &FightResult{
|
||||
LogAction: func() error { return fightlogs.AddAction(fight.ID, "Monster not found!") },
|
||||
}
|
||||
}
|
||||
|
||||
// Player attack damage calculation with sqrt scaling
|
||||
// Calculate damage
|
||||
attackPower := float64(user.Attack)
|
||||
minAttack := attackPower * 0.75
|
||||
maxAttack := attackPower
|
||||
rawAttack := math.Ceil(rand.Float64()*(maxAttack-minAttack) + minAttack)
|
||||
|
||||
// Progressive scaling using square root for smooth progression
|
||||
tohit := rawAttack / (1.2 + math.Sqrt(attackPower)*0.05)
|
||||
|
||||
// Critical hit chance based on strength
|
||||
// Critical hit
|
||||
criticalRoll := rand.Intn(150) + 1
|
||||
if float64(criticalRoll) <= math.Sqrt(float64(user.Strength)) {
|
||||
tohit *= 2 // Critical hit
|
||||
tohit *= 2
|
||||
}
|
||||
|
||||
// Monster defense calculation with more aggressive scaling
|
||||
// Monster defense
|
||||
armor := float64(monster.Armor)
|
||||
minBlock := armor * 0.75
|
||||
maxBlock := armor
|
||||
rawBlock := math.Ceil(rand.Float64()*(maxBlock-minBlock) + minBlock)
|
||||
|
||||
// Armor uses higher divisor to balance against player attack
|
||||
toblock := rawBlock / (1.8 + math.Sqrt(armor)*0.08)
|
||||
|
||||
// Calculate final damage
|
||||
damage := tohit - toblock
|
||||
if damage < 1 {
|
||||
damage = 1 // Minimum damage
|
||||
damage = 1
|
||||
}
|
||||
|
||||
// Apply uber damage bonus
|
||||
if fight.UberDamage > 0 {
|
||||
bonus := math.Ceil(damage * float64(fight.UberDamage) / 100)
|
||||
damage += bonus
|
||||
}
|
||||
|
||||
finalDamage := int(damage)
|
||||
|
||||
// Apply damage and add action
|
||||
fight.DamageMonster(finalDamage)
|
||||
fight.AddActionAttackHit(finalDamage)
|
||||
|
||||
// Check if monster is defeated
|
||||
if fight.MonsterHP <= 0 {
|
||||
fight.AddActionMonsterDeath(monster.Name)
|
||||
rewardGold, rewardExp := calculateRewards(monster, user)
|
||||
fight.WinFight(rewardGold, rewardExp)
|
||||
HandleFightWin(fight, user)
|
||||
newMonsterHP := fight.MonsterHP - finalDamage
|
||||
if newMonsterHP < 0 {
|
||||
newMonsterHP = 0
|
||||
}
|
||||
|
||||
result := &FightResult{
|
||||
FightUpdates: map[string]any{"monster_hp": newMonsterHP},
|
||||
LogAction: func() error { return fightlogs.AddAttackHit(fight.ID, finalDamage) },
|
||||
}
|
||||
|
||||
// Check if monster defeated
|
||||
if newMonsterHP <= 0 {
|
||||
rewardGold, rewardExp := calculateRewards(monster, user)
|
||||
|
||||
result.FightUpdates["victory"] = true
|
||||
result.FightUpdates["won"] = true
|
||||
result.FightUpdates["reward_gold"] = rewardGold
|
||||
result.FightUpdates["reward_exp"] = rewardExp
|
||||
|
||||
result.UserUpdates = map[string]any{
|
||||
"fight_id": 0,
|
||||
"currently": "Exploring",
|
||||
"gold": user.Gold + rewardGold,
|
||||
"exp": user.Exp + rewardExp,
|
||||
}
|
||||
|
||||
// Handle level up
|
||||
newLevel, newStats := calculateLevelUp(user.Level, user.Exp+rewardExp, user.Strength, user.Dexterity)
|
||||
if newLevel > user.Level {
|
||||
result.UserUpdates["level"] = newLevel
|
||||
result.UserUpdates["strength"] = newStats.Strength
|
||||
result.UserUpdates["dexterity"] = newStats.Dexterity
|
||||
}
|
||||
|
||||
result.LogAction = func() error {
|
||||
if err := fightlogs.AddAttackHit(fight.ID, finalDamage); err != nil {
|
||||
return err
|
||||
}
|
||||
return fightlogs.AddMonsterDeath(fight.ID, monster.Name)
|
||||
}
|
||||
result.Ended = true
|
||||
result.Victory = true
|
||||
result.Won = true
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func HandleSpell(fight *fights.Fight, user *users.User, spellID int) {
|
||||
func HandleSpell(fight *fights.Fight, user *users.User, spellID int) *FightResult {
|
||||
spell, err := spells.Find(spellID)
|
||||
if err != nil {
|
||||
fight.AddAction("Spell not found!")
|
||||
return
|
||||
return &FightResult{
|
||||
LogAction: func() error { return fightlogs.AddAction(fight.ID, "Spell not found!") },
|
||||
}
|
||||
}
|
||||
|
||||
// Check if user has enough MP
|
||||
if user.MP < spell.MP {
|
||||
fight.AddAction("Not enough MP to cast " + spell.Name + "!")
|
||||
return
|
||||
return &FightResult{
|
||||
LogAction: func() error { return fightlogs.AddAction(fight.ID, "Not enough MP to cast "+spell.Name+"!") },
|
||||
}
|
||||
}
|
||||
|
||||
// Check if user knows this spell
|
||||
if !user.HasSpell(spellID) {
|
||||
fight.AddAction("You don't know that spell!")
|
||||
return
|
||||
return &FightResult{
|
||||
LogAction: func() error { return fightlogs.AddAction(fight.ID, "You don't know that spell!") },
|
||||
}
|
||||
}
|
||||
|
||||
// Deduct MP
|
||||
user.MP -= spell.MP
|
||||
result := &FightResult{
|
||||
UserUpdates: map[string]any{"mp": user.MP - spell.MP},
|
||||
}
|
||||
|
||||
switch spell.Type {
|
||||
case spells.TypeHealing:
|
||||
// Heal user
|
||||
healAmount := spell.Attribute
|
||||
user.HP += healAmount
|
||||
if user.HP > user.MaxHP {
|
||||
user.HP = user.MaxHP
|
||||
newHP := user.HP + spell.Attribute
|
||||
if newHP > user.MaxHP {
|
||||
newHP = user.MaxHP
|
||||
}
|
||||
fight.AddAction("You cast " + spell.Name + " and healed " + strconv.Itoa(healAmount) + " HP!")
|
||||
result.UserUpdates["hp"] = newHP
|
||||
result.LogAction = func() error { return fightlogs.AddSpellHeal(fight.ID, spell.Name, spell.Attribute) }
|
||||
|
||||
case spells.TypeHurt:
|
||||
// Damage monster
|
||||
damage := spell.Attribute
|
||||
fight.DamageMonster(damage)
|
||||
fight.AddAction("You cast " + spell.Name + " and dealt " + strconv.Itoa(damage) + " damage!")
|
||||
newMonsterHP := fight.MonsterHP - spell.Attribute
|
||||
if newMonsterHP < 0 {
|
||||
newMonsterHP = 0
|
||||
}
|
||||
result.FightUpdates = map[string]any{"monster_hp": newMonsterHP}
|
||||
result.LogAction = func() error { return fightlogs.AddSpellHurt(fight.ID, spell.Name, spell.Attribute) }
|
||||
|
||||
// Check if monster is defeated
|
||||
if fight.MonsterHP <= 0 {
|
||||
fight.WinFight(10, 5) // Basic rewards
|
||||
if newMonsterHP <= 0 {
|
||||
result.FightUpdates["victory"] = true
|
||||
result.FightUpdates["won"] = true
|
||||
result.FightUpdates["reward_gold"] = 10
|
||||
result.FightUpdates["reward_exp"] = 5
|
||||
result.UserUpdates["fight_id"] = 0
|
||||
result.UserUpdates["currently"] = "Exploring"
|
||||
result.Ended = true
|
||||
result.Victory = true
|
||||
result.Won = true
|
||||
}
|
||||
|
||||
default:
|
||||
fight.AddAction("You cast " + spell.Name + " but nothing happened!")
|
||||
result.LogAction = func() error { return fightlogs.AddAction(fight.ID, "You cast "+spell.Name+" but nothing happened!") }
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func HandleRun(fight *fights.Fight, user *users.User) {
|
||||
// 20% chance to successfully run away
|
||||
func HandleRun(fight *fights.Fight, user *users.User) *FightResult {
|
||||
result := &FightResult{}
|
||||
|
||||
if rand.Float32() < 0.2 {
|
||||
fight.RunAway()
|
||||
user.FightID = 0
|
||||
fight.AddAction("You successfully ran away!")
|
||||
result.FightUpdates = map[string]any{"ran_away": true}
|
||||
result.UserUpdates = map[string]any{
|
||||
"fight_id": 0,
|
||||
"currently": "Exploring",
|
||||
}
|
||||
result.LogAction = func() error { return fightlogs.AddRunSuccess(fight.ID) }
|
||||
result.Ended = true
|
||||
} else {
|
||||
fight.AddAction("You failed to run away!")
|
||||
result.LogAction = func() error { return fightlogs.AddRunFail(fight.ID) }
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func HandleMonsterAttack(fight *fights.Fight, user *users.User) {
|
||||
// Load monster data
|
||||
func HandleMonsterAttack(fight *fights.Fight, user *users.User) *FightResult {
|
||||
monster, err := monsters.Find(fight.MonsterID)
|
||||
if err != nil {
|
||||
return
|
||||
return &FightResult{}
|
||||
}
|
||||
|
||||
// Monster attack damage calculation
|
||||
// Calculate damage
|
||||
attackPower := float64(monster.MaxDmg)
|
||||
minAttack := attackPower * 0.75
|
||||
maxAttack := attackPower
|
||||
tohit := math.Ceil(rand.Float64()*(maxAttack-minAttack)+minAttack) / 3
|
||||
|
||||
// User defense calculation
|
||||
defense := float64(user.Defense)
|
||||
minBlock := defense * 0.75
|
||||
maxBlock := defense
|
||||
toblock := math.Ceil(rand.Float64()*(maxBlock-minBlock)+minBlock) / 3
|
||||
|
||||
// Calculate final damage
|
||||
damage := tohit - toblock
|
||||
if damage < 1 {
|
||||
damage = 1 // Minimum damage
|
||||
damage = 1
|
||||
}
|
||||
|
||||
// Apply uber defense bonus (reduces damage taken)
|
||||
if fight.UberDefense > 0 {
|
||||
reduction := math.Ceil(damage * float64(fight.UberDefense) / 100)
|
||||
damage -= reduction
|
||||
if damage < 1 {
|
||||
damage = 1 // Still minimum 1 damage
|
||||
damage = 1
|
||||
}
|
||||
}
|
||||
|
||||
finalDamage := int(damage)
|
||||
|
||||
// Apply damage to user
|
||||
user.HP -= finalDamage
|
||||
if user.HP < 0 {
|
||||
user.HP = 0
|
||||
newHP := user.HP - finalDamage
|
||||
if newHP < 0 {
|
||||
newHP = 0
|
||||
}
|
||||
|
||||
// Add monster attack action using memory-optimized format
|
||||
fight.AddActionMonsterAttack(monster.Name, finalDamage)
|
||||
|
||||
// Check if user is defeated
|
||||
if user.HP <= 0 {
|
||||
fight.LoseFight()
|
||||
HandleFightLoss(fight, user)
|
||||
result := &FightResult{
|
||||
UserUpdates: map[string]any{"hp": newHP},
|
||||
LogAction: func() error { return fightlogs.AddMonsterAttack(fight.ID, monster.Name, finalDamage) },
|
||||
}
|
||||
|
||||
if newHP <= 0 {
|
||||
closestTown := findClosestTown(user.X, user.Y)
|
||||
townX, townY := 0, 0
|
||||
if closestTown != nil {
|
||||
townX, townY = closestTown.X, closestTown.Y
|
||||
}
|
||||
|
||||
result.FightUpdates = map[string]any{
|
||||
"victory": true,
|
||||
"won": false,
|
||||
}
|
||||
result.UserUpdates = map[string]any{
|
||||
"fight_id": 0,
|
||||
"currently": "In Town",
|
||||
"hp": user.MaxHP / 4,
|
||||
"gold": (user.Gold * 3) / 4,
|
||||
"x": townX,
|
||||
"y": townY,
|
||||
}
|
||||
result.Ended = true
|
||||
result.Victory = true
|
||||
result.Won = false
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func HandleFightWin(fight *fights.Fight, user *users.User) {
|
||||
// Add rewards to user
|
||||
user.GrantExp(fight.RewardExp)
|
||||
user.Gold += fight.RewardGold
|
||||
|
||||
// Reset fight state
|
||||
user.FightID = 0
|
||||
user.Currently = "Exploring"
|
||||
|
||||
fight.Save()
|
||||
user.Save()
|
||||
type LevelStats struct {
|
||||
Strength int
|
||||
Dexterity int
|
||||
}
|
||||
|
||||
func HandleFightLoss(fight *fights.Fight, user *users.User) {
|
||||
// Find closest town to user's position
|
||||
closestTown := findClosestTown(user.X, user.Y)
|
||||
if closestTown != nil {
|
||||
user.X = closestTown.X
|
||||
user.Y = closestTown.Y
|
||||
func calculateLevelUp(currentLevel, newExp, currentStr, currentDex int) (int, LevelStats) {
|
||||
level := currentLevel
|
||||
str := currentStr
|
||||
dex := currentDex
|
||||
nexp := newExp
|
||||
|
||||
for {
|
||||
expNeeded := exp.Calc(level + 1)
|
||||
if nexp < expNeeded {
|
||||
break
|
||||
}
|
||||
level++
|
||||
str++
|
||||
dex++
|
||||
nexp -= expNeeded
|
||||
}
|
||||
|
||||
// Apply death penalties
|
||||
user.HP = user.MaxHP / 4 // 25% of max health
|
||||
user.Gold = (user.Gold * 3) / 4 // 75% of gold
|
||||
|
||||
// Reset fight state
|
||||
user.FightID = 0
|
||||
user.Currently = "In Town"
|
||||
|
||||
fight.Save()
|
||||
user.Save()
|
||||
return level, LevelStats{Strength: str, Dexterity: dex}
|
||||
}
|
||||
|
||||
func findClosestTown(x, y int) *towns.Town {
|
||||
@ -235,7 +301,6 @@ func findClosestTown(x, y int) *towns.Town {
|
||||
}
|
||||
|
||||
func calculateRewards(monster *monsters.Monster, user *users.User) (int, int) {
|
||||
// Base rewards (83-100% of max)
|
||||
minExp := (monster.MaxExp * 5) / 6
|
||||
maxExp := monster.MaxExp
|
||||
exp := rand.Intn(maxExp-minExp+1) + minExp
|
||||
@ -244,7 +309,6 @@ func calculateRewards(monster *monsters.Monster, user *users.User) (int, int) {
|
||||
maxGold := monster.MaxGold
|
||||
gold := rand.Intn(maxGold-minGold+1) + minGold
|
||||
|
||||
// Apply bonus multipliers
|
||||
expBonus := (user.ExpBonus * exp) / 100
|
||||
exp += expBonus
|
||||
|
||||
@ -253,3 +317,32 @@ func calculateRewards(monster *monsters.Monster, user *users.User) (int, int) {
|
||||
|
||||
return gold, exp
|
||||
}
|
||||
|
||||
func ExecuteFightAction(fightID int, result *FightResult) error {
|
||||
return database.Transaction(func() error {
|
||||
// Update fight
|
||||
if len(result.FightUpdates) > 0 {
|
||||
if err := database.Update("fights", result.FightUpdates, "id", fightID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Update user
|
||||
if len(result.UserUpdates) > 0 {
|
||||
fight, err := fights.Find(fightID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := database.Update("users", result.UserUpdates, "id", fight.UserID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Add log entry
|
||||
if result.LogAction != nil {
|
||||
return result.LogAction()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"dk/internal/models/control"
|
||||
"dk/internal/control"
|
||||
"dk/internal/models/fights"
|
||||
"dk/internal/models/monsters"
|
||||
"dk/internal/models/towns"
|
||||
|
@ -5,52 +5,53 @@ import (
|
||||
"dk/internal/models/users"
|
||||
)
|
||||
|
||||
// UserEquipItem equips a given item onto a user. This overwrites any
|
||||
// previously equipped item in the slot. Does not save.
|
||||
func UserEquipItem(user *users.User, item *items.Item) {
|
||||
slotInUse := false
|
||||
if item.Type == items.TypeWeapon && user.WeaponID != 0 {
|
||||
slotInUse = true
|
||||
}
|
||||
if item.Type == items.TypeArmor && user.ArmorID != 0 {
|
||||
slotInUse = true
|
||||
}
|
||||
if item.Type == items.TypeShield && user.ShieldID != 0 {
|
||||
slotInUse = true
|
||||
}
|
||||
// UserEquipItem calculates equipment updates for a user equipping an item.
|
||||
// Returns map of database field updates without modifying the user struct.
|
||||
func UserEquipItem(user *users.User, item *items.Item) map[string]any {
|
||||
updates := make(map[string]any)
|
||||
|
||||
var oldItem *items.Item
|
||||
if slotInUse && item.Type == items.TypeWeapon {
|
||||
oldItem, _ = items.Find(user.WeaponID)
|
||||
} else if slotInUse && item.Type == items.TypeArmor {
|
||||
oldItem, _ = items.Find(user.ArmorID)
|
||||
} else if slotInUse && item.Type == items.TypeShield {
|
||||
oldItem, _ = items.Find(user.ShieldID)
|
||||
}
|
||||
|
||||
if oldItem != nil {
|
||||
switch oldItem.Type {
|
||||
case items.TypeWeapon:
|
||||
user.Attack -= oldItem.Att
|
||||
case items.TypeArmor:
|
||||
user.Defense -= oldItem.Att
|
||||
case items.TypeShield:
|
||||
user.Defense -= oldItem.Att
|
||||
}
|
||||
}
|
||||
// Calculate stat changes
|
||||
newAttack := user.Attack
|
||||
newDefense := user.Defense
|
||||
|
||||
// Remove old item stats if slot occupied
|
||||
switch item.Type {
|
||||
case items.TypeWeapon:
|
||||
user.Attack += item.Att
|
||||
user.WeaponID = item.ID
|
||||
user.WeaponName = item.Name
|
||||
if user.WeaponID != 0 {
|
||||
if oldItem, err := items.Find(user.WeaponID); err == nil {
|
||||
newAttack -= oldItem.Att
|
||||
}
|
||||
}
|
||||
// Add new item
|
||||
newAttack += item.Att
|
||||
updates["weapon_id"] = item.ID
|
||||
updates["weapon_name"] = item.Name
|
||||
|
||||
case items.TypeArmor:
|
||||
user.Defense += item.Att
|
||||
user.ArmorID = item.ID
|
||||
user.ArmorName = item.Name
|
||||
if user.ArmorID != 0 {
|
||||
if oldItem, err := items.Find(user.ArmorID); err == nil {
|
||||
newDefense -= oldItem.Att
|
||||
}
|
||||
}
|
||||
// Add new item
|
||||
newDefense += item.Att
|
||||
updates["armor_id"] = item.ID
|
||||
updates["armor_name"] = item.Name
|
||||
|
||||
case items.TypeShield:
|
||||
user.Defense += item.Att
|
||||
user.ShieldID = item.ID
|
||||
user.ShieldName = item.Name
|
||||
if user.ShieldID != 0 {
|
||||
if oldItem, err := items.Find(user.ShieldID); err == nil {
|
||||
newDefense -= oldItem.Att
|
||||
}
|
||||
}
|
||||
// Add new item
|
||||
newDefense += item.Att
|
||||
updates["shield_id"] = item.ID
|
||||
updates["shield_name"] = item.Name
|
||||
}
|
||||
|
||||
updates["attack"] = newAttack
|
||||
updates["defense"] = newDefense
|
||||
|
||||
return updates
|
||||
}
|
||||
|
@ -320,6 +320,28 @@ func Insert(tableName string, obj any, excludeFields ...string) (int64, error) {
|
||||
return DB().LastInsertRowID(), nil
|
||||
}
|
||||
|
||||
// Transaction executes multiple operations atomically
|
||||
func Transaction(fn func() error) error {
|
||||
conn := DB()
|
||||
|
||||
// Begin transaction
|
||||
if err := sqlitex.Execute(conn, "BEGIN", nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Execute operations
|
||||
err := fn()
|
||||
|
||||
if err != nil {
|
||||
// Rollback on error
|
||||
sqlitex.Execute(conn, "ROLLBACK", nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Commit on success
|
||||
return sqlitex.Execute(conn, "COMMIT", nil)
|
||||
}
|
||||
|
||||
func convertPlaceholders(query string) (string, []string) {
|
||||
var paramTypes []string
|
||||
|
||||
|
@ -61,6 +61,9 @@ func processLogin(ctx sushi.Ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
// Update last online time when logging in
|
||||
user.UpdateLastOnline()
|
||||
|
||||
ctx.Login(user.ID, user)
|
||||
|
||||
// Set success message
|
||||
@ -92,10 +95,10 @@ func showRegister(ctx sushi.Ctx) {
|
||||
|
||||
// processRegister handles registration form submission
|
||||
func processRegister(ctx sushi.Ctx) {
|
||||
username := strings.TrimSpace(string(ctx.PostArgs().Peek("username")))
|
||||
email := strings.TrimSpace(string(ctx.PostArgs().Peek("email")))
|
||||
userPassword := string(ctx.PostArgs().Peek("password"))
|
||||
confirmPassword := string(ctx.PostArgs().Peek("confirm_password"))
|
||||
username := strings.TrimSpace(ctx.Form("username").String())
|
||||
email := strings.TrimSpace(ctx.Form("email").String())
|
||||
userPassword := ctx.Form("password").String()
|
||||
confirmPassword := ctx.Form("confirm_password").String()
|
||||
|
||||
formData := map[string]string{
|
||||
"username": username,
|
||||
@ -108,18 +111,21 @@ func processRegister(ctx sushi.Ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if username already exists
|
||||
if _, err := users.ByUsername(username); err == nil {
|
||||
setFlashAndFormData(ctx, "Username already exists", formData)
|
||||
ctx.Redirect("/register")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if email already exists
|
||||
if _, err := users.ByEmail(email); err == nil {
|
||||
setFlashAndFormData(ctx, "Email already registered", formData)
|
||||
ctx.Redirect("/register")
|
||||
return
|
||||
}
|
||||
|
||||
// Create new user
|
||||
user := users.New()
|
||||
user.Username = username
|
||||
user.Email = email
|
||||
@ -127,6 +133,13 @@ func processRegister(ctx sushi.Ctx) {
|
||||
user.ClassID = 1
|
||||
user.Auth = 1
|
||||
|
||||
// Validate before inserting
|
||||
if err := user.Validate(); err != nil {
|
||||
setFlashAndFormData(ctx, fmt.Sprintf("Invalid user data: %s", err.Error()), formData)
|
||||
ctx.Redirect("/register")
|
||||
return
|
||||
}
|
||||
|
||||
if err := user.Insert(); err != nil {
|
||||
setFlashAndFormData(ctx, "Failed to create account", formData)
|
||||
ctx.Redirect("/register")
|
||||
@ -186,22 +199,22 @@ func authenticate(usernameOrEmail, plainPassword string) (*users.User, error) {
|
||||
var user *users.User
|
||||
var err error
|
||||
|
||||
// Try username first
|
||||
user, err = users.ByUsername(usernameOrEmail)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
// If username not found, try email
|
||||
user, err = users.ByEmail(usernameOrEmail)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("user not found")
|
||||
}
|
||||
}
|
||||
|
||||
isValid, err := password.VerifyPassword(plainPassword, user.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("password verification error: %w", err)
|
||||
}
|
||||
if !isValid {
|
||||
return nil, fmt.Errorf("invalid username/email or password")
|
||||
return nil, fmt.Errorf("invalid password")
|
||||
}
|
||||
|
||||
return user, nil
|
||||
|
@ -3,7 +3,9 @@ package routes
|
||||
import (
|
||||
"dk/internal/actions"
|
||||
"dk/internal/components"
|
||||
"dk/internal/database"
|
||||
"dk/internal/helpers"
|
||||
"dk/internal/models/fightlogs"
|
||||
"dk/internal/models/fights"
|
||||
"dk/internal/models/monsters"
|
||||
"dk/internal/models/spells"
|
||||
@ -25,7 +27,6 @@ func RegisterFightRoutes(app *sushi.App) {
|
||||
group.Post("/", handleFightAction)
|
||||
}
|
||||
|
||||
// requireFighting middleware ensures the user is in a fight
|
||||
func requireFighting() sushi.Middleware {
|
||||
return func(ctx sushi.Ctx, next func()) {
|
||||
user := ctx.GetCurrentUser()
|
||||
@ -60,16 +61,23 @@ func showFight(ctx sushi.Ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
// If turn 0, determine first strike and advance to turn 1
|
||||
// Initialize fight on first view
|
||||
if fight.Turn == 0 {
|
||||
// 50% chance user goes first
|
||||
err := database.Transaction(func() error {
|
||||
return database.Update("fights", map[string]any{
|
||||
"first_strike": rand.Float32() < 0.5,
|
||||
"turn": 1,
|
||||
}, "id", fight.ID)
|
||||
})
|
||||
if err != nil {
|
||||
ctx.SendError(500, "Failed to initialize fight")
|
||||
return
|
||||
}
|
||||
fight.FirstStrike = rand.Float32() < 0.5
|
||||
fight.Turn = 1
|
||||
fight.Save()
|
||||
}
|
||||
|
||||
monHpPct := helpers.ClampPct(float64(fight.MonsterHP), float64(fight.MonsterMaxHP), 0, 100)
|
||||
|
||||
monHpColor := ""
|
||||
if monHpPct < 35 {
|
||||
monHpColor = "danger"
|
||||
@ -86,15 +94,19 @@ func showFight(ctx sushi.Ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get recent fight actions
|
||||
lastAction, _ := fightlogs.GetLastAction(fight.ID)
|
||||
|
||||
components.RenderPage(ctx, "Fighting", "fight/fight.html", map[string]any{
|
||||
"fight": fight,
|
||||
"user": user,
|
||||
"monster": monster,
|
||||
"mon_hppct": monHpPct,
|
||||
"mon_hpcol": monHpColor,
|
||||
"spells": spellMap.ToSlice(),
|
||||
"action": sess.GetFlashMessage("action"),
|
||||
"mon_action": sess.GetFlashMessage("mon_action"),
|
||||
"fight": fight,
|
||||
"user": user,
|
||||
"monster": monster,
|
||||
"mon_hppct": monHpPct,
|
||||
"mon_hpcol": monHpColor,
|
||||
"spells": spellMap.ToSlice(),
|
||||
"action": sess.GetFlashMessage("action"),
|
||||
"mon_action": sess.GetFlashMessage("mon_action"),
|
||||
"last_action": lastAction,
|
||||
})
|
||||
}
|
||||
|
||||
@ -110,84 +122,85 @@ func handleFightAction(ctx sushi.Ctx) {
|
||||
}
|
||||
|
||||
action := string(ctx.FormValue("action"))
|
||||
var userAction string
|
||||
var result *actions.FightResult
|
||||
|
||||
switch action {
|
||||
case "attack":
|
||||
actions.HandleAttack(fight, user)
|
||||
userAction = fight.GetLastAction()
|
||||
result = actions.HandleAttack(fight, user)
|
||||
case "spell":
|
||||
spellIDStr := string(ctx.FormValue("spell_id"))
|
||||
if spellID, err := strconv.Atoi(spellIDStr); err == nil {
|
||||
actions.HandleSpell(fight, user, spellID)
|
||||
userAction = fight.GetLastAction()
|
||||
result = actions.HandleSpell(fight, user, spellID)
|
||||
} else {
|
||||
result = &actions.FightResult{
|
||||
LogAction: func() error { return fightlogs.AddAction(fight.ID, "Invalid spell!") },
|
||||
}
|
||||
}
|
||||
case "run":
|
||||
actions.HandleRun(fight, user)
|
||||
userAction = fight.GetLastAction()
|
||||
|
||||
// If successfully ran away, redirect to explore
|
||||
if fight.RanAway {
|
||||
user.Currently = "Exploring"
|
||||
user.Save()
|
||||
sess.SetFlash("success", "You successfully escaped!")
|
||||
ctx.Redirect("/explore", 302)
|
||||
return
|
||||
}
|
||||
result = actions.HandleRun(fight, user)
|
||||
default:
|
||||
fight.AddAction("Invalid action!")
|
||||
userAction = "Invalid action!"
|
||||
result = &actions.FightResult{
|
||||
LogAction: func() error { return fightlogs.AddAction(fight.ID, "Invalid action!") },
|
||||
}
|
||||
}
|
||||
|
||||
// Flash user action
|
||||
sess.SetFlash("action", userAction)
|
||||
// Execute the action
|
||||
err = actions.ExecuteFightAction(fight.ID, result)
|
||||
if err != nil {
|
||||
ctx.SendError(500, "Failed to execute fight action")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if fight ended due to user action
|
||||
if fight.Victory {
|
||||
if fight.Won {
|
||||
// Player won
|
||||
sess.SetFlash("success", fmt.Sprintf("Victory! You gained %d gold and %d experience!", fight.RewardGold, fight.RewardExp))
|
||||
sess.DeleteFlash("action")
|
||||
sess.DeleteFlash("mon_action")
|
||||
// Handle fight end states
|
||||
if result.Ended {
|
||||
if result.Won {
|
||||
sess.SetFlash("success", fmt.Sprintf("Victory! You gained rewards!"))
|
||||
ctx.Redirect("/explore", 302)
|
||||
} else {
|
||||
// Player lost
|
||||
} else if result.Victory {
|
||||
sess.SetFlash("error", "You have been defeated! You lost some gold and were sent to the nearest town.")
|
||||
sess.DeleteFlash("action")
|
||||
sess.DeleteFlash("mon_action")
|
||||
ctx.Redirect("/town", 302)
|
||||
} else {
|
||||
// Ran away
|
||||
sess.SetFlash("success", "You successfully escaped!")
|
||||
ctx.Redirect("/explore", 302)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Monster attacks back if fight is still active
|
||||
// Monster attacks back if fight continues
|
||||
if fight.IsActive() && user.HP > 0 {
|
||||
actions.HandleMonsterAttack(fight, user)
|
||||
monsterResult := actions.HandleMonsterAttack(fight, user)
|
||||
|
||||
// Check if fight ended due to monster attack
|
||||
if fight.Victory {
|
||||
if fight.Won {
|
||||
sess.SetFlash("success", fmt.Sprintf("Victory! You gained %d gold and %d experience!", fight.RewardGold, fight.RewardExp))
|
||||
sess.DeleteFlash("action")
|
||||
sess.DeleteFlash("mon_action")
|
||||
// Execute monster action
|
||||
err = actions.ExecuteFightAction(fight.ID, monsterResult)
|
||||
if err != nil {
|
||||
ctx.SendError(500, "Failed to execute monster action")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if monster action ended fight
|
||||
if monsterResult.Ended {
|
||||
if monsterResult.Won {
|
||||
sess.SetFlash("success", "Victory!")
|
||||
ctx.Redirect("/explore", 302)
|
||||
} else {
|
||||
sess.SetFlash("error", "You have been defeated! You lost some gold and were sent to the nearest town.")
|
||||
sess.DeleteFlash("action")
|
||||
sess.DeleteFlash("mon_action")
|
||||
sess.SetFlash("error", "You have been defeated!")
|
||||
ctx.Redirect("/town", 302)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
monsterAction := fight.GetLastAction()
|
||||
sess.SetFlash("mon_action", monsterAction)
|
||||
}
|
||||
|
||||
fight.IncrementTurn()
|
||||
fight.Save()
|
||||
user.Save()
|
||||
// Increment turn
|
||||
err = database.Transaction(func() error {
|
||||
return database.Update("fights", map[string]any{
|
||||
"turn": fight.Turn + 1,
|
||||
}, "id", fight.ID)
|
||||
})
|
||||
if err != nil {
|
||||
ctx.SendError(500, "Failed to increment turn")
|
||||
return
|
||||
}
|
||||
|
||||
// Redirect back to fight page
|
||||
ctx.Redirect("/fight", 302)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package routes
|
||||
import (
|
||||
"dk/internal/actions"
|
||||
"dk/internal/components"
|
||||
"dk/internal/database"
|
||||
"dk/internal/models/towns"
|
||||
"dk/internal/models/users"
|
||||
"slices"
|
||||
@ -53,9 +54,18 @@ func Move(ctx sushi.Ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
user.Currently = currently
|
||||
user.SetPosition(newX, newY)
|
||||
user.Save()
|
||||
err = database.Transaction(func() error {
|
||||
return database.Update("users", map[string]any{
|
||||
"currently": currently,
|
||||
"x": newX,
|
||||
"y": newY,
|
||||
}, "id", user.ID)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
ctx.SendError(500, "failed to update user position")
|
||||
return
|
||||
}
|
||||
|
||||
switch currently {
|
||||
case "In Town":
|
||||
@ -101,10 +111,20 @@ func Teleport(ctx sushi.Ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
user.TP -= town.TPCost
|
||||
user.SetPosition(town.X, town.Y)
|
||||
user.Currently = "In Town"
|
||||
user.Save()
|
||||
err = database.Transaction(func() error {
|
||||
return database.Update("users", map[string]any{
|
||||
"tp": user.TP - town.TPCost,
|
||||
"x": town.X,
|
||||
"y": town.Y,
|
||||
"currently": "In Town",
|
||||
}, "id", user.ID)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
sess.SetFlash("error", "Failed to complete teleport.")
|
||||
ctx.Redirect("/")
|
||||
return
|
||||
}
|
||||
|
||||
sess.SetFlash("success", "You teleported to "+town.Name+" successfully!")
|
||||
ctx.Redirect("/town")
|
||||
|
@ -3,6 +3,7 @@ package routes
|
||||
import (
|
||||
"dk/internal/actions"
|
||||
"dk/internal/components"
|
||||
"dk/internal/database"
|
||||
"dk/internal/helpers"
|
||||
"dk/internal/models/items"
|
||||
"dk/internal/models/towns"
|
||||
@ -93,9 +94,20 @@ func rest(ctx sushi.Ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
user.Gold -= town.InnCost
|
||||
user.HP, user.MP, user.TP = user.MaxHP, user.MaxMP, user.MaxTP
|
||||
user.Save()
|
||||
err := database.Transaction(func() error {
|
||||
return database.Update("users", map[string]any{
|
||||
"gold": user.Gold - town.InnCost,
|
||||
"hp": user.MaxHP,
|
||||
"mp": user.MaxMP,
|
||||
"tp": user.MaxTP,
|
||||
}, "id", user.ID)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
sess.SetFlash("error", "Failed to rest at inn.")
|
||||
ctx.Redirect("/town/inn")
|
||||
return
|
||||
}
|
||||
|
||||
components.RenderPage(ctx, town.Name+" Inn", "town/inn.html", map[string]any{
|
||||
"town": town,
|
||||
@ -158,9 +170,28 @@ func buyItem(ctx sushi.Ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
user.Gold -= item.Value
|
||||
actions.UserEquipItem(user, item)
|
||||
user.Save()
|
||||
// Get equipment updates from actions
|
||||
equipUpdates := actions.UserEquipItem(user, item)
|
||||
|
||||
err = database.Transaction(func() error {
|
||||
// Start with gold deduction
|
||||
updates := map[string]any{
|
||||
"gold": user.Gold - item.Value,
|
||||
}
|
||||
|
||||
// Add equipment updates
|
||||
for field, value := range equipUpdates {
|
||||
updates[field] = value
|
||||
}
|
||||
|
||||
return database.Update("users", updates, "id", user.ID)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
sess.SetFlash("error", "Failed to purchase item.")
|
||||
ctx.Redirect("/town/shop")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Redirect("/town/shop")
|
||||
}
|
||||
@ -228,11 +259,22 @@ func buyMap(ctx sushi.Ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
user.Gold -= mapped.MapCost
|
||||
townIDs := user.GetTownIDs()
|
||||
townIDs = append(townIDs, id)
|
||||
user.SetTownIDs(townIDs)
|
||||
user.Save()
|
||||
newTownsString := helpers.IntsToString(townIDs)
|
||||
|
||||
err = database.Transaction(func() error {
|
||||
return database.Update("users", map[string]any{
|
||||
"gold": user.Gold - mapped.MapCost,
|
||||
"towns": newTownsString,
|
||||
}, "id", user.ID)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
sess.SetFlash("error", "Failed to purchase map.")
|
||||
ctx.Redirect("/town/maps")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Redirect("/town/maps")
|
||||
}
|
||||
|
7
main.go
7
main.go
@ -9,6 +9,7 @@ import (
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"dk/internal/database"
|
||||
"dk/internal/models/users"
|
||||
"dk/internal/routes"
|
||||
"dk/internal/template"
|
||||
@ -55,6 +56,12 @@ func start(port string) error {
|
||||
return fmt.Errorf("failed to get current working directory: %w", err)
|
||||
}
|
||||
|
||||
err = database.Init(filepath.Join(cwd, "data/dk.db"))
|
||||
if err != nil {
|
||||
log.Fatal("Failed to initialize database:", err)
|
||||
}
|
||||
defer database.DB().Close()
|
||||
|
||||
template.InitializeCache(cwd)
|
||||
|
||||
app := sushi.New()
|
||||
|
Loading…
x
Reference in New Issue
Block a user