diff --git a/.gitignore b/.gitignore index 8ca8fe2e6b..2dfe7084fd 100644 --- a/.gitignore +++ b/.gitignore @@ -30,8 +30,9 @@ run.sh maps screenshots todo.txt -armorstats.csv -weaponstats.csv + +# Export data +*.csv # Game data *.sqlite diff --git a/CHANGELOG.md b/CHANGELOG.md index 9540993706..f037571a06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - - New level of detail feature, letting you see all the world's terrain at any view distance. - Point and directional lights now cast realistic shadows, using shadow mapping. - Added leaf and chimney particles @@ -35,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved projectile physics - Improved overhead aiming - Figure meshing no longer blocks the main thread. +- Overhauled persistence layer including no longer storing serialized JSON items in the database ### Removed diff --git a/Cargo.lock b/Cargo.lock index 98b7f50da7..42c70e9945 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4670,6 +4670,7 @@ dependencies = [ "futures-timer", "futures-util", "hashbrown", + "itertools", "lazy_static", "libsqlite3-sys", "portpicker", @@ -4752,7 +4753,6 @@ dependencies = [ "tracing-appender", "tracing-log", "tracing-subscriber", - "tracing-tracy", "treeculler", "uvth 3.1.1", "vek 0.12.0", diff --git a/assets/common/items/armor/back/admin.ron b/assets/common/items/armor/back/admin.ron index edf3262af2..ddb67b7503 100644 --- a/assets/common/items/armor/back/admin.ron +++ b/assets/common/items/armor/back/admin.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Admin's Cape", description: "With great power comes\ngreat responsibility.", kind: Armor( diff --git a/assets/common/items/armor/back/dungeon_purple-0.ron b/assets/common/items/armor/back/dungeon_purple-0.ron index f0c7c12f3d..ae1a07913f 100644 --- a/assets/common/items/armor/back/dungeon_purple-0.ron +++ b/assets/common/items/armor/back/dungeon_purple-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Cultist Cape", description: "Smells like dark magic and candles.", kind: Armor( diff --git a/assets/common/items/armor/back/leather_adventurer.ron b/assets/common/items/armor/back/leather_adventurer.ron index 75b55045d4..4f41259877 100644 --- a/assets/common/items/armor/back/leather_adventurer.ron +++ b/assets/common/items/armor/back/leather_adventurer.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Agile Cape", description: "'Tightly packed pieces of leather to endure all weather.'", kind: Armor( diff --git a/assets/common/items/armor/back/short_0.ron b/assets/common/items/armor/back/short_0.ron index aefa27f930..e479a37b62 100644 --- a/assets/common/items/armor/back/short_0.ron +++ b/assets/common/items/armor/back/short_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Short leather Cape", description: "Probably made of the finest leather.", kind: Armor( diff --git a/assets/common/items/armor/back/short_1.ron b/assets/common/items/armor/back/short_1.ron index 800c7ca622..f4c3c41137 100644 --- a/assets/common/items/armor/back/short_1.ron +++ b/assets/common/items/armor/back/short_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Green Blanket", description: "Keeps your shoulders warm.", kind: Armor( diff --git a/assets/common/items/armor/belt/assassin.ron b/assets/common/items/armor/belt/assassin.ron index f358a6c6d6..4471f9a76a 100644 --- a/assets/common/items/armor/belt/assassin.ron +++ b/assets/common/items/armor/belt/assassin.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Assassin Belt", description: "Only the best for a member of the creed.", kind: Armor( diff --git a/assets/common/items/armor/belt/bonerattler.ron b/assets/common/items/armor/belt/bonerattler.ron index 808e808942..979c5162c5 100644 --- a/assets/common/items/armor/belt/bonerattler.ron +++ b/assets/common/items/armor/belt/bonerattler.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Bonerattler Belt", description: "Made from the strongest bones.", kind: Armor( diff --git a/assets/common/items/armor/belt/cloth_blue_0.ron b/assets/common/items/armor/belt/cloth_blue_0.ron index 91a654529c..a3014d4d9f 100644 --- a/assets/common/items/armor/belt/cloth_blue_0.ron +++ b/assets/common/items/armor/belt/cloth_blue_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Linen Belt", description: "Soft and warm", kind: Armor( diff --git a/assets/common/items/armor/belt/cloth_green_0.ron b/assets/common/items/armor/belt/cloth_green_0.ron index 575a65365b..5ae50946cc 100644 --- a/assets/common/items/armor/belt/cloth_green_0.ron +++ b/assets/common/items/armor/belt/cloth_green_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Green Linen Belt", description: "Soft and warm.", kind: Armor( diff --git a/assets/common/items/armor/belt/cloth_purple_0.ron b/assets/common/items/armor/belt/cloth_purple_0.ron index 5a631bcb34..29d98cbca0 100644 --- a/assets/common/items/armor/belt/cloth_purple_0.ron +++ b/assets/common/items/armor/belt/cloth_purple_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Linen Belt", description: "Soft and warm.", kind: Armor( diff --git a/assets/common/items/armor/belt/cultist_belt.ron b/assets/common/items/armor/belt/cultist_belt.ron index 6b856636c6..d2551046de 100644 --- a/assets/common/items/armor/belt/cultist_belt.ron +++ b/assets/common/items/armor/belt/cultist_belt.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Cultist Belt", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/armor/belt/druid.ron b/assets/common/items/armor/belt/druid.ron index 9d66177f7d..cf1a2c3584 100644 --- a/assets/common/items/armor/belt/druid.ron +++ b/assets/common/items/armor/belt/druid.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Druid's Belt", description: "Twisted vines to keep everything secure.", kind: Armor( diff --git a/assets/common/items/armor/belt/leather_0.ron b/assets/common/items/armor/belt/leather_0.ron index fc945f685a..d1f49eb3a0 100644 --- a/assets/common/items/armor/belt/leather_0.ron +++ b/assets/common/items/armor/belt/leather_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Swift Belt", description: "Swift like the wind.", kind: Armor( diff --git a/assets/common/items/armor/belt/leather_2.ron b/assets/common/items/armor/belt/leather_2.ron index 02e85824f3..7600500e64 100644 --- a/assets/common/items/armor/belt/leather_2.ron +++ b/assets/common/items/armor/belt/leather_2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leather Belt", description: "A belt made from simple leather.", kind: Armor( diff --git a/assets/common/items/armor/belt/leather_adventurer.ron b/assets/common/items/armor/belt/leather_adventurer.ron index fb17f34099..113c4f604e 100644 --- a/assets/common/items/armor/belt/leather_adventurer.ron +++ b/assets/common/items/armor/belt/leather_adventurer.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Agile Belt", description: "'Tightly packed pieces of leather to endure all weather.'", kind: Armor( diff --git a/assets/common/items/armor/belt/plate_0.ron b/assets/common/items/armor/belt/plate_0.ron index d431365290..ce90a4e377 100644 --- a/assets/common/items/armor/belt/plate_0.ron +++ b/assets/common/items/armor/belt/plate_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron Belt", description: "A belt with a buckle forged from iron.", kind: Armor( diff --git a/assets/common/items/armor/belt/steel_0.ron b/assets/common/items/armor/belt/steel_0.ron index 9a4c6e7532..2ffc596d5a 100644 --- a/assets/common/items/armor/belt/steel_0.ron +++ b/assets/common/items/armor/belt/steel_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Steel Belt", description: "A belt forged from steel.", kind: Armor( diff --git a/assets/common/items/armor/belt/tarasque.ron b/assets/common/items/armor/belt/tarasque.ron index 848722dd4a..2be3efd473 100644 --- a/assets/common/items/armor/belt/tarasque.ron +++ b/assets/common/items/armor/belt/tarasque.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Tarasque Belt", description: "As strong as a tarasque shell.", kind: Armor( diff --git a/assets/common/items/armor/belt/twig.ron b/assets/common/items/armor/belt/twig.ron index bd6dba50b4..b9cc98d976 100644 --- a/assets/common/items/armor/belt/twig.ron +++ b/assets/common/items/armor/belt/twig.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Twig Belt", description: "A belt made from woven from twigs.", kind: Armor( diff --git a/assets/common/items/armor/belt/twigsflowers.ron b/assets/common/items/armor/belt/twigsflowers.ron index d24c75bc01..07d5b8a11c 100644 --- a/assets/common/items/armor/belt/twigsflowers.ron +++ b/assets/common/items/armor/belt/twigsflowers.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Flowery Belt", description: "A belt woven from twigs and flowers.", kind: Armor( diff --git a/assets/common/items/armor/belt/twigsleaves.ron b/assets/common/items/armor/belt/twigsleaves.ron index 0c825bbf84..a75b9f06ff 100644 --- a/assets/common/items/armor/belt/twigsleaves.ron +++ b/assets/common/items/armor/belt/twigsleaves.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leafy Belt", description: "A belt woven from twigs and leaves.", kind: Armor( diff --git a/assets/common/items/armor/chest/assassin.ron b/assets/common/items/armor/chest/assassin.ron index 01d0d61fef..2d05941b9a 100644 --- a/assets/common/items/armor/chest/assassin.ron +++ b/assets/common/items/armor/chest/assassin.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Assassin Chest", description: "Only the best for a member of the creed.", kind: Armor( diff --git a/assets/common/items/armor/chest/bonerattler.ron b/assets/common/items/armor/chest/bonerattler.ron index ddc4e00e75..557ec72cf2 100644 --- a/assets/common/items/armor/chest/bonerattler.ron +++ b/assets/common/items/armor/chest/bonerattler.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Bonerattler Cuirass", description: "Made from the strongest bones.", kind: Armor( diff --git a/assets/common/items/armor/chest/cloth_blue_0.ron b/assets/common/items/armor/chest/cloth_blue_0.ron index c89ff2328a..0adf472efc 100644 --- a/assets/common/items/armor/chest/cloth_blue_0.ron +++ b/assets/common/items/armor/chest/cloth_blue_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Linen Chest", description: "Soft and warm.", kind: Armor( diff --git a/assets/common/items/armor/chest/cloth_green_0.ron b/assets/common/items/armor/chest/cloth_green_0.ron index 440448e38d..c79232e0ef 100644 --- a/assets/common/items/armor/chest/cloth_green_0.ron +++ b/assets/common/items/armor/chest/cloth_green_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Green Linen Chest", description: "Soft and warm.", kind: Armor( diff --git a/assets/common/items/armor/chest/cloth_purple_0.ron b/assets/common/items/armor/chest/cloth_purple_0.ron index e2e5e967b9..4d9c2657da 100644 --- a/assets/common/items/armor/chest/cloth_purple_0.ron +++ b/assets/common/items/armor/chest/cloth_purple_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Linen Chest", description: "Soft and warm.", kind: Armor( diff --git a/assets/common/items/armor/chest/cultist_chest_blue.ron b/assets/common/items/armor/chest/cultist_chest_blue.ron index 42cb920efe..62e38ac5e7 100644 --- a/assets/common/items/armor/chest/cultist_chest_blue.ron +++ b/assets/common/items/armor/chest/cultist_chest_blue.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Cultist Chest", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/armor/chest/cultist_chest_purple.ron b/assets/common/items/armor/chest/cultist_chest_purple.ron index cd9412ce5b..992d05b7b2 100644 --- a/assets/common/items/armor/chest/cultist_chest_purple.ron +++ b/assets/common/items/armor/chest/cultist_chest_purple.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Cultist Chest", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/armor/chest/druid.ron b/assets/common/items/armor/chest/druid.ron index 24bc889fcc..bf8c1dd1b5 100644 --- a/assets/common/items/armor/chest/druid.ron +++ b/assets/common/items/armor/chest/druid.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Druid's Vest", description: "Druidic chest wrappings.", kind: Armor( diff --git a/assets/common/items/armor/chest/leather_0.ron b/assets/common/items/armor/chest/leather_0.ron index 174fec8fb3..e1c6cae15b 100644 --- a/assets/common/items/armor/chest/leather_0.ron +++ b/assets/common/items/armor/chest/leather_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Swift Chest", description: "Swift like the wind.", kind: Armor( diff --git a/assets/common/items/armor/chest/leather_2.ron b/assets/common/items/armor/chest/leather_2.ron index f7fcf151db..260a53f0b2 100644 --- a/assets/common/items/armor/chest/leather_2.ron +++ b/assets/common/items/armor/chest/leather_2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leather Cuirass", description: "A cuirass made of simple leather.", kind: Armor( diff --git a/assets/common/items/armor/chest/leather_adventurer.ron b/assets/common/items/armor/chest/leather_adventurer.ron index 705a045221..3a63dc1e10 100644 --- a/assets/common/items/armor/chest/leather_adventurer.ron +++ b/assets/common/items/armor/chest/leather_adventurer.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Agile Chest", description: "'Tightly packed pieces of leather to endure all weather.'", kind: Armor( diff --git a/assets/common/items/armor/chest/plate_green_0.ron b/assets/common/items/armor/chest/plate_green_0.ron index 7fae0ffd43..6c35dc9d00 100644 --- a/assets/common/items/armor/chest/plate_green_0.ron +++ b/assets/common/items/armor/chest/plate_green_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron Chestplate", description: "A chestplate forged from iron.", kind: Armor( diff --git a/assets/common/items/armor/chest/steel_0.ron b/assets/common/items/armor/chest/steel_0.ron index 9da979fa01..b87220681d 100644 --- a/assets/common/items/armor/chest/steel_0.ron +++ b/assets/common/items/armor/chest/steel_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Steel Cuirass", description: "A cuirass of steel plate.", kind: Armor( diff --git a/assets/common/items/armor/chest/tarasque.ron b/assets/common/items/armor/chest/tarasque.ron index ef73a183e7..ad8f6513ad 100644 --- a/assets/common/items/armor/chest/tarasque.ron +++ b/assets/common/items/armor/chest/tarasque.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Tarasque Cuirass", description: "As strong as a tarasque shell.", kind: Armor( diff --git a/assets/common/items/armor/chest/twig.ron b/assets/common/items/armor/chest/twig.ron index a102d1bc2c..c38f319cac 100644 --- a/assets/common/items/armor/chest/twig.ron +++ b/assets/common/items/armor/chest/twig.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Twig Shirt", description: "A shirt woven from twigs.", kind: Armor( diff --git a/assets/common/items/armor/chest/twigsflowers.ron b/assets/common/items/armor/chest/twigsflowers.ron index 782c3e4e95..b993a99469 100644 --- a/assets/common/items/armor/chest/twigsflowers.ron +++ b/assets/common/items/armor/chest/twigsflowers.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Flowery Shirt", description: "A shirt woven from twigs and flowers.", kind: Armor( diff --git a/assets/common/items/armor/chest/twigsleaves.ron b/assets/common/items/armor/chest/twigsleaves.ron index eb0e8f436c..a429c0ec3e 100644 --- a/assets/common/items/armor/chest/twigsleaves.ron +++ b/assets/common/items/armor/chest/twigsleaves.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leafy Shirt", description: "A shirt woven from twigs and leaves.", kind: Armor( diff --git a/assets/common/items/armor/chest/worker_green_0.ron b/assets/common/items/armor/chest/worker_green_0.ron index 43abc54500..fc8e617f83 100644 --- a/assets/common/items/armor/chest/worker_green_0.ron +++ b/assets/common/items/armor/chest/worker_green_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Green Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/armor/chest/worker_green_1.ron b/assets/common/items/armor/chest/worker_green_1.ron index 44e9eada14..4501fced23 100644 --- a/assets/common/items/armor/chest/worker_green_1.ron +++ b/assets/common/items/armor/chest/worker_green_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Green Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/armor/chest/worker_orange_0.ron b/assets/common/items/armor/chest/worker_orange_0.ron index 134e60c192..bb9cfb0831 100644 --- a/assets/common/items/armor/chest/worker_orange_0.ron +++ b/assets/common/items/armor/chest/worker_orange_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Orange Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/armor/chest/worker_orange_1.ron b/assets/common/items/armor/chest/worker_orange_1.ron index bf0aef7ba2..a432d6055b 100644 --- a/assets/common/items/armor/chest/worker_orange_1.ron +++ b/assets/common/items/armor/chest/worker_orange_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Orange Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/armor/chest/worker_purple_0.ron b/assets/common/items/armor/chest/worker_purple_0.ron index d0ff228e31..7fbb7fb079 100644 --- a/assets/common/items/armor/chest/worker_purple_0.ron +++ b/assets/common/items/armor/chest/worker_purple_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/armor/chest/worker_purple_1.ron b/assets/common/items/armor/chest/worker_purple_1.ron index 604ae3f656..f2315fbcf3 100644 --- a/assets/common/items/armor/chest/worker_purple_1.ron +++ b/assets/common/items/armor/chest/worker_purple_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/armor/chest/worker_red_0.ron b/assets/common/items/armor/chest/worker_red_0.ron index 859d8eb079..9a2314a798 100644 --- a/assets/common/items/armor/chest/worker_red_0.ron +++ b/assets/common/items/armor/chest/worker_red_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Red Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/armor/chest/worker_red_1.ron b/assets/common/items/armor/chest/worker_red_1.ron index 5f2f28d20e..860c7ca5ce 100644 --- a/assets/common/items/armor/chest/worker_red_1.ron +++ b/assets/common/items/armor/chest/worker_red_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Red Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/armor/chest/worker_yellow_0.ron b/assets/common/items/armor/chest/worker_yellow_0.ron index 37d2504917..8b0f1ea412 100644 --- a/assets/common/items/armor/chest/worker_yellow_0.ron +++ b/assets/common/items/armor/chest/worker_yellow_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Yellow Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/armor/chest/worker_yellow_1.ron b/assets/common/items/armor/chest/worker_yellow_1.ron index 5a3dde6a0f..dfc9ee8f19 100644 --- a/assets/common/items/armor/chest/worker_yellow_1.ron +++ b/assets/common/items/armor/chest/worker_yellow_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Yellow Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/armor/foot/assassin.ron b/assets/common/items/armor/foot/assassin.ron index 15eb08bc9c..e3493be3df 100644 --- a/assets/common/items/armor/foot/assassin.ron +++ b/assets/common/items/armor/foot/assassin.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Assassin Boots", description: "Only the best for a member of the creed.", kind: Armor( diff --git a/assets/common/items/armor/foot/bonerattler.ron b/assets/common/items/armor/foot/bonerattler.ron index 8226749815..328dcd0399 100644 --- a/assets/common/items/armor/foot/bonerattler.ron +++ b/assets/common/items/armor/foot/bonerattler.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Bonerattler Boots", description: "Made from the strongest bones.", kind: Armor( diff --git a/assets/common/items/armor/foot/cloth_blue_0.ron b/assets/common/items/armor/foot/cloth_blue_0.ron index f361b3ca64..7e3c750f8b 100644 --- a/assets/common/items/armor/foot/cloth_blue_0.ron +++ b/assets/common/items/armor/foot/cloth_blue_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Linen Boots", description: "Soft and warm.", kind: Armor( diff --git a/assets/common/items/armor/foot/cloth_green_0.ron b/assets/common/items/armor/foot/cloth_green_0.ron index a93045f877..f9bbdf8ddc 100644 --- a/assets/common/items/armor/foot/cloth_green_0.ron +++ b/assets/common/items/armor/foot/cloth_green_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Green Linen Boots", description: "Soft and warm.", kind: Armor( diff --git a/assets/common/items/armor/foot/cloth_purple_0.ron b/assets/common/items/armor/foot/cloth_purple_0.ron index 01fb8e1fe1..7ec9ae0b17 100644 --- a/assets/common/items/armor/foot/cloth_purple_0.ron +++ b/assets/common/items/armor/foot/cloth_purple_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Linen Boots", description: "Soft and warm.", kind: Armor( diff --git a/assets/common/items/armor/foot/cultist_boots.ron b/assets/common/items/armor/foot/cultist_boots.ron index 6aff96c399..443dfc26ad 100644 --- a/assets/common/items/armor/foot/cultist_boots.ron +++ b/assets/common/items/armor/foot/cultist_boots.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Cultist Boots", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/armor/foot/druid.ron b/assets/common/items/armor/foot/druid.ron index e99824ed97..94fa75b929 100644 --- a/assets/common/items/armor/foot/druid.ron +++ b/assets/common/items/armor/foot/druid.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Druid's Slippers", description: "For treading softly through the woods.", kind: Armor( diff --git a/assets/common/items/armor/foot/jackalope_slippers.ron b/assets/common/items/armor/foot/jackalope_slippers.ron index 82b52e87a7..99e3618cb1 100644 --- a/assets/common/items/armor/foot/jackalope_slippers.ron +++ b/assets/common/items/armor/foot/jackalope_slippers.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Fluffy Jackalope Slippers", description: "So warm and cozy!", kind: Armor( diff --git a/assets/common/items/armor/foot/leather_0.ron b/assets/common/items/armor/foot/leather_0.ron index a1bec22ea0..15b449b76a 100644 --- a/assets/common/items/armor/foot/leather_0.ron +++ b/assets/common/items/armor/foot/leather_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Swift Boots", description: "Swift like the wind.", kind: Armor( diff --git a/assets/common/items/armor/foot/leather_2.ron b/assets/common/items/armor/foot/leather_2.ron index f3f397e64d..f6d064dcc7 100644 --- a/assets/common/items/armor/foot/leather_2.ron +++ b/assets/common/items/armor/foot/leather_2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leather Boots", description: "Boots made of simple leather.", kind: Armor( diff --git a/assets/common/items/armor/foot/leather_adventurer.ron b/assets/common/items/armor/foot/leather_adventurer.ron index 1b4dea02cd..a152923ddd 100644 --- a/assets/common/items/armor/foot/leather_adventurer.ron +++ b/assets/common/items/armor/foot/leather_adventurer.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Agile Kickers", description: "'Tightly packed pieces of leather to endure all weather.", kind: Armor( diff --git a/assets/common/items/armor/foot/plate_0.ron b/assets/common/items/armor/foot/plate_0.ron index 50426aa112..93ae1ab726 100644 --- a/assets/common/items/armor/foot/plate_0.ron +++ b/assets/common/items/armor/foot/plate_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron Feet", description: "Boots forged from iron.", kind: Armor( diff --git a/assets/common/items/armor/foot/steel_0.ron b/assets/common/items/armor/foot/steel_0.ron index 8e946cbe82..e863c92556 100644 --- a/assets/common/items/armor/foot/steel_0.ron +++ b/assets/common/items/armor/foot/steel_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Steel Boots", description: "Boots forged from steel.", kind: Armor( diff --git a/assets/common/items/armor/foot/tarasque.ron b/assets/common/items/armor/foot/tarasque.ron index 11ad855d63..6a05c527b6 100644 --- a/assets/common/items/armor/foot/tarasque.ron +++ b/assets/common/items/armor/foot/tarasque.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Tarasque Boots", description: "As strong as a tarasque shell.", kind: Armor( diff --git a/assets/common/items/armor/foot/twig.ron b/assets/common/items/armor/foot/twig.ron index 7a933269a6..79400e136e 100644 --- a/assets/common/items/armor/foot/twig.ron +++ b/assets/common/items/armor/foot/twig.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Twig Boots", description: "Boots woven from twigs.", kind: Armor( diff --git a/assets/common/items/armor/foot/twigsflowers.ron b/assets/common/items/armor/foot/twigsflowers.ron index 9cd370cefd..1196276e2d 100644 --- a/assets/common/items/armor/foot/twigsflowers.ron +++ b/assets/common/items/armor/foot/twigsflowers.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Flowery Boots", description: "Boots woven from twigs and flowers.", kind: Armor( diff --git a/assets/common/items/armor/foot/twigsleaves.ron b/assets/common/items/armor/foot/twigsleaves.ron index 5bd146b70a..f2a0dd015c 100644 --- a/assets/common/items/armor/foot/twigsleaves.ron +++ b/assets/common/items/armor/foot/twigsleaves.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leafy Boots", description: "Boots woven from twigs and leaves.", kind: Armor( diff --git a/assets/common/items/armor/hand/assassin.ron b/assets/common/items/armor/hand/assassin.ron index 10a58f0798..43c2014529 100644 --- a/assets/common/items/armor/hand/assassin.ron +++ b/assets/common/items/armor/hand/assassin.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Assassin Gloves", description: "Only the best for a member of the creed.", kind: Armor( diff --git a/assets/common/items/armor/hand/bonerattler.ron b/assets/common/items/armor/hand/bonerattler.ron index 46d6816db8..19369a425a 100644 --- a/assets/common/items/armor/hand/bonerattler.ron +++ b/assets/common/items/armor/hand/bonerattler.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Bonerattler Gauntlets", description: "Made from the strongest bones.", kind: Armor( diff --git a/assets/common/items/armor/hand/cloth_blue_0.ron b/assets/common/items/armor/hand/cloth_blue_0.ron index 36d46658dc..509833940f 100644 --- a/assets/common/items/armor/hand/cloth_blue_0.ron +++ b/assets/common/items/armor/hand/cloth_blue_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Linen Wrists", description: "A strip of cloth.", kind: Armor( diff --git a/assets/common/items/armor/hand/cloth_green_0.ron b/assets/common/items/armor/hand/cloth_green_0.ron index 92ca42477e..d6686bb76f 100644 --- a/assets/common/items/armor/hand/cloth_green_0.ron +++ b/assets/common/items/armor/hand/cloth_green_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Green Linen Wrists", description: "A strip of cloth.", kind: Armor( diff --git a/assets/common/items/armor/hand/cloth_purple_0.ron b/assets/common/items/armor/hand/cloth_purple_0.ron index 12cda36497..b1f7ac3e9f 100644 --- a/assets/common/items/armor/hand/cloth_purple_0.ron +++ b/assets/common/items/armor/hand/cloth_purple_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Silk Wrists", description: "A strip of cloth.", kind: Armor( diff --git a/assets/common/items/armor/hand/cultist_hands_blue.ron b/assets/common/items/armor/hand/cultist_hands_blue.ron index e47abd428c..8df0262df9 100644 --- a/assets/common/items/armor/hand/cultist_hands_blue.ron +++ b/assets/common/items/armor/hand/cultist_hands_blue.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Cultist Gloves", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/armor/hand/cultist_hands_purple.ron b/assets/common/items/armor/hand/cultist_hands_purple.ron index 73d9760b4b..bef5075d7f 100644 --- a/assets/common/items/armor/hand/cultist_hands_purple.ron +++ b/assets/common/items/armor/hand/cultist_hands_purple.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Cultist Gloves", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/armor/hand/druid.ron b/assets/common/items/armor/hand/druid.ron index dab5caa7e6..36be184719 100644 --- a/assets/common/items/armor/hand/druid.ron +++ b/assets/common/items/armor/hand/druid.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Druid's Gloves", description: "Soft, strong, and flexible.", kind: Armor( diff --git a/assets/common/items/armor/hand/leather_0.ron b/assets/common/items/armor/hand/leather_0.ron index 59d4319a28..2691929b30 100644 --- a/assets/common/items/armor/hand/leather_0.ron +++ b/assets/common/items/armor/hand/leather_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Swift Gloves", description: "Swift like the wind.", kind: Armor( diff --git a/assets/common/items/armor/hand/leather_2.ron b/assets/common/items/armor/hand/leather_2.ron index 071a7f709d..aace9eca08 100644 --- a/assets/common/items/armor/hand/leather_2.ron +++ b/assets/common/items/armor/hand/leather_2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leather Gloves", description: "Gloves made of simple leather.", kind: Armor( diff --git a/assets/common/items/armor/hand/leather_adventurer.ron b/assets/common/items/armor/hand/leather_adventurer.ron index 4989e08539..bc293c5bc6 100644 --- a/assets/common/items/armor/hand/leather_adventurer.ron +++ b/assets/common/items/armor/hand/leather_adventurer.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Agile Gauntlets", description: "'Tightly packed pieces of leather to endure all weather.'", kind: Armor( diff --git a/assets/common/items/armor/hand/plate_0.ron b/assets/common/items/armor/hand/plate_0.ron index db3c8ce145..6c6d6943eb 100644 --- a/assets/common/items/armor/hand/plate_0.ron +++ b/assets/common/items/armor/hand/plate_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron Handguards", description: "Gauntlets forged from iron.", kind: Armor( diff --git a/assets/common/items/armor/hand/steel_0.ron b/assets/common/items/armor/hand/steel_0.ron index 01e8543ac5..f19f107583 100644 --- a/assets/common/items/armor/hand/steel_0.ron +++ b/assets/common/items/armor/hand/steel_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Steel Gauntlets", description: "Gauntlets forged from steel.", kind: Armor( diff --git a/assets/common/items/armor/hand/tarasque.ron b/assets/common/items/armor/hand/tarasque.ron index b45f0e558a..fb618dc29e 100644 --- a/assets/common/items/armor/hand/tarasque.ron +++ b/assets/common/items/armor/hand/tarasque.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Tarasque Gauntlets", description: "As strong as a tarasque shell.", kind: Armor( diff --git a/assets/common/items/armor/hand/twig.ron b/assets/common/items/armor/hand/twig.ron index 7f81556e5b..a6b7af131e 100644 --- a/assets/common/items/armor/hand/twig.ron +++ b/assets/common/items/armor/hand/twig.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Twig Wraps", description: "Handwraps woven from twigs.", kind: Armor( diff --git a/assets/common/items/armor/hand/twigsflowers.ron b/assets/common/items/armor/hand/twigsflowers.ron index 58a0d1f592..61c904eb36 100644 --- a/assets/common/items/armor/hand/twigsflowers.ron +++ b/assets/common/items/armor/hand/twigsflowers.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Flowery Wraps", description: "Handwraps woven from twigs and flowers.", kind: Armor( diff --git a/assets/common/items/armor/hand/twigsleaves.ron b/assets/common/items/armor/hand/twigsleaves.ron index 6514099f0b..0004e12f11 100644 --- a/assets/common/items/armor/hand/twigsleaves.ron +++ b/assets/common/items/armor/hand/twigsleaves.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leafy Wraps", description: "Handwraps woven from twigs and leaves.", kind: Armor( diff --git a/assets/common/items/armor/head/assa_mask_0.ron b/assets/common/items/armor/head/assa_mask_0.ron index 469672b130..2a8a66b28e 100644 --- a/assets/common/items/armor/head/assa_mask_0.ron +++ b/assets/common/items/armor/head/assa_mask_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Dark Assassin Mask", description: "Used to obscure your face.", kind: Armor( diff --git a/assets/common/items/armor/head/leather_0.ron b/assets/common/items/armor/head/leather_0.ron index 8f545c9f45..32784d35fc 100644 --- a/assets/common/items/armor/head/leather_0.ron +++ b/assets/common/items/armor/head/leather_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Swift Leather Cap", description: "Swift like the wind.", kind: Armor( diff --git a/assets/common/items/armor/neck/neck_0.ron b/assets/common/items/armor/neck/neck_0.ron index 12c315a85a..e33fc76029 100644 --- a/assets/common/items/armor/neck/neck_0.ron +++ b/assets/common/items/armor/neck/neck_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Plain Necklace", description: "It's become tarnished with age.", kind: Armor( diff --git a/assets/common/items/armor/neck/neck_1.ron b/assets/common/items/armor/neck/neck_1.ron index a6b3f8d919..3a34c4e9b4 100644 --- a/assets/common/items/armor/neck/neck_1.ron +++ b/assets/common/items/armor/neck/neck_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Gem of lesser Protection", description: "Surrounded by a discrete magical glow.", kind: Armor( diff --git a/assets/common/items/armor/pants/assassin.ron b/assets/common/items/armor/pants/assassin.ron index 25e48f965e..fa70f18c69 100644 --- a/assets/common/items/armor/pants/assassin.ron +++ b/assets/common/items/armor/pants/assassin.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Assassin Pants", description: "Only the best for a member of the creed.", kind: Armor( diff --git a/assets/common/items/armor/pants/bonerattler.ron b/assets/common/items/armor/pants/bonerattler.ron index 17dcc30fe5..edd053aed8 100644 --- a/assets/common/items/armor/pants/bonerattler.ron +++ b/assets/common/items/armor/pants/bonerattler.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Bonerattler Chausses", description: "Made from the strongest bones.", kind: Armor( diff --git a/assets/common/items/armor/pants/cloth_blue_0.ron b/assets/common/items/armor/pants/cloth_blue_0.ron index 33b08d3d30..898b4bf5c3 100644 --- a/assets/common/items/armor/pants/cloth_blue_0.ron +++ b/assets/common/items/armor/pants/cloth_blue_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Linen Skirt", description: "A skirt made from linen.", kind: Armor( diff --git a/assets/common/items/armor/pants/cloth_green_0.ron b/assets/common/items/armor/pants/cloth_green_0.ron index 5d633daa7d..01b70b1baf 100644 --- a/assets/common/items/armor/pants/cloth_green_0.ron +++ b/assets/common/items/armor/pants/cloth_green_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Green Linen Skirt", description: "A skirt made from linen.", kind: Armor( diff --git a/assets/common/items/armor/pants/cloth_purple_0.ron b/assets/common/items/armor/pants/cloth_purple_0.ron index b49a5d2617..09f5a771d1 100644 --- a/assets/common/items/armor/pants/cloth_purple_0.ron +++ b/assets/common/items/armor/pants/cloth_purple_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Linen Skirt", description: "A skirt made from linen.", kind: Armor( diff --git a/assets/common/items/armor/pants/cultist_legs_blue.ron b/assets/common/items/armor/pants/cultist_legs_blue.ron index 8c4328afde..0faf3fe4d2 100644 --- a/assets/common/items/armor/pants/cultist_legs_blue.ron +++ b/assets/common/items/armor/pants/cultist_legs_blue.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Cultist Skirt", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/armor/pants/cultist_legs_purple.ron b/assets/common/items/armor/pants/cultist_legs_purple.ron index bf9bf3dec9..0093a7b1e0 100644 --- a/assets/common/items/armor/pants/cultist_legs_purple.ron +++ b/assets/common/items/armor/pants/cultist_legs_purple.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Cultist Skirt", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/armor/pants/druid.ron b/assets/common/items/armor/pants/druid.ron index 40c82b7d62..2dd0c212d6 100644 --- a/assets/common/items/armor/pants/druid.ron +++ b/assets/common/items/armor/pants/druid.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Druid's Kilt", description: "Feel the breeze!", kind: Armor( diff --git a/assets/common/items/armor/pants/hunting.ron b/assets/common/items/armor/pants/hunting.ron index 391aa9ef26..b186a48e7d 100644 --- a/assets/common/items/armor/pants/hunting.ron +++ b/assets/common/items/armor/pants/hunting.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Hunting Pants", description: "Crafted from soft, supple leather.", kind: Armor( diff --git a/assets/common/items/armor/pants/leather_0.ron b/assets/common/items/armor/pants/leather_0.ron index 576e451736..4f25c2139a 100644 --- a/assets/common/items/armor/pants/leather_0.ron +++ b/assets/common/items/armor/pants/leather_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Swift Pants", description: "Swift like the wind.", kind: Armor( diff --git a/assets/common/items/armor/pants/leather_2.ron b/assets/common/items/armor/pants/leather_2.ron index f50ba7df0c..340ae7c256 100644 --- a/assets/common/items/armor/pants/leather_2.ron +++ b/assets/common/items/armor/pants/leather_2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leather Leg Armour", description: "Leg armour made of simple leather.", kind: Armor( diff --git a/assets/common/items/armor/pants/leather_adventurer.ron b/assets/common/items/armor/pants/leather_adventurer.ron index 1ab8986285..7e248501fb 100644 --- a/assets/common/items/armor/pants/leather_adventurer.ron +++ b/assets/common/items/armor/pants/leather_adventurer.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Agile Pantalons", description: "'Tightly packed pieces of leather to endure all weather.'", kind: Armor( diff --git a/assets/common/items/armor/pants/plate_green_0.ron b/assets/common/items/armor/pants/plate_green_0.ron index 3417367e47..a6beb54aee 100644 --- a/assets/common/items/armor/pants/plate_green_0.ron +++ b/assets/common/items/armor/pants/plate_green_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron Legguards", description: "Greaves forged from iron.", kind: Armor( diff --git a/assets/common/items/armor/pants/steel_0.ron b/assets/common/items/armor/pants/steel_0.ron index 4cf9e17498..f66a075ac3 100644 --- a/assets/common/items/armor/pants/steel_0.ron +++ b/assets/common/items/armor/pants/steel_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Steel Chausses", description: "Greaves forged from steel.", kind: Armor( diff --git a/assets/common/items/armor/pants/tarasque.ron b/assets/common/items/armor/pants/tarasque.ron index 12e61dc342..eded2f2955 100644 --- a/assets/common/items/armor/pants/tarasque.ron +++ b/assets/common/items/armor/pants/tarasque.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Tarasque Chausses", description: "As strong as a tarasque shell.", kind: Armor( diff --git a/assets/common/items/armor/pants/twig.ron b/assets/common/items/armor/pants/twig.ron index 1c8cd3de0c..c4ee3b22b9 100644 --- a/assets/common/items/armor/pants/twig.ron +++ b/assets/common/items/armor/pants/twig.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Twig Pants", description: "Pants woven from twigs. Chafey!", kind: Armor( diff --git a/assets/common/items/armor/pants/twigsflowers.ron b/assets/common/items/armor/pants/twigsflowers.ron index 17fa75baac..cc7b636aae 100644 --- a/assets/common/items/armor/pants/twigsflowers.ron +++ b/assets/common/items/armor/pants/twigsflowers.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Flowery Pants", description: "Pants woven from twigs and flowers. Fragrant!", kind: Armor( diff --git a/assets/common/items/armor/pants/twigsleaves.ron b/assets/common/items/armor/pants/twigsleaves.ron index c2f4564a7a..2fc10c1aa9 100644 --- a/assets/common/items/armor/pants/twigsleaves.ron +++ b/assets/common/items/armor/pants/twigsleaves.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leafy Pants", description: "Pants woven from twigs and leaves. Slightly less chafey than the twig pants!", kind: Armor( diff --git a/assets/common/items/armor/pants/worker_blue_0.ron b/assets/common/items/armor/pants/worker_blue_0.ron index 0d0a52bc58..d4df1cb10c 100644 --- a/assets/common/items/armor/pants/worker_blue_0.ron +++ b/assets/common/items/armor/pants/worker_blue_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Worker Pants", description: "Pants used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/armor/ring/ring_0.ron b/assets/common/items/armor/ring/ring_0.ron index fbb464ef74..23e882d7c5 100644 --- a/assets/common/items/armor/ring/ring_0.ron +++ b/assets/common/items/armor/ring/ring_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Scratched Ring", description: "Barely fits your finger.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/assassin.ron b/assets/common/items/armor/shoulder/assassin.ron index dcdc6eedc1..9fc27bc5f5 100644 --- a/assets/common/items/armor/shoulder/assassin.ron +++ b/assets/common/items/armor/shoulder/assassin.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Assassin Shoulder Guard", description: "Only the best for a member of the creed.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/bonerattler.ron b/assets/common/items/armor/shoulder/bonerattler.ron index 892e762b45..5ae7f0311e 100644 --- a/assets/common/items/armor/shoulder/bonerattler.ron +++ b/assets/common/items/armor/shoulder/bonerattler.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Bonerattler Shoulder Pad", description: "Made from the strongest bones.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/cloth_blue_0.ron b/assets/common/items/armor/shoulder/cloth_blue_0.ron index f4a94d3830..cfcd071361 100644 --- a/assets/common/items/armor/shoulder/cloth_blue_0.ron +++ b/assets/common/items/armor/shoulder/cloth_blue_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Linen Coat", description: "A warm coat.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/cloth_blue_1.ron b/assets/common/items/armor/shoulder/cloth_blue_1.ron index 4914b0a328..3bbec8c764 100644 --- a/assets/common/items/armor/shoulder/cloth_blue_1.ron +++ b/assets/common/items/armor/shoulder/cloth_blue_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Cloth Pads", description: "Simple shoulderpads made from blue cloth.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/cloth_green_0.ron b/assets/common/items/armor/shoulder/cloth_green_0.ron index ff3bdcac2a..ebd4e2d250 100644 --- a/assets/common/items/armor/shoulder/cloth_green_0.ron +++ b/assets/common/items/armor/shoulder/cloth_green_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Green Linen Coat", description: "A warm coat.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/cloth_purple_0.ron b/assets/common/items/armor/shoulder/cloth_purple_0.ron index 1de3f3abec..96d0a10ef1 100644 --- a/assets/common/items/armor/shoulder/cloth_purple_0.ron +++ b/assets/common/items/armor/shoulder/cloth_purple_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Linen Coat", description: "A warm coat.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron b/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron index 5a90f84f13..e1d26ad467 100644 --- a/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron +++ b/assets/common/items/armor/shoulder/cultist_shoulder_blue.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Cultist Mantle", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron b/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron index ca034b2da3..966bea62c7 100644 --- a/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron +++ b/assets/common/items/armor/shoulder/cultist_shoulder_purple.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Cultist Mantle", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/druidshoulder.ron b/assets/common/items/armor/shoulder/druidshoulder.ron index 10b91f0c77..f1a3dda7c5 100644 --- a/assets/common/items/armor/shoulder/druidshoulder.ron +++ b/assets/common/items/armor/shoulder/druidshoulder.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Druid Shoulders", description: "Forged for protectors of the wild.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/iron_spikes.ron b/assets/common/items/armor/shoulder/iron_spikes.ron index 25a9d616ff..1f4b64ec9b 100644 --- a/assets/common/items/armor/shoulder/iron_spikes.ron +++ b/assets/common/items/armor/shoulder/iron_spikes.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron Spiked Pauldrons", description: "Iron shoulder pads with spikes attached.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/leather_0.ron b/assets/common/items/armor/shoulder/leather_0.ron index 00f2d48814..5b0d1391a4 100644 --- a/assets/common/items/armor/shoulder/leather_0.ron +++ b/assets/common/items/armor/shoulder/leather_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leather Pauldrons", description: "Shoulder pads made of leather.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/leather_1.ron b/assets/common/items/armor/shoulder/leather_1.ron index a5f2219657..263379eae5 100644 --- a/assets/common/items/armor/shoulder/leather_1.ron +++ b/assets/common/items/armor/shoulder/leather_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Swift Shoulderpads", description: "Swift like the wind.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/leather_2.ron b/assets/common/items/armor/shoulder/leather_2.ron index 4fc916ce73..31672f23b1 100644 --- a/assets/common/items/armor/shoulder/leather_2.ron +++ b/assets/common/items/armor/shoulder/leather_2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leather Shoulder Pad", description: "A simple shoulder pad made of leather.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/leather_adventurer.ron b/assets/common/items/armor/shoulder/leather_adventurer.ron index 0da79970e3..e4f15a71e7 100644 --- a/assets/common/items/armor/shoulder/leather_adventurer.ron +++ b/assets/common/items/armor/shoulder/leather_adventurer.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Agile Guards", description: "'Tightly packed pieces of leather to endure all weather.'", kind: Armor( diff --git a/assets/common/items/armor/shoulder/leather_iron_0.ron b/assets/common/items/armor/shoulder/leather_iron_0.ron index bacff107bf..e3e5f9daec 100644 --- a/assets/common/items/armor/shoulder/leather_iron_0.ron +++ b/assets/common/items/armor/shoulder/leather_iron_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron and Leather Spaulders", description: "Robust spaulders made from iron and leather.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/leather_iron_1.ron b/assets/common/items/armor/shoulder/leather_iron_1.ron index 9b9f241735..f7beacf8ec 100644 --- a/assets/common/items/armor/shoulder/leather_iron_1.ron +++ b/assets/common/items/armor/shoulder/leather_iron_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron and Leather Spaulders", description: "Robust spaulders made from iron and leather.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/leather_iron_2.ron b/assets/common/items/armor/shoulder/leather_iron_2.ron index 30d8913828..ed7958b9e3 100644 --- a/assets/common/items/armor/shoulder/leather_iron_2.ron +++ b/assets/common/items/armor/shoulder/leather_iron_2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron and Leather Spaulders", description: "Robust spaulders made from iron and leather.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/leather_iron_3.ron b/assets/common/items/armor/shoulder/leather_iron_3.ron index 9443680196..1256bf0e3b 100644 --- a/assets/common/items/armor/shoulder/leather_iron_3.ron +++ b/assets/common/items/armor/shoulder/leather_iron_3.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron and Leather Spaulders", description: "Robust spaulders made from iron and leather.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/leather_strips.ron b/assets/common/items/armor/shoulder/leather_strips.ron index 08c57ff11c..4ed40cdf68 100644 --- a/assets/common/items/armor/shoulder/leather_strips.ron +++ b/assets/common/items/armor/shoulder/leather_strips.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leather Strips", description: "Shoulder wraps made from leather strips.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/plate_0.ron b/assets/common/items/armor/shoulder/plate_0.ron index b8472676ae..d76977ef71 100644 --- a/assets/common/items/armor/shoulder/plate_0.ron +++ b/assets/common/items/armor/shoulder/plate_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron Shoulderguards", description: "Shoulderguards forged from iron.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/steel_0.ron b/assets/common/items/armor/shoulder/steel_0.ron index 5b30394d20..e5ab3fd4d6 100644 --- a/assets/common/items/armor/shoulder/steel_0.ron +++ b/assets/common/items/armor/shoulder/steel_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Steel Shoulder Pad", description: "A simple shoulder pad made of steel.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/tarasque.ron b/assets/common/items/armor/shoulder/tarasque.ron index 8afbfb9fcf..b75e1a81b8 100644 --- a/assets/common/items/armor/shoulder/tarasque.ron +++ b/assets/common/items/armor/shoulder/tarasque.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Tarasque Shoulder Pad", description: "As strong as a tarasque shell.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/twigs.ron b/assets/common/items/armor/shoulder/twigs.ron index 03a1531fd2..e51871fa0c 100644 --- a/assets/common/items/armor/shoulder/twigs.ron +++ b/assets/common/items/armor/shoulder/twigs.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Twiggy Shoulders", description: "Spaulders made from tightly tied twigs.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/twigsflowers.ron b/assets/common/items/armor/shoulder/twigsflowers.ron index 6fd96cf963..a8a2033d31 100644 --- a/assets/common/items/armor/shoulder/twigsflowers.ron +++ b/assets/common/items/armor/shoulder/twigsflowers.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Flowery Shoulders", description: "Flowery, leafy spaulders.", kind: Armor( diff --git a/assets/common/items/armor/shoulder/twigsleaves.ron b/assets/common/items/armor/shoulder/twigsleaves.ron index a246c6db2c..f79456a271 100644 --- a/assets/common/items/armor/shoulder/twigsleaves.ron +++ b/assets/common/items/armor/shoulder/twigsleaves.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leafy Shoulders", description: "Spaulders made from tied twigs and leaves.", kind: Armor( diff --git a/assets/common/items/armor/starter/lantern.ron b/assets/common/items/armor/starter/lantern.ron index 1b948eca89..f1a5351c40 100644 --- a/assets/common/items/armor/starter/lantern.ron +++ b/assets/common/items/armor/starter/lantern.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Black Lantern", description: "Used by city guards.", kind: Lantern( diff --git a/assets/common/items/armor/starter/rugged_chest.ron b/assets/common/items/armor/starter/rugged_chest.ron index a3ff4e6a74..0ec419453e 100644 --- a/assets/common/items/armor/starter/rugged_chest.ron +++ b/assets/common/items/armor/starter/rugged_chest.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Rugged Shirt", description: "Smells like Adventure.", kind: Armor( diff --git a/assets/common/items/armor/starter/rugged_pants.ron b/assets/common/items/armor/starter/rugged_pants.ron index 926f336c9f..c4eded3314 100644 --- a/assets/common/items/armor/starter/rugged_pants.ron +++ b/assets/common/items/armor/starter/rugged_pants.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Rugged Commoner's Pants", description: "They remind you of the old days.", kind: Armor( diff --git a/assets/common/items/armor/starter/sandals_0.ron b/assets/common/items/armor/starter/sandals_0.ron index 156b7db23e..92e3e50cdf 100644 --- a/assets/common/items/armor/starter/sandals_0.ron +++ b/assets/common/items/armor/starter/sandals_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Worn out Sandals", description: "Loyal companions.", kind: Armor( diff --git a/assets/common/items/armor/tabard/admin.ron b/assets/common/items/armor/tabard/admin.ron index feede58206..35aab0584a 100644 --- a/assets/common/items/armor/tabard/admin.ron +++ b/assets/common/items/armor/tabard/admin.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Admin's Tabard", description: "With great power comes\ngreat responsibility.", kind: Armor( diff --git a/assets/common/items/boss_drops/exp_flask.ron b/assets/common/items/boss_drops/exp_flask.ron index d96917c03a..7dca158626 100644 --- a/assets/common/items/boss_drops/exp_flask.ron +++ b/assets/common/items/boss_drops/exp_flask.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Flask of Velorite Dusk", description: "Increases Exp by 250\n\nTake with plenty of water\n\n", kind: Consumable( diff --git a/assets/common/items/boss_drops/lantern.ron b/assets/common/items/boss_drops/lantern.ron index 85b829d6ca..fe6a7dcea2 100644 --- a/assets/common/items/boss_drops/lantern.ron +++ b/assets/common/items/boss_drops/lantern.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Magic Lantern", description: "Illuminates even the darkest dungeon\nA great monster was slain for this item", kind: Lantern( diff --git a/assets/common/items/boss_drops/potions.ron b/assets/common/items/boss_drops/potions.ron index a66cbe5512..590beb62b4 100644 --- a/assets/common/items/boss_drops/potions.ron +++ b/assets/common/items/boss_drops/potions.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Potent Potion", description: "A potent healing potion.\n\nRestores 100 health on use.\n\n", kind: Consumable( diff --git a/assets/common/items/boss_drops/xp_potion.ron b/assets/common/items/boss_drops/xp_potion.ron index 68f6a3099e..95be888bb5 100644 --- a/assets/common/items/boss_drops/xp_potion.ron +++ b/assets/common/items/boss_drops/xp_potion.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Potion of Skill", description: "Provides 250 XP to the drinker\n\n", kind: Consumable( diff --git a/assets/common/items/consumable/potion_big.ron b/assets/common/items/consumable/potion_big.ron index b933c21917..3356609f24 100644 --- a/assets/common/items/consumable/potion_big.ron +++ b/assets/common/items/consumable/potion_big.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Large Potion", description: "Restores 100 Health\n\n", kind: Consumable( diff --git a/assets/common/items/consumable/potion_med.ron b/assets/common/items/consumable/potion_med.ron index 70071181f5..6a8381d2b2 100644 --- a/assets/common/items/consumable/potion_med.ron +++ b/assets/common/items/consumable/potion_med.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Medium Potion", description: "Restores 70 Health\n\n", kind: Consumable( diff --git a/assets/common/items/consumable/potion_minor.ron b/assets/common/items/consumable/potion_minor.ron index c984d90656..37da1f5ba0 100644 --- a/assets/common/items/consumable/potion_minor.ron +++ b/assets/common/items/consumable/potion_minor.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Minor Potion", description: "Restores 50 Health\n\n", kind: Consumable( diff --git a/assets/common/items/crafting_ing/empty_vial.ron b/assets/common/items/crafting_ing/empty_vial.ron index a5396a3dee..562b66cf45 100644 --- a/assets/common/items/crafting_ing/empty_vial.ron +++ b/assets/common/items/crafting_ing/empty_vial.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Empty Vial", description: "Can be filled with fluids.", kind: Ingredient( diff --git a/assets/common/items/crafting_ing/leather_scraps.ron b/assets/common/items/crafting_ing/leather_scraps.ron index efec2df6b2..11fad8a574 100644 --- a/assets/common/items/crafting_ing/leather_scraps.ron +++ b/assets/common/items/crafting_ing/leather_scraps.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Leather Scraps", description: "Used to craft various items.", kind: Ingredient( diff --git a/assets/common/items/crafting_ing/shiny_gem.ron b/assets/common/items/crafting_ing/shiny_gem.ron index aec07575c3..49ed2b5638 100644 --- a/assets/common/items/crafting_ing/shiny_gem.ron +++ b/assets/common/items/crafting_ing/shiny_gem.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Shiny Gem", description: "It's so shiny!", kind: Ingredient( diff --git a/assets/common/items/crafting_ing/stones.ron b/assets/common/items/crafting_ing/stones.ron index 27efc702a3..ed8c6cef77 100644 --- a/assets/common/items/crafting_ing/stones.ron +++ b/assets/common/items/crafting_ing/stones.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Stones", description: "Pebbles from the ground.", kind: Ingredient( diff --git a/assets/common/items/crafting_ing/twigs.ron b/assets/common/items/crafting_ing/twigs.ron index ecac83bccc..c7df435e4f 100644 --- a/assets/common/items/crafting_ing/twigs.ron +++ b/assets/common/items/crafting_ing/twigs.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Twigs", description: "Dry.", kind: Ingredient( diff --git a/assets/common/items/crafting_tools/craftsman_hammer.ron b/assets/common/items/crafting_tools/craftsman_hammer.ron index c5bfa8ce47..3c66933706 100644 --- a/assets/common/items/crafting_tools/craftsman_hammer.ron +++ b/assets/common/items/crafting_tools/craftsman_hammer.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Craftsman Hammer", description: "Used to craft various items.", kind: Ingredient( diff --git a/assets/common/items/crafting_tools/mortar_pestle.ron b/assets/common/items/crafting_tools/mortar_pestle.ron index 60f8b60afd..b35f7f6082 100644 --- a/assets/common/items/crafting_tools/mortar_pestle.ron +++ b/assets/common/items/crafting_tools/mortar_pestle.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Mortar and Pestle", description: "Crushes and grinds things into\na fine powder or paste.\nUsed to craft various items.", kind: Ingredient( diff --git a/assets/common/items/debug/admin.ron b/assets/common/items/debug/admin.ron index cf94cac904..0281cd0a21 100644 --- a/assets/common/items/debug/admin.ron +++ b/assets/common/items/debug/admin.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Admin's Tabard", description: "With great power comes\ngreat responsibility.", kind: Armor( diff --git a/assets/common/items/debug/admin_back.ron b/assets/common/items/debug/admin_back.ron index e93a00e261..d81857adc9 100644 --- a/assets/common/items/debug/admin_back.ron +++ b/assets/common/items/debug/admin_back.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Admin's Cape", description: "With great power comes\ngreat responsibility.", kind: Armor( diff --git a/assets/common/items/debug/boost.ron b/assets/common/items/debug/boost.ron index 523a0c0704..1fda69471b 100644 --- a/assets/common/items/debug/boost.ron +++ b/assets/common/items/debug/boost.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Belzeshrub the Broom-God", description: "You can hear him giggle whenever\nyou hit the ground a bit too hard...", kind: Tool( diff --git a/assets/common/items/debug/cultist_belt.ron b/assets/common/items/debug/cultist_belt.ron index ee1147c0b4..cd25def1ec 100644 --- a/assets/common/items/debug/cultist_belt.ron +++ b/assets/common/items/debug/cultist_belt.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Cultist Belt", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/debug/cultist_boots.ron b/assets/common/items/debug/cultist_boots.ron index 146034afc8..14ccc88cb0 100644 --- a/assets/common/items/debug/cultist_boots.ron +++ b/assets/common/items/debug/cultist_boots.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Cultist Boots", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/debug/cultist_chest_blue.ron b/assets/common/items/debug/cultist_chest_blue.ron index 245855631d..97b872a7e1 100644 --- a/assets/common/items/debug/cultist_chest_blue.ron +++ b/assets/common/items/debug/cultist_chest_blue.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Cultist Chest", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/debug/cultist_hands_blue.ron b/assets/common/items/debug/cultist_hands_blue.ron index 21ddf151b8..1b1d81ff09 100644 --- a/assets/common/items/debug/cultist_hands_blue.ron +++ b/assets/common/items/debug/cultist_hands_blue.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Cultist Gloves", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/debug/cultist_legs_blue.ron b/assets/common/items/debug/cultist_legs_blue.ron index b3bf65bf22..6a6421dbe7 100644 --- a/assets/common/items/debug/cultist_legs_blue.ron +++ b/assets/common/items/debug/cultist_legs_blue.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Cultist Skirt", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/debug/cultist_purp_2h_boss-0.ron b/assets/common/items/debug/cultist_purp_2h_boss-0.ron index bdae5745c0..e9411a01ba 100644 --- a/assets/common/items/debug/cultist_purp_2h_boss-0.ron +++ b/assets/common/items/debug/cultist_purp_2h_boss-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Admin Greatsword", description: "Shouldn't this be a hammer?", kind: Tool( diff --git a/assets/common/items/debug/cultist_shoulder_blue.ron b/assets/common/items/debug/cultist_shoulder_blue.ron index 096df2ad5d..b65bbd3601 100644 --- a/assets/common/items/debug/cultist_shoulder_blue.ron +++ b/assets/common/items/debug/cultist_shoulder_blue.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Cultist Mantle", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/debug/dungeon_purple-0.ron b/assets/common/items/debug/dungeon_purple-0.ron index e877374a9a..16f192ad48 100644 --- a/assets/common/items/debug/dungeon_purple-0.ron +++ b/assets/common/items/debug/dungeon_purple-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Admin Cape", description: "Where did I put my banhammer again?", kind: Armor( diff --git a/assets/common/items/debug/possess.ron b/assets/common/items/debug/possess.ron index 523a0c0704..1fda69471b 100644 --- a/assets/common/items/debug/possess.ron +++ b/assets/common/items/debug/possess.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Belzeshrub the Broom-God", description: "You can hear him giggle whenever\nyou hit the ground a bit too hard...", kind: Tool( diff --git a/assets/common/items/flowers/blue.ron b/assets/common/items/flowers/blue.ron index 075e1fd6f2..72e6b189c2 100644 --- a/assets/common/items/flowers/blue.ron +++ b/assets/common/items/flowers/blue.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Blue Flower", description: "Matches the color of the sky.", kind: Ingredient( diff --git a/assets/common/items/flowers/pink.ron b/assets/common/items/flowers/pink.ron index 793869f784..3494920bdb 100644 --- a/assets/common/items/flowers/pink.ron +++ b/assets/common/items/flowers/pink.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Pink Flower", description: "Looks like a lollipop.", kind: Ingredient( diff --git a/assets/common/items/flowers/red.ron b/assets/common/items/flowers/red.ron index a3d32e486d..70c251f7ac 100644 --- a/assets/common/items/flowers/red.ron +++ b/assets/common/items/flowers/red.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Red Flower", description: "Roses are red...", kind: Ingredient( diff --git a/assets/common/items/flowers/sun.ron b/assets/common/items/flowers/sun.ron index 143ef7439b..fa7e72ff35 100644 --- a/assets/common/items/flowers/sun.ron +++ b/assets/common/items/flowers/sun.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Sunflower", description: "Smells like summer.", kind: Ingredient( diff --git a/assets/common/items/flowers/white.ron b/assets/common/items/flowers/white.ron index d772c6dc35..f8cc6685a1 100644 --- a/assets/common/items/flowers/white.ron +++ b/assets/common/items/flowers/white.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "White flower", description: "Pure and precious.", kind: Ingredient( diff --git a/assets/common/items/flowers/yellow.ron b/assets/common/items/flowers/yellow.ron index 5897a9c50a..0c233e4f14 100644 --- a/assets/common/items/flowers/yellow.ron +++ b/assets/common/items/flowers/yellow.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Yellow Flower", description: "Glows like the sun.", kind: Ingredient( diff --git a/assets/common/items/food/apple.ron b/assets/common/items/food/apple.ron index e31d3ce549..64f6a3f2af 100644 --- a/assets/common/items/food/apple.ron +++ b/assets/common/items/food/apple.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Apple", description: "Restores 20 Health\n\nRed and juicy\n\n", kind: Consumable( diff --git a/assets/common/items/food/apple_mushroom_curry.ron b/assets/common/items/food/apple_mushroom_curry.ron index ae0a04ef72..53d20e2087 100644 --- a/assets/common/items/food/apple_mushroom_curry.ron +++ b/assets/common/items/food/apple_mushroom_curry.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Mushroom Curry", description: "Restores 120 Health\n\nWho could say no to that?\n\n", kind: Consumable( diff --git a/assets/common/items/food/apple_stick.ron b/assets/common/items/food/apple_stick.ron index 27caeea816..47be0fb129 100644 --- a/assets/common/items/food/apple_stick.ron +++ b/assets/common/items/food/apple_stick.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Apple Stick", description: "Restores 60 Health\n\n", kind: Consumable( diff --git a/assets/common/items/food/cheese.ron b/assets/common/items/food/cheese.ron index 20ccee968f..7056565d54 100644 --- a/assets/common/items/food/cheese.ron +++ b/assets/common/items/food/cheese.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Dwarven Cheese", description: "Restores 15 Health\n\nAromatic and nutritious\n\n", kind: Consumable( diff --git a/assets/common/items/food/coconut.ron b/assets/common/items/food/coconut.ron index 37cb30b9e2..bf669d6c6d 100644 --- a/assets/common/items/food/coconut.ron +++ b/assets/common/items/food/coconut.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Coconut", description: "Restores 30 health\n\nReliable source of water and fat\n\n", kind: Consumable( diff --git a/assets/common/items/food/mushroom.ron b/assets/common/items/food/mushroom.ron index 2131683964..39f67c4dee 100644 --- a/assets/common/items/food/mushroom.ron +++ b/assets/common/items/food/mushroom.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Mushroom", description: "Restores 10 Health\n\nHopefully this one is not poisonous\n\n", kind: Consumable( diff --git a/assets/common/items/food/mushroom_stick.ron b/assets/common/items/food/mushroom_stick.ron index 41228a7ab9..17b73c76a2 100644 --- a/assets/common/items/food/mushroom_stick.ron +++ b/assets/common/items/food/mushroom_stick.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Mushroom Stick", description: "Restores 50 Health\n\n", kind: Consumable( diff --git a/assets/common/items/grasses/long.ron b/assets/common/items/grasses/long.ron index 7a86cbbb97..9668c69995 100644 --- a/assets/common/items/grasses/long.ron +++ b/assets/common/items/grasses/long.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Long Grass", description: "Greener than an orc's snout.", kind: Ingredient( diff --git a/assets/common/items/grasses/medium.ron b/assets/common/items/grasses/medium.ron index 19252c11bf..20220d15c5 100644 --- a/assets/common/items/grasses/medium.ron +++ b/assets/common/items/grasses/medium.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Medium Grass", description: "Greener than an orc's snout.", kind: Ingredient( diff --git a/assets/common/items/grasses/short.ron b/assets/common/items/grasses/short.ron index 4b11ea3b6f..2e50374e7b 100644 --- a/assets/common/items/grasses/short.ron +++ b/assets/common/items/grasses/short.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Short Grass", description: "Greener than an orc's snout.", kind: Ingredient( diff --git a/assets/common/items/lantern/black_0.ron b/assets/common/items/lantern/black_0.ron index 38f282072e..6c1ebe5764 100644 --- a/assets/common/items/lantern/black_0.ron +++ b/assets/common/items/lantern/black_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Black Lantern", description: "Used by city guards.", kind: Lantern( diff --git a/assets/common/items/lantern/blue_0.ron b/assets/common/items/lantern/blue_0.ron index c342ff14a7..362216e7c9 100644 --- a/assets/common/items/lantern/blue_0.ron +++ b/assets/common/items/lantern/blue_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Cool Blue Lantern", description: "This lantern is surprisingly cold when lit.", kind: Lantern( diff --git a/assets/common/items/lantern/green_0.ron b/assets/common/items/lantern/green_0.ron index 0409aba6a5..ce0e3f400c 100644 --- a/assets/common/items/lantern/green_0.ron +++ b/assets/common/items/lantern/green_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Lime Zest Lantern", description: "It has an opening that could fit a ring...", kind: Lantern( diff --git a/assets/common/items/lantern/red_0.ron b/assets/common/items/lantern/red_0.ron index bac1264ba0..0acd7af095 100644 --- a/assets/common/items/lantern/red_0.ron +++ b/assets/common/items/lantern/red_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Red Lantern", description: "Caution: contents hot", kind: Lantern( diff --git a/assets/common/items/npc_armor/back/dungeon_purple-0.ron b/assets/common/items/npc_armor/back/dungeon_purple-0.ron index 325c85adcb..2e42166bc0 100644 --- a/assets/common/items/npc_armor/back/dungeon_purple-0.ron +++ b/assets/common/items/npc_armor/back/dungeon_purple-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Cultist Cape", description: "Smells like dark magic and candles.", kind: Armor( diff --git a/assets/common/items/npc_armor/belt/cultist_belt.ron b/assets/common/items/npc_armor/belt/cultist_belt.ron index 6b856636c6..d2551046de 100644 --- a/assets/common/items/npc_armor/belt/cultist_belt.ron +++ b/assets/common/items/npc_armor/belt/cultist_belt.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Cultist Belt", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/npc_armor/chest/cultist_chest_purple.ron b/assets/common/items/npc_armor/chest/cultist_chest_purple.ron index ed500e8f1f..2f36b0f929 100644 --- a/assets/common/items/npc_armor/chest/cultist_chest_purple.ron +++ b/assets/common/items/npc_armor/chest/cultist_chest_purple.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Cultist Chest", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/npc_armor/chest/worker_green_0.ron b/assets/common/items/npc_armor/chest/worker_green_0.ron index fa343fddb7..666424cabb 100644 --- a/assets/common/items/npc_armor/chest/worker_green_0.ron +++ b/assets/common/items/npc_armor/chest/worker_green_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Green Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/npc_armor/chest/worker_green_1.ron b/assets/common/items/npc_armor/chest/worker_green_1.ron index 23b84137cf..46e7505659 100644 --- a/assets/common/items/npc_armor/chest/worker_green_1.ron +++ b/assets/common/items/npc_armor/chest/worker_green_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Green Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/npc_armor/chest/worker_orange_0.ron b/assets/common/items/npc_armor/chest/worker_orange_0.ron index bc07617dcf..5a8ba5503b 100644 --- a/assets/common/items/npc_armor/chest/worker_orange_0.ron +++ b/assets/common/items/npc_armor/chest/worker_orange_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Orange Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/npc_armor/chest/worker_orange_1.ron b/assets/common/items/npc_armor/chest/worker_orange_1.ron index 6c605d452c..542f760c7a 100644 --- a/assets/common/items/npc_armor/chest/worker_orange_1.ron +++ b/assets/common/items/npc_armor/chest/worker_orange_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Orange Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/npc_armor/chest/worker_purple_0.ron b/assets/common/items/npc_armor/chest/worker_purple_0.ron index 6a234e994e..a23bf63a62 100644 --- a/assets/common/items/npc_armor/chest/worker_purple_0.ron +++ b/assets/common/items/npc_armor/chest/worker_purple_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/npc_armor/chest/worker_purple_1.ron b/assets/common/items/npc_armor/chest/worker_purple_1.ron index 35bcc56647..6b121afa30 100644 --- a/assets/common/items/npc_armor/chest/worker_purple_1.ron +++ b/assets/common/items/npc_armor/chest/worker_purple_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/npc_armor/chest/worker_red_0.ron b/assets/common/items/npc_armor/chest/worker_red_0.ron index bbee3cbc2a..2b8cb069f8 100644 --- a/assets/common/items/npc_armor/chest/worker_red_0.ron +++ b/assets/common/items/npc_armor/chest/worker_red_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Red Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/npc_armor/chest/worker_red_1.ron b/assets/common/items/npc_armor/chest/worker_red_1.ron index 4b0966ee51..6d2bdf8946 100644 --- a/assets/common/items/npc_armor/chest/worker_red_1.ron +++ b/assets/common/items/npc_armor/chest/worker_red_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Red Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/npc_armor/chest/worker_yellow_0.ron b/assets/common/items/npc_armor/chest/worker_yellow_0.ron index d59b94169c..40f4680c80 100644 --- a/assets/common/items/npc_armor/chest/worker_yellow_0.ron +++ b/assets/common/items/npc_armor/chest/worker_yellow_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Yellow Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/npc_armor/chest/worker_yellow_1.ron b/assets/common/items/npc_armor/chest/worker_yellow_1.ron index d8db355878..0374da4c2e 100644 --- a/assets/common/items/npc_armor/chest/worker_yellow_1.ron +++ b/assets/common/items/npc_armor/chest/worker_yellow_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Yellow Worker Shirt", description: "Was used by a farmer, until recently.", kind: Armor( diff --git a/assets/common/items/npc_armor/foot/cultist_boots.ron b/assets/common/items/npc_armor/foot/cultist_boots.ron index da1de15c1a..81d2e15b8c 100644 --- a/assets/common/items/npc_armor/foot/cultist_boots.ron +++ b/assets/common/items/npc_armor/foot/cultist_boots.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Cultist Boots", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/npc_armor/hand/cultist_hands_purple.ron b/assets/common/items/npc_armor/hand/cultist_hands_purple.ron index 220c1fab77..22abb63e5f 100644 --- a/assets/common/items/npc_armor/hand/cultist_hands_purple.ron +++ b/assets/common/items/npc_armor/hand/cultist_hands_purple.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Cultist Gloves", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/npc_armor/pants/cultist_legs_purple.ron b/assets/common/items/npc_armor/pants/cultist_legs_purple.ron index 0eaf1e95ab..d8f377089f 100644 --- a/assets/common/items/npc_armor/pants/cultist_legs_purple.ron +++ b/assets/common/items/npc_armor/pants/cultist_legs_purple.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Cultist Skirt", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/npc_armor/shoulder/cultist_shoulder_purple.ron b/assets/common/items/npc_armor/shoulder/cultist_shoulder_purple.ron index ccf9691a7d..9fa067af13 100644 --- a/assets/common/items/npc_armor/shoulder/cultist_shoulder_purple.ron +++ b/assets/common/items/npc_armor/shoulder/cultist_shoulder_purple.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Purple Cultist Mantle", description: "Ceremonial attire used by members.", kind: Armor( diff --git a/assets/common/items/npc_weapons/axe/malachite_axe-0.ron b/assets/common/items/npc_weapons/axe/malachite_axe-0.ron index c639db3dd8..fd7e962871 100644 --- a/assets/common/items/npc_weapons/axe/malachite_axe-0.ron +++ b/assets/common/items/npc_weapons/axe/malachite_axe-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Malachite Axe", description: "An axe infused with malachite.", kind: Tool( diff --git a/assets/common/items/npc_weapons/axe/starter_axe.ron b/assets/common/items/npc_weapons/axe/starter_axe.ron index ccdd0c527c..f7428f4063 100644 --- a/assets/common/items/npc_weapons/axe/starter_axe.ron +++ b/assets/common/items/npc_weapons/axe/starter_axe.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Notched Axe", description: "Every dent tells the story of a chopped tree.", kind: Tool( diff --git a/assets/common/items/npc_weapons/bow/horn_longbow-0.ron b/assets/common/items/npc_weapons/bow/horn_longbow-0.ron index 6c055bfa3e..b8658520e7 100644 --- a/assets/common/items/npc_weapons/bow/horn_longbow-0.ron +++ b/assets/common/items/npc_weapons/bow/horn_longbow-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Horn Bow", description: "You don't recognize the creature these horns belong to.", kind: Tool( diff --git a/assets/common/items/npc_weapons/dagger/starter_dagger.ron b/assets/common/items/npc_weapons/dagger/starter_dagger.ron index 9d24de8e09..328a6febbf 100644 --- a/assets/common/items/npc_weapons/dagger/starter_dagger.ron +++ b/assets/common/items/npc_weapons/dagger/starter_dagger.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Rusty Dagger", description: "Easily concealed.", kind: Tool( diff --git a/assets/common/items/npc_weapons/empty/empty.ron b/assets/common/items/npc_weapons/empty/empty.ron index 489101c9f0..014484202c 100644 --- a/assets/common/items/npc_weapons/empty/empty.ron +++ b/assets/common/items/npc_weapons/empty/empty.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Empty", description: "You expected a description?", kind: Tool ( diff --git a/assets/common/items/npc_weapons/hammer/cultist_purp_2h-0.ron b/assets/common/items/npc_weapons/hammer/cultist_purp_2h-0.ron index d384a3eb88..d8440b8cf0 100644 --- a/assets/common/items/npc_weapons/hammer/cultist_purp_2h-0.ron +++ b/assets/common/items/npc_weapons/hammer/cultist_purp_2h-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Magical Cultist Warhammer", description: "This belonged to an evil Cult Leader.", kind: Tool( diff --git a/assets/common/items/npc_weapons/hammer/starter_hammer.ron b/assets/common/items/npc_weapons/hammer/starter_hammer.ron index e871d6cc38..54042edde4 100644 --- a/assets/common/items/npc_weapons/hammer/starter_hammer.ron +++ b/assets/common/items/npc_weapons/hammer/starter_hammer.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Sturdy Old Hammer", description: "'Property of...' The rest is missing.", kind: Tool( diff --git a/assets/common/items/npc_weapons/shield/shield_1.ron b/assets/common/items/npc_weapons/shield/shield_1.ron index 6fe9c4a971..bc977f66ee 100644 --- a/assets/common/items/npc_weapons/shield/shield_1.ron +++ b/assets/common/items/npc_weapons/shield/shield_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "A Tattered Targe", description: "Should withstand a few more hits, hopefully...", kind: Tool ( diff --git a/assets/common/items/npc_weapons/staff/bone_staff.ron b/assets/common/items/npc_weapons/staff/bone_staff.ron index a5675f2690..b787eba61c 100644 --- a/assets/common/items/npc_weapons/staff/bone_staff.ron +++ b/assets/common/items/npc_weapons/staff/bone_staff.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Bone Staff", description: "There's a red gem suspended in the bones.", kind: Tool( diff --git a/assets/common/items/npc_weapons/staff/cultist_staff.ron b/assets/common/items/npc_weapons/staff/cultist_staff.ron index f4ebacea3f..d31920b860 100644 --- a/assets/common/items/npc_weapons/staff/cultist_staff.ron +++ b/assets/common/items/npc_weapons/staff/cultist_staff.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Cultist Staff", description: "The fire gives off no heat.", kind: Tool( diff --git a/assets/common/items/npc_weapons/sword/cultist_purp_2h-0.ron b/assets/common/items/npc_weapons/sword/cultist_purp_2h-0.ron index 19bcd0369e..d388d15bdb 100644 --- a/assets/common/items/npc_weapons/sword/cultist_purp_2h-0.ron +++ b/assets/common/items/npc_weapons/sword/cultist_purp_2h-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Magical Cultist Greatsword", description: "This belonged to an evil Cult Leader.", kind: Tool( diff --git a/assets/common/items/npc_weapons/sword/cultist_purp_2h_boss-0.ron b/assets/common/items/npc_weapons/sword/cultist_purp_2h_boss-0.ron index 085690bc07..624d4b963d 100644 --- a/assets/common/items/npc_weapons/sword/cultist_purp_2h_boss-0.ron +++ b/assets/common/items/npc_weapons/sword/cultist_purp_2h_boss-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Magical Cultist Greatsword", description: "This belonged to an evil Cult Leader.", kind: Tool( diff --git a/assets/common/items/npc_weapons/sword/starter_sword.ron b/assets/common/items/npc_weapons/sword/starter_sword.ron index 5eef68dca3..cb776f9c31 100644 --- a/assets/common/items/npc_weapons/sword/starter_sword.ron +++ b/assets/common/items/npc_weapons/sword/starter_sword.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Battered Sword", description: "Held together by Rust and hope.", kind: Tool( diff --git a/assets/common/items/npc_weapons/sword/zweihander_sword_0.ron b/assets/common/items/npc_weapons/sword/zweihander_sword_0.ron index 35819b476e..b75c8c515c 100644 --- a/assets/common/items/npc_weapons/sword/zweihander_sword_0.ron +++ b/assets/common/items/npc_weapons/sword/zweihander_sword_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Sturdy Zweihander", description: "It's a big sword, and sharp too.", kind: Tool( diff --git a/assets/common/items/npc_weapons/tool/broom.ron b/assets/common/items/npc_weapons/tool/broom.ron index dfb46a6da7..bbde918a3b 100644 --- a/assets/common/items/npc_weapons/tool/broom.ron +++ b/assets/common/items/npc_weapons/tool/broom.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Broom", description: "It's beginning to fall apart.", kind: Tool ( diff --git a/assets/common/items/npc_weapons/tool/fishing_rod.ron b/assets/common/items/npc_weapons/tool/fishing_rod.ron index 0768f74587..60da887594 100644 --- a/assets/common/items/npc_weapons/tool/fishing_rod.ron +++ b/assets/common/items/npc_weapons/tool/fishing_rod.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Fishing Rod", description: "Smells of fish.", kind: Tool ( diff --git a/assets/common/items/npc_weapons/tool/hoe.ron b/assets/common/items/npc_weapons/tool/hoe.ron index 7c66734bbf..b115d8c2c4 100644 --- a/assets/common/items/npc_weapons/tool/hoe.ron +++ b/assets/common/items/npc_weapons/tool/hoe.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Hoe", description: "It's stained with dirt.", kind: Tool ( diff --git a/assets/common/items/npc_weapons/tool/pickaxe.ron b/assets/common/items/npc_weapons/tool/pickaxe.ron index 303be8c20b..1d2a64e27b 100644 --- a/assets/common/items/npc_weapons/tool/pickaxe.ron +++ b/assets/common/items/npc_weapons/tool/pickaxe.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Pickaxe", description: "It has a chipped edge.", kind: Tool ( diff --git a/assets/common/items/npc_weapons/tool/pitchfork.ron b/assets/common/items/npc_weapons/tool/pitchfork.ron index 3ddc68a41b..19720a4d1c 100644 --- a/assets/common/items/npc_weapons/tool/pitchfork.ron +++ b/assets/common/items/npc_weapons/tool/pitchfork.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Pitchfork", description: "One of the prongs is broken.", kind: Tool ( diff --git a/assets/common/items/npc_weapons/tool/rake.ron b/assets/common/items/npc_weapons/tool/rake.ron index 774edea21e..f4686d9f8b 100644 --- a/assets/common/items/npc_weapons/tool/rake.ron +++ b/assets/common/items/npc_weapons/tool/rake.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Rake", description: "Held together with twine.", kind: Tool ( diff --git a/assets/common/items/npc_weapons/tool/shovel-0.ron b/assets/common/items/npc_weapons/tool/shovel-0.ron index 77e7dc26eb..a7cfbe2b8b 100644 --- a/assets/common/items/npc_weapons/tool/shovel-0.ron +++ b/assets/common/items/npc_weapons/tool/shovel-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Shovel", description: "It's covered in manure.", kind: Tool ( diff --git a/assets/common/items/npc_weapons/tool/shovel-1.ron b/assets/common/items/npc_weapons/tool/shovel-1.ron index ab4f5aae8f..809de7523e 100644 --- a/assets/common/items/npc_weapons/tool/shovel-1.ron +++ b/assets/common/items/npc_weapons/tool/shovel-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Shovel", description: "It's been recently cleaned.", kind: Tool ( diff --git a/assets/common/items/ore/velorite.ron b/assets/common/items/ore/velorite.ron index 9d609bea1b..0729ce28a2 100644 --- a/assets/common/items/ore/velorite.ron +++ b/assets/common/items/ore/velorite.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Velorite", description: "Increases Exp by 20\n\nJust a slight touch makes you feel the knowledge of ancient times\n\n", kind: Consumable( diff --git a/assets/common/items/ore/veloritefrag.ron b/assets/common/items/ore/veloritefrag.ron index 104b1ee0c7..cd7037146c 100644 --- a/assets/common/items/ore/veloritefrag.ron +++ b/assets/common/items/ore/veloritefrag.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Velorite Fragment", description: "Increases Exp by 10\n\nSmall runes sparkle on its surface\n\n", kind: Consumable( diff --git a/assets/common/items/testing/test_boots.ron b/assets/common/items/testing/test_boots.ron index d317aeff25..87882f4621 100644 --- a/assets/common/items/testing/test_boots.ron +++ b/assets/common/items/testing/test_boots.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Testing Boots", description: "Hopefully this test doesn't break!", kind: Armor( diff --git a/assets/common/items/utility/bomb.ron b/assets/common/items/utility/bomb.ron index bfe33b06b9..7be55a10d6 100644 --- a/assets/common/items/utility/bomb.ron +++ b/assets/common/items/utility/bomb.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Bomb", description: "Boom!\n\n", kind: Throwable( diff --git a/assets/common/items/utility/bomb_pile.ron b/assets/common/items/utility/bomb_pile.ron index 84577dced8..d41c7e41ae 100644 --- a/assets/common/items/utility/bomb_pile.ron +++ b/assets/common/items/utility/bomb_pile.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Bomb", description: "Boom!\n\n", kind: Throwable( diff --git a/assets/common/items/utility/collar.ron b/assets/common/items/utility/collar.ron index 9ec7edc59c..b13f2bd8fd 100644 --- a/assets/common/items/utility/collar.ron +++ b/assets/common/items/utility/collar.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Collar", description: "Tames wild animals within 5 blocks\n\n", kind: Utility( diff --git a/assets/common/items/utility/firework_blue.ron b/assets/common/items/utility/firework_blue.ron index 5468670824..a475f494d9 100644 --- a/assets/common/items/utility/firework_blue.ron +++ b/assets/common/items/utility/firework_blue.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Firework Blue", description: "Recommended clearance: 42 chonks\n\n", kind: Throwable( diff --git a/assets/common/items/utility/firework_green.ron b/assets/common/items/utility/firework_green.ron index ae329c12da..b42b121140 100644 --- a/assets/common/items/utility/firework_green.ron +++ b/assets/common/items/utility/firework_green.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Firework Green", description: "Watch out for trees.\n\n", kind: Throwable( diff --git a/assets/common/items/utility/firework_purple.ron b/assets/common/items/utility/firework_purple.ron index e217d72ff7..f4e9617558 100644 --- a/assets/common/items/utility/firework_purple.ron +++ b/assets/common/items/utility/firework_purple.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Firework Purple", description: "Cult favourite.\n\n", kind: Throwable( diff --git a/assets/common/items/utility/firework_red.ron b/assets/common/items/utility/firework_red.ron index edda2afe76..5617cd480e 100644 --- a/assets/common/items/utility/firework_red.ron +++ b/assets/common/items/utility/firework_red.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Firework Red", description: "Humans sometimes use these\nas a flare in a pinch.\n\n", kind: Throwable( diff --git a/assets/common/items/utility/firework_yellow.ron b/assets/common/items/utility/firework_yellow.ron index 652408f515..8fe73ec1a6 100644 --- a/assets/common/items/utility/firework_yellow.ron +++ b/assets/common/items/utility/firework_yellow.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Firework Yellow", description: "The Great Dr. passed away after\ntesting this contraption indoors.\n\n", kind: Throwable( diff --git a/assets/common/items/utility/training_dummy.ron b/assets/common/items/utility/training_dummy.ron index 9e9a4bd22a..d321ffadd1 100644 --- a/assets/common/items/utility/training_dummy.ron +++ b/assets/common/items/utility/training_dummy.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Training Dummy", description: "His name is William. Fire at will.\n\n", kind: Throwable( diff --git a/assets/common/items/weapons/axe/bloodsteel_axe-0.ron b/assets/common/items/weapons/axe/bloodsteel_axe-0.ron index edcfa314ef..08f06e131c 100644 --- a/assets/common/items/weapons/axe/bloodsteel_axe-0.ron +++ b/assets/common/items/weapons/axe/bloodsteel_axe-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Bloodsteel Axe", description: "An axe forged from steel that thirsts for blood.", kind: Tool( diff --git a/assets/common/items/weapons/axe/bloodsteel_axe-1.ron b/assets/common/items/weapons/axe/bloodsteel_axe-1.ron index 245078f4f2..9b2285835e 100644 --- a/assets/common/items/weapons/axe/bloodsteel_axe-1.ron +++ b/assets/common/items/weapons/axe/bloodsteel_axe-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Executioner's Axe", description: "An axe forged from steel that thirsts for blood.", kind: Tool( diff --git a/assets/common/items/weapons/axe/bloodsteel_axe-2.ron b/assets/common/items/weapons/axe/bloodsteel_axe-2.ron index 99ffaf481f..f346ddc1ef 100644 --- a/assets/common/items/weapons/axe/bloodsteel_axe-2.ron +++ b/assets/common/items/weapons/axe/bloodsteel_axe-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Tribal Axe", description: "An axe forged from steel that thirsts for blood.", kind: Tool( diff --git a/assets/common/items/weapons/axe/bronze_axe-0.ron b/assets/common/items/weapons/axe/bronze_axe-0.ron index e0c22c91fc..9ca89e2216 100644 --- a/assets/common/items/weapons/axe/bronze_axe-0.ron +++ b/assets/common/items/weapons/axe/bronze_axe-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Bronze Axe", description: "Forged from bronze.", kind: Tool( diff --git a/assets/common/items/weapons/axe/bronze_axe-1.ron b/assets/common/items/weapons/axe/bronze_axe-1.ron index 618edfd854..b31cb42ca5 100644 --- a/assets/common/items/weapons/axe/bronze_axe-1.ron +++ b/assets/common/items/weapons/axe/bronze_axe-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Discus Axe", description: "Forged from bronze.", kind: Tool( diff --git a/assets/common/items/weapons/axe/cobalt_axe-0.ron b/assets/common/items/weapons/axe/cobalt_axe-0.ron index 33b9e5238e..996b4aef8b 100644 --- a/assets/common/items/weapons/axe/cobalt_axe-0.ron +++ b/assets/common/items/weapons/axe/cobalt_axe-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Cobalt Axe", description: "Forged from cobalt.", kind: Tool( diff --git a/assets/common/items/weapons/axe/iron_axe-0.ron b/assets/common/items/weapons/axe/iron_axe-0.ron index 79a9d08f13..ecb0811c41 100644 --- a/assets/common/items/weapons/axe/iron_axe-0.ron +++ b/assets/common/items/weapons/axe/iron_axe-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron Greataxe", description: "Forged from iron.", kind: Tool( diff --git a/assets/common/items/weapons/axe/iron_axe-1.ron b/assets/common/items/weapons/axe/iron_axe-1.ron index 282c0d5239..d67a9f66fa 100644 --- a/assets/common/items/weapons/axe/iron_axe-1.ron +++ b/assets/common/items/weapons/axe/iron_axe-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Ceremonial Axe", description: "Forged from iron.", kind: Tool( diff --git a/assets/common/items/weapons/axe/iron_axe-2.ron b/assets/common/items/weapons/axe/iron_axe-2.ron index edd17eb97f..fe0c94ebff 100644 --- a/assets/common/items/weapons/axe/iron_axe-2.ron +++ b/assets/common/items/weapons/axe/iron_axe-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Cyclone Axe", description: "Forged from iron.", kind: Tool( diff --git a/assets/common/items/weapons/axe/iron_axe-3.ron b/assets/common/items/weapons/axe/iron_axe-3.ron index cb897d1b8c..9d0aa6fda7 100644 --- a/assets/common/items/weapons/axe/iron_axe-3.ron +++ b/assets/common/items/weapons/axe/iron_axe-3.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron Battleaxe", description: "Forged from iron.", kind: Tool( diff --git a/assets/common/items/weapons/axe/iron_axe-4.ron b/assets/common/items/weapons/axe/iron_axe-4.ron index 9958211250..56f40d603b 100644 --- a/assets/common/items/weapons/axe/iron_axe-4.ron +++ b/assets/common/items/weapons/axe/iron_axe-4.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Butcher's Axe", description: "Forged from iron.", kind: Tool( diff --git a/assets/common/items/weapons/axe/iron_axe-5.ron b/assets/common/items/weapons/axe/iron_axe-5.ron index 807d3e8e3b..7e6ea471d2 100644 --- a/assets/common/items/weapons/axe/iron_axe-5.ron +++ b/assets/common/items/weapons/axe/iron_axe-5.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Barbarian's Axe", description: "Forged from iron.", kind: Tool( diff --git a/assets/common/items/weapons/axe/iron_axe-6.ron b/assets/common/items/weapons/axe/iron_axe-6.ron index eb4b47ad9f..c36c338e8f 100644 --- a/assets/common/items/weapons/axe/iron_axe-6.ron +++ b/assets/common/items/weapons/axe/iron_axe-6.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron Axe", description: "Forged from iron.", kind: Tool( diff --git a/assets/common/items/weapons/axe/iron_axe-7.ron b/assets/common/items/weapons/axe/iron_axe-7.ron index a55337f011..abc921226d 100644 --- a/assets/common/items/weapons/axe/iron_axe-7.ron +++ b/assets/common/items/weapons/axe/iron_axe-7.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron Labrys", description: "Forged from iron.", kind: Tool( diff --git a/assets/common/items/weapons/axe/iron_axe-8.ron b/assets/common/items/weapons/axe/iron_axe-8.ron index 865cd472b6..bb693c09f7 100644 --- a/assets/common/items/weapons/axe/iron_axe-8.ron +++ b/assets/common/items/weapons/axe/iron_axe-8.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Fanged Axe", description: "Forged from iron.", kind: Tool( diff --git a/assets/common/items/weapons/axe/iron_axe-9.ron b/assets/common/items/weapons/axe/iron_axe-9.ron index fd5bfbe693..51904f5fb3 100644 --- a/assets/common/items/weapons/axe/iron_axe-9.ron +++ b/assets/common/items/weapons/axe/iron_axe-9.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Wolfen Axe", description: "Forged from iron.", kind: Tool( diff --git a/assets/common/items/weapons/axe/malachite_axe-0.ron b/assets/common/items/weapons/axe/malachite_axe-0.ron index 20c8ac8dc1..5a38d7dd22 100644 --- a/assets/common/items/weapons/axe/malachite_axe-0.ron +++ b/assets/common/items/weapons/axe/malachite_axe-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Malachite Axe", description: "An axe infused with malachite.", kind: Tool( diff --git a/assets/common/items/weapons/axe/orc_axe-0.ron b/assets/common/items/weapons/axe/orc_axe-0.ron index 0cfde653f3..975ee1f72c 100644 --- a/assets/common/items/weapons/axe/orc_axe-0.ron +++ b/assets/common/items/weapons/axe/orc_axe-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Beast Cleaver", description: "Created by orcs to cleave beasts in 2.", kind: Tool( diff --git a/assets/common/items/weapons/axe/starter_axe.ron b/assets/common/items/weapons/axe/starter_axe.ron index ccdd0c527c..f7428f4063 100644 --- a/assets/common/items/weapons/axe/starter_axe.ron +++ b/assets/common/items/weapons/axe/starter_axe.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Notched Axe", description: "Every dent tells the story of a chopped tree.", kind: Tool( diff --git a/assets/common/items/weapons/axe/steel_axe-0.ron b/assets/common/items/weapons/axe/steel_axe-0.ron index 9e29b23f3d..48e574052d 100644 --- a/assets/common/items/weapons/axe/steel_axe-0.ron +++ b/assets/common/items/weapons/axe/steel_axe-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Steel Battleaxe", description: "Forged from steel.", kind: Tool( diff --git a/assets/common/items/weapons/axe/steel_axe-1.ron b/assets/common/items/weapons/axe/steel_axe-1.ron index 49885d3695..ea3146ea6f 100644 --- a/assets/common/items/weapons/axe/steel_axe-1.ron +++ b/assets/common/items/weapons/axe/steel_axe-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Steel Labrys", description: "Forged from steel.", kind: Tool( diff --git a/assets/common/items/weapons/axe/steel_axe-2.ron b/assets/common/items/weapons/axe/steel_axe-2.ron index 8fbb652983..e2a900774f 100644 --- a/assets/common/items/weapons/axe/steel_axe-2.ron +++ b/assets/common/items/weapons/axe/steel_axe-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Steel Axe", description: "Forged from steel.", kind: Tool( diff --git a/assets/common/items/weapons/axe/steel_axe-3.ron b/assets/common/items/weapons/axe/steel_axe-3.ron index dd9d993321..ed9a056a27 100644 --- a/assets/common/items/weapons/axe/steel_axe-3.ron +++ b/assets/common/items/weapons/axe/steel_axe-3.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Crescent Axe", description: "Forged from steel.", kind: Tool( diff --git a/assets/common/items/weapons/axe/steel_axe-4.ron b/assets/common/items/weapons/axe/steel_axe-4.ron index 80e69a601e..7c4217d2bf 100644 --- a/assets/common/items/weapons/axe/steel_axe-4.ron +++ b/assets/common/items/weapons/axe/steel_axe-4.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Moon Axe", description: "Forged from steel.", kind: Tool( diff --git a/assets/common/items/weapons/axe/steel_axe-5.ron b/assets/common/items/weapons/axe/steel_axe-5.ron index 516a7c9425..6700291fc1 100644 --- a/assets/common/items/weapons/axe/steel_axe-5.ron +++ b/assets/common/items/weapons/axe/steel_axe-5.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Owl Axe", description: "Forged from steel.", kind: Tool( diff --git a/assets/common/items/weapons/axe/steel_axe-6.ron b/assets/common/items/weapons/axe/steel_axe-6.ron index f7507bb93b..f5f0ebc2e1 100644 --- a/assets/common/items/weapons/axe/steel_axe-6.ron +++ b/assets/common/items/weapons/axe/steel_axe-6.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Spade Axe", description: "Forged from steel.", kind: Tool( diff --git a/assets/common/items/weapons/axe/worn_iron_axe-0.ron b/assets/common/items/weapons/axe/worn_iron_axe-0.ron index 6d24976a90..54b43f3a3d 100644 --- a/assets/common/items/weapons/axe/worn_iron_axe-0.ron +++ b/assets/common/items/weapons/axe/worn_iron_axe-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Worn Dwarven Axe", description: "Hopefully it's previous owner won't miss it.", kind: Tool( diff --git a/assets/common/items/weapons/axe/worn_iron_axe-1.ron b/assets/common/items/weapons/axe/worn_iron_axe-1.ron index 1a167843d7..0fd7bbeb5e 100644 --- a/assets/common/items/weapons/axe/worn_iron_axe-1.ron +++ b/assets/common/items/weapons/axe/worn_iron_axe-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Worn Elven Axe", description: "Hopefully it's previous owner won't miss it.", kind: Tool( diff --git a/assets/common/items/weapons/axe/worn_iron_axe-2.ron b/assets/common/items/weapons/axe/worn_iron_axe-2.ron index 0e65b3e90d..63f1afff7f 100644 --- a/assets/common/items/weapons/axe/worn_iron_axe-2.ron +++ b/assets/common/items/weapons/axe/worn_iron_axe-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Worn Human Axe", description: "Hopefully it's previous owner won't miss it.", kind: Tool( diff --git a/assets/common/items/weapons/axe/worn_iron_axe-3.ron b/assets/common/items/weapons/axe/worn_iron_axe-3.ron index bac71c7db0..68917beecb 100644 --- a/assets/common/items/weapons/axe/worn_iron_axe-3.ron +++ b/assets/common/items/weapons/axe/worn_iron_axe-3.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Worn Orcish Axe", description: "Hopefully it's previous owner won't miss it.", kind: Tool( diff --git a/assets/common/items/weapons/axe/worn_iron_axe-4.ron b/assets/common/items/weapons/axe/worn_iron_axe-4.ron index 011d63e566..75b9f07d8d 100644 --- a/assets/common/items/weapons/axe/worn_iron_axe-4.ron +++ b/assets/common/items/weapons/axe/worn_iron_axe-4.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Beetle Axe", description: "The blade is in the shape of a beetle.", kind: Tool( diff --git a/assets/common/items/weapons/bow/horn_longbow-0.ron b/assets/common/items/weapons/bow/horn_longbow-0.ron index 3a56a0ffa2..adf9010757 100644 --- a/assets/common/items/weapons/bow/horn_longbow-0.ron +++ b/assets/common/items/weapons/bow/horn_longbow-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Horn Bow", description: "You don't recognize the creature these horns belong to.", kind: Tool( diff --git a/assets/common/items/weapons/bow/iron_longbow-0.ron b/assets/common/items/weapons/bow/iron_longbow-0.ron index 61392b82a1..995cc14451 100644 --- a/assets/common/items/weapons/bow/iron_longbow-0.ron +++ b/assets/common/items/weapons/bow/iron_longbow-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Soldier's Bow", description: "Has an insignia on it.", kind: Tool( diff --git a/assets/common/items/weapons/bow/leafy_longbow-0.ron b/assets/common/items/weapons/bow/leafy_longbow-0.ron index 2a27bd55a9..72b1a45c72 100644 --- a/assets/common/items/weapons/bow/leafy_longbow-0.ron +++ b/assets/common/items/weapons/bow/leafy_longbow-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Elven Longbow", description: "There's a new leaf starting to grow.", kind: Tool( diff --git a/assets/common/items/weapons/bow/leafy_shortbow-0.ron b/assets/common/items/weapons/bow/leafy_shortbow-0.ron index 29f0810d58..eee19266c5 100644 --- a/assets/common/items/weapons/bow/leafy_shortbow-0.ron +++ b/assets/common/items/weapons/bow/leafy_shortbow-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Elven Shortbow", description: "The wood still seems alive.", kind: Tool( diff --git a/assets/common/items/weapons/bow/nature_ore_longbow-0.ron b/assets/common/items/weapons/bow/nature_ore_longbow-0.ron index 170d018d34..6b0a25c8f4 100644 --- a/assets/common/items/weapons/bow/nature_ore_longbow-0.ron +++ b/assets/common/items/weapons/bow/nature_ore_longbow-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Velorite Bow", description: "Infused with Velorite power.", kind: Tool( diff --git a/assets/common/items/weapons/bow/rare_longbow.ron b/assets/common/items/weapons/bow/rare_longbow.ron index 0eaf6d4d32..4b6a308bcd 100644 --- a/assets/common/items/weapons/bow/rare_longbow.ron +++ b/assets/common/items/weapons/bow/rare_longbow.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Enchanted Longbow", description: "You can sense power resting in the bow.", kind: Tool( diff --git a/assets/common/items/weapons/bow/starter_bow.ron b/assets/common/items/weapons/bow/starter_bow.ron index 45b7357aa7..387a8ac98f 100644 --- a/assets/common/items/weapons/bow/starter_bow.ron +++ b/assets/common/items/weapons/bow/starter_bow.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Uneven Bow", description: "Someone carved their initials into it.", kind: Tool( diff --git a/assets/common/items/weapons/bow/wood_longbow-0.ron b/assets/common/items/weapons/bow/wood_longbow-0.ron index 35f65f8c22..8f1bca9695 100644 --- a/assets/common/items/weapons/bow/wood_longbow-0.ron +++ b/assets/common/items/weapons/bow/wood_longbow-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Longbow", description: "It's been well used.", kind: Tool( diff --git a/assets/common/items/weapons/bow/wood_longbow-1.ron b/assets/common/items/weapons/bow/wood_longbow-1.ron index 05370d16fd..c7e38f0f8f 100644 --- a/assets/common/items/weapons/bow/wood_longbow-1.ron +++ b/assets/common/items/weapons/bow/wood_longbow-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Recurve Bow", description: "It's hard to pull all the way back.", kind: Tool( diff --git a/assets/common/items/weapons/bow/wood_shortbow-0.ron b/assets/common/items/weapons/bow/wood_shortbow-0.ron index 31bc5aa46c..b9b8075264 100644 --- a/assets/common/items/weapons/bow/wood_shortbow-0.ron +++ b/assets/common/items/weapons/bow/wood_shortbow-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Hunting Bow", description: "Strips of leather are wrapped around the handle.", kind: Tool( diff --git a/assets/common/items/weapons/bow/wood_shortbow-1.ron b/assets/common/items/weapons/bow/wood_shortbow-1.ron index 37d8df0b3c..20c7414a0e 100644 --- a/assets/common/items/weapons/bow/wood_shortbow-1.ron +++ b/assets/common/items/weapons/bow/wood_shortbow-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Horse Bow", description: "Works on the ground too.", kind: Tool( diff --git a/assets/common/items/weapons/dagger/starter_dagger.ron b/assets/common/items/weapons/dagger/starter_dagger.ron index 9d24de8e09..328a6febbf 100644 --- a/assets/common/items/weapons/dagger/starter_dagger.ron +++ b/assets/common/items/weapons/dagger/starter_dagger.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Rusty Dagger", description: "Easily concealed.", kind: Tool( diff --git a/assets/common/items/weapons/empty/empty.ron b/assets/common/items/weapons/empty/empty.ron index 489101c9f0..2885c99ec9 100644 --- a/assets/common/items/weapons/empty/empty.ron +++ b/assets/common/items/weapons/empty/empty.ron @@ -1,6 +1,6 @@ -Item( - name: "Empty", - description: "You expected a description?", +ItemDef( + name: "Empty Item", + description: "This item may grant abilities, but is invisible", kind: Tool ( ( kind: Empty, diff --git a/assets/common/items/weapons/hammer/bronze_hammer-0.ron b/assets/common/items/weapons/hammer/bronze_hammer-0.ron index d95c8ef6c8..a2a0a7011e 100644 --- a/assets/common/items/weapons/hammer/bronze_hammer-0.ron +++ b/assets/common/items/weapons/hammer/bronze_hammer-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Bronze Hammer", description: "Forged with bronze.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/bronze_hammer-1.ron b/assets/common/items/weapons/hammer/bronze_hammer-1.ron index 6f35d21888..d6e31fd4dd 100644 --- a/assets/common/items/weapons/hammer/bronze_hammer-1.ron +++ b/assets/common/items/weapons/hammer/bronze_hammer-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Bronze Club", description: "Forged with bronze.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/cobalt_hammer-0.ron b/assets/common/items/weapons/hammer/cobalt_hammer-0.ron index c479ec1ddb..e3a2b5463a 100644 --- a/assets/common/items/weapons/hammer/cobalt_hammer-0.ron +++ b/assets/common/items/weapons/hammer/cobalt_hammer-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Cobalt Hammer", description: "Forged with cobalt.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/cobalt_hammer-1.ron b/assets/common/items/weapons/hammer/cobalt_hammer-1.ron index b5d84c6ae5..c1b9b05986 100644 --- a/assets/common/items/weapons/hammer/cobalt_hammer-1.ron +++ b/assets/common/items/weapons/hammer/cobalt_hammer-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Cobalt Mace", description: "Forged with cobalt.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/cultist_purp_2h-0.ron b/assets/common/items/weapons/hammer/cultist_purp_2h-0.ron index b1cdc4a25d..b5835ac46c 100644 --- a/assets/common/items/weapons/hammer/cultist_purp_2h-0.ron +++ b/assets/common/items/weapons/hammer/cultist_purp_2h-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Magical Cultist Warhammer", description: "This belonged to an evil Cult Leader.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/flimsy_hammer.ron b/assets/common/items/weapons/hammer/flimsy_hammer.ron index 5e425a53e8..8d4949c923 100644 --- a/assets/common/items/weapons/hammer/flimsy_hammer.ron +++ b/assets/common/items/weapons/hammer/flimsy_hammer.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Flimsy Hammer", description: "The head is barely secured.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/hammer_1.ron b/assets/common/items/weapons/hammer/hammer_1.ron index 582f9ce6a3..c88eebee73 100644 --- a/assets/common/items/weapons/hammer/hammer_1.ron +++ b/assets/common/items/weapons/hammer/hammer_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Crude Mallet", description: "Breaks bones like sticks and stones.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/iron_hammer-0.ron b/assets/common/items/weapons/hammer/iron_hammer-0.ron index d13b660690..af48be808a 100644 --- a/assets/common/items/weapons/hammer/iron_hammer-0.ron +++ b/assets/common/items/weapons/hammer/iron_hammer-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron Hammer", description: "Forged with iron.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/iron_hammer-1.ron b/assets/common/items/weapons/hammer/iron_hammer-1.ron index 754bd6f571..e234f2a984 100644 --- a/assets/common/items/weapons/hammer/iron_hammer-1.ron +++ b/assets/common/items/weapons/hammer/iron_hammer-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron Battlehammer", description: "Forged with iron.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/iron_hammer-2.ron b/assets/common/items/weapons/hammer/iron_hammer-2.ron index 82ebea4ce4..64e280454a 100644 --- a/assets/common/items/weapons/hammer/iron_hammer-2.ron +++ b/assets/common/items/weapons/hammer/iron_hammer-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Iron Mace", description: "Forged with iron.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/iron_hammer-3.ron b/assets/common/items/weapons/hammer/iron_hammer-3.ron index 74f7b7fa3e..8f99eb36fa 100644 --- a/assets/common/items/weapons/hammer/iron_hammer-3.ron +++ b/assets/common/items/weapons/hammer/iron_hammer-3.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Crowned Mace", description: "Forged with iron.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/iron_hammer-4.ron b/assets/common/items/weapons/hammer/iron_hammer-4.ron index c9c1e0199c..b2e2219390 100644 --- a/assets/common/items/weapons/hammer/iron_hammer-4.ron +++ b/assets/common/items/weapons/hammer/iron_hammer-4.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Forge Hammer", description: "Forged with iron.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/iron_hammer-5.ron b/assets/common/items/weapons/hammer/iron_hammer-5.ron index d48eacb6d6..a67241c987 100644 --- a/assets/common/items/weapons/hammer/iron_hammer-5.ron +++ b/assets/common/items/weapons/hammer/iron_hammer-5.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Pike Hammer", description: "Forged with iron.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/iron_hammer-6.ron b/assets/common/items/weapons/hammer/iron_hammer-6.ron index ca1f7bfaf3..af02ebbae6 100644 --- a/assets/common/items/weapons/hammer/iron_hammer-6.ron +++ b/assets/common/items/weapons/hammer/iron_hammer-6.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Spiked Maul", description: "Forged with iron.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/iron_hammer-7.ron b/assets/common/items/weapons/hammer/iron_hammer-7.ron index b8a02b1fd3..8c1c8edeb5 100644 --- a/assets/common/items/weapons/hammer/iron_hammer-7.ron +++ b/assets/common/items/weapons/hammer/iron_hammer-7.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Giant's Fist", description: "Forged with iron.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/iron_hammer-8.ron b/assets/common/items/weapons/hammer/iron_hammer-8.ron index a171f2a604..482d35a933 100644 --- a/assets/common/items/weapons/hammer/iron_hammer-8.ron +++ b/assets/common/items/weapons/hammer/iron_hammer-8.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Lucerne Hammer", description: "Forged with iron.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/mjolnir.ron b/assets/common/items/weapons/hammer/mjolnir.ron index b9fc92943b..1333142c84 100644 --- a/assets/common/items/weapons/hammer/mjolnir.ron +++ b/assets/common/items/weapons/hammer/mjolnir.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Mjolnir", description: "It's crackling with lightning.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/ramshead_hammer.ron b/assets/common/items/weapons/hammer/ramshead_hammer.ron index b2662aaaea..a16be1350f 100644 --- a/assets/common/items/weapons/hammer/ramshead_hammer.ron +++ b/assets/common/items/weapons/hammer/ramshead_hammer.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Ram's Head Mace", description: "You feel an evil presence in the hammer.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/runic_hammer.ron b/assets/common/items/weapons/hammer/runic_hammer.ron index 33046cb565..c95cb04241 100644 --- a/assets/common/items/weapons/hammer/runic_hammer.ron +++ b/assets/common/items/weapons/hammer/runic_hammer.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Runic Hammer", description: "There are strange runes inscribed into it.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/starter_hammer.ron b/assets/common/items/weapons/hammer/starter_hammer.ron index e871d6cc38..54042edde4 100644 --- a/assets/common/items/weapons/hammer/starter_hammer.ron +++ b/assets/common/items/weapons/hammer/starter_hammer.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Sturdy Old Hammer", description: "'Property of...' The rest is missing.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/steel_hammer-0.ron b/assets/common/items/weapons/hammer/steel_hammer-0.ron index d3aad2c12a..5cd49bd7c0 100644 --- a/assets/common/items/weapons/hammer/steel_hammer-0.ron +++ b/assets/common/items/weapons/hammer/steel_hammer-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Steel Hammer", description: "Forged with steel.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/steel_hammer-1.ron b/assets/common/items/weapons/hammer/steel_hammer-1.ron index cfcc1e110c..82062f7b1b 100644 --- a/assets/common/items/weapons/hammer/steel_hammer-1.ron +++ b/assets/common/items/weapons/hammer/steel_hammer-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Steel Greathammer", description: "Forged with steel.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/steel_hammer-2.ron b/assets/common/items/weapons/hammer/steel_hammer-2.ron index 11ce5a083e..6d6b45f4c1 100644 --- a/assets/common/items/weapons/hammer/steel_hammer-2.ron +++ b/assets/common/items/weapons/hammer/steel_hammer-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Steel Club", description: "Forged with steel.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/steel_hammer-3.ron b/assets/common/items/weapons/hammer/steel_hammer-3.ron index ab97c15a7c..4c0d95d944 100644 --- a/assets/common/items/weapons/hammer/steel_hammer-3.ron +++ b/assets/common/items/weapons/hammer/steel_hammer-3.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Battle Mace", description: "Forged with steel.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/steel_hammer-4.ron b/assets/common/items/weapons/hammer/steel_hammer-4.ron index a999cf8692..2123d9ae0a 100644 --- a/assets/common/items/weapons/hammer/steel_hammer-4.ron +++ b/assets/common/items/weapons/hammer/steel_hammer-4.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Brute's Hammer", description: "Forged with steel.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/steel_hammer-5.ron b/assets/common/items/weapons/hammer/steel_hammer-5.ron index 1409c0121c..e087d84160 100644 --- a/assets/common/items/weapons/hammer/steel_hammer-5.ron +++ b/assets/common/items/weapons/hammer/steel_hammer-5.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Morning Star", description: "Forged with steel.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/stone_hammer-0.ron b/assets/common/items/weapons/hammer/stone_hammer-0.ron index 0a12b75c5f..2504524ca5 100644 --- a/assets/common/items/weapons/hammer/stone_hammer-0.ron +++ b/assets/common/items/weapons/hammer/stone_hammer-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Basalt Sledgehammer", description: "It seems brittle.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/stone_hammer-1.ron b/assets/common/items/weapons/hammer/stone_hammer-1.ron index cb33efc4b6..03c97e62ee 100644 --- a/assets/common/items/weapons/hammer/stone_hammer-1.ron +++ b/assets/common/items/weapons/hammer/stone_hammer-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Granite Sledgehammer", description: "It seems brittle.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/stone_hammer-2.ron b/assets/common/items/weapons/hammer/stone_hammer-2.ron index 8490c9b468..05fbefd3ac 100644 --- a/assets/common/items/weapons/hammer/stone_hammer-2.ron +++ b/assets/common/items/weapons/hammer/stone_hammer-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Rocky Maul", description: "It seems brittle.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/stone_hammer-3.ron b/assets/common/items/weapons/hammer/stone_hammer-3.ron index 5e58f89be2..9e839b04e3 100644 --- a/assets/common/items/weapons/hammer/stone_hammer-3.ron +++ b/assets/common/items/weapons/hammer/stone_hammer-3.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Stone Sledgehammer", description: "It seems brittle.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/wood_hammer-0.ron b/assets/common/items/weapons/hammer/wood_hammer-0.ron index 7f3ff8cb97..457ec16f3a 100644 --- a/assets/common/items/weapons/hammer/wood_hammer-0.ron +++ b/assets/common/items/weapons/hammer/wood_hammer-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Hardwood Mallet", description: "Seems sturdy enough.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/worn_iron_hammer-0.ron b/assets/common/items/weapons/hammer/worn_iron_hammer-0.ron index 666e0cfa3c..b73d14bed3 100644 --- a/assets/common/items/weapons/hammer/worn_iron_hammer-0.ron +++ b/assets/common/items/weapons/hammer/worn_iron_hammer-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Worn Dwarven Hammer", description: "The previous owner won't miss it much.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/worn_iron_hammer-1.ron b/assets/common/items/weapons/hammer/worn_iron_hammer-1.ron index f581ed5b17..7b5075258d 100644 --- a/assets/common/items/weapons/hammer/worn_iron_hammer-1.ron +++ b/assets/common/items/weapons/hammer/worn_iron_hammer-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Worn Elven Hammer", description: "The previous owner won't miss it much.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/worn_iron_hammer-2.ron b/assets/common/items/weapons/hammer/worn_iron_hammer-2.ron index 76d4ac2f24..a9acd46b05 100644 --- a/assets/common/items/weapons/hammer/worn_iron_hammer-2.ron +++ b/assets/common/items/weapons/hammer/worn_iron_hammer-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Worn Human Mace", description: "The previous owner won't miss it much.", kind: Tool( diff --git a/assets/common/items/weapons/hammer/worn_iron_hammer-3.ron b/assets/common/items/weapons/hammer/worn_iron_hammer-3.ron index fe9020cbd2..6e94ba5af9 100644 --- a/assets/common/items/weapons/hammer/worn_iron_hammer-3.ron +++ b/assets/common/items/weapons/hammer/worn_iron_hammer-3.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Worn Orcish Hammer", description: "The previous owner won't miss it much.", kind: Tool( diff --git a/assets/common/items/weapons/shield/shield_1.ron b/assets/common/items/weapons/shield/shield_1.ron index 6fe9c4a971..bc977f66ee 100644 --- a/assets/common/items/weapons/shield/shield_1.ron +++ b/assets/common/items/weapons/shield/shield_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "A Tattered Targe", description: "Should withstand a few more hits, hopefully...", kind: Tool ( diff --git a/assets/common/items/weapons/staff/amethyst_staff.ron b/assets/common/items/weapons/staff/amethyst_staff.ron index 05f476e24b..9251e88f44 100644 --- a/assets/common/items/weapons/staff/amethyst_staff.ron +++ b/assets/common/items/weapons/staff/amethyst_staff.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Amethyst Staff", description: "The amethyst faintly glows.", kind: Tool( diff --git a/assets/common/items/weapons/staff/bone_staff.ron b/assets/common/items/weapons/staff/bone_staff.ron index ae9ba4076a..cba3f16d2f 100644 --- a/assets/common/items/weapons/staff/bone_staff.ron +++ b/assets/common/items/weapons/staff/bone_staff.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Bone Staff", description: "There's a red gem suspended in the bones.", kind: Tool( diff --git a/assets/common/items/weapons/staff/cultist_staff.ron b/assets/common/items/weapons/staff/cultist_staff.ron index 626351932c..e52fe518f2 100644 --- a/assets/common/items/weapons/staff/cultist_staff.ron +++ b/assets/common/items/weapons/staff/cultist_staff.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Cultist Staff", description: "The fire gives off no heat.", kind: Tool( diff --git a/assets/common/items/weapons/staff/sceptre_velorite_0.ron b/assets/common/items/weapons/staff/sceptre_velorite_0.ron index e3fbad60ff..23205ce00b 100644 --- a/assets/common/items/weapons/staff/sceptre_velorite_0.ron +++ b/assets/common/items/weapons/staff/sceptre_velorite_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Velorite Sceptre", description: "Heals your allies with the mystical Velorite aura.", kind: Tool( diff --git a/assets/common/items/weapons/staff/staff_1.ron b/assets/common/items/weapons/staff/staff_1.ron index 275bc4ac4a..d0d00746a7 100644 --- a/assets/common/items/weapons/staff/staff_1.ron +++ b/assets/common/items/weapons/staff/staff_1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Humble Stick", description: "Walking stick with a sharpened end.", kind: Tool( diff --git a/assets/common/items/weapons/staff/staff_nature.ron b/assets/common/items/weapons/staff/staff_nature.ron index 6bac1e48eb..d1e55951b1 100644 --- a/assets/common/items/weapons/staff/staff_nature.ron +++ b/assets/common/items/weapons/staff/staff_nature.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Sceptre of Regeneration", description: "Heals your allies with the power of nature.", kind: Tool( diff --git a/assets/common/items/weapons/staff/starter_staff.ron b/assets/common/items/weapons/staff/starter_staff.ron index 0fb6f585d8..b1221d0a70 100644 --- a/assets/common/items/weapons/staff/starter_staff.ron +++ b/assets/common/items/weapons/staff/starter_staff.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Gnarled Rod", description: "Smells like resin and magic.", kind: Tool( diff --git a/assets/common/items/weapons/sword/cultist_purp_2h-0.ron b/assets/common/items/weapons/sword/cultist_purp_2h-0.ron index 6f6af1bd99..ad20c1bb81 100644 --- a/assets/common/items/weapons/sword/cultist_purp_2h-0.ron +++ b/assets/common/items/weapons/sword/cultist_purp_2h-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Magical Cultist Greatsword", description: "This belonged to an evil Cult Leader.", kind: Tool( diff --git a/assets/common/items/weapons/sword/greatsword_2h_dam-0.ron b/assets/common/items/weapons/sword/greatsword_2h_dam-0.ron index 82416701d3..e0856c304e 100644 --- a/assets/common/items/weapons/sword/greatsword_2h_dam-0.ron +++ b/assets/common/items/weapons/sword/greatsword_2h_dam-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Damaged Greatsword", description: "The blade has been chipped quite a few times.", kind: Tool( diff --git a/assets/common/items/weapons/sword/greatsword_2h_dam-1.ron b/assets/common/items/weapons/sword/greatsword_2h_dam-1.ron index 93c43c8a54..beb9185185 100644 --- a/assets/common/items/weapons/sword/greatsword_2h_dam-1.ron +++ b/assets/common/items/weapons/sword/greatsword_2h_dam-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Damaged Greatsword", description: "The blade has been chipped quite a few times.", kind: Tool( diff --git a/assets/common/items/weapons/sword/greatsword_2h_dam-2.ron b/assets/common/items/weapons/sword/greatsword_2h_dam-2.ron index e93d4f4f8f..f44f49e562 100644 --- a/assets/common/items/weapons/sword/greatsword_2h_dam-2.ron +++ b/assets/common/items/weapons/sword/greatsword_2h_dam-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Damaged Greatsword", description: "The blade has been chipped quite a few times.", kind: Tool( diff --git a/assets/common/items/weapons/sword/greatsword_2h_fine-0.ron b/assets/common/items/weapons/sword/greatsword_2h_fine-0.ron index a446d358da..1ced29eb4c 100644 --- a/assets/common/items/weapons/sword/greatsword_2h_fine-0.ron +++ b/assets/common/items/weapons/sword/greatsword_2h_fine-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Fine Greatsword", description: "It's been polished and sharpened recently.", kind: Tool( diff --git a/assets/common/items/weapons/sword/greatsword_2h_fine-1.ron b/assets/common/items/weapons/sword/greatsword_2h_fine-1.ron index 99a83724d3..7e8f54951f 100644 --- a/assets/common/items/weapons/sword/greatsword_2h_fine-1.ron +++ b/assets/common/items/weapons/sword/greatsword_2h_fine-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Fine Greatsword", description: "It's been polished and sharpened recently.", kind: Tool( diff --git a/assets/common/items/weapons/sword/greatsword_2h_fine-2.ron b/assets/common/items/weapons/sword/greatsword_2h_fine-2.ron index b96587c493..432a7c87e1 100644 --- a/assets/common/items/weapons/sword/greatsword_2h_fine-2.ron +++ b/assets/common/items/weapons/sword/greatsword_2h_fine-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Fine Greatsword", description: "It's been polished and sharpened recently.", kind: Tool( diff --git a/assets/common/items/weapons/sword/greatsword_2h_orn-0.ron b/assets/common/items/weapons/sword/greatsword_2h_orn-0.ron index 5666f00c40..7f0272bbef 100644 --- a/assets/common/items/weapons/sword/greatsword_2h_orn-0.ron +++ b/assets/common/items/weapons/sword/greatsword_2h_orn-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Ornamented Greatsword", description: "The sword's almost a work of art.", kind: Tool( diff --git a/assets/common/items/weapons/sword/greatsword_2h_orn-1.ron b/assets/common/items/weapons/sword/greatsword_2h_orn-1.ron index af5df7d585..68f34d9d96 100644 --- a/assets/common/items/weapons/sword/greatsword_2h_orn-1.ron +++ b/assets/common/items/weapons/sword/greatsword_2h_orn-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Ornamented Greatsword", description: "The sword's almost a work of art.", kind: Tool( diff --git a/assets/common/items/weapons/sword/greatsword_2h_orn-2.ron b/assets/common/items/weapons/sword/greatsword_2h_orn-2.ron index 6b900cb700..cd04a948f8 100644 --- a/assets/common/items/weapons/sword/greatsword_2h_orn-2.ron +++ b/assets/common/items/weapons/sword/greatsword_2h_orn-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Ornamented Greatsword", description: "The sword's almost a work of art.", kind: Tool( diff --git a/assets/common/items/weapons/sword/greatsword_2h_simple-0.ron b/assets/common/items/weapons/sword/greatsword_2h_simple-0.ron index 022fa7fc63..1725132330 100644 --- a/assets/common/items/weapons/sword/greatsword_2h_simple-0.ron +++ b/assets/common/items/weapons/sword/greatsword_2h_simple-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Simple Greatsword", description: "It's been well used.", kind: Tool( diff --git a/assets/common/items/weapons/sword/greatsword_2h_simple-1.ron b/assets/common/items/weapons/sword/greatsword_2h_simple-1.ron index 23be1fa330..9e622374a2 100644 --- a/assets/common/items/weapons/sword/greatsword_2h_simple-1.ron +++ b/assets/common/items/weapons/sword/greatsword_2h_simple-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Simple Greatsword", description: "It's been well used.", kind: Tool( diff --git a/assets/common/items/weapons/sword/greatsword_2h_simple-2.ron b/assets/common/items/weapons/sword/greatsword_2h_simple-2.ron index 11c1a1788d..ce3471217c 100644 --- a/assets/common/items/weapons/sword/greatsword_2h_simple-2.ron +++ b/assets/common/items/weapons/sword/greatsword_2h_simple-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Simple Greatsword", description: "It's been well used.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_dam-0.ron b/assets/common/items/weapons/sword/long_2h_dam-0.ron index eb4e6fca3f..32d62ed9e3 100644 --- a/assets/common/items/weapons/sword/long_2h_dam-0.ron +++ b/assets/common/items/weapons/sword/long_2h_dam-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Damaged Longsword", description: "It's slightly cracked.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_dam-1.ron b/assets/common/items/weapons/sword/long_2h_dam-1.ron index a15b765bb1..8b3d716fda 100644 --- a/assets/common/items/weapons/sword/long_2h_dam-1.ron +++ b/assets/common/items/weapons/sword/long_2h_dam-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Damaged Longsword", description: "It's slightly cracked.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_dam-2.ron b/assets/common/items/weapons/sword/long_2h_dam-2.ron index b1405fa475..7351829039 100644 --- a/assets/common/items/weapons/sword/long_2h_dam-2.ron +++ b/assets/common/items/weapons/sword/long_2h_dam-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Damaged Longsword", description: "It's slightly cracked.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_dam-3.ron b/assets/common/items/weapons/sword/long_2h_dam-3.ron index 4420dbec12..e06fea067a 100644 --- a/assets/common/items/weapons/sword/long_2h_dam-3.ron +++ b/assets/common/items/weapons/sword/long_2h_dam-3.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Damaged Longsword", description: "It's slightly cracked.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_dam-4.ron b/assets/common/items/weapons/sword/long_2h_dam-4.ron index cfe9c8da80..e2fe684867 100644 --- a/assets/common/items/weapons/sword/long_2h_dam-4.ron +++ b/assets/common/items/weapons/sword/long_2h_dam-4.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Damaged Longsword", description: "It's slightly cracked.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_dam-5.ron b/assets/common/items/weapons/sword/long_2h_dam-5.ron index 879165b971..9123e769fc 100644 --- a/assets/common/items/weapons/sword/long_2h_dam-5.ron +++ b/assets/common/items/weapons/sword/long_2h_dam-5.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Damaged Longsword", description: "It's slightly cracked.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_fine-0.ron b/assets/common/items/weapons/sword/long_2h_fine-0.ron index 9713113fdf..0ef1f66847 100644 --- a/assets/common/items/weapons/sword/long_2h_fine-0.ron +++ b/assets/common/items/weapons/sword/long_2h_fine-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Fine Longsword", description: "It shines when you hold it up to the light.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_fine-1.ron b/assets/common/items/weapons/sword/long_2h_fine-1.ron index 3097707106..8b47bd033e 100644 --- a/assets/common/items/weapons/sword/long_2h_fine-1.ron +++ b/assets/common/items/weapons/sword/long_2h_fine-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Fine Longsword", description: "It shines when you hold it up to the light.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_fine-2.ron b/assets/common/items/weapons/sword/long_2h_fine-2.ron index ff4fa5bdaf..557c3124d2 100644 --- a/assets/common/items/weapons/sword/long_2h_fine-2.ron +++ b/assets/common/items/weapons/sword/long_2h_fine-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Fine Longsword", description: "It shines when you hold it up to the light.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_fine-3.ron b/assets/common/items/weapons/sword/long_2h_fine-3.ron index cf2d4e7a5c..5816a01cf0 100644 --- a/assets/common/items/weapons/sword/long_2h_fine-3.ron +++ b/assets/common/items/weapons/sword/long_2h_fine-3.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Fine Longsword", description: "It shines when you hold it up to the light.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_fine-4.ron b/assets/common/items/weapons/sword/long_2h_fine-4.ron index 801cf19f62..13d8904a1b 100644 --- a/assets/common/items/weapons/sword/long_2h_fine-4.ron +++ b/assets/common/items/weapons/sword/long_2h_fine-4.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Fine Longsword", description: "It shines when you hold it up to the light.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_fine-5.ron b/assets/common/items/weapons/sword/long_2h_fine-5.ron index 2670fda4cc..49c4010991 100644 --- a/assets/common/items/weapons/sword/long_2h_fine-5.ron +++ b/assets/common/items/weapons/sword/long_2h_fine-5.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Fine Longsword", description: "It shines when you hold it up to the light.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_orn-0.ron b/assets/common/items/weapons/sword/long_2h_orn-0.ron index 8634a6a135..77cbbd20b2 100644 --- a/assets/common/items/weapons/sword/long_2h_orn-0.ron +++ b/assets/common/items/weapons/sword/long_2h_orn-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Ornamented Longsword", description: "It's probably the weapon of some noble.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_orn-1.ron b/assets/common/items/weapons/sword/long_2h_orn-1.ron index 686b1635a2..53551c8ee4 100644 --- a/assets/common/items/weapons/sword/long_2h_orn-1.ron +++ b/assets/common/items/weapons/sword/long_2h_orn-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Ornamented Longsword", description: "It's probably the weapon of some noble.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_orn-2.ron b/assets/common/items/weapons/sword/long_2h_orn-2.ron index 3ffe180aee..df3e8ac722 100644 --- a/assets/common/items/weapons/sword/long_2h_orn-2.ron +++ b/assets/common/items/weapons/sword/long_2h_orn-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Ornamented Longsword", description: "It's probably the weapon of some noble.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_orn-3.ron b/assets/common/items/weapons/sword/long_2h_orn-3.ron index fc831ee3f4..68a5764915 100644 --- a/assets/common/items/weapons/sword/long_2h_orn-3.ron +++ b/assets/common/items/weapons/sword/long_2h_orn-3.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Ornamented Longsword", description: "It's probably the weapon of some noble.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_orn-4.ron b/assets/common/items/weapons/sword/long_2h_orn-4.ron index bcc54c8a2f..93f2cec576 100644 --- a/assets/common/items/weapons/sword/long_2h_orn-4.ron +++ b/assets/common/items/weapons/sword/long_2h_orn-4.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Ornamented Longsword", description: "It's probably the weapon of some noble.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_orn-5.ron b/assets/common/items/weapons/sword/long_2h_orn-5.ron index 148aeb56ad..921a4e7894 100644 --- a/assets/common/items/weapons/sword/long_2h_orn-5.ron +++ b/assets/common/items/weapons/sword/long_2h_orn-5.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Ornamented Longsword", description: "It's probably the weapon of some noble.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_simple-0.ron b/assets/common/items/weapons/sword/long_2h_simple-0.ron index bae0afc87d..aaf992a157 100644 --- a/assets/common/items/weapons/sword/long_2h_simple-0.ron +++ b/assets/common/items/weapons/sword/long_2h_simple-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Simple Longsword", description: "It was well maintained by it's previous owner.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_simple-1.ron b/assets/common/items/weapons/sword/long_2h_simple-1.ron index 72a0d0f411..cd6fc1faa3 100644 --- a/assets/common/items/weapons/sword/long_2h_simple-1.ron +++ b/assets/common/items/weapons/sword/long_2h_simple-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Simple Longsword", description: "It was well maintained by it's previous owner.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_simple-2.ron b/assets/common/items/weapons/sword/long_2h_simple-2.ron index 8b1bf62e17..8f9ed9551e 100644 --- a/assets/common/items/weapons/sword/long_2h_simple-2.ron +++ b/assets/common/items/weapons/sword/long_2h_simple-2.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Simple Longsword", description: "It was well maintained by it's previous owner.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_simple-3.ron b/assets/common/items/weapons/sword/long_2h_simple-3.ron index ce66e82c0b..04be739948 100644 --- a/assets/common/items/weapons/sword/long_2h_simple-3.ron +++ b/assets/common/items/weapons/sword/long_2h_simple-3.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Simple Longsword", description: "It was well maintained by it's previous owner.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_simple-4.ron b/assets/common/items/weapons/sword/long_2h_simple-4.ron index b56926e289..0ca551720b 100644 --- a/assets/common/items/weapons/sword/long_2h_simple-4.ron +++ b/assets/common/items/weapons/sword/long_2h_simple-4.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Simple Longsword", description: "It was well maintained by it's previous owner.", kind: Tool( diff --git a/assets/common/items/weapons/sword/long_2h_simple-5.ron b/assets/common/items/weapons/sword/long_2h_simple-5.ron index 6f66fe30be..aed2407d3e 100644 --- a/assets/common/items/weapons/sword/long_2h_simple-5.ron +++ b/assets/common/items/weapons/sword/long_2h_simple-5.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Simple Longsword", description: "It was well maintained by it's previous owner.", kind: Tool( diff --git a/assets/common/items/weapons/sword/short_sword_0.ron b/assets/common/items/weapons/sword/short_sword_0.ron index 98e746af7c..f87b27a064 100644 --- a/assets/common/items/weapons/sword/short_sword_0.ron +++ b/assets/common/items/weapons/sword/short_sword_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Vicious Gladius", description: "There's blood encrusted on the blade.", kind: Tool( diff --git a/assets/common/items/weapons/sword/starter_sword.ron b/assets/common/items/weapons/sword/starter_sword.ron index 5eef68dca3..cb776f9c31 100644 --- a/assets/common/items/weapons/sword/starter_sword.ron +++ b/assets/common/items/weapons/sword/starter_sword.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Battered Sword", description: "Held together by Rust and hope.", kind: Tool( diff --git a/assets/common/items/weapons/sword/wood_sword.ron b/assets/common/items/weapons/sword/wood_sword.ron index c7a047b01a..a2c124427e 100644 --- a/assets/common/items/weapons/sword/wood_sword.ron +++ b/assets/common/items/weapons/sword/wood_sword.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Forest Spirit", description: "The resin glows.", kind: Tool( diff --git a/assets/common/items/weapons/sword/zweihander_sword_0.ron b/assets/common/items/weapons/sword/zweihander_sword_0.ron index d68cef20d5..348ad9bf26 100644 --- a/assets/common/items/weapons/sword/zweihander_sword_0.ron +++ b/assets/common/items/weapons/sword/zweihander_sword_0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Sturdy Zweihander", description: "It's a big sword, and sharp too.", kind: Tool( diff --git a/assets/common/items/weapons/tool/broom.ron b/assets/common/items/weapons/tool/broom.ron index a4343a6a7d..b6d97462e2 100644 --- a/assets/common/items/weapons/tool/broom.ron +++ b/assets/common/items/weapons/tool/broom.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Broom", description: "It's beginning to fall apart.", kind: Tool ( diff --git a/assets/common/items/weapons/tool/fishing_rod.ron b/assets/common/items/weapons/tool/fishing_rod.ron index 03a66f82c1..7fc58a2057 100644 --- a/assets/common/items/weapons/tool/fishing_rod.ron +++ b/assets/common/items/weapons/tool/fishing_rod.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Fishing Rod", description: "Smells of fish.", kind: Tool ( diff --git a/assets/common/items/weapons/tool/hoe.ron b/assets/common/items/weapons/tool/hoe.ron index a9551af028..e92ac40dc1 100644 --- a/assets/common/items/weapons/tool/hoe.ron +++ b/assets/common/items/weapons/tool/hoe.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Hoe", description: "It's stained with dirt.", kind: Tool ( diff --git a/assets/common/items/weapons/tool/pickaxe.ron b/assets/common/items/weapons/tool/pickaxe.ron index 00bf38958f..8975a01639 100644 --- a/assets/common/items/weapons/tool/pickaxe.ron +++ b/assets/common/items/weapons/tool/pickaxe.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Pickaxe", description: "It has a chipped edge.", kind: Tool ( diff --git a/assets/common/items/weapons/tool/pitchfork.ron b/assets/common/items/weapons/tool/pitchfork.ron index d1887c4908..300326aa31 100644 --- a/assets/common/items/weapons/tool/pitchfork.ron +++ b/assets/common/items/weapons/tool/pitchfork.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Pitchfork", description: "One of the prongs is broken.", kind: Tool ( diff --git a/assets/common/items/weapons/tool/rake.ron b/assets/common/items/weapons/tool/rake.ron index 6b210a92f0..74e802fa53 100644 --- a/assets/common/items/weapons/tool/rake.ron +++ b/assets/common/items/weapons/tool/rake.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Rake", description: "Held together with twine.", kind: Tool ( diff --git a/assets/common/items/weapons/tool/shovel-0.ron b/assets/common/items/weapons/tool/shovel-0.ron index 6f30d8356d..c459728217 100644 --- a/assets/common/items/weapons/tool/shovel-0.ron +++ b/assets/common/items/weapons/tool/shovel-0.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Shovel", description: "It's covered in manure.", kind: Tool ( diff --git a/assets/common/items/weapons/tool/shovel-1.ron b/assets/common/items/weapons/tool/shovel-1.ron index e4db84bbfe..55e513a02f 100644 --- a/assets/common/items/weapons/tool/shovel-1.ron +++ b/assets/common/items/weapons/tool/shovel-1.ron @@ -1,4 +1,4 @@ -Item( +ItemDef( name: "Shovel", description: "It's been recently cleaned.", kind: Tool ( diff --git a/client/src/lib.rs b/client/src/lib.rs index 64a20e35f2..e588cdd930 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -15,7 +15,7 @@ pub use specs::{ use byteorder::{ByteOrder, LittleEndian}; use common::{ - character::CharacterItem, + character::{CharacterId, CharacterItem}, comp::{ self, chat::{KillSource, KillType}, @@ -93,7 +93,7 @@ pub struct Client { pub world_map: (Arc, Vec2, Vec2), pub player_list: HashMap, pub character_list: CharacterList, - pub active_character_id: Option, + pub active_character_id: Option, recipe_book: RecipeBook, available_recipes: HashSet, @@ -473,7 +473,7 @@ impl Client { } /// Request a state transition to `ClientState::Character`. - pub fn request_character(&mut self, character_id: i32) { + pub fn request_character(&mut self, character_id: CharacterId) { self.singleton_stream .send(ClientMsg::Character(character_id)) .unwrap(); @@ -499,7 +499,7 @@ impl Client { } /// Character deletion - pub fn delete_character(&mut self, character_id: i32) { + pub fn delete_character(&mut self, character_id: CharacterId) { self.character_list.loading = true; self.singleton_stream .send(ClientMsg::DeleteCharacter(character_id)) diff --git a/common/Cargo.toml b/common/Cargo.toml index b91d8e398a..fe945f7621 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -17,7 +17,7 @@ specs = { git = "https://github.com/amethyst/specs.git", features = ["serde", "s vek = { version = "0.12.0", features = ["platform_intrinsics", "serde"] } dot_vox = "4.0" image = { version = "0.23.8", default-features = false, features = ["png"] } -serde = { version = "1.0.110", features = ["derive"] } +serde = { version = "1.0.110", features = ["derive", "rc"] } serde_json = "1.0.50" serde_repr = "0.1.6" ron = { version = "0.6", default-features = false } diff --git a/common/src/assets/mod.rs b/common/src/assets/mod.rs index bdfb96500b..d12421a2ef 100644 --- a/common/src/assets/mod.rs +++ b/common/src/assets/mod.rs @@ -65,7 +65,7 @@ fn reload(specifier: &str) -> Result<(), Error> where A::Output: Send + Sync + 'static, { - let asset = Arc::new(A::parse(load_file(specifier, A::ENDINGS)?)?); + let asset = Arc::new(A::parse(load_file(specifier, A::ENDINGS)?, specifier)?); let mut assets_write = ASSETS.write().unwrap(); match assets_write.get_mut(specifier) { Some(a) => *a = asset, @@ -84,7 +84,7 @@ pub trait Asset: Sized { const ENDINGS: &'static [&'static str]; /// Parse the input file and return the correct Asset. - fn parse(buf_reader: BufReader) -> Result; + fn parse(buf_reader: BufReader, specifier: &str) -> Result; // TODO: Remove this function. It's only used in world/ in a really ugly way.To // do this properly assets should have all their necessary data in one file. A @@ -108,12 +108,15 @@ pub trait Asset: Sized { where Self::Output: Send + Sync + 'static, { - let assets_write = ASSETS.read().unwrap(); - match assets_write.get(specifier) { + let assets_read = ASSETS.read().unwrap(); + match assets_read.get(specifier) { Some(asset) => Ok(Arc::clone(asset).downcast()?), None => { - drop(assets_write); // Drop the asset hashmap to permit recursive loading - let asset = Arc::new(f(Self::parse(load_file(specifier, Self::ENDINGS)?)?)); + drop(assets_read); // Drop the asset hashmap to permit recursive loading + let asset = Arc::new(f(Self::parse( + load_file(specifier, Self::ENDINGS)?, + specifier, + )?)); let clone = Arc::clone(&asset); ASSETS.write().unwrap().insert(specifier.to_owned(), clone); Ok(asset) @@ -129,27 +132,13 @@ pub trait Asset: Sized { return Ok(Arc::clone(assets).downcast()?); } - // Get glob matches - let glob_matches = read_dir(specifier.trim_end_matches(".*")).map(|dir| { - dir.filter_map(|direntry| { - direntry.ok().and_then(|file| { - file.file_name() - .to_string_lossy() - .rsplitn(2, '.') - .last() - .map(|s| s.to_owned()) - }) - }) - .collect::>() - }); - - match glob_matches { + match get_glob_matches(specifier) { Ok(glob_matches) => { let assets = Arc::new( glob_matches .into_iter() .filter_map(|name| { - Self::load(&specifier.replace("*", &name)) + Self::load(&name) .map_err(|e| { error!( ?e, @@ -172,6 +161,25 @@ pub trait Asset: Sized { } } + fn load_glob_cloned(specifier: &str) -> Result, Error> + where + Self::Output: Clone + Send + Sync + 'static, + { + match get_glob_matches(specifier) { + Ok(glob_matches) => Ok(glob_matches + .into_iter() + .map(|name| { + let full_specifier = &specifier.replace("*", &name); + ( + Self::load_expect_cloned(full_specifier), + full_specifier.to_string(), + ) + }) + .collect::>()), + Err(error) => Err(error), + } + } + /// Function used to load assets from the filesystem or the cache. /// Example usage: /// ```no_run @@ -267,7 +275,7 @@ pub trait Asset: Sized { impl Asset for DynamicImage { const ENDINGS: &'static [&'static str] = &["png", "jpg"]; - fn parse(mut buf_reader: BufReader) -> Result { + fn parse(mut buf_reader: BufReader, _specifier: &str) -> Result { let mut buf = Vec::new(); buf_reader.read_to_end(&mut buf)?; image::load_from_memory(&buf).map_err(Error::parse_error) @@ -277,7 +285,7 @@ impl Asset for DynamicImage { impl Asset for DotVoxData { const ENDINGS: &'static [&'static str] = &["vox"]; - fn parse(mut buf_reader: BufReader) -> Result { + fn parse(mut buf_reader: BufReader, _specifier: &str) -> Result { let mut buf = Vec::new(); buf_reader.read_to_end(&mut buf)?; dot_vox::load_bytes(&buf).map_err(Error::parse_error) @@ -288,12 +296,12 @@ impl Asset for DotVoxData { impl Asset for Value { const ENDINGS: &'static [&'static str] = &["json"]; - fn parse(buf_reader: BufReader) -> Result { + fn parse(buf_reader: BufReader, _specifier: &str) -> Result { serde_json::from_reader(buf_reader).map_err(Error::parse_error) } } -/// Load fron an arbitrary RON file. +/// Load from an arbitrary RON file. pub struct Ron(pub PhantomData); impl Deserialize<'de>> Asset for Ron { @@ -301,7 +309,7 @@ impl Deserialize<'de>> Asset for Ron { const ENDINGS: &'static [&'static str] = &["ron"]; - fn parse(buf_reader: BufReader) -> Result { + fn parse(buf_reader: BufReader, _specifier: &str) -> Result { ron::de::from_reader(buf_reader).map_err(Error::parse_error) } } @@ -464,3 +472,37 @@ pub fn read_dir(specifier: &str) -> Result { Err(Error::NotFound(dir_name.to_string_lossy().into_owned())) } } + +// Finds all files matching the provided glob specifier - includes files from +// subdirectories +fn get_glob_matches(specifier: &str) -> Result, Error> { + let specifier = specifier.trim_end_matches(".*"); + read_dir(specifier).map(|dir| { + dir.filter_map(|direntry| { + direntry.ok().and_then(|dir_entry| { + if dir_entry.path().is_dir() { + let sub_dir_glob = format!( + "{}.{}.*", + specifier.to_string(), + dir_entry.file_name().to_string_lossy() + ); + Some(get_glob_matches(&sub_dir_glob).ok()?) + } else { + Some(vec![format!( + "{}.{}", + specifier, + dir_entry + .file_name() + .to_string_lossy() + .rsplitn(2, '.') + .last() + .map(|s| s.to_owned()) + .unwrap() + )]) + } + }) + }) + .flat_map(|x| x) + .collect::>() + }) +} diff --git a/common/src/character.rs b/common/src/character.rs index 09ed09fbfb..dbf5f9713c 100644 --- a/common/src/character.rs +++ b/common/src/character.rs @@ -5,25 +5,13 @@ use serde::{Deserialize, Serialize}; /// The limit on how many characters that a player can have pub const MAX_CHARACTERS_PER_PLAYER: usize = 8; - -// TODO: Since loadout persistence came a few weeks after character persistence, -// we stored their main weapon in the `tool` field here. While loadout -// persistence is still new, saved characters may not have an associated loadout -// entry in the DB, so we use this `tool` field to create an entry the first -// time they enter the game. -// -// Once we are happy that all characters have a loadout, or we manually -// update/delete those that don't, it's no longer necessary and we can -// remove this from here, as well as in the DB schema and persistence code. +pub type CharacterId = i64; /// The minimum character data we need to create a new character on the server. -/// The `tool` field was historically used to persist the character's weapon -/// before Loadouts were persisted, and will be removed in the future. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Character { - pub id: Option, + pub id: Option, pub alias: String, - pub tool: Option, } /// Data needed to render a single character item in the character list diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 79292b797e..8505ef2797 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -165,6 +165,26 @@ pub struct ItemConfig { pub dodge_ability: Option, } +impl From for ItemConfig { + fn from(item: Item) -> Self { + if let ItemKind::Tool(tool) = &item.kind() { + let mut abilities = tool.get_abilities(); + let mut ability_drain = abilities.drain(..); + + return ItemConfig { + item, + ability1: ability_drain.next(), + ability2: ability_drain.next(), + ability3: ability_drain.next(), + block_ability: Some(CharacterAbility::BasicBlock), + dodge_ability: Some(CharacterAbility::Roll), + }; + } + + unimplemented!("ItemConfig is currently only supported for Tools") + } +} + #[derive(Arraygen, Clone, PartialEq, Default, Debug, Serialize, Deserialize)] #[gen_array(pub fn get_armor: &Option)] pub struct Loadout { @@ -204,7 +224,7 @@ impl Loadout { .iter() .flat_map(|armor| armor.as_ref()) .filter_map(|item| { - if let ItemKind::Armor(armor) = &item.kind { + if let ItemKind::Armor(armor) = &item.kind() { Some(armor.get_protection()) } else { None diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index f4ae3795b2..ce9895893a 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -127,7 +127,7 @@ impl< { const ENDINGS: &'static [&'static str] = &["json"]; - fn parse(buf_reader: BufReader) -> Result { + fn parse(buf_reader: BufReader, _specifier: &str) -> Result { serde_json::de::from_reader(buf_reader).map_err(assets::Error::parse_error) } } diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index c8eee66e91..448be8e1f6 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -5,15 +5,22 @@ pub mod tool; pub use tool::{Hands, Tool, ToolCategory, ToolKind}; use crate::{ - assets::{self, Asset, Ron}, + assets::{self, Asset, Error}, effect::Effect, lottery::Lottery, terrain::{Block, BlockKind}, }; +use crossbeam::atomic::AtomicCell; use rand::prelude::*; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; +use std::{ + fs::File, + io::BufReader, + num::{NonZeroU32, NonZeroU64}, + sync::Arc, +}; use vek::Rgb; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -51,8 +58,6 @@ impl Lantern { pub fn color(&self) -> Rgb { self.color.map(|c| c as f32 / 255.0) } } -fn default_amount() -> u32 { 1 } - #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum ItemKind { /// Something wieldable @@ -62,85 +67,211 @@ pub enum ItemKind { Consumable { kind: String, effect: Effect, - #[serde(default = "default_amount")] - amount: u32, }, Throwable { kind: Throwable, - #[serde(default = "default_amount")] - amount: u32, }, Utility { kind: Utility, - #[serde(default = "default_amount")] - amount: u32, }, Ingredient { kind: String, - #[serde(default = "default_amount")] - amount: u32, }, } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub type ItemId = AtomicCell>; + +/* /// The only way to access an item id outside this module is to mutably, atomically update it using +/// this structure. It has a single method, `try_assign_id`, which attempts to set the id if and +/// only if it's not already set. +pub struct CreateDatabaseItemId { + item_id: Arc, +} + +pub struct CreateDatabaseItemId { + item_id: Arc, +} */ + +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Item { - name: String, - description: String, + /// item_id is hidden because it represents the persistent, storage entity + /// ID for any item that has been saved to the database. Additionally, + /// it (currently) holds interior mutable state, making it very + /// dangerous to expose. We will work to eliminate this issue soon; for + /// now, we try to make the system as foolproof as possible by greatly + /// restricting opportunities for cloning the item_id. + #[serde(skip)] + item_id: Arc, + /// item_def is hidden because changing the item definition for an item + /// could change invariants like whether it was stackable (invalidating + /// the amount). + item_def: Arc, + /// amount is hidden because it needs to maintain the invariant that only + /// stackable items can have > 1 amounts. + amount: NonZeroU32, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ItemDef { + #[serde(skip)] + item_definition_id: String, + pub name: String, + pub description: String, pub kind: ItemKind, } -pub type ItemAsset = Ron; +impl ItemDef { + pub fn is_stackable(&self) -> bool { + matches!(self.kind, ItemKind::Consumable { .. } + | ItemKind::Ingredient { .. } + | ItemKind::Throwable { .. } + | ItemKind::Utility { .. }) + } +} + +impl PartialEq for Item { + fn eq(&self, other: &Self) -> bool { + self.item_def.item_definition_id == other.item_def.item_definition_id + } +} + +impl Asset for ItemDef { + const ENDINGS: &'static [&'static str] = &["ron"]; + + fn parse(buf_reader: BufReader, specifier: &str) -> Result { + let item: Result = + ron::de::from_reader(buf_reader).map_err(Error::parse_error); + + // Some commands like /give_item provide the asset specifier separated with \ + // instead of . + let specifier = specifier.replace('\\', "."); + + item.map(|item| ItemDef { + item_definition_id: specifier, + ..item + }) + } +} impl Item { // TODO: consider alternatives such as default abilities that can be added to a // loadout when no weapon is present - pub fn empty() -> Self { - Self { - name: "Empty Item".to_owned(), - description: "This item may grant abilities, but is invisible".to_owned(), - kind: ItemKind::Tool(Tool::empty()), + pub fn empty() -> Self { Item::new(ItemDef::load_expect("common.items.weapons.empty.empty")) } + + pub fn new(inner_item: Arc) -> Self { + Item { + item_id: Arc::new(AtomicCell::new(None)), + item_def: inner_item, + amount: NonZeroU32::new(1).unwrap(), } } - pub fn expect_from_asset(asset: &str) -> Self { (*ItemAsset::load_expect(asset)).clone() } + /// Creates a new instance of an `Item` from the provided asset identifier + /// Panics if the asset does not exist. + pub fn new_from_asset_expect(asset_specifier: &str) -> Self { + let inner_item = ItemDef::load_expect(asset_specifier); + Item::new(inner_item) + } + + /// Creates a Vec containing one of each item that matches the provided + /// asset glob pattern + pub fn new_from_asset_glob(asset_glob: &str) -> Result, Error> { + let items = ItemDef::load_glob(asset_glob)?; + + let result = items + .iter() + .map(|item_def| Item::new(Arc::clone(item_def))) + .collect::>(); + + Ok(result) + } + + /// Creates a new instance of an `Item from the provided asset identifier if + /// it exists + pub fn new_from_asset(asset: &str) -> Result { + let inner_item = ItemDef::load(asset)?; + Ok(Item::new(inner_item)) + } + + /// Duplicates an item, creating an exact copy but with a new item ID + pub fn duplicate(&self) -> Self { Item::new(Arc::clone(&self.item_def)) } + + /// FIXME: HACK: In order to set the entity ID asynchronously, we currently + /// start it at None, and then atomically set it when it's saved for the + /// first time in the database. Because this requires shared mutable + /// state if these aren't synchronized by the program structure, + /// currently we use an Atomic inside an Arc; this is clearly very + /// dangerous, so in the future we will hopefully have a better way of + /// dealing with this. + #[doc(hidden)] + pub fn get_item_id_for_database(&self) -> Arc { Arc::clone(&self.item_id) } + + /// Resets the item's item ID to None, giving it a new identity. Used when + /// dropping items into the world so that a new database record is + /// created when they are picked up again. + /// + /// NOTE: The creation of a new `Arc` when resetting the item ID is critical + /// because every time a new `Item` instance is created, it is cloned from + /// a single asset which results in an `Arc` pointing to the same value in + /// memory. Therefore, every time an item instance is created this + /// method must be called in order to give it a unique identity. + fn reset_item_id(&mut self) { + if let Some(item_id) = Arc::get_mut(&mut self.item_id) { + *item_id = AtomicCell::new(None); + } else { + self.item_id = Arc::new(AtomicCell::new(None)); + } + } + + /// Removes the unique identity of an item - used when dropping an item on + /// the floor. In the future this will need to be changed if we want to + /// maintain a unique ID for an item even when it's dropped and picked + /// up by another player. + pub fn put_in_world(&mut self) { self.reset_item_id() } + + pub fn increase_amount(&mut self, increase_by: u32) -> Result<(), assets::Error> { + let amount = u32::from(self.amount); + self.amount = amount + .checked_add(increase_by) + .and_then(NonZeroU32::new) + .ok_or(assets::Error::InvalidType)?; + Ok(()) + } + + pub fn decrease_amount(&mut self, decrease_by: u32) -> Result<(), assets::Error> { + let amount = u32::from(self.amount); + self.amount = amount + .checked_sub(decrease_by) + .and_then(NonZeroU32::new) + .ok_or(assets::Error::InvalidType)?; + Ok(()) + } pub fn set_amount(&mut self, give_amount: u32) -> Result<(), assets::Error> { - use ItemKind::*; - match self.kind { - Consumable { ref mut amount, .. } - | Throwable { ref mut amount, .. } - | Utility { ref mut amount, .. } - | Ingredient { ref mut amount, .. } => { - *amount = give_amount; - Ok(()) - }, - Tool { .. } | Lantern { .. } | Armor { .. } => { - // Tools and armor don't stack - Err(assets::Error::InvalidType) - }, + if give_amount == 1 || self.item_def.is_stackable() { + self.amount = NonZeroU32::new(give_amount).ok_or(assets::Error::InvalidType)?; + Ok(()) + } else { + Err(assets::Error::InvalidType) } } - pub fn name(&self) -> &str { &self.name } + pub fn item_definition_id(&self) -> &str { &self.item_def.item_definition_id } - pub fn description(&self) -> &str { &self.description } + pub fn is_stackable(&self) -> bool { self.item_def.is_stackable() } - pub fn amount(&self) -> u32 { - match &self.kind { - ItemKind::Tool(_) => 1, - ItemKind::Lantern(_) => 1, - ItemKind::Armor { .. } => 1, - ItemKind::Consumable { amount, .. } => *amount, - ItemKind::Throwable { amount, .. } => *amount, - ItemKind::Utility { amount, .. } => *amount, - ItemKind::Ingredient { amount, .. } => *amount, - } - } + pub fn name(&self) -> &str { &self.item_def.name } + + pub fn description(&self) -> &str { &self.item_def.description } + + pub fn kind(&self) -> &ItemKind { &self.item_def.kind } + + pub fn amount(&self) -> u32 { u32::from(self.amount) } pub fn try_reclaim_from_block(block: Block) -> Option { let chosen; let mut rng = rand::thread_rng(); - Some(ItemAsset::load_expect_cloned(match block.kind() { + Some(Item::new_from_asset_expect(match block.kind() { BlockKind::Apple => "common.items.food.apple", BlockKind::Mushroom => "common.items.food.mushroom", BlockKind::Velorite => "common.items.ore.velorite", @@ -182,7 +313,7 @@ impl Item { /// (i.e: one may be substituted for the other in crafting recipes or /// item possession checks). pub fn superficially_eq(&self, other: &Self) -> bool { - match (&self.kind, &other.kind) { + match (&self.kind(), &other.kind()) { (ItemKind::Tool(a), ItemKind::Tool(b)) => a.superficially_eq(b), // TODO: Differentiate between lantern colors? (ItemKind::Lantern(_), ItemKind::Lantern(_)) => true, diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index db5baf3541..146a1d4816 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -1,20 +1,20 @@ pub mod item; pub mod slot; -use crate::{assets::Asset, recipe::Recipe}; -use item::{Item, ItemAsset, ItemKind}; +use crate::recipe::Recipe; +use core::ops::Not; +use item::Item; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage, HashMapStorage}; use specs_idvs::IdvStorage; -use std::ops::Not; // The limit on distance between the entity and a collectible (squared) pub const MAX_PICKUP_RANGE_SQR: f32 = 64.0; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Inventory { - pub slots: Vec>, - pub amount: u32, + slots: Vec>, + amount: u32, } /// Errors which the methods on `Inventory` produce @@ -27,10 +27,20 @@ pub enum Error { #[allow(clippy::len_without_is_empty)] // TODO: Pending review in #587 impl Inventory { + pub fn new_empty() -> Inventory { + Inventory { + slots: vec![None; 36], + amount: 0, + } + } + pub fn slots(&self) -> &[Option] { &self.slots } pub fn len(&self) -> usize { self.slots.len() } + /// Total number of occupied slots in the inventory. + pub fn amount(&self) -> u32 { self.amount } + pub fn recount_items(&mut self) { self.amount = self.slots.iter().filter(|i| i.is_some()).count() as u32; } @@ -38,135 +48,23 @@ impl Inventory { /// Adds a new item to the first fitting group of the inventory or starts a /// new group. Returns the item again if no space was found. pub fn push(&mut self, item: Item) -> Option { - let item = match &item.kind { - ItemKind::Tool(_) | ItemKind::Armor { .. } | ItemKind::Lantern(_) => { - self.add_to_first_empty(item) - }, - ItemKind::Utility { - kind: item_kind, - amount: new_amount, - } => { - for slot in &mut self.slots { - if slot - .as_ref() - .map(|s| s.name() == item.name()) - .unwrap_or(false) - && slot - .as_ref() - .map(|s| s.description() == item.description()) - .unwrap_or(false) - { - if let Some(Item { - kind: ItemKind::Utility { kind, amount }, - .. - }) = slot - { - if *item_kind == *kind { - *amount += new_amount; - self.recount_items(); - return None; - } - } - } - } - // It didn't work - self.add_to_first_empty(item) - }, - ItemKind::Consumable { - kind: item_kind, - amount: new_amount, - .. - } => { - for slot in &mut self.slots { - if slot - .as_ref() - .map(|s| s.name() == item.name()) - .unwrap_or(false) - && slot - .as_ref() - .map(|s| s.description() == item.description()) - .unwrap_or(false) - { - if let Some(Item { - kind: ItemKind::Consumable { kind, amount, .. }, - .. - }) = slot - { - if *item_kind == *kind { - *amount += new_amount; - self.recount_items(); - return None; - } - } - } - } - // It didn't work - self.add_to_first_empty(item) - }, - ItemKind::Throwable { - kind: item_kind, - amount: new_amount, - .. - } => { - for slot in &mut self.slots { - if slot - .as_ref() - .map(|s| s.name() == item.name()) - .unwrap_or(false) - && slot - .as_ref() - .map(|s| s.description() == item.description()) - .unwrap_or(false) - { - if let Some(Item { - kind: ItemKind::Throwable { kind, amount, .. }, - .. - }) = slot - { - if *item_kind == *kind { - *amount += new_amount; - self.recount_items(); - return None; - } - } - } - } - // It didn't work - self.add_to_first_empty(item) - }, - ItemKind::Ingredient { - kind: item_kind, - amount: new_amount, - } => { - for slot in &mut self.slots { - if slot - .as_ref() - .map(|s| s.name() == item.name()) - .unwrap_or(false) - && slot - .as_ref() - .map(|s| s.description() == item.description()) - .unwrap_or(false) - { - if let Some(Item { - kind: ItemKind::Ingredient { kind, amount }, - .. - }) = slot - { - if *item_kind == *kind { - *amount += new_amount; - self.recount_items(); - return None; - } - } - } - } - // It didn't work - self.add_to_first_empty(item) - }, - }; - self.recount_items(); - item + if item.is_stackable() { + if let Some(slot_item) = self + .slots + .iter_mut() + .filter_map(Option::as_mut) + .find(|s| *s == &item) + { + return slot_item + .increase_amount(item.amount()) + .err() + .and(Some(item)); + } + } + + // No existing item to stack with or item not stackable, put the item in a new + // slot + self.add_to_first_empty(item) } /// Adds a new item to the first empty slot of the inventory. Returns the @@ -193,7 +91,6 @@ impl Inventory { leftovers.push(item); } } - self.recount_items(); if !leftovers.is_empty() { Err(Error::Full(leftovers)) } else { @@ -229,9 +126,10 @@ impl Inventory { pub fn insert(&mut self, cell: usize, item: Item) -> Result, Item> { match self.slots.get_mut(cell) { Some(slot) => { - let old = slot.take(); - *slot = Some(item); - self.recount_items(); + let old = core::mem::replace(slot, Some(item)); + if old.is_some() { + self.recount_items(); + } Ok(old) }, None => Err(item), @@ -241,126 +139,25 @@ impl Inventory { /// Checks if inserting item exists in given cell. Inserts an item if it /// exists. pub fn insert_or_stack(&mut self, cell: usize, item: Item) -> Result, Item> { - match &item.kind { - ItemKind::Tool(_) | ItemKind::Armor { .. } | ItemKind::Lantern(_) => { - self.insert(cell, item) - }, - ItemKind::Utility { - amount: new_amount, .. - } => match self.slots.get_mut(cell) { + if item.is_stackable() { + match self.slots.get_mut(cell) { Some(Some(slot_item)) => { - if slot_item.name() == item.name() - && slot_item.description() == item.description() - { - if let Item { - kind: ItemKind::Utility { amount, .. }, - .. - } = slot_item - { - *amount += *new_amount; - self.recount_items(); - Ok(None) - } else { - let old_item = std::mem::replace(slot_item, item); - self.recount_items(); - Ok(Some(old_item)) - } + Ok(if slot_item == &item { + slot_item + .increase_amount(item.amount()) + .err() + .and(Some(item)) } else { - let old_item = std::mem::replace(slot_item, item); - self.recount_items(); - Ok(Some(old_item)) - } + let old_item = core::mem::replace(slot_item, item); + // No need to recount--we know the count is the same. + Some(old_item) + }) }, Some(None) => self.insert(cell, item), None => Err(item), - }, - ItemKind::Ingredient { - amount: new_amount, .. - } => match self.slots.get_mut(cell) { - Some(Some(slot_item)) => { - if slot_item.name() == item.name() - && slot_item.description() == item.description() - { - if let Item { - kind: ItemKind::Ingredient { amount, .. }, - .. - } = slot_item - { - *amount += *new_amount; - self.recount_items(); - Ok(None) - } else { - let old_item = std::mem::replace(slot_item, item); - self.recount_items(); - Ok(Some(old_item)) - } - } else { - let old_item = std::mem::replace(slot_item, item); - self.recount_items(); - Ok(Some(old_item)) - } - }, - Some(None) => self.insert(cell, item), - None => Err(item), - }, - ItemKind::Consumable { - amount: new_amount, .. - } => match self.slots.get_mut(cell) { - Some(Some(slot_item)) => { - if slot_item.name() == item.name() - && slot_item.description() == item.description() - { - if let Item { - kind: ItemKind::Consumable { amount, .. }, - .. - } = slot_item - { - *amount += *new_amount; - self.recount_items(); - Ok(None) - } else { - let old_item = std::mem::replace(slot_item, item); - self.recount_items(); - Ok(Some(old_item)) - } - } else { - let old_item = std::mem::replace(slot_item, item); - self.recount_items(); - Ok(Some(old_item)) - } - }, - Some(None) => self.insert(cell, item), - None => Err(item), - }, - ItemKind::Throwable { - amount: new_amount, .. - } => match self.slots.get_mut(cell) { - Some(Some(slot_item)) => { - if slot_item.name() == item.name() - && slot_item.description() == item.description() - { - if let Item { - kind: ItemKind::Throwable { amount, .. }, - .. - } = slot_item - { - *amount += *new_amount; - self.recount_items(); - Ok(None) - } else { - let old_item = std::mem::replace(slot_item, item); - self.recount_items(); - Ok(Some(old_item)) - } - } else { - let old_item = std::mem::replace(slot_item, item); - self.recount_items(); - Ok(Some(old_item)) - } - }, - Some(None) => self.insert(cell, item), - None => Err(item), - }, + } + } else { + self.insert(cell, item) } } @@ -396,68 +193,17 @@ impl Inventory { /// Remove just one item from the slot pub fn take(&mut self, cell: usize) -> Option { if let Some(Some(item)) = self.slots.get_mut(cell) { - let mut return_item = item.clone(); - match &mut item.kind { - ItemKind::Tool(_) | ItemKind::Armor { .. } | ItemKind::Lantern(_) => { - self.remove(cell) - }, - ItemKind::Utility { kind, amount } => { - if *amount <= 1 { - self.remove(cell) - } else { - *amount -= 1; - return_item.kind = ItemKind::Utility { - kind: *kind, - amount: 1, - }; - self.recount_items(); - Some(return_item) - } - }, - ItemKind::Consumable { - kind, - amount, - effect, - } => { - if *amount <= 1 { - self.remove(cell) - } else { - *amount -= 1; - return_item.kind = ItemKind::Consumable { - kind: kind.clone(), - effect: *effect, - amount: 1, - }; - self.recount_items(); - Some(return_item) - } - }, - ItemKind::Throwable { kind, amount } => { - if *amount <= 1 { - self.remove(cell) - } else { - *amount -= 1; - return_item.kind = ItemKind::Throwable { - kind: *kind, - amount: 1, - }; - self.recount_items(); - Some(return_item) - } - }, - ItemKind::Ingredient { kind, amount } => { - if *amount <= 1 { - self.remove(cell) - } else { - *amount -= 1; - return_item.kind = ItemKind::Ingredient { - kind: kind.clone(), - amount: 1, - }; - self.recount_items(); - Some(return_item) - } - }, + let mut return_item = item.duplicate(); + + if item.is_stackable() && item.amount() > 1 { + item.decrease_amount(1).ok()?; + return_item + .set_amount(1) + .expect("Items duplicated from a stackable item must be stackable."); + self.recount_items(); + Some(return_item) + } else { + self.remove(cell) } } else { None @@ -465,12 +211,12 @@ impl Inventory { } /// Determine how many of a particular item there is in the inventory. - pub fn item_count(&self, item: &Item) -> usize { + pub fn item_count(&self, item: &Item) -> u64 { self.slots() .iter() .flatten() .filter(|it| it.superficially_eq(item)) - .map(|it| it.amount() as usize) + .map(|it| u64::from(it.amount())) .sum() } @@ -482,7 +228,7 @@ impl Inventory { pub fn contains_ingredients<'a>( &self, recipe: &'a Recipe, - ) -> Result, Vec<(&'a Item, usize)>> { + ) -> Result, Vec<(&'a Item, u32)>> { let mut slot_claims = vec![0; self.slots.len()]; let mut missing = Vec::new(); @@ -491,7 +237,7 @@ impl Inventory { for (i, slot) in self.slots().iter().enumerate() { if let Some(item) = slot.as_ref().filter(|item| item.superficially_eq(input)) { - let can_claim = (item.amount() as usize - slot_claims[i]).min(needed); + let can_claim = (item.amount() - slot_claims[i]).min(needed); slot_claims[i] += can_claim; needed -= can_claim; contains_any = true; @@ -517,8 +263,8 @@ impl Default for Inventory { slots: vec![None; 36], amount: 0, }; - inventory.push(ItemAsset::load_expect_cloned("common.items.food.cheese")); - inventory.push(ItemAsset::load_expect_cloned("common.items.food.apple")); + inventory.push(Item::new_from_asset_expect("common.items.food.cheese")); + inventory.push(Item::new_from_asset_expect("common.items.food.apple")); inventory } } diff --git a/common/src/comp/inventory/slot.rs b/common/src/comp/inventory/slot.rs index bcf380d5a4..02de138f85 100644 --- a/common/src/comp/inventory/slot.rs +++ b/common/src/comp/inventory/slot.rs @@ -1,6 +1,6 @@ use crate::{ comp, - comp::{item, item::armor}, + comp::{item, item::armor, ItemConfig}, }; use comp::{Inventory, Loadout}; use serde::{Deserialize, Serialize}; @@ -82,29 +82,6 @@ impl ArmorSlot { } } -// TODO: There are plans to save the selected abilities for each tool even -// when they are not equipped, when that is implemented this helper function -// should no longer be needed - -/// Create an ItemConfig for an item. Apply abilities to item. -fn item_config(item: item::Item) -> comp::ItemConfig { - let mut abilities = if let item::ItemKind::Tool(tool) = &item.kind { - tool.get_abilities() - } else { - Vec::new() - } - .into_iter(); - - comp::ItemConfig { - item, - ability1: abilities.next(), - ability2: abilities.next(), - ability3: abilities.next(), - block_ability: Some(comp::CharacterAbility::BasicBlock), - dodge_ability: Some(comp::CharacterAbility::Roll), - } -} - /// Replace an equipment slot with an item. Return the item that was in the /// slot, if any. Doesn't update the inventory. fn loadout_replace( @@ -127,10 +104,10 @@ fn loadout_replace( EquipSlot::Armor(ArmorSlot::Tabard) => replace(&mut loadout.tabard, item), EquipSlot::Lantern => replace(&mut loadout.lantern, item), EquipSlot::Mainhand => { - replace(&mut loadout.active_item, item.map(item_config)).map(|i| i.item) + replace(&mut loadout.active_item, item.map(ItemConfig::from)).map(|i| i.item) }, EquipSlot::Offhand => { - replace(&mut loadout.second_item, item.map(item_config)).map(|i| i.item) + replace(&mut loadout.second_item, item.map(ItemConfig::from)).map(|i| i.item) }, } } @@ -157,14 +134,11 @@ fn loadout_insert( /// LoadoutBuilder, /// }; /// -/// let mut inv = Inventory { -/// slots: vec![None], -/// amount: 0, -/// }; +/// let mut inv = Inventory::new_empty(); /// /// let mut loadout = LoadoutBuilder::new() /// .defaults() -/// .active_item(LoadoutBuilder::default_item_config_from_str(Some( +/// .active_item(Some(LoadoutBuilder::default_item_config_from_str( /// "common.items.weapons.sword.zweihander_sword_0", /// ))) /// .build(); @@ -188,7 +162,7 @@ fn swap_inventory_loadout( // Check if loadout slot can hold item if inventory .get(inventory_slot) - .map_or(true, |item| equip_slot.can_hold(&item.kind)) + .map_or(true, |item| equip_slot.can_hold(&item.kind())) { // Take item from loadout let from_equip = loadout_remove(equip_slot, loadout); @@ -220,8 +194,8 @@ fn swap_loadout(slot_a: EquipSlot, slot_b: EquipSlot, loadout: &mut Loadout) { let item_a = loadout_remove(slot_a, loadout); let item_b = loadout_remove(slot_b, loadout); // Check if items can go in the other slots - if item_a.as_ref().map_or(true, |i| slot_b.can_hold(&i.kind)) - && item_b.as_ref().map_or(true, |i| slot_a.can_hold(&i.kind)) + if item_a.as_ref().map_or(true, |i| slot_b.can_hold(&i.kind())) + && item_b.as_ref().map_or(true, |i| slot_a.can_hold(&i.kind())) { // Swap loadout_replace(slot_b, item_a, loadout).unwrap_none(); @@ -268,32 +242,27 @@ pub fn swap( /// use veloren_common::{ /// assets::Asset, /// comp::{ -/// item::ItemAsset, /// slot::{equip, EquipSlot}, /// Inventory, Item, /// }, /// LoadoutBuilder, /// }; /// -/// let boots: Option = Some(ItemAsset::load_expect_cloned( -/// "common.items.testing.test_boots", -/// )); +/// let boots = Item::new_from_asset_expect("common.items.testing.test_boots"); /// -/// let mut inv = Inventory { -/// slots: vec![boots.clone()], -/// amount: 1, -/// }; +/// let mut inv = Inventory::new_empty(); +/// inv.push(boots.duplicate()); /// /// let mut loadout = LoadoutBuilder::new().defaults().build(); /// /// equip(0, &mut inv, &mut loadout); -/// assert_eq!(boots, loadout.foot); +/// assert_eq!(Some(boots), loadout.foot); /// ``` pub fn equip(slot: usize, inventory: &mut Inventory, loadout: &mut Loadout) { use armor::Armor; use item::{armor::ArmorKind, ItemKind}; - let equip_slot = inventory.get(slot).and_then(|i| match &i.kind { + let equip_slot = inventory.get(slot).and_then(|i| match &i.kind() { ItemKind::Tool(_) => Some(EquipSlot::Mainhand), ItemKind::Armor(Armor { kind, .. }) => Some(EquipSlot::Armor(match kind { ArmorKind::Head(_) => ArmorSlot::Head, @@ -335,14 +304,11 @@ pub fn equip(slot: usize, inventory: &mut Inventory, loadout: &mut Loadout) { /// LoadoutBuilder, /// }; /// -/// let mut inv = Inventory { -/// slots: vec![None], -/// amount: 0, -/// }; +/// let mut inv = Inventory::new_empty(); /// /// let mut loadout = LoadoutBuilder::new() /// .defaults() -/// .active_item(LoadoutBuilder::default_item_config_from_str(Some( +/// .active_item(Some(LoadoutBuilder::default_item_config_from_str( /// "common.items.weapons.sword.zweihander_sword_0", /// ))) /// .build(); @@ -362,7 +328,7 @@ pub fn unequip(slot: EquipSlot, inventory: &mut Inventory, loadout: &mut Loadout #[cfg(test)] mod tests { use super::*; - use crate::{assets::Asset, comp::item::ItemAsset, LoadoutBuilder}; + use crate::{comp::Item, LoadoutBuilder}; #[test] fn test_unequip_items_both_hands() { @@ -371,37 +337,37 @@ mod tests { amount: 0, }; - let sword = LoadoutBuilder::default_item_config_from_str(Some( + let sword = LoadoutBuilder::default_item_config_from_str( "common.items.weapons.sword.zweihander_sword_0", - )); + ); let mut loadout = LoadoutBuilder::new() .defaults() - .active_item(sword.clone()) - .second_item(sword.clone()) + .active_item(Some(sword.clone())) + .second_item(Some(sword.clone())) .build(); - assert_eq!(sword, loadout.active_item); + assert_eq!(Some(sword.clone()), loadout.active_item); unequip(EquipSlot::Mainhand, &mut inv, &mut loadout); // We have space in the inventory, so this should have unequipped assert_eq!(None, loadout.active_item); unequip(EquipSlot::Offhand, &mut inv, &mut loadout); // There is no more space in the inventory, so this should still be equipped - assert_eq!(sword, loadout.second_item); + assert_eq!(Some(sword.clone()), loadout.second_item); // Verify inventory - assert_eq!(inv.slots[0], Some(sword.unwrap().item)); + assert_eq!(inv.slots[0], Some(sword.item)); assert_eq!(inv.slots.len(), 1); } #[test] fn test_equip_item() { - let boots: Option = Some(ItemAsset::load_expect_cloned( + let boots: Option = Some(Item::new_from_asset_expect( "common.items.testing.test_boots", )); - let starting_sandles: Option = Some(ItemAsset::load_expect_cloned( + let starting_sandles: Option = Some(Item::new_from_asset_expect( "common.items.armor.starter.sandals_0", )); @@ -426,11 +392,11 @@ mod tests { #[test] fn test_loadout_replace() { - let boots: Option = Some(ItemAsset::load_expect_cloned( + let boots: Option = Some(Item::new_from_asset_expect( "common.items.testing.test_boots", )); - let starting_sandles: Option = Some(ItemAsset::load_expect_cloned( + let starting_sandles: Option = Some(Item::new_from_asset_expect( "common.items.armor.starter.sandals_0", )); @@ -455,18 +421,18 @@ mod tests { #[test] fn test_loadout_remove() { - let sword = LoadoutBuilder::default_item_config_from_str(Some( + let sword = LoadoutBuilder::default_item_config_from_str( "common.items.weapons.sword.zweihander_sword_0", - )); + ); let mut loadout = LoadoutBuilder::new() .defaults() - .active_item(sword.clone()) + .active_item(Some(sword.clone())) .build(); // The swap should return the sword assert_eq!( - Some(sword.unwrap().item), + Some(sword.item), loadout_remove(EquipSlot::Mainhand, &mut loadout,) ); diff --git a/common/src/comp/inventory/test.rs b/common/src/comp/inventory/test.rs index e27bdf9274..0b6aae5464 100644 --- a/common/src/comp/inventory/test.rs +++ b/common/src/comp/inventory/test.rs @@ -1,10 +1,9 @@ use super::*; -use crate::assets::Asset; use lazy_static::lazy_static; lazy_static! { static ref TEST_ITEMS: Vec = vec![ - item::ItemAsset::load_expect_cloned("common.items.debug.boost"), - item::ItemAsset::load_expect_cloned("common.items.debug.possess") + Item::new_from_asset_expect("common.items.debug.boost"), + Item::new_from_asset_expect("common.items.debug.possess") ]; } diff --git a/common/src/comp/player.rs b/common/src/comp/player.rs index d0376c2e5a..a6818af377 100644 --- a/common/src/comp/player.rs +++ b/common/src/comp/player.rs @@ -1,3 +1,4 @@ +use crate::character::CharacterId; use authc::Uuid; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage, NullStorage}; @@ -9,7 +10,7 @@ pub const MAX_MOUNT_RANGE_SQR: i32 = 20000; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Player { pub alias: String, - pub character_id: Option, + pub character_id: Option, pub view_distance: Option, uuid: Uuid, } @@ -17,7 +18,7 @@ pub struct Player { impl Player { pub fn new( alias: String, - character_id: Option, + character_id: Option, view_distance: Option, uuid: Uuid, ) -> Self { diff --git a/common/src/comp/stats.rs b/common/src/comp/stats.rs index a45f3cfaff..4cb6bfd6b3 100644 --- a/common/src/comp/stats.rs +++ b/common/src/comp/stats.rs @@ -204,6 +204,33 @@ impl Stats { stats } + /// Creates an empty `Stats` instance - used during character loading from + /// the database + pub fn empty() -> Self { + Self { + name: "".to_owned(), + health: Health { + current: 0, + maximum: 0, + last_change: (0.0, HealthChange { + amount: 0, + cause: HealthSource::Revive, + }), + }, + level: Level { amount: 1 }, + exp: Exp { + current: 0, + maximum: 50, + }, + skill_set: SkillSet::default(), + endurance: 0, + fitness: 0, + willpower: 0, + is_dead: false, + body_type: comp::Body::Humanoid(comp::body::humanoid::Body::random()), + } + } + pub fn with_max_health(mut self, amount: u32) -> Self { self.health.maximum = amount; self.health.current = amount; diff --git a/common/src/event.rs b/common/src/event.rs index 671af425a6..7098522e93 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -1,4 +1,4 @@ -use crate::{comp, sync::Uid, util::Dir}; +use crate::{character::CharacterId, comp, sync::Uid, util::Dir}; use comp::item::{Item, Reagent}; use parking_lot::Mutex; use specs::Entity as EcsEntity; @@ -59,7 +59,7 @@ pub enum ServerEvent { /// Inserts default components for a character when loading into the game InitCharacterData { entity: EcsEntity, - character_id: i32, + character_id: CharacterId, }, UpdateCharacterData { entity: EcsEntity, diff --git a/common/src/loadout_builder.rs b/common/src/loadout_builder.rs index 968e648ff7..eb4dd66d37 100644 --- a/common/src/loadout_builder.rs +++ b/common/src/loadout_builder.rs @@ -1,10 +1,4 @@ -use crate::{ - assets::Asset, - comp::{ - item::{Item, ItemAsset, ItemKind}, - Body, CharacterAbility, ItemConfig, Loadout, - }, -}; +use crate::comp::{item::Item, Body, CharacterAbility, ItemConfig, Loadout}; use std::time::Duration; /// Builder for character Loadouts, containing weapon and armour items belonging @@ -17,9 +11,9 @@ use std::time::Duration; /// // Build a loadout with character starter defaults and a specific sword with default sword abilities /// let loadout = LoadoutBuilder::new() /// .defaults() -/// .active_item(LoadoutBuilder::default_item_config_from_str( -/// Some("common.items.weapons.sword.zweihander_sword_0"), -/// )) +/// .active_item(Some(LoadoutBuilder::default_item_config_from_str( +/// "common.items.weapons.sword.zweihander_sword_0" +/// ))) /// .build(); /// ``` pub struct LoadoutBuilder(Loadout); @@ -48,16 +42,16 @@ impl LoadoutBuilder { /// Set default armor items for the loadout. This may vary with game /// updates, but should be safe defaults for a new character. pub fn defaults(self) -> Self { - self.chest(Some(ItemAsset::load_expect_cloned( + self.chest(Some(Item::new_from_asset_expect( "common.items.armor.starter.rugged_chest", ))) - .pants(Some(ItemAsset::load_expect_cloned( + .pants(Some(Item::new_from_asset_expect( "common.items.armor.starter.rugged_pants", ))) - .foot(Some(ItemAsset::load_expect_cloned( + .foot(Some(Item::new_from_asset_expect( "common.items.armor.starter.sandals_0", ))) - .lantern(Some(ItemAsset::load_expect_cloned( + .lantern(Some(Item::new_from_asset_expect( "common.items.armor.starter.lantern", ))) } @@ -66,7 +60,7 @@ impl LoadoutBuilder { pub fn animal(body: Body) -> Self { Self(Loadout { active_item: Some(ItemConfig { - item: ItemAsset::load_expect_cloned("common.items.weapons.empty.empty"), + item: Item::new_from_asset_expect("common.items.weapons.empty.empty"), ability1: Some(CharacterAbility::BasicMelee { energy_cost: 10, buildup_duration: Duration::from_millis(600), @@ -102,31 +96,7 @@ impl LoadoutBuilder { /// abilities or their timings is desired, you should create and provide /// the item config directly to the [active_item](#method.active_item) /// method - pub fn default_item_config_from_item(maybe_item: Option) -> Option { - if let Some(item) = maybe_item { - if let ItemKind::Tool(tool) = &item.kind { - let mut abilities = tool.get_abilities(); - let mut ability_drain = abilities.drain(..); - - return Some(ItemConfig { - item, - ability1: ability_drain.next(), - ability2: ability_drain.next(), - ability3: ability_drain.next(), - block_ability: Some(CharacterAbility::BasicBlock), - dodge_ability: Some(CharacterAbility::Roll), - }); - } - } - - None - } - - /// Get an [Item](../comp/struct.Item.html) by its string - /// reference by loading its asset - pub fn item_from_str(item_ref: Option<&str>) -> Option { - item_ref.and_then(|specifier| ItemAsset::load_cloned(&specifier).ok()) - } + pub fn default_item_config_from_item(item: Item) -> ItemConfig { ItemConfig::from(item) } /// Get an item's (weapon's) default /// [ItemConfig](../comp/struct.ItemConfig.html) @@ -134,8 +104,8 @@ impl LoadoutBuilder { /// the default abilities for that item via the /// [default_item_config_from_item](#method.default_item_config_from_item) /// function - pub fn default_item_config_from_str(item_ref: Option<&str>) -> Option { - Self::default_item_config_from_item(Self::item_from_str(item_ref)) + pub fn default_item_config_from_str(item_ref: &str) -> ItemConfig { + Self::default_item_config_from_item(Item::new_from_asset_expect(item_ref)) } pub fn active_item(mut self, item: Option) -> Self { diff --git a/common/src/lottery.rs b/common/src/lottery.rs index 423575d62c..2b04b7231d 100644 --- a/common/src/lottery.rs +++ b/common/src/lottery.rs @@ -12,7 +12,7 @@ pub struct Lottery { impl Asset for Lottery { const ENDINGS: &'static [&'static str] = &["ron"]; - fn parse(buf_reader: BufReader) -> Result { + fn parse(buf_reader: BufReader, _specifier: &str) -> Result { ron::de::from_reader::, Vec<(f32, T)>>(buf_reader) .map(|items| Lottery::from_rates(items.into_iter())) .map_err(assets::Error::parse_error) @@ -48,16 +48,17 @@ impl Lottery { #[cfg(test)] mod tests { use super::*; - use crate::comp::item::ItemAsset; + use crate::{assets::Asset, comp::Item}; + #[test] fn test_loot_table() { let test = Lottery::::load_expect("common.loot_tables.loot_table"); - for (_, item) in test.iter() { + for (_, item_asset_specifier) in test.iter() { assert!( - ItemAsset::load(item).is_ok(), + Item::new_from_asset(item_asset_specifier).is_ok(), "Invalid loot table item '{}'", - item + item_asset_specifier ); } } diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs index 2b076fe504..b39e21316b 100644 --- a/common/src/msg/client.rs +++ b/common/src/msg/client.rs @@ -1,4 +1,5 @@ use crate::{ + character::CharacterId, comp, comp::{Skill, SkillGroupType}, terrain::block::Block, @@ -19,8 +20,8 @@ pub enum ClientMsg { tool: Option, body: comp::Body, }, - DeleteCharacter(i32), - Character(i32), + DeleteCharacter(CharacterId), + Character(CharacterId), /// Request `ClientState::Registered` from an ingame state ExitIngame, /// Request `ClientState::Spectator` from a registered or ingame state diff --git a/common/src/recipe.rs b/common/src/recipe.rs index 5bf7c7d3fd..76663022ca 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -1,6 +1,6 @@ use crate::{ assets::{self, Asset}, - comp::{item::ItemAsset, Inventory, Item}, + comp::{Inventory, Item}, }; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; @@ -8,17 +8,14 @@ use std::{fs::File, io::BufReader, sync::Arc}; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Recipe { - pub output: (Item, usize), - pub inputs: Vec<(Item, usize)>, + pub output: (Item, u32), + pub inputs: Vec<(Item, u32)>, } #[allow(clippy::type_complexity)] impl Recipe { /// Perform a recipe, returning a list of missing items on failure - pub fn perform( - &self, - inv: &mut Inventory, - ) -> Result, Vec<(&Item, usize)>> { + pub fn perform(&self, inv: &mut Inventory) -> Result, Vec<(&Item, u32)>> { // Get ingredient cells from inventory, inv.contains_ingredients(self)? .into_iter() @@ -30,7 +27,7 @@ impl Recipe { }); for i in 0..self.output.1 { - if let Some(item) = inv.push(self.output.0.clone()) { + if let Some(item) = inv.push(self.output.0.duplicate()) { return Ok(Some((item, self.output.1 - i))); } } @@ -38,7 +35,7 @@ impl Recipe { Ok(None) } - pub fn inputs(&self) -> impl ExactSizeIterator { + pub fn inputs(&self) -> impl ExactSizeIterator { self.inputs.iter().map(|(item, amount)| (item, *amount)) } } @@ -65,10 +62,10 @@ impl RecipeBook { impl Asset for RecipeBook { const ENDINGS: &'static [&'static str] = &["ron"]; - fn parse(buf_reader: BufReader) -> Result { + fn parse(buf_reader: BufReader, _specifier: &str) -> Result { ron::de::from_reader::< BufReader, - HashMap)>, + HashMap)>, >(buf_reader) .map_err(assets::Error::parse_error) .and_then(|recipes| { @@ -78,13 +75,11 @@ impl Asset for RecipeBook { .map::, _>( |(name, ((output, amount), inputs))| { Ok((name, Recipe { - output: ((&*ItemAsset::load(&output)?).clone(), amount), + output: (Item::new_from_asset(&output)?, amount), inputs: inputs .into_iter() - .map::, _>( - |(name, amount)| { - Ok(((&*ItemAsset::load(&name)?).clone(), amount)) - }, + .map::, _>( + |(name, amount)| Ok((Item::new_from_asset(&name)?, amount)), ) .collect::>()?, })) diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 8bd5788dec..aaa583f374 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -143,7 +143,7 @@ pub fn handle_wield(data: &JoinData, update: &mut StateUpdate) { /// If a tool is equipped, goes into Equipping state, otherwise goes to Idle pub fn attempt_wield(data: &JoinData, update: &mut StateUpdate) { - if let Some(ItemKind::Tool(tool)) = data.loadout.active_item.as_ref().map(|i| &i.item.kind) { + if let Some(ItemKind::Tool(tool)) = data.loadout.active_item.as_ref().map(|i| i.item.kind()) { update.character = CharacterState::Equipping(equipping::Data { time_left: tool.equip_time(), }); @@ -239,12 +239,12 @@ pub fn handle_ability1_input(data: &JoinData, update: &mut StateUpdate) { /// Will attempt to go into `loadout.active_item.ability2` pub fn handle_ability2_input(data: &JoinData, update: &mut StateUpdate) { if data.inputs.secondary.is_pressed() { - let active_tool_kind = match data.loadout.active_item.as_ref().map(|i| &i.item.kind) { + let active_tool_kind = match data.loadout.active_item.as_ref().map(|i| i.item.kind()) { Some(ItemKind::Tool(Tool { kind, .. })) => Some(kind), _ => None, }; - let second_tool_kind = match data.loadout.second_item.as_ref().map(|i| &i.item.kind) { + let second_tool_kind = match data.loadout.second_item.as_ref().map(|i| i.item.kind()) { Some(ItemKind::Tool(Tool { kind, .. })) => Some(kind), _ => None, }; @@ -319,7 +319,7 @@ pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) { } pub fn unwrap_tool_data<'a>(data: &'a JoinData) -> Option<&'a Tool> { - if let Some(ItemKind::Tool(tool)) = data.loadout.active_item.as_ref().map(|i| &i.item.kind) { + if let Some(ItemKind::Tool(tool)) = data.loadout.active_item.as_ref().map(|i| i.item.kind()) { Some(tool) } else { None diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 23f590b83e..239905cad0 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -271,7 +271,7 @@ impl<'a> System<'a> for Sys { } let tactic = match loadout.active_item.as_ref().and_then(|ic| { - if let ItemKind::Tool(tool) = &ic.item.kind { + if let ItemKind::Tool(tool) = &ic.item.kind() { Some(&tool.kind) } else { None diff --git a/common/src/terrain/structure.rs b/common/src/terrain/structure.rs index 5401cf7267..607510b262 100644 --- a/common/src/terrain/structure.rs +++ b/common/src/terrain/structure.rs @@ -100,8 +100,8 @@ impl ReadVol for Structure { impl Asset for Structure { const ENDINGS: &'static [&'static str] = &["vox"]; - fn parse(buf_reader: BufReader) -> Result { - let dot_vox_data = DotVoxData::parse(buf_reader)?; + fn parse(buf_reader: BufReader, specifier: &str) -> Result { + let dot_vox_data = DotVoxData::parse(buf_reader, specifier)?; if let Some(model) = dot_vox_data.models.get(0) { let palette = dot_vox_data diff --git a/items.csv b/items.csv new file mode 100644 index 0000000000..d7f4540a99 --- /dev/null +++ b/items.csv @@ -0,0 +1,370 @@ +Path,Name,Kind +common.items.armor.back.admin,Admin's Cape,Admin +common.items.armor.back.dungeon_purple-0,Purple Cultist Cape,DungPurp0 +common.items.armor.back.leather_adventurer,Agile Cape,Short2 +common.items.armor.back.short_0,Short leather Cape,Short0 +common.items.armor.back.short_1,Green Blanket,Short1 +common.items.armor.belt.assassin,Assassin Belt,Assassin +common.items.armor.belt.bonerattler,Bonerattler Belt,Bonerattler +common.items.armor.belt.cloth_blue_0,Blue Linen Belt,ClothBlue0 +common.items.armor.belt.cloth_green_0,Green Linen Belt,ClothGreen0 +common.items.armor.belt.cloth_purple_0,Purple Linen Belt,ClothPurple0 +common.items.armor.belt.cultist_belt,Cultist Belt,Cultist +common.items.armor.belt.druid,Druid's Belt,Druid +common.items.armor.belt.leather_0,Swift Belt,Leather0 +common.items.armor.belt.leather_2,Leather Belt,Leather2 +common.items.armor.belt.leather_adventurer,Agile Belt,Leather2 +common.items.armor.belt.plate_0,Iron Belt,Plate0 +common.items.armor.belt.steel_0,Steel Belt,Steel0 +common.items.armor.belt.tarasque,Tarasque Belt,Tarasque +common.items.armor.belt.twig,Twig Belt,Twig +common.items.armor.belt.twigsflowers,Flowery Belt,Twigsflowers +common.items.armor.belt.twigsleaves,Leafy Belt,Twigsleaves +common.items.armor.chest.assassin,Assassin Chest,Assassin +common.items.armor.chest.bonerattler,Bonerattler Cuirass,Bonerattler +common.items.armor.chest.cloth_blue_0,Blue Linen Chest,ClothBlue0 +common.items.armor.chest.cloth_green_0,Green Linen Chest,ClothGreen0 +common.items.armor.chest.cloth_purple_0,Purple Linen Chest,ClothPurple0 +common.items.armor.chest.cultist_chest_blue,Blue Cultist Chest,CultistBlue +common.items.armor.chest.cultist_chest_purple,Purple Cultist Chest,CultistPurple +common.items.armor.chest.druid,Druid's Vest,Druid +common.items.armor.chest.leather_0,Swift Chest,Leather0 +common.items.armor.chest.leather_2,Leather Cuirass,Leather2 +common.items.armor.chest.leather_adventurer,Agile Chest,Leather2 +common.items.armor.chest.plate_green_0,Iron Chestplate,PlateGreen0 +common.items.armor.chest.steel_0,Steel Cuirass,Steel0 +common.items.armor.chest.tarasque,Tarasque Cuirass,Tarasque +common.items.armor.chest.twig,Twig Shirt,Twig +common.items.armor.chest.twigsflowers,Flowery Shirt,Twigsflowers +common.items.armor.chest.twigsleaves,Leafy Shirt,Twigsleaves +common.items.armor.chest.worker_green_0,Green Worker Shirt,WorkerGreen0 +common.items.armor.chest.worker_green_1,Green Worker Shirt,WorkerGreen1 +common.items.armor.chest.worker_orange_0,Orange Worker Shirt,WorkerOrange0 +common.items.armor.chest.worker_orange_1,Orange Worker Shirt,WorkerOrange1 +common.items.armor.chest.worker_purple_0,Purple Worker Shirt,WorkerPurple0 +common.items.armor.chest.worker_purple_1,Purple Worker Shirt,WorkerPurple1 +common.items.armor.chest.worker_red_0,Red Worker Shirt,WorkerRed0 +common.items.armor.chest.worker_red_1,Red Worker Shirt,WorkerRed1 +common.items.armor.chest.worker_yellow_0,Yellow Worker Shirt,WorkerYellow0 +common.items.armor.chest.worker_yellow_1,Yellow Worker Shirt,WorkerYellow1 +common.items.armor.foot.assassin,Assassin Boots,Assassin +common.items.armor.foot.bonerattler,Bonerattler Boots,Bonerattler +common.items.armor.foot.cloth_blue_0,Blue Linen Boots,ClothBlue0 +common.items.armor.foot.cloth_green_0,Green Linen Boots,ClothGreen0 +common.items.armor.foot.cloth_purple_0,Purple Linen Boots,ClothPurple0 +common.items.armor.foot.cultist_boots,Cultist Boots,Cultist +common.items.armor.foot.druid,Druid's Slippers,Druid +common.items.armor.foot.jackalope_slippers,Fluffy Jackalope Slippers,JackalopeSlips +common.items.armor.foot.leather_0,Swift Boots,Leather0 +common.items.armor.foot.leather_2,Leather Boots,Leather2 +common.items.armor.foot.leather_adventurer,Agile Kickers,Leather2 +common.items.armor.foot.plate_0,Iron Feet,Plate0 +common.items.armor.foot.steel_0,Steel Boots,Steel0 +common.items.armor.foot.tarasque,Tarasque Boots,Tarasque +common.items.armor.foot.twig,Twig Boots,Twig +common.items.armor.foot.twigsflowers,Flowery Boots,Twigsflowers +common.items.armor.foot.twigsleaves,Leafy Boots,Twigsleaves +common.items.armor.hand.assassin,Assassin Gloves,Assassin +common.items.armor.hand.bonerattler,Bonerattler Gauntlets,Bonerattler +common.items.armor.hand.cloth_blue_0,Blue Linen Wrists,ClothBlue0 +common.items.armor.hand.cloth_green_0,Green Linen Wrists,ClothGreen0 +common.items.armor.hand.cloth_purple_0,Purple Silk Wrists,ClothPurple0 +common.items.armor.hand.cultist_hands_blue,Blue Cultist Gloves,CultistBlue +common.items.armor.hand.cultist_hands_purple,Purple Cultist Gloves,CultistPurple +common.items.armor.hand.druid,Druid's Gloves,Druid +common.items.armor.hand.leather_0,Swift Gloves,Leather0 +common.items.armor.hand.leather_2,Leather Gloves,Leather2 +common.items.armor.hand.leather_adventurer,Agile Gauntlets,Leather2 +common.items.armor.hand.plate_0,Iron Handguards,Plate0 +common.items.armor.hand.steel_0,Steel Gauntlets,Steel0 +common.items.armor.hand.tarasque,Tarasque Gauntlets,Tarasque +common.items.armor.hand.twig,Twig Wraps,Twig +common.items.armor.hand.twigsflowers,Flowery Wraps,Twigsflowers +common.items.armor.hand.twigsleaves,Leafy Wraps,Twigsleaves +common.items.armor.head.assa_mask_0,Dark Assassin Mask,AssaMask0 +common.items.armor.head.leather_0,Swift Leather Cap,Leather0 +common.items.armor.neck.neck_0,Plain Necklace,Neck0 +common.items.armor.neck.neck_1,Gem of lesser Protection,Neck1 +common.items.armor.pants.assassin,Assassin Pants,Assassin +common.items.armor.pants.bonerattler,Bonerattler Chausses,Bonerattler +common.items.armor.pants.cloth_blue_0,Blue Linen Skirt,ClothBlue0 +common.items.armor.pants.cloth_green_0,Green Linen Skirt,ClothGreen0 +common.items.armor.pants.cloth_purple_0,Purple Linen Skirt,ClothPurple0 +common.items.armor.pants.cultist_legs_blue,Blue Cultist Skirt,CultistBlue +common.items.armor.pants.cultist_legs_purple,Purple Cultist Skirt,CultistPurple +common.items.armor.pants.druid,Druid's Kilt,Druid +common.items.armor.pants.hunting,Hunting Pants,Hunting +common.items.armor.pants.leather_0,Swift Pants,Leather0 +common.items.armor.pants.leather_2,Leather Leg Armour,Leather2 +common.items.armor.pants.leather_adventurer,Agile Pantalons,Leather2 +common.items.armor.pants.plate_green_0,Iron Legguards,PlateGreen0 +common.items.armor.pants.steel_0,Steel Chausses,Steel0 +common.items.armor.pants.tarasque,Tarasque Chausses,Tarasque +common.items.armor.pants.twig,Twig Pants,Twig +common.items.armor.pants.twigsflowers,Flowery Pants,Twigsflowers +common.items.armor.pants.twigsleaves,Leafy Pants,Twigsleaves +common.items.armor.pants.worker_blue_0,Blue Worker Pants,WorkerBlue0 +common.items.armor.ring.ring_0,Scratched Ring,Ring0 +common.items.armor.shoulder.assassin,Assassin Shoulder Guard,Assassin +common.items.armor.shoulder.bonerattler,Bonerattler Shoulder Pad,Bonerattler +common.items.armor.shoulder.cloth_blue_0,Blue Linen Coat,ClothBlue0 +common.items.armor.shoulder.cloth_blue_1,Blue Cloth Pads,ClothBlue1 +common.items.armor.shoulder.cloth_green_0,Green Linen Coat,ClothGreen0 +common.items.armor.shoulder.cloth_purple_0,Purple Linen Coat,ClothPurple0 +common.items.armor.shoulder.cultist_shoulder_blue,Blue Cultist Mantle,CultistBlue +common.items.armor.shoulder.cultist_shoulder_purple,Purple Cultist Mantle,CultistPurple +common.items.armor.shoulder.druidshoulder,Druid Shoulders,DruidShoulder +common.items.armor.shoulder.iron_spikes,Iron Spiked Pauldrons,IronSpikes +common.items.armor.shoulder.leather_0,Leather Pauldrons,Leather0 +common.items.armor.shoulder.leather_1,Swift Shoulderpads,Leather1 +common.items.armor.shoulder.leather_2,Leather Shoulder Pad,Leather2 +common.items.armor.shoulder.leather_adventurer,Agile Guards,Leather2 +common.items.armor.shoulder.leather_iron_0,Iron and Leather Spaulders,IronLeather0 +common.items.armor.shoulder.leather_iron_1,Iron and Leather Spaulders,IronLeather1 +common.items.armor.shoulder.leather_iron_2,Iron and Leather Spaulders,IronLeather2 +common.items.armor.shoulder.leather_iron_3,Iron and Leather Spaulders,IronLeather3 +common.items.armor.shoulder.leather_strips,Leather Strips,LeatherStrips +common.items.armor.shoulder.plate_0,Iron Shoulderguards,Plate0 +common.items.armor.shoulder.steel_0,Steel Shoulder Pad,Steel0 +common.items.armor.shoulder.tarasque,Tarasque Shoulder Pad,Tarasque +common.items.armor.shoulder.twigs,Twiggy Shoulders,TwiggyShoulder +common.items.armor.shoulder.twigsflowers,Flowery Shoulders,FlowerShoulder +common.items.armor.shoulder.twigsleaves,Leafy Shoulders,LeafyShoulder +common.items.armor.starter.lantern,Black Lantern,Black0 +common.items.armor.starter.rugged_chest,Rugged Shirt,Rugged0 +common.items.armor.starter.rugged_pants,Rugged Commoner's Pants,Rugged0 +common.items.armor.starter.sandals_0,Worn out Sandals,Sandal0 +common.items.armor.tabard.admin,Admin's Tabard,Admin +common.items.boss_drops.exp_flask,Flask of Velorite Dusk, +common.items.boss_drops.lantern,Magic Lantern,Blue0 +common.items.boss_drops.potions,Potent Potion, +common.items.boss_drops.xp_potion,Potion of Skill, +common.items.consumable.potion_big,Large Potion, +common.items.consumable.potion_med,Medium Potion, +common.items.consumable.potion_minor,Minor Potion, +common.items.crafting_ing.empty_vial,Empty Vial, +common.items.crafting_ing.leather_scraps,Leather Scraps, +common.items.crafting_ing.shiny_gem,Shiny Gem, +common.items.crafting_ing.stones,Stones, +common.items.crafting_ing.twigs,Twigs, +common.items.crafting_tools.craftsman_hammer,Craftsman Hammer, +common.items.crafting_tools.mortar_pestle,Mortar and Pestle, +common.items.debug.admin,Admin's Tabard,Admin +common.items.debug.admin_back,Admin's Cape,Admin +common.items.debug.boost,Belzeshrub the Broom-God,Boost +common.items.debug.cultist_belt,Cultist Belt,Cultist +common.items.debug.cultist_boots,Cultist Boots,Cultist +common.items.debug.cultist_chest_blue,Blue Cultist Chest,CultistBlue +common.items.debug.cultist_hands_blue,Blue Cultist Gloves,CultistBlue +common.items.debug.cultist_legs_blue,Blue Cultist Skirt,CultistBlue +common.items.debug.cultist_purp_2h_boss-0,Admin Greatsword,CultPurp0 +common.items.debug.cultist_shoulder_blue,Blue Cultist Mantle,CultistBlue +common.items.debug.dungeon_purple-0,Purple Admin Cape,DungPurp0 +common.items.debug.possess,Belzeshrub the Broom-God,Boost +common.items.flowers.blue,Blue Flower, +common.items.flowers.pink,Pink Flower, +common.items.flowers.red,Red Flower, +common.items.flowers.sun,Sunflower, +common.items.flowers.white,White flower, +common.items.flowers.yellow,Yellow Flower, +common.items.food.apple,Apple, +common.items.food.apple_mushroom_curry,Mushroom Curry, +common.items.food.apple_stick,Apple Stick, +common.items.food.cheese,Dwarven Cheese, +common.items.food.coconut,Coconut, +common.items.food.mushroom,Mushroom, +common.items.food.mushroom_stick,Mushroom Stick, +common.items.grasses.long,Long Grass, +common.items.grasses.medium,Medium Grass, +common.items.grasses.short,Short Grass, +common.items.lantern.black_0,Black Lantern,Black0 +common.items.lantern.blue_0,Cool Blue Lantern,Blue0 +common.items.lantern.green_0,Lime Zest Lantern,Green0 +common.items.lantern.red_0,Red Lantern,Red0 +common.items.npc_armor.back.dungeon_purple-0,Purple Cultist Cape,DungPurp0 +common.items.npc_armor.belt.cultist_belt,Cultist Belt,Cultist +common.items.npc_armor.chest.cultist_chest_purple,Purple Cultist Chest,CultistPurple +common.items.npc_armor.chest.worker_green_0,Green Worker Shirt,WorkerGreen0 +common.items.npc_armor.chest.worker_green_1,Green Worker Shirt,WorkerGreen1 +common.items.npc_armor.chest.worker_orange_0,Orange Worker Shirt,WorkerOrange0 +common.items.npc_armor.chest.worker_orange_1,Orange Worker Shirt,WorkerOrange1 +common.items.npc_armor.chest.worker_purple_0,Purple Worker Shirt,WorkerPurple0 +common.items.npc_armor.chest.worker_purple_1,Purple Worker Shirt,WorkerPurple1 +common.items.npc_armor.chest.worker_red_0,Red Worker Shirt,WorkerRed0 +common.items.npc_armor.chest.worker_red_1,Red Worker Shirt,WorkerRed1 +common.items.npc_armor.chest.worker_yellow_0,Yellow Worker Shirt,WorkerYellow0 +common.items.npc_armor.chest.worker_yellow_1,Yellow Worker Shirt,WorkerYellow1 +common.items.npc_armor.foot.cultist_boots,Cultist Boots,Cultist +common.items.npc_armor.hand.cultist_hands_purple,Purple Cultist Gloves,CultistPurple +common.items.npc_armor.pants.cultist_legs_purple,Purple Cultist Skirt,CultistPurple +common.items.npc_armor.shoulder.cultist_shoulder_purple,Purple Cultist Mantle,CultistPurple +common.items.npc_weapons.axe.malachite_axe-0,Malachite Axe,MalachiteAxe0 +common.items.npc_weapons.axe.starter_axe,Notched Axe,BasicAxe +common.items.npc_weapons.bow.horn_longbow-0,Horn Bow,HornLongbow0 +common.items.npc_weapons.dagger.starter_dagger,Rusty Dagger,BasicDagger +common.items.npc_weapons.empty.empty,Empty, +common.items.npc_weapons.hammer.cultist_purp_2h-0,Magical Cultist Warhammer,CultPurp0 +common.items.npc_weapons.hammer.starter_hammer,Sturdy Old Hammer,BasicHammer +common.items.npc_weapons.shield.shield_1,A Tattered Targe,BasicShield +common.items.npc_weapons.staff.bone_staff,Bone Staff,BoneStaff +common.items.npc_weapons.staff.cultist_staff,Cultist Staff,CultistStaff +common.items.npc_weapons.sword.cultist_purp_2h-0,Magical Cultist Greatsword,CultPurp0 +common.items.npc_weapons.sword.cultist_purp_2h_boss-0,Magical Cultist Greatsword,CultPurp0 +common.items.npc_weapons.sword.starter_sword,Battered Sword,BasicSword +common.items.npc_weapons.sword.zweihander_sword_0,Sturdy Zweihander,Zweihander0 +common.items.npc_weapons.tool.broom,Broom,Broom +common.items.npc_weapons.tool.fishing_rod,Fishing Rod,FishingRod0 +common.items.npc_weapons.tool.hoe,Hoe,Hoe0 +common.items.npc_weapons.tool.pickaxe,Pickaxe,Pickaxe0 +common.items.npc_weapons.tool.pitchfork,Pitchfork,Pitchfork +common.items.npc_weapons.tool.rake,Rake,Rake +common.items.npc_weapons.tool.shovel-0,Shovel,Shovel0 +common.items.npc_weapons.tool.shovel-1,Shovel,Shovel1 +common.items.ore.velorite,Velorite, +common.items.ore.veloritefrag,Velorite Fragment, +common.items.testing.test_boots,Testing Boots,Dark +common.items.utility.bomb,Bomb, +common.items.utility.bomb_pile,Bomb, +common.items.utility.collar,Collar, +common.items.utility.firework_blue,Firework Blue, +common.items.utility.firework_green,Firework Green, +common.items.utility.firework_purple,Firework Purple, +common.items.utility.firework_red,Firework Red, +common.items.utility.firework_yellow,Firework Yellow, +common.items.utility.training_dummy,Training Dummy, +common.items.weapons.axe.bloodsteel_axe-0,Bloodsteel Axe,BloodsteelAxe0 +common.items.weapons.axe.bloodsteel_axe-1,Executioner's Axe,BloodsteelAxe1 +common.items.weapons.axe.bloodsteel_axe-2,Tribal Axe,BloodsteelAxe2 +common.items.weapons.axe.bronze_axe-0,Bronze Axe,BronzeAxe0 +common.items.weapons.axe.bronze_axe-1,Discus Axe,BronzeAxe1 +common.items.weapons.axe.cobalt_axe-0,Cobalt Axe,CobaltAxe0 +common.items.weapons.axe.iron_axe-0,Iron Greataxe,IronAxe0 +common.items.weapons.axe.iron_axe-1,Ceremonial Axe,IronAxe1 +common.items.weapons.axe.iron_axe-2,Cyclone Axe,IronAxe2 +common.items.weapons.axe.iron_axe-3,Iron Battleaxe,IronAxe3 +common.items.weapons.axe.iron_axe-4,Butcher's Axe,IronAxe4 +common.items.weapons.axe.iron_axe-5,Barbarian's Axe,IronAxe5 +common.items.weapons.axe.iron_axe-6,Iron Axe,IronAxe6 +common.items.weapons.axe.iron_axe-7,Iron Labrys,IronAxe7 +common.items.weapons.axe.iron_axe-8,Fanged Axe,IronAxe8 +common.items.weapons.axe.iron_axe-9,Wolfen Axe,IronAxe9 +common.items.weapons.axe.malachite_axe-0,Malachite Axe,MalachiteAxe0 +common.items.weapons.axe.orc_axe-0,Beast Cleaver,OrcAxe0 +common.items.weapons.axe.starter_axe,Notched Axe,BasicAxe +common.items.weapons.axe.steel_axe-0,Steel Battleaxe,SteelAxe0 +common.items.weapons.axe.steel_axe-1,Steel Labrys,SteelAxe1 +common.items.weapons.axe.steel_axe-2,Steel Axe,SteelAxe2 +common.items.weapons.axe.steel_axe-3,Crescent Axe,SteelAxe3 +common.items.weapons.axe.steel_axe-4,Moon Axe,SteelAxe4 +common.items.weapons.axe.steel_axe-5,Owl Axe,SteelAxe5 +common.items.weapons.axe.steel_axe-6,Spade Axe,SteelAxe6 +common.items.weapons.axe.worn_iron_axe-0,Worn Dwarven Axe,WornIronAxe0 +common.items.weapons.axe.worn_iron_axe-1,Worn Elven Axe,WornIronAxe1 +common.items.weapons.axe.worn_iron_axe-2,Worn Human Axe,WornIronAxe2 +common.items.weapons.axe.worn_iron_axe-3,Worn Orcish Axe,WornIronAxe3 +common.items.weapons.axe.worn_iron_axe-4,Beetle Axe,WornIronAxe4 +common.items.weapons.bow.horn_longbow-0,Horn Bow,HornLongbow0 +common.items.weapons.bow.iron_longbow-0,Soldier's Bow,IronLongbow0 +common.items.weapons.bow.leafy_longbow-0,Elven Longbow,LeafyLongbow0 +common.items.weapons.bow.leafy_shortbow-0,Elven Shortbow,LeafyShortbow0 +common.items.weapons.bow.nature_ore_longbow-0,Velorite Bow,NatureOreLongbow +common.items.weapons.bow.rare_longbow,Enchanted Longbow,RareLongbow +common.items.weapons.bow.starter_bow,Uneven Bow,ShortBow0 +common.items.weapons.bow.wood_longbow-0,Longbow,WoodLongbow0 +common.items.weapons.bow.wood_longbow-1,Recurve Bow,WoodLongbow1 +common.items.weapons.bow.wood_shortbow-0,Hunting Bow,WoodShortbow0 +common.items.weapons.bow.wood_shortbow-1,Horse Bow,WoodShortbow1 +common.items.weapons.dagger.starter_dagger,Rusty Dagger,BasicDagger +common.items.weapons.empty.empty,Empty, +common.items.weapons.hammer.bronze_hammer-0,Bronze Hammer,BronzeHammer0 +common.items.weapons.hammer.bronze_hammer-1,Bronze Club,BronzeHammer1 +common.items.weapons.hammer.cobalt_hammer-0,Cobalt Hammer,CobaltHammer0 +common.items.weapons.hammer.cobalt_hammer-1,Cobalt Mace,CobaltHammer1 +common.items.weapons.hammer.cultist_purp_2h-0,Magical Cultist Warhammer,CultPurp0 +common.items.weapons.hammer.flimsy_hammer,Flimsy Hammer,FlimsyHammer +common.items.weapons.hammer.hammer_1,Crude Mallet,BasicHammer +common.items.weapons.hammer.iron_hammer-0,Iron Hammer,IronHammer0 +common.items.weapons.hammer.iron_hammer-1,Iron Battlehammer,IronHammer1 +common.items.weapons.hammer.iron_hammer-2,Iron Mace,IronHammer2 +common.items.weapons.hammer.iron_hammer-3,Crowned Mace,IronHammer3 +common.items.weapons.hammer.iron_hammer-4,Forge Hammer,IronHammer4 +common.items.weapons.hammer.iron_hammer-5,Pike Hammer,IronHammer5 +common.items.weapons.hammer.iron_hammer-6,Spiked Maul,IronHammer6 +common.items.weapons.hammer.iron_hammer-7,Giant's Fist,IronHammer7 +common.items.weapons.hammer.iron_hammer-8,Lucerne Hammer,IronHammer8 +common.items.weapons.hammer.mjolnir,Mjolnir,Mjolnir +common.items.weapons.hammer.ramshead_hammer,Ram's Head Mace,RamsheadHammer +common.items.weapons.hammer.runic_hammer,Runic Hammer,RunicHammer +common.items.weapons.hammer.starter_hammer,Sturdy Old Hammer,BasicHammer +common.items.weapons.hammer.steel_hammer-0,Steel Hammer,SteelHammer0 +common.items.weapons.hammer.steel_hammer-1,Steel Greathammer,SteelHammer1 +common.items.weapons.hammer.steel_hammer-2,Steel Club,SteelHammer2 +common.items.weapons.hammer.steel_hammer-3,Battle Mace,SteelHammer3 +common.items.weapons.hammer.steel_hammer-4,Brute's Hammer,SteelHammer4 +common.items.weapons.hammer.steel_hammer-5,Morning Star,SteelHammer5 +common.items.weapons.hammer.stone_hammer-0,Basalt Sledgehammer,StoneHammer0 +common.items.weapons.hammer.stone_hammer-1,Granite Sledgehammer,StoneHammer1 +common.items.weapons.hammer.stone_hammer-2,Rocky Maul,StoneHammer2 +common.items.weapons.hammer.stone_hammer-3,Stone Sledgehammer,StoneHammer3 +common.items.weapons.hammer.wood_hammer-0,Hardwood Mallet,WoodHammer0 +common.items.weapons.hammer.worn_iron_hammer-0,Worn Dwarven Hammer,WornIronHammer0 +common.items.weapons.hammer.worn_iron_hammer-1,Worn Elven Hammer,WornIronHammer1 +common.items.weapons.hammer.worn_iron_hammer-2,Worn Human Mace,WornIronHammer2 +common.items.weapons.hammer.worn_iron_hammer-3,Worn Orcish Hammer,WornIronHammer3 +common.items.weapons.shield.shield_1,A Tattered Targe,BasicShield +common.items.weapons.staff.amethyst_staff,Amethyst Staff,AmethystStaff +common.items.weapons.staff.bone_staff,Bone Staff,BoneStaff +common.items.weapons.staff.cultist_staff,Cultist Staff,CultistStaff +common.items.weapons.staff.sceptre_velorite_0,Velorite Sceptre,SceptreVelorite +common.items.weapons.staff.staff_1,Humble Stick,BasicStaff +common.items.weapons.staff.staff_nature,Sceptre of Regeneration,Sceptre +common.items.weapons.staff.starter_staff,Gnarled Rod,BasicStaff +common.items.weapons.sword.cultist_purp_2h-0,Magical Cultist Greatsword,CultPurp0 +common.items.weapons.sword.greatsword_2h_dam-0,Damaged Greatsword,GreatswordDam0 +common.items.weapons.sword.greatsword_2h_dam-1,Damaged Greatsword,GreatswordDam1 +common.items.weapons.sword.greatsword_2h_dam-2,Damaged Greatsword,GreatswordDam2 +common.items.weapons.sword.greatsword_2h_fine-0,Fine Greatsword,GreatswordFine0 +common.items.weapons.sword.greatsword_2h_fine-1,Fine Greatsword,GreatswordFine1 +common.items.weapons.sword.greatsword_2h_fine-2,Fine Greatsword,GreatswordFine2 +common.items.weapons.sword.greatsword_2h_orn-0,Ornamented Greatsword,GreatswordOrn0 +common.items.weapons.sword.greatsword_2h_orn-1,Ornamented Greatsword,GreatswordOrn1 +common.items.weapons.sword.greatsword_2h_orn-2,Ornamented Greatsword,GreatswordOrn2 +common.items.weapons.sword.greatsword_2h_simple-0,Simple Greatsword,GreatswordSimple0 +common.items.weapons.sword.greatsword_2h_simple-1,Simple Greatsword,GreatswordSimple1 +common.items.weapons.sword.greatsword_2h_simple-2,Simple Greatsword,GreatswordSimple2 +common.items.weapons.sword.long_2h_dam-0,Damaged Longsword,LongDam0 +common.items.weapons.sword.long_2h_dam-1,Damaged Longsword,LongDam1 +common.items.weapons.sword.long_2h_dam-2,Damaged Longsword,LongDam2 +common.items.weapons.sword.long_2h_dam-3,Damaged Longsword,LongDam3 +common.items.weapons.sword.long_2h_dam-4,Damaged Longsword,LongDam4 +common.items.weapons.sword.long_2h_dam-5,Damaged Longsword,LongDam5 +common.items.weapons.sword.long_2h_fine-0,Fine Longsword,LongFine0 +common.items.weapons.sword.long_2h_fine-1,Fine Longsword,LongFine1 +common.items.weapons.sword.long_2h_fine-2,Fine Longsword,LongFine2 +common.items.weapons.sword.long_2h_fine-3,Fine Longsword,LongFine3 +common.items.weapons.sword.long_2h_fine-4,Fine Longsword,LongFine4 +common.items.weapons.sword.long_2h_fine-5,Fine Longsword,LongFine5 +common.items.weapons.sword.long_2h_orn-0,Ornamented Longsword,LongOrn0 +common.items.weapons.sword.long_2h_orn-1,Ornamented Longsword,LongOrn1 +common.items.weapons.sword.long_2h_orn-2,Ornamented Longsword,LongOrn2 +common.items.weapons.sword.long_2h_orn-3,Ornamented Longsword,LongOrn3 +common.items.weapons.sword.long_2h_orn-4,Ornamented Longsword,LongOrn4 +common.items.weapons.sword.long_2h_orn-5,Ornamented Longsword,LongOrn5 +common.items.weapons.sword.long_2h_simple-0,Simple Longsword,LongSimple0 +common.items.weapons.sword.long_2h_simple-1,Simple Longsword,LongSimple1 +common.items.weapons.sword.long_2h_simple-2,Simple Longsword,LongSimple2 +common.items.weapons.sword.long_2h_simple-3,Simple Longsword,LongSimple3 +common.items.weapons.sword.long_2h_simple-4,Simple Longsword,LongSimple4 +common.items.weapons.sword.long_2h_simple-5,Simple Longsword,LongSimple5 +common.items.weapons.sword.short_sword_0,Vicious Gladius,Short0 +common.items.weapons.sword.starter_sword,Battered Sword,BasicSword +common.items.weapons.sword.wood_sword,Forest Spirit,WoodTraining +common.items.weapons.sword.zweihander_sword_0,Sturdy Zweihander,Zweihander0 +common.items.weapons.tool.broom,Broom,Broom +common.items.weapons.tool.fishing_rod,Fishing Rod,FishingRod0 +common.items.weapons.tool.hoe,Hoe,Hoe0 +common.items.weapons.tool.pickaxe,Pickaxe,Pickaxe0 +common.items.weapons.tool.pitchfork,Pitchfork,Pitchfork +common.items.weapons.tool.rake,Rake,Rake +common.items.weapons.tool.shovel-0,Shovel,Shovel0 +common.items.weapons.tool.shovel-1,Shovel,Shovel1 diff --git a/server/Cargo.toml b/server/Cargo.toml index e47125e94a..7eb4747105 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -22,6 +22,7 @@ uvth = "3.1.1" futures-util = "0.3" futures-executor = "0.3" futures-timer = "2.0" +itertools = "0.9" lazy_static = "1.4.0" scan_fmt = "0.2.4" ron = { version = "0.6", default-features = false } diff --git a/server/src/character_creator.rs b/server/src/character_creator.rs new file mode 100644 index 0000000000..c8fcd35abe --- /dev/null +++ b/server/src/character_creator.rs @@ -0,0 +1,33 @@ +use crate::persistence::character_loader::CharacterLoader; +use common::{ + comp::{Body, Inventory, Stats}, + loadout_builder::LoadoutBuilder, +}; +use specs::{Entity, ReadExpect}; + +pub fn create_character( + entity: Entity, + player_uuid: String, + character_alias: String, + character_tool: Option, + body: Body, + character_loader: &ReadExpect<'_, CharacterLoader>, +) { + let stats = Stats::new(character_alias.to_string(), body); + + let loadout = LoadoutBuilder::new() + .defaults() + .active_item(Some(LoadoutBuilder::default_item_config_from_str( + character_tool.as_deref().unwrap(), + ))) + .build(); + + let inventory = Inventory::default(); + + character_loader.create_character( + entity, + player_uuid, + character_alias, + (body, stats, inventory, loadout), + ); +} diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 7716be69bd..7a8057a639 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -5,9 +5,8 @@ use crate::{client::Client, Server, StateExt}; use chrono::{NaiveTime, Timelike}; use common::{ - assets::Asset, cmd::{ChatCommand, CHAT_COMMANDS, CHAT_SHORTCUTS}, - comp::{self, item::ItemAsset, ChatType, Item, LightEmitter, WaypointArea}, + comp::{self, ChatType, Item, LightEmitter, WaypointArea}, event::{EventBus, ServerEvent}, msg::{DisconnectReason, Notification, PlayerListUpdate, ServerMsg}, npc::{self, get_npc_name}, @@ -121,7 +120,7 @@ fn handle_give_item( scan_fmt_some!(&args, &action.arg_fmt(), String, u32) { let give_amount = give_amount_opt.unwrap_or(1); - if let Ok(item) = ItemAsset::load_cloned(&item_name) { + if let Ok(item) = Item::new_from_asset(&item_name) { let mut item: Item = item; if let Ok(()) = item.set_amount(give_amount.min(2000)) { server @@ -149,7 +148,7 @@ fn handle_give_item( .get_mut(target) .map(|inv| { for i in 0..give_amount { - if inv.push(item.clone()).is_some() { + if inv.push(item.duplicate()).is_some() { server.notify_client( client, ChatType::CommandError.server_msg(format!( @@ -1644,14 +1643,13 @@ fn handle_debug( _args: String, _action: &ChatCommand, ) { - if let Ok(items) = ItemAsset::load_glob("common.items.debug.*") { + if let Ok(items) = comp::Item::new_from_asset_glob("common.items.debug.*") { server .state() .ecs() .write_storage::() .get_mut(target) - // TODO: Consider writing a `load_glob_cloned` in `assets` and using that here - .map(|inv| inv.push_all_unique(items.iter().map(|item| item.as_ref().clone()))); + .map(|inv| inv.push_all_unique(items.into_iter())); let _ = server .state .ecs() diff --git a/server/src/error.rs b/server/src/error.rs index 74fadd4f74..384cc8087b 100644 --- a/server/src/error.rs +++ b/server/src/error.rs @@ -7,6 +7,7 @@ pub enum Error { NetworkErr(NetworkError), ParticipantErr(ParticipantError), StreamErr(StreamError), + DatabaseErr(diesel::result::Error), Other(String), } @@ -22,12 +23,17 @@ impl From for Error { fn from(err: StreamError) -> Self { Error::StreamErr(err) } } +impl From for Error { + fn from(err: diesel::result::Error) -> Self { Error::DatabaseErr(err) } +} + impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::NetworkErr(err) => write!(f, "Network Error: {}", err), Self::ParticipantErr(err) => write!(f, "Participant Error: {}", err), Self::StreamErr(err) => write!(f, "Stream Error: {}", err), + Self::DatabaseErr(err) => write!(f, "Database Error: {}", err), Self::Other(err) => write!(f, "Error: {}", err), } } diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 6d64946d65..0ac5100999 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -1,5 +1,6 @@ use crate::{sys, Server, StateExt}; use common::{ + character::CharacterId, comp::{ self, Agent, Alignment, Body, Gravity, Item, ItemDrop, LightEmitter, Loadout, Pos, Projectile, Scale, Stats, Vel, WaypointArea, @@ -11,7 +12,11 @@ use comp::group; use specs::{Builder, Entity as EcsEntity, WorldExt}; use vek::{Rgb, Vec3}; -pub fn handle_initialize_character(server: &mut Server, entity: EcsEntity, character_id: i32) { +pub fn handle_initialize_character( + server: &mut Server, + entity: EcsEntity, + character_id: CharacterId, +) { server.state.initialize_character_data(entity, character_id); } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 297c535f75..b499b3dd6f 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -4,9 +4,8 @@ use common::{ comp::{ self, chat::{KillSource, KillType}, - item::ItemAsset, - object, Alignment, Body, Damage, DamageSource, Group, HealthChange, HealthSource, Player, - Pos, Stats, + object, Alignment, Body, Damage, DamageSource, Group, HealthChange, HealthSource, Item, + Player, Pos, Stats, }, lottery::Lottery, msg::{PlayerListUpdate, ServerMsg}, @@ -334,7 +333,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc let item = { let mut item_drops = state.ecs().write_storage::(); item_drops.remove(entity).map_or_else( - || ItemAsset::load_expect_cloned(lottery().choose()), + || Item::new_from_asset_expect(lottery().choose()), |item_drop| item_drop.0, ) }; diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 339ce8f41e..f3fa1dc713 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -3,7 +3,6 @@ use crate::{ Server, }; use common::{ - assets::Asset, comp::{self, item}, msg::ServerMsg, sync::{Uid, WorldSyncExt}, @@ -29,7 +28,7 @@ pub fn handle_lantern(server: &mut Server, entity: EcsEntity) { .get(entity) .and_then(|loadout| loadout.lantern.as_ref()) .and_then(|item| { - if let comp::item::ItemKind::Lantern(l) = &item.kind { + if let comp::item::ItemKind::Lantern(l) = item.kind() { Some(l) } else { None @@ -125,8 +124,8 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) { .expect("Could not read loadouts component while possessing") .or_insert(comp::Loadout::default()); - let item = item::ItemAsset::load_expect_cloned("common.items.debug.possess"); - if let item::ItemKind::Tool(tool) = &item.kind { + let item = comp::Item::new_from_asset_expect("common.items.debug.possess"); + if let item::ItemKind::Tool(tool) = item.kind() { let mut abilities = tool.get_abilities(); let mut ability_drain = abilities.drain(..); let debug_item = comp::ItemConfig { diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index e517060ea3..cbc4a0082e 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -172,7 +172,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv let (is_equippable, lantern_opt) = inventory .get(slot) - .map_or((false, None), |i| match &i.kind { + .map_or((false, None), |i| match i.kind() { ItemKind::Tool(_) | ItemKind::Armor { .. } => (true, None), ItemKind::Lantern(lantern) => (true, Some(lantern)), _ => (false, None), @@ -180,7 +180,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv if is_equippable { if let Some(loadout) = state.ecs().write_storage().get_mut(entity) { if let Some(lantern) = lantern_opt { - swap_lantern(&mut state.ecs().write_storage(), entity, lantern); + swap_lantern(&mut state.ecs().write_storage(), entity, &lantern); } slot::equip(slot, inventory, loadout); Some(comp::InventoryUpdateEvent::Used) @@ -188,7 +188,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv None } } else if let Some(item) = inventory.take(slot) { - match &item.kind { + match item.kind() { ItemKind::Consumable { kind, effect, .. } => { maybe_effect = Some(*effect); Some(comp::InventoryUpdateEvent::Consumed(kind.clone())) @@ -369,9 +369,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv }; // FIXME: We should really require the drop and write to be atomic! - if let (Some(item), Some(pos)) = + if let (Some(mut item), Some(pos)) = (item, state.ecs().read_storage::().get(entity)) { + item.put_in_world(); dropped_items.push(( *pos, state diff --git a/server/src/events/player.rs b/server/src/events/player.rs index 486b826982..111aae9c54 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -140,7 +140,7 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event state.read_storage::().get(entity), state .ecs() - .read_resource::(), + .read_resource::(), ) { if let Some(character_id) = player.character_id { updater.update(character_id, stats, inventory, loadout); diff --git a/server/src/lib.rs b/server/src/lib.rs index fef1fe646d..89683d1b28 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -4,6 +4,7 @@ #![cfg_attr(not(feature = "worldgen"), feature(const_panic))] pub mod alias_validator; +mod character_creator; pub mod chunk_generator; pub mod client; pub mod cmd; @@ -47,7 +48,10 @@ use futures_timer::Delay; use futures_util::{select, FutureExt}; use metrics::{ServerMetrics, StateTickMetrics, TickMetrics}; use network::{Network, Pid, ProtocolAddr}; -use persistence::character::{CharacterLoader, CharacterLoaderResponseType, CharacterUpdater}; +use persistence::{ + character_loader::{CharacterLoader, CharacterLoaderResponseType}, + character_updater::CharacterUpdater, +}; use specs::{join::Join, Builder, Entity as EcsEntity, RunNow, SystemData, WorldExt}; use std::{ i32, @@ -98,6 +102,12 @@ impl Server { #[allow(clippy::expect_fun_call)] // TODO: Pending review in #587 #[allow(clippy::needless_update)] // TODO: Pending review in #587 pub fn new(settings: ServerSettings) -> Result { + // Run pending DB migrations (if any) + debug!("Running DB migrations..."); + if let Some(e) = persistence::run_migrations(&settings.persistence_db_dir).err() { + panic!("Migration error: {:?}", e); + } + let (chunk_gen_metrics, registry_chunk) = metrics::ChunkGenMetrics::new().unwrap(); let (network_request_metrics, registry_network) = metrics::NetworkRequestMetrics::new().unwrap(); @@ -117,15 +127,10 @@ impl Server { .insert(ChunkGenerator::new(chunk_gen_metrics)); state .ecs_mut() - .insert(CharacterUpdater::new(settings.persistence_db_dir.clone())); + .insert(CharacterUpdater::new(settings.persistence_db_dir.clone())?); state .ecs_mut() - .insert(CharacterLoader::new(settings.persistence_db_dir.clone())); - state - .ecs_mut() - .insert(persistence::character::CharacterUpdater::new( - settings.persistence_db_dir.clone(), - )); + .insert(CharacterLoader::new(settings.persistence_db_dir.clone())?); state .ecs_mut() .insert(comp::AdminList(settings.admins.clone())); @@ -320,13 +325,6 @@ impl Server { state_tick_metrics, }; - // Run pending DB migrations (if any) - debug!("Running DB migrations..."); - - if let Some(e) = persistence::run_migrations(&settings.persistence_db_dir).err() { - info!(?e, "Migration error"); - } - debug!(?settings, "created veloren server with"); let git_hash = *common::util::GIT_HASH; @@ -477,7 +475,7 @@ impl Server { // Get character-related database responses and notify the requesting client self.state .ecs() - .read_resource::() + .read_resource::() .messages() .for_each(|query_result| match query_result.result { CharacterLoaderResponseType::CharacterList(result) => match result { diff --git a/server/src/migrations/2020-08-16-130513_item_persistence/down.sql b/server/src/migrations/2020-08-16-130513_item_persistence/down.sql new file mode 100644 index 0000000000..ce2790d67f --- /dev/null +++ b/server/src/migrations/2020-08-16-130513_item_persistence/down.sql @@ -0,0 +1,11 @@ +DROP TABLE stats; +DROP TABLE character; +DROP TABLE body; +DROP TABLE item; +DROP TABLE entity; + +ALTER TABLE _body_bak RENAME TO body; +ALTER TABLE _stats_bak RENAME TO stats; +ALTER TABLE _character_bak RENAME TO character; +ALTER TABLE _loadout_bak RENAME TO loadout; +ALTER TABLE _inventory_bak RENAME TO inventory; \ No newline at end of file diff --git a/server/src/migrations/2020-08-16-130513_item_persistence/up.sql b/server/src/migrations/2020-08-16-130513_item_persistence/up.sql new file mode 100644 index 0000000000..c102405825 --- /dev/null +++ b/server/src/migrations/2020-08-16-130513_item_persistence/up.sql @@ -0,0 +1,798 @@ +-- +-- 1) Back up old tables +-- + +ALTER TABLE body RENAME TO _body_bak; +ALTER TABLE stats RENAME TO _stats_bak; +ALTER TABLE character RENAME TO _character_bak; +ALTER TABLE loadout RENAME TO _loadout_bak; +ALTER TABLE inventory RENAME TO _inventory_bak; + +-- +-- 2) Create new tables +-- + +CREATE TABLE entity +( + entity_id INTEGER NOT NULL + PRIMARY KEY AUTOINCREMENT + DEFAULT NULL +); + +CREATE TABLE item +( + item_id INTEGER NOT NULL + PRIMARY KEY + REFERENCES entity(entity_id), + parent_container_item_id INTEGER NOT NULL + REFERENCES item(item_id), + item_definition_id TEXT NOT NULL, + stack_size INTEGER NOT NULL, + position TEXT NOT NULL +); + +CREATE UNIQUE INDEX idx_parent_container_item_id_position + ON item(parent_container_item_id, position); + +CREATE INDEX idx_item_definition_id + ON item(item_definition_id); + +CREATE TABLE body +( + body_id INTEGER NOT NULL + PRIMARY KEY + REFERENCES entity(entity_id), + variant TEXT NOT NULL, + body_data TEXT NOT NULL +); + +CREATE TABLE stats +( + stats_id INT NOT NULL + PRIMARY KEY + REFERENCES entity(entity_id), + level INT NOT NULL, + exp INT NOT NULL, + endurance INT NOT NULL, + fitness INT NOT NULL, + willpower INT NOT NULL +); + +CREATE TABLE character +( + character_id INT NOT NULL + PRIMARY KEY + REFERENCES body(body_id) + REFERENCES item(item_id) + REFERENCES stats(stats_id), + player_uuid TEXT NOT NULL, + alias TEXT NOT NULL +); + +CREATE INDEX idx_player_uuid + ON character(player_uuid); + +-- +-- 3) Create world pseudo-container - this must be entity_id 1 as this is referred to in code +-- + +-- Create entity_id for world pseudo-container +INSERT +INTO entity +VALUES (1); + +-- Create world pseudo-container with hard-coded entity ID of 1 +INSERT +INTO item +VALUES (1, + 1, + 'veloren.core.pseudo_containers.world', + 1, + 'world'); + +-- +-- 4) Create Character pseudo-containers for existing characters +-- + +-- Create an entity_id for each character's character pseudo-container +INSERT +INTO entity +SELECT id + 1 +FROM _character_bak; + +INSERT +INTO item +SELECT c.id + 1, + 1, -- Parent container as World pseudo-container + 'veloren.core.pseudo_containers.character', + 1, + c.id + 1 -- Position +FROM _character_bak c; + +-- +-- 5) Create Inventory pseudo-containers for existing characters +-- + +-- Create an entity_id for each character's inventory pseudo-container +INSERT +INTO entity +SELECT c.id + 1 + (1 * (SELECT MAX(cb.id) + 1 FROM _character_bak cb)) +FROM _character_bak c; + +INSERT +INTO item +SELECT c.id + 1 + (1 * (SELECT MAX(cb.id) + 1 FROM _character_bak cb)), + c.id + 1, -- Inventory pseudo-container has character's Player item pseudo-container as its parent + 'veloren.core.pseudo_containers.inventory', + 1, + 'inventory' -- Position +FROM _character_bak c; + +-- +-- 6) Create Loadout pseudo-containers for existing characters +-- + +-- Create an entity_id for each character's loadout pseudo-container +INSERT +INTO entity +SELECT c.id + 1 + (2 * (SELECT MAX(cb.id) + 1 FROM _character_bak cb)) +FROM _character_bak c; + +INSERT +INTO item +SELECT c.id + 1 + (2 * (SELECT MAX(cb.id) + 1 FROM _character_bak cb)), + c.id + 1, -- Loadout pseudo-container has character's Player item pseudo-container as its parent + 'veloren.core.pseudo_containers.loadout', + 1, + 'loadout' --Position +FROM _character_bak c; + +-- +-- 7) Migrate old body table to the new schema +-- + +INSERT +INTO body +SELECT b.character_id + 1, + 'humanoid', + json_object( + 'species', species, + 'body_type', body_type, + 'hair_style', hair_style, + 'beard', beard, + 'eyes', eyes, + 'accessory', accessory, + 'hair_color', hair_color, + 'skin', skin, + 'eye_color', eye_color + ) AS body_json +FROM _body_bak b; + +-- +-- 8) Migrate old stats table to the new schema +-- + +INSERT +INTO stats +SELECT s.character_id + 1, + s.level, + s.exp, + s.endurance, + s.fitness, + s.willpower +FROM _stats_bak s; + +-- Add default stats values for the 60~ characters with no stats records +INSERT +INTO stats +SELECT id + 1, 1, 0, 2, 2, 1 +FROM _character_bak +WHERE id + 1 NOT IN (SELECT stats_id FROM stats); + +-- 9) Migrate old character table to the new schema + +INSERT +INTO character +SELECT c.id + 1, + c.player_uuid, + c.alias +FROM _character_bak c; + +-- +-- 10) Create a temporary table containing mappings of item name/kind to item definition ID +-- + +CREATE TEMP TABLE _temp_item_defs +( + item_definition_id TEXT NOT NULL, + item_name TEXT NOT NULL, + kind TEXT +); + +INSERT INTO _temp_item_defs VALUES('common.items.armor.back.admin','Admin''s Cape','Admin'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.back.dungeon_purple-0','Purple Cultist Cape','DungPurp0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.back.leather_adventurer','Agile Cape','Short2'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.back.short_0','Short leather Cape','Short0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.back.short_1','Green Blanket','Short1'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.assassin','Assassin Belt','Assassin'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.bonerattler','Bonerattler Belt','Bonerattler'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.cloth_blue_0','Blue Linen Belt','ClothBlue0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.cloth_green_0','Green Linen Belt','ClothGreen0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.cloth_purple_0','Purple Linen Belt','ClothPurple0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.cultist_belt','Cultist Belt','Cultist'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.druid','Druid''s Belt','Druid'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.leather_0','Swift Belt','Leather0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.leather_2','Leather Belt','Leather2'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.leather_adventurer','Agile Belt','Leather2'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.plate_0','Iron Belt','Plate0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.steel_0','Steel Belt','Steel0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.tarasque','Tarasque Belt','Tarasque'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.twig','Twig Belt','Twig'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.twigsflowers','Flowery Belt','Twigsflowers'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.belt.twigsleaves','Leafy Belt','Twigsleaves'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.assassin','Assassin Chest','Assassin'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.bonerattler','Bonerattler Cuirass','Bonerattler'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.cloth_blue_0','Blue Linen Chest','ClothBlue0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.cloth_green_0','Green Linen Chest','ClothGreen0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.cloth_purple_0','Purple Linen Chest','ClothPurple0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.cultist_chest_blue','Blue Cultist Chest','CultistBlue'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.cultist_chest_purple','Purple Cultist Chest','CultistPurple'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.druid','Druid''s Vest','Druid'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.leather_0','Swift Chest','Leather0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.leather_2','Leather Cuirass','Leather2'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.leather_adventurer','Agile Chest','Leather2'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.plate_green_0','Iron Chestplate','PlateGreen0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.steel_0','Steel Cuirass','Steel0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.tarasque','Tarasque Cuirass','Tarasque'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.twig','Twig Shirt','Twig'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.twigsflowers','Flowery Shirt','Twigsflowers'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.twigsleaves','Leafy Shirt','Twigsleaves'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.worker_green_0','Green Worker Shirt','WorkerGreen0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.worker_green_1','Green Worker Shirt','WorkerGreen1'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.worker_orange_0','Orange Worker Shirt','WorkerOrange0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.worker_orange_1','Orange Worker Shirt','WorkerOrange1'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.worker_purple_0','Purple Worker Shirt','WorkerPurple0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.worker_purple_1','Purple Worker Shirt','WorkerPurple1'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.worker_red_0','Red Worker Shirt','WorkerRed0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.worker_red_1','Red Worker Shirt','WorkerRed1'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.worker_yellow_0','Yellow Worker Shirt','WorkerYellow0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.chest.worker_yellow_1','Yellow Worker Shirt','WorkerYellow1'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.assassin','Assassin Boots','Assassin'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.bonerattler','Bonerattler Boots','Bonerattler'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.cloth_blue_0','Blue Linen Boots','ClothBlue0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.cloth_green_0','Green Linen Boots','ClothGreen0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.cloth_purple_0','Purple Linen Boots','ClothPurple0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.cultist_boots','Cultist Boots','Cultist'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.druid','Druid''s Slippers','Druid'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.jackalope_slippers','Fluffy Jackalope Slippers','JackalopeSlips'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.leather_0','Swift Boots','Leather0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.leather_2','Leather Boots','Leather2'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.leather_adventurer','Agile Kickers','Leather2'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.plate_0','Iron Feet','Plate0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.steel_0','Steel Boots','Steel0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.tarasque','Tarasque Boots','Tarasque'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.twig','Twig Boots','Twig'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.twigsflowers','Flowery Boots','Twigsflowers'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.foot.twigsleaves','Leafy Boots','Twigsleaves'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.assassin','Assassin Gloves','Assassin'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.bonerattler','Bonerattler Gauntlets','Bonerattler'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.cloth_blue_0','Blue Linen Wrists','ClothBlue0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.cloth_green_0','Green Linen Wrists','ClothGreen0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.cloth_purple_0','Purple Silk Wrists','ClothPurple0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.cultist_hands_blue','Blue Cultist Gloves','CultistBlue'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.cultist_hands_purple','Purple Cultist Gloves','CultistPurple'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.druid','Druid''s Gloves','Druid'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.leather_0','Swift Gloves','Leather0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.leather_2','Leather Gloves','Leather2'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.leather_adventurer','Agile Gauntlets','Leather2'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.plate_0','Iron Handguards','Plate0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.steel_0','Steel Gauntlets','Steel0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.tarasque','Tarasque Gauntlets','Tarasque'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.twig','Twig Wraps','Twig'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.twigsflowers','Flowery Wraps','Twigsflowers'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.hand.twigsleaves','Leafy Wraps','Twigsleaves'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.head.assa_mask_0','Dark Assassin Mask','AssaMask0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.head.leather_0','Swift Leather Cap','Leather0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.neck.neck_0','Plain Necklace','Neck0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.neck.neck_1','Gem of lesser Protection','Neck1'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.assassin','Assassin Pants','Assassin'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.bonerattler','Bonerattler Chausses','Bonerattler'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.cloth_blue_0','Blue Linen Skirt','ClothBlue0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.cloth_green_0','Green Linen Skirt','ClothGreen0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.cloth_purple_0','Purple Linen Skirt','ClothPurple0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.cultist_legs_blue','Blue Cultist Skirt','CultistBlue'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.cultist_legs_purple','Purple Cultist Skirt','CultistPurple'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.druid','Druid''s Kilt','Druid'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.hunting','Hunting Pants','Hunting'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.leather_0','Swift Pants','Leather0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.leather_2','Leather Leg Armour','Leather2'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.leather_adventurer','Agile Pantalons','Leather2'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.plate_green_0','Iron Legguards','PlateGreen0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.steel_0','Steel Chausses','Steel0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.tarasque','Tarasque Chausses','Tarasque'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.twig','Twig Pants','Twig'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.twigsflowers','Flowery Pants','Twigsflowers'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.twigsleaves','Leafy Pants','Twigsleaves'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.worker_blue_0','Blue Worker Pants','WorkerBlue0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.ring.ring_0','Scratched Ring','Ring0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.assassin','Assassin Shoulder Guard','Assassin'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.bonerattler','Bonerattler Shoulder Pad','Bonerattler'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.cloth_blue_0','Blue Linen Coat','ClothBlue0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.cloth_blue_1','Blue Cloth Pads','ClothBlue1'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.cloth_green_0','Green Linen Coat','ClothGreen0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.cloth_purple_0','Purple Linen Coat','ClothPurple0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.cultist_shoulder_blue','Blue Cultist Mantle','CultistBlue'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.cultist_shoulder_purple','Purple Cultist Mantle','CultistPurple'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.druidshoulder','Druid Shoulders','DruidShoulder'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.iron_spikes','Iron Spiked Pauldrons','IronSpikes'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.leather_0','Leather Pauldrons','Leather0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.leather_1','Swift Shoulderpads','Leather1'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.leather_2','Leather Shoulder Pad','Leather2'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.leather_adventurer','Agile Guards','Leather2'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.leather_iron_0','Iron and Leather Spaulders','IronLeather0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.leather_iron_1','Iron and Leather Spaulders','IronLeather1'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.leather_iron_2','Iron and Leather Spaulders','IronLeather2'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.leather_iron_3','Iron and Leather Spaulders','IronLeather3'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.leather_strips','Leather Strips','LeatherStrips'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.plate_0','Iron Shoulderguards','Plate0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.steel_0','Steel Shoulder Pad','Steel0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.tarasque','Tarasque Shoulder Pad','Tarasque'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.twigs','Twiggy Shoulders','TwiggyShoulder'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.twigsflowers','Flowery Shoulders','FlowerShoulder'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.shoulder.twigsleaves','Leafy Shoulders','LeafyShoulder'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.starter.lantern','Black Lantern','Black0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.starter.rugged_chest','Rugged Shirt','Rugged0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.starter.rugged_pants','Rugged Commoner''s Pants','Rugged0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.starter.sandals_0','Worn out Sandals','Sandal0'); +INSERT INTO _temp_item_defs VALUES('common.items.armor.tabard.admin','Admin''s Tabard','Admin'); +INSERT INTO _temp_item_defs VALUES('common.items.boss_drops.exp_flask','Flask of Velorite Dusk',''); +INSERT INTO _temp_item_defs VALUES('common.items.boss_drops.lantern','Magic Lantern','Blue0'); +INSERT INTO _temp_item_defs VALUES('common.items.boss_drops.potions','Potent Potion',''); +INSERT INTO _temp_item_defs VALUES('common.items.boss_drops.xp_potion','Potion of Skill',''); +INSERT INTO _temp_item_defs VALUES('common.items.consumable.potion_big','Large Potion',''); +INSERT INTO _temp_item_defs VALUES('common.items.consumable.potion_med','Medium Potion',''); +INSERT INTO _temp_item_defs VALUES('common.items.consumable.potion_minor','Minor Potion',''); +INSERT INTO _temp_item_defs VALUES('common.items.crafting_ing.empty_vial','Empty Vial',''); +INSERT INTO _temp_item_defs VALUES('common.items.crafting_ing.leather_scraps','Leather Scraps',''); +INSERT INTO _temp_item_defs VALUES('common.items.crafting_ing.shiny_gem','Shiny Gem',''); +INSERT INTO _temp_item_defs VALUES('common.items.crafting_ing.stones','Stones',''); +INSERT INTO _temp_item_defs VALUES('common.items.crafting_ing.twigs','Twigs',''); +INSERT INTO _temp_item_defs VALUES('common.items.crafting_tools.craftsman_hammer','Craftsman Hammer',''); +INSERT INTO _temp_item_defs VALUES('common.items.crafting_tools.mortar_pestle','Mortar and Pestle',''); +INSERT INTO _temp_item_defs VALUES('common.items.debug.admin','Admin''s Tabard','Admin'); +INSERT INTO _temp_item_defs VALUES('common.items.debug.admin_back','Admin''s Cape','Admin'); +INSERT INTO _temp_item_defs VALUES('common.items.debug.boost','Belzeshrub the Broom-God','Boost'); +INSERT INTO _temp_item_defs VALUES('common.items.debug.cultist_belt','Cultist Belt','Cultist'); +INSERT INTO _temp_item_defs VALUES('common.items.debug.cultist_boots','Cultist Boots','Cultist'); +INSERT INTO _temp_item_defs VALUES('common.items.debug.cultist_chest_blue','Blue Cultist Chest','CultistBlue'); +INSERT INTO _temp_item_defs VALUES('common.items.debug.cultist_hands_blue','Blue Cultist Gloves','CultistBlue'); +INSERT INTO _temp_item_defs VALUES('common.items.debug.cultist_legs_blue','Blue Cultist Skirt','CultistBlue'); +INSERT INTO _temp_item_defs VALUES('common.items.debug.cultist_purp_2h_boss-0','Admin Greatsword','CultPurp0'); +INSERT INTO _temp_item_defs VALUES('common.items.debug.cultist_shoulder_blue','Blue Cultist Mantle','CultistBlue'); +INSERT INTO _temp_item_defs VALUES('common.items.debug.dungeon_purple-0','Purple Admin Cape','DungPurp0'); +INSERT INTO _temp_item_defs VALUES('common.items.debug.possess','Belzeshrub the Broom-God','Boost'); +INSERT INTO _temp_item_defs VALUES('common.items.flowers.blue','Blue Flower',''); +INSERT INTO _temp_item_defs VALUES('common.items.flowers.pink','Pink Flower',''); +INSERT INTO _temp_item_defs VALUES('common.items.flowers.red','Red Flower',''); +INSERT INTO _temp_item_defs VALUES('common.items.flowers.sun','Sunflower',''); +INSERT INTO _temp_item_defs VALUES('common.items.flowers.white','White flower',''); +INSERT INTO _temp_item_defs VALUES('common.items.flowers.yellow','Yellow Flower',''); +INSERT INTO _temp_item_defs VALUES('common.items.food.apple','Apple',''); +INSERT INTO _temp_item_defs VALUES('common.items.food.apple_mushroom_curry','Mushroom Curry',''); +INSERT INTO _temp_item_defs VALUES('common.items.food.apple_stick','Apple Stick',''); +INSERT INTO _temp_item_defs VALUES('common.items.food.cheese','Dwarven Cheese',''); +INSERT INTO _temp_item_defs VALUES('common.items.food.coconut','Coconut',''); +INSERT INTO _temp_item_defs VALUES('common.items.food.mushroom','Mushroom',''); +INSERT INTO _temp_item_defs VALUES('common.items.food.mushroom_stick','Mushroom Stick',''); +INSERT INTO _temp_item_defs VALUES('common.items.grasses.long','Long Grass',''); +INSERT INTO _temp_item_defs VALUES('common.items.grasses.medium','Medium Grass',''); +INSERT INTO _temp_item_defs VALUES('common.items.grasses.short','Short Grass',''); +INSERT INTO _temp_item_defs VALUES('common.items.lantern.black_0','Black Lantern','Black0'); +INSERT INTO _temp_item_defs VALUES('common.items.lantern.blue_0','Cool Blue Lantern','Blue0'); +INSERT INTO _temp_item_defs VALUES('common.items.lantern.green_0','Lime Zest Lantern','Green0'); +INSERT INTO _temp_item_defs VALUES('common.items.lantern.red_0','Red Lantern','Red0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.back.dungeon_purple-0','Purple Cultist Cape','DungPurp0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.belt.cultist_belt','Cultist Belt','Cultist'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.chest.cultist_chest_purple','Purple Cultist Chest','CultistPurple'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.chest.worker_green_0','Green Worker Shirt','WorkerGreen0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.chest.worker_green_1','Green Worker Shirt','WorkerGreen1'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.chest.worker_orange_0','Orange Worker Shirt','WorkerOrange0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.chest.worker_orange_1','Orange Worker Shirt','WorkerOrange1'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.chest.worker_purple_0','Purple Worker Shirt','WorkerPurple0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.chest.worker_purple_1','Purple Worker Shirt','WorkerPurple1'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.chest.worker_red_0','Red Worker Shirt','WorkerRed0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.chest.worker_red_1','Red Worker Shirt','WorkerRed1'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.chest.worker_yellow_0','Yellow Worker Shirt','WorkerYellow0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.chest.worker_yellow_1','Yellow Worker Shirt','WorkerYellow1'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.foot.cultist_boots','Cultist Boots','Cultist'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.hand.cultist_hands_purple','Purple Cultist Gloves','CultistPurple'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.pants.cultist_legs_purple','Purple Cultist Skirt','CultistPurple'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_armor.shoulder.cultist_shoulder_purple','Purple Cultist Mantle','CultistPurple'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.axe.malachite_axe-0','Malachite Axe','MalachiteAxe0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.axe.starter_axe','Notched Axe','BasicAxe'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.bow.horn_longbow-0','Horn Bow','HornLongbow0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.dagger.starter_dagger','Rusty Dagger','BasicDagger'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.empty.empty','Empty',''); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.hammer.cultist_purp_2h-0','Magical Cultist Warhammer','CultPurp0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.hammer.starter_hammer','Sturdy Old Hammer','BasicHammer'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.shield.shield_1','A Tattered Targe','BasicShield'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.staff.bone_staff','Bone Staff','BoneStaff'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.staff.cultist_staff','Cultist Staff','CultistStaff'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.sword.cultist_purp_2h-0','Magical Cultist Greatsword','CultPurp0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.sword.cultist_purp_2h_boss-0','Magical Cultist Greatsword','CultPurp0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.sword.starter_sword','Battered Sword','BasicSword'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.sword.zweihander_sword_0','Sturdy Zweihander','Zweihander0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.tool.broom','Broom','Broom'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.tool.fishing_rod','Fishing Rod','FishingRod0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.tool.hoe','Hoe','Hoe0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.tool.pickaxe','Pickaxe','Pickaxe0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.tool.pitchfork','Pitchfork','Pitchfork'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.tool.rake','Rake','Rake'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.tool.shovel-0','Shovel','Shovel0'); +INSERT INTO _temp_item_defs VALUES('common.items.npc_weapons.tool.shovel-1','Shovel','Shovel1'); +INSERT INTO _temp_item_defs VALUES('common.items.ore.velorite','Velorite',''); +INSERT INTO _temp_item_defs VALUES('common.items.ore.veloritefrag','Velorite Fragment',''); +INSERT INTO _temp_item_defs VALUES('common.items.testing.test_boots','Testing Boots','Dark'); +INSERT INTO _temp_item_defs VALUES('common.items.utility.bomb','Bomb',''); +INSERT INTO _temp_item_defs VALUES('common.items.utility.bomb_pile','Bomb',''); +INSERT INTO _temp_item_defs VALUES('common.items.utility.collar','Collar',''); +INSERT INTO _temp_item_defs VALUES('common.items.utility.firework_blue','Firework Blue',''); +INSERT INTO _temp_item_defs VALUES('common.items.utility.firework_green','Firework Green',''); +INSERT INTO _temp_item_defs VALUES('common.items.utility.firework_purple','Firework Purple',''); +INSERT INTO _temp_item_defs VALUES('common.items.utility.firework_red','Firework Red',''); +INSERT INTO _temp_item_defs VALUES('common.items.utility.firework_yellow','Firework Yellow',''); +INSERT INTO _temp_item_defs VALUES('common.items.utility.training_dummy','Training Dummy',''); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.bloodsteel_axe-0','Bloodsteel Axe','BloodsteelAxe0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.bloodsteel_axe-1','Executioner''s Axe','BloodsteelAxe1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.bloodsteel_axe-2','Tribal Axe','BloodsteelAxe2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.bronze_axe-0','Bronze Axe','BronzeAxe0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.bronze_axe-1','Discus Axe','BronzeAxe1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.cobalt_axe-0','Cobalt Axe','CobaltAxe0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.iron_axe-0','Iron Greataxe','IronAxe0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.iron_axe-1','Ceremonial Axe','IronAxe1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.iron_axe-2','Cyclone Axe','IronAxe2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.iron_axe-3','Iron Battleaxe','IronAxe3'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.iron_axe-4','Butcher''s Axe','IronAxe4'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.iron_axe-5','Barbarian''s Axe','IronAxe5'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.iron_axe-6','Iron Axe','IronAxe6'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.iron_axe-7','Iron Labrys','IronAxe7'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.iron_axe-8','Fanged Axe','IronAxe8'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.iron_axe-9','Wolfen Axe','IronAxe9'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.malachite_axe-0','Malachite Axe','MalachiteAxe0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.orc_axe-0','Beast Cleaver','OrcAxe0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.starter_axe','Notched Axe','BasicAxe'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.steel_axe-0','Steel Battleaxe','SteelAxe0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.steel_axe-1','Steel Labrys','SteelAxe1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.steel_axe-2','Steel Axe','SteelAxe2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.steel_axe-3','Crescent Axe','SteelAxe3'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.steel_axe-4','Moon Axe','SteelAxe4'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.steel_axe-5','Owl Axe','SteelAxe5'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.steel_axe-6','Spade Axe','SteelAxe6'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.worn_iron_axe-0','Worn Dwarven Axe','WornIronAxe0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.worn_iron_axe-1','Worn Elven Axe','WornIronAxe1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.worn_iron_axe-2','Worn Human Axe','WornIronAxe2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.worn_iron_axe-3','Worn Orcish Axe','WornIronAxe3'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.axe.worn_iron_axe-4','Beetle Axe','WornIronAxe4'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.bow.horn_longbow-0','Horn Bow','HornLongbow0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.bow.iron_longbow-0','Soldier''s Bow','IronLongbow0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.bow.leafy_longbow-0','Elven Longbow','LeafyLongbow0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.bow.leafy_shortbow-0','Elven Shortbow','LeafyShortbow0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.bow.nature_ore_longbow-0','Velorite Bow','NatureOreLongbow'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.bow.rare_longbow','Enchanted Longbow','RareLongbow'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.bow.starter_bow','Uneven Bow','ShortBow0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.bow.wood_longbow-0','Longbow','WoodLongbow0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.bow.wood_longbow-1','Recurve Bow','WoodLongbow1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.bow.wood_shortbow-0','Hunting Bow','WoodShortbow0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.bow.wood_shortbow-1','Horse Bow','WoodShortbow1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.dagger.starter_dagger','Rusty Dagger','BasicDagger'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.empty.empty','Empty',''); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.bronze_hammer-0','Bronze Hammer','BronzeHammer0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.bronze_hammer-1','Bronze Club','BronzeHammer1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.cobalt_hammer-0','Cobalt Hammer','CobaltHammer0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.cobalt_hammer-1','Cobalt Mace','CobaltHammer1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.cultist_purp_2h-0','Magical Cultist Warhammer','CultPurp0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.flimsy_hammer','Flimsy Hammer','FlimsyHammer'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.hammer_1','Crude Mallet','BasicHammer'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.iron_hammer-0','Iron Hammer','IronHammer0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.iron_hammer-1','Iron Battlehammer','IronHammer1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.iron_hammer-2','Iron Mace','IronHammer2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.iron_hammer-3','Crowned Mace','IronHammer3'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.iron_hammer-4','Forge Hammer','IronHammer4'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.iron_hammer-5','Pike Hammer','IronHammer5'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.iron_hammer-6','Spiked Maul','IronHammer6'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.iron_hammer-7','Giant''s Fist','IronHammer7'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.iron_hammer-8','Lucerne Hammer','IronHammer8'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.mjolnir','Mjolnir','Mjolnir'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.ramshead_hammer','Ram''s Head Mace','RamsheadHammer'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.runic_hammer','Runic Hammer','RunicHammer'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.starter_hammer','Sturdy Old Hammer','BasicHammer'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.steel_hammer-0','Steel Hammer','SteelHammer0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.steel_hammer-1','Steel Greathammer','SteelHammer1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.steel_hammer-2','Steel Club','SteelHammer2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.steel_hammer-3','Battle Mace','SteelHammer3'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.steel_hammer-4','Brute''s Hammer','SteelHammer4'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.steel_hammer-5','Morning Star','SteelHammer5'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.stone_hammer-0','Basalt Sledgehammer','StoneHammer0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.stone_hammer-1','Granite Sledgehammer','StoneHammer1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.stone_hammer-2','Rocky Maul','StoneHammer2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.stone_hammer-3','Stone Sledgehammer','StoneHammer3'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.wood_hammer-0','Hardwood Mallet','WoodHammer0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.worn_iron_hammer-0','Worn Dwarven Hammer','WornIronHammer0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.worn_iron_hammer-1','Worn Elven Hammer','WornIronHammer1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.worn_iron_hammer-2','Worn Human Mace','WornIronHammer2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.hammer.worn_iron_hammer-3','Worn Orcish Hammer','WornIronHammer3'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.shield.shield_1','A Tattered Targe','BasicShield'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.staff.amethyst_staff','Amethyst Staff','AmethystStaff'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.staff.bone_staff','Bone Staff','BoneStaff'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.staff.cultist_staff','Cultist Staff','CultistStaff'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.staff.sceptre_velorite_0','Velorite Sceptre','SceptreVelorite'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.staff.staff_1','Humble Stick','BasicStaff'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.staff.staff_nature','Sceptre of Regeneration','Sceptre'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.staff.starter_staff','Gnarled Rod','BasicStaff'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.cultist_purp_2h-0','Magical Cultist Greatsword','CultPurp0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_dam-0','Damaged Greatsword','GreatswordDam0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_dam-1','Damaged Greatsword','GreatswordDam1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_dam-2','Damaged Greatsword','GreatswordDam2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_fine-0','Fine Greatsword','GreatswordFine0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_fine-1','Fine Greatsword','GreatswordFine1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_fine-2','Fine Greatsword','GreatswordFine2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_orn-0','Ornamented Greatsword','GreatswordOrn0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_orn-1','Ornamented Greatsword','GreatswordOrn1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_orn-2','Ornamented Greatsword','GreatswordOrn2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_simple-0','Simple Greatsword','GreatswordSimple0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_simple-1','Simple Greatsword','GreatswordSimple1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_simple-2','Simple Greatsword','GreatswordSimple2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_dam-0','Damaged Longsword','LongDam0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_dam-1','Damaged Longsword','LongDam1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_dam-2','Damaged Longsword','LongDam2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_dam-3','Damaged Longsword','LongDam3'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_dam-4','Damaged Longsword','LongDam4'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_dam-5','Damaged Longsword','LongDam5'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_fine-0','Fine Longsword','LongFine0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_fine-1','Fine Longsword','LongFine1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_fine-2','Fine Longsword','LongFine2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_fine-3','Fine Longsword','LongFine3'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_fine-4','Fine Longsword','LongFine4'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_fine-5','Fine Longsword','LongFine5'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_orn-0','Ornamented Longsword','LongOrn0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_orn-1','Ornamented Longsword','LongOrn1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_orn-2','Ornamented Longsword','LongOrn2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_orn-3','Ornamented Longsword','LongOrn3'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_orn-4','Ornamented Longsword','LongOrn4'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_orn-5','Ornamented Longsword','LongOrn5'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_simple-0','Simple Longsword','LongSimple0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_simple-1','Simple Longsword','LongSimple1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_simple-2','Simple Longsword','LongSimple2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_simple-3','Simple Longsword','LongSimple3'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_simple-4','Simple Longsword','LongSimple4'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_simple-5','Simple Longsword','LongSimple5'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.short_sword_0','Vicious Gladius','Short0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.starter_sword','Battered Sword','BasicSword'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.wood_sword','Forest Spirit','WoodTraining'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.zweihander_sword_0','Sturdy Zweihander','Zweihander0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.tool.broom','Broom','Broom'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.tool.fishing_rod','Fishing Rod','FishingRod0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.tool.hoe','Hoe','Hoe0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.tool.pickaxe','Pickaxe','Pickaxe0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.tool.pitchfork','Pitchfork','Pitchfork'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.tool.rake','Rake','Rake'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.tool.shovel-0','Shovel','Shovel0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.tool.shovel-1','Shovel','Shovel1'); + +-- Accounts for spelling mistake in "Ornimented Greatsword" legacy items +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_orn-0','Ornimented Greatsword','GreatswordOrn0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_orn-1','Ornimented Greatsword','GreatswordOrn1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.greatsword_2h_orn-2','Ornimented Greatsword','GreatswordOrn2'); + +-- Accounts for spelling mistake in "Ornimented Longsword" legacy items +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_orn-0','Ornimented Longsword','LongOrn0'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_orn-1','Ornimented Longsword','LongOrn1'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_orn-2','Ornimented Longsword','LongOrn2'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_orn-3','Ornimented Longsword','LongOrn3'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_orn-4','Ornimented Longsword','LongOrn4'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.long_2h_orn-5','Ornimented Longsword','LongOrn5'); + +-- Accounts for legacy "Hunting Pants" item with Kind = Green +INSERT INTO _temp_item_defs VALUES('common.items.armor.pants.hunting','Hunting Pants','Green'); + +-- Accounts for legacy "Wooden Sword" and "Wooden Training Sword" items +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.wood_sword','Wooden Sword','WoodTraining'); +INSERT INTO _temp_item_defs VALUES('common.items.weapons.sword.wood_sword','Wooden Training Sword','WoodTraining'); + +-- Accounts for renamed "Powerful Potion" item +INSERT INTO _temp_item_defs VALUES('common.items.boss_drops.potions','Powerful Potion',''); + +-- Remove items that have another item with an identical Weapon/Armor Kind and Name so are +-- therefore indistinguishable in inventory/loadout JSON +DELETE FROM _temp_item_defs WHERE item_definition_id = 'common.items.utility.bomb_pile'; +DELETE FROM _temp_item_defs WHERE item_definition_id = 'common.items.debug.admin_back'; +DELETE FROM _temp_item_defs WHERE item_definition_id = 'common.items.debug.admin'; +DELETE FROM _temp_item_defs WHERE item_definition_id = 'common.items.debug.possess'; +DELETE FROM _temp_item_defs WHERE item_definition_id = 'common.items.lantern.black_0'; +DELETE FROM _temp_item_defs WHERE item_definition_id = 'common.items.debug.cultist_chest_blue'; +DELETE FROM _temp_item_defs WHERE item_definition_id = 'common.items.debug.cultist_hands_blue'; +DELETE FROM _temp_item_defs WHERE item_definition_id = 'common.items.debug.cultist_shoulder_blue'; +DELETE FROM _temp_item_defs WHERE item_definition_id = 'common.items.debug.cultist_legs_blue'; +DELETE FROM _temp_item_defs WHERE item_definition_id = 'common.items.debug.cultist_belt'; +DELETE FROM _temp_item_defs WHERE item_definition_id = 'common.items.debug.cultist_boots'; + +-- Remove NPC items that players can never have +DELETE FROM _temp_item_defs WHERE item_definition_id LIKE 'common.items.npc_%'; + +-- +-- 11) Migrate inventory items extracted from the inventory items JSON in the old schema +-- + +CREATE TEMP TABLE _temp_inventory_items +( + temp_item_id INTEGER + PRIMARY KEY AUTOINCREMENT NOT NULL, + parent_container_item_id INTEGER NOT NULL, + item_definition_id TEXT NOT NULL, + stack_size INTEGER NOT NULL, + position TEXT NOT NULL +); + +WITH slots AS ( + SELECT character_id + 1 as character_id, + value AS slot_json + FROM _inventory_bak, + json_tree(_inventory_bak.items) + WHERE key = 'slots' +), + item_json AS ( + SELECT character_id, + key as position, + value + FROM slots, + json_each(slots.slot_json) + WHERE type = 'object' + ), + items AS ( + SELECT i.character_id, + value, + position, + json_extract(i.value, '$.name') AS item_name, + COALESCE( + json_extract(value, '$.kind.Consumable.amount'), + json_extract(value, '$.kind.Ingredient.amount'), + json_extract(value, '$.kind.Throwable.amount'), + json_extract(value, '$.kind.Utility.amount') + ) AS amount, + COALESCE( + json_extract(value, '$.kind.Tool.kind.Sword'), + json_extract(value, '$.kind.Tool.kind.Axe'), + json_extract(value, '$.kind.Tool.kind.Hammer'), + json_extract(value, '$.kind.Tool.kind.Bow'), + json_extract(value, '$.kind.Tool.kind.Dagger'), + json_extract(value, '$.kind.Tool.kind.Staff'), + json_extract(value, '$.kind.Tool.kind.Shield'), + json_extract(value, '$.kind.Tool.kind.Debug'), + json_extract(value, '$.kind.Tool.kind.Farming'), + json_extract(value, '$.kind.Tool.kind.Empty'), + json_extract(value, '$.kind.Armor.kind.Shoulder'), + json_extract(value, '$.kind.Armor.kind.Chest'), + json_extract(value, '$.kind.Armor.kind.Belt'), + json_extract(value, '$.kind.Armor.kind.Hand'), + json_extract(value, '$.kind.Armor.kind.Pants'), + json_extract(value, '$.kind.Armor.kind.Foot'), + json_extract(value, '$.kind.Armor.kind.Back'), + json_extract(value, '$.kind.Armor.kind.Ring'), + json_extract(value, '$.kind.Armor.kind.Neck'), + json_extract(value, '$.kind.Armor.kind.Head'), + json_extract(value, '$.kind.Armor.kind.Tabard'), + json_extract(value, '$.kind.Lantern.kind') + ) AS weapon_armor_kind + FROM item_json i + ) +INSERT INTO _temp_inventory_items +SELECT NULL, + inv.item_id AS parent_container_item_id, + d.item_definition_id, + COALESCE(amount, 1), + i.position +FROM items i + JOIN item inv ON (inv.parent_container_item_id = i.character_id AND inv.position = 'inventory') + LEFT JOIN _temp_item_defs d ON ((i.weapon_armor_kind = d.kind AND i.item_name = d.item_name) OR (i.weapon_armor_kind IS NULL AND i.item_name = d.item_name)); + +-- Create an entity_id for each inventory item +INSERT +INTO entity +SELECT NULL +FROM _temp_inventory_items; + +-- Insert an item record for each item +INSERT +INTO item +SELECT e.entity_id, + i.parent_container_item_id, + i.item_definition_id, + i.stack_size, + i.position +FROM _temp_inventory_items i + JOIN entity e ON (e.entity_id = ( + (SELECT MAX(entity_id) FROM entity) + - (SELECT COUNT(1) FROM _temp_inventory_items) + + i.temp_item_id)); + +-- +-- 12) Migrate loadout items extracted from the loadout items JSON in the old schema +-- + +CREATE TEMP TABLE _temp_loadout_items +( + temp_item_id INTEGER + PRIMARY KEY AUTOINCREMENT NOT NULL, + parent_container_item_id INTEGER NOT NULL, + item_definition_id TEXT NOT NULL, + position TEXT NOT NULL +); + +WITH item_json AS ( + SELECT character_id + 1 as character_id, + j.key, + j.value + FROM _loadout_bak l, + json_each(items) j + WHERE value IS NOT NULL), + items AS ( + SELECT character_id, + key AS position, + COALESCE( + json_extract(i.value, '$.name'), + json_extract(i.value, '$.item.name')) AS item_name, + COALESCE( + json_extract(value, '$.item.kind.Tool.kind.Sword'), + json_extract(value, '$.item.kind.Tool.kind.Axe'), + json_extract(value, '$.item.kind.Tool.kind.Hammer'), + json_extract(value, '$.item.kind.Tool.kind.Bow'), + json_extract(value, '$.item.kind.Tool.kind.Dagger'), + json_extract(value, '$.item.kind.Tool.kind.Staff'), + json_extract(value, '$.item.kind.Tool.kind.Shield'), + json_extract(value, '$.item.kind.Tool.kind.Debug'), + json_extract(value, '$.item.kind.Tool.kind.Farming'), + json_extract(value, '$.item.kind.Tool.kind.Empty'), + json_extract(value, '$.kind.Armor.kind.Shoulder'), + json_extract(value, '$.kind.Armor.kind.Chest'), + json_extract(value, '$.kind.Armor.kind.Belt'), + json_extract(value, '$.kind.Armor.kind.Hand'), + json_extract(value, '$.kind.Armor.kind.Pants'), + json_extract(value, '$.kind.Armor.kind.Foot'), + json_extract(value, '$.kind.Armor.kind.Back'), + json_extract(value, '$.kind.Armor.kind.Ring'), + json_extract(value, '$.kind.Armor.kind.Neck'), + json_extract(value, '$.kind.Armor.kind.Head'), + json_extract(value, '$.kind.Armor.kind.Tabard'), + json_extract(value, '$.kind.Lantern.kind') + ) AS weapon_armor_kind + FROM item_json i + ) +INSERT +INTO _temp_loadout_items +SELECT NULL, + inv.item_id AS parent_container_item_id, + d.item_definition_id, + i.position +FROM items i + JOIN item inv ON (inv.parent_container_item_id = i.character_id AND inv.position = 'loadout') + LEFT JOIN _temp_item_defs d ON ((i.weapon_armor_kind = d.kind AND i.item_name = d.item_name) OR (i.weapon_armor_kind IS NULL AND i.item_name = d.item_name)); + +-- Create an entity_id for each loadout item +INSERT +INTO entity +SELECT NULL +FROM _temp_loadout_items; + +-- Insert an item record for each item +INSERT +INTO item +SELECT e.entity_id, + l.parent_container_item_id, + l.item_definition_id, + 1, --stack size + l.position +FROM _temp_loadout_items l + JOIN entity e ON (e.entity_id = ( + (SELECT MAX(entity_id) FROM entity) + - (SELECT COUNT(1) FROM _temp_loadout_items) + + l.temp_item_id)); diff --git a/server/src/persistence/character.rs b/server/src/persistence/character.rs index 90369e0b6b..e3b7fff2b1 100644 --- a/server/src/persistence/character.rs +++ b/server/src/persistence/character.rs @@ -1,270 +1,94 @@ //! Database operations related to character data //! -//! Methods in this module should remain private - database updates and loading -//! are communicated via requests to the [`CharacterLoader`] and -//! [`CharacterUpdater`] while results/responses are polled and handled each -//! server tick. - +//! Methods in this module should remain private to the persistence module - +//! database updates and loading are communicated via requests to the +//! [`CharacterLoader`] and [`CharacterUpdater`] while results/responses are +//! polled and handled each server tick. extern crate diesel; -use super::{ - error::Error, - establish_connection, - models::{ - Body, Character, Inventory, InventoryUpdate, Loadout, LoadoutUpdate, NewCharacter, - NewLoadout, Stats, StatsJoinData, StatsUpdate, +use super::{error::Error, models::*, schema, VelorenTransaction}; +use crate::{ + comp, + persistence::{ + character::conversions::{ + convert_body_from_database, convert_body_to_database_json, + convert_character_from_database, convert_inventory_from_database_items, + convert_items_to_database_items, convert_loadout_from_database_items, + convert_stats_from_database, convert_stats_to_database, + }, + character_loader::{CharacterDataResult, CharacterListResult}, + error::Error::DatabaseError, + PersistedComponents, }, - schema, }; -use crate::{comp, persistence::models::SkillSetData}; -use common::{ - character::{Character as CharacterData, CharacterItem, MAX_CHARACTERS_PER_PLAYER}, - LoadoutBuilder, -}; -use crossbeam::{channel, channel::TryIter}; -use diesel::prelude::*; -use tracing::{error, warn}; +use common::character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER}; +use core::ops::Range; +use diesel::{prelude::*, sql_query, sql_types::BigInt}; +use std::sync::Arc; +use tracing::{debug, error}; -type CharacterLoaderRequest = (specs::Entity, CharacterLoaderRequestKind); +/// Private module for very tightly coupled database conversion methods. In +/// general, these have many invariants that need to be maintained when they're +/// called--do not assume it's safe to make these public! +mod conversions; -/// Available database operations when modifying a player's character list -enum CharacterLoaderRequestKind { - CreateCharacter { - player_uuid: String, - character_alias: String, - character_tool: Option, - body: comp::Body, - }, - DeleteCharacter { - player_uuid: String, - character_id: i32, - }, - LoadCharacterList { - player_uuid: String, - }, - LoadCharacterData { - player_uuid: String, - character_id: i32, - }, -} +pub(crate) type EntityId = i64; -/// A tuple of the components that are persisted to the DB for each character -pub type PersistedComponents = (comp::Body, comp::Stats, comp::Inventory, comp::Loadout); +const CHARACTER_PSEUDO_CONTAINER_DEF_ID: &str = "veloren.core.pseudo_containers.character"; +const INVENTORY_PSEUDO_CONTAINER_DEF_ID: &str = "veloren.core.pseudo_containers.inventory"; +const LOADOUT_PSEUDO_CONTAINER_DEF_ID: &str = "veloren.core.pseudo_containers.loadout"; +const INVENTORY_PSEUDO_CONTAINER_POSITION: &str = "inventory"; +const LOADOUT_PSEUDO_CONTAINER_POSITION: &str = "loadout"; +const WORLD_PSEUDO_CONTAINER_ID: EntityId = 1; -type CharacterListResult = Result, Error>; -type CharacterDataResult = Result; - -/// Wrapper for results for character actions. Can be a list of -/// characters, or component data belonging to an individual character -#[derive(Debug)] -pub enum CharacterLoaderResponseType { - CharacterList(CharacterListResult), - CharacterData(Box), -} - -/// Common message format dispatched in response to an update request -#[derive(Debug)] -pub struct CharacterLoaderResponse { - pub entity: specs::Entity, - pub result: CharacterLoaderResponseType, -} - -/// A bi-directional messaging resource for making requests to modify or load -/// character data in a background thread. -/// -/// This is used on the character selection screen, and after character -/// selection when loading the components associated with a character. -/// -/// Requests messages are sent in the form of -/// [`CharacterLoaderRequestKind`] and are dispatched at the character select -/// screen. -/// -/// Responses are polled on each server tick in the format -/// [`CharacterLoaderResponse`] -pub struct CharacterLoader { - update_rx: Option>, - update_tx: Option>, - handle: Option>, -} - -impl CharacterLoader { - pub fn new(db_dir: String) -> Self { - let (update_tx, internal_rx) = channel::unbounded::(); - let (internal_tx, update_rx) = channel::unbounded::(); - - let handle = std::thread::spawn(move || { - while let Ok(request) = internal_rx.recv() { - let (entity, kind) = request; - - if let Err(e) = internal_tx.send(CharacterLoaderResponse { - entity, - result: match kind { - CharacterLoaderRequestKind::CreateCharacter { - player_uuid, - character_alias, - character_tool, - body, - } => CharacterLoaderResponseType::CharacterList(create_character( - &player_uuid, - &character_alias, - character_tool, - &body, - &db_dir, - )), - CharacterLoaderRequestKind::DeleteCharacter { - player_uuid, - character_id, - } => CharacterLoaderResponseType::CharacterList(delete_character( - &player_uuid, - character_id, - &db_dir, - )), - CharacterLoaderRequestKind::LoadCharacterList { player_uuid } => { - CharacterLoaderResponseType::CharacterList(load_character_list( - &player_uuid, - &db_dir, - )) - }, - CharacterLoaderRequestKind::LoadCharacterData { - player_uuid, - character_id, - } => CharacterLoaderResponseType::CharacterData(Box::new( - load_character_data(&player_uuid, character_id, &db_dir), - )), - }, - }) { - error!(?e, "Could not send send persistence request"); - } - } - }); - - Self { - update_tx: Some(update_tx), - update_rx: Some(update_rx), - handle: Some(handle), - } - } - - /// Create a new character belonging to the player identified by - /// `player_uuid` - pub fn create_character( - &self, - entity: specs::Entity, - player_uuid: String, - character_alias: String, - character_tool: Option, - body: comp::Body, - ) { - if let Err(e) = self.update_tx.as_ref().unwrap().send(( - entity, - CharacterLoaderRequestKind::CreateCharacter { - player_uuid, - character_alias, - character_tool, - body, - }, - )) { - error!(?e, "Could not send character creation request"); - } - } - - /// Delete a character by `id` and `player_uuid` - pub fn delete_character(&self, entity: specs::Entity, player_uuid: String, character_id: i32) { - if let Err(e) = self.update_tx.as_ref().unwrap().send(( - entity, - CharacterLoaderRequestKind::DeleteCharacter { - player_uuid, - character_id, - }, - )) { - error!(?e, "Could not send character deletion request"); - } - } - - /// Loads a list of characters belonging to the player identified by - /// `player_uuid` - pub fn load_character_list(&self, entity: specs::Entity, player_uuid: String) { - if let Err(e) = self - .update_tx - .as_ref() - .unwrap() - .send((entity, CharacterLoaderRequestKind::LoadCharacterList { - player_uuid, - })) - { - error!(?e, "Could not send character list load request"); - } - } - - /// Loads components associated with a character - pub fn load_character_data( - &self, - entity: specs::Entity, - player_uuid: String, - character_id: i32, - ) { - if let Err(e) = self.update_tx.as_ref().unwrap().send(( - entity, - CharacterLoaderRequestKind::LoadCharacterData { - player_uuid, - character_id, - }, - )) { - error!(?e, "Could not send character data load request"); - } - } - - /// Returns a non-blocking iterator over CharacterLoaderResponse messages - pub fn messages(&self) -> TryIter { - self.update_rx.as_ref().unwrap().try_iter() - } -} - -impl Drop for CharacterLoader { - fn drop(&mut self) { - drop(self.update_tx.take()); - if let Err(e) = self.handle.take().unwrap().join() { - error!(?e, "Error from joining character loader thread"); - } - } +#[derive(Clone, Copy)] +struct CharacterContainers { + inventory_container_id: EntityId, + loadout_container_id: EntityId, } /// Load stored data for a character. /// /// After first logging in, and after a character is selected, we fetch this /// data for the purpose of inserting their persisted data for the entity. -fn load_character_data(player_uuid: &str, character_id: i32, db_dir: &str) -> CharacterDataResult { - let connection = establish_connection(db_dir)?; +pub fn load_character_data( + requesting_player_uuid: String, + char_id: CharacterId, + connection: VelorenTransaction, +) -> CharacterDataResult { + use schema::{body::dsl::*, character::dsl::*, item::dsl::*, stats::dsl::*}; - let result = schema::character::dsl::character - .filter(schema::character::id.eq(character_id)) - .filter(schema::character::player_uuid.eq(player_uuid)) - .inner_join(schema::body::table) - .inner_join(schema::stats::table) - .inner_join(schema::inventory::table) - .inner_join(schema::loadout::table) - .first::<(Character, Body, Stats, Inventory, Loadout)>(&connection); + let character_containers = get_pseudo_containers(connection, char_id)?; - match result { - Ok((character_data, body_data, stats_data, inventory, loadout)) => Ok(( - comp::Body::from(&body_data), - comp::Stats::from(StatsJoinData { - alias: &character_data.alias, - body: &comp::Body::from(&body_data), - stats: &stats_data, - }), - comp::Inventory::from(inventory), - comp::Loadout::from(&loadout), - )), - Err(e) => { - error!( - ?e, - ?character_id, - "Failed to load character data for character" - ); - Err(Error::CharacterDataError) - }, - } + // TODO: Make inventory and loadout item loading work with recursive items when + // container items are supported + let inventory_items = item + .filter(parent_container_item_id.eq(character_containers.inventory_container_id)) + .load::(&*connection)?; + + let loadout_items = item + .filter(parent_container_item_id.eq(character_containers.loadout_container_id)) + .load::(&*connection)?; + + let (character_data, stats_data) = character + .filter( + schema::character::dsl::character_id + .eq(char_id) + .and(player_uuid.eq(requesting_player_uuid)), + ) + .inner_join(stats) + .first::<(Character, Stats)>(&*connection)?; + + let char_body = body + .filter(schema::body::dsl::body_id.eq(char_id)) + .first::(&*connection)?; + + Ok(( + convert_body_from_database(&char_body)?, + convert_stats_from_database(&stats_data, character_data.alias), + convert_inventory_from_database_items(&inventory_items)?, + convert_loadout_from_database_items(&loadout_items)?, + )) } /// Loads a list of characters belonging to the player. This data is a small @@ -274,173 +98,288 @@ fn load_character_data(player_uuid: &str, character_id: i32, db_dir: &str) -> Ch /// In the event that a join fails, for a character (i.e. they lack an entry for /// stats, body, etc...) the character is skipped, and no entry will be /// returned. -fn load_character_list(player_uuid: &str, db_dir: &str) -> CharacterListResult { - let result = schema::character::dsl::character - .filter(schema::character::player_uuid.eq(player_uuid)) - .order(schema::character::id.desc()) - .inner_join(schema::body::table) - .inner_join(schema::stats::table) - .inner_join(schema::loadout::table) - .load::<(Character, Body, Stats, Loadout)>(&establish_connection(db_dir)?); +pub fn load_character_list( + player_uuid_: &str, + connection: VelorenTransaction, +) -> CharacterListResult { + use schema::{body::dsl::*, character::dsl::*, item::dsl::*, stats::dsl::*}; - match result { - Ok(data) => Ok(data - .iter() - .map(|(character_data, body_data, stats_data, loadout)| { - let character = CharacterData::from(character_data); - let body = comp::Body::from(body_data); - let level = stats_data.level as usize; - let loadout = comp::Loadout::from(loadout); + let result = character + .filter(player_uuid.eq(player_uuid_)) + .inner_join(stats) + .order(schema::character::dsl::character_id.desc()) + .load::<(Character, Stats)>(&*connection)?; - CharacterItem { - character, - body, - level, - loadout, - } + result + .iter() + .map(|(character_data, char_stats)| { + let char = convert_character_from_database(character_data); + + let db_body = body + .filter(schema::body::dsl::body_id.eq(character_data.character_id)) + .first::(&*connection)?; + + let char_body = convert_body_from_database(&db_body)?; + + let loadout_container_id = get_pseudo_container_id( + connection, + character_data.character_id, + LOADOUT_PSEUDO_CONTAINER_POSITION, + )?; + + // TODO: Make work with recursive items if containers are ever supported as part + // of a loadout + let loadout_items = item + .filter(parent_container_item_id.eq(loadout_container_id)) + .load::(&*connection)?; + + let loadout = convert_loadout_from_database_items(&loadout_items)?; + + Ok(CharacterItem { + character: char, + body: char_body, + level: char_stats.level as usize, + loadout, }) - .collect()), - Err(e) => { - error!(?e, ?player_uuid, "Failed to load character list for player"); - Err(Error::CharacterDataError) - }, - } + }) + .collect() } -/// Create a new character with provided comp::Character and comp::Body data. -/// -/// Note that sqlite does not support returning the inserted data after a -/// successful insert. To workaround, we wrap this in a transaction which -/// inserts, queries for the newly created character id, then uses the character -/// id for subsequent insertions -fn create_character( +pub fn create_character( uuid: &str, character_alias: &str, - character_tool: Option, - body: &comp::Body, - db_dir: &str, + persisted_components: PersistedComponents, + connection: VelorenTransaction, ) -> CharacterListResult { - check_character_limit(uuid, db_dir)?; + use schema::item::dsl::*; - let connection = establish_connection(db_dir)?; + check_character_limit(uuid, connection)?; - connection.transaction::<_, diesel::result::Error, _>(|| { - use schema::{body, character, character::dsl::*, inventory, loadout, stats}; + use schema::{body, character, stats}; - match body { - comp::Body::Humanoid(body_data) => { - let new_character = NewCharacter { - player_uuid: uuid, - alias: &character_alias, - tool: character_tool.as_deref(), - }; + let (body, stats, inventory, loadout) = persisted_components; - diesel::insert_into(character::table) - .values(&new_character) - .execute(&connection)?; + // Fetch new entity IDs for character, inventory and loadout + let mut new_entity_ids = get_new_entity_ids(connection, |next_id| next_id + 3)?; - let inserted_character = character - .filter(player_uuid.eq(uuid)) - .order(id.desc()) - .first::(&connection)?; + // Create pseudo-container items for character + let character_id = new_entity_ids.next().unwrap(); + let inventory_container_id = new_entity_ids.next().unwrap(); + let loadout_container_id = new_entity_ids.next().unwrap(); + let pseudo_containers = vec![ + Item { + stack_size: 1, + item_id: character_id, + parent_container_item_id: WORLD_PSEUDO_CONTAINER_ID, + item_definition_id: CHARACTER_PSEUDO_CONTAINER_DEF_ID.to_owned(), + position: character_id.to_string(), + }, + Item { + stack_size: 1, + item_id: inventory_container_id, + parent_container_item_id: character_id, + item_definition_id: INVENTORY_PSEUDO_CONTAINER_DEF_ID.to_owned(), + position: INVENTORY_PSEUDO_CONTAINER_POSITION.to_owned(), + }, + Item { + stack_size: 1, + item_id: loadout_container_id, + parent_container_item_id: character_id, + item_definition_id: LOADOUT_PSEUDO_CONTAINER_DEF_ID.to_owned(), + position: LOADOUT_PSEUDO_CONTAINER_POSITION.to_owned(), + }, + ]; + let pseudo_container_count = diesel::insert_into(item) + .values(pseudo_containers) + .execute(&*connection)?; - let new_body = Body { - character_id: inserted_character.id as i32, - species: body_data.species as i16, - body_type: body_data.body_type as i16, - hair_style: body_data.hair_style as i16, - beard: body_data.beard as i16, - eyes: body_data.eyes as i16, - accessory: body_data.accessory as i16, - hair_color: body_data.hair_color as i16, - skin: body_data.skin as i16, - eye_color: body_data.eye_color as i16, - }; + if pseudo_container_count != 3 { + return Err(Error::OtherError(format!( + "Error inserting initial pseudo containers for character id {} (expected 3, actual {})", + character_id, pseudo_container_count + ))); + } - diesel::insert_into(body::table) - .values(&new_body) - .execute(&connection)?; + // Insert stats record + let db_stats = convert_stats_to_database(character_id, &stats); + let stats_count = diesel::insert_into(stats::table) + .values(&db_stats) + .execute(&*connection)?; - let default_stats = comp::Stats::new(String::from(new_character.alias), *body); + if stats_count != 1 { + return Err(Error::OtherError(format!( + "Error inserting into stats table for char_id {}", + character_id + ))); + } - // Insert some default stats - let new_stats = Stats { - character_id: inserted_character.id as i32, - level: default_stats.level.level() as i32, - exp: default_stats.exp.current() as i32, - endurance: default_stats.endurance as i32, - fitness: default_stats.fitness as i32, - willpower: default_stats.willpower as i32, - skills: SkillSetData(default_stats.skill_set), - }; + // Insert body record + let new_body = Body { + body_id: character_id, + body_data: convert_body_to_database_json(&body)?, + variant: "humanoid".to_string(), + }; - diesel::insert_into(stats::table) - .values(&new_stats) - .execute(&connection)?; + let body_count = diesel::insert_into(body::table) + .values(&new_body) + .execute(&*connection)?; - // Default inventory - let inventory = - Inventory::from((inserted_character.id, comp::Inventory::default())); + if body_count != 1 { + return Err(Error::OtherError(format!( + "Error inserting into body table for char_id {}", + character_id + ))); + } - diesel::insert_into(inventory::table) - .values(&inventory) - .execute(&connection)?; + // Insert character record + let new_character = NewCharacter { + character_id, + player_uuid: uuid, + alias: &character_alias, + }; + let character_count = diesel::insert_into(character::table) + .values(&new_character) + .execute(&*connection)?; - // Insert a loadout with defaults and the chosen active weapon - let loadout = LoadoutBuilder::new() - .defaults() - .active_item(LoadoutBuilder::default_item_config_from_str( - character_tool.as_deref(), - )) - .build(); + if character_count != 1 { + return Err(Error::OtherError(format!( + "Error inserting into character table for char_id {}", + character_id + ))); + } - let new_loadout = NewLoadout::from((inserted_character.id, &loadout)); + // Insert default inventory and loadout item records + let mut inserts = Vec::new(); - diesel::insert_into(loadout::table) - .values(&new_loadout) - .execute(&connection)?; - }, - _ => warn!("Creating non-humanoid characters is not supported."), - }; - - Ok(()) + get_new_entity_ids(connection, |mut next_id| { + let (inserts_, _deletes) = convert_items_to_database_items( + &loadout, + loadout_container_id, + &inventory, + inventory_container_id, + &mut next_id, + ); + inserts = inserts_; + next_id })?; - load_character_list(uuid, db_dir) + let expected_inserted_count = inserts.len(); + let inserted_items = inserts + .into_iter() + .map(|item_pair| item_pair.model) + .collect::>(); + let inserted_count = diesel::insert_into(item) + .values(&inserted_items) + .execute(&*connection)?; + + if expected_inserted_count != inserted_count { + return Err(Error::OtherError(format!( + "Expected insertions={}, actual={}, for char_id {}--unsafe to continue transaction.", + expected_inserted_count, inserted_count, character_id + ))); + } + + load_character_list(uuid, connection) } /// Delete a character. Returns the updated character list. -fn delete_character(uuid: &str, character_id: i32, db_dir: &str) -> CharacterListResult { - use schema::character::dsl::*; +pub fn delete_character( + requesting_player_uuid: &str, + char_id: CharacterId, + connection: VelorenTransaction, +) -> CharacterListResult { + use schema::{body::dsl::*, character::dsl::*, stats::dsl::*}; - let connection = establish_connection(db_dir)?; - - if let Err(e) = connection.transaction::<_, diesel::result::Error, _>(|| { - diesel::delete( - character - .filter(id.eq(character_id)) - .filter(player_uuid.eq(uuid)), + // Load the character to delete - ensures that the requesting player + // owns the character + let _character_data = character + .filter( + schema::character::dsl::character_id + .eq(char_id) + .and(player_uuid.eq(requesting_player_uuid)), ) - .execute(&connection)?; + .first::(&*connection)?; - Ok(()) - }) { - error!(?e, "Error during stats batch update transaction"); + // Delete character + let character_count = diesel::delete( + character + .filter(schema::character::dsl::character_id.eq(char_id)) + .filter(player_uuid.eq(requesting_player_uuid)), + ) + .execute(&*connection)?; + + if character_count != 1 { + return Err(Error::OtherError(format!( + "Error deleting from character table for char_id {}", + char_id + ))); } - load_character_list(uuid, db_dir) + // Delete stats + let stats_count = diesel::delete(stats.filter(schema::stats::dsl::stats_id.eq(char_id))) + .execute(&*connection)?; + + if stats_count != 1 { + return Err(Error::OtherError(format!( + "Error deleting from stats table for char_id {}", + char_id + ))); + } + // Delete body + let body_count = diesel::delete(body.filter(schema::body::dsl::body_id.eq(char_id))) + .execute(&*connection)?; + + if body_count != 1 { + return Err(Error::OtherError(format!( + "Error deleting from body table for char_id {}", + char_id + ))); + } + + // Delete all items, recursively walking all containers starting from the + // "character" pseudo-container that is the root for all items owned by + // a character. + let item_count = diesel::sql_query(format!( + " + WITH RECURSIVE + parents AS ( + SELECT item_id + FROM item + WHERE item.item_id = {} -- Item with character id is the character pseudo-container + UNION ALL + SELECT item.item_id + FROM item, + parents + WHERE item.parent_container_item_id = parents.item_id + ) + DELETE + FROM item + WHERE EXISTS (SELECT 1 FROM parents WHERE parents.item_id = item.item_id)", + char_id + )) + .execute(&*connection)?; + + if item_count < 3 { + return Err(Error::OtherError(format!( + "Error deleting from item table for char_id {} (expected at least 3 deletions, found \ + {})", + char_id, item_count + ))); + } + + load_character_list(requesting_player_uuid, connection) } /// Before creating a character, we ensure that the limit on the number of /// characters has not been exceeded -fn check_character_limit(uuid: &str, db_dir: &str) -> Result<(), Error> { +pub fn check_character_limit(uuid: &str, connection: VelorenTransaction) -> Result<(), Error> { use diesel::dsl::count_star; use schema::character::dsl::*; let character_count = character .select(count_star()) .filter(player_uuid.eq(uuid)) - .load::(&establish_connection(db_dir)?)?; + .load::(&*connection)?; match character_count.first() { Some(count) => { @@ -454,139 +393,198 @@ fn check_character_limit(uuid: &str, db_dir: &str) -> Result<(), Error> { } } -type CharacterUpdateData = (StatsUpdate, InventoryUpdate, LoadoutUpdate); - -/// A unidirectional messaging resource for saving characters in a -/// background thread. +/// NOTE: This relies heavily on serializability to work correctly. /// -/// This is used to make updates to a character and their persisted components, -/// such as inventory, loadout, etc... -pub struct CharacterUpdater { - update_tx: Option>>, - handle: Option>, -} +/// The count function takes the starting entity id, and returns the desired +/// count of new entity IDs. +/// +/// These are then inserted into the entities table. +fn get_new_entity_ids( + conn: VelorenTransaction, + mut max: impl FnMut(i64) -> i64, +) -> Result, Error> { + use super::schema::entity::dsl::*; -impl CharacterUpdater { - pub fn new(db_dir: String) -> Self { - let (update_tx, update_rx) = channel::unbounded::>(); - let handle = std::thread::spawn(move || { - while let Ok(updates) = update_rx.recv() { - batch_update(updates.into_iter(), &db_dir); - } - }); - - Self { - update_tx: Some(update_tx), - handle: Some(handle), - } + #[derive(QueryableByName)] + struct NextEntityId { + #[sql_type = "BigInt"] + entity_id: i64, } - /// Updates a collection of characters based on their id and components - pub fn batch_update<'a>( - &self, - updates: impl Iterator, - ) { - let updates = updates - .map(|(id, stats, inventory, loadout)| { - ( - id, - ( - StatsUpdate::from(stats), - InventoryUpdate::from(inventory), - LoadoutUpdate::from((id, loadout)), - ), - ) - }) - .collect(); - - if let Err(e) = self.update_tx.as_ref().unwrap().send(updates) { - error!(?e, "Could not send stats updates"); - } - } - - /// Updates a single character based on their id and components - pub fn update( - &self, - character_id: i32, - stats: &comp::Stats, - inventory: &comp::Inventory, - loadout: &comp::Loadout, - ) { - self.batch_update(std::iter::once((character_id, stats, inventory, loadout))); - } -} - -fn batch_update(updates: impl Iterator, db_dir: &str) { - let connection = establish_connection(db_dir); - - if let Err(e) = connection.and_then(|connection| { - connection.transaction::<_, diesel::result::Error, _>(|| { - updates.for_each( - |(character_id, (stats_update, inventory_update, loadout_update))| { - update( - character_id, - &stats_update, - &inventory_update, - &loadout_update, - &connection, - ) - }, - ); - - Ok(()) - }) - }) { - error!(?e, "Error during stats batch update transaction"); - } -} - -/// NOTE: Only call while a transaction is held! -fn update( - character_id: i32, - stats: &StatsUpdate, - inventory: &InventoryUpdate, - loadout: &LoadoutUpdate, - connection: &SqliteConnection, -) { - // Update Stats - if let Err(e) = - diesel::update(schema::stats::table.filter(schema::stats::character_id.eq(character_id))) - .set(stats) - .execute(connection) - { - error!(?e, ?character_id, "Failed to update stats for character",) - } - - // Update Inventory - if let Err(e) = diesel::update( - schema::inventory::table.filter(schema::inventory::character_id.eq(character_id)), + // The sqlite_sequence table is used here to avoid reusing entity IDs for + // deleted entities. This table always contains the highest used ID for each + // AUTOINCREMENT column in a SQLite database. + let next_entity_id = sql_query( + " + SELECT seq + 1 AS entity_id + FROM sqlite_sequence + WHERE name = 'entity'", ) - .set(inventory) - .execute(connection) - { - warn!( - ?e, - ?character_id, - "Failed to update inventory for character", + .load::(&*conn)? + .pop() + .ok_or_else(|| Error::OtherError("No rows returned for sqlite_sequence query ".to_string()))? + .entity_id; + + let max_entity_id = max(next_entity_id); + + // Create a new range of IDs and insert them into the entity table + let new_ids: Range = next_entity_id..max_entity_id; + + let new_entities: Vec = new_ids.clone().map(|x| Entity { entity_id: x }).collect(); + + let actual_count = diesel::insert_into(entity) + .values(&new_entities) + .execute(&*conn)?; + + if actual_count != new_entities.len() { + return Err(Error::OtherError(format!( + "Error updating entity table: expected to add the range {:?}) to entities, but actual \ + insertions={}", + new_ids, actual_count + ))); + } + + debug!( + "Created {} new persistence entity_ids: {}", + new_ids.end - new_ids.start, + new_ids + .clone() + .map(|x| x.to_string()) + .collect::>() + .join(", ") + ); + Ok(new_ids) +} + +/// Fetches the pseudo_container IDs for a character +fn get_pseudo_containers( + connection: VelorenTransaction, + character_id: CharacterId, +) -> Result { + let character_containers = CharacterContainers { + loadout_container_id: get_pseudo_container_id( + connection, + character_id, + LOADOUT_PSEUDO_CONTAINER_POSITION, + )?, + inventory_container_id: get_pseudo_container_id( + connection, + character_id, + INVENTORY_PSEUDO_CONTAINER_POSITION, + )?, + }; + + Ok(character_containers) +} + +fn get_pseudo_container_id( + connection: VelorenTransaction, + character_id: CharacterId, + pseudo_container_position: &str, +) -> Result { + use super::schema::item::dsl::*; + match item + .select(item_id) + .filter( + parent_container_item_id + .eq(character_id) + .and(position.eq(pseudo_container_position)), ) - } - - // Update Loadout - if let Err(e) = diesel::update( - schema::loadout::table.filter(schema::loadout::character_id.eq(character_id)), - ) - .set(loadout) - .execute(connection) + .first::(&*connection) { - warn!(?e, ?character_id, "Failed to update loadout for character",) + Ok(id) => Ok(id), + Err(e) => { + error!( + ?e, + ?character_id, + ?pseudo_container_position, + "Failed to retrieve pseudo container ID" + ); + Err(DatabaseError(e)) + }, } } -impl Drop for CharacterUpdater { - fn drop(&mut self) { - drop(self.update_tx.take()); - if let Err(e) = self.handle.take().unwrap().join() { - error!(?e, "Error from joining character update thread"); +pub fn update( + char_id: CharacterId, + char_stats: comp::Stats, + inventory: comp::Inventory, + loadout: comp::Loadout, + connection: VelorenTransaction, +) -> Result>, Error> { + use super::schema::{item::dsl::*, stats::dsl::*}; + + let pseudo_containers = get_pseudo_containers(connection, char_id)?; + + let mut upserts = Vec::new(); + + // First, get all the entity IDs for any new items, and identify which slots to + // upsert and which ones to delete. + get_new_entity_ids(connection, |mut next_id| { + let (upserts_, _deletes) = convert_items_to_database_items( + &loadout, + pseudo_containers.loadout_container_id, + &inventory, + pseudo_containers.inventory_container_id, + &mut next_id, + ); + upserts = upserts_; + next_id + })?; + + // Next, delete any slots we aren't upserting. + debug!("Deleting items for character_id {}", char_id); + let existing_items = parent_container_item_id + .eq(pseudo_containers.inventory_container_id) + .or(parent_container_item_id.eq(pseudo_containers.loadout_container_id)); + let non_upserted_items = item_id.ne_all( + upserts + .iter() + .map(|item_pair| item_pair.model.item_id) + .collect::>(), + ); + + let delete_count = diesel::delete(item.filter(existing_items.and(non_upserted_items))) + .execute(&*connection)?; + debug!("Deleted {} items", delete_count); + + // Upsert items + let expected_upsert_count = upserts.len(); + let mut upserted_comps = Vec::new(); + if expected_upsert_count > 0 { + let (upserted_items, upserted_comps_): (Vec<_>, Vec<_>) = upserts + .into_iter() + .map(|model_pair| (model_pair.model, model_pair.comp)) + .unzip(); + upserted_comps = upserted_comps_; + debug!( + "Upserting items {:?} for character_id {}", + upserted_items, char_id + ); + + let upsert_count = diesel::replace_into(item) + .values(&upserted_items) + .execute(&*connection)?; + if upsert_count != expected_upsert_count { + return Err(Error::OtherError(format!( + "Expected upsertions={}, actual={}, for char_id {}--unsafe to continue \ + transaction.", + expected_upsert_count, upsert_count, char_id + ))); } } + + let db_stats = convert_stats_to_database(char_id, &char_stats); + let stats_count = diesel::update(stats.filter(stats_id.eq(char_id))) + .set(db_stats) + .execute(&*connection)?; + + if stats_count != 1 { + return Err(Error::OtherError(format!( + "Error updating stats table for char_id {}", + char_id + ))); + } + + Ok(upserted_comps) } diff --git a/server/src/persistence/character/conversions.rs b/server/src/persistence/character/conversions.rs new file mode 100644 index 0000000000..310a3a79b6 --- /dev/null +++ b/server/src/persistence/character/conversions.rs @@ -0,0 +1,329 @@ +use crate::persistence::{ + character::EntityId, + models::{Body, Character, Item, Stats}, +}; + +use crate::persistence::{error::Error, json_models::HumanoidBody}; +use common::{ + character::CharacterId, + comp::{Body as CompBody, *}, + loadout_builder, +}; +use core::{convert::TryFrom, num::NonZeroU64}; +use itertools::{Either, Itertools}; +use std::sync::Arc; + +pub struct ItemModelPair { + pub comp: Arc, + pub model: Item, +} + +/// The left vector contains all item rows to upsert; the right-hand vector +/// contains all item rows to delete (by parent ID and position). +pub fn convert_items_to_database_items( + loadout: &Loadout, + loadout_container_id: EntityId, + inventory: &Inventory, + inventory_container_id: EntityId, + next_id: &mut i64, +) -> (Vec, Vec<(EntityId, String)>) { + // Loadout slots. + let loadout = [ + ("active_item", loadout.active_item.as_ref().map(|x| &x.item)), + ("second_item", loadout.second_item.as_ref().map(|x| &x.item)), + ("lantern", loadout.lantern.as_ref()), + ("shoulder", loadout.shoulder.as_ref()), + ("chest", loadout.chest.as_ref()), + ("belt", loadout.belt.as_ref()), + ("hand", loadout.hand.as_ref()), + ("pants", loadout.pants.as_ref()), + ("foot", loadout.foot.as_ref()), + ("back", loadout.back.as_ref()), + ("ring", loadout.ring.as_ref()), + ("neck", loadout.neck.as_ref()), + ("head", loadout.head.as_ref()), + ("tabard", loadout.tabard.as_ref()), + ]; + + let loadout = loadout + .iter() + .map(|&(slot, item)| (slot.to_string(), item, loadout_container_id)); + + // Inventory slots. + let inventory = inventory + .slots() + .iter() + .enumerate() + .map(|(slot, item)| (slot.to_string(), item.as_ref(), inventory_container_id)); + + // Construct new items. + inventory.chain(loadout) + .partition_map(|(position, item, parent_container_item_id)| { + if let Some(item) = item { + // Try using the next available id in the sequence as the default for new items. + let new_item_id = NonZeroU64::new(u64::try_from(*next_id) + .expect("We are willing to crash if the next entity id overflows \ + (or is otherwise negative).")).expect("next_id should not be zero, either"); + + let comp = item.get_item_id_for_database(); + Either::Left(ItemModelPair { + model: Item { + item_definition_id: item.item_definition_id().to_owned(), + position, + parent_container_item_id, + // Fast (kinda) path: acquire read for the common case where an id has + // already been assigned. + item_id: comp.load() + // First, we filter out "impossible" entity IDs--IDs that are larger + // than the maximum sequence value (next_id). This is important + // because we update the item ID atomically, *before* we know whether + // this transaction has completed successfully, and we don't abort the + // process on a failed transaction. In such cases, new IDs from + // aborted transactions will show up as having a higher value than the + // current max sequence number. Because the only place that modifies + // the item_id through a shared reference is (supposed to be) this + // function, which is part of the batch update transaction, we can + // assume that any rollback during the update would fail to insert + // *any* new items for the current character; this means that any items + // inserted between the failure and now (i.e. values less than next_id) + // would either not be items at all, or items belonging to other + // characters, leading to an easily detectable SQLite failure that we + // can use to atomically set the id back to None (if it was still the + // same bad value). + // + // Note that this logic only requires that all the character's items be + // updated within the same serializable transaction; the argument does + // not depend on SQLite-specific details (like locking) or on the fact + // that a user's transactions are always serialized on their own + // session. Also note that since these IDs are in-memory, we don't + // have to worry about their values during, e.g., a process crash; + // serializability will take care of us in those cases. Finally, note + // that while we have not yet implemented the "liveness" part of the + // algorithm (resetting ids back to None if we detect errors), this is + // not needed for soundness, and this part can be deferred until we + // switch to an execution model where such races are actually possible + // during normal gameplay. + .and_then(|item_id| Some(if item_id >= new_item_id { + // Try to atomically exchange with our own, "correct" next id. + match comp.compare_exchange(Some(item_id), Some(new_item_id)) { + Ok(_) => { + let item_id = *next_id; + // We won the race, use next_id and increment it. + *next_id += 1; + item_id + }, + Err(item_id) => { + // We raced with someone, and they won the race, so we know + // this transaction must abort unless they finish first. So, + // just assume they will finish first, and use their assigned + // item_id. + EntityId::try_from(item_id?.get()) + .expect("We always choose legal EntityIds as item ids") + }, + } + } else { EntityId::try_from(item_id.get()).expect("We always choose legal EntityIds as item ids") })) + // Finally, we're in the case where no entity was assigned yet (either + // ever, or due to corrections after a rollback). This proceeds + // identically to the "impossible ID" case. + .unwrap_or_else(|| { + // Try to atomically compare with the empty id. + match comp.compare_exchange(None, Some(new_item_id)) { + Ok(_) => { + let item_id = *next_id; + *next_id += 1; + item_id + }, + Err(item_id) => { + EntityId::try_from(item_id.expect("TODO: Fix handling of reset to None when we have concurrent writers.").get()) + .expect("We always choose legal EntityIds as item ids") + }, + } + }), + stack_size: if item.is_stackable() { + item.amount() as i32 + } else { + 1 + }, + }, + // Continue to remember the atomic, in case we detect an error later and want + // to roll back to preserve liveness. + comp, + }) + } else { + Either::Right((parent_container_item_id, position)) + } + }) +} + +pub fn convert_body_to_database_json(body: &CompBody) -> Result { + let json_model = match body { + common::comp::Body::Humanoid(humanoid_body) => HumanoidBody::from(humanoid_body), + _ => unimplemented!("Only humanoid bodies are currently supported for persistence"), + }; + + serde_json::to_string(&json_model).map_err(Error::SerializationError) +} + +pub fn convert_stats_to_database(character_id: CharacterId, stats: &common::comp::Stats) -> Stats { + Stats { + stats_id: character_id, + level: stats.level.level() as i32, + exp: stats.exp.current() as i32, + endurance: stats.endurance as i32, + fitness: stats.fitness as i32, + willpower: stats.willpower as i32, + } +} + +pub fn convert_inventory_from_database_items(database_items: &[Item]) -> Result { + let mut inventory = Inventory::new_empty(); + for db_item in database_items.iter() { + let mut item = common::comp::Item::new_from_asset(db_item.item_definition_id.as_str())?; + + // NOTE: Since this is freshly loaded, the atomic is *unique.* + let comp = item.get_item_id_for_database(); + + // Item ID + comp.store(Some(NonZeroU64::try_from(db_item.item_id as u64).map_err( + |_| Error::ConversionError("Item with zero item_id".to_owned()), + )?)); + + // Stack Size + if db_item.stack_size == 1 || item.is_stackable() { + item.set_amount(u32::try_from(db_item.stack_size).map_err(|_| { + Error::ConversionError(format!( + "Invalid item stack size for stackable={}: {}", + item.is_stackable(), + &db_item.stack_size + )) + })?) + .map_err(|_| Error::ConversionError("Error setting amount for item".to_owned()))?; + } + + // Insert item into inventory + + // Slot position + let slot = &db_item.position.parse::().map_err(|_| { + Error::ConversionError(format!( + "Failed to parse item position: {}", + &db_item.position + )) + })?; + + let insert_res = inventory.insert(*slot, item).map_err(|_| { + // If this happens there were too many items in the database for the current + // inventory size + Error::ConversionError("Error inserting item into inventory".to_string()) + })?; + + if insert_res.is_some() { + // If inventory.insert returns an item, it means it was swapped for an item that + // already occupied the slot. Multiple items being stored in the database for + // the same slot is an error. + return Err(Error::ConversionError( + "Inserted an item into the same slot twice".to_string(), + )); + } + } + + Ok(inventory) +} + +pub fn convert_loadout_from_database_items(database_items: &[Item]) -> Result { + let mut loadout = loadout_builder::LoadoutBuilder::new(); + for db_item in database_items.iter() { + let item = common::comp::Item::new_from_asset(db_item.item_definition_id.as_str())?; + // NOTE: item id is currently *unique*, so we can store the ID safely. + let comp = item.get_item_id_for_database(); + comp.store(Some(NonZeroU64::try_from(db_item.item_id as u64).map_err( + |_| Error::ConversionError("Item with zero item_id".to_owned()), + )?)); + + match db_item.position.as_str() { + "active_item" => loadout = loadout.active_item(Some(ItemConfig::from(item))), + "second_item" => loadout = loadout.second_item(Some(ItemConfig::from(item))), + "lantern" => loadout = loadout.lantern(Some(item)), + "shoulder" => loadout = loadout.shoulder(Some(item)), + "chest" => loadout = loadout.chest(Some(item)), + "belt" => loadout = loadout.belt(Some(item)), + "hand" => loadout = loadout.hand(Some(item)), + "pants" => loadout = loadout.pants(Some(item)), + "foot" => loadout = loadout.foot(Some(item)), + "back" => loadout = loadout.back(Some(item)), + "ring" => loadout = loadout.ring(Some(item)), + "neck" => loadout = loadout.neck(Some(item)), + "head" => loadout = loadout.head(Some(item)), + "tabard" => loadout = loadout.tabard(Some(item)), + _ => { + return Err(Error::ConversionError(format!( + "Unknown loadout position on item: {}", + db_item.position.as_str() + ))); + }, + } + } + + Ok(loadout.build()) +} + +pub fn convert_body_from_database(body: &Body) -> Result { + Ok(match body.variant.as_str() { + "humanoid" => { + let json_model = serde_json::de::from_str::(&body.body_data)?; + CompBody::Humanoid(common::comp::humanoid::Body { + species: common::comp::humanoid::ALL_SPECIES + .get(json_model.species as usize) + .ok_or_else(|| { + Error::ConversionError(format!("Missing species: {}", json_model.species)) + })? + .to_owned(), + body_type: common::comp::humanoid::ALL_BODY_TYPES + .get(json_model.body_type as usize) + .ok_or_else(|| { + Error::ConversionError(format!( + "Missing body_type: {}", + json_model.body_type + )) + })? + .to_owned(), + hair_style: json_model.hair_style, + beard: json_model.beard, + eyes: json_model.eyes, + accessory: json_model.accessory, + hair_color: json_model.hair_color, + skin: json_model.skin, + eye_color: json_model.eye_color, + }) + }, + _ => { + return Err(Error::ConversionError( + "Only humanoid bodies are supported for characters".to_string(), + )); + }, + }) +} + +pub fn convert_character_from_database(character: &Character) -> common::character::Character { + common::character::Character { + id: Some(character.character_id), + alias: String::from(&character.alias), + } +} + +pub fn convert_stats_from_database(stats: &Stats, alias: String) -> common::comp::Stats { + let mut new_stats = common::comp::Stats::empty(); + new_stats.name = alias; + new_stats.level.set_level(stats.level as u32); + new_stats.exp.update_maximum(stats.level as u32); + new_stats.exp.set_current(stats.exp as u32); + new_stats.update_max_hp(new_stats.body_type); + new_stats.health.set_to( + new_stats.health.maximum(), + common::comp::HealthSource::Revive, + ); + new_stats.endurance = stats.endurance as u32; + new_stats.fitness = stats.fitness as u32; + new_stats.willpower = stats.willpower as u32; + + new_stats +} diff --git a/server/src/persistence/character_loader.rs b/server/src/persistence/character_loader.rs new file mode 100644 index 0000000000..cc34b65daa --- /dev/null +++ b/server/src/persistence/character_loader.rs @@ -0,0 +1,213 @@ +use crate::persistence::{ + character::{create_character, delete_character, load_character_data, load_character_list}, + error::Error, + establish_connection, PersistedComponents, +}; +use common::character::{CharacterId, CharacterItem}; +use crossbeam::{channel, channel::TryIter}; +use tracing::error; + +pub(crate) type CharacterListResult = Result, Error>; +pub(crate) type CharacterDataResult = Result; +type CharacterLoaderRequest = (specs::Entity, CharacterLoaderRequestKind); + +/// Available database operations when modifying a player's character list +enum CharacterLoaderRequestKind { + CreateCharacter { + player_uuid: String, + character_alias: String, + persisted_components: PersistedComponents, + }, + DeleteCharacter { + player_uuid: String, + character_id: CharacterId, + }, + LoadCharacterList { + player_uuid: String, + }, + LoadCharacterData { + player_uuid: String, + character_id: CharacterId, + }, +} + +/// Wrapper for results for character actions. Can be a list of +/// characters, or component data belonging to an individual character +#[derive(Debug)] +pub enum CharacterLoaderResponseType { + CharacterList(CharacterListResult), + CharacterData(Box), +} + +/// Common message format dispatched in response to an update request +#[derive(Debug)] +pub struct CharacterLoaderResponse { + pub entity: specs::Entity, + pub result: CharacterLoaderResponseType, +} + +/// A bi-directional messaging resource for making requests to modify or load +/// character data in a background thread. +/// +/// This is used on the character selection screen, and after character +/// selection when loading the components associated with a character. +/// +/// Requests messages are sent in the form of +/// [`CharacterLoaderRequestKind`] and are dispatched at the character select +/// screen. +/// +/// Responses are polled on each server tick in the format +/// [`CharacterLoaderResponse`] +pub struct CharacterLoader { + update_rx: Option>, + update_tx: Option>, + handle: Option>, +} + +impl CharacterLoader { + pub fn new(db_dir: String) -> diesel::QueryResult { + let (update_tx, internal_rx) = channel::unbounded::(); + let (internal_tx, update_rx) = channel::unbounded::(); + + let mut conn = establish_connection(&db_dir)?; + + let handle = std::thread::spawn(move || { + while let Ok(request) = internal_rx.recv() { + let (entity, kind) = request; + + if let Err(e) = internal_tx.send(CharacterLoaderResponse { + entity, + result: match kind { + CharacterLoaderRequestKind::CreateCharacter { + player_uuid, + character_alias, + persisted_components, + } => CharacterLoaderResponseType::CharacterList(conn.transaction(|txn| { + create_character( + &player_uuid, + &character_alias, + persisted_components, + txn, + ) + })), + CharacterLoaderRequestKind::DeleteCharacter { + player_uuid, + character_id, + } => { + CharacterLoaderResponseType::CharacterList(conn.transaction(|txn| { + delete_character(&player_uuid, character_id, txn) + })) + }, + CharacterLoaderRequestKind::LoadCharacterList { player_uuid } => { + CharacterLoaderResponseType::CharacterList( + conn.transaction(|txn| load_character_list(&player_uuid, txn)), + ) + }, + CharacterLoaderRequestKind::LoadCharacterData { + player_uuid, + character_id, + } => { + CharacterLoaderResponseType::CharacterData(Box::new(conn.transaction( + |txn| load_character_data(player_uuid, character_id, txn), + ))) + }, + }, + }) { + error!(?e, "Could not send send persistence request"); + } + } + }); + + Ok(Self { + update_tx: Some(update_tx), + update_rx: Some(update_rx), + handle: Some(handle), + }) + } + + /// Create a new character belonging to the player identified by + /// `player_uuid` + pub fn create_character( + &self, + entity: specs::Entity, + player_uuid: String, + character_alias: String, + persisted_components: PersistedComponents, + ) { + if let Err(e) = self.update_tx.as_ref().unwrap().send(( + entity, + CharacterLoaderRequestKind::CreateCharacter { + player_uuid, + character_alias, + persisted_components, + }, + )) { + error!(?e, "Could not send character creation request"); + } + } + + /// Delete a character by `id` and `player_uuid` + pub fn delete_character( + &self, + entity: specs::Entity, + player_uuid: String, + character_id: CharacterId, + ) { + if let Err(e) = self.update_tx.as_ref().unwrap().send(( + entity, + CharacterLoaderRequestKind::DeleteCharacter { + player_uuid, + character_id, + }, + )) { + error!(?e, "Could not send character deletion request"); + } + } + + /// Loads a list of characters belonging to the player identified by + /// `player_uuid` + pub fn load_character_list(&self, entity: specs::Entity, player_uuid: String) { + if let Err(e) = self + .update_tx + .as_ref() + .unwrap() + .send((entity, CharacterLoaderRequestKind::LoadCharacterList { + player_uuid, + })) + { + error!(?e, "Could not send character list load request"); + } + } + + /// Loads components associated with a character + pub fn load_character_data( + &self, + entity: specs::Entity, + player_uuid: String, + character_id: CharacterId, + ) { + if let Err(e) = self.update_tx.as_ref().unwrap().send(( + entity, + CharacterLoaderRequestKind::LoadCharacterData { + player_uuid, + character_id, + }, + )) { + error!(?e, "Could not send character data load request"); + } + } + + /// Returns a non-blocking iterator over CharacterLoaderResponse messages + pub fn messages(&self) -> TryIter { + self.update_rx.as_ref().unwrap().try_iter() + } +} + +impl Drop for CharacterLoader { + fn drop(&mut self) { + drop(self.update_tx.take()); + if let Err(e) = self.handle.take().unwrap().join() { + error!(?e, "Error from joining character loader thread"); + } + } +} diff --git a/server/src/persistence/character_updater.rs b/server/src/persistence/character_updater.rs new file mode 100644 index 0000000000..97dd4fcac5 --- /dev/null +++ b/server/src/persistence/character_updater.rs @@ -0,0 +1,113 @@ +use crate::comp; +use common::{character::CharacterId, comp::item::ItemId}; + +use crate::persistence::{establish_connection, VelorenConnection}; +use crossbeam::channel; +use std::sync::Arc; +use tracing::{debug, error}; + +pub type CharacterUpdateData = (comp::Stats, comp::Inventory, comp::Loadout); + +/// A unidirectional messaging resource for saving characters in a +/// background thread. +/// +/// This is used to make updates to a character and their persisted components, +/// such as inventory, loadout, etc... +pub struct CharacterUpdater { + update_tx: Option>>, + handle: Option>, +} + +impl CharacterUpdater { + pub fn new(db_dir: String) -> diesel::QueryResult { + let (update_tx, update_rx) = + channel::unbounded::>(); + + let mut conn = establish_connection(&db_dir)?; + + let handle = std::thread::spawn(move || { + while let Ok(updates) = update_rx.recv() { + debug!("Persistence batch update starting"); + execute_batch_update(updates, &mut conn); + debug!("Persistence batch update finished"); + } + }); + + Ok(Self { + update_tx: Some(update_tx), + handle: Some(handle), + }) + } + + /// Updates a collection of characters based on their id and components + pub fn batch_update<'a>( + &self, + updates: impl Iterator< + Item = ( + CharacterId, + &'a comp::Stats, + &'a comp::Inventory, + &'a comp::Loadout, + ), + >, + ) { + let updates = updates + .map(|(character_id, stats, inventory, loadout)| { + ( + character_id, + (stats.clone(), inventory.clone(), loadout.clone()), + ) + }) + .collect::>(); + + if let Err(e) = self.update_tx.as_ref().unwrap().send(updates) { + error!(?e, "Could not send stats updates"); + } + } + + /// Updates a single character based on their id and components + pub fn update( + &self, + character_id: CharacterId, + stats: &comp::Stats, + inventory: &comp::Inventory, + loadout: &comp::Loadout, + ) { + self.batch_update(std::iter::once((character_id, stats, inventory, loadout))); + } +} + +fn execute_batch_update( + updates: Vec<(CharacterId, CharacterUpdateData)>, + connection: &mut VelorenConnection, +) { + let mut inserted_items = Vec::>::new(); + + if let Err(e) = connection.transaction::<_, super::error::Error, _>(|txn| { + for (character_id, (stats, inventory, loadout)) in updates { + inserted_items.append(&mut super::character::update( + character_id, + stats, + inventory, + loadout, + txn, + )?); + } + + Ok(()) + }) { + error!(?e, "Error during character batch update transaction"); + } + + // NOTE: On success, updating thee atomics is already taken care of + // internally. +} + +impl Drop for CharacterUpdater { + fn drop(&mut self) { + drop(self.update_tx.take()); + if let Err(e) = self.handle.take().unwrap().join() { + error!(?e, "Error from joining character update thread"); + } + } +} diff --git a/server/src/persistence/error.rs b/server/src/persistence/error.rs index 2fd97900c6..8cffd45c8d 100644 --- a/server/src/persistence/error.rs +++ b/server/src/persistence/error.rs @@ -2,10 +2,13 @@ extern crate diesel; +use common::assets::Error as AssetsError; use std::fmt; -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub enum Error { + // An invalid asset was returned from the database + AssetError(String), // The player has already reached the max character limit CharacterLimitReached, // An error occurred while establish a db connection @@ -16,20 +19,31 @@ pub enum Error { DatabaseError(diesel::result::Error), // Unable to load body or stats for a character CharacterDataError, + SerializationError(serde_json::Error), + ConversionError(String), + OtherError(String), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", match self { + Self::AssetError(error) => error.to_string(), Self::CharacterLimitReached => String::from("Character limit exceeded"), Self::DatabaseError(error) => error.to_string(), Self::DatabaseConnectionError(error) => error.to_string(), Self::DatabaseMigrationError(error) => error.to_string(), Self::CharacterDataError => String::from("Error while loading character data"), + Self::SerializationError(error) => error.to_string(), + Self::ConversionError(error) => error.to_string(), + Self::OtherError(error) => error.to_string(), }) } } +impl From for Error { + fn from(error: AssetsError) -> Error { Error::AssetError(error.to_string()) } +} + impl From for Error { fn from(error: diesel::result::Error) -> Error { Error::DatabaseError(error) } } @@ -38,6 +52,10 @@ impl From for Error { fn from(error: diesel::ConnectionError) -> Error { Error::DatabaseConnectionError(error) } } +impl From for Error { + fn from(error: serde_json::Error) -> Error { Error::SerializationError(error) } +} + impl From for Error { fn from(error: diesel_migrations::RunMigrationsError) -> Error { Error::DatabaseMigrationError(error) diff --git a/server/src/persistence/json_models.rs b/server/src/persistence/json_models.rs new file mode 100644 index 0000000000..03f4fe4c59 --- /dev/null +++ b/server/src/persistence/json_models.rs @@ -0,0 +1,31 @@ +use common::comp; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +pub struct HumanoidBody { + pub species: u8, + pub body_type: u8, + pub hair_style: u8, + pub beard: u8, + pub eyes: u8, + pub accessory: u8, + pub hair_color: u8, + pub skin: u8, + pub eye_color: u8, +} + +impl From<&comp::humanoid::Body> for HumanoidBody { + fn from(body: &comp::humanoid::Body) -> Self { + HumanoidBody { + species: body.species as u8, + body_type: body.body_type as u8, + hair_style: body.hair_style, + beard: body.beard, + eyes: body.eyes, + accessory: body.accessory, + hair_color: body.hair_color, + skin: body.skin, + eye_color: body.eye_color, + } + } +} diff --git a/server/src/persistence/mod.rs b/server/src/persistence/mod.rs index 70afd430fe..82ba256dd8 100644 --- a/server/src/persistence/mod.rs +++ b/server/src/persistence/mod.rs @@ -6,19 +6,25 @@ //! - [`diesel-cli`](https://github.com/diesel-rs/diesel/tree/master/diesel_cli/) //! for generating and testing migrations -pub mod character; - +pub(in crate::persistence) mod character; +pub mod character_loader; +pub mod character_updater; mod error; +mod json_models; mod models; mod schema; extern crate diesel; +use common::comp; use diesel::{connection::SimpleConnection, prelude::*}; use diesel_migrations::embed_migrations; use std::{env, fs, path::PathBuf}; use tracing::{info, warn}; +/// A tuple of the components that are persisted to the DB for each character +pub type PersistedComponents = (comp::Body, comp::Stats, comp::Inventory, comp::Loadout); + // See: https://docs.rs/diesel_migrations/1.4.0/diesel_migrations/macro.embed_migrations.html // This macro is called at build-time, and produces the necessary migration info // for the `embedded_migrations` call below. @@ -44,15 +50,48 @@ pub fn run_migrations(db_dir: &str) -> Result<(), diesel_migrations::RunMigratio let _ = fs::create_dir(format!("{}/", db_dir)); embedded_migrations::run_with_output( - &establish_connection(db_dir).expect( - "If we cannot execute migrations, we should not be allowed to launch the server, so \ - we don't populate it with bad data.", - ), + &establish_connection(db_dir) + .expect( + "If we cannot execute migrations, we should not be allowed to launch the server, \ + so we don't populate it with bad data.", + ) + .0, &mut std::io::LineWriter::new(TracingOut), ) } -fn establish_connection(db_dir: &str) -> QueryResult { +/// A database connection blessed by Veloren. +pub struct VelorenConnection(SqliteConnection); + +/// A transaction blessed by Veloren. +#[derive(Clone, Copy)] +pub struct VelorenTransaction<'a>(&'a SqliteConnection); + +impl VelorenConnection { + /// Open a transaction in order to be able to run a set of queries against + /// the database. We require the use of a transaction, rather than + /// allowing direct session access, so that (1) we can control things + /// like the retry process (at a future date), and (2) to avoid + /// accidentally forgetting to open or reuse a transaction. + /// + /// We could make things even more foolproof, but we restrict ourselves to + /// this for now. + pub fn transaction(&mut self, f: F) -> Result + where + F: for<'a> FnOnce(VelorenTransaction<'a>) -> Result, + E: From, + { + self.0.transaction(|| f(VelorenTransaction(&self.0))) + } +} + +impl<'a> core::ops::Deref for VelorenTransaction<'a> { + type Target = SqliteConnection; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +pub fn establish_connection(db_dir: &str) -> QueryResult { let db_dir = &apply_saves_dir_override(db_dir); let database_url = format!("{}/db.sqlite", db_dir); @@ -61,23 +100,21 @@ fn establish_connection(db_dir: &str) -> QueryResult { // Use Write-Ahead-Logging for improved concurrency: https://sqlite.org/wal.html // Set a busy timeout (in ms): https://sqlite.org/c3ref/busy_timeout.html - if let Err(e) = connection.batch_execute( - " + connection + .batch_execute( + " PRAGMA foreign_keys = ON; PRAGMA journal_mode = WAL; - PRAGMA busy_timeout = 250; + PRAGMA busy_timeout = 250; ", - ) { - warn!( - ?e, + ) + .expect( "Failed adding PRAGMA statements while establishing sqlite connection, including \ enabling foreign key constraints. We will not allow connecting to the server under \ - these conditions." + these conditions.", ); - return Err(e); - } - Ok(connection) + Ok(VelorenConnection(connection)) } fn apply_saves_dir_override(db_dir: &str) -> String { diff --git a/server/src/persistence/models.rs b/server/src/persistence/models.rs index 1c59e5127f..fb594a3cd5 100644 --- a/server/src/persistence/models.rs +++ b/server/src/persistence/models.rs @@ -1,412 +1,58 @@ extern crate serde_json; -use super::schema::{body, character, inventory, loadout, stats}; -use crate::comp; -use common::character::Character as CharacterData; -use diesel::sql_types::Text; -use serde::{Deserialize, Serialize}; -use tracing::warn; +use super::schema::{body, character, entity, item, stats}; -/// The required elements to build comp::Stats from database data -pub struct StatsJoinData<'a> { - pub alias: &'a str, - pub body: &'a comp::Body, - pub stats: &'a Stats, -} - -/// `Character` represents a playable character belonging to a player -#[derive(Identifiable, Queryable, Debug)] -#[table_name = "character"] -pub struct Character { - pub id: i32, - pub player_uuid: String, - pub alias: String, - pub tool: Option, +#[derive(Debug, Insertable, PartialEq)] +#[table_name = "entity"] +pub struct Entity { + pub entity_id: i64, } #[derive(Insertable)] #[table_name = "character"] pub struct NewCharacter<'a> { + pub character_id: i64, pub player_uuid: &'a str, pub alias: &'a str, - pub tool: Option<&'a str>, } -impl From<&Character> for CharacterData { - fn from(character: &Character) -> CharacterData { - CharacterData { - id: Some(character.id), - alias: String::from(&character.alias), - tool: character.tool.clone(), - } - } -} - -/// `Body` represents the body variety for a character, which has a one-to-one -/// relationship with Characters. This data is set during player creation, and -/// while there is currently no in-game functionality to modify it, it will -/// likely be added in the future. -#[derive(Associations, Identifiable, Queryable, Debug, Insertable)] -#[belongs_to(Character)] +#[derive(Identifiable, Queryable, Debug)] #[primary_key(character_id)] -#[table_name = "body"] -pub struct Body { - pub character_id: i32, - pub species: i16, - pub body_type: i16, - pub hair_style: i16, - pub beard: i16, - pub eyes: i16, - pub accessory: i16, - pub hair_color: i16, - pub skin: i16, - pub eye_color: i16, +#[table_name = "character"] +pub struct Character { + pub character_id: i64, + pub player_uuid: String, + pub alias: String, } -impl From<&Body> for comp::Body { - fn from(body: &Body) -> comp::Body { - comp::Body::Humanoid(comp::humanoid::Body { - species: comp::humanoid::ALL_SPECIES[body.species as usize], - body_type: comp::humanoid::ALL_BODY_TYPES[body.body_type as usize], - hair_style: body.hair_style as u8, - beard: body.beard as u8, - eyes: body.eyes as u8, - accessory: body.accessory as u8, - hair_color: body.hair_color as u8, - skin: body.skin as u8, - eye_color: body.eye_color as u8, - }) - } +#[primary_key(item_id)] +#[table_name = "item"] +#[derive(Debug, Insertable, Queryable, AsChangeset)] +pub struct Item { + pub item_id: i64, + pub parent_container_item_id: i64, + pub item_definition_id: String, + pub stack_size: i32, + pub position: String, } -/// `Stats` represents the stats for a character, and have a one-to-one -/// relationship with `Character`. #[derive(Associations, AsChangeset, Identifiable, Queryable, Debug, Insertable)] -#[belongs_to(Character)] -#[primary_key(character_id)] +#[primary_key(stats_id)] #[table_name = "stats"] pub struct Stats { - pub character_id: i32, + pub stats_id: i64, pub level: i32, pub exp: i32, pub endurance: i32, pub fitness: i32, pub willpower: i32, - pub skills: SkillSetData, } -impl From> for comp::Stats { - fn from(data: StatsJoinData) -> comp::Stats { - let level = data.stats.level as u32; - - let mut base_stats = comp::Stats::new(String::from(data.alias), *data.body); - - base_stats.level.set_level(level); - base_stats.exp.update_maximum(level); - - base_stats.exp.set_current(data.stats.exp as u32); - - base_stats.update_max_hp(base_stats.body_type); - base_stats - .health - .set_to(base_stats.health.maximum(), comp::HealthSource::Revive); - - base_stats.endurance = data.stats.endurance as u32; - base_stats.fitness = data.stats.fitness as u32; - base_stats.willpower = data.stats.willpower as u32; - base_stats.skill_set = data.stats.skills.0.clone(); - base_stats - } -} - -#[derive(AsChangeset, Debug, PartialEq)] -#[primary_key(character_id)] -#[table_name = "stats"] -pub struct StatsUpdate { - pub level: i32, - pub exp: i32, - pub endurance: i32, - pub fitness: i32, - pub willpower: i32, - pub skills: SkillSetData, -} - -impl From<&comp::Stats> for StatsUpdate { - fn from(stats: &comp::Stats) -> StatsUpdate { - StatsUpdate { - level: stats.level.level() as i32, - exp: stats.exp.current() as i32, - endurance: stats.endurance as i32, - fitness: stats.fitness as i32, - willpower: stats.willpower as i32, - skills: SkillSetData(stats.skill_set.clone()), - } - } -} - -/// A wrapper type for the SkillSet of a character used to serialise to and from -/// JSON If the column contains malformed JSON, a default skillset is returned -#[derive(AsExpression, Debug, Deserialize, Serialize, PartialEq, FromSqlRow)] -#[sql_type = "Text"] -pub struct SkillSetData(pub comp::SkillSet); - -impl diesel::deserialize::FromSql for SkillSetData -where - DB: diesel::backend::Backend, - String: diesel::deserialize::FromSql, -{ - fn from_sql( - bytes: Option<&::RawValue>, - ) -> diesel::deserialize::Result { - let t = String::from_sql(bytes)?; - - match serde_json::from_str(&t) { - Ok(data) => Ok(Self(data)), - Err(e) => { - warn!(?e, "Failed to deserialize skill set data"); - Ok(Self(comp::SkillSet::default())) - }, - } - } -} - -impl diesel::serialize::ToSql for SkillSetData -where - DB: diesel::backend::Backend, -{ - fn to_sql( - &self, - out: &mut diesel::serialize::Output, - ) -> diesel::serialize::Result { - let s = serde_json::to_string(&self.0)?; - >::to_sql(&s, out) - } -} - -/// Inventory storage and conversion. Inventories have a one-to-one relationship -/// with characters. -/// -/// We store inventory rows as a (character_id, json) tuples, where the json is -/// a serialised Inventory component. -#[derive(Associations, AsChangeset, Identifiable, Queryable, Debug, Insertable)] -#[belongs_to(Character)] -#[primary_key(character_id)] -#[table_name = "inventory"] -pub struct Inventory { - character_id: i32, - items: InventoryData, -} - -/// A wrapper type for Inventory components used to serialise to and from JSON -/// If the column contains malformed JSON, a default inventory is returned -#[derive(SqlType, AsExpression, Debug, Deserialize, Serialize, FromSqlRow, PartialEq)] -#[sql_type = "Text"] -pub struct InventoryData(comp::Inventory); - -impl diesel::deserialize::FromSql for InventoryData -where - DB: diesel::backend::Backend, - String: diesel::deserialize::FromSql, -{ - fn from_sql( - bytes: Option<&::RawValue>, - ) -> diesel::deserialize::Result { - let t = String::from_sql(bytes)?; - serde_json::from_str(&t).map_err(Box::from) - } -} - -impl diesel::serialize::ToSql for InventoryData -where - DB: diesel::backend::Backend, -{ - fn to_sql( - &self, - out: &mut diesel::serialize::Output, - ) -> diesel::serialize::Result { - let s = serde_json::to_string(&self.0)?; - >::to_sql(&s, out) - } -} - -impl From<(i32, comp::Inventory)> for Inventory { - fn from(data: (i32, comp::Inventory)) -> Inventory { - let (character_id, inventory) = data; - - Inventory { - character_id, - items: InventoryData(inventory), - } - } -} - -impl From for comp::Inventory { - fn from(inventory: Inventory) -> comp::Inventory { inventory.items.0 } -} - -#[derive(AsChangeset, Debug, PartialEq)] -#[primary_key(character_id)] -#[table_name = "inventory"] -pub struct InventoryUpdate { - pub items: InventoryData, -} - -impl From<&comp::Inventory> for InventoryUpdate { - fn from(inventory: &comp::Inventory) -> InventoryUpdate { - InventoryUpdate { - items: InventoryData(inventory.clone()), - } - } -} - -/// Loadout holds the armor and weapons owned by a character. This data is -/// seperate from the inventory. At the moment, characters have a single Loadout -/// which is loaded with their character data, however there are plans for each -/// character to have multiple Loadouts which they can switch between during -/// gameplay. Due to this Loadouts have a many to one relationship with -/// characters, and a distinct `id`. -#[derive(Associations, Queryable, Debug, Identifiable)] -#[belongs_to(Character)] -#[primary_key(id)] -#[table_name = "loadout"] -pub struct Loadout { - pub id: i32, - pub character_id: i32, - pub items: LoadoutData, -} - -/// A wrapper type for Loadout components used to serialise to and from JSON -/// If the column contains malformed JSON, a default loadout is returned, with -/// the starter sword set as the main weapon -#[derive(SqlType, AsExpression, Debug, Deserialize, Serialize, FromSqlRow, PartialEq)] -#[sql_type = "Text"] -pub struct LoadoutData(comp::Loadout); - -impl diesel::deserialize::FromSql for LoadoutData -where - DB: diesel::backend::Backend, - String: diesel::deserialize::FromSql, -{ - fn from_sql( - bytes: Option<&::RawValue>, - ) -> diesel::deserialize::Result { - let t = String::from_sql(bytes)?; - serde_json::from_str(&t).map_err(Box::from) - } -} - -impl diesel::serialize::ToSql for LoadoutData -where - DB: diesel::backend::Backend, -{ - fn to_sql( - &self, - out: &mut diesel::serialize::Output, - ) -> diesel::serialize::Result { - let s = serde_json::to_string(&self.0)?; - >::to_sql(&s, out) - } -} - -impl From<&Loadout> for comp::Loadout { - fn from(loadout: &Loadout) -> comp::Loadout { loadout.items.0.clone() } -} - -#[derive(Insertable, PartialEq, Debug)] -#[table_name = "loadout"] -pub struct NewLoadout { - pub character_id: i32, - pub items: LoadoutData, -} - -impl From<(i32, &comp::Loadout)> for NewLoadout { - fn from(data: (i32, &comp::Loadout)) -> NewLoadout { - let (character_id, loadout) = data; - - NewLoadout { - character_id, - items: LoadoutData(loadout.clone()), - } - } -} - -#[derive(Insertable, PartialEq, Debug, AsChangeset)] -#[table_name = "loadout"] -pub struct LoadoutUpdate { - pub character_id: i32, - pub items: LoadoutData, -} - -impl From<(i32, &comp::Loadout)> for LoadoutUpdate { - fn from(data: (i32, &comp::Loadout)) -> LoadoutUpdate { - let (character_id, loadout) = data; - - LoadoutUpdate { - character_id, - items: LoadoutData(loadout.clone()), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::comp; - - #[test] - fn stats_update_from_stats() { - let mut stats = comp::Stats::new( - String::from("Test"), - comp::Body::Humanoid(comp::humanoid::Body::random()), - ); - - stats.level.set_level(2); - stats.exp.set_current(20); - - stats.endurance = 2; - stats.fitness = 3; - stats.willpower = 4; - - assert_eq!(StatsUpdate::from(&stats), StatsUpdate { - level: 2, - exp: 20, - endurance: 2, - fitness: 3, - willpower: 4, - skills: SkillSetData(stats.skill_set) - }) - } - - #[test] - fn loads_stats_with_correct_level() { - let data = StatsJoinData { - alias: "test", - body: &comp::Body::from(&Body { - character_id: 0, - species: 0, - body_type: comp::humanoid::BodyType::Female as i16, - hair_style: 0, - beard: 0, - eyes: 0, - accessory: 0, - hair_color: 0, - skin: 0, - eye_color: 0, - }), - stats: &Stats { - character_id: 0, - level: 3, - exp: 70, - endurance: 0, - fitness: 2, - willpower: 3, - skills: SkillSetData(comp::SkillSet::new()), - }, - }; - - let stats = comp::Stats::from(data); - - assert_eq!(stats.level.level(), 3); - assert_eq!(stats.exp.current(), 70); - } +#[derive(Associations, Identifiable, Insertable, Queryable, Debug)] +#[primary_key(body_id)] +#[table_name = "body"] +pub struct Body { + pub body_id: i64, + pub variant: String, + pub body_data: String, } diff --git a/server/src/persistence/schema.rs b/server/src/persistence/schema.rs index 09ebfdc871..d13e22bf22 100644 --- a/server/src/persistence/schema.rs +++ b/server/src/persistence/schema.rs @@ -1,57 +1,47 @@ table! { - body (character_id) { - character_id -> Integer, - species -> SmallInt, - body_type -> SmallInt, - hair_style -> SmallInt, - beard -> SmallInt, - eyes -> SmallInt, - accessory -> SmallInt, - hair_color -> SmallInt, - skin -> SmallInt, - eye_color -> SmallInt, + body (body_id) { + body_id -> BigInt, + variant -> Text, + body_data -> Text, } } table! { - character (id) { - id -> Integer, + character (character_id) { + character_id -> BigInt, player_uuid -> Text, alias -> Text, - tool -> Nullable, } } table! { - inventory (character_id) { - character_id -> Integer, - items -> Text, + entity (entity_id) { + entity_id -> BigInt, } } table! { - loadout (id) { - id -> Integer, - character_id -> Integer, - items -> Text, + item (item_id) { + item_id -> BigInt, + parent_container_item_id -> BigInt, + item_definition_id -> Text, + stack_size -> Integer, + position -> Text, } } table! { - stats (character_id) { - character_id -> Integer, + stats (stats_id) { + stats_id -> BigInt, level -> Integer, exp -> Integer, endurance -> Integer, fitness -> Integer, willpower -> Integer, - skills -> Text, } } -joinable!(body -> character (character_id)); -joinable!(inventory -> character (character_id)); -joinable!(loadout -> character (character_id)); -joinable!(stats -> character (character_id)); +joinable!(character -> body (character_id)); +joinable!(character -> stats (character_id)); -allow_tables_to_appear_in_same_query!(body, character, inventory, loadout, stats); +allow_tables_to_appear_in_same_query!(body, character, entity, item, stats,); diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 6376c17444..97c3be95ec 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -1,8 +1,9 @@ use crate::{ - client::Client, persistence::character::PersistedComponents, settings::ServerSettings, + client::Client, persistence::PersistedComponents, settings::ServerSettings, sys::sentinel::DeletedEntities, SpawnPoint, }; use common::{ + character::CharacterId, comp, effect::Effect, msg::{CharacterInfo, ClientState, PlayerListUpdate, ServerMsg}, @@ -39,7 +40,7 @@ pub trait StateExt { projectile: comp::Projectile, ) -> EcsEntityBuilder; /// Insert common/default components for a new character joining the server - fn initialize_character_data(&mut self, entity: EcsEntity, character_id: i32); + fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId); /// Update the components associated with the entity's current character. /// Performed after loading component data from the database fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents); @@ -133,7 +134,7 @@ impl StateExt for State { .with(comp::Sticky) } - fn initialize_character_data(&mut self, entity: EcsEntity, character_id: i32) { + fn initialize_character_data(&mut self, entity: EcsEntity, character_id: CharacterId) { let spawn_point = self.ecs().read_resource::().0; self.write_component(entity, comp::Energy::new(1000)); diff --git a/server/src/sys/message.rs b/server/src/sys/message.rs index 8773c8aa2d..a88440b963 100644 --- a/server/src/sys/message.rs +++ b/server/src/sys/message.rs @@ -1,10 +1,11 @@ use super::SysTimer; use crate::{ alias_validator::AliasValidator, + character_creator, client::Client, login_provider::LoginProvider, metrics::{NetworkRequestMetrics, PlayerMetrics}, - persistence::character::CharacterLoader, + persistence::character_loader::CharacterLoader, ServerSettings, }; use common::{ @@ -380,12 +381,13 @@ impl Sys { debug!(?error, ?alias, "denied alias as it contained a banned word"); client.notify(ServerMsg::CharacterActionError(error.to_string())); } else if let Some(player) = players.get(entity) { - character_loader.create_character( + character_creator::create_character( entity, player.uuid().to_string(), alias, tool, body, + character_loader, ); } }, diff --git a/server/src/sys/persistence.rs b/server/src/sys/persistence.rs index c1dd0982b4..91bc9c4fe4 100644 --- a/server/src/sys/persistence.rs +++ b/server/src/sys/persistence.rs @@ -1,5 +1,5 @@ use crate::{ - persistence::character, + persistence::character_updater, sys::{SysScheduler, SysTimer}, }; use common::{ @@ -17,7 +17,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Stats>, ReadStorage<'a, Inventory>, ReadStorage<'a, Loadout>, - ReadExpect<'a, character::CharacterUpdater>, + ReadExpect<'a, character_updater::CharacterUpdater>, Write<'a, SysScheduler>, Write<'a, SysTimer>, ); diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index ace7d43814..8cec2c23cd 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -1,10 +1,9 @@ use super::SysTimer; use crate::{chunk_generator::ChunkGenerator, client::Client, Tick}; use common::{ - assets::Asset, comp::{ self, bird_medium, - item::{self, ItemAsset}, + item::{self}, Alignment, CharacterAbility, ItemConfig, Player, Pos, }, event::{EventBus, ServerEvent}, @@ -125,44 +124,45 @@ impl<'a> System<'a> for Sys { // let damage = stats.level.level() as i32; TODO: Make NPC base damage // non-linearly depend on their level - let active_item = - if let Some(item::ItemKind::Tool(tool)) = main_tool.as_ref().map(|i| &i.kind) { - let mut abilities = tool.get_abilities(); - let mut ability_drain = abilities.drain(..); + let active_item = if let Some(item::ItemKind::Tool(tool)) = + main_tool.as_ref().map(|i| i.kind()) + { + let mut abilities = tool.get_abilities(); + let mut ability_drain = abilities.drain(..); - main_tool.map(|item| comp::ItemConfig { - item, - ability1: ability_drain.next(), - ability2: ability_drain.next(), - ability3: ability_drain.next(), - block_ability: None, - dodge_ability: Some(comp::CharacterAbility::Roll), - }) - } else { - Some(ItemConfig { - // We need the empty item so npcs can attack - item: ItemAsset::load_expect_cloned("common.items.weapons.empty.empty"), - ability1: Some(CharacterAbility::BasicMelee { - energy_cost: 0, - buildup_duration: Duration::from_millis(0), - recover_duration: Duration::from_millis(400), - base_healthchange: -40, - range: 3.5, - max_angle: 15.0, - }), - ability2: None, - ability3: None, - block_ability: None, - dodge_ability: None, - }) - }; + main_tool.map(|item| comp::ItemConfig { + item, + ability1: ability_drain.next(), + ability2: ability_drain.next(), + ability3: ability_drain.next(), + block_ability: None, + dodge_ability: Some(comp::CharacterAbility::Roll), + }) + } else { + Some(ItemConfig { + // We need the empty item so npcs can attack + item: comp::Item::new_from_asset_expect("common.items.weapons.empty.empty"), + ability1: Some(CharacterAbility::BasicMelee { + energy_cost: 0, + buildup_duration: Duration::from_millis(0), + recover_duration: Duration::from_millis(400), + base_healthchange: -40, + range: 3.5, + max_angle: 15.0, + }), + ability2: None, + ability3: None, + block_ability: None, + dodge_ability: None, + }) + }; let mut loadout = match alignment { comp::Alignment::Npc => comp::Loadout { active_item, second_item: None, shoulder: None, - chest: Some(ItemAsset::load_expect_cloned( + chest: Some(comp::Item::new_from_asset_expect( match rand::thread_rng().gen_range(0, 10) { 0 => "common.items.npc_armor.chest.worker_green_0", 1 => "common.items.npc_armor.chest.worker_green_1", @@ -176,14 +176,14 @@ impl<'a> System<'a> for Sys { _ => "common.items.npc_armor.chest.worker_orange_1", }, )), - belt: Some(ItemAsset::load_expect_cloned( + belt: Some(comp::Item::new_from_asset_expect( "common.items.armor.belt.leather_0", )), hand: None, - pants: Some(ItemAsset::load_expect_cloned( + pants: Some(comp::Item::new_from_asset_expect( "common.items.armor.pants.worker_blue_0", )), - foot: Some(ItemAsset::load_expect_cloned( + foot: Some(comp::Item::new_from_asset_expect( match rand::thread_rng().gen_range(0, 2) { 0 => "common.items.armor.foot.leather_0", _ => "common.items.armor.starter.sandals_0", @@ -199,30 +199,30 @@ impl<'a> System<'a> for Sys { comp::Alignment::Enemy => comp::Loadout { active_item, second_item: None, - shoulder: Some(ItemAsset::load_expect_cloned( + shoulder: Some(comp::Item::new_from_asset_expect( "common.items.npc_armor.shoulder.cultist_shoulder_purple", )), - chest: Some(ItemAsset::load_expect_cloned( + chest: Some(comp::Item::new_from_asset_expect( "common.items.npc_armor.chest.cultist_chest_purple", )), - belt: Some(ItemAsset::load_expect_cloned( + belt: Some(comp::Item::new_from_asset_expect( "common.items.npc_armor.belt.cultist_belt", )), - hand: Some(ItemAsset::load_expect_cloned( + hand: Some(comp::Item::new_from_asset_expect( "common.items.npc_armor.hand.cultist_hands_purple", )), - pants: Some(ItemAsset::load_expect_cloned( + pants: Some(comp::Item::new_from_asset_expect( "common.items.npc_armor.pants.cultist_legs_purple", )), - foot: Some(ItemAsset::load_expect_cloned( + foot: Some(comp::Item::new_from_asset_expect( "common.items.npc_armor.foot.cultist_boots", )), - back: Some(ItemAsset::load_expect_cloned( + back: Some(comp::Item::new_from_asset_expect( "common.items.npc_armor.back.dungeon_purple-0", )), ring: None, neck: None, - lantern: Some(ItemAsset::load_expect_cloned( + lantern: Some(comp::Item::new_from_asset_expect( "common.items.lantern.black_0", )), head: None, @@ -267,7 +267,7 @@ impl<'a> System<'a> for Sys { } loadout = comp::Loadout { active_item: Some(comp::ItemConfig { - item: ItemAsset::load_expect_cloned( + item: comp::Item::new_from_asset_expect( "common.items.npc_weapons.sword.zweihander_sword_0", ), ability1: Some(CharacterAbility::BasicMelee { @@ -284,22 +284,22 @@ impl<'a> System<'a> for Sys { dodge_ability: None, }), second_item: None, - shoulder: Some(ItemAsset::load_expect_cloned( + shoulder: Some(comp::Item::new_from_asset_expect( "common.items.armor.shoulder.plate_0", )), - chest: Some(ItemAsset::load_expect_cloned( + chest: Some(comp::Item::new_from_asset_expect( "common.items.armor.chest.plate_green_0", )), - belt: Some(ItemAsset::load_expect_cloned( + belt: Some(comp::Item::new_from_asset_expect( "common.items.armor.belt.plate_0", )), - hand: Some(ItemAsset::load_expect_cloned( + hand: Some(comp::Item::new_from_asset_expect( "common.items.armor.hand.plate_0", )), - pants: Some(ItemAsset::load_expect_cloned( + pants: Some(comp::Item::new_from_asset_expect( "common.items.armor.pants.plate_green_0", )), - foot: Some(ItemAsset::load_expect_cloned( + foot: Some(comp::Item::new_from_asset_expect( "common.items.armor.foot.plate_0", )), back: None, diff --git a/tools/src/main.rs b/tools/src/main.rs index d16089f00d..37ee04f4c0 100644 --- a/tools/src/main.rs +++ b/tools/src/main.rs @@ -1,23 +1,16 @@ -use std::{ - error::Error, - ffi::OsString, - path::{Component, PathBuf}, -}; +use std::error::Error; use structopt::StructOpt; -use common::{ - assets::{self, Asset}, - comp, -}; +use common::comp; use comp::item::{ armor::{ArmorKind, Protection}, tool::ToolKind, - ItemAsset, + ItemKind, }; #[derive(StructOpt)] struct Cli { - /// Available arguments: "armor_stats", "weapon_stats" + /// Available arguments: "armor_stats", "weapon_stats", "all_items" function: String, } @@ -25,63 +18,20 @@ fn armor_stats() -> Result<(), Box> { let mut wtr = csv::Writer::from_path("armorstats.csv")?; wtr.write_record(&["Path", "Kind", "Name", "Protection"])?; - for folder in - assets::read_dir("common.items.armor").expect("Failed to iterate over armor folders!") + for item in comp::item::Item::new_from_asset_glob("common.items.armor.*") + .expect("Failed to iterate over item folders!") { - match folder { - Ok(folder) => { - let mut glob_folder = folder.path().display().to_string().replace("/", "."); - glob_folder.push_str(".*"); + match item.kind() { + comp::item::ItemKind::Armor(armor) => { + let protection = match armor.get_protection() { + Protection::Invincible => "Invincible".to_string(), + Protection::Normal(value) => value.to_string(), + }; + let kind = get_armor_kind(&armor.kind); - for file in std::fs::read_dir(folder.path())?.filter_map(|f| f.ok()) { - let asset_identifier = &file - .path() - .components() - .skip_while(|s| s != &Component::Normal(&OsString::from("common"))) - .inspect(|s| { - dbg!(&s); - }) - .collect::() - .with_extension("") - .display() - .to_string() - .replace("/", "."); - - let asset = ItemAsset::load_expect_cloned(asset_identifier); - - match &asset.kind { - comp::item::ItemKind::Armor(armor) => { - let protection = match armor.get_protection() { - Protection::Invincible => "Invincible".to_string(), - Protection::Normal(value) => value.to_string(), - }; - let kind = match armor.kind { - ArmorKind::Shoulder(_) => "Shoulder".to_string(), - ArmorKind::Chest(_) => "Chest".to_string(), - ArmorKind::Belt(_) => "Belt".to_string(), - ArmorKind::Hand(_) => "Hand".to_string(), - ArmorKind::Pants(_) => "Pants".to_string(), - ArmorKind::Foot(_) => "Foot".to_string(), - ArmorKind::Back(_) => "Back".to_string(), - ArmorKind::Ring(_) => "Ring".to_string(), - ArmorKind::Neck(_) => "Neck".to_string(), - ArmorKind::Head(_) => "Head".to_string(), - ArmorKind::Tabard(_) => "Tabard".to_string(), - }; - - wtr.write_record(&[ - asset_identifier, - &kind, - asset.name(), - &protection, - ])?; - }, - // Skip non-armor - _ => println!("Skipping non-armor item: {:?}", asset), - } - } + wtr.write_record(&[item.item_definition_id(), &kind, item.name(), &protection])?; }, - Err(e) => println!("Skipping folder due to {}", e), + _ => println!("Skipping non-armor item: {:?}", item), } } @@ -93,59 +43,24 @@ fn weapon_stats() -> Result<(), Box> { let mut wtr = csv::Writer::from_path("weaponstats.csv")?; wtr.write_record(&["Path", "Kind", "Name", "Power", "Equip Time (ms)"])?; - for folder in - assets::read_dir("common.items.weapons").expect("Failed to iterate over weapon folders!") + for item in comp::item::Item::new_from_asset_glob("common.items.weapons.*") + .expect("Failed to iterate over item folders!") { - match folder { - Ok(folder) => { - let mut glob_folder = folder.path().display().to_string().replace("/", "."); - glob_folder.push_str(".*"); - for file in std::fs::read_dir(folder.path())?.filter_map(|f| f.ok()) { - let asset_identifier = &file - .path() - .components() - .skip_while(|s| s != &Component::Normal(&OsString::from("common"))) - .inspect(|s| { - dbg!(&s); - }) - .collect::() - .with_extension("") - .display() - .to_string() - .replace("/", "."); - let asset = ItemAsset::load_expect_cloned(asset_identifier); + match item.kind() { + comp::item::ItemKind::Tool(tool) => { + let power = tool.base_power().to_string(); + let equip_time = tool.equip_time().subsec_millis().to_string(); + let kind = get_tool_kind(&tool.kind); - match &asset.kind { - comp::item::ItemKind::Tool(tool) => { - let power = tool.base_power().to_string(); - let equip_time = tool.equip_time().subsec_millis().to_string(); - let kind = match tool.kind { - ToolKind::Sword(_) => "Sword".to_string(), - ToolKind::Axe(_) => "Axe".to_string(), - ToolKind::Hammer(_) => "Hammer".to_string(), - ToolKind::Bow(_) => "Bow".to_string(), - ToolKind::Dagger(_) => "Dagger".to_string(), - ToolKind::Staff(_) => "Staff".to_string(), - ToolKind::Shield(_) => "Shield".to_string(), - ToolKind::Debug(_) => "Debug".to_string(), - ToolKind::Farming(_) => "Farming".to_string(), - ToolKind::Empty => "Empty".to_string(), - }; - - wtr.write_record(&[ - asset_identifier, - &kind, - asset.name(), - &power, - &equip_time, - ])?; - }, - // Skip non-armor - _ => println!("Skipping non-weapon item: {:?}", asset), - } - } + wtr.write_record(&[ + item.item_definition_id(), + &kind, + item.name(), + &power, + &equip_time, + ])?; }, - Err(e) => println!("Skipping folder due to {}", e), + _ => println!("Skipping non-weapon item: {:?}", item), } } @@ -153,6 +68,89 @@ fn weapon_stats() -> Result<(), Box> { Ok(()) } +fn get_tool_kind(kind: &ToolKind) -> String { + match kind { + ToolKind::Sword(_) => "Sword".to_string(), + ToolKind::Axe(_) => "Axe".to_string(), + ToolKind::Hammer(_) => "Hammer".to_string(), + ToolKind::Bow(_) => "Bow".to_string(), + ToolKind::Dagger(_) => "Dagger".to_string(), + ToolKind::Staff(_) => "Staff".to_string(), + ToolKind::Shield(_) => "Shield".to_string(), + ToolKind::Debug(_) => "Debug".to_string(), + ToolKind::Farming(_) => "Farming".to_string(), + ToolKind::Empty => "Empty".to_string(), + } +} + +fn get_tool_kind_kind(kind: &ToolKind) -> String { + match kind { + ToolKind::Sword(x) => x.clone(), + ToolKind::Axe(x) => x.clone(), + ToolKind::Hammer(x) => x.clone(), + ToolKind::Bow(x) => x.clone(), + ToolKind::Dagger(x) => x.clone(), + ToolKind::Staff(x) => x.clone(), + ToolKind::Shield(x) => x.clone(), + ToolKind::Debug(x) => x.clone(), + ToolKind::Farming(x) => x.clone(), + ToolKind::Empty => "".to_string(), + } +} + +fn get_armor_kind(kind: &ArmorKind) -> String { + match kind { + ArmorKind::Shoulder(_) => "Shoulder".to_string(), + ArmorKind::Chest(_) => "Chest".to_string(), + ArmorKind::Belt(_) => "Belt".to_string(), + ArmorKind::Hand(_) => "Hand".to_string(), + ArmorKind::Pants(_) => "Pants".to_string(), + ArmorKind::Foot(_) => "Foot".to_string(), + ArmorKind::Back(_) => "Back".to_string(), + ArmorKind::Ring(_) => "Ring".to_string(), + ArmorKind::Neck(_) => "Neck".to_string(), + ArmorKind::Head(_) => "Head".to_string(), + ArmorKind::Tabard(_) => "Tabard".to_string(), + } +} + +fn get_armor_kind_kind(kind: &ArmorKind) -> String { + match kind { + ArmorKind::Shoulder(x) => x.clone(), + ArmorKind::Chest(x) => x.clone(), + ArmorKind::Belt(x) => x.clone(), + ArmorKind::Hand(x) => x.clone(), + ArmorKind::Pants(x) => x.clone(), + ArmorKind::Foot(x) => x.clone(), + ArmorKind::Back(x) => x.clone(), + ArmorKind::Ring(x) => x.clone(), + ArmorKind::Neck(x) => x.clone(), + ArmorKind::Head(x) => x.clone(), + ArmorKind::Tabard(x) => x.clone(), + } +} + +fn all_items() -> Result<(), Box> { + let mut wtr = csv::Writer::from_path("items.csv")?; + wtr.write_record(&["Path", "Name", "Kind"])?; + + for item in comp::item::Item::new_from_asset_glob("common.items.*") + .expect("Failed to iterate over item folders!") + { + let kind = match item.kind() { + ItemKind::Armor(armor) => get_armor_kind_kind(&armor.kind), + ItemKind::Lantern(lantern) => lantern.kind.clone(), + ItemKind::Tool(tool) => get_tool_kind_kind(&tool.kind), + _ => "".to_owned(), + }; + + wtr.write_record(&[item.item_definition_id(), item.name(), &kind])?; + } + + wtr.flush()?; + Ok(()) +} + fn main() { let args = Cli::from_args(); if args.function.eq_ignore_ascii_case("armor_stats") { @@ -163,7 +161,14 @@ fn main() { if let Err(e) = weapon_stats() { println!("Error: {}", e) } + } else if args.function.eq_ignore_ascii_case("all_items") { + if let Err(e) = all_items() { + println!("Error: {}", e) + } } else { - println!("Invalid argument, available arguments:\n\"armor_stats\"\n\"weapon_stats\"") + println!( + "Invalid argument, available \ + arguments:\n\"armor_stats\"\n\"weapon_stats\"\n\"all_items\"" + ) } } diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index a5a8e5c9c2..a6a0cbad51 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -12,7 +12,6 @@ version = "0.7.0" gl = ["gfx_device_gl", "gfx_gl"] hot-anim = ["anim/use-dyn-lib"] singleplayer = ["server"] -tracy = ["tracing-tracy", "common/tracy"] tweak = ["const-tweaker"] default = ["gl", "singleplayer", "native-dialog"] @@ -67,7 +66,7 @@ num = "0.2" rand = "0.7" rodio = {version = "0.11", default-features = false, features = ["wav", "vorbis"]} ron = {version = "0.6", default-features = false} -serde = "1.0" +serde = {version = "1.0", features = [ "rc" ]} serde_derive = "1.0" treeculler = "0.1.0" uvth = "3.1.1" @@ -82,9 +81,6 @@ tracing-appender = "0.1" tracing-log = "0.1.1" tracing-subscriber = {version = "0.2.3", default-features = false, features = ["env-filter", "fmt", "chrono", "ansi", "smallvec", "tracing-log"]} -# Tracy -tracing-tracy = { version = "0.2.0", optional = true } - [target.'cfg(target_os = "macos")'.dependencies] dispatch = "0.1.4" diff --git a/voxygen/src/audio/sfx/event_mapper/combat/mod.rs b/voxygen/src/audio/sfx/event_mapper/combat/mod.rs index efc36f6997..50c78cbf6c 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/mod.rs @@ -9,8 +9,8 @@ use super::EventMapper; use common::{ comp::{ - item::{Item, ItemKind, ToolCategory}, - CharacterAbilityType, CharacterState, ItemConfig, Loadout, Pos, + item::{ItemKind, ToolCategory}, + CharacterAbilityType, CharacterState, Loadout, Pos, }, event::EventBus, state::State, @@ -135,32 +135,30 @@ impl CombatEventMapper { loadout: Option<&Loadout>, ) -> SfxEvent { if let Some(active_loadout) = loadout { - if let Some(ItemConfig { - item: - Item { - kind: ItemKind::Tool(data), - .. - }, - .. - }) = &active_loadout.active_item - { - // Check for attacking states - if character_state.is_attack() { - return SfxEvent::Attack( - CharacterAbilityType::from(character_state), - ToolCategory::from(&data.kind), - ); - } else if let Some(wield_event) = match ( - previous_state.weapon_drawn, - character_state.is_dodge(), - Self::weapon_drawn(character_state), - ) { - (false, false, true) => Some(SfxEvent::Wield(ToolCategory::from(&data.kind))), - (true, false, false) => Some(SfxEvent::Unwield(ToolCategory::from(&data.kind))), - _ => None, - } { - return wield_event; + if let Some(item_config) = &active_loadout.active_item { + if let ItemKind::Tool(data) = item_config.item.kind() { + if character_state.is_attack() { + return SfxEvent::Attack( + CharacterAbilityType::from(character_state), + ToolCategory::from(&data.kind), + ); + } else if let Some(wield_event) = match ( + previous_state.weapon_drawn, + character_state.is_dodge(), + Self::weapon_drawn(character_state), + ) { + (false, false, true) => { + Some(SfxEvent::Wield(ToolCategory::from(&data.kind))) + }, + (true, false, false) => { + Some(SfxEvent::Unwield(ToolCategory::from(&data.kind))) + }, + _ => None, + } { + return wield_event; + } } + // Check for attacking states } } diff --git a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs index eb381ae404..8dbb73ce5c 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs @@ -1,10 +1,8 @@ use super::*; use crate::audio::sfx::SfxEvent; use common::{ - assets::Asset, comp::{ - item::{tool::ToolCategory, ItemAsset}, - CharacterAbilityType, CharacterState, ItemConfig, Loadout, + item::tool::ToolCategory, CharacterAbilityType, CharacterState, Item, ItemConfig, Loadout, }, states, }; @@ -15,7 +13,7 @@ fn maps_wield_while_equipping() { let mut loadout = Loadout::default(); loadout.active_item = Some(ItemConfig { - item: ItemAsset::load_expect_cloned("common.items.weapons.axe.starter_axe"), + item: Item::new_from_asset_expect("common.items.weapons.axe.starter_axe"), ability1: None, ability2: None, ability3: None, @@ -43,7 +41,7 @@ fn maps_unwield() { let mut loadout = Loadout::default(); loadout.active_item = Some(ItemConfig { - item: ItemAsset::load_expect_cloned("common.items.weapons.bow.starter_bow"), + item: Item::new_from_asset_expect("common.items.weapons.bow.starter_bow"), ability1: None, ability2: None, ability3: None, @@ -69,7 +67,7 @@ fn maps_basic_melee() { let mut loadout = Loadout::default(); loadout.active_item = Some(ItemConfig { - item: ItemAsset::load_expect_cloned("common.items.weapons.axe.starter_axe"), + item: Item::new_from_asset_expect("common.items.weapons.axe.starter_axe"), ability1: None, ability2: None, ability3: None, @@ -105,7 +103,7 @@ fn matches_ability_stage() { let mut loadout = Loadout::default(); loadout.active_item = Some(ItemConfig { - item: ItemAsset::load_expect_cloned("common.items.weapons.sword.starter_sword"), + item: Item::new_from_asset_expect("common.items.weapons.sword.starter_sword"), ability1: None, ability2: None, ability3: None, @@ -146,7 +144,7 @@ fn ignores_different_ability_stage() { let mut loadout = Loadout::default(); loadout.active_item = Some(ItemConfig { - item: ItemAsset::load_expect_cloned("common.items.weapons.sword.starter_sword"), + item: Item::new_from_asset_expect("common.items.weapons.sword.starter_sword"), ability1: None, ability2: None, ability3: None, diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 0996912c16..9e0fe94c06 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -170,7 +170,7 @@ impl From<&InventoryUpdateEvent> for SfxEvent { InventoryUpdateEvent::Collected(item) => { // Handle sound effects for types of collected items, falling back to the // default Collected event - match &item.kind { + match &item.kind() { ItemKind::Tool(tool) => SfxEvent::Inventory(SfxInventoryEvent::CollectedTool( ToolCategory::try_from(&tool.kind).unwrap(), )), diff --git a/voxygen/src/hud/bag.rs b/voxygen/src/hud/bag.rs index c21d4ca3f5..881742b54e 100644 --- a/voxygen/src/hud/bag.rs +++ b/voxygen/src/hud/bag.rs @@ -179,8 +179,8 @@ impl<'a> Widget for Bag<'a> { self.stats.exp.maximum(), &self.localized_strings.get("hud.bag.exp") ); - let space_used = inventory.amount; - let space_max = inventory.slots.len(); + let space_used = inventory.amount(); + let space_max = inventory.slots().len(); let bag_space = format!("{}/{}", space_used, space_max); let bag_space_percentage = space_used as f32 / space_max as f32; let level = (self.stats.level.level()).to_string(); diff --git a/voxygen/src/hud/buttons.rs b/voxygen/src/hud/buttons.rs index 2c57697873..40a163ee9d 100644 --- a/voxygen/src/hud/buttons.rs +++ b/voxygen/src/hud/buttons.rs @@ -191,8 +191,8 @@ impl<'a> Widget for Buttons<'a> { .set(state.ids.bag_text, ui); } if !self.show_bag { - let space_used = inventory.amount; - let space_max = inventory.slots.len(); + let space_used = inventory.amount(); + let space_max = inventory.slots().len(); let bag_space = format!("{}/{}", space_used, space_max); let bag_space_percentage = space_used as f32 / space_max as f32; Text::new(&bag_space) diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index 6bf0a59f70..6535bc6deb 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -321,7 +321,7 @@ impl<'a> Widget for Crafting<'a> { .and_then(|r| self.client.recipe_book().get(r.as_str())) { // Title - Text::new(&recipe.output.0.name().to_string()) + Text::new(&recipe.output.0.name()) .mid_top_with_margin_on(state.ids.align_ing, -22.0) .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(14)) @@ -364,7 +364,8 @@ impl<'a> Widget for Crafting<'a> { // Widget generation for every ingredient for (i, (item, amount)) in recipe.inputs.iter().enumerate() { // Grey color for images and text if their amount is too low to craft the item - let col = if self.inventory.item_count(item) as f32 / *amount as f32 >= 1.0 { + let item_count_in_inventory = self.inventory.item_count(item); + let col = if item_count_in_inventory >= u64::from(*amount) { TEXT_COLOR } else { TEXT_DULL_RED_COLOR @@ -402,7 +403,7 @@ impl<'a> Widget for Crafting<'a> { // Ingredients text and amount // Don't show inventory amounts above 999 to avoid the widget clipping let over9k = "99+"; - let in_inv: &str = &self.inventory.item_count(item).to_string(); + let in_inv: &str = &item_count_in_inventory.to_string(); // Show Ingredients // Align "Required" Text below last ingredient if *amount == 0 { @@ -413,7 +414,7 @@ impl<'a> Widget for Crafting<'a> { .font_size(self.fonts.cyri.scale(14)) .color(col) .set(state.ids.req_text[i], ui); - Text::new(item.name()) + Text::new(&item.name()) .right_from(state.ids.ingredient_frame[i], 10.0) .font_id(self.fonts.cyri.conrod_id) .font_size(self.fonts.cyri.scale(14)) @@ -424,8 +425,8 @@ impl<'a> Widget for Crafting<'a> { let input = format!( "{}x {} ({})", amount, - item.name(), - if self.inventory.item_count(item) > 99 { + &item.name(), + if item_count_in_inventory > 99 { over9k } else { in_inv diff --git a/voxygen/src/hud/hotbar.rs b/voxygen/src/hud/hotbar.rs index 9b615530d2..7ca59b2945 100644 --- a/voxygen/src/hud/hotbar.rs +++ b/voxygen/src/hud/hotbar.rs @@ -73,7 +73,7 @@ impl State { loadout .active_item .as_ref() - .map(|i| &i.item.kind) + .map(|i| i.item.kind()) .filter(|kind| { use common::comp::item::{tool::ToolKind, ItemKind}; if let ItemKind::Tool(kind) = kind { diff --git a/voxygen/src/hud/item_imgs.rs b/voxygen/src/hud/item_imgs.rs index 155fd18bc0..4cda77f2ce 100644 --- a/voxygen/src/hud/item_imgs.rs +++ b/voxygen/src/hud/item_imgs.rs @@ -30,7 +30,7 @@ pub enum ItemKey { } impl From<&Item> for ItemKey { fn from(item: &Item) -> Self { - match &item.kind { + match &item.kind() { ItemKind::Tool(Tool { kind, .. }) => ItemKey::Tool(kind.clone()), ItemKind::Lantern(Lantern { kind, .. }) => ItemKey::Lantern(kind.clone()), ItemKind::Armor(Armor { kind, .. }) => ItemKey::Armor(kind.clone()), @@ -82,7 +82,7 @@ struct ItemImagesSpec(HashMap); impl Asset for ItemImagesSpec { const ENDINGS: &'static [&'static str] = &["ron"]; - fn parse(buf_reader: BufReader) -> Result { + fn parse(buf_reader: BufReader, _specifier: &str) -> Result { ron::de::from_reader(buf_reader).map_err(assets::Error::parse_error) } } diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index 08e106cc27..cba416f454 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -600,7 +600,7 @@ impl<'a> Widget for Skillbar<'a> { Image::new(self.imgs.skillbar_slot_big_bg) .w_h(38.0 * scale, 38.0 * scale) .color( - match self.loadout.active_item.as_ref().map(|i| &i.item.kind) { + match self.loadout.active_item.as_ref().map(|i| i.item.kind()) { Some(ItemKind::Tool(Tool { kind, .. })) => match kind { ToolKind::Bow(_) => Some(BG_COLOR_2), ToolKind::Staff(_) => Some(BG_COLOR_2), @@ -612,7 +612,7 @@ impl<'a> Widget for Skillbar<'a> { .middle_of(state.ids.m1_slot) .set(state.ids.m1_slot_bg, ui); Button::image( - match self.loadout.active_item.as_ref().map(|i| &i.item.kind) { + match self.loadout.active_item.as_ref().map(|i| i.item.kind()) { Some(ItemKind::Tool(Tool { kind, .. })) => match kind { ToolKind::Sword(_) => self.imgs.twohsword_m1, ToolKind::Dagger(_) => self.imgs.onehdagger_m1, @@ -663,12 +663,12 @@ impl<'a> Widget for Skillbar<'a> { }, } - let active_tool_kind = match self.loadout.active_item.as_ref().map(|i| &i.item.kind) { + let active_tool_kind = match self.loadout.active_item.as_ref().map(|i| i.item.kind()) { Some(ItemKind::Tool(Tool { kind, .. })) => Some(kind), _ => None, }; - let second_tool_kind = match self.loadout.second_item.as_ref().map(|i| &i.item.kind) { + let second_tool_kind = match self.loadout.second_item.as_ref().map(|i| i.item.kind()) { Some(ItemKind::Tool(Tool { kind, .. })) => Some(kind), _ => None, }; @@ -795,7 +795,7 @@ impl<'a> Widget for Skillbar<'a> { .2 .active_item .as_ref() - .map(|i| &i.item.kind) + .map(|i| i.item.kind()) .and_then(|kind| match kind { ItemKind::Tool(Tool { kind, .. }) => match kind { ToolKind::Staff(_) => Some(( diff --git a/voxygen/src/hud/slots.rs b/voxygen/src/hud/slots.rs index 5887008e7d..3e32e7cc3d 100644 --- a/voxygen/src/hud/slots.rs +++ b/voxygen/src/hud/slots.rs @@ -38,13 +38,7 @@ impl SlotKey for InventorySlot { fn amount(&self, source: &Inventory) -> Option { source .get(self.0) - .and_then(|item| match item.kind { - ItemKind::Tool { .. } | ItemKind::Lantern(_) | ItemKind::Armor { .. } => None, - ItemKind::Utility { amount, .. } - | ItemKind::Consumable { amount, .. } - | ItemKind::Throwable { amount, .. } - | ItemKind::Ingredient { amount, .. } => Some(amount), - }) + .map(|item| item.amount()) .filter(|amount| *amount > 1) } @@ -109,7 +103,7 @@ impl<'a> SlotKey, HotbarImageSource<'a>> for HotbarSlot { hotbar::SlotContents::Ability3 => loadout .active_item .as_ref() - .map(|i| &i.item.kind) + .map(|i| i.item.kind()) .and_then(|kind| { match kind { ItemKind::Tool(Tool { kind, .. }) => match kind { @@ -139,13 +133,7 @@ impl<'a> SlotKey, HotbarImageSource<'a>> for HotbarSlot { hotbar::SlotContents::Inventory(idx) => inventory.get(idx), hotbar::SlotContents::Ability3 => None, }) - .and_then(|item| match item.kind { - ItemKind::Tool { .. } | ItemKind::Lantern(_) | ItemKind::Armor { .. } => None, - ItemKind::Utility { amount, .. } - | ItemKind::Consumable { amount, .. } - | ItemKind::Throwable { amount, .. } - | ItemKind::Ingredient { amount, .. } => Some(amount), - }) + .map(|item| item.amount()) .filter(|amount| *amount > 1) } diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index 270cc08b57..2847cb221d 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -19,7 +19,7 @@ pub fn loadout_slot_text<'a>( } pub fn item_text<'a>(item: &'a Item) -> (&'_ str, Cow<'a, str>) { - let desc = match &item.kind { + let desc: Cow = match item.kind() { ItemKind::Armor(armor) => Cow::Owned(armor_desc(&armor, item.description())), ItemKind::Tool(tool) => Cow::Owned(tool_desc(&tool, item.description())), /*ItemKind::Consumable(kind, effect, ..) => { @@ -55,13 +55,6 @@ fn armor_desc(armor: &Armor, desc: &str) -> String { Protection::Invincible => "Inf".to_string(), }; - // TODO: remove when legacy descriptions are fixed by persistence overhaul - let desc = if desc.contains("") { - "Legacy item." - } else { - desc - }; - if !desc.is_empty() { format!( "{}\n\nArmor: {}\n\n{}\n\n", @@ -88,13 +81,6 @@ fn tool_desc(tool: &Tool, desc: &str) -> String { }; let power = tool.base_power(); - // TODO: remove when legacy descriptions are fixed by persistence overhaul - let desc = if desc.contains("") { - "Legacy item." - } else { - desc - }; - if !desc.is_empty() { format!( "{}\n\nPower: {:0.1}\n\n{}\n\n", diff --git a/voxygen/src/i18n.rs b/voxygen/src/i18n.rs index 1de029c950..ca554f4d5b 100644 --- a/voxygen/src/i18n.rs +++ b/voxygen/src/i18n.rs @@ -142,7 +142,7 @@ impl Asset for VoxygenLocalization { /// Load the translations located in the input buffer and convert them /// into a `VoxygenLocalization` object. #[allow(clippy::into_iter_on_ref)] // TODO: Pending review in #587 - fn parse(buf_reader: BufReader) -> Result { + fn parse(buf_reader: BufReader, _specifier: &str) -> Result { let mut asked_localization: VoxygenLocalization = from_reader(buf_reader).map_err(assets::Error::parse_error)?; diff --git a/voxygen/src/menu/char_selection/ui.rs b/voxygen/src/menu/char_selection/ui.rs index d45afc32f9..1dbef3af5f 100644 --- a/voxygen/src/menu/char_selection/ui.rs +++ b/voxygen/src/menu/char_selection/ui.rs @@ -12,8 +12,8 @@ use crate::{ use client::Client; use common::{ assets::Asset, - character::{Character, CharacterItem, MAX_CHARACTERS_PER_PLAYER}, - comp::{self, humanoid, item::ItemAsset}, + character::{Character, CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER}, + comp::{self, humanoid}, LoadoutBuilder, }; use conrod_core::{ @@ -255,7 +255,7 @@ pub enum Event { tool: Option, body: comp::Body, }, - DeleteCharacter(i32), + DeleteCharacter(CharacterId), } const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0); @@ -353,13 +353,17 @@ impl CharSelectionUi { character: Character { id: None, alias: name.clone(), - tool: tool.map(|specifier| specifier.to_string()), }, body, level: 1, loadout: LoadoutBuilder::new() .defaults() - .active_item(LoadoutBuilder::default_item_config_from_str(*tool)) + .active_item(Some(LoadoutBuilder::default_item_config_from_str( + (*tool).expect( + "Attempted to create character with non-existent \ + item_definition_id for tool", + ), + ))) .build(), }]) }, @@ -378,23 +382,23 @@ impl CharSelectionUi { Mode::Create { loadout, tool, .. } => { loadout.active_item = tool.map(|tool| comp::ItemConfig { // FIXME: Error gracefully. - item: (*ItemAsset::load_expect(tool)).clone(), + item: comp::Item::new_from_asset_expect(tool), ability1: None, ability2: None, ability3: None, block_ability: None, dodge_ability: None, }); - // FIXME: Error gracefully. - loadout.chest = Some(ItemAsset::load_expect_cloned( + // FIXME: Error gracefully + loadout.chest = Some(comp::Item::new_from_asset_expect( "common.items.armor.starter.rugged_chest", )); - // FIXME: Error gracefully. - loadout.pants = Some(ItemAsset::load_expect_cloned( + // FIXME: Error gracefully + loadout.pants = Some(comp::Item::new_from_asset_expect( "common.items.armor.starter.rugged_pants", )); - // FIXME: Error gracefully. - loadout.foot = Some(ItemAsset::load_expect_cloned( + // FIXME: Error gracefully + loadout.foot = Some(comp::Item::new_from_asset_expect( "common.items.armor.starter.sandals_0", )); Some(loadout.clone()) diff --git a/voxygen/src/profile.rs b/voxygen/src/profile.rs index f83d257aab..e94291404c 100644 --- a/voxygen/src/profile.rs +++ b/voxygen/src/profile.rs @@ -1,4 +1,5 @@ use crate::hud; +use common::character::CharacterId; use directories_next::ProjectDirs; use hashbrown::HashMap; use serde_derive::{Deserialize, Serialize}; @@ -37,7 +38,7 @@ impl Default for CharacterProfile { #[serde(default)] pub struct ServerProfile { /// A map of character's by id to their CharacterProfile. - pub characters: HashMap, + pub characters: HashMap, } impl Default for ServerProfile { @@ -116,7 +117,7 @@ impl Profile { pub fn get_hotbar_slots( &mut self, server: &str, - character_id: i32, + character_id: CharacterId, ) -> [Option; 10] { self.servers .entry(server.to_string()) @@ -141,7 +142,7 @@ impl Profile { pub fn set_hotbar_slots( &mut self, server: &str, - character_id: i32, + character_id: CharacterId, slots: [Option; 10], ) { self.servers diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 96b43753cf..b7d0b09f02 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -103,7 +103,7 @@ impl Asset for Glsl { const ENDINGS: &'static [&'static str] = &["glsl"]; - fn parse(mut buf_reader: BufReader) -> Result { + fn parse(mut buf_reader: BufReader, _specifier: &str) -> Result { let mut string = String::new(); buf_reader.read_to_string(&mut string)?; Ok(string) diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index 2934f38ee8..421b3259eb 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -133,7 +133,7 @@ impl CharacterCacheKey { shoulder: if let Some(ItemKind::Armor(Armor { kind: ArmorKind::Shoulder(armor), .. - })) = loadout.shoulder.as_ref().map(|i| &i.kind) + })) = loadout.shoulder.as_ref().map(|i| i.kind()) { Some(armor.clone()) } else { @@ -142,7 +142,7 @@ impl CharacterCacheKey { chest: if let Some(ItemKind::Armor(Armor { kind: ArmorKind::Chest(armor), .. - })) = loadout.chest.as_ref().map(|i| &i.kind) + })) = loadout.chest.as_ref().map(|i| i.kind()) { Some(armor.clone()) } else { @@ -151,7 +151,7 @@ impl CharacterCacheKey { belt: if let Some(ItemKind::Armor(Armor { kind: ArmorKind::Belt(armor), .. - })) = loadout.belt.as_ref().map(|i| &i.kind) + })) = loadout.belt.as_ref().map(|i| i.kind()) { Some(armor.clone()) } else { @@ -160,7 +160,7 @@ impl CharacterCacheKey { back: if let Some(ItemKind::Armor(Armor { kind: ArmorKind::Back(armor), .. - })) = loadout.back.as_ref().map(|i| &i.kind) + })) = loadout.back.as_ref().map(|i| i.kind()) { Some(armor.clone()) } else { @@ -169,7 +169,7 @@ impl CharacterCacheKey { pants: if let Some(ItemKind::Armor(Armor { kind: ArmorKind::Pants(armor), .. - })) = loadout.pants.as_ref().map(|i| &i.kind) + })) = loadout.pants.as_ref().map(|i| i.kind()) { Some(armor.clone()) } else { @@ -180,14 +180,14 @@ impl CharacterCacheKey { tool: if are_tools_visible { Some(CharacterToolKey { active: if let Some(ItemKind::Tool(tool)) = - loadout.active_item.as_ref().map(|i| &i.item.kind) + loadout.active_item.as_ref().map(|i| i.item.kind()) { Some(tool.kind.clone()) } else { None }, second: if let Some(ItemKind::Tool(tool)) = - loadout.second_item.as_ref().map(|i| &i.item.kind) + loadout.second_item.as_ref().map(|i| i.item.kind()) { Some(tool.kind.clone()) } else { @@ -198,7 +198,7 @@ impl CharacterCacheKey { None }, lantern: if let Some(ItemKind::Lantern(lantern)) = - loadout.lantern.as_ref().map(|i| &i.kind) + loadout.lantern.as_ref().map(|i| i.kind()) { Some(lantern.kind.clone()) } else { @@ -207,7 +207,7 @@ impl CharacterCacheKey { hand: if let Some(ItemKind::Armor(Armor { kind: ArmorKind::Hand(armor), .. - })) = loadout.hand.as_ref().map(|i| &i.kind) + })) = loadout.hand.as_ref().map(|i| i.kind()) { Some(armor.clone()) } else { @@ -216,7 +216,7 @@ impl CharacterCacheKey { foot: if let Some(ItemKind::Armor(Armor { kind: ArmorKind::Foot(armor), .. - })) = loadout.foot.as_ref().map(|i| &i.kind) + })) = loadout.foot.as_ref().map(|i| i.kind()) { Some(armor.clone()) } else { diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 15e14f6998..e02d6df6a0 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -679,7 +679,7 @@ impl FigureMgr { let active_item_kind = loadout .and_then(|l| l.active_item.as_ref()) - .map(|i| &i.item.kind); + .map(|i| i.item.kind()); let active_tool_kind = if let Some(ItemKind::Tool(tool)) = active_item_kind { Some(tool.kind.clone()) } else { @@ -688,7 +688,7 @@ impl FigureMgr { let second_item_kind = loadout .and_then(|l| l.second_item.as_ref()) - .map(|i| &i.item.kind); + .map(|i| i.item.kind()); let second_tool_kind = if let Some(ItemKind::Tool(tool)) = second_item_kind { Some(tool.kind.clone()) diff --git a/voxygen/src/scene/simple.rs b/voxygen/src/scene/simple.rs index 3e1a5d73f0..927f0a95e3 100644 --- a/voxygen/src/scene/simple.rs +++ b/voxygen/src/scene/simple.rs @@ -274,7 +274,7 @@ impl Scene { let active_item_kind = loadout .and_then(|l| l.active_item.as_ref()) - .map(|i| &i.item.kind); + .map(|i| i.item.kind()); let active_tool_kind = if let Some(ItemKind::Tool(tool)) = active_item_kind { Some(tool.kind.clone()) @@ -284,7 +284,7 @@ impl Scene { let second_item_kind = loadout .and_then(|l| l.second_item.as_ref()) - .map(|i| &i.item.kind); + .map(|i| i.item.kind()); let second_tool_kind = if let Some(ItemKind::Tool(tool)) = second_item_kind { Some(tool.kind.clone()) diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 9907ce55ba..7ae6a20aa0 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -141,7 +141,7 @@ impl SessionState { message: self .voxygen_i18n .get("hud.chat.loot_msg") - .replace("{item}", item.name().to_string().as_str()), + .replace("{item}", item.name()), chat_type: ChatType::Loot, }); }, diff --git a/voxygen/src/ui/mod.rs b/voxygen/src/ui/mod.rs index 960d35d52f..0e5ad64e6b 100644 --- a/voxygen/src/ui/mod.rs +++ b/voxygen/src/ui/mod.rs @@ -105,7 +105,7 @@ pub struct Font(text::Font); impl assets::Asset for Font { const ENDINGS: &'static [&'static str] = &["ttf"]; - fn parse(mut buf_reader: BufReader) -> Result { + fn parse(mut buf_reader: BufReader, _specifier: &str) -> Result { let mut buf = Vec::new(); buf_reader.read_to_end(&mut buf)?; Ok(Font(text::Font::from_bytes(buf).unwrap())) diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index a1369a9e44..db9817404b 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -10,7 +10,7 @@ use crate::{ use common::{ assets::Asset, astar::Astar, - comp::{self, item::ItemAsset}, + comp::{self}, generation::{ChunkSupplement, EntityInfo}, lottery::Lottery, npc, @@ -476,8 +476,8 @@ impl Floor { .with_alignment(comp::Alignment::Enemy) .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) .with_automatic_name() - .with_loot_drop(ItemAsset::load_expect_cloned(chosen)) - .with_main_tool(ItemAsset::load_expect_cloned(match rng.gen_range(0, 6) { + .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) + .with_main_tool(comp::Item::new_from_asset_expect(match rng.gen_range(0, 6) { 0 => "common.items.npc_weapons.axe.malachite_axe-0", 1 => "common.items.npc_weapons.sword.cultist_purp_2h-0", 2 => "common.items.npc_weapons.sword.cultist_purp_2h-0", @@ -517,7 +517,7 @@ impl Floor { "Cult Leader {}", npc::get_npc_name(npc::NpcKind::Humanoid) )) - .with_main_tool(ItemAsset::load_expect_cloned( + .with_main_tool(comp::Item::new_from_asset_expect( match rng.gen_range(0, 1) { //Add more possible cult leader npc_weapons here _ => { @@ -525,7 +525,7 @@ impl Floor { }, }, )) - .with_loot_drop(ItemAsset::load_expect_cloned(chosen)); + .with_loot_drop(comp::Item::new_from_asset_expect(chosen)); supplement.add_entity(entity); } diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index 00f1db5e12..66b84ca9b8 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -13,9 +13,8 @@ use crate::{ IndexRef, }; use common::{ - assets::Asset, astar::Astar, - comp::{self, bird_medium, humanoid, item::ItemAsset, object, quadruped_small}, + comp::{self, bird_medium, humanoid, object, quadruped_small, Item}, generation::{ChunkSupplement, EntityInfo}, path::Path, spiral::Spiral2d, @@ -904,7 +903,7 @@ impl Settlement { comp::Alignment::Tame }) .do_if(is_human && rng.gen(), |entity| { - entity.with_main_tool(ItemAsset::load_expect_cloned( + entity.with_main_tool(Item::new_from_asset_expect( match rng.gen_range(0, 7) { 0 => "common.items.npc_weapons.tool.broom", 1 => "common.items.npc_weapons.tool.hoe",