From 8661cb1ffca9ae25c7be22d2edf5cd8440879f67 Mon Sep 17 00:00:00 2001 From: Ben Wallis Date: Fri, 8 Jan 2021 19:12:09 +0000 Subject: [PATCH] Refactored loadout to have public functions for each slot instead of requiring callers to use the _INDEX consts --- .cargo/config | 2 +- CHANGELOG.md | 7 + Cargo.lock | 2 + assets/common/cave_scatter.ron | 4 +- assets/common/items/armor/back/backpack_0.ron | 1 + .../common/items/armor/bag/heavy_seabag.ron | 12 + .../items/armor/bag/knitted_red_pouch.ron | 12 + assets/common/items/armor/bag/liana_kit.ron | 12 + .../items/armor/bag/mindflayer_spellbag.ron | 12 + .../items/armor/bag/reliable_backpack.ron | 12 + .../items/armor/bag/soulkeeper_cursed.ron | 12 + .../items/armor/bag/soulkeeper_pure.ron | 12 + .../items/armor/bag/sturdy_red_backpack.ron | 12 + .../items/armor/bag/tiny_leather_pouch.ron | 12 + .../common/items/armor/bag/tiny_red_pouch.ron | 12 + .../items/armor/bag/troll_hide_pack.ron | 12 + .../common/items/armor/bag/woven_red_bag.ron | 12 + .../items/crafting_ing/cloth_scraps_red.ron | 8 + .../items/crafting_ing/leather_troll.ron | 8 + .../crafting_ing/mindflayer_bag_damaged.ron | 8 + .../common/items/debug/admin_black_hole.ron | 12 + assets/common/items/flowers/red.ron | 4 +- assets/common/items/food/coconut.ron | 2 +- .../common/items/testing/test_bag_18_slot.ron | 12 + .../common/items/testing/test_bag_9_slot.ron | 12 + .../common/items/weapons/dagger/basic_0.ron | 3 +- .../common/items/weapons/dagger/cultist_0.ron | 3 +- .../loot_table_boss_cultist-leader.ron | 1 + .../loot_tables/loot_table_cave_large.ron | 6 +- .../loot_tables/loot_table_crafting.ron | 2 +- .../loot_tables/loot_table_cultists.ron | 5 +- .../loot_tables/loot_table_humanoids.ron | 2 +- .../loot_tables/loot_table_maneater.ron | 5 + .../common/loot_tables/loot_table_saurok.ron | 19 + .../common/loot_tables/loot_table_troll.ron | 4 + .../loot_tables/loot_table_weapon_rare.ron | 4 +- assets/common/recipe_book.ron | 359 +++++++- assets/voxygen/element/bag/bot.vox | Bin 45431 -> 0 bytes assets/voxygen/element/bag/mid.vox | Bin 44440 -> 0 bytes assets/voxygen/element/bag/slot.vox | Bin 57163 -> 0 bytes assets/voxygen/element/bag/top.vox | Bin 47687 -> 0 bytes .../voxygen/element/buttons/inv_collapse.png | Bin 0 -> 1691 bytes .../element/buttons/inv_collapse_hover.png | Bin 0 -> 1691 bytes .../element/buttons/inv_collapse_press.png | Bin 0 -> 1691 bytes assets/voxygen/element/buttons/inv_expand.png | Bin 0 -> 1934 bytes .../element/buttons/inv_expand_hover.png | Bin 0 -> 1934 bytes .../element/buttons/inv_expand_press.png | Bin 0 -> 1932 bytes assets/voxygen/element/buttons/key_button.png | Bin 0 -> 1632 bytes .../element/buttons/key_button_press.png | Bin 0 -> 1888 bytes .../element/frames/prompt_dialog_bot.png | Bin 0 -> 1947 bytes .../element/frames/prompt_dialog_mid.png | Bin 0 -> 2079 bytes .../element/frames/prompt_dialog_top.png | Bin 0 -> 2076 bytes assets/voxygen/element/icons/bag.png | Bin 0 -> 3154 bytes assets/voxygen/element/icons/fire_bolt_1.png | Bin 777 -> 0 bytes assets/voxygen/element/icons/fire_spell_0.png | Bin 699 -> 0 bytes .../voxygen/element/icons/item_bag_blue.png | Bin 0 -> 2226 bytes .../element/icons/item_bag_blue_face.png | Bin 0 -> 2199 bytes .../element/icons/item_bag_brown_face.png | Bin 0 -> 2189 bytes .../element/icons/item_bag_green_large.png | Bin 0 -> 3180 bytes .../element/icons/item_bag_green_mid.png | Bin 0 -> 3149 bytes .../voxygen/element/icons/item_bag_large.png | Bin 0 -> 2947 bytes .../element/icons/item_bag_leather_large.png | Bin 0 -> 3152 bytes .../element/icons/item_bag_leather_small.png | Bin 0 -> 3110 bytes assets/voxygen/element/icons/item_bag_med.png | Bin 0 -> 2947 bytes .../element/icons/item_bag_red_face.png | Bin 0 -> 2199 bytes .../voxygen/element/icons/item_bag_skull.png | Bin 0 -> 2191 bytes .../voxygen/element/icons/item_bag_small.png | Bin 0 -> 2911 bytes .../voxygen/element/icons/item_bag_tiny.png | Bin 0 -> 2927 bytes .../voxygen/element/icons/item_cloth_red.png | Bin 0 -> 2086 bytes .../element/icons/item_flayer_soul.png | Bin 0 -> 1941 bytes .../element/icons/item_leather_green.png | Bin 0 -> 1749 bytes .../voxygen/element/icons/skill_charge_2.png | Bin 1408 -> 0 bytes assets/voxygen/element/misc_bg/inv_bg.png | Bin 53053 -> 0 bytes assets/voxygen/element/misc_bg/inv_bg_0.png | Bin 105406 -> 46659 bytes assets/voxygen/element/misc_bg/inv_bg_bag.png | Bin 0 -> 94614 bytes .../voxygen/element/misc_bg/inv_frame_bag.png | Bin 0 -> 10619 bytes assets/voxygen/element/misc_bg/inv_runes.png | Bin 58594 -> 0 bytes assets/voxygen/element/misc_bg/inv_slots.png | Bin 31400 -> 0 bytes assets/voxygen/element/slider/scrollbar_1.png | Bin 0 -> 2240 bytes assets/voxygen/i18n/en.ron | 0 assets/voxygen/i18n/en/_manifest.ron | 1 + assets/voxygen/i18n/en/hud/bag.ron | 1 + assets/voxygen/item_image_manifest.ron | 59 +- client/src/lib.rs | 5 +- common/net/src/msg/ecs_packet.rs | 10 +- common/src/bin/csv_export/main.rs | 2 + common/src/character.rs | 4 +- common/src/cmd.rs | 11 +- common/src/combat.rs | 37 +- common/src/comp/ability.rs | 101 +-- common/src/comp/inventory/item/armor.rs | 9 + common/src/comp/inventory/item/mod.rs | 159 +++- common/src/comp/inventory/loadout.rs | 363 ++++++++ .../{ => comp/inventory}/loadout_builder.rs | 524 ++++------- common/src/comp/inventory/mod.rs | 535 +++++++++-- common/src/comp/inventory/slot.rs | 518 ++--------- common/src/comp/inventory/test.rs | 329 ++++++- common/src/comp/inventory/test_helpers.rs | 24 + common/src/comp/mod.rs | 6 +- common/src/event.rs | 3 +- common/src/generation.rs | 3 +- common/src/lib.rs | 13 +- common/src/recipe.rs | 7 +- common/src/states/behavior.rs | 8 +- common/src/states/glide.rs | 5 +- common/src/states/glide_wield.rs | 4 +- common/src/states/utils.rs | 71 +- common/src/terrain/sprite.rs | 2 +- common/sys/src/agent.rs | 17 +- common/sys/src/beam.rs | 8 +- common/sys/src/buff.rs | 12 +- common/sys/src/character_behavior.rs | 26 +- common/sys/src/lib.rs | 2 +- common/sys/src/melee.rs | 8 +- common/sys/src/projectile.rs | 10 +- common/sys/src/shockwave.rs | 10 +- common/sys/src/state.rs | 4 - server-cli/Cargo.toml | 1 + server-cli/src/logging.rs | 5 + server/Cargo.toml | 2 + server/src/character_creator.rs | 20 +- server/src/cmd.rs | 64 +- server/src/events/entity_creation.rs | 11 +- server/src/events/entity_manipulation.rs | 36 +- server/src/events/interaction.rs | 53 +- server/src/events/inventory_manip.rs | 168 ++-- server/src/events/player.rs | 5 +- server/src/lib.rs | 16 +- .../2020-11-28-205542_item-storage/down.sql | 1 + .../2020-11-28-205542_item-storage/up.sql | 94 ++ server/src/persistence/character.rs | 26 +- .../src/persistence/character/conversions.rs | 126 ++- server/src/persistence/character_loader.rs | 20 +- server/src/persistence/character_updater.rs | 29 +- server/src/persistence/mod.rs | 1 - server/src/rtsim/entity.rs | 6 +- server/src/rtsim/tick.rs | 7 +- server/src/state_ext.rs | 14 +- server/src/sys/msg/character_screen.rs | 7 +- server/src/sys/persistence.rs | 11 +- server/src/sys/sentinel.rs | 16 +- server/src/sys/terrain.rs | 13 +- voxygen/Cargo.toml | 1 + .../src/audio/sfx/event_mapper/combat/mod.rs | 53 +- .../audio/sfx/event_mapper/combat/tests.rs | 100 +-- voxygen/src/hud/bag.rs | 848 ++++++++++-------- voxygen/src/hud/buttons.rs | 4 +- voxygen/src/hud/hotbar.rs | 23 +- voxygen/src/hud/img_ids.rs | 24 +- voxygen/src/hud/mod.rs | 128 ++- voxygen/src/hud/prompt_dialog.rs | 192 ++++ voxygen/src/hud/settings_window.rs | 1 + voxygen/src/hud/skillbar.rs | 53 +- voxygen/src/hud/slots.rs | 48 +- voxygen/src/hud/util.rs | 28 +- voxygen/src/logging.rs | 5 + voxygen/src/menu/char_selection/mod.rs | 17 +- voxygen/src/menu/char_selection/ui/mod.rs | 62 +- voxygen/src/profile.rs | 41 +- voxygen/src/scene/figure/cache.rs | 66 +- voxygen/src/scene/figure/mod.rs | 91 +- voxygen/src/scene/simple.rs | 26 +- voxygen/src/session.rs | 170 +++- voxygen/src/ui/widgets/slot.rs | 12 +- world/src/layer/scatter.rs | 6 +- world/src/site/dungeon/mod.rs | 74 +- world/src/site/settlement/mod.rs | 8 +- 167 files changed, 4138 insertions(+), 2245 deletions(-) create mode 100644 assets/common/items/armor/bag/heavy_seabag.ron create mode 100644 assets/common/items/armor/bag/knitted_red_pouch.ron create mode 100644 assets/common/items/armor/bag/liana_kit.ron create mode 100644 assets/common/items/armor/bag/mindflayer_spellbag.ron create mode 100644 assets/common/items/armor/bag/reliable_backpack.ron create mode 100644 assets/common/items/armor/bag/soulkeeper_cursed.ron create mode 100644 assets/common/items/armor/bag/soulkeeper_pure.ron create mode 100644 assets/common/items/armor/bag/sturdy_red_backpack.ron create mode 100644 assets/common/items/armor/bag/tiny_leather_pouch.ron create mode 100644 assets/common/items/armor/bag/tiny_red_pouch.ron create mode 100644 assets/common/items/armor/bag/troll_hide_pack.ron create mode 100644 assets/common/items/armor/bag/woven_red_bag.ron create mode 100644 assets/common/items/crafting_ing/cloth_scraps_red.ron create mode 100644 assets/common/items/crafting_ing/leather_troll.ron create mode 100644 assets/common/items/crafting_ing/mindflayer_bag_damaged.ron create mode 100644 assets/common/items/debug/admin_black_hole.ron create mode 100644 assets/common/items/testing/test_bag_18_slot.ron create mode 100644 assets/common/items/testing/test_bag_9_slot.ron create mode 100644 assets/common/loot_tables/loot_table_maneater.ron create mode 100644 assets/common/loot_tables/loot_table_saurok.ron create mode 100644 assets/common/loot_tables/loot_table_troll.ron delete mode 100644 assets/voxygen/element/bag/bot.vox delete mode 100644 assets/voxygen/element/bag/mid.vox delete mode 100644 assets/voxygen/element/bag/slot.vox delete mode 100644 assets/voxygen/element/bag/top.vox create mode 100644 assets/voxygen/element/buttons/inv_collapse.png create mode 100644 assets/voxygen/element/buttons/inv_collapse_hover.png create mode 100644 assets/voxygen/element/buttons/inv_collapse_press.png create mode 100644 assets/voxygen/element/buttons/inv_expand.png create mode 100644 assets/voxygen/element/buttons/inv_expand_hover.png create mode 100644 assets/voxygen/element/buttons/inv_expand_press.png create mode 100644 assets/voxygen/element/buttons/key_button.png create mode 100644 assets/voxygen/element/buttons/key_button_press.png create mode 100644 assets/voxygen/element/frames/prompt_dialog_bot.png create mode 100644 assets/voxygen/element/frames/prompt_dialog_mid.png create mode 100644 assets/voxygen/element/frames/prompt_dialog_top.png create mode 100644 assets/voxygen/element/icons/bag.png delete mode 100644 assets/voxygen/element/icons/fire_bolt_1.png delete mode 100644 assets/voxygen/element/icons/fire_spell_0.png create mode 100644 assets/voxygen/element/icons/item_bag_blue.png create mode 100644 assets/voxygen/element/icons/item_bag_blue_face.png create mode 100644 assets/voxygen/element/icons/item_bag_brown_face.png create mode 100644 assets/voxygen/element/icons/item_bag_green_large.png create mode 100644 assets/voxygen/element/icons/item_bag_green_mid.png create mode 100644 assets/voxygen/element/icons/item_bag_large.png create mode 100644 assets/voxygen/element/icons/item_bag_leather_large.png create mode 100644 assets/voxygen/element/icons/item_bag_leather_small.png create mode 100644 assets/voxygen/element/icons/item_bag_med.png create mode 100644 assets/voxygen/element/icons/item_bag_red_face.png create mode 100644 assets/voxygen/element/icons/item_bag_skull.png create mode 100644 assets/voxygen/element/icons/item_bag_small.png create mode 100755 assets/voxygen/element/icons/item_bag_tiny.png create mode 100644 assets/voxygen/element/icons/item_cloth_red.png create mode 100644 assets/voxygen/element/icons/item_flayer_soul.png create mode 100644 assets/voxygen/element/icons/item_leather_green.png delete mode 100644 assets/voxygen/element/icons/skill_charge_2.png delete mode 100644 assets/voxygen/element/misc_bg/inv_bg.png create mode 100644 assets/voxygen/element/misc_bg/inv_bg_bag.png create mode 100644 assets/voxygen/element/misc_bg/inv_frame_bag.png delete mode 100644 assets/voxygen/element/misc_bg/inv_runes.png delete mode 100644 assets/voxygen/element/misc_bg/inv_slots.png create mode 100644 assets/voxygen/element/slider/scrollbar_1.png create mode 100644 assets/voxygen/i18n/en.ron create mode 100644 common/src/comp/inventory/loadout.rs rename common/src/{ => comp/inventory}/loadout_builder.rs (57%) create mode 100644 common/src/comp/inventory/test_helpers.rs create mode 100644 server/src/migrations/2020-11-28-205542_item-storage/down.sql create mode 100644 server/src/migrations/2020-11-28-205542_item-storage/up.sql create mode 100644 voxygen/src/hud/prompt_dialog.rs diff --git a/.cargo/config b/.cargo/config index 9884717112..f588f639ff 100644 --- a/.cargo/config +++ b/.cargo/config @@ -5,7 +5,7 @@ rustflags = [ [alias] generate = "run --package tools --" -test-server = "-Zpackage-features run --bin veloren-server-cli --no-default-features" +test-server = "-Zpackage-features run --bin veloren-server-cli --no-default-features -- -b" tracy-server = "-Zunstable-options -Zpackage-features run --bin veloren-server-cli --no-default-features --features tracy,simd --profile no_overflow" test-voxygen = "-Zpackage-features run --bin veloren-voxygen --no-default-features --features gl,simd" tracy-voxygen = "-Zunstable-options -Zpackage-features run --bin veloren-voxygen --no-default-features --features tracy,gl,simd --profile no_overflow" diff --git a/CHANGELOG.md b/CHANGELOG.md index 60cb12e115..29f595209b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Initial support for alternate style keyboards - Flying birds travel the world - Plugin system now based on Wasmer 1.0.0 +- Added 4x Bag loadout slots, used for upgrading inventory space +- Added an additional Ring loadout slot +- The inventory can now be expanded to fill the whole window +- Added /dropall admin command (drops all inventory items on the ground) ### Changed @@ -26,10 +30,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Glider can now be deployed even when not on the ground - Gliding now has an energy cost for strenuous maneuvers based on lift - Translations are now folders with multiple files instead of a huge single file +- Default inventory slots reduced to 18 - existing characters given 3x 6-slot bags as compensation +- Protection rating was moved to the top left of the loadout view ### Removed - SSAAx4 option +- The Stats button and associated screen were removed ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 7f31cb6e6a..bdbf7973ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6089,6 +6089,7 @@ version = "0.8.0" dependencies = [ "authc", "chrono", + "const-tweaker", "crossbeam-channel 0.5.0", "diesel", "diesel_migrations", @@ -6179,6 +6180,7 @@ dependencies = [ "image", "inline_tweak", "itertools", + "lazy_static", "native-dialog", "num 0.3.1", "old_school_gfx_glutin_ext", diff --git a/assets/common/cave_scatter.ron b/assets/common/cave_scatter.ron index cd00eb2b47..13e3ccf291 100644 --- a/assets/common/cave_scatter.ron +++ b/assets/common/cave_scatter.ron @@ -4,7 +4,7 @@ (110, Stones), (150, ShortGrass), (120, CaveMushroom), - (2, ShinyGem), - (2, Chest), + (4, ShinyGem), + (5, Chest), (15, Crate), ] diff --git a/assets/common/items/armor/back/backpack_0.ron b/assets/common/items/armor/back/backpack_0.ron index 145163ba89..0b623b4fcc 100644 --- a/assets/common/items/armor/back/backpack_0.ron +++ b/assets/common/items/armor/back/backpack_0.ron @@ -9,4 +9,5 @@ ItemDef( ) ), quality: High, + slots: 18, ) diff --git a/assets/common/items/armor/bag/heavy_seabag.ron b/assets/common/items/armor/bag/heavy_seabag.ron new file mode 100644 index 0000000000..d679a2070f --- /dev/null +++ b/assets/common/items/armor/bag/heavy_seabag.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Heavy Seabag", + description: "Commonly used by sailors.", + kind: Armor( + ( + kind: Bag("BluePouch"), + stats: (protection: Normal(0.0)), + ) + ), + quality: Moderate, + slots: 14, +) diff --git a/assets/common/items/armor/bag/knitted_red_pouch.ron b/assets/common/items/armor/bag/knitted_red_pouch.ron new file mode 100644 index 0000000000..a3829eee99 --- /dev/null +++ b/assets/common/items/armor/bag/knitted_red_pouch.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Knitted Red Pouch", + description: "Made from some patches of dyed cloth.", + kind: Armor( + ( + kind: Bag("RedSmall"), + stats: (protection: Normal(0.0)), + ) + ), + quality: Moderate, + slots: 9, +) diff --git a/assets/common/items/armor/bag/liana_kit.ron b/assets/common/items/armor/bag/liana_kit.ron new file mode 100644 index 0000000000..6bebf02b17 --- /dev/null +++ b/assets/common/items/armor/bag/liana_kit.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Liana Kit", + description: "Woven from dried lianas.", + kind: Armor( + ( + kind: Bag("GreenMid"), + stats: (protection: Normal(0.0)), + ) + ), + quality: Moderate, + slots: 12, +) diff --git a/assets/common/items/armor/bag/mindflayer_spellbag.ron b/assets/common/items/armor/bag/mindflayer_spellbag.ron new file mode 100644 index 0000000000..c9962ce616 --- /dev/null +++ b/assets/common/items/armor/bag/mindflayer_spellbag.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Mindflayer Spellbag", + description: "You can almost feel the Mindflayer's\nevil presence flowing through the fabric.", + kind: Armor( + ( + kind: Bag("PurpleSkull"), + stats: (protection: Normal(0.0)), + ) + ), + quality: Epic, + slots: 27, +) diff --git a/assets/common/items/armor/bag/reliable_backpack.ron b/assets/common/items/armor/bag/reliable_backpack.ron new file mode 100644 index 0000000000..732bb75e69 --- /dev/null +++ b/assets/common/items/armor/bag/reliable_backpack.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Reliable Backpack", + description: "It will never give you up.", + kind: Armor( + ( + kind: Bag("LeatherLarge"), + stats: (protection: Normal(0.0)), + ) + ), + quality: High, + slots: 16, +) diff --git a/assets/common/items/armor/bag/soulkeeper_cursed.ron b/assets/common/items/armor/bag/soulkeeper_cursed.ron new file mode 100644 index 0000000000..237296d588 --- /dev/null +++ b/assets/common/items/armor/bag/soulkeeper_cursed.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Cursed Soulkeeper", + description: "WIP", + kind: Armor( + ( + kind: Bag("RedFace"), + stats: (protection: Normal(0.0)), + ) + ), + quality: Legendary, + slots: 36, +) diff --git a/assets/common/items/armor/bag/soulkeeper_pure.ron b/assets/common/items/armor/bag/soulkeeper_pure.ron new file mode 100644 index 0000000000..7cb1b49c62 --- /dev/null +++ b/assets/common/items/armor/bag/soulkeeper_pure.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Purified Soulkeeper", + description: "WIP", + kind: Armor( + ( + kind: Bag("BlueFace"), + stats: (protection: Normal(0.0)), + ) + ), + quality: Legendary, + slots: 36, +) diff --git a/assets/common/items/armor/bag/sturdy_red_backpack.ron b/assets/common/items/armor/bag/sturdy_red_backpack.ron new file mode 100644 index 0000000000..e46a915ef4 --- /dev/null +++ b/assets/common/items/armor/bag/sturdy_red_backpack.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Sturdy Red Backpack", + description: "Made from some patches of dyed cloth.", + kind: Armor( + ( + kind: Bag("RedLarge"), + stats: (protection: Normal(0.0)), + ) + ), + quality: High, + slots: 18, +) diff --git a/assets/common/items/armor/bag/tiny_leather_pouch.ron b/assets/common/items/armor/bag/tiny_leather_pouch.ron new file mode 100644 index 0000000000..21d195fdca --- /dev/null +++ b/assets/common/items/armor/bag/tiny_leather_pouch.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Tiny Leather Pouch", + description: "Made from a few patches of leather.", + kind: Armor( + ( + kind: Bag("LeatherSmall"), + stats: (protection: Normal(0.0)), + ) + ), + quality: Common, + slots: 6, +) diff --git a/assets/common/items/armor/bag/tiny_red_pouch.ron b/assets/common/items/armor/bag/tiny_red_pouch.ron new file mode 100644 index 0000000000..e8b944c994 --- /dev/null +++ b/assets/common/items/armor/bag/tiny_red_pouch.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Tiny Red Pouch", + description: "Made from a single patch of dyed cloth.", + kind: Armor( + ( + kind: Bag("RedTiny"), + stats: (protection: Normal(0.0)), + ) + ), + quality: Common, + slots: 3, +) diff --git a/assets/common/items/armor/bag/troll_hide_pack.ron b/assets/common/items/armor/bag/troll_hide_pack.ron new file mode 100644 index 0000000000..242a32dc4d --- /dev/null +++ b/assets/common/items/armor/bag/troll_hide_pack.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Trollhide Pack", + description: "Trolls were definitely hurt\nin the making of this.", + kind: Armor( + ( + kind: Bag("GreenLarge"), + stats: (protection: Normal(0.0)), + ) + ), + quality: High, + slots: 18, +) diff --git a/assets/common/items/armor/bag/woven_red_bag.ron b/assets/common/items/armor/bag/woven_red_bag.ron new file mode 100644 index 0000000000..fea605d7e3 --- /dev/null +++ b/assets/common/items/armor/bag/woven_red_bag.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Woven Red Bag", + description: "Made from some patches of dyed cloth.", + kind: Armor( + ( + kind: Bag("RedMed"), + stats: (protection: Normal(0.0)), + ) + ), + quality: Moderate, + slots: 15, +) diff --git a/assets/common/items/crafting_ing/cloth_scraps_red.ron b/assets/common/items/crafting_ing/cloth_scraps_red.ron new file mode 100644 index 0000000000..4fa8452d4e --- /dev/null +++ b/assets/common/items/crafting_ing/cloth_scraps_red.ron @@ -0,0 +1,8 @@ +ItemDef( + name: "Red Cloth Scraps", + description: "Dyed red with flower pigments.", + kind: Ingredient( + kind: "ClothScrapsRed", + ), + quality: Common, +) diff --git a/assets/common/items/crafting_ing/leather_troll.ron b/assets/common/items/crafting_ing/leather_troll.ron new file mode 100644 index 0000000000..aed22f2733 --- /dev/null +++ b/assets/common/items/crafting_ing/leather_troll.ron @@ -0,0 +1,8 @@ +ItemDef( + name: "Troll Hide", + description: "Looted from cave trolls.", + kind: Ingredient( + kind: "TrollLeather", + ), + quality: High, +) diff --git a/assets/common/items/crafting_ing/mindflayer_bag_damaged.ron b/assets/common/items/crafting_ing/mindflayer_bag_damaged.ron new file mode 100644 index 0000000000..530bc6233f --- /dev/null +++ b/assets/common/items/crafting_ing/mindflayer_bag_damaged.ron @@ -0,0 +1,8 @@ +ItemDef( + name: "Glowing Remains", + description: "Looted from an evil being.\n\nWith some additional work it can surely be\nbrought back to it's former glory...", + kind: Ingredient( + kind: "FlayerBagDamaged", + ), + quality: Epic, +) diff --git a/assets/common/items/debug/admin_black_hole.ron b/assets/common/items/debug/admin_black_hole.ron new file mode 100644 index 0000000000..f2ec6911d7 --- /dev/null +++ b/assets/common/items/debug/admin_black_hole.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Admin's Black Hole", + description: "It just works.", + kind: Armor( + ( + kind: Bag("BrownFace"), + stats: (protection: Normal(0.0)), + ) + ), + quality: Artifact, + slots: 900, +) diff --git a/assets/common/items/flowers/red.ron b/assets/common/items/flowers/red.ron index 39167814ac..463b5b5100 100644 --- a/assets/common/items/flowers/red.ron +++ b/assets/common/items/flowers/red.ron @@ -1,8 +1,8 @@ ItemDef( name: "Red Flower", - description: "Roses are red...", + description: "Can be used as a dying ingredient.", kind: Ingredient( - kind: "Flower", + kind: "FlowerRed", ), quality: Common, ) diff --git a/assets/common/items/food/coconut.ron b/assets/common/items/food/coconut.ron index 75992f3335..7c728d55e2 100644 --- a/assets/common/items/food/coconut.ron +++ b/assets/common/items/food/coconut.ron @@ -1,6 +1,6 @@ ItemDef( name: "Coconut", - description: "Restores 20 health over 10 seconds\n\nReliable source of water and fat", + description: "Restores 20 health over 10 seconds\n\nReliable source of water and fat.\n\nNaturally growing at the top of palm trees.", kind: Consumable( kind: "Coconut", effect: [ diff --git a/assets/common/items/testing/test_bag_18_slot.ron b/assets/common/items/testing/test_bag_18_slot.ron new file mode 100644 index 0000000000..4c708efeab --- /dev/null +++ b/assets/common/items/testing/test_bag_18_slot.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Test 18 slot bag", + description: "Used for unit tests do not delete", + kind: Armor( + ( + kind: Bag("TestBag18Slot"), + stats: (protection: Normal(0.0)), + ) + ), + quality: High, + slots: 18, +) diff --git a/assets/common/items/testing/test_bag_9_slot.ron b/assets/common/items/testing/test_bag_9_slot.ron new file mode 100644 index 0000000000..454d51d06e --- /dev/null +++ b/assets/common/items/testing/test_bag_9_slot.ron @@ -0,0 +1,12 @@ +ItemDef( + name: "Test 9 slot bag", + description: "Used for unit tests do not delete", + kind: Armor( + ( + kind: Bag("TestBag9Slot"), + stats: (protection: Normal(0.0)), + ) + ), + quality: High, + slots: 9, +) diff --git a/assets/common/items/weapons/dagger/basic_0.ron b/assets/common/items/weapons/dagger/basic_0.ron index 7040efb5c3..4ee3f82ff6 100644 --- a/assets/common/items/weapons/dagger/basic_0.ron +++ b/assets/common/items/weapons/dagger/basic_0.ron @@ -6,7 +6,8 @@ ItemDef( kind: Dagger, stats: ( equip_time_millis: 0, - power: 1.80 + power: 1.80, + speed: 1.0 ), ) ), diff --git a/assets/common/items/weapons/dagger/cultist_0.ron b/assets/common/items/weapons/dagger/cultist_0.ron index 70c9a034d3..d581e75484 100644 --- a/assets/common/items/weapons/dagger/cultist_0.ron +++ b/assets/common/items/weapons/dagger/cultist_0.ron @@ -6,7 +6,8 @@ ItemDef( kind: Dagger, stats: ( equip_time_millis: 0, - power: 2.00 + power: 2.00, + speed: 1.5 ), ) ), diff --git a/assets/common/loot_tables/loot_table_boss_cultist-leader.ron b/assets/common/loot_tables/loot_table_boss_cultist-leader.ron index 9abc2fdc06..2ff4fc93ff 100644 --- a/assets/common/loot_tables/loot_table_boss_cultist-leader.ron +++ b/assets/common/loot_tables/loot_table_boss_cultist-leader.ron @@ -11,6 +11,7 @@ (1, "common.items.weapons.staff.cultist_staff"), (1, "common.items.weapons.hammer.cultist_purp_2h-0"), (1, "common.items.weapons.sword.cultist_purp_2h-0"), + (0.25, "common.items.weapons.crafting_ing.mindflayer_bag_damaged"), // misc (1, "common.items.boss_drops.lantern"), (0.1, "common.items.glider.glider_purp"), diff --git a/assets/common/loot_tables/loot_table_cave_large.ron b/assets/common/loot_tables/loot_table_cave_large.ron index 6f2696d083..0f0a97eee4 100644 --- a/assets/common/loot_tables/loot_table_cave_large.ron +++ b/assets/common/loot_tables/loot_table_cave_large.ron @@ -1,7 +1,7 @@ [ // Misc (0.25, "common.items.armor.neck.neck_1"), - (0.2, "common.items.crafting_ing.cloth_scraps"), + (0.5, "common.items.crafting_ing.cloth_scraps"), (1.0, "common.items.crafting_ing.empty_vial"), (0.1, "common.items.glider.glider_blue"), (0.1, "common.items.glider.glider_morpho"), @@ -41,7 +41,7 @@ // staves (1.00, "common.items.weapons.staff.bone_staff"), (1.00, "common.items.weapons.staff.amethyst_staff"), - (0.1, "common.items.weapons.sceptre.sceptre_velorite_0"), + (0.05, "common.items.weapons.sceptre.sceptre_velorite_0"), // hammers (0.30, "common.items.weapons.hammer.cobalt_hammer-0"), (0.30, "common.items.weapons.hammer.cobalt_hammer-1"), @@ -56,7 +56,7 @@ (0.05, "common.items.weapons.hammer.steel_hammer-4"), (0.05, "common.items.weapons.hammer.steel_hammer-5"), // bows - (0.1, "common.items.weapons.bow.nature_ore_longbow-0"), + (0.05, "common.items.weapons.bow.nature_ore_longbow-0"), ] diff --git a/assets/common/loot_tables/loot_table_crafting.ron b/assets/common/loot_tables/loot_table_crafting.ron index 0e407f813c..fe9f61e862 100644 --- a/assets/common/loot_tables/loot_table_crafting.ron +++ b/assets/common/loot_tables/loot_table_crafting.ron @@ -1,7 +1,7 @@ [ // crafting ingredients (2, "common.items.crafting_ing.leather_scraps"), - (2, "common.items.crafting_ing.cloth_scraps"), + (4, "common.items.crafting_ing.cloth_scraps"), (1, "common.items.crafting_ing.empty_vial"), (0.10, "common.items.crafting_ing.shiny_gem"), diff --git a/assets/common/loot_tables/loot_table_cultists.ron b/assets/common/loot_tables/loot_table_cultists.ron index c02ebbfaac..b046ac92da 100644 --- a/assets/common/loot_tables/loot_table_cultists.ron +++ b/assets/common/loot_tables/loot_table_cultists.ron @@ -5,7 +5,7 @@ (3, "common.items.food.apple"), (3, "common.items.food.mushroom"), (3, "common.items.food.coconut"), - (3, "common.items.crafting_ing.cloth_scraps"), + (5, "common.items.crafting_ing.cloth_scraps"), // crafted (0.5, "common.items.food.apple_mushroom_curry"), (0.5, "common.items.food.apple_stick"), @@ -65,6 +65,9 @@ //gloves (0.50, "common.items.armor.hand.leather_0"), (0.50, "common.items.armor.hand.leather_2"), + //backpack + (0.001, "common.items.armor.back.backpack_0"), + (0.1, "common.items.armor.bag.heavy_seabag"), // Common Weapons // swords (0.4, "common.items.weapons.sword.wood_sword"), diff --git a/assets/common/loot_tables/loot_table_humanoids.ron b/assets/common/loot_tables/loot_table_humanoids.ron index 122a3c2294..e7872e3107 100644 --- a/assets/common/loot_tables/loot_table_humanoids.ron +++ b/assets/common/loot_tables/loot_table_humanoids.ron @@ -2,7 +2,7 @@ // Crafting Ingredients (2, "common.items.crafting_ing.empty_vial"), (0.10, "common.items.crafting_ing.shiny_gem"), - (2, "common.items.crafting_ing.cloth_scraps"), + (4, "common.items.crafting_ing.cloth_scraps"), // Consumables (0.2, "common.items.consumable.potion_minor"), // Ring diff --git a/assets/common/loot_tables/loot_table_maneater.ron b/assets/common/loot_tables/loot_table_maneater.ron new file mode 100644 index 0000000000..524a6b2030 --- /dev/null +++ b/assets/common/loot_tables/loot_table_maneater.ron @@ -0,0 +1,5 @@ +[ + (1, "common.items.flowers.red"), + (1, "common.items.crafting_ing.twigs"), + (0.5, "common.items.food.coconut"), +] diff --git a/assets/common/loot_tables/loot_table_saurok.ron b/assets/common/loot_tables/loot_table_saurok.ron new file mode 100644 index 0000000000..0fe75b1c74 --- /dev/null +++ b/assets/common/loot_tables/loot_table_saurok.ron @@ -0,0 +1,19 @@ +[ + (2, "common.items.crafting_ing.empty_vial"), + (0.01, "common.items.crafting_ing.shiny_gem"), + (3, "common.items.crafting_ing.cloth_scraps"), + (2, "common.items.crafting_ing.leather_scraps"), + // Consumables + (0.5, "common.items.consumable.potion_minor"), + // Ring + (0.2, "common.items.armor.ring.ring_gold_0"), + // Utility + (0.1, "common.items.utility.collar"), + // Bag + (0.1, "common.items.armor.bag.liana_kit"), + // Food + (2.0, "common.items.food.coconut"), + (0.3, "common.items.food.apple_mushroom_curry"), + (0.6, "common.items.food.apple_stick"), + (0.8, "common.items.food.mushroom_stick"), +] \ No newline at end of file diff --git a/assets/common/loot_tables/loot_table_troll.ron b/assets/common/loot_tables/loot_table_troll.ron new file mode 100644 index 0000000000..8707fc9cf8 --- /dev/null +++ b/assets/common/loot_tables/loot_table_troll.ron @@ -0,0 +1,4 @@ +[ + (1, "common.items.crafting_ing.leather_troll"), + (0.5, "common.items.crafting_ing.leather_scraps"), +] \ No newline at end of file diff --git a/assets/common/loot_tables/loot_table_weapon_rare.ron b/assets/common/loot_tables/loot_table_weapon_rare.ron index a831bb2620..5755a38271 100644 --- a/assets/common/loot_tables/loot_table_weapon_rare.ron +++ b/assets/common/loot_tables/loot_table_weapon_rare.ron @@ -25,9 +25,9 @@ (0.30, "common.items.weapons.hammer.cobalt_hammer-1"), (0.15, "common.items.weapons.hammer.runic_hammer"), (0.15, "common.items.weapons.hammer.ramshead_hammer"), - (0.10, "common.items.weapons.hammer.mjolnir"), + (0.01, "common.items.weapons.hammer.mjolnir"), // bows (0.60, "common.items.weapons.bow.horn_longbow-0"), (0.30, "common.items.weapons.bow.iron_longbow-0"), - (0.10, "common.items.weapons.bow.rare_longbow"), + (0.05, "common.items.weapons.bow.rare_longbow"), ] \ No newline at end of file diff --git a/assets/common/recipe_book.ron b/assets/common/recipe_book.ron index 5fb252b86b..e6db59bb0e 100644 --- a/assets/common/recipe_book.ron +++ b/assets/common/recipe_book.ron @@ -1,42 +1,321 @@ { - // Tools - "crafting_hammer": (("common.items.crafting_tools.craftsman_hammer", 1),[("common.items.crafting_ing.twigs", 6), ("common.items.crafting_ing.stones", 6)]), - "mortar_pestle": (("common.items.crafting_tools.mortar_pestle", 1), [("common.items.crafting_ing.stones", 6), ("common.items.food.coconut", 2), ("common.items.crafting_tools.craftsman_hammer", 0)]), - "sewing_set": (("common.items.crafting_tools.sewing_set", 1),[("common.items.crafting_ing.leather_scraps", 2), ("common.items.crafting_ing.twigs", 4), ("common.items.crafting_ing.stones", 2), ("common.items.crafting_ing.shiny_gem", 1)]), - // Ore and more - "velorite_frag": (("common.items.ore.veloritefrag", 2), [("common.items.ore.velorite", 1), ("common.items.crafting_tools.craftsman_hammer", 0)]), - //Potions - "potion_s": (("common.items.consumable.potion_minor", 1), [("common.items.crafting_ing.empty_vial", 1), ("common.items.food.apple", 4), ("common.items.crafting_ing.honey", 1)]), - "potion_m": (("common.items.consumable.potion_med", 1), [("common.items.consumable.potion_minor", 2), ("common.items.ore.veloritefrag", 4)]), - "collar_basic": (("common.items.utility.collar", 1), [("common.items.crafting_ing.leather_scraps", 5), ("common.items.crafting_ing.shiny_gem", 1)]), - "bomb_coconut": (("common.items.utility.bomb", 1), [("common.items.crafting_ing.stones", 10), ("common.items.food.coconut", 2), ("common.items.ore.veloritefrag", 2), ("common.items.crafting_tools.mortar_pestle", 0)]), - // Firework - "firework_blue": (("common.items.utility.firework_blue", 1), [("common.items.crafting_ing.twigs", 1), ("common.items.crafting_ing.stones", 1), ("common.items.food.coconut", 1), ("common.items.ore.veloritefrag", 1), ("common.items.crafting_tools.mortar_pestle", 0)]), - "firework_green": (("common.items.utility.firework_green", 1), [("common.items.crafting_ing.twigs", 1), ("common.items.crafting_ing.stones", 1), ("common.items.food.coconut", 1), ("common.items.ore.veloritefrag", 1), ("common.items.crafting_tools.mortar_pestle", 0)]), - "firework_purple": (("common.items.utility.firework_purple", 1), [("common.items.crafting_ing.twigs", 1), ("common.items.crafting_ing.stones", 1), ("common.items.food.coconut", 1), ("common.items.ore.veloritefrag", 1), ("common.items.crafting_tools.mortar_pestle", 0)]), - "firework_red": (("common.items.utility.firework_red", 1), [("common.items.crafting_ing.twigs", 1), ("common.items.crafting_ing.stones", 1), ("common.items.food.coconut", 1), ("common.items.ore.veloritefrag", 1), ("common.items.crafting_tools.mortar_pestle", 0)]), - "firework_yellow": (("common.items.utility.firework_yellow", 1), [("common.items.crafting_ing.twigs", 1), ("common.items.crafting_ing.stones", 1), ("common.items.food.coconut", 1), ("common.items.ore.veloritefrag", 1), ("common.items.crafting_tools.mortar_pestle", 0)]), - // Food - "apple_shroom_curry": (("common.items.food.apple_mushroom_curry", 1), [("common.items.food.mushroom", 8), ("common.items.food.coconut", 1), ("common.items.food.apple", 4), ("common.items.crafting_tools.mortar_pestle", 0)]), - "apples_stick": (("common.items.food.apple_stick", 1),[("common.items.crafting_ing.twigs", 2), ("common.items.food.apple", 2)]), - "mushroom_stick": (("common.items.food.mushroom_stick", 1),[("common.items.crafting_ing.twigs", 2), ("common.items.food.mushroom", 3)]), - "sunflower_icetea": (("common.items.food.sunflower_icetea", 4),[("common.items.crafting_ing.empty_vial", 1), ("common.items.crafting_ing.icy_fang", 1),("common.items.flowers.sunflower", 4), ("common.items.crafting_ing.honey", 1)]), - // Gliders - "Leaves Glider": (("common.items.glider.glider_leaves", 1),[("common.items.crafting_ing.twigs", 5), ("common.items.crafting_ing.leather_scraps", 5), ("common.items.crafting_ing.cloth_scraps", 5), ("common.items.crafting_ing.shiny_gem", 1), ("common.items.crafting_tools.craftsman_hammer", 0),("common.items.crafting_tools.sewing_set", 0)]), - "Sand Raptor Wings": (("common.items.glider.glider_sandraptor", 1),[("common.items.crafting_ing.raptor_feather", 6), ("common.items.crafting_ing.twigs", 5), ("common.items.crafting_ing.leather_scraps", 5), ("common.items.crafting_ing.cloth_scraps", 5), ("common.items.crafting_ing.shiny_gem", 1), ("common.items.crafting_tools.craftsman_hammer", 0),("common.items.crafting_tools.sewing_set", 0)]), - "Snow Raptor Wings": (("common.items.glider.glider_snowraptor", 1),[("common.items.crafting_ing.raptor_feather", 6), ("common.items.crafting_ing.twigs", 5), ("common.items.crafting_ing.leather_scraps", 5), ("common.items.crafting_ing.cloth_scraps", 5), ("common.items.crafting_ing.icy_fang", 1), ("common.items.crafting_ing.shiny_gem", 1), ("common.items.crafting_tools.craftsman_hammer", 0),("common.items.crafting_tools.sewing_set", 0)]), - "Wood Raptor Wings": (("common.items.glider.glider_woodraptor", 1),[("common.items.crafting_ing.raptor_feather", 6), ("common.items.crafting_ing.twigs", 15), ("common.items.crafting_ing.leather_scraps", 5), ("common.items.crafting_ing.cloth_scraps", 5), ("common.items.crafting_ing.shiny_gem", 1), ("common.items.crafting_tools.craftsman_hammer", 0),("common.items.crafting_tools.sewing_set", 0)]), - // Weapons - "velorite_sceptre": (("common.items.weapons.sceptre.sceptre_velorite_0", 1),[("common.items.crafting_ing.twigs", 20), ("common.items.ore.veloritefrag", 10), ("common.items.crafting_ing.shiny_gem", 4), ("common.items.crafting_tools.craftsman_hammer", 0)]), - // Enhanced starting weapons - "better bow": (("common.items.weapons.bow.wood_shortbow-0", 1), [("common.items.crafting_ing.leather_scraps", 8),("common.items.crafting_ing.twigs", 6), ("common.items.crafting_ing.stones", 0)]), - "better sword": (("common.items.weapons.sword.wood_sword", 1), [("common.items.crafting_ing.leather_scraps", 4),("common.items.crafting_ing.twigs", 10), ("common.items.ore.veloritefrag", 1), ("common.items.crafting_ing.stones", 0)]), - // Adventurer/Beginner Leather Set - "adventure back": (("common.items.armor.back.leather_adventurer", 1),[("common.items.crafting_ing.leather_scraps", 4)]), - "adventure belt": (("common.items.armor.belt.leather_adventurer", 1),[("common.items.crafting_ing.leather_scraps", 2)]), - "adventure chest": (("common.items.armor.chest.leather_adventurer", 1),[("common.items.crafting_ing.leather_scraps", 12)]), - "adventure feet": (("common.items.armor.foot.leather_adventurer", 1),[("common.items.crafting_ing.leather_scraps", 3)]), - "adventure hands": (("common.items.armor.hand.leather_adventurer", 1),[("common.items.crafting_ing.leather_scraps", 4)]), - "adventure pants": (("common.items.armor.pants.leather_adventurer", 1),[("common.items.crafting_ing.leather_scraps", 8)]), - "adventure shoulder": (("common.items.armor.shoulder.leather_adventurer", 1),[("common.items.crafting_ing.leather_scraps", 12)]), + "crafting_hammer": ( + ("common.items.crafting_tools.craftsman_hammer", 1), + [ + ("common.items.crafting_ing.twigs", 6), + ("common.items.crafting_ing.stones", 6), + ], + ), + "mortar_pestle": ( + ("common.items.crafting_tools.mortar_pestle", 1), + [ + ("common.items.crafting_ing.stones", 6), + ("common.items.food.coconut", 1), + ("common.items.crafting_tools.craftsman_hammer", 0), + ], + ), + "sewing_set": ( + ("common.items.crafting_tools.sewing_set", 1), + [ + ("common.items.crafting_ing.leather_scraps", 2), + ("common.items.crafting_ing.twigs", 4), + ("common.items.crafting_ing.stones", 2), + ], + ), + "velorite_frag": ( + ("common.items.ore.veloritefrag", 2), + [ + ("common.items.ore.velorite", 1), + ("common.items.crafting_tools.craftsman_hammer", 0), + ], + ), + "potion_s": ( + ("common.items.consumable.potion_minor", 1), + [ + ("common.items.crafting_ing.empty_vial", 1), + ("common.items.food.apple", 4), + ("common.items.crafting_ing.honey", 1), + ], + ), + "potion_m": ( + ("common.items.consumable.potion_med", 1), + [ + ("common.items.consumable.potion_minor", 2), + ("common.items.ore.veloritefrag", 4), + ], + ), + "collar_basic": ( + ("common.items.utility.collar", 1), + [ + ("common.items.crafting_ing.leather_scraps", 5), + ("common.items.crafting_ing.shiny_gem", 1), + ], + ), + "bomb_coconut": ( + ("common.items.utility.bomb", 1), + [ + ("common.items.crafting_ing.stones", 10), + ("common.items.food.coconut", 2), + ("common.items.ore.veloritefrag", 2), + ("common.items.crafting_tools.mortar_pestle", 0), + ], + ), + "firework_blue": ( + ("common.items.utility.firework_blue", 1), + [ + ("common.items.crafting_ing.twigs", 1), + ("common.items.crafting_ing.stones", 1), + ("common.items.food.coconut", 1), + ("common.items.ore.veloritefrag", 1), + ("common.items.crafting_tools.mortar_pestle", 0), + ], + ), + "firework_green": ( + ("common.items.utility.firework_green", 1), + [ + ("common.items.crafting_ing.twigs", 1), + ("common.items.crafting_ing.stones", 1), + ("common.items.food.coconut", 1), + ("common.items.ore.veloritefrag", 1), + ("common.items.crafting_tools.mortar_pestle", 0), + ], + ), + "firework_purple": ( + ("common.items.utility.firework_purple", 1), + [ + ("common.items.crafting_ing.twigs", 1), + ("common.items.crafting_ing.stones", 1), + ("common.items.food.coconut", 1), + ("common.items.ore.veloritefrag", 1), + ("common.items.crafting_tools.mortar_pestle", 0), + ], + ), + "firework_red": ( + ("common.items.utility.firework_red", 1), + [ + ("common.items.crafting_ing.twigs", 1), + ("common.items.crafting_ing.stones", 1), + ("common.items.food.coconut", 1), + ("common.items.ore.veloritefrag", 1), + ("common.items.crafting_tools.mortar_pestle", 0), + ], + ), + "firework_yellow": ( + ("common.items.utility.firework_yellow", 1), + [ + ("common.items.crafting_ing.twigs", 1), + ("common.items.crafting_ing.stones", 1), + ("common.items.food.coconut", 1), + ("common.items.ore.veloritefrag", 1), + ("common.items.crafting_tools.mortar_pestle", 0), + ], + ), + "apple_shroom_curry": ( + ("common.items.food.apple_mushroom_curry", 1), + [ + ("common.items.food.mushroom", 8), + ("common.items.food.coconut", 1), + ("common.items.food.apple", 4), + ("common.items.crafting_tools.mortar_pestle", 0), + ], + ), + "apples_stick": ( + ("common.items.food.apple_stick", 1), + [("common.items.crafting_ing.twigs", 2), ("common.items.food.apple", 2)], + ), + "mushroom_stick": ( + ("common.items.food.mushroom_stick", 1), + [ + ("common.items.crafting_ing.twigs", 2), + ("common.items.food.mushroom", 3), + ], + ), + "sunflower_icetea": ( + ("common.items.food.sunflower_icetea", 4), + [ + ("common.items.crafting_ing.empty_vial", 1), + ("common.items.crafting_ing.icy_fang", 1), + ("common.items.flowers.sunflower", 4), + ("common.items.crafting_ing.honey", 1), + ], + ), + "Leaves Glider": ( + ("common.items.glider.glider_leaves", 1), + [ + ("common.items.crafting_ing.twigs", 5), + ("common.items.crafting_ing.leather_scraps", 5), + ("common.items.crafting_ing.cloth_scraps", 5), + ("common.items.crafting_ing.shiny_gem", 1), + ("common.items.crafting_tools.craftsman_hammer", 0), + ("common.items.crafting_tools.sewing_set", 0), + ], + ), + "Sand Raptor Wings": ( + ("common.items.glider.glider_sandraptor", 1), + [ + ("common.items.crafting_ing.raptor_feather", 6), + ("common.items.crafting_ing.twigs", 5), + ("common.items.crafting_ing.leather_scraps", 5), + ("common.items.crafting_ing.cloth_scraps", 5), + ("common.items.crafting_ing.shiny_gem", 1), + ("common.items.crafting_tools.craftsman_hammer", 0), + ("common.items.crafting_tools.sewing_set", 0), + ], + ), + "Snow Raptor Wings": ( + ("common.items.glider.glider_snowraptor", 1), + [ + ("common.items.crafting_ing.raptor_feather", 6), + ("common.items.crafting_ing.twigs", 5), + ("common.items.crafting_ing.leather_scraps", 5), + ("common.items.crafting_ing.cloth_scraps", 5), + ("common.items.crafting_ing.icy_fang", 1), + ("common.items.crafting_ing.shiny_gem", 1), + ("common.items.crafting_tools.craftsman_hammer", 0), + ("common.items.crafting_tools.sewing_set", 0), + ], + ), + "Wood Raptor Wings": ( + ("common.items.glider.glider_woodraptor", 1), + [ + ("common.items.crafting_ing.raptor_feather", 6), + ("common.items.crafting_ing.twigs", 15), + ("common.items.crafting_ing.leather_scraps", 5), + ("common.items.crafting_ing.cloth_scraps", 5), + ("common.items.crafting_ing.shiny_gem", 1), + ("common.items.crafting_tools.craftsman_hammer", 0), + ("common.items.crafting_tools.sewing_set", 0), + ], + ), + "velorite_sceptre": ( + ("common.items.weapons.sceptre.sceptre_velorite_0", 1), + [ + ("common.items.crafting_ing.twigs", 20), + ("common.items.ore.veloritefrag", 10), + ("common.items.crafting_ing.shiny_gem", 4), + ("common.items.crafting_tools.craftsman_hammer", 0), + ], + ), + "better bow": ( + ("common.items.weapons.bow.wood_shortbow-0", 1), + [ + ("common.items.crafting_ing.leather_scraps", 8), + ("common.items.crafting_ing.twigs", 6), + ("common.items.crafting_ing.stones", 0), + ], + ), + "better sword": ( + ("common.items.weapons.sword.wood_sword", 1), + [ + ("common.items.crafting_ing.leather_scraps", 4), + ("common.items.crafting_ing.twigs", 10), + ("common.items.ore.veloritefrag", 1), + ("common.items.crafting_ing.stones", 0), + ], + ), + "adventure back": ( + ("common.items.armor.back.leather_adventurer", 1), + [("common.items.crafting_ing.leather_scraps", 4)], + ), + "adventure belt": ( + ("common.items.armor.belt.leather_adventurer", 1), + [("common.items.crafting_ing.leather_scraps", 2)], + ), + "adventure chest": ( + ("common.items.armor.chest.leather_adventurer", 1), + [("common.items.crafting_ing.leather_scraps", 12)], + ), + "adventure feet": ( + ("common.items.armor.foot.leather_adventurer", 1), + [("common.items.crafting_ing.leather_scraps", 3)], + ), + "adventure hands": ( + ("common.items.armor.hand.leather_adventurer", 1), + [("common.items.crafting_ing.leather_scraps", 4)], + ), + "adventure pants": ( + ("common.items.armor.pants.leather_adventurer", 1), + [("common.items.crafting_ing.leather_scraps", 8)], + ), + "adventure shoulder": ( + ("common.items.armor.shoulder.leather_adventurer", 1), + [("common.items.crafting_ing.leather_scraps", 12)], + ), + "red cloth": ( + ("common.items.crafting_ing.cloth_scraps_red", 1), + [ + ("common.items.crafting_ing.cloth_scraps", 1), + ("common.items.flowers.red", 1), + ("common.items.crafting_tools.mortar_pestle", 0), + ], + ), + "tiny red pouch": ( + ("common.items.armor.bag.tiny_red_pouch", 1), + [ + ("common.items.crafting_ing.cloth_scraps_red", 3), + ("common.items.crafting_tools.sewing_set", 0), + ], + ), + "tiny leather pouch": ( + ("common.items.armor.bag.tiny_leather_pouch", 1), + [ + ("common.items.crafting_ing.leather_scraps", 6), + ("common.items.crafting_tools.sewing_set", 0), + ], + ), + "knitted red pouch": ( + ("common.items.armor.bag.knitted_red_pouch", 1), + [ + ("common.items.crafting_ing.cloth_scraps_red", 3), + ("common.items.armor.bag.tiny_red_pouch", 2), + ("common.items.crafting_tools.sewing_set", 0), + ], + ), + "woven red bag": ( + ("common.items.armor.bag.woven_red_bag", 1), + [ + ("common.items.crafting_ing.cloth_scraps_red", 6), + ("common.items.armor.bag.knitted_red_pouch", 1), + ("common.items.crafting_tools.sewing_set", 0), + ], + ), + "traveler backpack": ( + ("common.items.armor.back.backpack_0", 1), + [ + ("common.items.crafting_ing.shiny_gem", 2), + ("common.items.crafting_ing.twigs", 2), + ("common.items.crafting_ing.cloth_scraps", 3), + ("common.items.crafting_ing.leather_scraps", 3), + ("common.items.armor.bag.tiny_leather_pouch", 2), + ("common.items.crafting_tools.sewing_set", 0), + ], + ), + "sturdy red backpack": ( + ("common.items.armor.bag.sturdy_red_backpack", 1), + [ + ("common.items.crafting_ing.shiny_gem", 2), + ("common.items.crafting_ing.cloth_scraps_red", 3), + ("common.items.armor.bag.woven_red_bag", 1), + ("common.items.crafting_tools.sewing_set", 0), + ], + ), + "troll hide pack": ( + ("common.items.armor.bag.troll_hide_pack", 1), + [ + ("common.items.crafting_ing.leather_troll", 10), + ("common.items.crafting_ing.leather_scraps", 10), + ("common.items.crafting_ing.shiny_gem", 1), + ("common.items.crafting_tools.sewing_set", 0), + ], + ), + "Mindflayer Spellbag": ( + ("common.items.armor.bag.mindflayer_spellbag", 1), + [ + ("common.items.crafting_ing.mindflayer_bag_damaged", 1), + ("common.items.crafting_ing.leather_scraps", 10), + ("common.items.crafting_ing.shiny_gem", 4), + ("common.items.ore.veloritefrag", 10), + ("common.items.crafting_tools.sewing_set", 0), + ], + ), } diff --git a/assets/voxygen/element/bag/bot.vox b/assets/voxygen/element/bag/bot.vox deleted file mode 100644 index 4ebd7c9df71aa2a44fff74027e8183b745d5ec0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45431 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 I*Nw0L0RU)bIsgCw diff --git a/assets/voxygen/element/bag/mid.vox b/assets/voxygen/element/bag/mid.vox deleted file mode 100644 index b3d7bb8bdc55003d9fa9e59ecdd219db95402cad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44440 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 diff --git a/assets/voxygen/element/bag/slot.vox b/assets/voxygen/element/bag/slot.vox deleted file mode 100644 index f4644fd9604c670cfb498390ec43b48b7ae61d6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57163 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 diff --git a/assets/voxygen/element/bag/top.vox b/assets/voxygen/element/bag/top.vox deleted file mode 100644 index 4c139b37577004cecd6ea6fbf43f8046138f9a98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47687 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 RwCudF^|TL&%!!2f@qcNqxN-mh diff --git a/assets/voxygen/element/buttons/inv_collapse.png b/assets/voxygen/element/buttons/inv_collapse.png new file mode 100644 index 0000000000000000000000000000000000000000..ab2be9777ccd1e2d629d6fffc759acd271a226da GIT binary patch literal 1691 zcmcIlJB-{!7x11(d>osFyIc`vJTu<4ZtXF) z?{@bjgp`Vh6d^=`;1CFfh9X4-5())XfCMBe=#z*78VZC2RT%GjO@#8e21{elc)t1m z?|=CB8!OAt&m4H@fFKAnt)|oF&m(+1xbFdezve$Y;E(-r^K>c*2M_0CkMRD^tRU?D zF6gXfYwk%4v#^2)^Jrxl#vCmO$LEJJ!W%RbJ=zT-Tl)R0uO%@cwzR6dz>OQU7c{pM zdTM*QgSR)ZNu>Fw#p6SZ6NEHF;xOz-sWr5vF|Wnv`D;ZI$B=BpmKJh@;+nf6HdsQ% zS_Q}$04UbYidxe&!}X`{3V z6Azd&M6se)K@I$k+odsBxgNU#} zdMsB|9H0)$s4eALlT|1K*ij8j)hu8>2~-P!3Do6vKv0Hu2WBMjN4tT#uGNat3`Ll> z99!a|DnURjs1m3{!Qz{z>5Z$1**|l0Si^%+CfWp$lCe z>H8L!ayRiQ+PaVQT_yg1lwO1t_fXWOym2a0?u?SRm`PHU|K@AFcUwD}!g-I&r)6{H zi?Th_h*KxL`7T{Nv?=C}B-Tes#L&!FqCZF_Hjc=p7fC*5D)T-Z9le(0N- zV~09d9{GFr0y?A9*$+N-_Ub<*?N?v_vHi)-_d4J2KfTnxA^n_qbB~%Y9lo@)cH-)_ ziR MT3mK6K701uKTfC`@c;k- literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/buttons/inv_collapse_hover.png b/assets/voxygen/element/buttons/inv_collapse_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..6ba5c25ccd40997f0b4a939e7d2cc14bd347eb11 GIT binary patch literal 1691 zcmcIlOKjXk7N+Aaxl>i}R93hCx*yG1~ZIew1yMbM$4$*c+Q^cX;nend4+GA`d zyPFFF38WM#m0IHrwaDj3!>Vclhr4kZ{GT!x?3bi2@ER8+m`R4n- z|KZ;|Ypbu%9D4eYAP6(9W~0rY&+_r;!2|sMga6EcKOTykCsILp`0;$~6F$H2q#*3S z5_HzHb@!-)StujIJSq>vn4<+@esLHhyg@V3qun5?Nw>fKP7(uBla5zh=*D&03!2*r zJ+{5t!P^_yCeq@9I6rhaK}a(s4#R$wI>VYY=5_cyf2~O37?N$&q@~=TxbCirb(T=k zlp(+nYGT!vRnssm>m^Zx8dP9aQ6bPY2bxYr7mG{c))L}7?Z#_`ExxKry)26zMHvhR z@<5ka(p6O3wiT!;ng%!mq@yT9LlC92Q-%gjaT3H?z#=hcL>}ABY7$pEaUqOL+9)l; z!~>=bQLLyk%w3uSU3YhA7?$WXTi)VU3cdFwPCKKRDs7sw%>>isEgEIBQ)2{|XXDMJ zKlYShh4yL4VJR1;mZst!%UIfD5AdVhF1?AJdO}ggk`812X`j*S2JhlUNnQ0 zr*2eyAIIps#jGEQ**($)CVd=4S(x(4{Vq z^nVtY@*wdk+S*9r)y_{P)pX MUTs`__2dVC1Hqvi!TY$@c2XYcofmIw}alOsm;w!}%FF9zF69{D1F1J%H20ar0a%2*)1Jk3+)8*Pj%G z!#9J@TDIn#wkQu3;&GQL!!QQ4Ae>ql#)Pi3OmtZ{h-~SPufCSVz_X>ZRR=k7o%Mp| zR>IC~t#s(tIyF6MVO~5nw16OF84-tJKT55kEsc3CSm(D@NgPA6bz54@1Bz?Ts#xa< z6Ke{RDMDC0X)0Px*A3%E5hILLbW+ujjIo7kR<$A)KMA5Go^Q1quNJZ3%9eUr7F()1 z7z~s_Md3+T)lAb=5mqsl0U@WOC?i8TN@u4G4VKa*h_ir4V$MihzLD7yD4nDb#wBf( z7G;8hsY4R0nu79_ra;Hp9~y=wI?a|gAxfe5zQk!~6f?EWQofN;wzSEjY<3#VqvhUs zBk7Mb^{C4FECg5z!n86}+~XNfd;9@;l#k17Vym7olJTU&d4Jld)hUaJv4X@G&IXak z2kBg{sMx?7BxAOecTLu?jBrOYEUjv3#tEcZ2u+|4_`o9>IT)CGfj>G3)N!mb;f+Xs+r6f>w^6aPB-%M<@+bL^*9BN zvO*U22%^5B$tE?ca?Q|z(!e#js#9hn=2i$MQ?}QW0NxzZzn3*v6C$By6z@ zmHtn2sSgsLlFj|4AFT2Jqx7Pzv`3;YgTbjvc`|A~VkS*d|J|?gvwOzT6b>^kUzWoa zF3R!DBA`xS`2M;x{}&AIQLC}oY0l6;_x2X*FO%EvZn>8~Ilp}OwDbGL#m!4^KJx9} zj<$OI${l+7gVC+4q<`(P<3FR_*4=04m$xs`w*0&|*V_JYZFl>Nt9Lri&BT?TJMq%( zFRwqb{M`>Xe!F(%?YX7HN0K*YZ?!KjD{sC3P4C=CKmGFFr#pv^?f!T{Purj6pO4nk LO5^ia&R_T&y0jbp literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/buttons/inv_expand.png b/assets/voxygen/element/buttons/inv_expand.png new file mode 100644 index 0000000000000000000000000000000000000000..d4dcdf4c3dc3498670e97707f4bbe1296395c4f1 GIT binary patch literal 1934 zcmcIlU1%Id9ABXgNhJ@qV2hM?*@9YicV}lmZg-nArgyP->cx;ABtGff?Cjl=yPfUs zCU*%!r4TSi6qPX8gbt2LwSFFPDmQ&>n;LvC$Fu{@@>NLo*hYUP}bw(L>q$i15j!BZ4q; zJ*Y0E3zb(~%3BKYc#SEYRs?84n3(KDgf6mFtg(6!=A}QkZb)L_<)wMEf+|sgHGbv$YEd2yoS0zr$VMC`PhVd8f3QkU0-em1O1Vi%Gw=B263ptw+(6$?CO zqNO02B80`9qiB|4*!FWGMi{9mr)o&X*hQ9W>SF(qz*_A2?p*Q3zAgC6ON}&*Tvcti z+e%wkcwARC$8l7IRg7go$jM5Wl8ziECk6~fme4qe(tw9z#z<;>Da}iuwC6%A8q$VI zKTHrXwL>CRQ&8s809dKqAKGdS(MdYJ3|9JjcO*`#D-l!YSi+ZL%BGiDn4TCI^XPDG zv=leHo_bVeP1XWf0>ZSRsi?tIo;3I_ehjCF-b8L8W+dftmGkBxQL_UU5n~03PtOOT z$J@!tOi_OUE0UDur7UZ*hGm4Sn(b%Ei0{qLd);TnAYc+gOtgs_AkL8=7oWvj!ig6Q9^MFK2Xh!TKVnOIi4` z^}Vw7I0cSjgk00gc{XDjxCcbkH-HFXD*KLsZH*zK=ga}yt8oB3hcvgdW~w}3#6;I? z*uZklVnk-l$jLRs_hqE(nrZojnO;s3J?i>AZV{-HphfCTjl#Ml{;Q_In>?;?4`Qxw zA1@aQvoZIBCQKxAXHJXd>4K#>mL+3F%gP>PfV-t2fgG>&H_};luHS10Vz)Y7LbFYg zmuOZE%#-@V!46^Gn?pU?c7p-weX67fOSqr5NzA6|kdg;hp}L#+ge*Tmy5?aM+rDg> zwjpC166@HI5N6snhH@5h2pzcozew+gl{QFNXK=!*(!)Emw{qV-5BF@qhsndv&ZybJ z+N&`2zs1*`+ja;;BD+IyvS;1ll>$G*ONoULrx>oB4?aD94X&%@a&f9!y8X+YtK)CI zFuF#bytb7;dUOAkZ_XW@zZL!Q;l|j_&F?PVdj0%m;q8xuuP*9`zP|kO$uHKO<8L;9 zd}(d}#NS(^$1a@u`KkAQMR$vv_P)i&zV`Qzzw^8D(}wiz*yiVl&#&FSxc=F1&%8U+ i`0M?T9QE#<$?L-5Qycip5%XYnW0t39idRm*ap51g(PT&f literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/buttons/inv_expand_hover.png b/assets/voxygen/element/buttons/inv_expand_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..7fca1eca8ff2c184bcadbceea9ea3adfcf49cf83 GIT binary patch literal 1934 zcmcIlUx?g99FC%;*LuiNOXU!aDTs$oGMQxaua};8yXWnyInV1tPY`sHOm^dKGBL^B z?j1r!JoFSQD1A`+Qu|^btp7lSLRCZxzG}e-3w=@F^vOPnIN5(*<*q#x7qXemeDlrs z`~AL|*<6^P8{Id)kK?$}TD7vs-uJR+Z0{cS|7~zjo4rPo>eCs=?LSaFw{Y*CzMJFr zTny_=`I7U5<|;R}HeeOowQa-frxNi(dj zr{viBeBE1L_Do-xI?7LWEJhHK9P^#16=zn*7P`C^YZu*;z;_|}vMo#(3-U|O0$-*n z;WZHg4?@K2rYLKwY8Vgm2qGv!U6LU{$byvIvWi20+KTF*J&X=q#UIWvle{ZcCii*AgNvl8mmT9+_PwaejEPnC}he zCM#*H8>#O}q(veI%a|~ExKz@lInA1Mhd74A!)Ow#oD!VVv`%SjP^g6g3y+Wp`3H}O zu}|CCM4_lZfmCo#Y@w(ckP(2WE*qAtTF`t5$`*t@sKb2V;~Z}XMy910*MX@Y25Y;4 zI*wI~vmD1Bsa0%&i4w!mw~zv56%zyah8qCQPy=8fVglVmfvZDg8nV~b#pYM2w^F39 znBTLlPd&yl%#cN(VQR9A0ns%DD4J>j0|zPy7@MM-23BNa!1iPsvYmrlTUiTLexV90 zii=bPT#Ya?qUyj^g8)E9kxN>DNy*m*-uJA4rV(a72_xJfQW7@={%5t1(7rBW)&!Z#DXr#C?YqRr}^E1!A{1;h#WSam0 literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/buttons/inv_expand_press.png b/assets/voxygen/element/buttons/inv_expand_press.png new file mode 100644 index 0000000000000000000000000000000000000000..40e3da0a5f07294e4661f0056cf1495c6371357b GIT binary patch literal 1932 zcmcIlONbmr7;Yg1U3bNp;3K}84r0{QboFz(+ijQKnSG4do88U8u1YY~Rn;@iOn0^2 zy*slDg5)9!9z_xYUOWlDZjl8=5Q0Gu9)uhef~O#e9#qtWSUoQ{vYRXc2YR}?>ifU{ z`yW;H`qJY3#Msd>K@cWt3)L0=euO`VZa>KXF9#2G`0I{j;rUDu?mS#Pw+ZJj+%E_R zKMm_^`I`5fgIQEUg!#18jS`L)gvqIHf^duGqE8!P>`Fgxev*e$Z6ZxQBTjZ5P7u)?iQT9jXHM6Zdb|!F7t^vN_8@u7m8J`a;+nT4R#-|! zvjhQ#P!-E|NilWZvQCI9RG|#ZvI2psI?!|sO&t6r?ky#Ovr?TK_~Ku#)Xek5k>yUO zQ|f3XmNsO?wrv@zvZ?}(0NG}oqb`WEV?%~2&2SnfdB|e1U_?G!&s~Wt?S~L0Bic9{ zq=_d??xI9iO0WoN2=u%gL!)Sf&hps}?q#5NSK_R`nNWF!W^6shbasQr`LUrhf=9N= zdfM(qO0Z1ZG~%#~3sXj}k|xVp)?|CcFqMW65#@fR|Ee%;jRV_jB_-Ysv z*2$hI6b%e$73I{GimCxc1yHRkmZRtnw4Z{C17RQP@jf6ZN4tU5vIF(&z>I{!=6;}_ z=hWgXM=_?gsw;6(r7$E8wSC{fx&=(^>p(Yj6Ci`=Krw7w4$7Ku2j!kFZeL}1y+~hS z-)~#OFy|O$$RWh`iLYT`nt=j*Rab#Uu?EUE!au0R9Ks(9wR*G+Rph} zrLvT=AZ+u6Y-N5%tj$(T#Wqc#mXxCHxeOleLYVOyZw@xnDf;qY)DFd7cRC0co5GcF z(G8SHgXwUWH198pdNBKI_r2?I-3GjsI_w^dTpXZ;GGn#c*_`@Q=}@L}Q+(lwUcM+rM4o*A?Hg)AfZ*-+%k@#H(<; zx_q+ww!8M2u?_EPJzyWdXJ+j8)9A<>FHL>&5qR&(*8AtrotrrQZ0+l>{ygKp`_|Zl z7uo9g;^z-rDAARSAFTOc@^ywGBEAGqM>|*tUnKNhq0mSKJ9smFU literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/buttons/key_button.png b/assets/voxygen/element/buttons/key_button.png new file mode 100644 index 0000000000000000000000000000000000000000..88c2fe2a28f01e5fc6e369cc898b8135c10c01ce GIT binary patch literal 1632 zcmcIl&x_@f|e(YqHae8zbHe)l>II|#VrBdk*(@ABM z%yiG3#DlVe|A2V$s<;Oavao_4JSgkUlNS;1;>GjgtL}7MWOtm4fl5^+Uw!X;Kk{<3 zzx~GY((_A-qAd5ex;rpGBgciu;QsN=7nWhVm~UM#73J|2InF7ceEOWCJo-a2*su13 zYaSD+Mk5h&ZJOqQR+QDN)10ycUZIc=lg!6|-uV`zB=YfIJ0L;6!AHr~Nx^qdwg>Fw zfVmOAx`tM#9uTCwqG+0qv(lUTc*g6&T0Yh>nn9`qAFoS;Xg}zq4N-8^(TK_jF_G<}+h3X4~wIYhWo$l+kE!g>ZR8_gB>yybuo3u1h40XeG zU7eV^X{vxw%j2w~Q#C6uFBrPKWJQu!iO7&-q@g&hd<;tGE~I%wo0WB#AYl5G=DMK~ z>Cyri1gAsOv_Y5E<`Gz_^*)fe931Cd-{GY=EEwNB;#qZhVJu?J+5E5=&peHo&c{3j zSP8<6##BBMl_*EzA$~Nk8*g%Nqu{g>#XyMhB2oPX3o=cOpqKWNEE1FQWvQq>z`L~K zK9*Ti4O1o7z_7iR>9ws_h~W`3hXzmw5v}Oiz#>ZGMT2@O9U6ZPj6H>z3mJu241>2iN0$&at-_I&lMZgG>Y-*YvH4dFvwYg=e)V6J) z4b6@bGsB@4MiKL3QKS^=BuVLz>v=ZB=pQv3Vl0Y4L=f|qtY%;bFSe2rQg>W`AG`cU zy&5NIR&*X^vetdfWRiJ=>&M9hqIWi(bK1#>1L-NrpTkNKR})(B^&v$1KZ{F$nD~+& zoi6=siT@v^*I{KNnhiNLP8~~UblGC&NzwnEui3l%+R*|IJx(s0<_eppJ@X8x3uwNd ze|Yy7XxwYP?)t#F`^Qh8om0>g-+%WGZr!`LvieAV``NuqD>uISa`&Sjzy9m|wc+_E vo!{OQPhaSNaO>x%Uc7T<>0pmkrSxSG3W0`Zq|vNauV!nUUDvx%xyD(r(^E&I(XQ}Hqe`-O z?L!ZR7Ef_W?&w-F0ddsCh?L&1_+ckkMXf>Mo=9};L`+YNW zcYSSj;o?gdB}rQ7v|AhSey)7Z&%^)0-NglXT}az^a!GpTO8K0T?tl7%B+Y*nb+?Kw z?=6>!SfzpRxjKqdKuglirBO=RHZNqK_oAesJo@r$MUH}ovRU`Amo|AnYVT+K_WoLz z?Qb(DP?p}1Z;o6bhDFuV^rygTSrED#tv9D@!B<1+7e(r7+Hg2jhc#7X zJxzBUN5e!T1OWo&dr3h@D9NwR7+O4MS(Fx$NaT`{`eLVOD4=u_LY!8#Nj^;zBupF8 zRMS;lhBO0u-pSB7uF!e0vJ0zB^&Ux_clT1RZSY*|WQ?!u@}#&rTP$GJ+;k@!j3W)0 z#s@qGSPsJUYN@m@3X%84F>zGGRWzyF%s4GX))itfE7baoMJ7bW@~fLs5{O}by;L-v zz+1H74W+Ca(h0&wSGU|6aqGrwSa&g=Kt1q5KnwaWFu^XdTtoi{SOihHcN(bYxt%00 zXu^1>)lfi`8byIyvn&&1$3ivY)R0*>2y$w^gG|e;*?~p4X&GZ(Fux_(PMN-P{=~L{ zV8Bsj$YqA<)J?EazzhpwGy>#X*hji<8=*-V;f6h9dpnC@=g`4n)>2has~2{BRV z+aa>Jp(AQp7BYOFn0m-`n}VuLWVAm3QMC3jYurtX*~BC1~69%Mkc?I?#D?@c$-ZT{Z0HHhS~J6+1kP0>(T=>{Gs z)9&b~G@mM<3GJ}Yf%F8IOklYPiy_VUat})KpDxsn6Q9%Flcf{84&@*WQLqBi4Wo`6 z6F!-*+uShB(4f{V^#4WrG_9;plOBf?R#TonnA6$)m^wVU0iUK0M+c*o2kXRP+LPuR zj~zOMp^)P)IN8f)b)~>Zbt&-#@|3}qb1>Ka4z8K9etGtuwfO#z f=U%?@@b~!-J0HzG9-4Ff(R-8tA literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/frames/prompt_dialog_bot.png b/assets/voxygen/element/frames/prompt_dialog_bot.png new file mode 100644 index 0000000000000000000000000000000000000000..63ebcd40cfa314139da5fd72ba3ee1dcbc491ab2 GIT binary patch literal 1947 zcmcIlOKclO7~V#u3ZXuraEN+ZZ6p*_W*@WOr|MRz9TH1yh*G13TgEf9_A1_;?d~RC z=h6Zq5;qRvQgJEb+#?)7MI5;z4u~TXN5lakICjQfyJ?k%2&tCsd42yk-~X7IPq#ML zYYW#FBuT2ZH(J}0bm5-C{R* z*T*bY5n)}bmT}6_l62!%nIgPT3%N^sQPNQU{PufAjz~kw6vo3tNo9A)&*(Pjr9 z?PHH9w{FTeN}m(Nv_Nth50cz38_Jm1=Y27)De@Rn>^GEEv7x*hY{^ZQQQ1~?fOXxJ z9Zxmvx&@uLWvD}4Gx*m6hU3>=--L4YQTT2d3H|NXJJnu%)KL0Gk@}i891hi?sj{r6 z8J_29I@BNp90BrCQlJtf`SO&ZMRS}*X%VqR7L2IN4vL1tl}=oU(;01&S7G7-(@KuyuC@NUiVQer>)Yg;RXhK z2#9516H#0bsXKN3rAV*Biu)+(QGUZ}%KvxfVT9?m#`7tMol`ZkytJ?D%jF4jqf zY0nqmc`1#@Ml6>{@ z4<{#&9^bn*zwinE?E2RqF7CA7{IU3FuDpA5`*?0?VQ=OBL#b+xyJ8?_t7dQI%kgk; yrD~pHrzUe_g5$Zg_WRQP_0PY$`pUodNp11B@9ur`VAK}RXM1h4_2~BAM^6Cad0Phn literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/frames/prompt_dialog_mid.png b/assets/voxygen/element/frames/prompt_dialog_mid.png new file mode 100644 index 0000000000000000000000000000000000000000..3cae737d584475e330fc62cca55bcd301cb6a624 GIT binary patch literal 2079 zcmcIl&u`pB6m}#Opior^4ipuz+yg~r?D6=wm2H*mrpZb;3F%4=w~lAVyH?g7V>{VR zE=b(?4-i7)&dT(dz`nC72 zDT;Evzuntcl$Cd2z4F>BTtB?^+3)c8da?a^r6_N_-TYRRZ@+t2QC|Ha9URmL(WfDk znNGQkg+9#+uvV0{`_qE5Ls6@-7^Zng`}5~tG&SWN?U57VsOXAOx_w%Tho?IOc6!JH zuHC<p? z*S3BC6O~|s4eS{vMudde4IN9Je>Ct`@+92vJ(&A~yN))h>moFa$z-BWEM1mE!wiDJ zz{DT~fd#5g@|sRjUfo_G^hCwVw5U^=s|_NJxKE(D2yT>HnImA5bkzbL5)x5jr34_F<*^SwN+32_1+2L#2wO^^kVpg49Za$~5eO^Hha zx(NM$p*~M58_|3y;0OK5*JtJ`oFH+ML;(U-YJ(~pL4(S;ID=JCI9Uv-q7;NRM%FFW zs1^h$9Y`S%O3((CSeZb<@CX~ibr_+g6bCvS4~7N;3ACaXE&x$tdhbcBxN1WJUMeV3 zjm!y!3xuFLE*^~M6274t+25Zj&*6e!2mqJ@gy|BYLRFF~DOJ*a@<=>RWD{ZvWPw#B z*(FK-c%z(g7MLV40?sKDgS-@0p4Jq_90V(?3Km4`8mc2vf^z9-MsH*&dLp4?2x0*0 z0CT{zDmyr^#EYKV!9X1j#vW8uHpmHfs|5j~Fj3?gH%EC)l!-!3JgrA59mS(&!E8YF zw9(@+aJ1P#dHQt$f2+jRXyw!V2ga6{IKYvp5JS6JPG{g))MPNysGFiZfEP@ahtm@@ zrKfcytBlN@&(2KzTs40;AlI)OpP;z;M$5AXbS%Ce)T`QYaPYmV8>}@V!48g#CjHjfLaF2_o=35 zbYSz}vRkLQA}6}n1xmgq9vQmm^yUtLPuauqo zxh62LWe9ZY(79EwIiWMQ#)T8G!#k_<8WxXkDA`Li)Aihg$_JK{t>>Ay3hSF~D{pM=RH*%@)2p|QurJ#%;bUjfE5<7qKYwoj z?4P9dv*CSBv-2l*jk@Z){$$GK8I7*C&4)j_zRmk(ch6>F(U`5yz86;>XwB+)e^znh z9BehcY5uQ&F1lomwk(7qxno~G7I~un&ZO-8q^UXIrbUNTuC<+@wqWH~qZ`EN%CyUW zx#o_~_I=k`v3zW9!>z^($L-@Yvgb5kxp~I9v4=~(J-pGS+nf2@nvUKJ<;53wo3=Ey zRWeY^h>nBd9fj?6j?qh#mpJR@u5;z&HCGtkZAtHHpLN>=?U-Cu5e*yE-`jsY%e*rq zr!~DUa~M?V^Phe1#+b_GB-8v5)T=Hl+FO)56#8&n#=PVePUzgoHxHzQRy%jDUh&y4 zP;Y8<-nQ0#hr2GE@-L}fDj(@1^OKf<9QGtn&4E&*d&;vOEjHlwz;9IXZfsM=_q%t{ zL%wKfKlm8nd^@vPswg_0xorG3Na=1U?Pk{odQU8Ddv$*AsJX|#I+AkZPT#d%HDybu b=RrTTezU%_aq+|2_uF1rV%?tq!Yh9RZu#Hz literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/icons/bag.png b/assets/voxygen/element/icons/bag.png new file mode 100644 index 0000000000000000000000000000000000000000..7fcc427d6d062437bc91572e6b3ab1a923a77e4a GIT binary patch literal 3154 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 diff --git a/assets/voxygen/element/icons/item_bag_blue.png b/assets/voxygen/element/icons/item_bag_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..120b47ffe2fdc60d3dc510bb8a5c03bc80b10468 GIT binary patch literal 2226 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 Kls=z4we~OEO}CK% literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/icons/item_bag_green_large.png b/assets/voxygen/element/icons/item_bag_green_large.png new file mode 100644 index 0000000000000000000000000000000000000000..4c580970b8f2c1c072e5534ba38f92e3e879fc74 GIT binary patch literal 3180 zcmd5hbmbHR(UeD|Gqb_aK7#+kX@ zy(1!}52wsz|uDaDq8 zz|}dfR;$HpQrz*1oSzq|tnIhL1Oemf*yV&cAG*{aG)P3}j(FvUa)WTROe@q5e4rH~TDRl`j$d-75gon$ zivkF3E*I&T(w0gkQsD=ERdAyfkSVGC{HRO0A?iCL9-)0zFmq+w8$TGLlkuDdJCHY+ zT|Mnl95`Oyamwv154QbfL?JFRt2SA-;ne*5LTxPun!y20v0=&)uOKm*7X@9^bW!Q% zMV;rHq&dg1%+U@~K|~^-7l4k}Wx1OdCP@Kr1`hCy#0Jq#$E#q7mQ}$;%DHxtWuhGD zX{YRXpct%^I`^~b^q}XMRvBLSLmPUTY+pJlXvriJ<3hvSTu#s0et>O4vzZhNzQ!%f z(31usP%#80Oie|mB5O$1k`z&`pkRY&QlcQY`7;g~3Goy1H%rNI2*{W;ub>*(G_in8 ziAYGLvWe8BY9NXgjcC*mdBbQ+d&IM#nsK>PYG{=K5(!p?1WzTT8CXIxkwv7L0!BP3 z5D+8_)R4lOnbh2t(gXhaka53NlfAguT9vJ}*CT!n7ZxBeG0jy|5O-~jpaA0BZ-o|& zw)AeK+ae2?MkVN&gcY7*Nr+Ag5ovB)Q{-n>k!bPPN)&`%1GP;xzGDV8?9rYg-gKgF7!Q(+yNFaO?;R1j~C-Sz(6O?A`_Sf;{F|dGa^n?3`mkFA*{l9P-MVff#(ZIlqJ`1EafI0T;>SNCxO=nKjuhhAF zUGMo*?`?ka(lhsLS1%s_afS5Kh4dGRb7Q+puYI0NANs!7^-c9n<#L`_uEhGjJ$CYxKX3c_Gt2gVy=_l&&)ko9Eq`cU|NbL?tQ$XlYW4Z~6UFy0 oTzKoyg71F%?edN}OFmS%o!hFP?3*~MhyMq%eFK?8y<2wt2@QG{?EnA( literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/icons/item_bag_green_mid.png b/assets/voxygen/element/icons/item_bag_green_mid.png new file mode 100644 index 0000000000000000000000000000000000000000..2fd35db721244c8f4c1caeb2d061733eebe77f1f GIT binary patch literal 3149 zcmcguTZj`^7|yE5YImVh+N$(2&O*_;lR0OunW<5Clhq9vyR0d$;DaY;&LpGBoN;D$ zll4i8ScH~JDO7A}6?%CqltK&J(wjikcJro5RY4zIFoyBPp=CbREAJ0SE7i-jtyd468}{i|<9!E^HBalMSnL4no1>2o1F zcJ63}o;yP=M?A1!*w?l}K$FEpXg3>vWViETN3IR$~UX6B$6thkMkl}+I68Bq+10_a=&G*>XJB!4Bjd=D;q5jKn;w zaNg)=d8Y5Lpx~?`>^|=K4sS(!5^X&ORwOaYi%H5QWJ-!rQLwF8wqou@ij7d0w9FmP zT^u0Ailm?l&e=$VYcImfq<}Yv#AHokhuSU=n*^feHA#)hfnO7aGzWHpH+Tq&!8&#D zeW_5G3AyVv;E!nb@T5?hDj3)@3`xo2j=ANsUGk%t_>`53c@ccgdY)tJI@KJ7=#p;f zL_&s^lX4WPlB+8^R*9`aESrazK?ydHI#zYuMreLoTt6Sg2kTRW9)2dfzYW0 zo6w}f^CC^q$sDow+FKz%>xx8=zg1@-^ggKVYLB=Zw@An)YLLq{t3qDYc4kRWJylq8PJ3%=89UTHYRRrN$<;r=1gU5W|PGzn8=NTdp2 z$syg4a+nZCRltl172EZFAVe5y8&{y7Rm;|(TDyh)UqaNAP3y$3F&InoLpVcSMl_w7 zQk57Onh(??VVWvoVj3Tn%xLHc4wbv%U%NZ)a8< zcIzu2m`Q_kx65=-`E=TEw|C0-w$<))u?vesNvr9s2=QmeKo+pOr_UCupxOx5Ju8|2d^R63j+z{USB|C{X zeYpFZ6HjjZ(RlIt?QLv$=`Oi=zJ6tBZghxT9ujT|#`wuo4d?0eBR5VDA9?)egYoh2 zN51ohk8F71(i@{{=969Mx3RqkzkK%N%@@utZL%^y;icU8*XV~$7swaaXngka`_HtV od;8|0*|%QXalU%{+U;AJ*C*bI{@9`~B@2IP>S*!P$y2ZX4OIH^5dZ)H literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/icons/item_bag_large.png b/assets/voxygen/element/icons/item_bag_large.png new file mode 100644 index 0000000000000000000000000000000000000000..84b38c35d826aef3807605f25a0e89c0cddd9fc7 GIT binary patch literal 2947 zcmcguU5wOJ6fW5bfmMtMnpH`>6fqL)^xpo>v@)=}GrQ~rW?gs3fQcIJ+}_SMJJVa* z?#wJP(U1_8@Sr{jF(y6{6CX^B;tNDjUPv?%4RH_9~l+|VM}?cG|TT><7?AK{{M9I;Gg`q*`Ino6oh-W#n%Sm-S7H9-&wC;KZ@t2F2Mc~>6@rYi56~9R5 z-Ki4+ojWmIB_|e$X^RK;gMA%~8)(u9gHE&Ig;u8^cI{ex9lw@E&=rXm3gTD{2#!`} zK#>I$7+EM02o;bsv&hgi13m>5s6ZL&GEyXDSWvT24kSO3qXo8O&6Xw;EdEpw=cCBC zWVzjLXWMF)1$7yjrYS>3RuqX#Na2zf;f~~myZRPNG$euRM=tX~Y!TPkVpI^hr@auG zernbWlQi*!$sO#=C=26|x`H-I<^08<(FM1OOdGUGy(r|eD3$f+S;WG5wu0&O^DO{8 zwUtVWv8*r6W(pBT6D=M_l8|Mk!|IYx7I$-3KbkO_r8o1r4s|ZxRral7 zKyk!^Dr1d)m1p|#0tICi@Z=HKvspXb6Qd;xw1gvC5aW_dFefQSRY8_wT1sv&R4fR4 zq7`Pl&eDJ=;?k%JRZGcRigJr6=bMcqyr!^CEQbY6%(LY-ah=M(R~JEA1FOgyEa1T0 zJ9Y4VxmcVDnBzA1hj8}bI4DmP4P+XIq-0Uo-ActOdtrnQzC z8u!di&ir;-=3%3=qjx3p@+h!f)0>M7D4f-uxH2@z;W zxW>tnhdL)6;`Ajd=S+@wY%lhK6eZoNSgL7hTEAQWNs1ENWFC8U%C{x?E`rf&wt{sV zONvHR2|Ai8&LnVtmsLwc5^9`DqEpA>)o$v?+WnV*(RW7Ygt^2d!E z2Zqj%oZ7bciQnGeegXRl-)JDhuU@1(Kg@%!dp`|kcX$IoRpePetqEM&fg=-h_P z)yxkd8oK`CW+USWU+dRCyK?Urj&SOo+JHZA>SDd0yz2XXm+EpTYc{3YuEp}93PX*6Vs)S#*dx+8?%_S82|tP literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/icons/item_bag_leather_large.png b/assets/voxygen/element/icons/item_bag_leather_large.png new file mode 100644 index 0000000000000000000000000000000000000000..2ce43a5558b07be85b6009c84ac0e96916f1eded GIT binary patch literal 3152 zcmd5;-D@0G6yLN(V{Ii?!H_84PApX7-MRP9$L@^XhGaJ%OS?5B3ylaickkTU9lAR+ zotb1eeKT6WANpn~f}j}R+6R3QD~L)(&^Pff5XA?Bj8u;&Puq_iUI--TzSh8%SM#dsrN% zobpn%R61!guaTyfSK;Ya!xz-4)Uk<{PuW=>feNoWZch63+Gi4QtekXOE1{B~=QU^U ze83mapDMHSXPIG16UV`^mMH`@ctk;~QFlYLm6O_XO>vGN%MxglL}zo-WULS@mF7X- z3pmiz2r`5ekTKF&S2dD(3J^q)jKx=jIAf|=Qz4-Hk%YFuvdx9UOjnn<%Sp8;@=aN8 zHk;|DlJs(5|(ycX!wa( zH|&N<1WayGU&d(^yVNGMSVHHo1ogIZi^;ss8{CaTL5mYwzve|=So5|KoxJ{w0ukC$ zDbcZ|EsaK^B8;Y2g&W;~Y)KuK*L*H7@X%Wc7@uAhW{&l|38MwR8P8p?3-UIzZ{`C| zBQGd>UcHy)`JTUk;4}e8Pdl#VHNz)kZCwUlpb^hWampdeK%$okHi==9%s3(@LLJhQ zXF2v-A1Nk~pfWa0te6;$BfLo}>dm4N-I3U0rtJj{Dx&2yXqC&pTa`eP12gZ{y+9}y z)+znZi~0O~;Mq=Hya*R&r$BK!uVX{kAxY!5xuud>bi;_cj28x6;2Dgv}Edvo~C@O^p zLlwwuY*Vb;6_U|<(w+$%fo59oml|7T35kqmv4*$;4T~yJWvUQnV+s+gFoPK?=9Us; zW>a%d%B1iw6d4b@n4F^3?y2q^zZ3DtIv!U?L(y53{#}45HqAh zS`o#PMVbz?m{QIZQ8O8V*pBagA;JRNxGeCj7^bTBfc;NG)RoO@)U9$cmgKu|My*&x zV2(nfG6f=ARbVzQe~N9!ES;c=Mz?oH5qLHU`H!4oJ66CBNssFyDU$7jX7Rx3tAYMH zOH2Umiu-r;?O1U-=C)=q3M+a>^h-n4MXysR)C`>wOH>`l|C=Q3=*;dWz~AAs<*?fw ze8f!ZpSvBVTj0}fzn$JG-`Z9?_k~Vb%*3sxy&{-fHPGDxO6&}#7-z|VyOUWF12X3K z?slZbO|b2F-y+z1B(}@=n5?zpCRvQRE80#V*52hkZ=Dk>^GC(PWI6i{yK(d8#NPLZ zFZ{ryKi+xxO*%N|^NmBpV;|Cw$EOC;&;K(1+r*cp1Mbl41Ly)6d2H;B#p^?lFVex8 z%)X6RFCGRXADmcTzxT|(bOG96cT>pCGi+2xg42+I? m=Hc&;WEEf-!|xphkL*pI{PYaJymS@BJAZNdRN>0h^7@~FZ|@%f literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/icons/item_bag_leather_small.png b/assets/voxygen/element/icons/item_bag_leather_small.png new file mode 100644 index 0000000000000000000000000000000000000000..a5317a744eec39d8746a872788a8907420065646 GIT binary patch literal 3110 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 literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/icons/item_bag_red_face.png b/assets/voxygen/element/icons/item_bag_red_face.png new file mode 100644 index 0000000000000000000000000000000000000000..865bc619541480176a6c26e0181f55a87faa6622 GIT binary patch literal 2199 zcmb_eOKjXk7@Bv3)qOW|a>h=*X-p7Gnswn=uwW)Yh%$%>?+5*d%j8!NlEYdhIZ zxKyA@qL&JZ0s+_IBr*QGC$3(yVLWLhuQaVz3?}?41|-1Vvc)sPkQd)PMzM% zaXZg?mAPcD^om7-dImdzO*74U$f7xJwQYp;Q z1#faSqBE;g6|%ZWOh-7dpC4&j44_UE%s1;bKen1#p@nO)`*c_o_*O`=m=(s-g#28o z%;$oL@>)g$M3Q7)H#1OERHVPiBMC_&WQPi%ZYhQ(Bfj$yShmPRd;$%F}v4{<2jba-(j*bA;6c^Y~VVVuq zM6%>)iq@9P=H~;ll(s-RzwHJmAPnOcJNL=ejO<6e%5h{pM0yUs(L;)^T9AHyq0!^mS)(lmmwwbp5t;}7NF{UqOD;{@j zHIA#DyPCIubJ9qsl5AiV$U4P9GZYP&GNVrv=pqVLlR$k$OdZ7_+GgDn%cPnNiAI2g zO$cnH=)fed4P+Hdj%gT%j+H*F7HXTKxV@H^XRJvt)wa@MF+-2^`0y>8SeT^t&k7{% zd+G?CVQ-U_zo!~^<~J571Kq|C?d);jCJh|Xu`275yZTMsihPWhx2KMv?owCPfPqXG zs0wRZ(~)Ihu;$Q^iyV#MuJnHD%%4&PGIQ#ds&)N&Kh!&AAq&{AQnr_f!u^Z6HwU&^ z-XZ3{KQ{lLm{x1HJ50Q@`C9M#wi~vPts%CDrMJD!lRfmdU+S|uMQn4mpM3lswlSY9 z=Eo|=yXTG%4<3~s8y)`og7M}9?_FI#{9y3M)t^Rk%CXT?2lxE)`^oaD!ml4c#9f}f z_QlU{@7$|Yg@MCATnRrtK6LZx&(0K%Z44ZGb#7qoiH-Tw>qmbZ{2tvnJN$lrqddQM z;iKIr8m~WdIXm>#(DgIV;vYr*MEUxc>*p_hSed!@^6Edr`WvI!Bd>i|xH$azj@2vQ b{>6Q8lfU%I=#825uS0QSDt~@_{;hujt<}Hv literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/icons/item_bag_skull.png b/assets/voxygen/element/icons/item_bag_skull.png new file mode 100644 index 0000000000000000000000000000000000000000..c383c20f3c5ee1e889248d036d234056bf2c6580 GIT binary patch literal 2191 zcmb_eU1%It6y8=)Tdg39SX*SACX%3cXYQS!of)&K$!?k~bZbZ#8nvi*@7&oPvpX}L znPfKsq0~GWun)$EMy*;9!3PoXpw=uwx?359fhg<4BYQd&f9N=>tprF%W&HkOg$N;Qg_^woLf?_ zpS(Yny6ru;G#k$r_gl=br>X6idAeB-z&e#0+1m^#o8z%i<`vh=iobsHsVKO1Ry?E? zxTi60h+(_u>d_anClW`mrBX{SBad z(^olw&=!k5AM0|d*LyCacwzx$bON%jbyQjmxHQ8fe?DYZF61=!!;n7gwq(;H#v?MeWVvlx^ljH!>^CKd zC?%PIouw#OnL^6gG^W=zwwdMlVV#0tw@xcu3cQLa^ulQ6{F)zvV_>KBj~8;e>CkuF z8cakpljA~RBBx_h*AZxJ(JdCOf)~ZqW4w^hiV&Q%>)Mt_uLjc!|pmee?u|utZ!5~fNoL`ZSRrq#0?tqu?l3#b?Z&qh zu?lRnyhhA_KQ{lLm{w}GGfcWR`C4;*?FKfoH3U5@dF(Y$c?Zq0FaPv~^)uad@s=ZV Xy9UoMzkf0L%TSm&kpE!(xnutV%Bj9B literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/icons/item_bag_small.png b/assets/voxygen/element/icons/item_bag_small.png new file mode 100644 index 0000000000000000000000000000000000000000..c0d570e5fd82d2c8ed290e01e1ad26267359d834 GIT binary patch literal 2911 zcmcImO>Epm6yDM(YD05~IJ6Xj1{am0;`NNz>$Od{A=yonMcoo@S8BO+c0As-vgAF39F z9nm7QU1z>e6me-(hAB(ct@O>JoNqRc@P@)Ru^bjOFwd6Tz*VaFUR45d4Xhlivw$1s z-lh8A7jn7jfH`iRe+WyH2G9AOrYxMW=}uQ$gJRx(C@U8%?QU>b&pUZWkV+=n=pmtjHyAn zqNfcRn#j-`)uBe(?x8&!xV)Qjy&qM0WphM=4U~qI-_u!qtA5cUCawX7Nb%2!HTjU0Ko~K;l!F<>5QTlxB z0cdxEg^B3!tV9GmDbGmfwYx@rHkFC4ezQh7=xtcrF%vQ;YT|&7Re3Ep>5CtwW_GZ;h+VBfF11dG6xInVA6uQ;93x^_Z5cl#8j9fB~_G#L@7 z%ecbHV!$-#F~XRV6z5EacWfv2z7%!4Rkc#4rD?s!{x2!&Vv`#7s+4a_%3TD5NQR^} zJ0la@(Bw1~9Yj?mBMR*lL=~-q)b)e8W%S(RV916H*r4cw7coiJPnt~ww~qt;Jd0fb zZN~jS_BLeP=7#2ajJsz=YEP`rxKVDMM4jh+CzFRnceZy`Ypt2HSq}1T*5W%ryeHhr zcoiHL`s{9Z0Wmk)f%l!Adaw9_Of2J*Ab*I@lGNj!8t{|i#S-|ApEA!B@?&LVgj`=) z8QuE+*%$Y|ynL$g&*OuCXakRZ{_{`2t$cFXIkEfU#pMtFeCxultKf|tiSGx7&*9pQ zm9L|%@bqi=_rEg(`Nc0^`1X|tPd@V5uJiX@Onf@>5+1y8>DSrqOENeOU;XghzH4Cr z_~paV*$Z&#qbtj&)PbFQYulFox(+YBv*XH+N3CC;{_zK6=bMXLuAX`Cnb!xuc}N_A N3lm53ACI3n`7bTTvK{~c literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/icons/item_bag_tiny.png b/assets/voxygen/element/icons/item_bag_tiny.png new file mode 100755 index 0000000000000000000000000000000000000000..ccd022a9661bf8f48a195912f182c3fc675e66b8 GIT binary patch literal 2927 zcmcImUx?g97*D<9IIlgh6r|dhGzC#=HZzlK_7c0jZg;P}1-G8pm7Yq$$;@Oo+$NJI zx!b+N2me5&`qIZ@eNaK4q(~n`>VqIc1@*-@Ull>|K?Q{($T``~rrUDYyILDaGMRky z{k}iH@0+=>aO&jr)T2|9Bu&@nYm4H&H@zn(#PcctiqXD03x)F zMvk$mExw;a#L3*Ma3c%IrqXe9Eo9mvi}^}K*xafx^U%Lj60)RQ>&=ZNbn3S0;a1L#ky(`1F4n^u?mPoEMgguQ9Z4h zo5({g72#hjG9G6&c^tR0QO7%aH|mdQX#t8w@?bPjq}>^D5T|)BwO}p7JEei0S|XR*R)w~u@jO^%(TMpW zj<9H~EQ-sD8*yLG87wDU#$nj;2u>Z>Rs)(fuRNT}mN+q9y>p0)E`b;rh6)KVRqP0| z%z&b)&OnSA(FJEpqGAWWkAx_zR%GjzZ5YF9{VyTPbdxp?T1;$9+C4af=nOC^WGXJY zRCS2qs16ekhztV~2Z~y?wsz*W)^neoA=@%wo1~|_$Vsww&?t+%5e|&!Gw%Pv z+m>-VYnodz?!G0d-?0YmM!UTe4ZdrGNS;Wmv%jm_oAsQXa!_ouw%7skJ>hQJYs%qt zJ-^poAnhA{$4Aai!zpnfONaSMP<-TPNfwAojl@au*Vk8P#VPaMdacr&*-LJ0Y|M^* zaOu?#&wqKL{?8lEn|mte&$DyqKPrAQMqZxyVdb%_%f}|3`}#2cOQ~!ey%hYuas9x) zPY=H`0WZ?YZ>0Sbe_Sxfzuy3R^zj!vxF|h*>ErO&)b5Lw8`Bk28h3xW{KdJq&KLIo zIK7mN9W$;S{OXF*p1kVPGY|Qb2gfEKdBB`Jt6$qSKE3PO`0}6clZ6-FTYpxP4t#h0 Vr_0~2olTF#^|@2EE5~1Y?Oz*5u3-QG literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/icons/item_cloth_red.png b/assets/voxygen/element/icons/item_cloth_red.png new file mode 100644 index 0000000000000000000000000000000000000000..54760ba5a537a192aafc5ae8f030d409c8c36df3 GIT binary patch literal 2086 zcmbVNO>Epm6gEXFAvENWOU19_lBlT6dd5HNwaKPRc0;njhLA38iV*5}Jl?gkYa83i zZgMK+N2=g}kdXRAPpBL?a702?Re{tKhh7lkfP}b!IG}Ri!l^v3cQ;FwrcqaGdp!Q; zd*6HW-n{cG%Zp>9PmBtJFjiSAuflUYy+#hg{l%X?dl?>&hD)boL3nH;y&e)SeSTOF z9(l{FttD&KS1lH_a?}ZIp6j$iKnudm@lHtDI!{EK*FC=|-M)515H)3PLXA<24CnoA9eYt%O@7x-ezNbO)FDN3L;bDNfRZFWiFcBvUo8U$-AiJXD+E&QrRUQW$5#tM+AoED-O=y)OPLm+21wpgFQ7e7P zqC#?tc=VL#J3%{serN$N(}WkLbk`6zkdm({#8OO4F^*xy!Z?Fg1IKf>hM+`23Q3_P zZxQ_%COc3_8HXnHzF>!0ZV7Ccb% zTI4}N(&jL0s>%UI4Kv~zu7NaSf;6J($hHlJbVoN7v!Jqqqo!%UW4X^V2mZyd<8iO7 z=4rh*YI?J~i&ku1!#bsi5zPjvnuBbGxQH7rGYwr=nT==Uw4#7Q%tjflytn;so?A;^ zs%NXG;sk$ZphxVcwnbT5vPFrdxyl`>H|_1RTl+fa+> zXEOAibU0{u@G@wb+=n)38#Ws~x>{<0NBDxG|=OO+m!jnc2HFcRSnN zP3{u#r9wbNun%G=iawN4*N=~j$4E4HDv_jvPh{6V>D;G> zBx(N-L3Jrza$j`_Z{{%N9?P|xkw8n*)O0(-WSOP1$7(@XRQ~$@nj#0ZsGKxh=td=0 z59Zcmc4B?LO4gT&O_k}F<*BwK3^ZAa<#w|XCQiGkbnH6fJsVaPxdTa;i^@#qP+oEu zdT9~~Q`HsMj;G&hd(Q!_R${MW6!ZZ8K? zWf^HXBp*H*gp{|E7lszFGEP}h$#xCE0zhUJX%4a-R5${W17R2HavJz+Lr@I?q*q}+ zgX-uA)NVnAWGGJYJ;9VXK98GN1ROMRjj2&sQ{+J!of2>GSU48!^x^SJsk9JtKWK=F zWbxQhxiVWaHQO`+%4r?CuIp67B*h_Nm2y!L$;kx)bxh`Yo=tR6Ff9y-p4Whl3=J3s zs3AxUA499ZzRbx=CV^~yw;CuX!p1Fno~@fuD=?79x(RHU_kra@8!(gUre|T__XgHp zivv-RxG`)sV?~8Us^@*v#1K#)Tfoxu9U zhczZTtg769FniNwm*r3}yO-eJf)SN6+zq;mZVN`u8f#Z!>h0p|%ndbRv5}o2(b==t z!I2_928R+0MV?}D8AN+Q+@!> za{a4!HY)PJW52`AXSN?3-GLXMd-&1s{LQJicDCos-#q?_|K`X8PhB|u&-qKfG|t?S Uum8OIMRssjX6MUWN8dj4AClc=_y7O^ literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/icons/item_leather_green.png b/assets/voxygen/element/icons/item_leather_green.png new file mode 100644 index 0000000000000000000000000000000000000000..38c8e16e20f2209420da72fe8b3e31902866b11e GIT binary patch literal 1749 zcmbVNOKclO7+zFSmC_ag5(*s9vMD9B9q-d??=(wE?X)q%Axc~|fPju?XYEzIJIn4S zc7g;A5{I@Dj|z}LMJ?(Tg#UA>aU_6$cPpph7)VMO=DGFhF z;1LtejDo15=wi;wsHTzCa=S%M(G*#QD~r?|Hu6~4#O_Z5YmwvP$>RR5E%>sfdXj`# zmYdCHrm1ImRFhT9vSdY*H4Om*#q&WzS}2GI*BFW{rjZvW9uLHnkyQC?VoRX3j*(4jqCI2$$E#vLlN2J=~v#DGSr;8w=0he=vY5|Cl#R}T%)!)~5Emjw5*}4JZ>%M1VokEB zX&F@qGCnqK+D|m-1&Z}hBSbxSU0e$ zUI&GeaY#aL33e!UdE^st*z-w^$zf2F#9kP2fj4*rhC!X)KVK>oCL->74OoaL59||5 zqXkp7OcQ~RZMo$#E(LKy0?JB7TY}(ZJkP;-npIRvEaVWKAk{D|RMqo&WEiePb2&xN z=Q{4f{vxNdX$hqJJEy_n6gd8sN2_^@x>XD1tB!>%&B&v?=BmibQ`gWr@eLJaa)$V`S(`{mow+ z(cp#8e*0;|h1K0Z&YXSo;^AiLk=75vz{B5OUi}U=cRj!Q{s-3Px)I!Q!9R9J=0m``XNRUF4ZGduflHe@%GG@G_DU7?}dP=ig0R&22%2zn5Yg3?0| z3JL{1h$r!24@E2#Pl70T(UX*3JV>cnEw&O(V`nUJG=r9x2DArcUL^a2^88}BVfYELJsIeFo!@#sGw3JF;R|6~& zRRlO|aldn3tcq~(;K~W{sTigMWt)9&m==c1@3u@#^ZS~LWuhACs75w1=)0$5FdIZm zsc0#c9yI~L^@`5batYIFz;)k!`Juxp?#8vn0*8ik=ryB}YnFu~%Y1t59RRLeo8{~` zKZJIciD|yD+BtW>76Cr88;6E-tXHbYl7uWt%vCFxYJ&3@FSC2s4st^SbZzZraUx10 zk|YsHqFZI5!$c&BZu)(;DRPu`W9|OHdenpiB})?P)UoOovLtiq@(l-0$}O`v5#0dk z08Y2c4qzv60@&8>E+3epM6j$z;d&UMqg!PvR=G2<{w|dP^8j7-Az@^RNRr2;Q3p=fs|;kd2Ha4VQb}igEYIBE zw?p&J4cCck0@#k|xRao|xsj;0Zs>YprNAfwJdN!O}wN@bw1Z$;Q7~J&CJgOR}RMttU2}bfcLA|xfZlT|HstI5z zrP~M6Dx|V3qbL!oHG{t1G}ANl^!27Gm32xb-KXB#WV3$>r{6Hu1hC$CRv1O6Aq9MJ zNE=~st2eDr&{G(8;5r5q)Cy3|K6+IH&@0tYxKc@HB;QCCTfIz76GH0_XCPRRTJ3U8 zZZ6Bi;8JSBfpbsFx>crFTk)wD3f4-s2Zgf%%l!+?|7NrYQ{2+ZH{5rpe{%dw%S8Z24;_KOdNKHhl$iS}SY7N(Bv-wfQDQzsZbaKyRKy?s5gs?}bV{Ih(OXI^}p z%fFvx=UAGH=jJF>?*uF6-8H+_E7n#7g}nK;*LW#3q(RC7O#S?&k0CUaL2#?CBJjcm zeI{_j*gv~{`_2l6Ve;`OUjt$|!Sn4R%)C43v}=d$KH8RvY5bvlPdpw)iA02JiFnM{ zeEv!-f<29 O0000%Q*^)74gEILdx>-@bhe8tTgW`}XaJ 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%0DUpOHwoOj&<%bRbWjog3fc4rg_JIauQ2fjN4 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 NVLDr3!sVjSKjR0L4k|=mg8$znq;pX0y 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 Y0xwTjxfGCpS^xk507*qoM6N<$f_YxWI{*Lx literal 105406 zcmbTd2UJsC*Dgx$gc<~;BLbmUA@p7p43LE0oAeISdoLmq1f>@Vp$kZF0wM?mlwJf8 z2vP;4mt5ZO|Nd|M=Z^*HA zgkHFN-Jx-CWb)ksX9D&)(ZAEpmIB_twfCn6%o{#ZsxR7yfZN<@HNOhimXSVU4-R7_A*3M?W47JbbA?+@pl zHXnOOuz{M!zjfUm$#XvU^Ya1=3kL=U3I&P_dHOgBi^|Bz2#bgbi-`%|Aq0JcJp624 z2zvN({kH@)2VXlMXD>fzPY?EgB-+?|`uoXq-dXxzF1UOBN34hMzr%DFFyR+AUc#b6 zBLBGbFQC2Me{f#@K5qX~Zf_^-;O5}&;NjapQrD0&;JL}|2h4?D7Xu) zuI_(y{BLb>cmIzHUqAJLJ2(CvkpC^(7aHW{AZ*~^>*?=f=b#>NXC~J_-gtpkeH?83 zJbj>^o^Jm=QIP*OnO#g&NQ|A=$l1f*Gtigse++O?v+;9~=lo~Z1VtVTibz4lMDC^x zEGEV;A_f)_`7fxhr@gad(Eo(W2#Se7MP$H|l3+>k{{?!NGWIrpHvdPky&c%m)5qQB z&S7VF8z%>0FApb9_Wy(ttm^6J>2s&}&N}h`*{`jt3i0uDbauNt;cK9&!mh2ZDkUl- zB_${(B>FFNb#=ko9=?7y9(E4eYVw?S!4YzHwg<~N%1Am$ir5M|NZLIXlyH!+7qpeM zlM)oQ6Sox=7kz9eCieKh$E$hT`Tvsw|2^LR|Ks>)KF)U~Xyf+(Th2d)`A_bEwVZwL zW;N*FrDN#e{qL5WGyA{N1#DyYPZ`K_+Wj;C4)&b??soqF9fAK68~EJePU!zjUi=r# z*VEB2(8k9>+37A?|Hr%&zB69|0wSNhTk#9Kh6J8)!o5=$j-sz zj^TXnsO*UhV;2r?ww|_{GW5m5Zr5<8oi^>mnL?%H(a|)OBYqbRty|(V!gP{)EEeKl zoROC2)znvjdi%D6D@5xjF2dv$iNHZyhkN_=9`;%hi&kWNIaG)}KgQk;6?(qJpf}N& zm0!mb2Y#$u%>h>83b(gsJuN>}yIL;yxBWX-_?_4^(p|e~-_BZ*Cdzv#thZt=?bekH>FYdY%5rXRkk&t-{?jtZQihqQ^$W|G)t4>=o%YWt9oht8xUkwlL!Jtcv2x zI$%VEg43;BL|eqM=j~YCP|sB$c7N{xd%AMFX!Y_!{1kiaba8Nafsg*%+cPk261{S4e)(2l(b zFF_C08g?^WG=4aV`|(7+aGCtXWRlBjXZs2znXgEGr7+!{Gb>@`J93`nVu_(ca)si*;u`ccUtmM9qZX?qA5=)?s(Bm)P67+bZn#5hmio>h?{~ ztpXdma4aI?b}@9IP?7EUxQ7tQ$he752%t}ddZqNdi-A)_^0JQ?ViMoob5_X9$Dw}w z=UTSC!8@u*cZgx+TGkY2I$pUaK}t||Zt`c8l+;!3C0&@_->ylHfge5KGW{Ya+!z+> z-Br87LKe62pE)aLH1TVcb& zXr^5NcG_uYUzGmtDak;J zHBa)GNqimRExC*!7sn-Ju0)L&_dU!Uf5bAH>u2O+S?hr0KLkWDMR``_U4`xATgT(G z4n2l96k(5`hnr`k2vIY$ivv;Rq(U?9r`6x;y6yDRC!92QJ}f+Fd(gKpt23H+H4diS zlB}Bb?ylR8Eo%NcLmr~roG^C=U4Gm7WAd%-&f6QaaEqp)iJ5WYoKZ1`ftp%zxxpuQ|*IOq%GVLAz`P%B9yTHkPl#aa# zca=b%i=l&KHL5B+K2tE;8hZBb1~AeNL;M`|U9K)56oLOPa@~Q$^(2c-vAgvexWMIp zy_0HC8%PNbV|Zc-JNb%7Z}#Rwp%ndPvp*=C9v^*N?I5TMFP0hBfE5p2UBZ<&lGU0$ z^TayabI1Eq@SvSu7Z{RV5&nMTb~t_nLnhx}5f%koS!#_6?VzlA5vp;t1z!R8xUzj%z6`-;-=a=f7WCNWpb3C_MC^ z>h@baI&A#JME^Cl>OmO*7!UIc+PpW#QCzEPnIrI` z5m(mrS8Fdluq^MOLhR|ZkA6*Ls30op1IlrUaw%81VK)bt5;UPSEr!BVg;6%PrRy-y z%iH7Dy+uQnaaaaW4vX3V)WgYTSI{e1jhqy%b!6VQoGpxh@q2YN+PwUtlj40&-D7;H zoT6Q%Pc-LZ<8vx2(@;H=k>Jv45tC-GIhFCpe43{wD6zjIp`)|Ie@t8aOCid7kIdag zPMHF%3I%;CfHUe$m8TA{u?97z=F7a0-c0<{uBrGqZN$LjXfTr; zbKf6Lo!wfG-@W|^y2Eim)BG6JWDk^v+8X)8=A6_Mm3jC^6rC_O9;BP%q92L$IeIr& zo~I*ghq;xCQ+mY=KgyWc8#VHNY(3^2uIUIW_6f+4!I$wGi|}2r0hwt$upk6{{7r8^ zzo#g>Qfev(Yc4hAhlR}L#uT5{k&(Et*PvlqF<;{0E)ckR(|@bg#)1%B`OMVcTfM1;~2++#p|w7%*cw}t)7N@ z2M(~pB^9x>0-<`ITHqX?*(me!t2VNg21Pk#(h4Kmh$f$^ zw|IrDBI~U*T?%i+FYs#oL^fgQ{fT)_LYAH1BRl1~3QXl8iCU1~Zb!D2zGdSps}i@f z(dKjEFPT+3_(h;Lh;QhW^_|;P{V_-haajP{G2jMnvBK;B&MkZu{DiWD*E}AM*`}DO zQhh@CqADA|s)Vtp^4o>2vNEM%KE)D;sL>wxAl8SdZrrVZ%yGI;zxbS)lc)c(gez!KRzx)1?iOVfAA%Pquj z)n6%9`2n9zZKh=c^b(pic<7XuzHz#NT_e{IjgN?(Ajn;Kg5lsilB5T?!`OVjNjQq^ z5)h1im^Y7IPhuxH*#C-6M5NogEwA)20-#thdiAbqzji3OnaKsdoyWT1Lf#dEeWku z=j8?_tnPk%W|7NW0k(rC=@1Se)=*jt`%g;^K6@v&3k_A%@>+N5AO!fog~UOsr8Re} z6WN=j8`0K@#`5xL`+KJQAJc3IH$JWzGnrN5N_V-!b@@oSB;WZwGzUF28m!wY`a$1- z=8z1`-R7Tm9lY{+n1_lqd}Yp}+u{P)gc^Wc{kdyE(eL@~pubp++bpmE^Z-Us`s5SoH{r|NbpiaCSdtf-xLPG|Fm!pD$byz02IjQR2T zf)H($;?ca!mXSYIG0#petC;A&XZh49Fzag&KiWnhIgi2I9j0sj^yBpg>m=rwNgk`NwE-LZPbyJpmvCbacvY9ac4XV->v2{;$Db6) zpS|b#J1DRajMhp!5mx_qoR=$l(x+F2os^&eVqJ#9`NEa|n7&Z_%4-U5iDE`yOi2zY z9rM;}nD^H!eqK%Ij_+>*-BiH1#k3KAZ9t%WuQLa$9qtH+NulvPoU%)aF4KjcYXYOk$SsVbT^wQlj3{|4m>MX^ z@L0}2qLG}SWN*L(Mx-s0k=7{m%Rf$z*GNxOJFNBFIpPmTGIHL7OUKLphv7KEQcNx- z+@fI`#1D6i7_5T4i$1i=OEqhj1bx>rit8ifL5|jv+xbs@u)v90q}^r?AE&4SPw+R# zp;Y|L5&_=%`syedT6`v$wnI6^OGhafrWjAZb*(F%$C7Z)r`jhEkg&)wig6UcDZv=c zjHt#bA?&LF$=nP(FTS-3CM44wjG95vbS6&hIYH_|_U~%#{PkIKKplRLl7OkUHHn&F zE*V7JTc8ZJ$B=E=x1}9kFKH2M@&k+)0U|w*Ut(P9YejR8pa{xEqlF=(Z zY@wj@WBdj?n9;w#^%rV4d`}n-fPDEi92FE8|7AK=B}oQuoAvz9V#BvBieLFN72<4$ z$Et*k$OTz|^3r@38K{Q#-cU%zTcZH_Uc})lTl_D-1co*6Ab7l%KY*pSsOiCK?zf)O z)L*>rttbivp0;LN=P%Bm&||l;jgSZUkiY3~6;t~7-uAbTcje;{vG`kvswMb+oni!) zyZF6Y7ETW>Y!?S`6PY=v)@ssy2l_s}&UyX5l=>$%=kiDMK|}rYLcKqR2?#r8OlUU% z@5%C_ixg7g2o>`ox>2_O#nAM}`(LWmv~XW$Jq9-B2U5-GaAn}8nn$KoJ^zXG9M!w? zk;I~ztbE*1+xU(!LoMuY-*y-uV)93}@x<#ce~{%u-6e=ai#yYxf77f;`q)_bGSU|s zo@CUBecz^rn7TYQ>2|?~g`MvgV>NA@ELcRQO}%3S`GX?L9WclvSs(3c1L(B4t14Ve zUz5iR*fvM%mELDxUS%9q+_vXOth4tpO-M=SFtdH@J6X{>^-R9bxC~X^dMwPM?Hytx zv*goDLvI9cH|W@JJqjf4>(4%hS?@C!P}X-9E`7pm5(3OY9qi)(=2EpQAIm0m$4L2d zr{z%S$Eoc=d3EA7O}@zb7y#(aYD{<-W%1r4f;&qYtuE*rbozljHJj?w92l2tnX@U2 zcPwp^k`d4&%rV=-@}q7a{T!H>K@HO^v0i0Y2g&Gp$Gi~VN5HG5Z|d4AcMAp| z9+vPZuBbvFSBZ@c;OTJ`iM3>BzWnQsANhPc>U6oOc4GBgdV# zk*}mpa{!8-E&R|)VV_UZvgS;R)K%5S&Nq~Yad{8mOyzC*i$c#L!9FFeO`#9I~Mrx`zO|o zLDS z*{Fw~4h?;~asX8%^+kp z3%gTLB8c~1CCvNTT|M!KLk?evb0v*i&m6P(u%t3sbCkrrqFOnMB1n+xtMcr>y9AL|&Mqg`=P7K@?HTycF z2l>AykC=bg?nQxA2|uHNJG@VzSB<8n9-0+Cv;Nm7S&DhIaJUjkvxtchDeYT=J0f@h zW#6G-2R_DC_qG>Jbu<*nfPIh#T73}(**{(+H3sn28x(p$l=Ay}_$N%YB}LEB03LPa zzle0(6B#DqXM_{A$sbiV{2``713j;e!8<*4y&Xy^DDjB~SMT$X>FRoyH+Rokru`uY z!_t1^-8<5_($~#8k;Pm(`S>R4Q)4c_Bfb*f6{hzl8<6e?2ccO1C+Qf2lK4d8=~sL{ zR4k9bn_eu}=4m^?hXws zsVd#_BLZJW==mRz=^BYW5wNK?wPEq8wQKyvP7W*oiYiT?dSwtF2$s_+R%N345M5?~ zNJQ>cn5+>FVjNBL6D@LQEBidS4_G%=8+E;>)(qJR5=eGRQq1es2nD>KcA=zP2!rYz z?O49qm$Q71mACl%B2H<0ZdR7;M96tozY%%)7m$lsexn03hbfHz+<}4tj5^Kr{CLf; z%XkTF)k&Oyueo6FXDZ0ZDCqn4U%obmF{mKI9jD*cBvZ|bQ}aDjYrSCmaunurcw-}7 z&$({It!wbRS0<0pe(yGPT{!nb9}yk12J#OxZ!aQ|bn^P3IyAC8oNbYzGDuuyd@a;I z$wW{UU^%T201`~d*d+-^NwKSbZO`>>CPFbi2~2}0sU@^pHR}O8ttuBfv_?mU;X$S_ zg#O6|Kh2iTtFqEy%ZtA-5W{$|f6Vl;j6PbJTu=_HNG^Vz#&iY4Rci)=H9Qd+yPYO9 zHE;CUbQ)i3bIT+Im3t1xw<7qAo0H}x{gcv16u+3%r_U69@AhU8LS;|hH<zR->`j^set1CzrhLEN)-Iy3obc^}X{W`cz6yUa{%ctC49^il>y2Moo@c-kN^ z!=dExX5F2IQ0dwCsDP?uW18Hm{j}GqHArdbmO_QPDYpPX->7mGSu|U&3oVT{*Roe| zZBk8+;zyU%PSt>=)3pE2G5@|$$wj&&cVkABl6)ThPeJ5>QHjSEdjdbM@7M=LhPFE^5&TmR# zdyOMl6ds_>Kn1~>^8Gzj{BZj~z!?gcwz^wX?~Y z;AG2(qimq6E%?BX5gT~1mHDHt2^vdW%YZP8PzPPUD-b zB`oS8n%23n5s$cuLj@oDPFb}%mjYO&hTjWymia=R=1>050^FZ~O;dqrwEm=b*D?4E zjvEdNVJ6g3CHer1Oh(WFQ}#?x#O|2^37EfHotXa>U*Bac-aP(fZ)_J3oQGxWyH`7q zoI+bg!D_;QGWBrr+NZ9S4GvD9j(6FEGWFPfUmjjCnQn4<^AlNZRDTRYN+VtxxH!QI zOM;*{^a(9nUYl9@8j`qBGI3z0V^)Wp=KmAf}=01pVjC7aWqRhBJIjbksL_ zIW|A#66}Wf%^u!856|=!&OXTREygo?j5BEc9qrumpEwdTgpb25#MwH-U3E=+EN zs#ycZUm2ngLf^EuOx;xdP`pPL@wN3BK{?k(%|lg^Ke%gN{fs4R`Q4m6LHmPWYO59| zOZyxR!8rwFsEbwU1`lxIS4xKQpG~x@@Ppsm;Q_QJJUNv`>qOoNcE9M%4$b48&1|Dv zECOe$>tGo(=xTKne!J9o<)5p3;d+wr+_NJZXUN6kG5kNG*=uqDq_?QqnTAB5=zP@zHwYa-oCuLxjn_AHA6kZZ?z)e z=$Nwwm@9x+TKxHS%(aZ-)iD}-7;()eJj;dE3%^mW z<+{8+zHCLmtW)f93h%iH+AQ1!r?1@t4ujT0ie}SZVkEB%Z!56BXA99o$G(bezc8}v zL)fF9YvG8Sf9+0P`rcme?Ccaq#1COldu}`>Iv z^qpN?$f1Y+wOQG7dwnt=p@0ot>c^rFWY`YD=}l6{mr1j)Yq*?xs!m>xXP7M*+U&w6Y*9@Pt6rh8` z(HE^1&5<+MPN~D-0CdaZr(mU?{&5V)Oh3QU*pEVArK&io-Y8nd0czz@N_yYdB{9T; z>lcX+($11iS zMidfCG!@upyhY~{!*4x~Z^!W6Vt0-M#tfq0(dQxDV1|g>6N0)v;mcZ-20gPA zQ95Jc7C4N`Ow5A}gv6hE0U#GACLhGkd_(Jd{hnd$C{|+#V)Y-cF2~;EPKSL;x5VO! zqZ5Xxi8D<{4UuMI6)NhVCYrF78X-x(U7!UU%5;D&3`;;eGu z;f?+)qk33HsRwbmqhZ&9_y6p#TW+RavEn0)uNvQzaYdW7GFJhp*tZxXlBQ`?V6hD_2b{wsp`;?~ml__B)d<{>H{LpvswA7d3;SkY{-mE%|Ae0w1n7!yG(|=zhTC9q_=?C-)3E}(evsQ-Z8~06p z#3SWmzU>9P?!&P49AgxJV9=Q3HD4GGIqpGMCSC$f)fTbIRm0%07zPpB8>Ha6Sns<| zn+N@r1z0u~-3YVv7xnpMMEI(cYW`rXur%yJgwvyrhSIg&V`gg;^)%XSs~wJOIUbUR zFe{iwb$Lg7I1x$gJ^B^*h9PN&Czs}&fNYa;d;wOi>cKboS*Ec;8h_++_J8+&d3k*; zeAHP8pV+Battpx9(eWWlT7gX?YjJFso^Z5J} zMfSXLn*6cTh_v%2^Zs;oPC?%$Tf}W}Ct(g|rT^CzkJ8u>1r*&A0YprrLptkHFh9A) z_Bv}}2%4A|{;Q)geB;qeF?>Wt`b#Z2qn7PlpQ}m7XnrZ9O`WO@KOm%|F`;c}oQ z5rKyBny;O##;y_vzi=H-?Cb(qUeD|MDf#3jN%^&(x@ElB0YXirgu~k>z)6@R6&5wQLtqD0)6&EVXat_s`jAp>~`b@kv<&kzT&f2xa9u6h0$dDJmSa(nLbs~O<cTrVP2>yQ1i=F(m|8LhFMQoM2F96{-``L~jTI!MOT z9!Fj0&#|@73P>AcA3lnr-OV&@^od7KPL5GUrbG>8*262-nXCyuUW zmrun1YWo%>(QGzXuR=0jr1zIKpU|SO#6aq6&`05)>S3ZK&0I`@)mj>8b#PB9$! zx>RaqNhFgP&s2daZ5UJ`EgMWk_csxjh>-W>;NtAp+Z=57XS`8BD9n}RD~Y>?DM@w0jb@u!0*?MWKRKLe%AX9 zW8(Ol3ifyZ1dyVV&DcB`bPI7@0-cZHXF+}5umcyv-L~#G&S4}+35?(c{1TfWn+_M! z<^JLnR&Ncu$1WawpYh!+zvgI7C>_1rx$bNUs;S3`#bYE7_3moW0hRXnkek7{&Zt-Q zeH!o-jl6uMR$4jya#ePs7+5LvW=%>Ek)N?INx4G?mPPk4}1qswd2A_~;Y9sz5?Z~ zHRZ3<;!+&IxPh#hC53o@&X_bQ!CcSWORu+q{X`~{5F&#ZVZ@di5rg;uc{X=~a>opP zWt})qqH7WbrTKduJuyw;=2J#8(0kK^0;tmyByP3;+62 zZQ3sWQ$0z541T!OKAJZetP~p-m%YnC%QIQP^y4-<_gN(uf{BmO<}7Aei92i3Hd9H* zy4kE>Vk*RsvxEUKgk^AxdFUNs{(?0QThA@U*zZ8UERfHX?}g*B0wf2vbCr6fZaN=Qo)k7u3N}oW63rWwqygA?IMG(f=G0Djl&Xf{7KzPl1K7T=1dqSI}P9B zdE4ubCl9ttPCR0f`GxQF--s)9g@qTtXMd4cU)768qC@SYUUnK*#xyEkLIy#uMZ*H; zW<^adeqVb*JymKpnab&kPyvOinsd|pF}&S2f3sit4YCu|=@lAeaUWglMq62T^SvIR?mdK+vyMtVECo=aLMbUp7oOu|~x ze4r~k6Z1vYDpuvNC*l!S`yIe&Y583u(5tVw~e8p9(~) zZUqbsI{ow`g~TA8o^;b1PcV7Nk+p8CIaQANKU+&Rk$U*rRs3?lN;>rjuLh90Jz=Am zLd^Q^aj{O-b{tv>h!5SIS>kv9&|7Xjam!pp6j=Z;Tu_7Qe-S~iO7Kh!`Nkn#BGHsvx8eOn_EnWu~+C}||n6!XJDTuq%yX_#I|iKac^ zM=4q7+U_FA#sRnLtqCR;hFcF&FOP;Y^f;oE&|9N5+OqdQdPUWV(j0X~`^WkOS>r*GVF~bJaK_q>CS;WV zCwr!rr)}S$vP9-bo53f3Q@aV<8HrLWTBN)N2gF)kPw++}L2as^z_f1ht(mdlVfX#H zdUT<(1Px`PR*yGTXYtjeHZz=bNP>Mv$-`24;PxA!-1D1G7zB-F(x5VdJr#Ok%eW;l z^C`*8XCQ>cK<%DicB9h6P)25vI!y9Kf8c1Z>Z9-=y%(*xAP=H%v?FyVu9Kfx6}UbC ztUbL*ArItTG!5eV?1gc4nojBS@<8&DIKUV~fWbwU6=!BUp)fj!1HR8Y@({^~VeKv6eLu(En zm8(2K_!B)75gf`{#}E+tKb@*Rf=?s|^gXV_kqrB3F-#`$XBW^Xbe&y7NP|E&`c2-; zl^Lr>MJI@70UrcoaDY@I2pd&tpSvYaQ5S>Mm!@B?L+benwN~s}4wPc$i-A<9k5XIK z7+FytBE|)NnD*l_Jiq{xm9fAkO0Fs6J2EUuI+K8_U09-fWB2JU^Fc}t@D;*vA=5A! zC61B~{QHk{c#_8i7Lt967P$@k}Da2=F{I3i27iU0HF5rB!I+O7y;}bcUr4oz819PH{d6$K1l` zRXjb3biqOU|uUIlJg9GJlZPC#2&bNT!S02UZdHXBX58 z&KN0)&!L$=i!LOm&NNeQz==ch)qas~TJ+KfCp%jrB6(He{hlal?>MR+(GC+uHh}nd zTbq~z9ZGgIvoTREgA~~ds!1}xtEHg1+ef9U3wE`lrV6W|$%!SMT7i+uXVfw|m01YU z>Tzq5fE8mA73GwVm#H{$6Xr}F)uvz>B|iU*TnQ9|)sXY!6)t0CrsrKnG8gC=tEM4{ z`X!Dw&l7^_n~|%_+}IBxbTFiDFK{(3e0P_&r|W( zweaRyq~$d=rMLlh#PxL=IHU-U(iv7;M_KOM7RKntNJEYmpWYzG1lEzcpP!zQw?{Wy zmYEP-60>-X2J)jIO9a=Q4q3!<(PCEbB?6woqIE}Uh293T% z4G+JaQ-Sq;87``#NC&`4XCIX!Vvhy}NhKUjNJMC;^4uafKk1ii7#T3MHO1oWv7-et zAC8(mAt~m!nq{aa4p<%CH6biy?3`M-@Sh_+modZFr#KO+X4-eo{(|C-|Lm7<&`9%x z`h5v6E@&XX`22&4a!HsFoua7h?xR9poZ`sON)q`1?HPc2jRGQ4toTRBnrhqUH??Mc z)GQy32ql$i)29Q7o=K}V2KCm@P>p2xM%rdGDdKJuCZ8L~JF$NZCQ^ zk^qmV?~jnnvAR9q(!rlQ&{UCmD@6Q8UKz>lb6wL!x`+A`?P|`2Ahu+}^vwUl)BYVZdwzYW`7GAbZ3bHkh&ZAzHyAxG9{YNqh$g4z z(^X8rp(Ec|+&Ic-G(AA{Mo9^-Rr=!!!ZHkoT`IO6fh^>ZL@vaIKFwa`7D-Sc$K^}c zQifC2td-t01z(V?N`MFU!39-;0zFZ| zQmTI)^yjyflEnlA=h8PJ7JknIj$rR&k0VnWb3$89G@=K?d}l}Yi`VT(cW*f?AE1hC zKjz`nXV#eh)a3ZF^sbC1Qqk$q2rBPDgK61ObsRZdfYqGjkF*`%cVQEk5ZS)vy%!(3 z5Vl9Dw*7I7#93;0fmhVYlG|4TpB@3;P8COpt`!`sPj42!RT2ka~wM6I=tL6hNjYwNM}2{%4SEe9;58 zN7Pv@p)Yf`Ddnyv?5J3+pNIvE4;Wm(!6{;t$9bfRdp&?7WI*+upxh6QQ!#SsSF+i9_eYnk+nKJiI+D)Whn;v21##bNgF#+~1llf^f3Fm9>QD>OYfkv-H6d^VvWgiF@6@oELm!z+{Yqq5ER ztSxiNz39s4&BJdr`vl+E%+5#ar~DW89!ZyyL9o0b*aN^%9fX&TJ#}9K6L^Pwj?~_d z;TLvbbznfjXhF&&$cm3F&$QQoGC$+mhrDP__o;d`6E!a=g@AWxVVM-7^KOz9GJp1a zZ-KOX#KV7`;4$HgVI5l0Ik9#nxBS5nBf{|MyRUT>pA?smHy!zAzofO-_3Z3Ws6aEut(0Q zZajWJ5c3PGsoOxYd9DNJf7Pp-MoOu$xcwAmhKs@Hbro{#lHpMj?M|U9GUq=BDUFTF z5k1vnqe&lvURF$W%1wVV`eKm~{NXZ)S|9tW2{*UZ?K!DI6VF?5@j|4T%{yq+mWHY} zp39+wbp1%A{(2c8B{GuqiFO7o3djFLz1U>gdjyD9zH{TNL@e})g(-z%2%m1lB73Ex0l0g6!?vIOUVU98z=&&+N*#&z#hP_tL?vRZFJt(d5p5e=l@;Z83Z2+ZB&2J^!plR19?0v$bxb+1R#mkqAe ztW8MestQSOI_`he{6TQUl)M@v{k8Q$|0(D@sw{oN^aZ{XJ*}q6{h`Ban``=e>7BGq zP{{S8vzJhaXY5XhPXRm=Cfb&08k(t9gO{ZxWJ@SXm(o&{g|dPx7jF*(y%cbLD#Pmx zsOM|eGLA@8&ce8H~}Jv_|H$~qAYMP z2)rYa*U9<2raPd*$F39k)a{z{dA*z8=LH<5n|@PVRpTW7ET;nbVjP6N+x&t>@E@GG zbD@i)N#w-z@#K;k;cUuzU54C!4yB@1#ix~Al4iBE6FKYVwCt;)*Mz2dsjT&{ztlWR zUC;bY$*WNH6No5>_%WmpbG%qyi>UEOJNbl>bu=S1O=Y(9a6EE-IT zG#svuw$AH4(LTQ*TQKAt%0Ts5=M&inO4Y1&->>%A^)Vw z@EIQw?^HWDZQPXOc`Znz8sN#KtWQ`uxArymnN;W-_#YLKZ0AGKca&53g$Gfqe~x{J za2(!ElXAOi?mM=Wr)1%SFf(Ocj@%5Br<_(s**T!3MqJaz?QW)|xT$CMdae2Eg2asW zx!##nZHDr&=V(n&IBAs|!TYuc`I!h6Lc-Qvrt{DnzP>YqOvr~zz_u-kY&tSvkD^E4 zmG89T)tHQ`(;(rO3HkhftbNadOy|c5h4^xRGDrj`m#S2ZE%7T;rg#+1FuGYg3^^e95nuUWchj70-_$U^YdE#jkC|7B_jUNC@|#$hh6R zmz4VD>MutAmiE0BYfWJgKn-G8{R%TAQQ^;E-9aR*`1A&K=_ZYxo_xyI3&H6s6y%XX zMCz%eSSW6Cvt!_S8o&Dzowmz^zx1_z{J(`jNYeL7UdPp6-{c4Ii=>rsNlWRXNJ?xQ zp7%L;_io3m_xf=OM()ZUeT%f+-mpKf>9p>Cpw}v!mq;fDs!AvTudcD5=ri88bYxPx zk-j3+S0|19tMBFO$IDsCmB15-<%vEJo@$S2Ozq!$OE=%N)ASCX{`*=7kKKfG)gM&o z>mP4*o^p&l9tWsz$i_B@P(TnJ_2RPTy1VA-hTJ9_iIBj|^^wdN7=&pr*9qax0KBua z$z#wLs-vpM@pqFLQJ$`$`qq}@obUL;hT^zmVoQZUp?oYZxQVBiyl@p2Sl0BntlPY6yr_TKkYZ zKqxaJK}n?odi*RIsTP9$f=@e+$S zY>b9+O>braDh+Br2PwpUFJ;BK0XCmQ7eA{z)z};JkOcQs5w0e}G}1l@h%w#UAFZ{_ zlsTA9J8~8n&YO5^hoEbWuXRM2+}&#uGQ`^Y5&|2CLnpX$d>jl~bRKw#IXXrs3@9_H zq+F^b6$cWsY1D*Bbg1Oh4q6%h#eu08kNk{n-66xcBfd6WfTKc8fulcv5 zMMYn6f`H#@N>9eSU@-?Y3$+|E)t z#BrtGT&qYgN#%F?&nq>AX=uL9X-%J}4R7IGX?R;%^p|}&gvxe*ntQqyDc7p3o!tJK6ixnCC5ceS7Il?UmBvWc@H?O4YkWuT4MhnF8`iZZI;0C8Gx#*KiG_uRhK2WXlcfgU?IC#x|1 z-M2NuS|xo(J(c?~mdju@KA?a1TF9CXWzX8zVw{ti5!&1r8X=r~)d(i*p=i67a=Mt^ zsrNftEc#59g@u>_TavQrG;RjGN?FCP)w@5GAlmAjEhYN4`p&%JVocRwHhxl%H`iY? zy<4JH29^GX#YoV`VCH^mic-!J`%L+R9;pq$UWt`9=9o6Wl@H^ffk2c8pSl7~x5w`V z{H(SJ`LiFdB95#M&J(X*S}O(Atm7CM@SfQR2PlW&c@YwRf9S_kRrKi9l^Jw#r9^() z=R2yo35;)JAk`3U(AfVZg=$GQ+ql(b3e@n$l@JCY#UaFp7O=osE%$Fs)E&T113D*D z8|IZ|%wwR4UfK`Q$vYU8Eh34`#Xn&L{2ke?A1%d^NJpUyz7*3?!E=$QLPw_X&z?d2 z{$o;REzd%uxh5xXx80;j)q%;hZLq&DSEBk)hYOLWA=95mNy$~C8}Rq_h&sIi{SyTi zV3qQgS;=m{1u6Z8OEc+V4bDfXq91tYFWr;H@o!Z6LKg9Y8Y=^;o#oS z&DekFxqtnKAnFI=nJLF!BLRJ`nMSmq`V+|218xK_j*L);NymZh9aH~7%};qHtVFsE z2T#q@ND%W@KGeq{VYZMU_UN>8nVI(AA(+Uq;Zx9sN;t`HQ)pZ0XEL&=<0+koHZu%b zKSt5CHjac^=;;%oM%y@T;eTvm$1n97`zbcyf~EsDX1dbQcfu$}wcuWHJ_D7BH&^~j zrl^Jg3&TJ>zk}UYd{-@I1tE;iuAG1}zFamYg#k)97AQKxY5{X3K;;a{Azo$%dV~+7 z7C=^#$V1e*fMbBl{dk7*>CTr{;G!orygDraMF_04_?RL=)Z&9`5}~4rNS`S~w9=!l zwD__LH{xhTJvS7f93pI2g?wkOSV`I-pP!1Ti=>Y7tm#73H6*H-jY3yR;)^Irpu~a& z=X~E*k~epsxq+g)*DE1gcD69Ir&N+40g8cX?xcvwvr5PJiOrgZLDYRylBj-h2&dgq zd@zL0Qp4{l!(X9%7&W)CHIt zzRJwyK}Xa^;z9S1s^UdQaKvKcLDxZBbsd`BYPbrZEI`cx_+4D19H3k+K49Y zy}Kjw+o_L8w%~o^-R75C3cQt#4E(VSk--MZWthpUq z4z_@E2^qEcP=G@4VWh?P5MEHHb<=idPpfD^XXBw^d)&-OEH=pULo^r*P?Z#lo8a8Q z24Jt>(c)t>3`O`nC5yFNd3`6YONG8%9e%s5m(KpaCq7No+Puu7h}Nb`s=h(R+37fZ z!f?=`k*p-e4wyzI>v4aFM4z6w&tT=CJ-b@1$T^pdhu&Iz&j5v-4Xct2m2k-CX9%a% zw+!pUHVPg7$|?wAN4{tm@%K>9n+2aH4^h7iMS@+aqp_mJS11xFsUrwFI5?v?9pJu6 zP(^Fq(&8%=B#?wnb-=8ML0^)dlvtn+$tAg3Fd|*`f!gI!ok)b8b%Ls?i1tKu1QeiV z0?PKd8=!Uy9@^q#vw~q9FcrE__NWPAw=$wrgVe74iPMwb6^HG%>sG6?_&!K1Rt2c< zWVcai@x5tug^YZQj=%_|L6Ja7=47`WJRG)VH4NesHBeaFW!Z`MOAUl zFUzEk@+^bFhj)6^D>I*+wm_$S;0$k_g&tjlm2TIRI?7_Pj+(i$ghqS`1g056kwhtt z^(;%C`zo zNC9Nk{Id&0+UorbqC*#{nZGG~o0)jD+koXi7^13!E{jZF)a?n9Nb%(= zHFGBu-!Yk6F}QO86OUlD$l${n7;(98qAlxKc@HuCF7F$WI#L>nS&L5rp5oen-j9_e zudCp&QkA*fD<|mfqI4@d!li-=Qb%{-{npXqd;Y#EY#!OLFb}hbklJ^AJ%)g(U)be8 z{E2*_=J5MV48N5Y-wCa}6-~cg&_l;>$RUb;C&5wH0;q=V(JmSx>(nfR`9PrxeGnq| z3++iVzb^u&*+F}*LdGkzLo3-ZXNPuTQ8=Z}g9zE?Sglz=Bgp}(lUQU|*ox=qDAc>j zUMJw}B(ZLytVD#Ff<#t|QCfV=IonY)Ur~x#vfHSPq%u_8F*J0kS9gJm#HB^HYiVR_j8Jls%) zi4M9Q$s<)o*GDn$M?a~8g1*xRWb~uQq zlFHrI;VL>&=8N$?-F_ zV`igJsU3H1p;rRTYI|U1=44k+!d+M?Nkl~ggz`aX%*roD8JZ2`$QrDt(X0~9E4zvW z5lrJA1Tzq+q@ZgRkN9g3V3U&9ZxoBIth`;Z*chm~{IyqrO8fe?-%}!?=mE^FfT@|Y zJzO~!? zNgP*^ovVB)1cIvk-k2>5^LvYiq{u9^nlaxEr`=%~8D3^kt~IwpX|NfCYiP!XsL0Ul z3pm4G+Bk(JG&^)wI1n8lsa4KQci@f;&76boWrF~08-`Ld2N@L_qB5*03Z=_HP>4nn6P6Cjo2{RW>b>xwI(8dQCgl(?ncvSeT0`*R+~SJ? zD$fzV$q@B-sRDEky6)z1p05nelw=M9N3aSCRX7YJfp`)_Xz?jJLWj`(Ahs2bn)wPj zvolvf2WM9-)*XI#wfGdzkp?J2BZ&bj&&uZK?uf-^7ui*6z^R z-#i6D-5rXaP!#Csda+DC2WjOiG>7f4g8^h%SuQ8qYs`VFJIjcixO}i>A0n8^fi+Ui zGAc05Ddmq-mMfLr#;XO`Bw*f)0F|})hM^R5OG$~U(1uWj7iJe{w9e}yl*i9RZht0aPCw{ z6@X&5+j4?-g2?!&)tAq>c+D>RJBh{Sgn{nwoXD7H?v6ClTw~Dkoq5GGn0c3=HIY0q zyP6qRl-S4@Zf00N?VGY)RkZ-z;c3?&=#)m=j?m?0;ex|C_Nar3ISf!!i*IIEWrhW$ zjwF`-r&|MV|ip>uldO++#FeM3x9)Pqup$;Ed z4-t&P5QbHNLiB)dlfQOSseDHTeKkpCZUra-MYB0ZM_{ysm{1@hK1)Cu9^o#Yuf$^C z9Ca)4SN1{^t;c$dV{wQYFJ5W^SXK%#>qG}lMpo?vAD3sfLh9Ys1NeQA zo(cq|z%-*kdEbdL{xHO4JvlkHq;Pxwr!q&stQUGFk^sn00<{Q6Dug5 z6T?%gL;%zX;$dA?P!oip0#vSW;vv^{YIc-H;%A9cSXnYB2Pnw2-vBu5=qp-a2}9~A ztar+9$y$62r4>bfl`)~~n4RAzCpvF4&frt@9ngtR1;vQY|EVRCm1;$I4Wi)0Ncb#3 zDNB|Wo@0D4oPwgA%%TU{ZMYe)i|)&aE|ekX`9f|jy@tJHz7P-Rsl^u+kH0@yHFZ_b zyKcQJ+JQ{wD5{tv4ixf=nO4xq?!97~4}#XKd|_S(TtWsiU!k`wurR)3J$grr?}Wf- zTfPPasBE_p(OJyL`Z9Ek4vDiWQU!P%(*N z5MGzG+vV3Nl>ikunGk~C7>OrT~(388PuD`QnMthGGqp0%@<-G zK(PgoH{M*p-${$_`9LM^gJfB&Ozst>fgMSGXUNJBU@^Ojtxh5m`!+y*10N7a)|g>M zDJyq~dK1E<@%7(eucmL<>)<9NS0r`hBrx4d$sy-5GWj8vP6d z8JZK;OMr6GRtR;$hG(&Sdmc77bBH=y0g4kfTOG3vFawn0Lp=)K#3xX6t5z9Z^@;rV z@6-aw2QfIP|H=WXvJ8*?>3J~SHnjMLp;RH(1C%!)VrAv^yAVB?v0yAhyzXFsy_wBat zPT1t^q6ncnj^IB{P{}+SnMiDG{bt8(HcPzy>`SE#fdY<9OhF~}96Q-sd&dJ1t=ssngJ4* z3QCmmN;2o3oH-q_Sk~fmRI%s1M~9#@H*_oS)kLMEpO^#G6!}F5QLdm+0Ab>tmRPK) zpuWPK%MDOWlW&4WR0Y-HKkc>xcc>}ubK#jrC^nHo!U(!7K*bjDTM-dv%8I|VQh|@I z)X~jATUBr1EaZF;y~){-HpR^JpBpkNo2y|mTFASSG&?hhQzTEthf>f}gzlLK1GH)g zDg^OWSfS!3>?7VW99IcWh0rKhTVI|lEJB{oq0xHyIH!%@$sr!e5}Gj zNdpu4t?nPP+ZaO4dBnVcxp|98BVI#T^R za{RlH*xcHaK^wqN??ju6@gq?AuQKpz-Q)~hA;Dg->Yo5L1MK&IPcHA8~g^T{&bc zXn9^6KMBlR0;;T7?m>;-Nj@1pX>1fcZ&F7yKn9v zXqY;Cu$P-Cj0{za=d_B%j_B-zII+e7vTO|m^w(^WBK^Irb{sP-CK8+J1XTSREZBKS zB0de0I+|+&@#Hcvgc5ZRDb{xr4>uTQw^SqqnhvYi0Y9%x9E>v#8=D~xlODi^-dQrI z@`alB(Umm2fJr{IA6=CQ*YB&U!XL!wGV`At?$b;_r#dMFy4Z&&sUwF+L#2*36(e>JJZF~m4Q4O%`vAZIBwA-B z33l&dlHg>-F$uY#dWC3gR+4y2h{E2ENmRDiLBN4br8wmj@QLJ80Wv5b4$Pir(iLa$ z8T>Tm%OU_OD|zG~6-8qs|LL}<#fM@D^A{Y*^8MDVpt?h_0V*h^y*5$>6|1P^5A|2> zRma09D<>!hI$9VVo9?cot}A9)%`KF75Nhqk3MvexsU`RG+F$rcLIl$Be_`tZ8C&Lc z5hZ-yh}@*At^$<051*VTT8KWO#pfu2WT_*?b;(jkuFua)wH{Cr5RHT>g2Z+A{4Nvh zGJ_PONn}!LtTIpYn@(G-RFY-P+CA?1n6Tut0IA%+1*pJ(LJ37&6DwedVYG_T^B>5t zaDWz#lB{qmuKb8!ni2rvdkau0V`f7_C=M#*`q9Ez)#m$z-2OwDm{snuIY32| zyDSciDyaEj2}On(Gefgs1gN{vjGLclZnCU2@-~CosG!_?)Jb4E2?~-sssdDYON{~Q zmxf{y_+u5DD3vVte{4r&4|{+Dhc7UH~)3|e}T?OU%Z%Fcm8oU8#O=PWsDkmp6Wi2*G zdn==Vs`r_#Dzp~9%mBq&y@(LGBlPMXaMC4J%o1m1h}7usq-DztC$nA3OctvB&A=|s zz03&*G%-xd^z(kIBn%PrPbP(@fHGzBDZmgY@&01_8@OXv`x_-PReVs=MI9&Yi`3B^ zMmd#J2MjToUrgKt4Pquj{3NGc$eoA>Pb`Vw<~8X+P$)Qs^4sl0J79<`)$}d?J<6TL zgX~rQr|N0;r=nBdHh0L+a||crbwYM2!?<6L?i$i+>jWrPMWGIfO2Uvx^2&_YO&(WNM7o4+1awlZ zdjyz4$y_%9jU5u^_2lv&s}7qKi(QdN?|>&zkeaBG8K7PlP7G2r!7n><=8YYp#>`(P z&!&{XtSf1A3I#-a)S{)woO5IKlv_ftgRJlms*EZ}ldzZD4Kq=BG2FqNB39CQs4i6) z4CyNu8oWZ{RBGu}5M`P3^FF_3icB;%ptI;qF~q&nhNM-W{kpEME9vm z>Vo1!(nX`Fh+H-Ed>}Dto2(JWlqKSU8iUk@8wL1L5q?h5Zb_7KE`*L*T~$@BcumnB zHOVF?FhI3}GFp#WyCd{hhKE;Kwj8Ph39J~P(CjI|K%x`&yDLVabz;58olVvxEgzP=iP#Ntk*5<=A^vL2NX^6B(AQj0^`5H$*J-FF@F2L_eh< zo#k-8L(3p9#0oLA8>A4g`$wRR6+2fW^Pnaf!ihr&}sLdN#~o3K?cFNLF#l%Z)F`I8yxnCNIinyd;>+q;KW=B;@*a{|5QX+qniRO zkvlCcnS~WpRdOlxh8lJ`+Dva!E6nID{46izOOSe3?tS&=zks^6f}pec)OX>8dde*~ z2d6g#pO}@+1s%UHlH8w*y5dFT!{q`)PSPiiR+r+;F!5l9cy|M!g`Qgiaflk7g_)yf znPCB^nFE&V+Mz$j)t&jNd8CA5tbo+s@#R{T3$?Y@!FfG8738D2(n<#taT1|xJI z%2d#7s%#lt5B*Z4k_DM%cTGZ0M4DW&1%LunZj}xapeo;|n-Gf~VD@+;^a7^Emwlh| z>`{3LM~M`TbHpuVq%|Aoh((03gW1^S&zG?D;5plIatfh6~%3u+8bj7tf1an ze8cT6SH^^e*F0Ous`bMd&UrqOhy77$0QIvccLcTlP*73gvnW70yo(OR^R8CCVi1~t5b}Onv>Z13iG3D;;3LSbZKoNA?)wa7N0Y78qEU+i&RzX#QGwh@yh z!%KPK(zXL_qBzSa6$(<7;{Yby_7VzENL@gIBakT}nm;k;+(GzkmrN+ZRCA_`Hp*L& zN<-gR@xcJ)+%E&}z0%4%WlSkm&C>x{_yAp`k1n&QU}A_kO3D(x1D;?WQ7i$vi_Qpi zPA<9}49&Od)5a1|W#JRoBYRG6=Y<6Ng1o(h{fp4JccEbKv~q8zI5?`VI%49b7N292 z(eZs63(`o6fzH&em3%*1;CXhD+kS{&l3}cswR~(Posv4rzE3JE`w9zua$_>19H}wH z>!~VNC3VE0sWE`%*N2r8@qI$%&3M1E!T_p1(E;I-EL{=VZct-j38rkG1F)J!%?E1f z-V?_Swbpa;aw8m>3{Ph2=DA`+b0}nHSb-%nf{z8LZkv?>BUDnV0@Q`}fM>>U${>tf ze^2oIU+3U#>k0Mx?4*noBbc_TBLs~SmSN7M2AV;2#9u4F4+jjPzR+9c1P102-S;;1 z6DXFiFnb~3iOZso0SYM-D8!Bfo^Uex+%xY4sObBYEuWdf018mA11z>`?s=Y=6H zBm}0G?MQsLH(m_6UzSpc(Sq#)AG$tXZg5k+K>7JubMYH+-*T%o2LIeDM+e#443e~Y zGx|$O9kChnFF3SN!I=9v%34*n@OjUiAkjx&lQV0IfivE#e6FB&UQVpld^Q`5O7(hE zQ?r^ekApr@x1c2PYVke)vZJYV@`Ggty1KXs-50aMtV4F`X=C+?*TEMV>r)+@DTl{l z{2RBy5P}0q!EQW-F73$NwwbNpIv(!dVfID zn9-H_b-t1`RR~a*Bsm)m<`!*M^uHTuJw%X34%nwgJ*zGl7mx%SdSx)gH~@yY5ogW{ z1(Y*7wdn6ayN}sRqYN!*yZx0p6*EXhf}o=b_H7vBIm6h>dLycrN0KgMnn(X9#56}L z<0ZPXnl+HNUQ8tN{Zg%1NBsAj_%7|tk#-aaTt)K?l6jC>RGhu3SyVjm=5>A}YLi)% zyze17i3c8SUk0}ey?pi)sGfn;e3RL?WIk1^7--wbPU zF)+oKn0Fv8FedZQ98$X~=8lOS`aH;KHQScFwR(!6uNFQUx>pC=ok$rGP{dz3BW9}B zry$;^e1TRSR#lK?!}zLo1c&Phn#>XkR7p8#F(>(X+pkP(Gmv$&bFyQx;j9s6B>8Q+ z2wWt`upZ=JQ#%pOh*5x^_bW@jIWs<`SkKO5r^s-@6R03zD8Sj-YisdUB_9GqFaj9E z*XLk{CUD0!FyF=*(XoYW*x2jg;G4km~+0R^9@BHL#Ahf1F3_s@>kOhXf)^^{7vqqWG? z?vb{ha?Y&+8f@`Z0jJVna*3L`Cpo(Cb0cGjUP70f2Lcp=Ge=7Y#GiT*>K3GtI@a#6 z#UNTd--U`f(&9tR^XPQkfuF~g5CmJKdl1d)B2Nn_SzC4sUEe2nE_Bi|+(rLI;x7xh zRV6jsNm-MT!IWPadRhytl3Gn%s%kpVAn&5h8zsx~4DyleavqCd8TZ;})p2CS{uPv6 za_0cj0pi{aJVG=x{vy-?WH8Fweo`})v;dVMY0Gh)NgcV;MT$}0fhl&rNUWkDjG1hN z9H7u<7Ba zr50aSGat>0XeHpN*LQ|;LumKKjK92bsplPy5MjA}4N!lD%!mdlqy_igaL%osH#cOQ z#UO1qWw(K5aq;@lgGF5b7hGNFCZdDrj(w{xpa?X3LS8_bSJZXPT@`$Fey>w~b%Su` zMS70;nx+6e9oaNQtc;Xj=GM!Imj|s3LNm&$;${iV zohXH9=#Xu!hX+xxulRqRy~#*?YD4T&+AuvSJ9a*dbKX6#)#POpsyPML9bJtQeeFW|HBqF-Tnw-gKa% z0HtOJtTm+$y}0?qsd~c%G+KF%TgLM_ zuK< zXa*4xT}+hni-^o3tvq#jUDV_gdtMnyBD9`5m>8_vtxiq}!MXuW1xx2MJ2T0wG$Ioh zGMi$kPbMjrDT!OgSQB6jUr_S>y$V3gjW9ud!LXHnt)iUjXA9j@^ zRus>>Fm`36rvi6e6SC#^lR%(#L8Zcg*56m5qJMe)EBEb!;+#lMMl(?B!T})VM3BMK zBj1VnH|2982B-^BzTlf>9_Vdz6ypT68!04q&vjMNQJDG7WD4JeKfU5l4U@S%7JH{C zI5Bizg*K|J$ANa=#e9q{`c*!Za_~gHGe|=)A4qR7*xcp;(m0Ag#nh*ny$K516~BoE z`%Z=9Se|rX

SZQ>SA{yeAlW`%rLCl-Od=2L>k8DaRTNA#izbRC4HgL z8%PjPS;4CSxQZr;na8a_#6+U03Q$P?|1HxKZ6{}V%e2_oLCS1D^1q=VH8Vfd;z9F( zH|;R;h*>y-=cBpF7a2agm6H>IoB*;KRCa>ZP7uw{VTWqPmqNd%>>uvJ92B8nk!;c1 zdU>*qPR`^fsdS?D(*49%dsudW5!OJrr)J5M70}#O~uTk)sRMFyS zwZ{LyXz_Kl^;D0s+bVR2Y^z3zuM3ylB3TIyl@bbKX_n&2ZM(?L$eI4P(Be}qi8o~l z;S=>B{y}*LkzV7k43C`;GCTU`|Nn?z#s}n#h4+jz1q#b=s8gTR$Xl^&KlMH<0X8}> ze;d&Ai*_GDL_)Z9wuT^&oS@uE5)@nIZv=P}oU+Z0LgF#;SCs^OgM<<#ovC&Hmof-t z0V)!mStXS_LOcA^VWR06P~s-6-Kzoyp|o@GgFL7~BrZDWzKp|njSTOEeB1F8K`0aY z`U%d-7}+d9ieH6O|B*QG(@&bRLfb)Hoc%hl_7XfpW{ILRD(hWSPl_8v9rRHO&gRBERN76_y zReW-<15fR*Eaiiem!Hb$FYtVn6t&QLEP1J_Ql-&|I8aeVLhzq10ctuJR{@H4a#C`A z4*`ntOkMB<3TSr`0F~ja6VO!Lh@VBOnOBj}LF?TLf^BXI4x>bM_*aheZ=OZXk{O}< zly+QpdW}%nY-p&Qc9mh8lclSnuJWsypNpBRqHRKN;k9gKRbIS9~6nD10TA1eCZ$XIqxM#5EKXmn;_+#a5N(qH6t~s!+c{1vP;@ z-ypHe0V*3qWr#tS~inP)5u_)jhb7B{+kh$JPX!cg3Hn0D}&Nhzg4bTXi6Izz{hz z-mzpyfhNiexqu4SdAAb;I~=H%`Z>85@c>2!oG4f^J{1d4h$r|>*4CWes>+(8V~c&n zTI!cXQ8?#b3<&|3O!=3E4_b@wS$VSOM=UsH4gI%~(P{V+eEA1p! zUS?}yr8Yypjmbrd`YAYTn zkAgrPRo-I-2c?V>nktLw?%1-gYUiD_)rhZ!;Ljbi#Hw0+Q`0xW6RPDJ1IriC`h%!I z5a3j07rzIz(TGw{Me^o=OJKrhT%SrdD#FWii;qdJxwaU?C;$|_$8o`3DPSB`b5_8V zMBQ-!tyeLW$7Y0gpU%-a*&+L zrfLvx6yr_7%196|vTp=Qwkx6?OC-lf(<+GZRk*`7{TFN{}`xm*n8}O{(vV z+EgyJv{eagG-jZ86k1R~t3XRCKxLgvXw_Z_S!X!86Fe&M*HOU9hY44I(>xVGwXx7) zE5%~f0IWMSQ*iQoVKuBWf%v<*q`VgbiBVYg%>r3SjN|}yA>f5!Fq1lB*3l|Jtw{4! zGYI9@n*#_tIMmESwG1=dKHC+bj8`cDtOoMcAm&zLT^o&^fVdNYtHdR*AG9u5psHGP zY8|Upw+9;oU}YVj`3cO(@at*?|0W?Ld*(kIZ6(v|`UhoVaD|R@ z0#pt-jz?1licnSVrg38>xY`LUmBj^XzjZ60Q)s=FicBeRprj!u7D`nfp#dudAxm7C zyiga#*>ym6q>kcqk<`(55{Zq$3I!z~DMgg9% zDyUKLow|{e!Ow5amfdJ%3b_KzH(Hh1QI#3`)}+}Jv)h0f7?5DuJ&;gqBER;xXk9Q> zY^L?b2*@u(JwT|;WMYT$zjF||0l@()boBMQ^P|cQ$Po5gf!tB<2WMZK%1A9*zq5WjBbjy&O6R$*LuhsiRf3q6 z_8mIZ9^gPh@6W!&iS}D(X5)G|?Sd6n;O*)=ueN?W8jm@-$mq9xzga6+Rql#Vj6N4i z>~>v{^J^neNxn8n&HT-}gtYjW!D9|s$f}(wFq~-dsq;6&$yW=NR)9i0CJL*Br$W&R%5+m&`!O+fT9B7@(k*R#Hxg%=>Dts@MDm>gcN9UU?0% z?7Iq1xy5o?VD)#r-tPRViz3_&+PUJb5}900q5Fk`p5sxKtClxdJV|6KdPY|em|ZW+ zy*KRbY4whP63Cr81}X-gh{?4Z7{&s<$JII=COBiI;29ZaKBbN_99+E+0xM+VEt?^^ za_}d>8E=)`tezSSZd5@{EwbqFi4-Ja3xicoxvkSZ#5%JG)@y$YR^V{DWVqtFh&S@> zI>Qnal?&fyBwW`Ruv2O6xaj_;7%GI+?mzANqVP6Ak|apYudDr_*g zdA4_-tv=P}8{oWE^Z6m*CN6HpFC>RZxy5XiTmhKfDfD zp=$`siVxPxQ>g9v$l|v!Kq<$>N{g>6E?YS`4ilU)OvW@~VzU7Cs-7kqhZyh0*;A~Z zN(JskS(|RP#5T54ORu1}&U>S58hxh7SJ~zMthCKm{GfIbd7loBJ8Bnbwp6W&w;%-) z7B|s3QC;sU##c#VH7f%emTxWqD(Q_rbQj6BAa9ggeJecB6-V^nRj9NosEBiSD;#Fe zfdWoemKcRPxg%vX%%L1=@x4`0tStX&@kO=n%6ErVP)ZxF3!3P1ahy$`)h#-NV#mQW z{#~`)PTEy`vQ4%VygERb)u7x7@KK?Jtx+eSRbEW$^DtG3Zaa53`O6ifQilJ!iY!y3 z#|cFrNf>}Repdm?;TI{9ZpC+65r1`FpE<5eEk4Cn+A$7v)yT)9wjM>T%u+h}tsws= zq+Xt#KamYJn?2Y;7sK ziVgu*sWz~dV|R_OfFi0EyQ-j2ViwijpF!$Qh`Tcxj=_mZuyuX)h7e9*Hb+@G!j({R z?KuWf8Y=BJ92npr(iCH?YdKXVV@HYu2EYOwp@LH+*a1sw8l3Vl`TI~ir#kQ*-y%0q ztf1+bS=N&q0hczP^jSG>OkDBynaVAkl%YHnQ^rzyTZ9{GS^){-#}r5 z&7!ga=*x*1B)qbr@oT2QY{fbEwm}#}5x3H)iZ?P6-e3tWe4l^==!KNa81di2+TGn;Cy1x3 z(n*~bShx3oDv*2%p6?>&s=)i7N?={R?kmZMtI)B^CtH!ST4{4LA?B>8@+Ka8bu)g= zu%XlL&n)UrDyeA0!2(oATd$+Bs%$t`F#%Pf0s~ZKn^9S>4*@$|wSvDjDy}T0WNlRB0#OQU|itD&OECV zj8^=RR%bfs-zvhbLVc~wl$=)ANwi>9wVe#&tAWM^BsfB?`RA_iuN%yi7;q;LBhFu8 z1^=p|V$%COUr=&kKrtzzl|;W|?Vg1hhG8}D*mS%C)bl@6Nupx}STTiF#WWhs z;$bk6727$AElP1%5SWlCEJE%f)#e8XOnI2>DkkjCA=Dx4ywA$as5-oNypcMf1NGjY zQhiuO`myg*1=tS3x62E2+Hua>>jbS+3CqqLwps%$KxNPbs-j|WdOK%`6?~+@I0l0; z7@!%DE9N?yw4yGyKot^F>S#3JB&+bHB- zHeO@1r(Mv(C);3ZK)+jQoFdkq2qGs{D1L`wq6(@`3(*PcE1z*?j?|Tes;bhH2u?>~ z1MGNmyHS2%S4T7=;Mi&FoJFrc?6= z)WWGMEoW}lsRCCIi_x_$^m^Z(VvWW}~**Hn^O zfG`Juc|OeZVg{&KMa4ihQNz(E3MSQoHlxmIg$by{W5)+j*qzs>71S^!F{&?K1*q=p zqkwrgV3sk{P>z)>K*dEA+k4rf8c6|tHxwCm^wV|_T3r*qRhan>bi6}M{v>?+)b;yB z0`Mt7gk#uvIstH{hdQsfBBRCb>qWs_*v01n6;OXrED7X|q#-cyIT`a2VhZtz*1Z|xuyo!?_dibzE!C$q85hbpMb z$S_t>bGBYpEcc|{*U{Ev71wT|QVBm?VCD?0FgBzwlys4Lk5QlpS!v2HJS&x7iV_Lp znM?2)oMtxkg(@ZypiZ@ux&g#R0(PEP1x(hCQmv|rQ&b(W6_CLB?C!AqceWT^^yPzi zi|qt~-!bnwMM7Tv4oC=>Q8y}Cr*F~XLjmeL*}*^mo)hINvCdZTqcQvMj7nUBZLa8C z+7ddqAC6dT6{tE+jiZ4EbYFjnVO@S+<`zV8M&@>z(R*|5R2j8ZGpFjX*b#;85NTbY zg)0z?b=%5vCPl1iWtHgo`1f2yg*raW;D>45M)V@MA)0ai7G=~ zB@3?1rB3wkRTWW5{hUIQIoF_)i~59!s&m+$KufMFT{tb%e`O-FYB;$%Q#=8#VFsYo z$nc*|yKiQODoM^-d|5+u$M#|ueWwg+5hxB}0$3b)FfG1ez!Wp?UAMastjHg zO~@60?ZVYBEk4w;8-`<|BVkmb`%t7+kvXDLDijN9@kOJ;*P;kL;JIKzi|?PMIq5UU zMfwoFNGrrZS4w78x~HQ)u%bNhDKy(B6vN$nv%-a1O%ytFxD|cG&iv*RNY0aq!tYE1 zV}QyD>VsHtJparA3MI!edCnD73{a~YsJDiu=^#dfXeKp??UGY?jHJc)V3zNd7N087 zvQ&m6ui;ccN*bx^iN@0S!YKeEjZ+{NIp?C@^6TG-{@%$Avf`+^s(a_ED4bF(=m0A! zojF%Z=cLN|uD0Ka0sd(R-jh7EjxOR(veil2RudC6hhk2IhX%%=hUewE2X1>51R%>c;KN%&NnCS0UrHdc}#1yaE?=0~=}$b^#AN zvz@M%-;R**r-QTFyL12NO6mcuIkMAk7LA=qK17j;fyz0PnaxF2fjPq6m3H4)Jii~6 z)KR6C*CqCj4QV8Ys5?jrIx~VCn4&P_lXEa1uZ1E6Vk58N%{f{rpl;-VuLxZ+faJ(NOz9$5{M8-Gy#*)&)0`F5 zB*wV`YKlQldHqMQb^kuxEAJ{QoPPZsW4jZFdZo7ER(jpf)UgoRZPj*y;%+eBCBwQ# z6rJ@$(_bIO=?3W%kd96G5+X6WK^-GDS~^5U8tLvX1tdmpFj~4(LQ)3ODGWx5fTa5D z`2+UT?&scjobx&#Q0EI9xnVW1t9|({+Tdclymj}1S6457xg5j?_ zKWb{j$FfjY=u|VAsxGk~Gm=!9=3)Y#N*dGS0u=K~g@l=LG>X!t?UjZVQ;%Pqc#O8M z&e-s=^B_TdiUZGr7*D?oG{i=&Y$#jrwGVSM;VUK0skJSKnw}|F$6aG1on;r6gMIf? zU-DL=41re1AYS|dhI%P`g#L zlCS&+MAuQgD|oo5qXEOP^WP6b;@nkuM4q8QQvMYlU=O56EF^g{|1_1}3hGODQjL7J zX_>2N9BYQ)T-;LpgTir)!W!<(*Oul0yWA!rDV$^E)-^P7e5b)o@Wv`RN=OQ`HOUKa zbKexQ_ut)0DRR^q)mR!9U2bY^!djE@nt8=QPh;QBP9(lEdts|Jx-F;#nfNEtaxFP{ zPo%up^gN?4KiKsy^X%W;|GB2pznL(%&R+NK8K+!~G5B!$GI;zc z+jK7ty$Ur`Ow45dDH5He|n6Hu}Q5BY_3vsVT&$p0>B=M`NzLwt5zwfDc%40L=UEhm@^i{S5N=RF$}%tVktHxE zIEdU4Y?#Zn!H)$?$NQ65RUwQ16=CN2kG_-zk_OB8SKh4FBvDz}57;(QnC1Hu>ca8n z!vi18me|v?*sDJF&B0IeDBF9HVjff<;0l9e=iLbX@bYbRV3}{?6&>h~oi7S7@yF}tw{BZz-5 zq(U7LP|FxbB8SkHs501BHP%%GbS8h2L$gzVwIP*mEEzcL+bUiop-U)8+!2zU#moMB zZ>q`@rtw`3KBpybgRg!O-S|Sd?cX%8(OjEozBLAwC{N1Cfi181nOs2MEuY5*i9EP3C1iBeX zVhymuSDs5H?{zr^7#~@At0tcEb98i8np{qDRV40{`@}-$&#|Cj-n1H`3WobCsLg~f zl%y5fE=8zVNwF1F!4MF-?+neriM7r>SPCJ8al`>S^#R{?C4Uco|i<}+dh}3hywC_Ou_Zc$BA&lGl2BWuMyg2k?t+M2>$5<=JW*h zem8(#JJ?vg{Zl&;4jcP|oNu3-^37<=1b5zC_Qbn9)^jOkw*DYLuIt4G?GjPN`-ep3RKyD@!S{4B)ctEK2$yO*36)Z+CetzE)$0ijkgp1#L8c>nt`ZJxeoD95%Z zLbT=evFE^>Ge3cMDBS@+Y_&$C+jGxc^eq3OR>&TA5ZHTaEeiSC=OiV1z-BmS#y@y* z+X&y8kvCiq>(chqop7y{0Y0Uk%0e| z>GH3JtvGwiq1e8dQYeKEM6_od--Cl|yz)OTO){&I5gAb>9yUoJGPk=+?--)$DB4pA z&L>0*Sv&&9nwBG4w)s1~WJA>0>_%MN5bUOGqjfa9`r#DdRYIA?BSC54OfZYY^%(!R zN_dXxk(68r3Lav!myMnUhvDQW#RQBQJ?c&6loLTD6`1~)(C6yn(Ll=U@mob)43War zjwmB~?y_oYxMZsmb`;^`Tm&jpns?k}&dt~iX~#X6x2 z3C%6ilTZ|?Q1e;$rfWt%)^H&hdU}lv+rqv(^B=9RGq)OkLb}hX@_iH=yzoJVAqR^y zdfR+#0d>@P=-k97+)?UsoF-vnJ0QmY=R(15#L2#yeVw`w#pLMs^!gQf2~CF^F?oCD zb0-1vtga42Z8@{Tq1E@k#mq&hT=}S9QGE3fvu3}k1AIW&R@|_!20;Oyp&5OsBz!n` zPBZZogR!2Ld^iF347(+`4U7|5pWOy!9~8MuAJe{JiuZszcGIi+NbkVf^sQ3MV%^*R z6nvIq`Q2^aE}1r<=jbIbVXnI%@3r6tedwS5;2SpZZ*ZZ|^ygg`*Bso1P7ie1SRVdX9A=TS;2ER_x(9AM>YQO9V@VU9mnLom@;dtO;Yu+siQx%+|bbT4m(WKyNyq{Ms&}}$E7FSdxpD1Os}R01 zkksV7yE2tNHXrnksKvSS?3kDQkrclyCQ7Z9Sq!yV{&dsEos4}KdwsT+XYLQQTNTRx zwswB^ZTn$t(xL4v&q$c?=lO77Y73*MYd{D|qQZ04o0UZ2mIl zM`5X6LJ}Yt|xuENDDbt1Q*yS|iq%)rypZw~Z2(Q+gU@;+UA-=5q z5Zh%K9GalRL1-M6tIZUjGuktRz$HHUxDD$8o^{BJS5{8Mt%sl9u^t22Qx4Fx|>V(GXwEu3E)J?K~9 zrEG@eju!-~Kndy}!;@$I2COt6&@co?BvBOMW81mm8ADVM)zo(Yy&%dbkIa!CTYL_N zZXadwZ85a=MWrBFKN{6&v6TY#8nseC!s!IyPlU$vPWPV$KU8?sfrZN_L1)I_e75EJ zY9`-7RClF#1=`-73zhLoF!AR+VpIcyhL&hd`-#=zB@UST5bnxS=v&3#Jn8uX0#@jT z`LyL=vsr~#XD_@r-kkF=Uw!_O;;EfTgbqFbY55a-&iYZw_P{jSddB?S1S;7bw(RR|&SYc@zw$IF$eC^Fim!H13JrR(nAlZ$ixEeVvvuB;{X- zd-^l5KlPOziR%(F6CR)l_9&f{<|jH8CsMq`jFx=R zOWA%TMhg2k$-DcJr-KY7K_oG2 zn@;waBHBSma)428yreeW%fEsij1h*r`jzTR>cz*HiVtDZ!Ar+jkP=VVUU~1 zNf}3c+Z=k9lbPyBJ%fb{zVx*;2c23tt?d;eqJx#CiUL52)K?wB&9^M0pct>ZrhH%2 zNI}cK81HH3=X$V>4{(xUv`wtljfu8_JqVW3!D;gh{CsPPb}H66EMe@wszNQ^y^D<$%u$LNJ@b%~tK1_0Ldx{Xxs781~KO zA8?bJzU-Rso=dikd+Yq#O%F060FUV$qKa!sfXK(51wdgIE+w7DItp^cwjJ!ely0V) zd1G?l09I}$Al8Z6ePS4I#se*x`|(g%jr&dvAL~5JJ>cd&-t~B5uux-dt1D5-B0Jfm zc)o~V_k_C(OdGDxxlY01ULxA@4Xco@;cbx8DcW7DHY!qC&yiFyK=sTiJz_|MVZ@<* zTUnE6L;OH~P<9a4!$~M1)<_A_ipCr%$$Ia<1M^-ulF0BJ_ViB* zjqtSIGRl;wT@_48`bFAHAIg%S9+POZhlL?^Bv z{&fAfzw70Q7e2i={y&k|KPBGZe2TJg&GuoqHR6=|5w^ZN{pDZLX7_iham^8t-54Bf zp=xVhIIfdSQC6jeZuiL9Jr5uImP-@Y57Dvk?r7|0{4>f=X7H*mfG<$fBRD+PB0WvU zzmP|Pr0N*Zku=qJ{8h8L?Z+<4we)WKQhd@*C7G7m?t zNTeMvIvg=`708NDADHo5snBPKj?jMSr}CA}OLBVp%uZ217XQ!0Q#XEwu@In~O83p@ zs0n8hgrv$JqDlqXKSUjm8{p^HclpkIOq*dH!^G0^Cj4z*raju=G<$CVRyi+Y|UaYz38_z zDDW~6$WG+=ePfL?^>Ve-Gsevfo4=h!4dI@J@4XDd<<|AM=dEP)*xT!R~ynXwdo@?JbLEA4hv{v5O{sPDKB9mn*3reG54R9zg*-98* z&-`FDpUJV4lwO>0MDg~$P}*0Vw1oMQa5*BczA+-yK8>^pIhv@Z4n@<;I%>ZZ`A|n2 zVZt+7`>S6l3qLF3H+c$`(XlDiWKm$dBd#m^X8Tk}$JcfPF&u98>-z1rQZZKKAp`gn zf!Rp!O81=V4p)(8|E9_3GYYZY3=`V($GTR?>oG}9bq(1$hj|hZ{CeaOm~nP#!0n+y zoNZbSfNEFmr#0gCn1z%sN^wm07N~b6w zfsFgSn!IZ0RYwPQS198{!3PmTdF2K$&wl)jmf)!f2m`Qly9f#R8$>Ivv4GnT0S+r* z8Qws1|5}MH@mFe*abML0;;V_PftO$1?0EZj8nXn^_ekj=W{wLCm(E~N$WCwLWGL13RR8>Wn3b#2UTjc;?*^ao5^ z1v-9)xvJWXnh7{RBaDeV?(NNKz6(NPUxm#MP0F(15P!D59d_Iv!`APHwpPh=KH~XE z#kEmUh3jc)cR??~vEi8M!Bzu}AR?K%B&sDou`V(hXSGJT9ksXJnM!)7SV5^0FSBd0 zviI8fJ43eZD8*Lb&JW|9Y66RS_??62O!kqds#quX4ybw(F~OH0juWVki<}qQU0&Q9 z5hG+1$3>eP(%VejPYo1$d4I~AhgL;jr~3S%$#9=#h1Pq-`eZ-NRR77o=YA>*A8t3d zanWx19px|i#JaYmqPRGskS_;&Wv6w-y#b~!(H|@|c@JGXZQTh)XWy7fxLA&z=*q*bWFq?* z9g76KQ*pj^%MO_XPcJ6J=?Hj?tt}@Q=4B$$nkappD%tyDBYcW+8$5m7BO_9yQ&XDn{?uT$%@8DtXXO}v$68s#R(O&?r9ZA}wjaYb zg{nrtc&Ka3I4mx+htA`H^3{_(;K&lr;|EaDwO31n_!&&US@@yDSqaX12$#n^d2{vx zx4?s|z-$waZjk7Q#2dN?KbM%y_vfncq9crdX#L?`kACSDRX7rzd~G11BSG_}miguP zRNOCvNqvv{5|zz-jsODrZlKzx30N2xB0W&ZDW2v@_typ^avrt+^J2~mXvJ|-{KNv1 z9GPOTQT#e+L9GW~@JIiO6jU?=eDVBIHna6E>{IMJ*S#kcHCx}V?zuCHs zT#t9Ofx!Qomh}HN3#*4E*rgjDE2vDD9&E+;|08#df(oI~{w@OE)qUzxjCu>YyZiLoZ9k1$ zj;|c{_de8BgNy(zZ2np=|8fB*I8Ogr)wl2Zhni1CF^zdcyz2+(bH#XngXga`QIT`v z9@%5TG1~YlUAxBT$M$bZ4YGUi9zZb1j774bcmpxj8DJv(zb5Z+A+O zwGX(n-jgM2k3Mh*)B6*i)t~i{mQkDsZ?L=z*9254vf#K_(bSFQPI)J+|L5kPyUmrUra zEO*7@0-bf~E_SOsD&Ci!d}4QMm(eQGF&~m1NgvArLeMN*+9Lb@($=?(l**45-I`nU zg8uN2U>Hc<5W^qpY=_(BB`7U-6AN@CVV9R3>JDXiQhMI@s(^~C47ca z%z>y%IguF?iIoMv$s9Ue-fp@M@ML6EoD|v0mUSN832s}nEwFEj?xVF5p&o^?GtqpZf)za&TxnfZl_!qsf@<`Z?a_z;G*tfSI!w#n!LP($dRUWqS#|Fco zPiN#6US;fi_9B0CsT}k4%zYg5vs2Rdo5}S@!3WryfPVuTFfJ`b+L?Wzr%wOM^KN!V z3|dRG5s>%Q#twfkIB^qDXKn3HoX1>YsH^RiWJ9>(`&H`UoyiYn9|B`4L&UW(N;HRN z`3(~tjAz0ZkAD8HfP$hZ=FMrEuSp*}P<82r3#=9@BVyDgRBp&MY!e#Jvu?ZlpQ~<` zWL`m5{BO=^LpcI<;W`A9gj@L&52+?&&;=Lf-j^Z=m+BU4P+CbPfeV^*`K?euF?ts4 z$+n>P=Ssj?pTLO2v**2mtMKg{K~6;De+FiKy(+$EbCMzDp3BGoqztCT*qV%%YOoD8 z99YmjTpuQbWv?cHq5t@8yc1B7)14COWVpH#>mQ?nc)a_|UjT7_aHCM^fUDci9x_~f zqJvivI2C^N+qyn<{bRtDh4bCEF4N?><>0$ADn6HfU6mgioAxrGS+ZXNTj*pYrNeXW zPXnO~;DS!)Zu`6PbzSsvMGyG<$PUYm>Ny2(!`*ehSRwD?^(3;@uR( ze;^X!R>w=-@M(RN;E|rlpOf#$ed8dGI;i(L;9E`PzC{DfEc>n9Myi0>1KzTUbr33x zuj%E9)6nJ{{>;Kp?^oSFo8s-8>>(aWK`>tVr}~uat_Tr(1Pl%$Og0-+puA!r{;7 z1ykQ#j9u&_VP)HWiKOLaDO&c6($6cz6IP)^CZ6nTTkEF#lTI7L0tOl7F9iSFPFx() zE|qd%nua%EF`co~JpUUQY-0=~ zVyFmt{hu+Um%w-r#FPE2^H(n6SRJa%sDB!yMrohkYb>Jbt@MDho+al7&kj{e>r*pi z7dzuwwP(!ft#f$`1*Q>qS4*DVKYkZwRkp&i9Bg!{P|$--y}q-XaP^8uxNOs-TS2-H;JnOzXn2lx#2``Xw@_P?e%G z3wZc@{~WCeOx5g|5}yr#&h}{u&L_QkGIe)zTb?nLFf>fN9KbXY7{O#2qzF(w9a=>J zJYrSff7&Fk^8Ksc_bI@F!6`w7!7E+=_5O{Dx;asy>>8JxV}W}_bv!OjnP`hCK40w2 zi?qLO0ElhjraK*FT393Z1p}`%P>m%*!VjsNdhH^viL=I~Tu)z=-y>?YFE2x;VuNh> zDy2urTfa3vqzsy$RoQ?N$J=ujO<%DJE#va~XsS22=7~oaPD&yD$1Z0zF61u>jCuF> zre)|r4Be47n2qo*kU_A7ipZ2!bq9EY)zamPVjK0^x?d;1Ai?Rfo%3pK?PfjUwjUQr z8S$6G|F?C)s`m*wq56;R4-DE)YatIb{@lcIG7A!YVsZzF8IkgTq5qSzJ-rszE_K|~ zAk6*}peu{pe5Kqu;ISoh9Z_J1!w3Iic^)#MEy4mauEVO%6p4@+k{neEd_mg0{j#Lw zKw0E>!_g{+J%7;W1cuL*R&D3w6R)6^_LuG1`PrZ%^Q{&iXWTy`fBL9mik=XyaU2}I zpb(L*uk_;xEFdye=K(C@RMc7kq$bTJz;UvYg4o>5tm(+=k~huaX~7pK%n4 zm`YHUH=IYycREm-raL}D@*BspZ43FBn-#PmQHyH^ z7+FpilXYi=^pOr*LDQQ9q`mR|pj=>r?hX~iZ~N@xxf+hL3qX$fc}}&9)y-*2q&^_+ zqj+mNn+8YE3U9_>HKkU6PSQ!*Yyd^dtE$+j>!S~UtfbSobGt`e+ec#)2j|I+l4-ie zW%R-YV1T5Qj3Kd-YWXx+`8yaBkLj*E{o0~5f%i-r(*}ZKp`h<{qNStXao67}oATn1 z)Xg8B`@$aeu)H+SEXzISjiY>stiPd<--Gws-htU)q`q`0#@^kcqU@@Mzt1~iS)s{a ze%2UvTKWI6r6Pz`c_4Wz%n0XQ&06w|!V?(lWLXNOD+6=Zxpg%0N#hiVV zn#`BH33xsY*91U(cGEiIztje3v+yie(n`sy-6(X+{UDJrZTd*s;FkK=gAII=U{)u; zvi>EnWnH{HZac}9h&V~FE07U$rt;zR0o*Bt_eX;Hv5UsFFyS{_6_CKb_}0H*S6{(!81m6G^5QkS;cA@P<4=bnex66dR^y*Lpft$0JhiVzz1auBNOh)m_ z38rY@EoSq27Ahx&Es4ahWi)48E{-|!5pC}+RS^Q}La3Pm|xtq^`hP`V7Fnw-AS9~{*EC&P>ep`s|obgZhz?hW}_^nf_Xz${E1f&j1=rok~y7aVqf<;-tWrb&NO1sHZ`*Q_beqrEaVkD)=RiQjj?@WMJSVH_8XXYjX zz{BWm8l!;4vv|)efELe1U(+?xc1!PT?KX(o4msRMdGtl|=dg%z!flU^QsJ*sGEbpj z{0S7;x>p%bQ8=1nqcv`le3NDbu0dD;r)YaXiSp#d|Im<7fziJ-7V%>Lz1HTN{$RtC@G~dS?(k z_Gcot&s!!DY;oC)8b#Db$g%R-Vq;L#$(6HCaSs935~T*CW+jAfso5A zATsBTA-HVJZ#-%-GhSN$ypdL|oGiS0XZ{d11#J}|0}A`}Z4A4g!i1HOVNdf0pG5O| z7x2Z3to7f6*9(i%6eKbeTrpH732~if5^B_5*-SysGoiQj$E+MDO;L~)kKs>an!k^K z!j6-{CMi6KiI{cz?Pt><*Xq^#YltFZ(-dBA)^Ct`Zq z>6Lm=AroRvrm__CPkdHoPhrKt4)T`|`oQb4Ff?iVa!n;BCc#+yPGzCaO3)H%P)97ug;=3vx3h#YMr(3QsQ5CdUHFr&yvd$PIN&~ zsW+Cx-+STL@;1c(FH@>^qR_K?x(d&;k_^w1RRO>A)3N?9kECQBx7`GfwFDI?Ae=EF zv2)*EQPlHi3vx6o_j1SfwP8u17C~A ztQ*nS&HpMk=ALu2ogQ!jD%q+__+)m#u)GQ@0TPxdSbRrEP9zQ1KP)%6JnC=IU!qf- z3kXPhFHY86fY*%O9F6f58+TwWB5k464Kp04mtsg{GL}>cG%}uwvJz5h7+mCWFGTjh ztWUGd*2#V1jArgRZRepnJ@l)89DdPeW^?i^tvMXx2Nmu8^+(mOqpD@>PN)o7JjHmh z7{d3ovrugXA1Z5II{}Y>8n*DDKY+vQdw($|3 zH3=N+8G0VO@SE)W5=dCW+a5E`VJh+IBHx;(&d*s4hMJwzit}J>1hFsSfiB~guG*m< zuSeC310oNn;enn=Nx>v6-O{-T3BpWu_r{1sy#cTMeO}V(6K4Mkt0z_?s%y!Fr3S^7 zUIhaaoI;k`=7jzIK!vVS6o&m#$@)9ZMb4PH6Yi%x@3d0YE)~YlF+IL}>XUfGWSuT} zT>;3#@n0s!w7xVwIPnpU!(WE9BZjaDeWJU0ObG9inZae;*y{72o|joS`VuOd>%2RP zyLixCx{emjLGB-tTj5o99t6hK%xsfinpfP(dn0ke4x%WY8Gd1L=%6C8;v>raG&el& zBMrzd3th0^mqa~WQbc*<(+w(!PF^%;etKD(mrarUy~9iW z*?pOp-`!nD4t93DpDp2 zTJzEK%lCgY`pb&0PafRK>0Q()ti`4pZRinD$fmhXzi#PmM`JksP?jmx-ez2*k4k1* zD+ZSdopzA|h}iyEO;jT0c{&t?ZSw3-5!nQ=e8qnv`}3Fq#)gYsi0AGwJpNLaMCZ5j z*pEcGtb2rt+>6+U+*FwyuoH-rdxG%}enFw%LZ#@vhfQkA@;mU)nc%zz-_vK@XM(M? zq&mXwk6!K8WVHde+dkqE-=ZZ{bYC(`NE%xr<4+P3=!MWle&!m$5HKpu4)gObXWW31 z??q7>`Est*F^RLxqWExT%B@yR%4Z5}dJ=i|snKeY#8}eLU#fU?KnycTJGX=yKm{li ze&~O6eV$B=u`E|4%jO4HW%?x%;*hT=X%I8kR5gtTQD0DOiSDkfXK6_PbTZzhI(w8> z3{Eg{kXnG`0>d{$V$FSIaIz?;{U@e2+A?*hgYWIms1^Z4)^p>{ z5mMoyKwNxoG9hLgjB!q*`;x%dVZx7|ktNTkcbbK_pZ>(TqYD_q z7)iS*lWxf)q?<0hh2i%uYC`NfJK+r(eCN9*YgY zvbm;k!1QTV{D>?~_=9|xE!R|LiDitL=4iI-OT!w>+uPOpIsbAP$yiH!S!({;W!|{I z6L2r5z}~!daOBg(>>NnY!Rm$so2R+ zR;sm{AExRO)XL16};$0 z_3bk&&^q-bq^z6)Js|&a4p=zegcIzsDykI_#yaCp;HqUip>5hZ+hLRnolkpg)AV6CSEidz_Tu$;V|3X=3-I%Ikn0v$&H)lHm9RB6#xr znJ$P2c#T^>n%PLodE@r>B*^xDJElu+CUj*a(#xfK!rb@40#q}OlkOY%6IP?#IQK&C zHlYYi)=_q_hPQV=bp@I}7!J)7?Ro5xB`t~g$G{Me`$xorR$dHg_e(iSm91&~b85Mj z$8Sk|8uWQZMN|pFYd6?QL2XJ*gdq0g#e-2DfQO~oMSJ2G6X)j`%N<v4T0x8FAF4sBy9O=Wr?_}z8=SfkzbAdOTyVQF00CPQY+ep~*$s&FrX zj#=%hJ>0Nc&>YU9@o09(-cY+bBJcMpGF1r!iK~CF=J#&IXy02aNKXt?CX2=9R~TK^ z;~lPlf<}9f)P{^$T^T{8C?V6zhzbFt^qi-%4GDRpO0B~(%)pXbPA#FZ>L%LkX$JL; z;telg?L!p2@ZaaEU2oD5p%YTJ7iZr;(S|}3I;<|@ONV0t2$>SGxg!dEigw?19Kk}g zlIcL4i%jqLt~XOT4JteA+kC3UpMP^TwKQ_SFDBfig+o1^4q&Z_9*e?Z$xm10PxFZ0 z8><>r@)Zvv=x;kkxGaccC~7eBxepj9>jOekOGY7>t5FTBa(;tLD%_0;tRHrZKOCPc zO8fjM25pc9VvqiF_rTz)P8a=$H(Z3lhc4e&lk%N5ldXW)I==N$TvM%a0-{C}3@hpM zs<$dR6Ek}s+ciNiEy(Q{iN%+`Wm&A2!8z&!;bfh*_&@=2MtDfXoVCH@r$9~1Hb}H- zbsoMq6d#7$8PIEi%C<7;_1&cFe|6Ao1$@-@=`dV=;Q(V07NoGyfY;7WKZ7V zRO-2ec9!^Cpzn%P0~6h|5M`hwK8ztmaWi!+3= za0~vo?ei?rc15WmTr#vk{BuqNZd6sd^wZ``j`()4^MO%x?BQdo=4<&6LsqY^D5&?Z zV~uSRJm6}drh5rxo|jW7geC4#85#^8LRIN>v;V_U?UuMRd<;Z;jD9mCmR}Z(S6dth zkH1)WtKC7dXEprOt&vXII*T8Rgx=S-eLwXF6Efein!5hCU&4z-}?iM zkVZ`xW%f}ri1QJJi#y3YTTlBpcB-I0@q1{UAkD7oQ<5H}`;X5+wN=ShZPu*s8; zj`5tXXTcHz=Z{L+V8AYB=xFn`w5@H-|%_q z9p7xvuqHE#S;;Y52ZOK;$2MKtCH{B1(xWC{g&AjmxGbo5vWWRkH)9rawPNYZBGWXVAxUc4Tm+)M)v^T0l|x-3M{o zw)JRXpC^t`!3hOYWP3X-i$CA?R%q5aPPp?Syu%9xmfj&zY8Aa2>$@gbJ5m?U@*`XU zOYfCXE@5!xo1MXV?eLXrjkBl{6Z~aV5D)Dk*P=^VJt|m+J!raNR>F%A{+IHTbaZ)d*JY1CWv~B@f4ZqV z1@&2k3b&nX9b@}_AkJ26azeC))!ez!UnmT&o1VhGP5}b(izGoFZ~_BAfG$qLMkSG| zM?XD6&OU*C3n7$72KlRUhHmJ<78A8Z8_)&v|@WlABI85Yjg@P-2h23gfQ z%tIR@B5D-jPF#e>A9xwZ<8_QUepICq~!IZo{UYAzlc!+eyftL-W z>Sh9Flg87Ps%HGGbRU=`eYmt*nt4YSnlUxdhRpq}`GSDaFvA94PnWZ%%BqfDkrRqt z%bh|**j;vm4>9dzO25y=HWG+`gWdmGxn2s@NgGL~8frhEyr+j#NT09D9k}d@)DO_c z{~8+#9;xa+_pch@i_jDkKVNx=CsTd#6uRkE;O-Zb%p{p!jK36-Qq!HpZYPDrR3zj+ z`nGLhpoR=_){O5!eP3W=|AX!ef!4WcK5wo{3NcYx@BYI1sKE#(aPr!@vJ{}ADL~E) zUsPaQIyy3zt+E@#p9~N58@2?x6B~W2Y`U)4sf|1dghF(%I&5!~HD!HSsNdhQC~uq@ zco4AXsq-`}n$#1B_He+(h6dj(I0b051BivLjerTHKtnZPC+EW*Uo&~BnrklFk_(8^KOl_s4MCP|>mLX7_1O&|vbi*VVrR zfCc?E`A6Y->Z;~mLL4+8*QUnpRL(SSbqyG@^ZF%g8s}XI5_&5#O;?gb3yh$2LG&N+Yv%RIXHa!)b}n zf~HM@(kg5wG{xAcHR6yqEqxRV{#i?~t~awv{Ms9%zkm7Ja5Q0Rhunu8G2Cj`T8y4B zAZ{-0+G*pLo!+Q)ht(4-#r-6DK^DPpZtnGIKiPwJ?PGZcAm>LyrXz0ls2M1)vdbOa zbgfjbiUA}SK`PgY8S>q0*9-#obyR9drohB=*%amtgV#}QHRqNK3HQu;|B^^?IrD(Z z(xqQ0o89d5BXN1vRwp&C{ zrz~}mwXRr4!a>nDehr-6`SEYlFnXwQ_GX8h|XD;Y1MjrGt#<@bG9`}1N+j*|42vX# z_;1ur_`x^&M^WdjLlny;P0JPkywW(O*_F!Vk754S9lR+E({Y8}Q9jm|?i z7-aUr|J#O!m!gT0Vl_n>oVQjrg+}uJpg1X}CU7fND1v!Cg=kLKvko2q&nA14JIp=T>S_n>7V+$YN$NY|&R-YJ@3v=s_2S>Slj(h)~&oF>*mhO5CH5 z968cXR48p3qdw1XmC=7l3Xkuf@Jc+5f0~#3=!f5r-(QqNPPnI8Tg!|Tt4sRii&87o zK*-jO8lS5=nzUX@j&oiA@{=pIu|B^|#mQhsLdIW_hD_)++Qgb6%UC*EaEF5N03l6Y z3UQh+$ND^%w~GSgaZ71I({|G#NuPmuiB&~2?smyr4rux8Zi=2Beng0?RB&{;2>wER zfKv!ACz^q|AhInIatI^-m(a41aFlx9HJ`iu%Hw;s&BV*E9^ZX3KW01ZIy7E93;eI< z=AGJl!RJ-$do^C4tViX{k9hWL3qP7Ih9z_P4mHQR)7H%_B+xXWhW+gS@8gxy0JkOC z!03FkN#`fCUfNA{CH-t6n5v%%?z{E(6ndHWGOUv8pM#+`lYva*E@+kQ`z?=G1emA> zdfcUtWNI6yVUlrWsaPXc$6s*%ghgQ$fi1(;rC`CyhBhS2NiM zwi2viB3w3|YC&^xD1LpZa1sR7sf!(Y0_34}Dve+Y1opVNOi?gMR#p z4c~XFhG(icMXE0HsW)UX#gi!DFCiPVZ*tWtVF#gGoVC(;{RY(y?`}Sbim#5TMtOey zlj^JAwL3*Fd=9wMPRisn!bp^BM5FGIEKrYlWvpd3tecR_ooB)7Fg`PU6`!uU3Xw zNWdLb!fsm7Ts^e{)3AWjT1L-xX0O)zz9j8ACHcrej*eV>R}Xs>k*i(F=g^&=IU!?> zB-n2*$u<$V7<|l%MmmOsXmhX9S?M8K%m}i6&XOZ?h-R$K>TVLQJmznjATWMTlgs?bOVAyFgytplS(Ukn{|`q& zxV{RmK7`eLMJwuP{V`2brR39=I&x&>yTh?zf+4y_gDdE}v7kK&jTnh{O#J&?{1DBz zpv9;7AfJ-ic@)+Vze5XdzHg`9^e7x*R|&h_pw-oyTb%{&+*f!0b4tkeM9@Q8e9s4J z@#PuR7i;mc>dDaJW8efdnpkbA4f6V~qD5yEGY%9gmALH1ZX*(P<1fqsoLix|MqBPM zkX3S7Qpr(0UmZ?X$V95WC3L%@bP6KsvH*nw6C&oW1SqGq=gJ}dE*YNH>f+PN+pAy? zB&W1`ZB+379mau5>+hssZpzj=Csg73OFu@t_Y>mpthYIQ*_)v(bi&*PliKzPv5P<|@0UwH0RV%Hv zPpLQTR!}U6?)qH0oTpC&uTND%p8#1nj>TP>q7y3Yh@_qqx#&#oh{eu*`O?rdgkmdg zJ;w_E)EY^m$an=8CqI3s0%iY>meA@AOCSnQxuB64uYAZrwG$Q_?3 z)e~k?M_)vbZ)$@*Wmuk@2@!SU=8RC@vl;|f ze3Pnx{s{ruQ%07n&sRwsBI-`j*h$o7`%w{fBUoZ26>^3$?$UC`Mk0P|`Z&30K&6>l z1;k;TyJZFCTB)Bv_jLopFal0Qj@&*7vs4vSCiG>o*e}ph_yS_FO3fU}x~%BSoie8T zS1Axwf#(w~I!D={8|*4j#p>@q7OgC**W9T#U6OJ~CMoX;*6REEsidYB9}0!j%RvDr5_DH+KQ}{+wre0eoC<2^z;>mKR=&vZ!1b?a13IW0M~g3Rj!uAL zT4N4^&Ovc?pyxaMq*XO&!AI*uFH%1 zq{PyW()mep?I)9%)iud1-?L~enpOQg=U0$oQb!1Cp7-~y(B#XYsv#U7cIYuogLI_# za*4kyXz0;&+PyI?zF9$KgQ+eaUujRR_=9xOce@e0(?cGn)X}|=I*KY1zeFmLibRk! zf?9ls+c2LDET2UGxun%k`c}EH@YM=s^`4zf3Qi;=Cs2%46_&O5E`jNtsQahX;`?dw zvDW=Cbm<)WO2?gHHK0{)4l7peqX1=8N*1W#p<=N+C1uC&qw0BPk--)5*Bh{c@A#V_ z)Qck)JDKHoM%evX_|L#JA1pv+cM9?ss#5G$Lz(;2onf7*F1n=Q z9faOVwU1BFpH50Tb@lj=7T-4rP;=h&+>237LP;InO{t^NL;>GEkch7+1dZQNsGxKD zB^Db4RM%jt`@!yoB>k%4)rtYvDF!#Ips3UlAONxemBA0cAm=}uEsMpT$hJSl!EpkX z1F-mnxa$t8?KDJu<-27yj}E!_gg9^~Nm;4Fx`|A#qGBsNN*(1E9|}6uCbs$%ruF(uoqnU`0an8zKJ>ROJMiU0<4CNdHL8g7zg;@?$yKeLCqFl4p85uidwBcx*B>NMBHhOw(jhu`YuZS;A9Zn;eG8YS2&;r=lk!9^q%BI zq30p7*yo=!KxME|RY9pBl@n7}N#PV2f^~8_+IsHmM=+XOK~(__eICk#8e^R)JOER8 z)%Rqj*V@$vbePdYz&0~eo64Z105wG@GvDHiSZ$!p@2$E@!DLnHXjSml(VshI>+Mt( z>|l4lgQ}`lb^qM&t6qNv#=ipX{$0rk`#!5x-~}W>0u%^Nw1Pqbip`?tBcp)F*b_ET z+InN`3l&t_fWsKwj6#`0eSTR%&0e2_4sop7m6iH9INZwwTnDW;#-0}6Akgw0pqRym z5{vyJ)zc&tvHXLO4YrBZ5F7oZPsOd@@?X-o4YA3R4sS2R%J=AOOaMV^1ot^J+ zYW5Tf>e+51KVGw3V7ZaW^IyRhN9k+Eq!ej|GF08JR^E!%sRMWKoX1%?;zWTVE2vXw zx-QXm)o9RdMOFcfRa$qVwWTbOk<`)D^kR}KbCdjVGoVN;3Q))nzB-Uj3Wj#(4V?q< zbi2^~?#{n6GU=%x_etZzQ*FFXTlUl(Q^_1XglS<8P*FlGS5Xlj{TXI|x5vSO&^$j#pjqr}Sh=@p>F zg3SR6axB8;n8B$GP^s6>L^Dsf`dop~(?nEP6OSF5&WvfdE!hoRonX0&mfV$GTG4Ji zo%lG(Rt6P)7o^=O8aorhTtT5q>iJ+&M`+Z+W;+NlrV`~YF}Cyk(L8snb95CwI648U z6L7jizla*?qVF8zK!%cWFYF*dh`8|LU=%v$hHPsFr|8frEH*9yin8NLmbiPco+P+d z2lTF&RCTbQ@Ty{(b|=t~-C&vDcPD6nN(ySndSxe&1MlC+1G%>(Ak$v_$ouW}B`mBN)n}vi*^t-eicXUGoR9DN+jg-te zcbJt>ml@sB;#+dwjSjVri0Z1V>uOta zcmB2`h5D!PeK=UQd4SkwYHDk$j<6_d%eXLUs6DKctg98n-=E9o(l}ab4K=u2x<3 z`%ZiFcl&ScCJD}WKAGg~K+oqsaRSs#5LKHG`9nF$YE+tWs5(Q4%Q7cgcSdi!Gmr|& zfvfL|eZKe8m6xviV zK%H#a?aW^%^ z-TCj+euIg}#{ZA4{AtCZf|Mf0j1P6X4H0tIe2ODWi@kGByb1r zy#fzs8AA*rr#}B6>g1?=XA3nGaW4xz_&?3rQ?%B2C^yig((KH4PLXDJ4nEglZ>N>k z4OYAJ50_}{20mBgZUw2gB9^`k9 zRqE)r2y#=(6E4BH1J0P0*8|yY?0oTIw&1FgHlyQKRer~T3N6D#5kfBxN#{b9e5Vkg zVsM%PN}8;tOt52ZzDktbozv{-1D@jbxK)t)T?Lcx@{@LS^SbbIHwe2)!VdT9baLb- zW5^i*C4n$>Zbr$3PltZ<^b*L9~gn6DFS@v$UuCX6#kRRJm@`+l|dMG`v- ze$+~ZvzoZy2-f?d49!jLan`s z*2@w`83FXn%6>o*7u~&wcKy5rs1wlP(+7XoR{eJbqMZP+dO+?3y-&`TR^~mOw%$o) ziJhdY5|2g6AkycX38m_WjNloSTYbo|REcYM(9o++;Er&?Fp$ur_&lR9GGve0*|W6x z+yLXk3K{yYG7j7w;<4YMvnCRlrfTsU%v5llrk%zNOqa?j*}6}2wfV@KY{fU{)U07= z9`Pv-Q1=jD;qP>3QYVx=RuYPnfos){{^aL5Me5E0Dpyjkb|BE_d+yoU>bn<7Xh!!{ zx_u+jS55>R_WY{SapCGK;<5^^jx6fmT70>J>iBoCk}|(3V@$802Zwn9SV@_Cu-yQ4 z8HyG}+zE!5er;#DzNR0%VnhC8FsF<&mvb|WD1vy5ET{NIe z`x&ATiWSrg#Ww@gSmYzQ71SsK+7Kd}-5rl<4)cUp{A>Q%w%{%t9g%b&hLK@(+GVZv8Kh#8InjM8NM+i|PJm+Kvg}+`ovSX-lu5v4!`2RH z?dAq31d>>VHLkSm*idc=^qtGUa(cDV&*}N7Bur+Hd1`WHv|d(2{$g-)F|h3fs8zW3 zEt2P)PGdZ zcUp~Iqb=8n41pmWBJQ(2@H2Okfhi7e-@p>dHTPi}6P{=x?aV$po=lx!dP2nZgw)U} zciT@OF+UmHe|PfISqs&_xd94QQ5k-Yl2L^2n=o{h^v?6;ZPWnH(5_YcC`7S`FjQn( zd{s;Egm3~4P!7nULSrcBNXO@-E5&g$&?KycMKdS1kXTEPY3xNfdFEq$0m2~MD59Mc z1622b?7Exo&QW&wP$$W$p9G3H9IREb+7qIz-9vxXi)sZ;xx2O=hLK@(+x-R!-W#1m z1ge>EIz(Ouo~VRoV`pc_XwVUZW#?)xGT8H3k>Q{RSwV6ENjT2As5Mu4(RcCq6|uJ( z`k}d0_Gig3awM03Pd#L%43TF|Q6gmmBHk8xa`v2{2x_N6d~Y6xvh*q0ot2EcOGxda z!nzg7>a1#|YWx&H;H2!+4*%&B2*{lzio(m2!}JAu4qEG3ZQ=_kxV*v7g=QaW`Bj}7 zW*zVHJ%`x=b5+L~`p!}AVdrvX9slG(y=G7`KsgtnQiiBzM<>K$-RDn!!Y%c95f}o^ znqryG;HC4=xnSo5LCGT-_hmgTcp2YB1oM+b)>YMkZmXt3)SVDObpz!IMeuGlu{&GZ zIk>x3Q0Ka>0C`rm3LmBiS>7`_`OASSCXX4}7lPHB=yRS;Rmu}@1b>k%BioLVq~9DT z-*K3DtDJdnDbP9^u33xkQbDO;!}v~)8gKq0chXz!@~Kw+Q`o{_ExiedV0O@vfE&ON zmzmQS@S}-WvFf8NX)oTe>?EU`mL{(LCj!<0f{+IlaAh_rcBaQYS&0;{5(r~3LU zJf=?tlvA^(9qtXXVgIDh)rnxVI|KYzBsA_lL4S|?>yr2Zc=_C$+0pt7gD3vV+I@`B zJB<%xkW%fwIBM8+2;bo^#iBy-Qk8N9YY|E)01|UR(7}2jDr~!?00h zDOsIZiM?jCK_p$2``59jtx!X3=F|yHPAg=09&-u>wj!+RLa7}S#je`MDu?S6@AGLS z->FLTR2HBR_r-epYIAM zxa#V##g}0bPF83drKqJhoL%R|jOexha#} z*s&Rf+C;3V1|ab|+I>TaBCJ@)t69`A1JqCfYF1EVtQq@tW>dE&xWSn<4WVq2l_3x? zBKL%qYVlX7n0}b$e=^b6ICPDV40up&y{fg>txk3iMkfHcS?~%{YuEScQy`3;dvmHv zshn-y_SmlGU^kFO5oxzP$rQJ}Fyt$|d-B1+(7~fLA$kon+%g za?@==y06Vpv8dhm{5Rw>aH*z3vidw5Vlw+oZk4xGMh&xsnki3ooq0zSY;jwdHFb>` zqtub5#m5M_t1!i3wgiWHp@vY1U@F3|{|5`>g4PQty%3=Od<#&8Q)diLDnLDmLU|s# zpE_`N0>>xXO*?k=E-d>LX}NQ_ueLvTIc2A2MyGgE9XS3@wb@mgh(RcRzKo{L(nu&7 zje+VXK)nDIv*pL;Qn8B@XHu*Kba7!Qz!NIbXVP?4KpTYb&p4ApD8FZN zoJS3V%+I4Q9aEeb=4fS@ExWNPw4eyQNQ>_+KsnlXj&ar~p5K+e=)l{Rw%*$fPL-2t z>BUFnH+=yJdCsI_KN_*uFB)OR%xQJGKrTQ@~YOr&V(731yJqt-{ewW;&8gD;1w}4^XQQ1*%9F`3AG+ z3`PG+e5suH-iw)3M)xt7UUwFy+#+0Y@X_SMI(mYU-+Dpuxz4uE&}^8`AI7X{49|zE z6QGpQ;QhT~x_}|RNpJ!NXR@8gP(-UU2G?f&gu23N(E6k?;;Gr#C-_v|WTP8cKN;Ya z9sQ|m{V>I0e^=6mIwBRRx3uke_m#rLUV`w;=)UTltIox$H8)1za8`|8*txD~MUSjz z?xH|CsE!q_JXig^vT8>|#R!IYK45SPec~9LA{ZizQ0B$zNESUB!mlR}z#THK``WGv zo;#zc+L9f=s8z93bx{AmF=*~qoTrkL-MQT9gia-ntnCLAQ<21G@C2)*a;tAOK)ItD zG@BYGO=ujX6+NGKQIb$%v8Mu#1G+!~aERBReOGnkGMqf4^}Z5PZ%P)$;Pia31)MV} zrV8PZ1v{;=oxtR(O`PEBtT6viAm2{5zE)Jh)%{yZGFhbZeN1@5(Rt=_xI!tY4X zIs&t*nnJ-TS7sAD5rfq0%=wu~a5+z1PGLM{LSP?t#r~>0~Ep%Tq-ChaJaq1 zuC|`5j-CTklrwTFCuPw*k$TTR=i=bnL%ZxiP>T5cDhCbD{vy9F|pWRAc9|pWVw)SofCtT#`L;}a3+@BNk!BbUJ*UzbwsO(z8cW4pawYi!^#{GY3^%p@ZR#HxYib@leGw-G2RE$~1 zhWP6WT93VlZ1L?HSlz;fa))v)DW#y|i*8JGt3*7f*Uf%M>-`l7b&})6ka|v=V0U)p zLXjOM>{Br3-GO)qXNDEjtpF=jGbG0fz5{EV z7y0>|=MA`61{0mC7@YF#>CNGiY3q?z9)goQ!Dp9vs9JEJu+jKkNJ7SIL9M6C`^l{| zc8JD1Gn7wyGdbkp)db)~b$ya3?IZ*zUc3743P8aSeu^tKTYWJ|MPwfeRx>!w2dtt( zGQT24hOgB>x~ zQ$6&o$Ja}x;sH_H<*56w&s9VR-B1Dm0V|!#3ap0GF01gS6?tnmpPz`=i`|i zhag4ekSh2&lR|Q=-FJAYUA6RB1WCJ%SwXQarcmM4y;vv&C)nJ-dGW?Mxn)r zcuNd^9xt5oAen*b5}*vL_Mgn;pDvi7wRe#)Nl@}rn_~s`z2e8?2BT9*DVP7WqTs!Y zEX>!Sn)R)i0e0@w@8m^&%DP_7yG|xYyUxR%cKNCHB~qQZB)@aU{eJemP<``}gH)c$ zM|GVz@?c(3$Y}7kLDWSyNZr=o8&OxOnRmerjx^GpSQ9&Zs*&L2DnMl!`xu|#UKmRKRsf)3y2vGT4yfyhqsHj-cOzpti-?aE< z1*Q6fZh)!45G&6gBv9sFYwpL!oA=bRnm0Dc8d2@6XhfC=ohxmK73g~Z&=uTlqA*6oTN2m(y7%Bc!avBjqX6inPj zix1I?#)5KC7H43i9!He?4aN6JT6}}do|vb|ic!@tw44U{JO~u=f}4Z+(+p7epmoJ; z+hO3^+~gI=V08#vDW0E(lkc=^b_V*NAi194ICT$p2NkxfYJR#ky28CWnb4e0yjI(Z zyVvxMAC>-{`AEIZ$U}1dJj( z2anId6Rt5KRrCGPs+x~y=8=^oVt|Sj)02k~Eu2~Pbef=hqV?1n%I`Y7u0Wu7s=`xh z8LRqp3^KiIn{i@R_OA;3yTF>0@N-A0d=;+mf;_sP{Tq!Ch{@p$P?NYD0~DG=-3e%7 z6v_Xsu!8T1gAd_`j%HBTPB`a!Rd_I| zBiQ0|MFNL_9l8;>6QE|9`YRIFlApopm1+F2vZtj+%vzp{A}@^G-ir&jGBP_I zggQK%9l~vgh`JjTPAX}j&(G8yI)SpI{=OynAH^mgRj;tksS^%c*2$hnVab6P`>GyN_5wy#*-%&YB4`K>NM9?#}aV${o$0 zv(ZOP@>c`YXxJ!Kyjq3sgB4UI-a3Twlw(ZDO3Mn?{w8lEgkrOL`P(Cz5_q#@3x9M*X;y4&N*CvS0L_GkGtyNr`pjw`(|b4 zb&{5VUL&6^F-d*}s6^V03g?#q6_Rw6#K+*oMmJ-@KEo|kI#%$n3MvPv74)5p_CO!R zvE_Jip8#u`S-WShzzj~E0EK2xReP`FFLQzebE=nqlH|I}$y&7%sSedoK&4N+B6sD5 zP9ZA0NkiwebOOw3($r1RPIMmHz=-TGmwuklpeY{vRzZaTh3q$^Xl%d&Jpb;(zEy!y zS;OZ*<;I6cmO5e)uWNYa5Wv`A4h_u)QIUw~lqornAm%YZO@c1-eac=fh(y;#60Ho8 zPX`CbvSVeIwCZZgL|J!dA)R2d3K}?xlv@RwtmrtdSW@g#1WuBTPoiIUq@G*{Y}V$3 zv#3UZLXNxHz>tE-QljtLXxxC9>k7ZMg9d~Fs(OJ+Q)!rBh)zIb!6}Nx#sKy8GeFD= z<{?1+5@CGa{4e5|JFBOR==&6Msynm!M5}2R;*EmfsrgYHn#aL;CupOAJ4;UT_nCiZ z&+SxaJFxl_L9g@gRr2u3#HISTuCwqeaej41>{L=Q31(|D`9!_c&6(To_z{hLo{3CE zTY=UhO1cUpmB2!`u%Sv4k0cgb`7?El1}iPT${!@VLgoN9+0wrwo|4PKriFV+MO7)I%!Rp+_Y2{SG8S&H}q-E>e+q@2PCR}lD@_V5GSOrd(o5KQH z4)CAg*r|%WTeY5$WpW*lPtB2Cz(hP_zEg7i*2Vd$q~^Jm1Jqk3^^Y?y2B_?`%OsCh z0uJkR4bugq0$V(gn)xdFj=f>7xH?0#Z0&vnbEk`HFVW!3{!g>T_pPkm=i&+f{>hDV zl70Uvt+`zlfR%YjXJ(S`X9YgU_Z%slXQ;4R53Kcs-p4(}av)@ZvwMg-0>>+e#vN_M zPq&+QEBI57xDK)vJTo$U3sB5y_s1J*2B(V_A4v9H2X#k_uWNj$dMb{hRp`T{G;CN6o^F-b2X&=-t1BD7yzD>_fhQ%1ySyz=uW!Zx>X+IrK$I? zh}QXYRaI7X@S{X1^0m2G zq+Z_1&~~-by2!DW;BtbWQU&BwNT^QBq-@g#vCa$BH$5If+%wV+^1tVwIRP?PY}GY% z17vP-uE6VeU3^chRpti03M@K7YuF_@KZLruN|rO!dUe!v52HQ7eFNHM(?NZO>d^k2=8G!gYM|w zog(C(fK#7rsW>gL9FTHgfhZ?qU_*yf&YlnO1_utBKqvlC71* z?-V$qx-LlFTqVuXc$K;9q5#EgH>r-&<+1bvQ_?Rud=5tQDu^!YyeA9c5I>W?XMs z?my*dq|Z+q5-b^{xzpCmhx#|*mgGR7g3Tj=W2%7mNC7ZIhrbQ3-D==O z!K|vLPOD(7LhIe1?Iv3*?YrvtcH4Sy^F|S%9>8HYl67Z*nvWMyf+X-LKslf|2ZJy` zp;EQ`vKVdrcN(B1nyvs*bSf@~A2rKS*KT8$yir6ERZtlu!78Yojocx}o;(;%&9J&W ztepdACDYEc!fz4`x#F;t4d9Nk0MB~uHBV6Oecw1m10`i zJF_zCCPTZeQRf=63W~`ceSrWq5z9+)qWy5JV_dUm=zR~JBls7R|O>$ z%`b_5vO#iv{;G;dfgnc1ZsVb}_*^tbrCPfZ&;~$zi~xkV*t`?5*w+9twfG_dE(fTp zt+d;A_>}TQ#V?wyj)b=n=I^_LMRF+fFd99tExJ&3< zwJ+#+Eur_!?X3qna9KrC9rE)4kd<_51?T6hDys@$==F0YrG78l!?+byrvmC6?57f{ zP8D`Cq3XQe>Ws`uf>D49)$FvJLP(1&5B& zplcnkG@;%&Py#;r3Q!=kcu5_R07W-8Ie302a_IySxYHsz3F+P8#l&`AXRZ_5BRN1J zRNH*s6y)YKcC8#$=^bzB?y!kas)IzpH%0 z-9Mfu_owbcDwBU4!UUvcJA1J$G=}-)ua`h4|dkt%fVy@rfIv9-!DVQ zF98ZX9|f)adn^FPKp2CkR6F?zQ0jX##9<8jD|4V-L%@}!sB4>YTABSs7X>NSsQbkN z6idcwfJ!YsH47RBFLkw(Hh_du{Cv;nT>PN2MeisiE)XASMfLV3hHS$KP|v=rYVn~8 zY934vAkvsY5*?YF-=XcbDZQduVdm8v48ab*6}?A?=-biaLn4T52F0}a9zZ=9!G2FrQhg3y$&414abK;HAfI+IIm*>v>^Cz)+Grr7!4?lx}E$XScfe<|( zRbSbO6=mv=q|%@HRI9U@6-3?&$cNdv{4UYg(}`#fP;8F*{BtHMRs|IT)H8Xn0@M)b zyH3k)h(ZE&(wIr&U@gAU&{|Tag9Aukj18%bi<;Zz-+u!s1r$4&^ z^(`pJ>!E@=s;E{;>8oqlS+ghSu%|TpR+3Zox~zT8lq8}S9}|p4wnVNLpG&k=&gx9= zs4_CV5OuRyEMxdOZ^)#MDrqEHH4&oj1@j+n)Z_-dG^;>{TeUpC7o>_F4Y$t~((R|k zSXZ+|H!v|TA|x5~D4a&)+~w;^W_-J^Gay)j=EpZ|uAQkwau<*M-0G2y=U9Lw6&#(- zeF6G}QkBRR)%T&+TBpT#YTo2XhV6C%T$F3K{hinRC4i9trA!Ndi}4en-dcR4aKc^niE_|=qX17ZExvouoyvTGti=}t)OSK$WhH>v_UHtqoj&;r zXKA%>zY1eNHTXt;Ns55V;J2}|i0HHk$Nf$L=lS=0fa>OU$yW+EF;it$+xhu1F=CJW zKGWyN32Y%?;WlPy{l#Ejs%DFWvPv>eNr8O=F216A(OJ_~l3E3-Zvzw*1Jw7AAJkks z^Sna=YOYg8)IC;62rvW-T1@j5!puhl9`0JZpVIEj5*<)$Dg`Jebrh8(m{d)rByn5fS5j#Yg}h2<1KX@&mK;rp$UfF+$GC25)8mU&Py zK+ScB6m+sA921LWz_=B(Uf1^tp$A75ppeS!C>#*;2fK^toWHp2UH;6RN;Pv4puU6_ zUuN*TT70K?Lyn@uj`exB<*-BI+?_c&71Y-i)pXG)dm}T3O!IlDPe~XQm|}}K2Pih* zjO#5{?lYrRfemS?y$&D{MC14PwXS~xaBK<2H5`Mus-P-uz!US)RdB>9^qiwkzN1RM zQvs>~6{U`50;bf=ndQa+qT9C*h=99R@CvRDTYM`(RNFCjX0AdVKlSz%N zkh7Y3oMpfv@`mH>idFXSBJOsru~*sj&hI<66!HHFOdHN|vQZ@p5G>H3e`C@z^Z6)P zQDu`*L0!Z?!~5ny^?c4-C3s!iZ|gk5;wQ1#t)NcLq`GoeJIKJU)qO|7;v}z2Y4K%E zNT$X2wGp6ZkV0B~OqfBP^Y=jbRW2BVfa%aV|BOk$0@Ob;3tNj%p*^a>*l23^WwBT! zb(Dh@)8hLgAc;xNWG$stDg8&GX1?0`_>{%RDckn$`znP9WMvKrwQudbdupfU8PZ)) z5u%n^6h8M#Ryu$>H$McQi$mb)sFdIAmC$qe01dP*6(%ac;}5=hJLxk+Q?lzZ{Hm6v_MoB;C{Xo!~M zJsbczFtI?Qp2zqrD4;2R-{pJG*&9g8qNI~b1~PEv(`53Kknx>8wxYxLFzrbkSua%4 zq5u`!(rmr4F~~^h=n8c`M1fHiCN8fE1t_W{p`s$29Sy;Ua>Zi1Qb*65Fxk?Rl*ZWA z5e1cp>r!A7Y~)XFpvR)BGXqVms7`b?9Q&NCy_qxf$_8T|sFS3K+8lAC#&#Jy=uyR# z1IYjn6wpS3_S%7vDFK8)RRzLaNoCaP{aQ`EuKis9*}0V^X8B%qc}}}j1*gr|Rp&q1 zrC4p}I~=u$T4hQSIatM{-BpsP&gnrg2ktCOfh|VDEL@Z%cC`4$#*nRR8Jq*uRFsGt z`Rk`||26eF zAv#D`XFr~72|CFzk~;bU22s0>xyGmb+kE6Tx&nq!oh~JJ=gzY7^Dji*P9#M9CAZ=l zW~5aF;aovgMupFt_K)ufD=3z|KS%|2>HzNyhut9JRz96}p_?H)pD)V~RT*SP>&40` z2d6m0K@|}SIx#q@05$(VCPVZ2w4DX4lzHRf#eQ(yz`s64icGh|-0?mID;3>{yi+tE~vKK!%;qJ&@6TRicoS z3U`L%+NBBJM{Iag56WczLy7rGTxEDEZC(?R#$0D92HE?j0% zw*~Rn>I>``TQL)@H|Y+raaDlIT6~oq!ATW|6|T}wfICgFbU!ybAl^hVjs81;lS8Cl z_WZ8R0EVg+^~Uy|9^3{(gxl<5|85U-3C^Zj-@ z3cou!Ix&YmwI8~Ps+%y+y)e%+2B2}Ij!92)Nw;sX>^Wmf3CkQ7=SqkLsQBC*m@aGYAMZJz&yprL;IYq?18N?O4*<%@yefk5 zyTH$7@{kgYYVPxij=~NAVkap)Ex{E3UKw&CEj}5b0NR;}#X91zuGqILMq3FqKos8& zxPkeDC;~1LmmNfbf+93>1=$b8Eu4@1E9)9>gb}yM-m@L zi*FPT>cgBhM4uAW+E=bBw=D}Bs-GEfR=xUNpj`c zma4wk_dw4_L3+-mvQhI zL>7yEQ<5MRe^!z}iV#DHzgC5Z@ycIn~XUKLd>^TMiNjMvSCqTWq zu$p4AOy0lRgsZ%foqm2LINKo^yY>S4aQiOoGe8AZf+FStoPiKusd9|a`gnIToT^~!O3T~s0PBQ zLV(~4;7~ZZc>P<- zaM=xr{Ff{T!o*^!v0E@LjD!D`%7)ohcZj_mam*7!ma7iA=o&HPUTh_0O950p<%4#B zHbxnsNF@|!ou|Z-OOR0tTx>Qlhz?~rBLnEZH)$j^iwg7EET)XAs1p@^2kLz)3ENE= ztAv#OJ8PMuBsrc>F>(6_B=y+Rtwgm~)_VtSt(@40`TB1a)boLqBu0UkA3|5JBZ)DD zZAYcW_tv@*axhWqXfo;1%p`6?X2sm;MOVt^Zfl{d=)7aWzG~s2s;7un%60+>sT9Rf znSBMawx$gFz>8uP3~ODu>vLBu6Lk!n^{)_vL-EBx6&1}q8T|Y~0u)Q&R_CBAv#~CvZqCHVa`#KAdX(#vtXk?rsa(dKJI5q9?tf_;P^i47G+C zaFFuE3V349nqp7>MZ65b=*{OxNF7lgg3uuAB6_;0p;b+@&L-}XD4n*Si%?>IH*f8| zAEUw8#z3vTI2gWw2ohhY>Vc0%M*I*0%glyghz`t8`I;h^DOxBkayZVV+x+)KQf1aykgr`s|2+GhJ4t5Hy66 z1PoB42`5*rI%F9<42>tak-@AX2B=pBWvH3Yo6Tg7Vt|UzSy_;sp7$wvB3HU)rLEGD znu!D89H1gf?2BZG*)cS8oe*q0gK2I}SnRDi)J1zH2dmETcsSykF-QflMFda0LC}eI z-zz|+`K;qOt+;|tJFhFJbpmx)&6>Kr#8vh4i%JJck~47~iN$7nl@Y{Z-*|jsHq`|~ zAY}+App6aM(hFuj{h(BfuVd{#nku#}{uOBHM8Q}pcl2}fo_r9ef_em}SCrAX8|I|? z`3XO%T|`y)cXEFnp_IO^R^v5{mT*1hTBxM9h8XIeav1IGgDOL<0$S!_L6~va2N8On zDyT~pH8c1epcoZnMXcBX8+62#Pst{&qH3M*8+8u9h3wb!&!|VK)XZn{7L#_Q#rNi~ zS9AuH&_XUes?ku#3jU%b;Y2{?kf^{Aqv$`X*Y8Lj4dGlGwfJJMooSEFh2vy|pAJB~ zhHj_W@0FPCWgwg^-JyI7;CL{jLFN{EA;yxHo`XJB6-_l1J{8yyZDSZxGl=Jfswj)d z4!{!G>GivCo<7YVI>qTbxmR5PMppID6_nDZj1|8jS{NqM4O{@?Du?F3+SolpYzmMjA$iD!^nN#Go*qfwx8D{}1R#M0%0#)!82+kD~A4#pkfuBPy zz8(J#*O9R!7CVI05tEUDN)m~zivko3Qbh}+D_i7lq;B84(>LEmOu4fKloe*O^W1#1 zxBh%Y5lr?#l0WwZ9B_KzYSyXRz{i$3d6(hI7nd*N` zLfmeH3x04kAiIvczcZOE!4fHPD`vEBi8j)t972yT&nkCY)9$QknCl-(9jOZHM32#V zzOs(LC2cgN#Wx4RkzM^FCKeS`6`)S(gPmw)tfKh3^R!h#+H6S3d~R}pLbIm`sU0Ff zvFz|B{A)%7q|k8~RT5qm=&0(=(@sUIl8{Jl75INZL@%^!lmyEp(_BsMBr}}~Y6lU! zQgy8&cO4{b1`);J!~#?#=|Uw5A!NXKe;u({C*XAA1_J|?0cr(xq%44yok!K{yl9*W zY56B)Hp;sY73V*ZqQqqg3|bhUM96i)5G>m}q5HQ()Xi?J-$fSw%-CmN9aT`#QbLNR z5@{hfff{Cj8UWBnV|54Jdf`Dau!74J9>s8s4SVlG@?9~VQETtH z31cQ9w8t(!I0#OT=1{Rk<&p@UP3721pO7`$@vU+yr&xI^Q^INQ@N*Deh7AY^zmF} z{|-gulL_fJ0A^hR6tJF&zGzW6`I(%vvvKm}@~A|uIc0}|&SOQ$?eGez7N6sESZV83 zXh6p&Q?yc%s5%OFGvOPBy7^exPZu84 zO2n&Lb5#q&8BQw*aLf>28dPNSeT5qBGsxYIf zlh`ptZMRstB4OBQxHt91GM^)(H={3;gVuA0x=O9tA)dP(2{%9v!q3X6tOz^LT7?4~ zZYW7$UnsZmGyk7pt@2=oguJ$AzXNSmO6TaQHmij>aj<0Oa`Z&&)ZQ|P&1-a=4l;iqV5nG zeIDi&1Y4!anD8~@NbSA2@n&$!BA6Eu*rd+v!d_=jzOz160A#_<@k{H_Hij5Zzh-g* z%{4m>b5@H~SE9=KJSek~AvLi(=Ze3NC!Y| z@agKHKtv9>!CB9aHT+n_G|&LG%6D=;nAt&TJZW-wYNc@%r>KawFAzDF#@Q~&ou zN<3|JRtd+h42)0B1E3Hr5xr!yCl;WX_+_l1W`MdAciXBW8YbOgG(io+u#Rzu&H4G! z4#72koABWw{L^yaNAuc9$(XHJSUcVE?T79S`-VuKNWRkVAPuPrWW&VT$RNgm{ zpsP@>h@_hxr0>83ZH#qZYNajd@(NX9bKyaqYQdq5Q=#>`kv49+h~|2;SMVyRzt94? z2stJw?aC0fJI`6+I=N^lR~4etKC1#0Yw<LKoPrD1s@Nm>O4ISTwM0^De!BlrptbSFPB zqFj;05hJo!1l^Uw!*K!F;YUG`gxbh&J~f6a`3e}K8op)cQ)R(C3{VWYc@N%5h#Bu{MLO=$-Sw}6 z*eIN(l^EM zSwW)GsLTZ23y`Kk2kL?+6qH;Z1<+jn+J96yRX^01^va88~%Y|@%N+h;}xZ9cWBE+xa2Ic^z z9CYWXBKxB$1&NmePuNpC+In5OkwuS4e_n{XL!j@5h>EL2Fyt8Ol$5N6aZe@g+;qPP zY+%NbTNuhG3eH>10dTH3c6#kh%gpiQ+2Mz<1Bh+78JtjS4{f>`oZbQy*m(2a#o+Wx zSOS2?mDX}wY+a{a1usW4vrb}%W(9+&o5yPeeSv@tNA!uxRa9R4^I)a3+*FOAL}!J( zyNYOa#AHuegCIF5mHf`@>4JzD$`z5=U6iYug?1767Vj~gr4%jTQ;I1J0lQ-FUfRfnz3fsk6d3D)opU9K=qpsd^Srx@Gm(jI-;CBW zQ#W^LNUFGD`vuxyI;8&a%F0o-Z$r8thEPWvsD68*t%E4SXJ4pmUc&L>_os@9yX@{&JsAq z9YnLJJddgpluIBW`{NTy7HcKabuYRV)vPKfsBFfB(7|71uT=hMl~JX#psCK+9dzH# zbV^sB=MtbQx&L8;CsvvFE#hQQ4ww%{Kic1J406& zJ(xGbY&|^4XF@FrM!J1IracL|KKZp~LNE!3k(cH==Z=Ddr_g#G=iC!c&{l#)>@?hf zYJHBRRmkWWcM_mTtM4zgPU1R{WZsbU#0tIY9I#d&3nVTuDZhuSSE)WfN?vCx^sT}Y z0HT;cvjBAm>h-9-S0z|gvA_z99r(ElfESp#WAUqc;GH3G$H0o&2BQF#Gw-h-q0D(R zV+#ZEK5`pl_Jn7UQXB6f4tOT0iog~y8w1a00q{F1C12VL?74#B&Z?(4B#)UhxCXzj z>kLk;wU@I(WEW8x6Rysh9LLb^>+ihJYM@m{bd^LHL%PmNYTmy~rvdN?CvYNn92n}e z+6OtYbKC`16T3_$VTs*0*=N-w&Sz7{0SYI_8Eu}aGa~k7)+D+O3f!}*jH2R3{GaXtz?ceWKC<#ta|tqSZHp z)U2Y|j3us#+0s(U4hm2#AUse&C=$EN=PVLC^?52$X|#_~D?BE@89;=TRU(KeR!(0* ztv=Fha)7#nN~*KoMw5oor7h*SaeA0Vx4C0EAQTg>zuROmOvCnwhaEy?fRhbbf_fkQH zxf99=5&BfM1kuJp$`8XB>Z)MH0^5$%(Tdn7s-PIC2=UZjEhj|a4Rc!*pxi_Xt+NaY zsyY>A0A%K*#q(LRiS~_Zab`GqPUMi&aZIk2Ywjw_wM(l|4%M!ZgG%V2qwWqnrWC3e`7*)D)Ec{%5fI6M-jK?_E_*d05JV9omqJo8>+qRZ%&hR`Y`h zD`r(w1V!XtI+{H>XiNr-K*oehHp@jB&Clf?{H_)m3!M4=R3hjEPUyCqwA$cI3izlo z0&i@I=hiryO`vDOeDH$?X$%668L`hOAJo&ocT(&GkJC{RsEB+ zQB2-fM37zDM#l--rC+>NP*Lj0)z%ve0qV9m?3GT9L|Y1#ywT9G3Q`Q+m&Ikj5;65r zpxrFEy4V12#iU*zwGmOpSH1qLqGT<-*+<9QCkC&}SN;$l9ogtJU!KRydD3ncpyz zu{@lO`RGNwwIjL{B|mC1+?qJp|c6i)R$wR8Ud{b`5d9ge%s9W+{9^By31*MvkmGVFjX+i@Lnpq3S~$7I2)`RXLf^4gWBmWzvQe8Y2dH>Gk%vvp2vtIHir~s@Iacj1(E4AMhH(#qotf^91pjy+ zUL6OLROC(r>NudbI3xD501n?8Qd0av>05!fGT+$?4ov_3x+@(j_3fI z{cBi8sD`vR!Y^_S)*RTaTFsD=R*oJ30P z1GiJ5_8GFp$QqMxv)@oH1*`VBZ{CvKT%AV203X&A%U zXXDcWY7{d7hvzYxEtl9|uL!*PM#lkU=`i{OmwpDRD3t_eF|S92l)h=xrG@<+hN$uJ z6&F8vTq>NrVbXh`&jYHWXqXl^+FvX%2BUuMD^n~ak#bAp`yFPB{{L-(A~-n;2(N&2o$t`+XC-L`#YL}= zo|gj?`<`EE2BD;jA|N93aa01aBejyzp?6`);ri6vgfnjro)yAxwc=80)Q*Zf;zms{ z5pA`mdDJwPoLz^@>=rzp2Q`3K!?i~fDUx)30osF`*v03wZUXu|Y9B?Zq8GtA?H{JC z7Xwsve!cNP6VlafDUXKI%4jGJ9Su;Q64zwU?^wa#3(d8Q^C;SYK~>_8&@drLWzqz~G8_RXa*GPBlP@yMMG0KhiEy}3u{G@^ zdniFA+2{?- zdMYqPKB#WSYga6Or8P#u6QKOQtAVk|8uNlh3pf5O|9|y5=yj_KPrd);@e_RSp*1nT zfBxMIEE)9B75^!NBC3E@t%;ktHYy&|XskD6cX3MXzY~B}OHu6+Uk) zuL=qs7;MA7EmSjF$Jf6#4g(gb1~87x8;)&A$j1Sy1?YLzLUR`ISM~2=^Pt!61|;|y z(t(i!kMq4NwPm*9piOn^rNcwjIDAKu6sbVyzJ8@%fj+xxy;P4|(4ojt$#5A*-?&sHAjb>{#7{qKLLzk@#0srO|+>pmYn*mqLjv-&*o&y~;AeZKnl z{2AEi$ISgaNB`eFC|PiHUuX3uxd$`b z(9WU8u9tg1+{1k(@H>Z2=Xom!*7JVPdtL?pZm>KNY@B=D36@ntP^}Yn-K*rlU8}p- z*?GRRzdFCSvlg6vS^eH>BIMrBPLkLCKHa_k3Q+&%-~5|jkAM4b|Lw2Gzx#Lp?)9_y zc>a7oXFmV=v;6$|y!`s}=kw3_JM(qt&*z6kpKU(!Yvk8KkNKSVnc~mlpXl%T_v34@ zYl5yKVLV#ettc6ebHyh-!cDxygvE;t80a>eg2;4&wLHz-}Cp#|CZk? zzb;xc`TE6clh+;kZtQ-=---7fT|c(2*g9h0Bj4NVGotrZdo!O8=zY|lsn#}HhiDBt z_krDebe81jL9d~bI`+EkKCt&;iFdW1SmL1W3wmu9?Blu4 zl4G@3SdtO{{e|M2k?_C$Z~xoB{+s{izxnI==kvizz<>Ym|NWow->Y(Qez527{CEG| zfA`mXpZWLaBe9rUl@iBiV{yo0l^Y70GdhYx=dQSZP z`26^q`ML4)v43Ol8-FkV8Q&|ve*PTjv&GMiK2Q8C@j6$(&t41t8@k5vI#ky@zgB!r zbkEf1jjw@zx4O^hGpqMQpQrj+Ui0X_s5P#hpMRF$mtE`XUZdxzy^{Ad`?qS%vHh1{ zgY9Fr-`LO5@3L#e_9Xj`=$v5dPVI&K8LKr^t>gIqv50V5@bj~MGlx_w|2pq_s|Ut>Punn5OqdH930mXu8wQ04|LCrUs0V>aZ<|Dq)3{cM(L9Mx1QpSoX2Eq9M`OD8h zHs6CSSaxGoU_gg7TM#NxR5zY2lq%an0aFDg7QFK2Mc2-$0`l+1&77}e4gl35jP9!n zSSY}=@2-Mz{5KUq*ulz@A$E_@HLQ{j^>BiwTCXgz$_I4k9;svieXgp)L)X*|gjIr} z5>dAQoN5kr3Ro9}1<>lcRtX;3<7|!Red{D%`MR?4K_2nU-=F`VTaWR1GXOs0$=_yh zdHyg4s602BKc4|*M#ifD{tPy`GMd3Dw)x`EauihuFlvvn!x*&+(QBg{>Kq`dBFcxl zI*3;eQMRaAYgD!KP#X=kN2-m&GI3RT=76U*xyq{5^{Off6iig5h^k?hoOG%amCT^~ z$*M`GT4v9$DrEItSo@GAPpm3q2`0Pd)#s~P%K1ImJyD4=+waxBXNg%po1B1>_d4sW zRIPuNurUllj2q8$e7@E^({n(}#mI~&f13d(2BzmDS5)&c15Pf{VxW0GC(fJZmzl3Q z|BL}D{{MXbd=GVyVS00c2v!&c8t0?ocTT}z-s-5G`0i2<4 z)zWh}hf~F}ijM_p6s*~2M{CP%zp%;<9Y$5lMg?OR2;#JNS)zjigZm!U!K*+*>iw&E zEUU(#r7K1>jJb zX@z`aRh?6*u)tLv5~{LdRe=h6Y=-6p64hf@$%O(8pp4aN>7w^^KpIu%T|tDa>sPgV zRp#v^Th7c&0esLpsOHd4yR8ZYRgzkP6ICY7-B0g|2%&0 z44QZas0sJ3WGGNYG~Xb$>N09Cw)j+lioY9?gQ_3RS{~IQ>{c>WYoxFV<+ju_*bsfwx7=5V0GYF_HJHaoLZ_uf<#2a@%_^(;EV~Y>8&d6E7a8fyKN$e4N_L!9tD3o1t>0>$RO zO_i&h`1g6Ca`lA%J_FW#z6wy)MXa*93O&byRi_Q&4$c*6xN3zs9I$Hszz#z+*K%5g z)%9@Or0gDa0553Ms?4tgGiN9*bw5}|qT0+=yO3Qswg%MtRV|?k;@qKGuYTQ01ua)9B8pgB$=_7fkE*n*0P3hLIP+Pjy{V9InVREuvCXS0$-MBhc!-kfE{ zFQ$SMnoZ>vpVK~5#7zZzt+HNcXj)}^svX2Co6aJu5JnZhLbbZ8h1jhC9gYox)2kMz z%EYTov^q>GIJw(@S2H~}ACGP3U!yJATC=u$z=e~j`%u`f6SP8N$rZMhk!@M0%B;JTflBw#1r8|bA6MZ-{eGnl=YTdG9lVZwkLq%$If+X4*tM%fl$|x< ziUc^+kxKT-kdg6MX4hOCr2}>Tn?%WTOmj) z9lR=&QvG`d7q3JlD-~1o@ z$5qup=U!Hp$R>~%HS>;D??n5}7eT=#@}M&OJWCm=0F~Q{701QZL~|s59F&XG&OpIS z`NlXRs;YJ300Pvow>$6XXpT9M--=M{@Xe}h(SZVYs|lyI$dnc;yNF8h+5sw5wi60u z;%ZSkKo3_*oc&$}FLxXdT{pyN_ndOXY8mLr@p4iXqw3;RMCU3Qb~uoZcNDaI zcLNjyL&VlxZ2v_NMZS4R;wZKXpYQk7;&TR_Y7~qYMO+Kz>jxVEwJKF3y&`Oi5yD z>oHPp1ViNKBioG(lBh_XjMXFz2>8n+(oC?P3_)R&< zhpXe(p#V5iJ_-u&z}?wrciWvED7I_M?jC|wh2~gAbSr+BeA?BGR1V0F%6o^H?3^#E zDytGurDjr9cj(;CBC;w-{W2_!C_aX!L+HB*eqbPo44yy%N&zO2<$Ek#)asW?m>8-PlKtRr1j_5Ph~T79RA=v~t#cj98Uug%rD%R>?bWmrm8hd5u z?n3S>(ys#y>R5<4)+Vl;le5;VV5mfpJLa7Z8NZ@}RNuiN@L!Z9l))eZFd*NjSV>g@ zEj#TZp49x1&Hz=Fr_KzfnrW7d_S zqs;ECi79zhT`7esTv2aVNL%#tw+bqw^RkRlzG=)-gSGOq1$>@6>JFUb@%E%4q zl?u4S%WMPCFSK*bS}ZUYV&po)++f_ zzG9U}n`?`6;Xy^IBUBmXH)2*A2(6c&k6M2DizB;@%16nWF*tS@-QlFVL5|h4E4Adv zy|BtpLC`B(3%7OCX%|*7S=ENC_%M#oR|mj=$iaUI`*z14$yJ_LbB_wZP#JcrRu-Gp zI0lvunhWXDIX+Z>6z;-mW~UUsT_t~~ZCR0HU6W0xO6h!GMdEdx9aLhax&umkk$tw8 zk^~A+dA5`nII_lwKSTT|^xCR|VleZnwcw!XDl{HDpsEtR%8)CaHAl691s0~tu!1&o zOdvalnDXtZ{B{)Yi&agP{F17yE5zH50)gWwyNVpFs#m8%RN8({$6~ep+}#r{`PEHG ztAObEFR^x-+p1UhRVjN^svnh$fdVc$V%in-{g1+tTFs!|0#vSYlsY-9oDjM%w(OYd zME)|02i4tp6|?^jVCmjfNpK*WZaJRaK%qrFSs-@*rfz`ar(QsRtIXNvf*Qcw?g;NKzLLyd*9V%&Z z!4b|}$Z1Eb>QIduI@Ai4R9#SfERbYbqG`X8pCA83GbW_nhXiM%;zYCocDPHOa#(4! z?Qnb)skE9)Rlo^H-MLaGP}j^+iFeHO91+W|N<%gDceD|mwuV|`U0#zjqjCHC3Wm=N z10B_CMLnnxd{tHJAoyHBK&K7bIW*M__YYzBt~vuQGLuOhRWN!rBX-tR_53Q~REZ#4 zi;jzUe9lFS55dDDxB-c|vZ^T0sOB3p#fTi><`>H@ixtG&mFcO_P%hNB8d5vF1;@s^ z@;y=+uUj!WZIDjYs2oJA1IyKL>mn!JHlWjjsz5Ic>cRApAl+8vu9m+W1krXde1zc?cA#>ywR8W0#FR!#ZC zu&QhR8C!ggpsI^vQ$n*9v)bwTc9_|n9ycTUI;FG=2UgxsRR*r$w@OaPQ9)oW4Tpg0 zAkC`#<7y$QWI<*Bj$(w%&#Np|6!}{zPbfd8%4(h^Z_fOW?LVc?UZLWumLhXF{zI5N zx=D{xvQSp?4n?49f4bGXW0j(+FGrS3&8FW_e9<{KUT9|1K7&;LQb>O<2A#Z+&~xJ> zD@m{gx}t&RF6L@MyTmz`^)L;+pQp-@9YeA9mR1+eZ1nbRD2p% z<+zATw?eB_uPYxR1r~9EL6whF)jD^AK{pXl6a^K;D?+&9BUaWqs%_|=CvNM#vKDdL zepNDG5x7}!>Z;wu*MTj*e1jS6Jc1?S2T;hq2!6;F6w~BGVzJI4u``=sW_MLjx0>^) zfwQ9xSE1w_862nNXG3pSkFSF7R++!*xx1G4u0?@!@T%a?S~!kxQKgrse8rr(jibEr zM`4;+Ni|i_>n;IRRcTJps^)+WIeBM()@iG)@Rce_unK8eS=hU6Zda?&Rc1%ehgy8d z9wWEo=J)ZQ!hxN57pKn+&yxRilPqKENZoC?NisZ`><%8b%MZ>`MO9FPxNr;1nONPbly znF>u?brT#8ST!n8)C))Mr<(Uwt^t)girYb|I8hGz)lpQbl6CaE7oskLepf2xsD&4u zcJu5i|Bh0om>`K*K{>`^jzxtUmOHTPiaXH(Td0{$gw3m ztJ=|RL%3Y2?!l%4bOoYu4GCR^hChT+r6UjJBzlgbwj*&=nMA6}&TWCIilyU#tNhM7 z8ibC$PKD%kzOxGwtJb13^K@oa)jCta8(2XhHHqB%W6BU&G&X)YX4?_L7ICr6DhmZD zrOD<>8dZRaN=AlNB@XM{aSN^b!Org}q#xRpf0!<_ZmXi}GgZN#EAnX7?{|17?!2TD z(d^#i3We!t%{gd1=lZdBdB^Rzs(>q-nyTVb0?`$a#2Mgsv=*J~=psj*`|c9d9WIlr zQd02(UAl;)JTjmAQj(Z|$?6actrzVvP-PTbcdX^dRL!#*dQ>xahvBL$?I;^pGZObO zuZI0D0hAro&djVrDYH^b;#2V8_-*1%wVAq8fEn2}RWraa;DU z3Tg%@1^Lbw4$Yw==P7>>h181`RBjPQsUz2{&F!x{fvgI8>=5l{G*v6av4MBNBP;rE zP8&^STI{nc#FK(=JD?N?IN`KC9Me5k!MfSHn&l}qc?Tq z_FYu)avYZx5Trs{y2>ufGfw?m1z4&OpA{fO@k||IXNS1&xLHTL4LE~B6;9p+xgE$V zs2FgV7;Q!aMzPpvx8X|GbfCvd2-T6faeBEGX|dwsIESb59#V)awc%HWu1e~p;>M@| zq~uo|`)@}9!BLBFdhO1^=CqEL7GejbsU9+vIW3v;+zmBNRc*i+(9o*ytrle!>^tJIs{j+n;I2|qcL(6?JrEtiX^nL@Uo~j1P@u|G zuF{%R!)!-($l*h|O8hIq$oXDX#pHr`DlmitQgZo%O0d@%S5$MR&b+p=y{~iC_Wc8w`%ZL0UTU=?#iG}Wh0djlM7d^vf57e z>Y@`Fy0YVq)1Be{VcMxXfwxlMUSZ|C*}B_~bnHs1c}msJbSzN1;2ss&9lMZf)~67c z>OQHY*MZ}+{pxsNIqD`%#ohrCRWo)4R#ENM4*Iquf}H;iDoNyw5Cxlv;LBe;Bm1%% zdcNUID0comTfSEZpOW8j5J*+4rfTb{n_Uf|I|vu0Q&tfltF{P31*+b4MaEQzTV;)* z5K;=Mq`ZMxJH>&9yHHtIQK6DQa;zwv8Cq50c1YkXuvS6ZHSBc6g4MjN(&bbDqfW)A zKmrVvDW^R=QMx{4cRewsZMIHF+Q0&YwtC`uZ z7Gss+SB8iV*QW|_&cNUCBT^NxBlqJHxE0^bY5BQ6ZVtNBN$x5upbiMh0c=#{dPPxj z5R@vox#vTG{WmRca!@$aT zjkV8|yit`gtH9#u?y>KnN_I!M)M;V4Ab^UZ z^bWDs;m(pqv;Y;Y#i_n4sjdoz%WPuYxif;9Us`;Qb1s93 z$BHTgACNI&lu%*{7P-Y&Eeuy`rs_kp7DJWAR+&oG)30EuPPSUf`*#NLt{s5m3DhCI zR<;6FRZ-2nRF&C*{=1^R73z@7GGI?Sq>C+kM2n>U-I8kd=F(bNb&)bhcip*h=ZyFs?*nZP+G2tup^A>+M26G zNCoOY3aj;MQ!Ai>qqO4sMX8M4^=fh<#|qfwIP6xU!d1oTaM~QtvWknPXcbOCRJKEv zj%P(;uG||cgHTsdr&6(>uL~V@k)n9i%FEX7nPi&-R8~VrGD>+i6f>PR8j)|S4 zZRMhA6q?ShD7xwZ3Jv6BE0tKTV~kc2Xe%*F)y`2sfvSzs@yT&C;#|OslhwObO66ar zh@RE`uK+n6{tDB+V<=fS1Ft$EZpG}V-*?1P)vU83A$KaPKMcEe#(8qusLJb3Q3?Ji z9CE8xtg8>1zgy`CrQqRe4i#H>euP4}Xp=f%7PdYv zC_bbjk&Ojog_CDY$mlSunKL*!s?ZRMuhX(~E254To|3Vtd~1~UlnSm2*uYu^Zf55o z*Sb6zcevao!Mg25MKN$-`IRWBd&s*ynTllV1cXX)!zEcFKQg84(QRE;zvs*v)t*u9 zzmB;kOKhrr>9m#I*{+%^JLFy$iR*Sas!CV2DXR((?cGa&%HRejbCkgoOr;#nu=4DR z{r~*1C_p*-R230Sy;fyOP%Um&y1E3mYWVC_5#2dT<>get`c;)$N%waSa0eOZSdgsp zm8wAOu=pK;-bzxYN}#IQpVE+12ul|&=>!4R-s{Y^9MF_Ir>sC&u3C$;9+k>MRb^EY zP!%xD(c0`dLsTx&)jW$OS{JFKh^9k;3Dojq|Bhx@S(yU8M+8mG4>>=ZL&9^(d=Apg z@n2!@?T{@Uf*czjS1f*~7tFFl1}0!bUKWTM^uMc)-tn1nb>zTYbpupADs>grYOAWQ3hC=;8CH@?)xE1+!n=OtMxUrki!aZbkYxu7Pzt?<`mB|xWwrPml#r9HxvIaaWyFYv)eOWj^>g?i zN9AGg*KU=hs(%Jr>Zn(&DyloO zR!XJ8k!Et=yMx$us38ugN>L&p%sjs^(#Jz+y*Oh+Q1xtdm=!9Jejl=YcNHSsIg`ty zaljdlj+TRVV*~4|8gURb4m`O-UR7u(XO812wljviJIL?Avnz2;XCo@ElykT`QbbD7 z)8VC5cH0V4>_~l8GpWjbpqlq}6dpPXFOEe-$MDY;5pX5hDm}kSb>EqLtE7QxOIG@v z&g?cSYLrW;Qj4et)2`;AbKRZnTNz$fK$QL~n-(U!7BxcV5M@tUReI=Us;brxJ6hW{z5N zNGp$t!J<|lQjeIyh{4s-3`!B-9PGVfqdPraXBg?slPXo^j&-zaUdMPuyZ$t6u#Yk< z$E8yZtSe#CipbqEX7e&2u>A@ z-5GV2q*PWuO14QQ*_Dr*nrpjSc&-muS5dv|f}Fn#te~=(Yg8$ZHzNzXGE4Xjwm@yX zJo}33=}e2y)!Smipvpni5q7K=9@Fe|s+kVj#SJW#6?|3kIqY#qpUd^LQXAeet6R}$ z?6|l(WYlW?sJ3Wjr&00xs_I4sunuU*)xoQ(`O1r^8so4>4$Z!r4{eQX=S+LoEN|#gm1bRc?KvcA zrz7BkketfUUEiwucK|gGWZJRpae-W}O-=Rtu5C_LNmuQ)1$i z_-T%gTGx7|vVZ6ha;w&->nr3~T~xC+N2`x{`M8OMOG0+7K&n}n^5b$;=Uo+UM?_i8 zTD!LE3fQA68+QayS+`W<0!OY%DWO-y{P@}4DyWH+Q*Ah;2$6vhd4@F~*%u1QB4zuH zh~!Y@%&LW<`uvspZdF}%Y#N+^r~2qhrK0kUaR{2Oe3Z+r`G3kggCt3gW69zWB$p&F z0fY!|g!?c4?C_^B<2~M1X8N8*HUCh6MF|?+Rh5~Mk?wA0s!vT^HC1DVdNFMTsfA=E zxTy_7Ekko?N&G#7tTKvJ>xrSP9&@6nh>3(aA`-kwy4^&OTUfG*#M#b;laiJyku=}5 z0M~6Yu>onD&v~-uj5DaSQyKJ7HRyKV(|!LfSFfarF8J`gkZuNLoYMA$Nh~rN@)-kT z>iZJ)faIgwQ#~1%jnE-csh<#uZWvue#3oRzJs8lYNL2%YNUaLXfIL%V2l+{C9vI2A zx0Hiz>&g;drq&~+4N&Eu4s?dnl0J9F>qUYeLwzS@o)HgO1F-cwZrZE$xwkF8`oq&E za?hG-5^k-gs@URw!pT?l@pYtz){kQc+GdbSyFNMctzp%)Y^)QUB(b&)nbd%mQf-WT z20m*1S!RB)Y10^kvGJNDT1c9KBqOA0ZPJk564ED*V-muoVZ5C)n&)LuOGdC_;(1a4 zWOvU^2ZHopBAnX;6v1}iCJ&p6-Vku?9PCr%dxttoZN9s8M{2#BBvHu|-3EBR|2#K? z&M`yiv`AU0=$5l(evX6+#!yOC1yRLKl(y;JPYSbbi^c}inU)0^R_WBbMf(uc)10<9 zp@q|XO*E5qU(wH!=!V#BMCBmwkd2P97$;J`PTy}gy<}=l_JpY_w!#>)!I-rwDMr)% zW0b@xG-&f(|V;jVVd`va6rRFw1+nm`KMB&49qpB8fR)6meq3&$kSOcLS{LMkol)9g9TqBdUe?29Lu-nvXo>Z-*?v3hh@mVwpnOc)F&IG`1 zP%jpFZ=kn^kCzO_Mv+IY97C$6sDjO`$5stw*0dCRQ$Z*C0Uboh{7P&!t0*;zs}H2F znps&oofARc<}^{enjpZBh-WvUiPUYdaA)JcvQ?Uy%O|5fK?vv&L8$?zO+WqqPcri< z_vw~>pC{AJjyg*DJ)26uzux?Ji|>deCULn;3#g z(MSXBjmX48Dy{K#lV$WH0@zX;ce-CTWNPxrq{!_}g+*=GP5QJ23#UplNvNmiMO7)a z_|w6ZDjE~vLZgGt+CiwV4H$W=Y^3*mQaF?FV)OWsBr)YU-Ls~;;oLu4UsPw{tsB(+ zVk_z>olFgbDlLvv57**P45gbE&CP;CuiZ$G-+(tMdc?>}NrAOVEVhAd6Bf%<4-+jt z`M}aCcak8JFe2sX*mxC!?Ap(g5oZwWmD*7T0Y&1UZOeCa$m!t425m-j&}19gVAzt` z-tY|R^O){OnpYYXaZ8fu2oI-1N@~5D;WWWtJ7!mbi(!9o`if#})=31vncv6j-P1HgbzsOXTE=8i_F_T3EXz+yW? z?mb(2N^)*jMb!=G7;>jMR9B%@k;JwqY-67jMkvKokjIC}!RAeUAQ_gxuBJ*dL4@|Y z6Tgo7*oo~&a(LUWnjX)9cazzfGOf0Y-9`jKtvG`sqPPW8?Y%L@5=7NxwVCj}O?%YL zSIulTRk|BA(?&SdRrhq5G(c=)U!XSjbRCm$>Qf%nEi128QEkoDR$SM*>t;*|p>%Ir z9qQ;SI)5636Bn%eusk8rlDsN3bC^6i8*)HW5}HLQ4N12+r46r)qB1C2B+cAR97p%z znm$m}+L~G-DL`}Ml`&KWqg0!!qBKV`=IPXqq88tFhPKHxO7eGu&DvCUsb!ZAJ=212 z;!6lDU;-b>SWIiybRQFsHUaT5QN$VK7hACPfls1vimZDwv18Im@8qJEgP;0>L^E;L5jn69Bwp`~^+eHN@2%tn+Lv=JRvt%A)S zZVlRMd*ImEW(a$h*rXfplN|+YIGrObw!YX~DAAd8>yFs*^lqbWeRI0G)LltcSnNh+ z^gv4IhSFe7X-4TrCOv~dnp#px!acQcOj{;xwq}+^H*dl!z2&W$pgh{plgyjiUz>~` z8wHZuxEr;jUC61WY#x_pAeLreuZy96ZwT&b1E52V>E@&Z7O@gjd(KphDF>`;L)&hT zG4hk3r&aMy(eBmvbXQR6`@2@({X)C;;r-uxKDHtB(uc$5;}B(d6Zvh)+-c}fl^yw) z()A!D6vek&luTNrjO(AS8O1zIGa!=hPA5wnEkFktQm~Q9mws*{6if#(V(`TqWf_xt^L zo%cD<*Lm#>M|V%@H;urGjanMtQVzA<+vyn7Jpe_56sN1gm!?&8^=B6LXZ3b52r65- z28w{Hg#xBA52icLI}XL@hai18oV5|hFoGFB^By!diON3yQ~c#!f`+G43+9*8&4=GU z6js-jhC^%J=uQkvNO~ww~fe zj0Qja&;5mWWVXQXsk|F_4i6`G#3){^C^JF$+n%BLfTbD;d(-M-bbQqs4tG2(z*#Dv zAax}!EzdD9F3{lh-H@xa7DVgL($j>c@i}X|SyiWtDYZKQ96%p)t z9tVq!EH^swM*}m-`mdm8$V||E-!Q-4W|_&Gp7n=4i{4>Mf&o&vx>1q zw~99i55@(Fgw+57hJ+mJY(t7UDf^{KJdy?jt3}yMd%{i3|I$khh?2gEJ*=q2`mUNv zZVx=Z;m_A#_f6!z#=xJoaGt8a{@%KOb_Nb8)EBcMXBYPT6t*!ha*zZhM%cUx6U)=# z?fAGWn%T9GBSFfOCjb;N=i?3$EZ`+NZ7M|DeVREa((5IJ!pPQyc(1=I*w2o#@U(c8 z*f=e}Ln!W@%6E=sxNI8RKOZFp>F!GYQ9EmuJRB@lSHJpa`O;;?xvzh1OXIxbI!l^e z3eu-tVpYBo+Ad4FpBU2>^$jbY;A^o~zl$!kcYm|EVv^e7pg41dr}kWd7qWpY1{btV zeQB~CzT{&R{z_(SlDz5H-1?o?4)?8rTn-b@VmmBI%nAbD-Ql|HIF@j!_~{> zXD9*s2EAECvbbP`kV$Z58-?r#SD;Pjc6_KJl1<3h$g@E{Zo;!WtldU~o}zb66xdqt z7*z;Fd>9pe6%k}Fjr6>Y#bXc5j@FVyN4Q6I%ep{m@b zzRgQ;S`RZod(0w={N20EB#m!eT~Q9GxNLs=<;z?2I3c|;URJdIYf1Z-->dItZ*|dO z*IMQd%F6~Vvw>A~Vx_}3#KdJGHa&e{p5qp#Wyc^$ds4vAf&AH+Ac!~Mj7TZxHyMVZ zqEH~Bcz%D$tWjxwT|V8g*vqn}d;@hFgtdL>zYt}es;j-$A4Rag@AFdLQ@XI*XeRE8 z5uUrC#X0<;UbN?oN8U<*Kfe;SFq8`Ty zf4ryBDvdCcaNxayk4@~0ZHOmJiJEtj*Y+dl9C$7cT$2`Vtv=&*Q)xlKUg&C5_&vS)#ax~hHK{&btEN(X?n9Y9 zF4|?EW-9s_S)Hqzl5lri`B)eM!22!DHFxclho{uU-4=ZyvXP|N2^2`2(Py;A;eV1q zt4rI)W@4o8g}uEc_hfE#e|KNmzUugKFKVf$c8_c&=n^;mDcD@G^NG{mB+%04ETUU} zi=)3c6Qu%fdUa)Z?zw?2w>!TnYd=c=k={j=W|qiznX2Flt;{D%3{$q<1J*%zGng)W z%{@b4m+_dbE&^Wpiqur3H}NQD@OSpH={#dxxumLEReo9RdII`J^~bxJKMZKIM(DHA zR!Y5j`h)|SXaipC%L*TC!`{4e8(`0wt}3CzPqLsEXT-3(m7CvI3e;#_j5~KzV5G^azUahz3z#W&`=C6bVYr*X-sRkAa;4&*rpgA0 zE3=0VA5C!?0jG*m80K~F)%Ho}(9ZVAA*lf|Pq7SY1(&J^M@JeeTT0!56UhH>_?l6? zBDV!}7AF&$vW`|!;8t`}Q6TrluASNq7`aK89aG=4W1ZsXf z_(m61P2p4SuPQ!z6DhK^cNM-Fz48XtYVC-+@&!4XCtP|tUBg#NTA=rPUr(dQqx0~g z!$_rl&=V1+qs;SGRxfaJ{;6rjV?!=ynqZ&n@K$^e&E<+Fax59C-e_l2M3Ic#?xIg6 zzGy((EqadTE3Xv_J#@LbBLoYz?}v_NB9kB2;IG0zX4~Lrny%YAK%UAcn@98gvPvlv zt;$P8PsF}K98K&uNZ*lfFBo5L51L9C>6iKsTU(2c8GJc1DOehJyJ~Cw;b7Hi}w)SCJddygLa{kurI7o zCX!Dc8G9t{Bex zIFZX7@bTR)G_*q4)?4BSeT#I`jE9Y`xV|dB(x**@|C|;&2snH~1gLzms6VTOF=v0_ zIh3oPh$fxTOmiDv056TuMT|-*obM%jh0zw}=PPydg@$tUaxlD~X#2B5E#FFZZ; z3})nkf3L`he>gDp*Zq3mjQ?Z1TH*5C!u^6!0B07imZ%v#H?X9OCiZ9EeW~huaZExqA=#>X}&3uT7vmhF+&GU@am3QnZnH< z+zugV6;&H=X(2vw{tT8Swny#>i8-hsy%-Rj=W|?2B|9ewAGqfqnQ17FQju|JIin|( zX|TIgWu~Y#l;6%ZkPxoRp#YGdl8_H9_R`w=j99a!rOWCb>SAj!-+W{dM5jGh^I4=- zqSa-GTu|eKxCyzIk7>nL8?`1PSSp#Q%LS`*6Vdc}DgZCFakX6NBJ!u1&GZ9nk#EG9 zbln}f%OH~RWS!E7oe7duQ}nmV-9LZ1FW z^Tx$yz>gn_+iWay`=7IU`XB2>?H%Mr~p4h=xa7$D^y4V(vWOFox zj7o@#^EsG{c|od5#G18?F;18gCgW;;cT5rpd*o#K!NPoca=8g>|KN6uE>-EF|3vgs zKCywwJEBz_XqaB~S;6TEULw91>kR=Ac%7p4Cnke322&mg_KL-Fne25t@O<%^g_T*+ zw2jFOw7Pa{Ab|#MVJTt^HWJjIR|*@4gO8@h6e8fH$}kr1kJgnpNV^igLS*I$zR^r& z1&UhN8$!a)XSBz{eLCkK{knWc7oO5|Nr*K)?a za3bhgDL&c@1oTc+DkB0NopUN9T?jge2YeULS4BJ>R}t!Ck^DW#^q6g$)f#@1lSrB% zE%Rg!>BB(sogjYLMdvLhp7tB&ql-Agk5OXsO}4g(#2ya9qd zM~GY%eN$|wdT@tQY8<7#5<&g1Y4F<$$~@Nq!#qrVt6r-zPJQSf|C#}a zKy;+rDHNaE=CsY^;sJhoD`#;E;fQ=U)kEX*dnZz%yYMJaHx<9qd@s&P+QPI0-3&7` zxma*3;rGc=oc^-gd{v@<&J#Yk<&}qw*mny1?A$-b_t!vg#cZnCuZUV1r>@wGt`Fn% z1f$`6DA6LUgz>xvu(VO@%mDl7e%PRJ`=@QnKaK1-PF=jIIj@sau$Nn*f=&ptOz_Y9 zS+<$Ofp=3D^&Ri-_!mABs|229;qiX<(-jh9Y5va7qwSN{5GM(|U`+W`lmCXaoGnmh z?GA&ttkQJ>CTodki3YQE{>!uTuBCk*Mvi3xsx+)P=Nm~tj6Iokx_Raj&*~eFx{Q}5 zG6n<-vD%t*L-t+Ic^+OpHYJQSjVUEa!K$)*7rwpV>AL3mb{8~bylq~mF6sY`g@d7c z@7pkuH+5-S_wu~BUXw{_gEPG`bd$9Db`EQN8eBE7a&dVpkDNA~=#Z^y&ibEAvcB&9 z5F*{3GacL^8tmrzo4;3_OcU$n0pv$$b0&TX(AgL2Gjor@jCT-aJ7I?M7T38UN%T~d z4l=%OvlFo#1wI~ke-8n52k0QJZ!8$vZYC2Y`_|7o&;CP$*@9knHp zh#pHcsV{@?yjepG-UQGkBK*4Z(|h}%?9_Fz7O3h$;?k#w9Q-d=;@|S|RxP~imTpw0 z{L&^((;vtO+#Eix5xa8PyseVo!a)RHf*VV>7a(<)W4qhj#yMiTG;~A$8y8XRRwHo?wya})L1wzsDaj#MH<_KW^h+G3^I`KBsu$b(aDp`)4_Z#_i?_2q%% z6GY?$jB;+tCHGjLwaE~2iPrrw9z>y)h~z2f*j-C33!ha*Tqut^G@l=4(KCijw^dIV zOj$J1nZLxNy-=4_{GddxIhKy1GX@a}hCi9&2`YSae;^$%Blb);n}1TKUZr`Nqmd7z zAb0F8nCjKkOX=;j0w5jZIpERCgksOw(T*5huTTMFZzX5qlTS;E-pBG5KxmaM0CJ17 zvGjwq%wpveyIXyTI)U zrFRy$-=JU$F^10`iTYQE@OWHNuzG`asIj7U{TS;U=4`?q?po#(+NFc{X(F1X6OjW1 z+fa78VN5idJIL9z*noHCbveqlVjnN1$e)+^Zn6i3`1+;4A3G(V(u4cc=A(RrpOHBW zLBzz!KA>fik@V8(AZXJ2!CK0E&D8>csion;WcS+}ZvzXdQcZ1=5JPHPy*x0*ZZXk? zahcoGBhC~A>`6QbCYdUD1sAf(i(3G5GCa@Ge&?|LXHE8GJmFRN)D_DpTPA1VwT3-o z|0&pj!Xh_Q)kGv=lD`Iz@Sp)yTYuH40YME@&AY4Y8woWNzzQ2eIUx^Wr3>CNO~iJa z+r|Y+Hq}!yUM0ldzkm1Qb94aPjIMq%L0)P6xixJ-aG6jpAwWaoKV#*Hkd z$CT`&l0`MZ9q5#!%%za=&sCHO{PUIlnPuvLfkwHPgF=(hlSS2X0dEJx!Xi0<&kRNO zi2T>u8-PVW06YyCF(=_Ok@W?iuF(vqwKGa%AgQ3`7}V_V$*t`p_z35_lhFHRgY!0y zA1&FF_gq!JWa=gA@_Fyu!FwMcvzWwwg1)$#N+uZizYRBP9pwFsVT8^HPn$NQ%@hrh z0<5@WjovmgXWRGaRgTvQ1(L1t2JG`{@-6JN_l$;1s17Hov{L@z5va^B;2B zx1N7MoM-F)8TkHli-}azVP3|y0gP$Qli8ploK6jP5>l!XQU2NTIn9BO`|-#qwKa>e z>f{bzXaA9jdeU((tAHQz!|w2b&Wq}i><1fuPy@0x0NBhgbZXh&Ji(A9S%1(~^LNK|If{+Ik;eGwyacIx_cFsFDS2hNZUSl3TB>m*_zL9!Wqxcs3S; zX8hb6>l~5O7Ov(C=X_V2Zf#{7KlTI2^Ys!Ba5Kkt1DYyf{m(0W(5)_z?Yv%;w%jE$ z26uV|rM+K#S`+6e3{>nnamD znk@C`uWC9Ms^Kmc}tl_RbCsNUp!kyqw8M@y}pj`1ZyCzZUu{jgN;GFrjqC^JQ zAm^V~^Dn$DW68aqH{WxHci)hbT>bu=X9;55aeO!OE+HZxT{_PEA0>)2{V{Ie^Jj!K zvjVrKeX6dw`=iO~)G((MhR1<S?<;`YKe8e>;`wB3-!*7-_R z?~vt{!heAmIPI)TASqmd86%mNuUS7C-B30k5fEGe%HR3*KG=|+EpprI=W6MC{lC9C zuu0yXx*4Aw3h|GPL!7IrzPTapC@uQril8_968S%4UH6x2-p;~^bY~~k1kv2BlU&U9#kiHVqbKfJ;swB47;m% z^l#vU&n_6TLDhpbgB#wiWh_F*8V_^#ia)mmMo>~WUWQpH(y`)oyFdPJ|3NNFNi1In zN}Kpo^Y0a}={quy8Kk({1ERqQT_zFs}(y3VrsQ3y;`u*@Jm#fe68Qkeov`xp_sljq|O14s{VeaCheqB^ciUBr76X z3fBFrWtzBjeCYmrdW25y)AKnwK|fbXxB2_TuWZyOyzGZT{3{c2ib91j4;1cJ@kYg>e!k<@aMx7*UqSBN5b|p z{_q%AhU#p>h6FGLr^!%s63EGLkE1vDG&F!k9sAkq#}M(w)eB*zA2w*LzBRMt*gX{p zff4k_)qS-iP0XrUZatZm@rJ@AD?wneM6YR{Ir`$7PITJT1 zbhj?5TR;z7baQTIHt@PJaa;rFF8I;nwgjTL}7*v5Rn$II_M z^O}K2ZJ-|cj96VbJOmmy1-A;|sT!!;W%u>5xpjYKeJTiC{eTZ?Sr_o%zXxxt>VMhX zEP)vc8b%aFxxR$-vSZ>Fs%v%h2aSqvEmv7M2!C9G4)O7$kwTB+#79}EMQ3Z$m+go6 ztd61^ys+Ttvc}+Sc8nf3mkUcS7f~OP$|d8FzH0r{F!HhrF8~vw^AgXX-FN7lR~pL@ zkMi?kbW2^L0(3xIFaEaQ_dLQdN==@mzOhM(WTymbmzQ?w$+tNjmJuu@xNjk>TyC;` zn=kB4ko@|XMnL%Z-ozEXh~eyNP3DY!;CTGN5^Dl97Ab2sD-if0UqxD+8c*$nOA_Z1 zyq7<1Ld>s4m3FRqW~Ifjj=8w(r7@D%`CHd3DkMEB_OshmZ=XK zx*l5T@u1x>?dPlA4JrzFZ8YO6*pZc1_o?b7JmnW;n6E2Vf`3dOV zUa+nw%4Ha*R62w?r>IvyT|psi`)G{8*#^Y6hmR@7Ps|eQaN-@(4Hj?P45YC@JzbVU zYmp!W_fXrPQj&uS$^oMOHF%XvZT{Y8n)DT+6ng3B{AH6w>fGbgK7i@o(7qy4l`e|`O)iIs4#g1OPRj_&c%waQoGRe9Lp z8u^gIm+}F|yl;MMjy@K7jcB7=FjDXOo~YLn8D>27PR8Jq;_~ovF=T#tc=GQ*ML%cv>7h8P#o6Ib|L2FW zc$EDQq@??EB!ZPk#$eZSUz1Q`l|L|_Qy!NP2Gavw^`VnvvTt|*5}l@d)!Ri)6UeyiImy2&@ zE=;fVP0nEn0AHyRps#LR%@Xh9S(!nR+>;PzccdU2lY&4y;-y!HtmacL)~`B)G?y1e zG>rp;)j;>27STy1Xn$18#B+^b_t+u4ujXsAtb^dd>Oh=WE0_|_c7O{rLe**{}mp|NsuVamK zqyfCMt|F62F7E3F(CxZJjriX#l7H|l7PN`)Z9n=E$y7J(bndCR5}_+QhzngypK%8J zbhX-39gN>d3d)71z7!_+lB-#Z_Y)7d|MXITyi0rRMc!jB(btlB1MdgqKqJvFkZCLM zN-ebqskMZr`jms>HVaV$12@SE(*eYHstoON0Ar^|&6QnP;YjYwYkEiZl;kp)G>w_* zZ0lDL$xq9EEaDowe^OoL-ureq%_FS*5{oa7X-3YtXli+)2KD5)=70J}08RhlT#W_n zbwIj?PA4pekmk@(8?7QowIUz&Q6Y&PGPgus@}jh3gvzEPbvaA&0yn`@Q4aJPqr~>sHRxyc5A*&^0|<$ehp}`$gK6; zhpGCaL_meKU+jD|?>9_Z(A<&Hvll-pjATA@BHG=Ca5O=csd@Sz_|7*}ZqafARHp7c z8{Xwp3~GCAIO$e8Lui2PHC)U|rEaM3=qAz(z~f2LQ(;bbLajJU%hfsGX3D{yA8HOv zJLtSsPFEAr9;^HSew@@50gum4Nxi6>i27DlheK`Sh;rO3u;S zu|NF5`^e9NW>E6#dtD3JYp@HokZl2@MA=Rv+R>2b`Ezd*;H4^K3sq&S$7Flsbd{L| zws$HR>FCtdXQMnzj|npP09ndiz&WSFoxW% zQB+>aE|H2ah~~I;^SVA+>bJ#22lvg1=DcyulJI4eQH|VQNckk|D8FxSc;>B7R%Zox zv7C$wX2Xh3oclml4jha6D+7EPW_BdTkrd2{?Lt_DwE&3AxGDFHkj?b9jOot^G;NVd<0l964e+6HPF4UfPbt#K9! zJ?h-|z`rIGF38RuCdTzY2&|Cwy!0NL@=Dc~<{Y}MH7V0%81TvA>HTX#{}r2!1y6cX zc%;C!huP;Jo9zrIVus+l3ssq7g{yMn1p3WfV_zHzpXmU!&gG|jUsiNcQskDXlxR>s zOQ@Z&ey3Bs%=x;v8rLk~;&%3%?xx8uJAj_)GJJ=^&S>B804|1o6s`piD_a@_r3AI%PR4cPL0AKG{*Ar)>+Znx4y65TTL{>S8DrTbjs-^eX;GyOJ-a=`Qdn9tL)G!qxKSXZ+ha8GEJ2tb;mm3mTP&>#aFJuR?4ac1Bk(*a=72!|i zr&|^QKkx=;!aAj#Eyb4VHs^|zzjioeP`NAE{D;uy%>ub^Fn3bKJ8$fm-~8Uu4_c(c z#biTaj=ixRUMd2#`PAVXcbvrz)Ffp$nvdop2(ot-*=x2TzO^QMdO5mRVE1-Io?VBB zCy*2rCL>q*zmMwI-i`P44>wwWavI6ACb%6|v*o7)tKJmTzmRPlO+I`fB+=LZL8iV} zVy=2^3bxDQelDer(V^3?=FP?2CZy1k$)Zl)o3j4T8!Dw;X&13>t%V37aVm{nZ4+d3 zK$iY{{ZG&?$Ra^Ms@)jVTt6$~_weiN%A=2#1wc{JFl+XT+{1Osh4KB=g#C!mdC=Bn z3k#EE1uLn&HZ@fxLcg?5XA6E`=hF<$hhXzJWl@D=O z$t4!x3K*1vnrK&Z8~dAy{O3$}8PMDD;Q*XU#;d+b=b(2-%_oN||A<^>M)hcW5)8aFhke&ACF~3iAomc1Id4ks<#v-O|RKX2};)<}oDnD6FjBz$h1nDP<^`v=ZHAFy~g-1B%nH%LUst zV6PM_Gs(?Kdx5>!X}aS(qyN;?h1^FDEU zCG5DtXY>e^TB1MB&J-9gRA*BM?AiQj+PW=ow`4Ig!mS&+Ryd>Vm3M=mPAAz~*Gm3CIwCz<5b-c3P1B z)!&-ZhtWV+2|@Nxb#iLO{7>CCi4j@^HU9W>R!BcC4L)Z5G{=I75}8TXE5qK@(%Pg_ ze~ns@R&)F1g$0!j+I=%${mC>A!;dS`AkSlEnyjCNF%9XWYPZXr>BI*0@{RWp_I0+2 zDx$_3|E6Ctsy=I!Ve-sH$HhdOM|3@(R)ViW@-CN3PE;$<5=6Bg0)EpyPlh21PbHA` znJ9ByEyHp(Ehm{;k_Oq+oEH$DLZm#>8UvjXfRz-paQc;R3#>i(39FN$%&QM&IQ>U6 zvkPL6pq*7hsxk$bLzCQ`?;*scehR|Xeph0>Ktu3ay)R+9h%(_AIttn5(b931H~#4i zAl$<|$IJc${EJ&bouoIG*I~{dGRI0Ctd{W*X+!{{@_mS{js+i0a&_d{h<yPZ z)E7`U`%}E&*s|s6@k9p(6I#MPXzqeSroO=NcC2Jf-KBZcT6}QFh2@a`N-bZXt@k2z zQM}iCgD%J(@7KY3pjo|TT{jFqiQV1Jcn!;zP6VlJGE45Sa8pPM`;%o2J(9leil*~J zq9WA9;+rxqtOsdSRcfr;7ih`V?fTVs-S*cJNttp?K&{mDZD4l`_$bldjABn5tU@B< zN{0~wuf&MBSKXDrX`{|@-^N_t2Rm&d7Yk_1Mzhfs13qb6jIDau2d*NYx2zi%x}7w7 zTJ(T#dOyfbv0XT*fJi=nmgU9!&B;jSJRqv6y$Zyari{n4F>RmVT-yw0VA1wQk;NI; zWfoDa`q1cJtdgXG_|kLvcjLF1B)ejjaPRM2@9cy+${K+azx41+UVZ|&gjGm>5sJXs>|G2`DX zBUwESAw1uR;~-XIel-Xa%kM79P;Ll(4CS&q_eWkyEbcYj zuopRH?!L#(pL^wqQ!-LD8{BkCiNQ zsi3L;ZgxPLpTFqEe{oDP&@5dep?|E(wQC~id6A_m1hdQA5Cy*Z45+dCN3S@{{V2Wm5*e5r+RgG0zlB&*i^d4B&Ni z_k<$lZ#2(@af1eXfC)>PQx_xl;5zf@laF#Vp53S*39`zh!~pbjuflYN@?BOBy~-o{ zvnt0hhc8&+4*=m{9dTj)BTGea`BBGPed+^u3PaIZyWGrnj(#JX>nrh(3u95e9TOCk z%}Mbxt+D^$aexddHiVdTOuYPQNl!tur#QY2(uPwBzc+`){*li)9X46i_Y37h#h=6t zXQmkr&v-9Qf{ukG=3V;5S~{bjsL5@xb;CDrgQlk>cxYq{4E=4@$UGEqzXJc}YPtZP zT56d7`?TXqFlUSPz8jP2Bgl_#LZ%ygv|9cdgRc4mEu-4~dKwPHRoW-jG?ssqTc}$m zhUY>hwr*es-Pp4XbTsW*kPaQXkW8FB=L=YgsK|TZ*iHCuZ@627B%th#Wjtz18@TJT zoMoAzP(j*@wLk#FN)<#TUCn87`Kr52>CV%#QscJ8M&f^-M( zP>uxPx>f zk1>zxmz9(Ps6-!6as3!eSH*NDTqeNDxj@mXN_kXlV+Oj0R}mN=NlVzm+M!jry+Pv+ z8--Y4{t=3*wASd<7_9IbwlXT2)f6A}p_$}`NcTKZI1QLQ@(CTf^qu!lNX)`NU~XLbQb2*Ul~6!Tsst$5mUFV@WkEH+`u%vXLs@(nRO>lK57xT{)jr$m zm<3Ju>0R1UZ_KIu41Ss{_R+LW&m3?6h1Jf`4PX$?wH*LC8@2S?N&6=9`~v4m99KRn zX*V*~9|&~fSewR}-UXRGoX~~k7EIRf-cNr!eY<3QXFYdKsw5JPkB%B9xgx@$fy?aJ zW}m^l8jr9)YdNF@)S53h|1opWsLf7b-5CoID0o*PclPH;$BXP(Vm|#lh00n8* z<7D~scpno?kl_+tI5IP2V}(>po`y?+Ufb5Kyh#M&xSHb|t!q=y&NpKxK^P z**3Rx^-DxRkm1$I%NIDlIBu)=v-^2;SJfK0s6*rTI-0knEl4$=q~#awH={~T6G5tH zEjZbushT*?Y?Ka7AJ3D-?x=fT%7{BZ;lt69#zEeC#^k}5_mBPiic}&ndRHbfE#FOs z)H)9E4j`?~nI$kn-U*0gxK#g>g>$=v+A_EfW-L zFbRB{nI+XLcWK#rb+W74qB5{F2>h%KL@HTrve&W_Oe^|hFBhq7CpO1fI|Vyo1Bg1t z9MiJ8e-L9oAu(SgAHtW_sG7|T{mL`85nhH7o*p4N>c44K(1g1`AATgGXmLnOPWM0} z)I9EA-a>{@dsm4^7ccOrla#0EZe+ics2L$44Xmg{Z<`cefjDEVf&|kI&-<4?+rlb5 zJyC7QwpbZ z=$OE+?@v1w2v8DCO8rp!vRZf~FLCEo-HP3pI1eUFS2>px=mD2VJ6UP}3Tb`l9pNRGKHc2>px<5#pSc&gFT~_ihsJsycUCg!Epo)7 z&212zDre~z{6f3dG(8iF>yj`K43{x;Zfz{o`}|V7vpC)IA-lGR_U^llXw*4+2r}m; zKxSLB-^pCwUL;m*e%xhtXA~=40r#4ENkP2h410j|kL#3Em@Qiy6ovddMXnR-dy7}HZnPbD%b`1(!1^( zmfE{Zro^;yWmwR^K#@K-muh4n7HgTvG0OsQ$V}L8J!fc9#zVp+cWI830wR!@R<)xt zD3E>}1lQG%Mu~oaWxv4?{SNO{%5NL9J;e!8-JBTo9I&!~%e5iWq=nXX{DIuEfW4nn z5%*n81zToL1$$JX{B(J>u%TVBe8$F9o41VZPAsOD!P~~-e@Vn;=Dw!O=4`9wJMmo zkHjag=NzwM_`Kx%lkq4Hfsc9^ZfoD^=f4I83)l4@$;uQ&PL4iuLrlEjF*Rko@9vP< zfY%){&JVn#iz1%R^EGP3-@AxqI!W||qb#GAB8-98W;~$r-U7NnJ6~$UnrZ?Q1Uo8k zr~Z!4m~$TsOeGCUUK_~#Y7vE!l7Y~SRt$NPuSVUc=DZ8{86S?9->!eQ^y-7Z?~vGn zar6`}D{wRzE&8?~;iwjG@l-^?)9Ew16K~vj<&W0ME6$lrL>;i=eWEyQsN2VhY@IZQ zi0$~OmPN{c;?C^FTd;G(0>pMbTEg~qkgP(RNr@!RCVf7ee9pyXiYy=xd_gr#<)0+E z02HIx)_Hr-Y1ya@X5qk3lmN&=(J{8^4B2`k44e`EUJHt}XlPH@)TqScJ6 z%Y4U<-o@JNh-4cnHSsukgNqE3fxOlu-jALPIg25^3Newp<5DtJp%N=7TBu3HtWPLL zO3xW9s}^k0IBFp+qlyY4prVPjC7iXQA_3GSxV#rfC9j^dsu^)qNL01#`h}Z0@D=@+ z4|%?{G3@nEpYON2Gp$MOBkgF*rGwJpJFf?<(0o@;VbB%OW}j$*}A_bvJw2MV+s@j7zY&GpF1Y!Ta|Hb!uV~(a#uJiy+roEe`F@ zEK=_uaY%7KQoP9JC714v;%TBwJGnHQ3r>!z!rvYYbmsz`ewszjI<=bJ07&v#bO?wo z@_h4R950`4R}sVTb=93CnJ)6&eUWoN8`zqR|LTrgVM})>tZ*<8Q+2zAd0Q1tI%RKssZ_{ zf?Gy!vI^xK+!wy}hdzaWV55C}8JNO|1ktL@|M@(`XH+b4j7CXsu6p{YJ@y+o_J-U< zp2(YvfW6cIPDcO6ydXy3OW|tBrrxIDd%^JQIF=$~k9f+UI=^qd6ovXn<%4B$laW;8 zd#1Pa?d@rS17lTjc~EMb53`Jz9Xb20XONtN#^_Z;-q`~>*fz}z3#Umvz<&+@vcD)~~3{THGXfk~g{xL=w@Xam#k$)=!sTmvCC z;#SLT&WIJUUeR$*cdF0wIs8`sdiP35i)6L4(`NRCWiDw_SROVaZ^~_%nZ^#Q5|TjN zIyYq37ddm^w=e9J!tv54I~E32Hr)PJT|pE)a3Py98TPXDu{X&Lfa z@i|;O^+7q=I;*+!9#76f&iW{niHTj%LnB@*W+Rq-oQwl9wp20U*fPw~+OCd?**N`s z7H@{d5bgT4>Y-$GulnV7B(pC~Y9jeIPs`1N`BZoi4zLS$Z=c!#-^JA3!1CxkdrtEm z>)yxNWunL^W^jHhG@r;*-)0&w_;BSz@VTKBp*qfDYcv1knZGO^NAHPx zzROJ(^B6BK8Vpu^mDuG%`3WGnMi|KReSr=-Z+{w2KJCFUR#zRP!Xg~9t=hX1Sg|<19@SMQ|14AlPqe*iA898Qh0eKTmdo-Y>9J_B&7AFyIkzCQ2>hQ06(emmQbvrSHoEazh0Q_erd1G~N1@s9m9N1@9* zC~*RnN8k<9|Drr|FJW6PKxGv@egU%>fRQgSPrv&GkAC3=tpu$bnEpI(PPGt&qdW)z z+`;sVShom;oj92h?>icXd$%9Qm~mV>D6=nIAInR~I)48Gys0zNu`)MjVYKP_swVX( zr_&VGBSF4TQ-Asern5#<9FodIJm+LSY?csq+K`pF#5BQ(O`tmk3dxW6EN}#TzILOU z?gqG?niH6MofwoaS>WWpKyteX*t#noqWu`(cfD7SW5`kGcjkQdYl9!)4?HRI5<1_0 zZ&&LdqzymvR2Hri z4W^*O%hZK$gC0v5A!ltG^}m`hfH|-L9e;Ho&&C|u*>~aI3`0onU|WDUKXDl zwZCMZnU@Z6;L7D2=)d>%5e30YsP#tPBFZVyvc?B*SIe&T&bu&9h%YkeCy4>3=T8`? zamdy8vXQ=DBJYd&YNU82op$(#N{l{^XEb118%vI>4EU%_C74I;9Pofl2F8+dxoU>< z=7+a&xCGU%aoV36(|e5mnkjxjsn(6AG@^^a3i!8I>lqSF-fWq|fJCxtYuMS!u%9k; z9unwdZ}NoN%BPH9O;0TMs$(UVTycNM^Rw&}4uome3`{`Chxv=pl6rY64CO9rUFel6 zX5VIDp3ZF_^(jbS3i&B7aa)D+5$u92~lVPAQ|=~ z&~|L=IzSYXqC6PZ|9cJL`&g@rC8PSIFj-X7{jSJge_*e^kPGe=;7-|;_%vnT_?*>E z{Bqzjlw3gx_SAN5U4#}L^$GqP#+dp7h|rP>?$f@VxBXr&<^(Qqk8H^(U3qyy5^?p4 zxgB?kO4Sd8L|1b=3!XCNF`#54)-5k_%rb>MXwa?pbG%~S73YtlJxbJ?tSm=x^@H}y zNC5zV7E=kO0Ewv*{9^|pGWWjk^&C#MT>T*1cr1N7M$cI5dCxecT zrBgnPtKu^``KxZbajax~c=^YRE+x5Hl@A-?$VepgOUn z1v_i6tTK6}8T%-eSzd-*QXI|q`78c7ADdo7b{L#)uCt$*t3sya< zebwGIXDkhi$T+B-R!?(+#lweeA}-#kdT=+pi_k?8UEe|s*42GTMtikz}Tl$6TH)$g#z7WHIp(v{^ z@gaWSn?()GgVui2iT7YA1Ax6N>jqqaKq_-kCv@2uCe&bI1}IaNBJ#{FJPUGVximB~ z_6K}ZUtC4!1MH&S7CW-^9zfa+b)`cH<0cp#{s{K~HaL%McV*)z|MIv=gE#DC?3ir3 z(CS2E<<5Qdl3ec6oxFaHgLD*>t@fCawcPOs6QNStbhP5E{%3Rg^|O^-BP+vzg*SEo zPJ%)&=)HryyYptMq}EeE6?24$u)pI{S;-0f4#pj>Ls>=m*Hd9X!}|3L8H|!& zJ(5tFYS7>)c%8s0A7vz9db4WEbHb2^oLM<`o=MN*+E-C#Va~Ozo(AF~5&7rt^LuX% z6$`OGNWg=h?|aKf zzbn9wmmF<=ifoWmBF>q~C1HqZ@X=(>G&xCp&-2NWEb}6Lo#&}}#h;$uyrbwY%d{@= zd!drwY^b{S#HWwU`G_Hra1Vd{oL57en-?0GaO~tlG8sO9Asf+>5IIROShe1St2adk z5$eRi$ItKFAxME%kcdrznzMUTuItF<Yu9sS^_2(y_CuRe*C$~(nvOk|2KQ8!7+?a6=^wnR}dk?}@mYjHsYm^JM$ zg6YycjE2*6eUnHd%?~UY-sVIRMkgsm$=hWQ03=1XTs&)%HV3tBcY!Lx>6)1t-XtbT z)iy2ahNh8(MkF~mLV*;+Vr&Pb2sVtsjpfE}oYwnxKjuBQ`S`oD&rBQFcX1o{rf`1n zwC+0M#Myw(&)%}~$@A2){yK5R?SuLXPQ?TU66n-W1I*mXEYN27oKD4th@J!uhUl;f z?IT58nhT~%&GOuA?v4R*(uX_cLM7jv%}z3yDie=lW+EGw9?k5m044#MDFkstAWurh zq~N2ti0-pxW^W|Cuxdh*-4lI+Jb`v2PuJ(RE1eDM);QEz)ps=WdsCRCf64#U`JU1S zb)NVnH?KY@Gn+91d)w2rhuJt2 zFVKGY?X1gqvNq|=X2G&E%oJ5)IuM4SZF9gVXUPPPTCSg^+*{#OlDWT)eKFAEl+v6& z&xYb*r29rpGJREvd2R7Un)$7qn~YN*PhzT$s`0j>k)}xbx(MBgy4?mfneOZWH95OY zZ`=$U$t`UecGHK+R9(~yH)L$%#n}2iC~NBx(b!v$+SKA9Q@2ql+7qjtS=hLRbj{N1 znc7k$Oqs6ZBp^vdc{@8KFsV)A-I%Hk9mc374TzYe;q;i~dD`NclC+U4K_)VPJ|?+pn>AX1Zc2N zR3*{#HKBD|X@4R*(1K4KzA0Kg9b_ndbrUOK{7EEjw(;|30iqMO@x7(F9U*KC>~%Wi zB*Sh}?pmXm<~ou?LRXJNYk?%tq&7fDp{!)?;7 zzwG>i%Id2dP<^v%JFxz2T|CeCRgr|LfDF0XGTqDqX;nc9?_*A4^kFw`AR=%O<185h zrv+=8Kcw(8YE7DoZDWo$aMRSXO{ZPj5K@xwR_<#e4$_P>(Wz5{?KFFw47Jq zZKgJe@`31js&J>zx;1s%D$iiI3}nDqTx`oTwZT8W>ZVlpm``yV=Ns-;UuTxRdxW=W zqMJ?CH*_-NCKg|GCE>Aj9rNzUv3khG6S{(+#Lh;VYYe8PABpfgx z(`J0Q|rjIFMq_}C-2fmC7Gxd2GVN?=0xzYo*2smB!Q4oWm1QM+W*O8N$){Y zxot9*3_FlwNyx+aKx(h4jOz2R8ire)ckd_NjOu_Reuu@@4XXN=CKg}!_g%a1_DkK> zQ+=Fn!@89nlm}ywL1y6jW{zU#BX%fjltXE@MgMOG(-z@Exi3c8lFY(}Vs0>4loe$b zdLvpPYk)yZnd~Uzg(3i{F=<;U?AD`1){8A%nG$fx9ztG7YgpabTMTHGYzpSSCsBlz zn3J&Ley^$!;#TL~KHK`5K41_x+32T_jpFnMZ^XpeG|ud)Zct|@JR4l6+N;EgDG#cn zq2GftS}2o@+VH!`vtVGB8{_Px2;9&GlKY%;iwJsZ6i6FLuYpi*hVUD7&NfaZ`IV@p zm=-cSWVRbYLJkp1$`I-;O=ZK1v}psW5=u6Rq((D7$!YdFRd6Yr&iat18F5k?CSiq% z-k{%WJWf>6-wfBgf;wyZ(Kb|~#664p`10KUF zItiv~hE7Q2g`)pb&zYd4(vLQUczE2B;!oJQD^GL#HKfw~XIhh|!oW)}HI2w@Lf)Cwg5#T)i0 z!F%t&zxRz(DRnHqskL{0_fM5lUX4>#az3^Grmwm|-8a)zO`V^qsH1d}qpF6OZ4|;s zRo6rdHfk73>ofpNi)u1+0eU`0`C}aM>Ga>7qzSiTgBMA>VrCfBN}+>~C1cyzaC62Bps z{-=9m`b3iW_@BGgcenKFFRB$3Wl1HPc^5NZE2w+-rRwMj(b-nUoX8F~W63zYle)1z1<*B^Dmya5QblJLo=uA_wdl5)OeUscs@M$-)lfEU+r`optTENhvFIM51m>jr zB+r}qtQdH%c|U9hmm#Q3m5hz0Ph|AB_ta+2*|p?u`S>YvZp+g7U7h#omX|+QSnYQk zR5jgBVN&-7HF>4(zdx#sZpL&j?7cbNRn*yCDXKf2NUfS>`d}FUgGn=`576WdvM3Vs zcr>G;_7`Q|q!aS=9-2sj)KZ~Mdec5J_@tz2OVW5dcSuS@i?vF)FY?hCHC#HpP3%E} z&64@lY=)^#WvU3%Ztw1WW8om%iBazv!h``6nAv5@eXAll@;03f==MR?O|9M^-;BCv zr=wX?;wMp?k79nO-*q;(?eFZKhka~%ZQbLh{;Mf})9-up`rm$Sf0wx@==IfWHr+$^ z9y8a2b{O{&(EBQ~!VSnf+1s%bWLcyRQ7m`}3>s)ApKeUxRrbKkD=Uqkr$4 zd+2O%Kh;S0uU`1{27hcg=0-i=_m#(|56@SB|Mu%_wr>_Xvw;7I4#7+r=>5bLMA%)O}iCm)(f%`?0x~f7AkJJ}++$p4->d9jNVVpAO?c>UwQ| z&%IuKWgXc5j#`=h^xJR$=d0@@-u=Xz(RcS3yYIhyk4bQGzW)CAzyJ1+fBfV3ue-Ha z|D4Lm^XL0HbuGyA+`HG({cL3t*4I~OZc~Ku{rL4Ybo0LY`1Lh*EUT{fdw$RP^Zobr zwWs&9`*Xc7?%$oBbA3;{_vHTl(>0r}P5t@q{j9%hx)%2}>t?L!y{|vt{eSmdy5HAb zgX!L-YgF&C`hHCJW_Nw2-)TNy-REff%%;zDeZ21aPqUKgwRZQz^m#XHR`+=I_tAaY zeSf;P^}p#kCH+9Ea=QDXW4N08_5RtjFSQ#~w;)e9+;nm7i%ln*vpJm&Dt&0{1&qu1g1j9`UxFnq?_^-i(ZRqGG zON?acDF($b_3n-6KG2?H9_RDom$+@$Ri0eu^TPB}G6-ISoLBpEiDJ%#|^#b}e{euG=uNOI|LV)<+7J)Aay zDYl-Hd}*Qj5u2uwann7rMHg+1&6|i9+U!XIIzg++B14uC3hGSp^qo|_3KnW*^@rM^ z&PMdHfz(oQs*27AWHzgFvAfObuAq_}m}IW$hEJ|=>onhl(pmP|WU@373SbQ7R9p^yl5 zWKL-&F*JEUs6~(fmPE5K2H%vClwcofHc~h%hot)8V#0Mx8y?%IXi- z;yar~9Rj|YE1iw#ZttBI-@QqlKO0(gip=g70$PYuZY?FZ>Q>oT5$;=OMJeR z;ykt3HU}Kj&NiFQ)bZFrrjuDcL3ZoURAEEixc-n8)W=utX}+C5Z8kF-XqQ?TW=K1= zXH1)m;?Qa1*hIUNcwmzVY!{hnqp_PIEl`x4O7p=9^JCrp_U}w&MQSbD;K3BBo;m~u z$wi1NBZ8sO(GC5apsF-aqx&Wat1R}5u61gu8^M-Y&o=o_RoQnQ>kr%FBXYox+Nf&# z?^IQk(%LDKHYZq^ zm<`Gh3hovli6Gvz1WElxfK01uGSJm+AQ%l>DL9e9vL;}ST2?f~r&j7_@NNj;CfILV z@t6f-6B=iPB?LQ8?YTtHpIStgGEc3>BuLpDLJV^*&1n2fw&3{Sm`bqtkRCRCY5G0#VvFIth z&m_+ppWKEXkv6GKAfA;yrvzepohfh02xd}ywtH^rFhMPCid`Yo^>oclm1+(i)3u&ot#@-4#=nk>CISKc(9HkJ+GZ#8LG_QOmCrK}_Fw{*BrZ6ahUccT5yBot8;% zn^Z_CWj3Y7ZVbh#Wn%~K(+Ak5ry3Kl3DryOsASlspiF8x(Z*(Snhg2F%;eJi$wsqN zNL~`tP&Gz^6WUbhJ+y7^=~_{TAqh36YJ2MHB(cdPS|XuEm+f?nxOIcNkw<^34XP7M zoQ~RjgCkB+Y}VRcbs%i87I_3ezFb zprLHia$0*T%BWUQb&dYhZBX^=_DS6_9Z6npW>7S6H&lnT2&MtC@wB9t86}>Y{y)ty zQg451g(PoR>gy+|uWjF@)@#~;rj42o1Vkes9})RY=nzAdkp=addEa)XNg|>x3^-Xk z(tDB^m)-ZgF})`I5*<*8y=mwnlXo!9meXc?!(i{!+bLe-PqRTy==!svY?5g0zfoYI zNyYtYNZ?l!S2y2hf(W-kd0n{M+b}%}f4e<%Z&GF7gi^i@v$NM@lN|ijdv6L{e48Zw zHqm#JK22%WZ$2yCb)eTu!Gmu@``-NBCPegYAZBgpP0<{Gnhk0S0IQ;je`liXwAh*y z;2)Lpn>LQ^hxP4Z{FRvPA02}}rHlTk=dw+P-KP8gsMJ_9)EgWT?pJ1?luMDQG)6exM)K2=zW^P?ifOv1Oho&1=t|M%>Amd^A?Yxc5>?FtYtome zRNWMFZWEDHigXt_p6-KmXqc{X_xrlTXu7T`8G3slsXyCoeAC+apSTzNH&NKV9|;7e QjQ{`u07*qoM6N<$fAOs9Ad#wPNqpnypooDhWZ5 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�jKRC*gD+nP!>VZK(>{<_RPh=2 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 literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/misc_bg/inv_frame_bag.png b/assets/voxygen/element/misc_bg/inv_frame_bag.png new file mode 100644 index 0000000000000000000000000000000000000000..09f19eeb2a9684456b028b684f7c1556f0787026 GIT binary patch literal 10619 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 ReadStorage { self.state.read_storage() } - pub fn loadouts(&self) -> ReadStorage { self.state.read_storage() } - /// Send a chat message to the server. pub fn send_chat(&mut self, message: String) { match validate_chat_msg(&message) { @@ -1455,11 +1453,10 @@ impl Client { self.presence = None; self.clean_state(); }, - ServerGeneral::InventoryUpdate(mut inventory, event) => { + ServerGeneral::InventoryUpdate(inventory, event) => { match event { InventoryUpdateEvent::CollectFailed => {}, _ => { - inventory.recount_items(); // Push the updated inventory component to the client self.state.write_component(self.entity, inventory); }, diff --git a/common/net/src/msg/ecs_packet.rs b/common/net/src/msg/ecs_packet.rs index 7b28f98496..25860655e7 100644 --- a/common/net/src/msg/ecs_packet.rs +++ b/common/net/src/msg/ecs_packet.rs @@ -19,6 +19,7 @@ sum_type! { Energy(comp::Energy), Health(comp::Health), LightEmitter(comp::LightEmitter), + Inventory(comp::Inventory), Item(comp::Item), Scale(comp::Scale), Group(comp::Group), @@ -28,7 +29,6 @@ sum_type! { Collider(comp::Collider), Gravity(comp::Gravity), Sticky(comp::Sticky), - Loadout(comp::Loadout), CharacterState(comp::CharacterState), Pos(comp::Pos), Vel(comp::Vel), @@ -51,6 +51,7 @@ sum_type! { Energy(PhantomData), Health(PhantomData), LightEmitter(PhantomData), + Inventory(PhantomData), Item(PhantomData), Scale(PhantomData), Group(PhantomData), @@ -60,7 +61,6 @@ sum_type! { Collider(PhantomData), Gravity(PhantomData), Sticky(PhantomData), - Loadout(PhantomData), CharacterState(PhantomData), Pos(PhantomData), Vel(PhantomData), @@ -83,6 +83,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::Energy(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Health(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::LightEmitter(comp) => sync::handle_insert(comp, entity, world), + EcsCompPacket::Inventory(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Item(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Scale(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Group(comp) => sync::handle_insert(comp, entity, world), @@ -92,7 +93,6 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::Collider(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Gravity(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Sticky(comp) => sync::handle_insert(comp, entity, world), - EcsCompPacket::Loadout(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::CharacterState(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Pos(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Vel(comp) => sync::handle_insert(comp, entity, world), @@ -113,6 +113,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::Energy(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Health(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::LightEmitter(comp) => sync::handle_modify(comp, entity, world), + EcsCompPacket::Inventory(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Item(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Scale(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Group(comp) => sync::handle_modify(comp, entity, world), @@ -122,7 +123,6 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::Collider(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Gravity(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Sticky(comp) => sync::handle_modify(comp, entity, world), - EcsCompPacket::Loadout(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::CharacterState(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Pos(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Vel(comp) => sync::handle_modify(comp, entity, world), @@ -145,6 +145,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPhantom::LightEmitter(_) => { sync::handle_remove::(entity, world) }, + EcsCompPhantom::Inventory(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Item(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Scale(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Group(_) => sync::handle_remove::(entity, world), @@ -154,7 +155,6 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPhantom::Collider(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Gravity(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Sticky(_) => sync::handle_remove::(entity, world), - EcsCompPhantom::Loadout(_) => sync::handle_remove::(entity, world), EcsCompPhantom::CharacterState(_) => { sync::handle_remove::(entity, world) }, diff --git a/common/src/bin/csv_export/main.rs b/common/src/bin/csv_export/main.rs index 5e408e78a9..cdfa9bc220 100644 --- a/common/src/bin/csv_export/main.rs +++ b/common/src/bin/csv_export/main.rs @@ -102,6 +102,7 @@ fn get_armor_kind(kind: &ArmorKind) -> String { ArmorKind::Neck(_) => "Neck".to_string(), ArmorKind::Head(_) => "Head".to_string(), ArmorKind::Tabard(_) => "Tabard".to_string(), + ArmorKind::Bag(_) => "Bag".to_string(), } } @@ -118,6 +119,7 @@ fn get_armor_kind_kind(kind: &ArmorKind) -> String { ArmorKind::Neck(x) => x.clone(), ArmorKind::Head(x) => x.clone(), ArmorKind::Tabard(x) => x.clone(), + ArmorKind::Bag(x) => x.clone(), } } diff --git a/common/src/character.rs b/common/src/character.rs index dbf5f9713c..fc596b432d 100644 --- a/common/src/character.rs +++ b/common/src/character.rs @@ -1,6 +1,6 @@ //! Structs representing a playable Character -use crate::comp; +use crate::{comp, comp::inventory::Inventory}; use serde::{Deserialize, Serialize}; /// The limit on how many characters that a player can have @@ -21,5 +21,5 @@ pub struct CharacterItem { pub character: Character, pub body: comp::Body, pub level: usize, - pub loadout: comp::Loadout, + pub inventory: Inventory, } diff --git a/common/src/cmd.rs b/common/src/cmd.rs index c2a8fc63d6..b3c3c3e4d4 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -42,6 +42,7 @@ pub enum ChatCommand { Campfire, Debug, DebugColumn, + DropAll, Dummy, Explosion, Faction, @@ -94,6 +95,7 @@ pub static CHAT_COMMANDS: &[ChatCommand] = &[ ChatCommand::Campfire, ChatCommand::Debug, ChatCommand::DebugColumn, + ChatCommand::DropAll, ChatCommand::Dummy, ChatCommand::Explosion, ChatCommand::Faction, @@ -230,6 +232,7 @@ impl ChatCommand { "Prints some debug information about a column", NoAdmin, ), + ChatCommand::DropAll => cmd(vec![], "Drops all your items on the ground", Admin), ChatCommand::Dummy => cmd(vec![], "Spawns a training dummy", Admin), ChatCommand::Explosion => cmd( vec![Float("radius", 5.0, Required)], @@ -445,6 +448,7 @@ impl ChatCommand { ChatCommand::Campfire => "campfire", ChatCommand::Debug => "debug", ChatCommand::DebugColumn => "debug_column", + ChatCommand::DropAll => "dropall", ChatCommand::Dummy => "dummy", ChatCommand::Explosion => "explosion", ChatCommand::Faction => "faction", @@ -533,12 +537,11 @@ impl Display for ChatCommand { impl FromStr for ChatCommand { type Err = (); - #[allow(clippy::manual_strip)] fn from_str(keyword: &str) -> Result { - let kwd = if keyword.starts_with('/') { - &keyword[1..] + let kwd = if let Some(stripped) = keyword.strip_prefix('/') { + stripped } else { - &keyword[..] + &keyword }; if keyword.len() == 1 { if let Some(c) = keyword diff --git a/common/src/combat.rs b/common/src/combat.rs index a414735cd6..7d3cc401c1 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -1,5 +1,8 @@ use crate::{ - comp::{HealthChange, HealthSource, Loadout}, + comp::{ + inventory::item::{armor::Protection, ItemKind}, + HealthChange, HealthSource, Inventory, + }, uid::Uid, util::Dir, }; @@ -31,8 +34,32 @@ pub struct Damage { } impl Damage { - pub fn modify_damage(self, loadout: Option<&Loadout>, uid: Option) -> HealthChange { + /// Returns the total damage reduction provided by all equipped items + pub fn compute_damage_reduction(inventory: &Inventory) -> f32 { + let protection = inventory + .equipped_items() + .filter_map(|item| { + if let ItemKind::Armor(armor) = &item.kind() { + Some(armor.get_protection()) + } else { + None + } + }) + .map(|protection| match protection { + Protection::Normal(protection) => Some(protection), + Protection::Invincible => None, + }) + .sum::>(); + match protection { + Some(dr) => dr / (60.0 + dr.abs()), + None => 1.0, + } + } + + pub fn modify_damage(self, inventory: Option<&Inventory>, uid: Option) -> HealthChange { let mut damage = self.value; + let damage_reduction = inventory.map_or(0.0, |inv| Damage::compute_damage_reduction(inv)); + match self.source { DamageSource::Melee => { // Critical hit @@ -41,7 +68,6 @@ impl Damage { critdamage = damage * 0.3; } // Armor - let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); damage *= 1.0 - damage_reduction; // Critical damage applies after armor for melee @@ -63,7 +89,6 @@ impl Damage { damage *= 1.2; } // Armor - let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); damage *= 1.0 - damage_reduction; HealthChange { @@ -76,7 +101,6 @@ impl Damage { }, DamageSource::Explosion => { // Armor - let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); damage *= 1.0 - damage_reduction; HealthChange { @@ -89,7 +113,6 @@ impl Damage { }, DamageSource::Shockwave => { // Armor - let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); damage *= 1.0 - damage_reduction; HealthChange { @@ -102,7 +125,6 @@ impl Damage { }, DamageSource::Energy => { // Armor - let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); damage *= 1.0 - damage_reduction; HealthChange { @@ -119,7 +141,6 @@ impl Damage { }, DamageSource::Falling => { // Armor - let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction()); if (damage_reduction - 1.0).abs() < f32::EPSILON { damage = 0.0; } diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 0afd6067c0..f6584a385f 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -1,9 +1,8 @@ use crate::{ assets::{self, Asset}, comp::{ - item::{armor::Protection, tool::AbilityMap, Item, ItemKind}, - projectile::ProjectileConstructor, - Body, CharacterState, EnergySource, Gravity, LightEmitter, StateUpdate, + projectile::ProjectileConstructor, Body, CharacterState, EnergySource, Gravity, + LightEmitter, StateUpdate, }, states::{ behavior::JoinData, @@ -12,10 +11,7 @@ use crate::{ }, Knockback, }; -use arraygen::Arraygen; use serde::{Deserialize, Serialize}; -use specs::{Component, DerefFlaggedStorage}; -use specs_idvs::IdvStorage; use std::time::Duration; use vek::Vec3; @@ -304,7 +300,7 @@ impl CharacterAbility { } } - fn default_roll() -> CharacterAbility { + pub fn default_roll() -> CharacterAbility { CharacterAbility::Roll { energy_cost: 100, buildup_duration: 100, @@ -499,93 +495,6 @@ impl CharacterAbility { } } -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct ItemConfig { - pub item: Item, - pub ability1: Option, - pub ability2: Option, - pub ability3: Option, - pub block_ability: Option, - pub dodge_ability: Option, -} - -impl From<(Item, &AbilityMap)> for ItemConfig { - fn from((item, map): (Item, &AbilityMap)) -> Self { - if let ItemKind::Tool(tool) = &item.kind() { - let abilities = tool.get_abilities(map); - - return ItemConfig { - item, - ability1: Some(abilities.primary), - ability2: Some(abilities.secondary), - ability3: abilities.skills.get(0).cloned(), - block_ability: None, - dodge_ability: Some(CharacterAbility::default_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 { - pub active_item: Option, - pub second_item: Option, - - pub lantern: Option, - pub glider: Option, - - #[in_array(get_armor)] - pub shoulder: Option, - #[in_array(get_armor)] - pub chest: Option, - #[in_array(get_armor)] - pub belt: Option, - #[in_array(get_armor)] - pub hand: Option, - #[in_array(get_armor)] - pub pants: Option, - #[in_array(get_armor)] - pub foot: Option, - #[in_array(get_armor)] - pub back: Option, - #[in_array(get_armor)] - pub ring: Option, - #[in_array(get_armor)] - pub neck: Option, - #[in_array(get_armor)] - pub head: Option, - #[in_array(get_armor)] - pub tabard: Option, -} - -impl Loadout { - pub fn get_damage_reduction(&self) -> f32 { - let protection = self - .get_armor() - .iter() - .flat_map(|armor| armor.as_ref()) - .filter_map(|item| { - if let ItemKind::Armor(armor) = &item.kind() { - Some(armor.get_protection()) - } else { - None - } - }) - .map(|protection| match protection { - Protection::Normal(protection) => Some(protection), - Protection::Invincible => None, - }) - .sum::>(); - match protection { - Some(dr) => dr / (60.0 + dr.abs()), - None => 1.0, - } - } -} - impl From<(&CharacterAbility, AbilityKey)> for CharacterState { fn from((ability, key): (&CharacterAbility, AbilityKey)) -> Self { match ability { @@ -975,7 +884,3 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { } } } - -impl Component for Loadout { - type Storage = DerefFlaggedStorage>; -} diff --git a/common/src/comp/inventory/item/armor.rs b/common/src/comp/inventory/item/armor.rs index a3beb9e5af..aba06d5873 100644 --- a/common/src/comp/inventory/item/armor.rs +++ b/common/src/comp/inventory/item/armor.rs @@ -13,6 +13,7 @@ pub enum ArmorKind { Neck(String), Head(String), Tabard(String), + Bag(String), } impl Armor { @@ -43,4 +44,12 @@ pub struct Armor { impl Armor { pub fn get_protection(&self) -> Protection { self.stats.protection } + + #[cfg(test)] + pub fn test_armor(kind: ArmorKind, protection: Protection) -> Armor { + Armor { + kind, + stats: Stats { protection }, + } + } } diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 60e87e784f..063ee47561 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -6,10 +6,15 @@ pub use tool::{AbilitySet, Hands, Tool, ToolKind, UniqueKind}; use crate::{ assets::{self, AssetExt, Error}, + comp::{ + inventory::{item::tool::AbilityMap, InvSlot}, + Body, CharacterAbility, + }, effect::Effect, lottery::Lottery, terrain::{Block, SpriteKind}, }; +use core::mem; use crossbeam_utils::atomic::AtomicCell; use rand::prelude::*; use serde::{Deserialize, Serialize}; @@ -94,6 +99,15 @@ pub enum ItemKind { }, } +impl ItemKind { + pub fn is_equippable(&self) -> bool { + matches!( + self, + ItemKind::Tool(_) | ItemKind::Armor { .. } | ItemKind::Glider(_) | ItemKind::Lantern(_) + ) + } +} + pub type ItemId = AtomicCell>; /* /// The only way to access an item id outside this module is to mutably, atomically update it using @@ -101,11 +115,7 @@ pub type ItemId = AtomicCell>; /// 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 { @@ -124,22 +134,54 @@ pub struct Item { /// amount is hidden because it needs to maintain the invariant that only /// stackable items can have > 1 amounts. amount: NonZeroU32, + /// The slots for items that this item has + slots: Vec, } #[derive(Debug, Serialize, Deserialize)] pub struct ItemDef { #[serde(default)] item_definition_id: String, + pub item_config: Option, pub name: String, pub description: String, pub kind: ItemKind, pub quality: Quality, + #[serde(default)] + pub slots: u16, } impl PartialEq for ItemDef { fn eq(&self, other: &Self) -> bool { self.item_definition_id == other.item_definition_id } } +#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +pub struct ItemConfig { + pub ability1: Option, + pub ability2: Option, + pub ability3: Option, + pub block_ability: Option, + pub dodge_ability: Option, +} + +impl From<(&ItemKind, &AbilityMap)> for ItemConfig { + fn from((item_kind, map): (&ItemKind, &AbilityMap)) -> Self { + if let ItemKind::Tool(tool) = item_kind { + let abilities = tool.get_abilities(map); + + return ItemConfig { + ability1: Some(abilities.primary), + ability2: Some(abilities.secondary), + ability3: abilities.skills.get(0).cloned(), + block_ability: None, + dodge_ability: Some(CharacterAbility::default_roll()), + }; + } + + unimplemented!("ItemConfig is currently only supported for Tools") + } +} + impl ItemDef { pub fn is_stackable(&self) -> bool { matches!( @@ -150,6 +192,25 @@ impl ItemDef { | ItemKind::Utility { .. } ) } + + #[cfg(test)] + pub fn new_test( + item_definition_id: String, + item_config: Option, + kind: ItemKind, + quality: Quality, + slots: u16, + ) -> Self { + Self { + item_definition_id, + item_config, + name: "test item name".to_owned(), + description: "test item description".to_owned(), + kind, + quality, + slots, + } + } } impl PartialEq for Item { @@ -170,8 +231,19 @@ impl assets::Compound for ItemDef { description, kind, quality, + slots, } = raw; + let item_config = if let ItemKind::Tool(_) = kind { + let ability_map_handle = + cache.load::("common.abilities.weapon_ability_manifest")?; + let ability_map = &*ability_map_handle.read(); + + Some(ItemConfig::from((&kind, ability_map))) + } else { + None + }; + // Some commands like /give_item provide the asset specifier separated with \ // instead of . // @@ -180,10 +252,12 @@ impl assets::Compound for ItemDef { Ok(ItemDef { item_definition_id, + item_config, name, description, kind, quality, + slots, }) } } @@ -195,6 +269,8 @@ struct RawItemDef { description: String, kind: ItemKind, quality: Quality, + #[serde(default)] + slots: u16, } impl assets::Asset for RawItemDef { @@ -229,11 +305,12 @@ impl Item { // loadout when no weapon is present pub fn empty() -> Self { Item::new_from_asset_expect("common.items.weapons.empty.empty") } - pub fn new(inner_item: Arc) -> Self { + pub fn new_from_item_def(inner_item: Arc) -> Self { Item { item_id: Arc::new(AtomicCell::new(None)), - item_def: inner_item, amount: NonZeroU32::new(1).unwrap(), + slots: vec![None; inner_item.slots as usize], + item_def: inner_item, } } @@ -241,7 +318,7 @@ impl Item { /// Panics if the asset does not exist. pub fn new_from_asset_expect(asset_specifier: &str) -> Self { let inner_item = Arc::::load_expect_cloned(asset_specifier); - Item::new(inner_item) + Item::new_from_item_def(inner_item) } /// Creates a Vec containing one of each item that matches the provided @@ -254,11 +331,43 @@ impl Item { /// it exists pub fn new_from_asset(asset: &str) -> Result { let inner_item = Arc::::load_cloned(asset)?; - Ok(Item::new(inner_item)) + Ok(Item::new_from_item_def(inner_item)) + } + + pub fn new_default_for_body(body: &Body) -> Self { + let mut item = Item::new_from_asset_expect("common.items.weapons.empty.empty"); + + let empty_def = &*item.item_def; + item.item_def = Arc::new(ItemDef { + slots: empty_def.slots, + name: empty_def.name.clone(), + kind: empty_def.kind.clone(), + description: empty_def.description.clone(), + item_definition_id: empty_def.item_definition_id.clone(), + quality: empty_def.quality, + item_config: Some(ItemConfig { + ability1: Some(CharacterAbility::BasicMelee { + energy_cost: 10, + buildup_duration: 500, + swing_duration: 100, + recover_duration: 100, + base_damage: body.base_dmg(), + knockback: 0.0, + range: body.base_range(), + max_angle: 20.0, + }), + ability2: None, + ability3: None, + block_ability: None, + dodge_ability: None, + }), + }); + + 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)) } + pub fn duplicate(&self) -> Self { Item::new_from_item_def(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 @@ -320,6 +429,11 @@ impl Item { } } + /// Returns an iterator that drains items contained within the item's slots + pub fn drain(&mut self) -> impl Iterator + '_ { + self.slots.iter_mut().filter_map(|x| mem::take(x)) + } + pub fn item_definition_id(&self) -> &str { &self.item_def.item_definition_id } pub fn is_same_item_def(&self, item_def: &ItemDef) -> bool { @@ -338,6 +452,26 @@ impl Item { pub fn quality(&self) -> Quality { self.item_def.quality } + pub fn slots(&self) -> &[InvSlot] { &self.slots } + + pub fn slots_mut(&mut self) -> &mut [InvSlot] { &mut self.slots } + + pub fn item_config_expect(&self) -> &ItemConfig { + &self + .item_def + .item_config + .as_ref() + .expect("Item was expected to have an ItemConfig") + } + + pub fn free_slots(&self) -> usize { self.slots.iter().filter(|x| x.is_none()).count() } + + pub fn populated_slots(&self) -> usize { self.slots().len().saturating_sub(self.free_slots()) } + + pub fn slot(&self, slot: usize) -> Option<&InvSlot> { self.slots.get(slot) } + + pub fn slot_mut(&mut self, slot: usize) -> Option<&mut InvSlot> { self.slots.get_mut(slot) } + pub fn try_reclaim_from_block(block: Block) -> Option { let chosen; let mut rng = rand::thread_rng(); @@ -416,6 +550,7 @@ pub trait ItemDesc { fn name(&self) -> &str; fn kind(&self) -> &ItemKind; fn quality(&self) -> &Quality; + fn num_slots(&self) -> u16; fn item_definition_id(&self) -> &str; } @@ -428,6 +563,8 @@ impl ItemDesc for Item { fn quality(&self) -> &Quality { &self.item_def.quality } + fn num_slots(&self) -> u16 { self.item_def.slots } + fn item_definition_id(&self) -> &str { &self.item_def.item_definition_id } } @@ -440,6 +577,8 @@ impl ItemDesc for ItemDef { fn quality(&self) -> &Quality { &self.quality } + fn num_slots(&self) -> u16 { self.slots } + fn item_definition_id(&self) -> &str { &self.item_definition_id } } diff --git a/common/src/comp/inventory/loadout.rs b/common/src/comp/inventory/loadout.rs new file mode 100644 index 0000000000..4de423af8e --- /dev/null +++ b/common/src/comp/inventory/loadout.rs @@ -0,0 +1,363 @@ +use crate::comp::{ + inventory::{ + item::ItemKind, + slot::{ArmorSlot, EquipSlot}, + InvSlot, + }, + Item, +}; +use serde::{Deserialize, Serialize}; +use std::ops::Range; +use tracing::warn; + +#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +pub struct Loadout { + slots: Vec, +} + +#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +pub struct LoadoutSlot { + /// The EquipSlot that this slot represents + pub(super) equip_slot: EquipSlot, + /// The contents of the slot + slot: InvSlot, + /// The unique string that represents this loadout slot in the database (not + /// synced to clients) + #[serde(skip)] + persistence_key: String, +} + +impl LoadoutSlot { + fn new(equip_slot: EquipSlot, persistence_key: String) -> LoadoutSlot { + LoadoutSlot { + equip_slot, + slot: None, + persistence_key, + } + } +} + +pub(super) struct LoadoutSlotId { + // The index of the loadout item that provides this inventory slot. + pub loadout_idx: usize, + // The index of the slot within its container + pub slot_idx: usize, +} + +pub enum LoadoutError { + InvalidPersistenceKey, +} + +impl Loadout { + pub(super) fn new_empty() -> Self { + Self { + slots: vec![ + (EquipSlot::Lantern, "lantern".to_string()), + (EquipSlot::Glider, "glider".to_string()), + ( + EquipSlot::Armor(ArmorSlot::Shoulders), + "shoulder".to_string(), + ), + (EquipSlot::Armor(ArmorSlot::Chest), "chest".to_string()), + (EquipSlot::Armor(ArmorSlot::Belt), "belt".to_string()), + (EquipSlot::Armor(ArmorSlot::Hands), "hand".to_string()), + (EquipSlot::Armor(ArmorSlot::Legs), "pants".to_string()), + (EquipSlot::Armor(ArmorSlot::Feet), "foot".to_string()), + (EquipSlot::Armor(ArmorSlot::Back), "back".to_string()), + (EquipSlot::Armor(ArmorSlot::Ring1), "ring1".to_string()), + (EquipSlot::Armor(ArmorSlot::Ring2), "ring2".to_string()), + (EquipSlot::Armor(ArmorSlot::Neck), "neck".to_string()), + (EquipSlot::Armor(ArmorSlot::Head), "head".to_string()), + (EquipSlot::Armor(ArmorSlot::Tabard), "tabard".to_string()), + (EquipSlot::Armor(ArmorSlot::Bag1), "bag1".to_string()), + (EquipSlot::Armor(ArmorSlot::Bag2), "bag2".to_string()), + (EquipSlot::Armor(ArmorSlot::Bag3), "bag3".to_string()), + (EquipSlot::Armor(ArmorSlot::Bag4), "bag4".to_string()), + (EquipSlot::Mainhand, "active_item".to_string()), + (EquipSlot::Offhand, "second_item".to_string()), + ] + .into_iter() + .map(|(equip_slot, persistence_key)| LoadoutSlot::new(equip_slot, persistence_key)) + .collect(), + } + } + + /// Replaces the item in the Loadout slot that corresponds to the given + /// EquipSlot and returns the previous item if any + pub(super) fn swap(&mut self, equip_slot: EquipSlot, item: Option) -> Option { + self.slots + .iter_mut() + .find(|x| x.equip_slot == equip_slot) + .and_then(|x| core::mem::replace(&mut x.slot, item)) + } + + /// Returns a reference to the item (if any) equipped in the given EquipSlot + pub(super) fn equipped(&self, equip_slot: EquipSlot) -> Option<&Item> { + self.slot(equip_slot).and_then(|x| x.slot.as_ref()) + } + + fn slot(&self, equip_slot: EquipSlot) -> Option<&LoadoutSlot> { + self.slots + .iter() + .find(|loadout_slot| loadout_slot.equip_slot == equip_slot) + } + + pub(super) fn loadout_idx_for_equip_slot(&self, equip_slot: EquipSlot) -> Option { + self.slots + .iter() + .position(|loadout_slot| loadout_slot.equip_slot == equip_slot) + } + + /// Returns all loadout items paired with their persistence key + pub(super) fn items_with_persistence_key(&self) -> impl Iterator)> { + self.slots + .iter() + .map(|x| (x.persistence_key.as_str(), x.slot.as_ref())) + } + + /// Sets a loadout item in the correct slot using its persistence key. Any + /// item that already exists in the slot is lost. + pub fn set_item_at_slot_using_persistence_key( + &mut self, + persistence_key: &str, + item: Item, + ) -> Result<(), LoadoutError> { + if let Some(slot) = self + .slots + .iter_mut() + .find(|x| x.persistence_key == persistence_key) + { + slot.slot = Some(item); + Ok(()) + } else { + Err(LoadoutError::InvalidPersistenceKey) + } + } + + /// Swaps the contents of two loadout slots + pub(super) fn swap_slots(&mut self, equip_slot_a: EquipSlot, equip_slot_b: EquipSlot) { + if self.slot(equip_slot_b).is_none() || self.slot(equip_slot_b).is_none() { + // Currently all loadouts contain slots for all EquipSlots so this can never + // happen, but if loadouts with alternate slot combinations are + // introduced then it could. + warn!("Cannot swap slots for non-existent equip slot"); + return; + } + + let item_a = self.swap(equip_slot_a, None); + let item_b = self.swap(equip_slot_b, None); + + // Check if items can go in the other slots + if item_a + .as_ref() + .map_or(true, |i| equip_slot_b.can_hold(&i.kind())) + && item_b + .as_ref() + .map_or(true, |i| equip_slot_a.can_hold(&i.kind())) + { + // Swap + self.swap(equip_slot_b, item_a).unwrap_none(); + self.swap(equip_slot_a, item_b).unwrap_none(); + } else { + // Otherwise put the items back + self.swap(equip_slot_a, item_a).unwrap_none(); + self.swap(equip_slot_b, item_b).unwrap_none(); + } + } + + /// Gets a slot that an item of a particular `ItemKind` can be equipped + /// into. The first empty slot compatible with the item will be + /// returned, or if there are no free slots then the first occupied slot + /// will be returned. The bool part of the tuple indicates whether an item + /// is already equipped in the slot. + pub(super) fn get_slot_to_equip_into(&self, item_kind: &ItemKind) -> Option { + let mut suitable_slots = self + .slots + .iter() + .filter(|s| s.equip_slot.can_hold(item_kind)); + + let first = suitable_slots.next(); + + first + .into_iter() + .chain(suitable_slots) + .find(|loadout_slot| loadout_slot.slot.is_none()) + .map(|x| x.equip_slot) + .or_else(|| first.map(|x| x.equip_slot)) + } + + /// Returns the `InvSlot` for a given `LoadoutSlotId` + pub(super) fn inv_slot(&self, loadout_slot_id: LoadoutSlotId) -> Option<&InvSlot> { + self.slots + .get(loadout_slot_id.loadout_idx) + .and_then(|loadout_slot| loadout_slot.slot.as_ref()) + .and_then(|item| item.slot(loadout_slot_id.slot_idx)) + } + + /// Returns the `InvSlot` for a given `LoadoutSlotId` + pub(super) fn inv_slot_mut(&mut self, loadout_slot_id: LoadoutSlotId) -> Option<&mut InvSlot> { + self.slots + .get_mut(loadout_slot_id.loadout_idx) + .and_then(|loadout_slot| loadout_slot.slot.as_mut()) + .and_then(|item| item.slot_mut(loadout_slot_id.slot_idx)) + } + + /// Returns all inventory slots provided by equipped loadout items, along + /// with their `LoadoutSlotId` + pub(super) fn inv_slots_with_id(&self) -> impl Iterator { + self.slots + .iter() + .enumerate() + .filter_map(|(i, loadout_slot)| { + loadout_slot.slot.as_ref().map(|item| (i, item.slots())) + }) + .flat_map(|(loadout_slot_index, loadout_slots)| { + loadout_slots + .iter() + .enumerate() + .map(move |(item_slot_index, inv_slot)| { + ( + LoadoutSlotId { + loadout_idx: loadout_slot_index, + slot_idx: item_slot_index, + }, + inv_slot, + ) + }) + }) + } + + /// Returns all inventory slots provided by equipped loadout items + pub(super) fn inv_slots_mut(&mut self) -> impl Iterator { + self.slots.iter_mut() + .filter_map(|x| x.slot.as_mut().map(|item| item.slots_mut())) // Discard loadout items that have no slots of their own + .flat_map(|loadout_slots| loadout_slots.iter_mut()) //Collapse iter of Vec to iter of InvSlot + } + + /// Gets the range of loadout-provided inventory slot indexes that are + /// provided by the item in the given `EquipSlot` + pub(super) fn slot_range_for_equip_slot(&self, equip_slot: EquipSlot) -> Option> { + self.slots + .iter() + .map(|loadout_slot| { + ( + loadout_slot.equip_slot, + loadout_slot + .slot + .as_ref() + .map_or(0, |item| item.slots().len()), + ) + }) + .scan(0, |acc_len, (equip_slot, len)| { + let res = Some((equip_slot, len, *acc_len)); + *acc_len += len; + res + }) + .find(|(e, len, _)| *e == equip_slot && len > &0) + .map(|(_, slot_len, start)| start..start + slot_len) + } + + /// Attempts to equip the item into a compatible, unpopulated loadout slot. + /// If no slot is available the item is returned. + #[must_use = "Returned item will be lost if not used"] + pub(super) fn try_equip(&mut self, item: Item) -> Result<(), Item> { + if let Some(loadout_slot) = self + .slots + .iter_mut() + .find(|s| s.slot.is_none() && s.equip_slot.can_hold(item.kind())) + { + loadout_slot.slot = Some(item); + Ok(()) + } else { + Err(item) + } + } + + pub(super) fn items(&self) -> impl Iterator { + self.slots.iter().filter_map(|x| x.slot.as_ref()) + } +} + +#[cfg(test)] +mod tests { + use crate::comp::{ + inventory::{ + item::{ + armor::{Armor, ArmorKind, Protection}, + ItemKind, + }, + loadout::Loadout, + slot::{ArmorSlot, EquipSlot}, + test_helpers::get_test_bag, + }, + Item, + }; + + #[test] + fn test_slot_range_for_equip_slot() { + let mut loadout = Loadout::new_empty(); + + let bag1_slot = EquipSlot::Armor(ArmorSlot::Bag1); + let bag = get_test_bag(18); + loadout.swap(bag1_slot, Some(bag)); + + let result = loadout.slot_range_for_equip_slot(bag1_slot).unwrap(); + + assert_eq!(0..18, result); + } + + #[test] + fn test_slot_range_for_equip_slot_no_item() { + let loadout = Loadout::new_empty(); + let result = loadout.slot_range_for_equip_slot(EquipSlot::Armor(ArmorSlot::Bag1)); + + assert_eq!(None, result); + } + + #[test] + fn test_slot_range_for_equip_slot_item_without_slots() { + let mut loadout = Loadout::new_empty(); + + let feet_slot = EquipSlot::Armor(ArmorSlot::Feet); + let boots = Item::new_from_asset_expect("common.items.testing.test_boots"); + loadout.swap(feet_slot, Some(boots)); + let result = loadout.slot_range_for_equip_slot(feet_slot); + + assert_eq!(None, result); + } + + #[test] + fn test_get_slot_to_equip_into_second_bag_slot_free() { + let mut loadout = Loadout::new_empty(); + + loadout.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(get_test_bag(1))); + + let result = loadout + .get_slot_to_equip_into(&ItemKind::Armor(Armor::test_armor( + ArmorKind::Bag("test".to_string()), + Protection::Normal(0.0), + ))) + .unwrap(); + + assert_eq!(EquipSlot::Armor(ArmorSlot::Bag2), result); + } + + #[test] + fn test_get_slot_to_equip_into_no_bag_slots_free() { + let mut loadout = Loadout::new_empty(); + + loadout.swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(get_test_bag(1))); + loadout.swap(EquipSlot::Armor(ArmorSlot::Bag2), Some(get_test_bag(1))); + loadout.swap(EquipSlot::Armor(ArmorSlot::Bag3), Some(get_test_bag(1))); + loadout.swap(EquipSlot::Armor(ArmorSlot::Bag4), Some(get_test_bag(1))); + + let result = loadout + .get_slot_to_equip_into(&ItemKind::Armor(Armor::test_armor( + ArmorKind::Bag("test".to_string()), + Protection::Normal(0.0), + ))) + .unwrap(); + + assert_eq!(EquipSlot::Armor(ArmorSlot::Bag1), result); + } +} diff --git a/common/src/loadout_builder.rs b/common/src/comp/inventory/loadout_builder.rs similarity index 57% rename from common/src/loadout_builder.rs rename to common/src/comp/inventory/loadout_builder.rs index a6b8c75813..82a36418cc 100644 --- a/common/src/loadout_builder.rs +++ b/common/src/comp/inventory/loadout_builder.rs @@ -1,7 +1,11 @@ use crate::comp::{ biped_large, golem, - item::{tool::AbilityMap, Item, ItemKind}, - quadruped_low, quadruped_medium, theropod, Body, CharacterAbility, ItemConfig, Loadout, + inventory::{ + loadout::Loadout, + slot::{ArmorSlot, EquipSlot}, + }, + item::{Item, ItemKind}, + quadruped_low, quadruped_medium, theropod, Body, }; use rand::Rng; @@ -13,19 +17,18 @@ use rand::Rng; /// use veloren_common::{ /// assets::AssetExt, /// comp::item::tool::AbilityMap, +/// comp::Item, /// LoadoutBuilder, /// }; /// -/// let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest"); -/// /// // Build a loadout with character starter defaults and a specific sword with default sword abilities /// let loadout = LoadoutBuilder::new() /// .defaults() -/// .active_item(Some(LoadoutBuilder::default_item_config_from_str( -/// "common.items.weapons.sword.zweihander_sword_0", &map -/// ))) +/// .active_item(Some(Item::new_from_asset_expect("common.items.weapons.sword.zweihander_sword_0"))) /// .build(); /// ``` +#[derive(Clone)] +pub struct LoadoutBuilder(Loadout); #[derive(Copy, Clone)] pub enum LoadoutConfig { @@ -40,29 +43,9 @@ pub enum LoadoutConfig { Warlock, } -pub struct LoadoutBuilder(Loadout); - impl LoadoutBuilder { #[allow(clippy::new_without_default)] // TODO: Pending review in #587 - pub fn new() -> Self { - Self(Loadout { - active_item: None, - second_item: None, - shoulder: None, - chest: None, - belt: None, - hand: None, - pants: None, - foot: None, - back: None, - ring: None, - neck: None, - lantern: None, - glider: None, - head: None, - tabard: None, - }) - } + pub fn new() -> Self { Self(Loadout::new_empty()) } /// Set default armor items for the loadout. This may vary with game /// updates, but should be safe defaults for a new character. @@ -73,7 +56,7 @@ impl LoadoutBuilder { .pants(Some(Item::new_from_asset_expect( "common.items.armor.starter.rugged_pants", ))) - .foot(Some(Item::new_from_asset_expect( + .feet(Some(Item::new_from_asset_expect( "common.items.armor.starter.sandals_0", ))) .lantern(Some(Item::new_from_asset_expect( @@ -89,7 +72,6 @@ impl LoadoutBuilder { pub fn build_loadout( body: Body, mut main_tool: Option, - map: &AbilityMap, config: Option, ) -> Self { // If no main tool is passed in, checks if species has a default main tool @@ -251,284 +233,230 @@ impl LoadoutBuilder { // Constructs ItemConfig from Item let active_item = if let Some(ItemKind::Tool(_)) = main_tool.as_ref().map(|i| i.kind()) { - main_tool.map(|item| ItemConfig::from((item, map))) + main_tool } else { - Some(LoadoutBuilder::animal(body)) + Some(Item::new_default_for_body(&body)) }; // Creates rest of loadout let loadout = if let Some(config) = config { use LoadoutConfig::*; match config { - Guard => Loadout { - active_item, - second_item: None, - shoulder: Some(Item::new_from_asset_expect( + Guard => LoadoutBuilder::new() + .active_item(active_item) + .shoulder(Some(Item::new_from_asset_expect( "common.items.armor.shoulder.steel_0", - )), - chest: Some(Item::new_from_asset_expect( + ))) + .chest(Some(Item::new_from_asset_expect( "common.items.armor.chest.steel_0", - )), - belt: Some(Item::new_from_asset_expect( + ))) + .belt(Some(Item::new_from_asset_expect( "common.items.armor.belt.steel_0", - )), - hand: Some(Item::new_from_asset_expect( + ))) + .hands(Some(Item::new_from_asset_expect( "common.items.armor.hand.steel_0", - )), - pants: Some(Item::new_from_asset_expect( + ))) + .pants(Some(Item::new_from_asset_expect( "common.items.armor.pants.steel_0", - )), - foot: Some(Item::new_from_asset_expect( + ))) + .feet(Some(Item::new_from_asset_expect( "common.items.armor.foot.steel_0", - )), - back: None, - ring: None, - neck: None, - lantern: match rand::thread_rng().gen_range(0, 3) { + ))) + .lantern(match rand::thread_rng().gen_range(0, 3) { 0 => Some(Item::new_from_asset_expect("common.items.lantern.black_0")), _ => None, - }, - glider: None, - head: None, - tabard: None, - }, - Outcast => Loadout { - active_item, - second_item: None, - shoulder: Some(Item::new_from_asset_expect( + }) + .build(), + Outcast => LoadoutBuilder::new() + .active_item(active_item) + .shoulder(Some(Item::new_from_asset_expect( "common.items.armor.shoulder.cloth_purple_0", - )), - chest: Some(Item::new_from_asset_expect( + ))) + .chest(Some(Item::new_from_asset_expect( "common.items.armor.chest.cloth_purple_0", - )), - belt: Some(Item::new_from_asset_expect( + ))) + .belt(Some(Item::new_from_asset_expect( "common.items.armor.belt.cloth_purple_0", - )), - hand: Some(Item::new_from_asset_expect( + ))) + .hands(Some(Item::new_from_asset_expect( "common.items.armor.hand.cloth_purple_0", - )), - pants: Some(Item::new_from_asset_expect( + ))) + .pants(Some(Item::new_from_asset_expect( "common.items.armor.pants.cloth_purple_0", - )), - foot: Some(Item::new_from_asset_expect( + ))) + .feet(Some(Item::new_from_asset_expect( "common.items.armor.foot.cloth_purple_0", - )), - back: None, - ring: None, - neck: None, - lantern: match rand::thread_rng().gen_range(0, 3) { + ))) + .lantern(match rand::thread_rng().gen_range(0, 3) { 0 => Some(Item::new_from_asset_expect("common.items.lantern.black_0")), _ => None, - }, - glider: None, - head: None, - tabard: None, - }, - Highwayman => Loadout { - active_item, - second_item: None, - shoulder: Some(Item::new_from_asset_expect( + }) + .build(), + Highwayman => LoadoutBuilder::new() + .active_item(active_item) + .shoulder(Some(Item::new_from_asset_expect( "common.items.armor.shoulder.leather_0", - )), - chest: Some(Item::new_from_asset_expect( + ))) + .chest(Some(Item::new_from_asset_expect( "common.items.armor.chest.leather_0", - )), - belt: Some(Item::new_from_asset_expect( + ))) + .belt(Some(Item::new_from_asset_expect( "common.items.armor.belt.leather_0", - )), - hand: Some(Item::new_from_asset_expect( + ))) + .hands(Some(Item::new_from_asset_expect( "common.items.armor.hand.leather_0", - )), - pants: Some(Item::new_from_asset_expect( + ))) + .pants(Some(Item::new_from_asset_expect( "common.items.armor.pants.leather_0", - )), - foot: Some(Item::new_from_asset_expect( + ))) + .feet(Some(Item::new_from_asset_expect( "common.items.armor.foot.leather_0", - )), - back: None, - ring: None, - neck: None, - lantern: match rand::thread_rng().gen_range(0, 3) { + ))) + .lantern(match rand::thread_rng().gen_range(0, 3) { 0 => Some(Item::new_from_asset_expect("common.items.lantern.black_0")), _ => None, - }, - glider: None, - head: None, - tabard: None, - }, - Bandit => Loadout { - active_item, - second_item: None, - shoulder: Some(Item::new_from_asset_expect( + }) + .build(), + Bandit => LoadoutBuilder::new() + .active_item(active_item) + .shoulder(Some(Item::new_from_asset_expect( "common.items.armor.shoulder.assassin", - )), - chest: Some(Item::new_from_asset_expect( + ))) + .chest(Some(Item::new_from_asset_expect( "common.items.armor.chest.assassin", - )), - belt: Some(Item::new_from_asset_expect( + ))) + .belt(Some(Item::new_from_asset_expect( "common.items.armor.belt.assassin", - )), - hand: Some(Item::new_from_asset_expect( + ))) + .hands(Some(Item::new_from_asset_expect( "common.items.armor.hand.assassin", - )), - pants: Some(Item::new_from_asset_expect( + ))) + .pants(Some(Item::new_from_asset_expect( "common.items.armor.pants.assassin", - )), - foot: Some(Item::new_from_asset_expect( + ))) + .feet(Some(Item::new_from_asset_expect( "common.items.armor.foot.assassin", - )), - back: None, - ring: None, - neck: None, - lantern: match rand::thread_rng().gen_range(0, 3) { + ))) + .lantern(match rand::thread_rng().gen_range(0, 3) { 0 => Some(Item::new_from_asset_expect("common.items.lantern.black_0")), _ => None, - }, - glider: None, - head: None, - tabard: None, - }, - CultistNovice => Loadout { - active_item, - second_item: None, - shoulder: Some(Item::new_from_asset_expect( + }) + .build(), + CultistNovice => LoadoutBuilder::new() + .active_item(active_item) + .shoulder(Some(Item::new_from_asset_expect( "common.items.armor.shoulder.steel_0", - )), - chest: Some(Item::new_from_asset_expect( + ))) + .chest(Some(Item::new_from_asset_expect( "common.items.armor.chest.steel_0", - )), - belt: Some(Item::new_from_asset_expect( + ))) + .belt(Some(Item::new_from_asset_expect( "common.items.armor.belt.steel_0", - )), - hand: Some(Item::new_from_asset_expect( + ))) + .hands(Some(Item::new_from_asset_expect( "common.items.armor.hand.steel_0", - )), - pants: Some(Item::new_from_asset_expect( + ))) + .pants(Some(Item::new_from_asset_expect( "common.items.armor.pants.steel_0", - )), - foot: Some(Item::new_from_asset_expect( + ))) + .feet(Some(Item::new_from_asset_expect( "common.items.armor.foot.steel_0", - )), - back: Some(Item::new_from_asset_expect( + ))) + .back(Some(Item::new_from_asset_expect( "common.items.armor.back.dungeon_purple-0", - )), - ring: None, - neck: None, - lantern: match rand::thread_rng().gen_range(0, 3) { + ))) + .lantern(match rand::thread_rng().gen_range(0, 3) { 0 => Some(Item::new_from_asset_expect("common.items.lantern.black_0")), _ => None, - }, - glider: None, - head: None, - tabard: None, - }, - CultistAcolyte => Loadout { - active_item, - second_item: None, - shoulder: Some(Item::new_from_asset_expect( + }) + .build(), + CultistAcolyte => LoadoutBuilder::new() + .active_item(active_item) + .shoulder(Some(Item::new_from_asset_expect( "common.items.armor.shoulder.cultist_shoulder_purple", - )), - chest: Some(Item::new_from_asset_expect( + ))) + .chest(Some(Item::new_from_asset_expect( "common.items.armor.chest.cultist_chest_purple", - )), - belt: Some(Item::new_from_asset_expect( + ))) + .belt(Some(Item::new_from_asset_expect( "common.items.armor.belt.cultist_belt", - )), - hand: Some(Item::new_from_asset_expect( + ))) + .hands(Some(Item::new_from_asset_expect( "common.items.armor.hand.cultist_hands_purple", - )), - pants: Some(Item::new_from_asset_expect( + ))) + .pants(Some(Item::new_from_asset_expect( "common.items.armor.pants.cultist_legs_purple", - )), - foot: Some(Item::new_from_asset_expect( + ))) + .feet(Some(Item::new_from_asset_expect( "common.items.armor.foot.cultist_boots", - )), - back: Some(Item::new_from_asset_expect( + ))) + .back(Some(Item::new_from_asset_expect( "common.items.armor.back.dungeon_purple-0", - )), - ring: None, - neck: None, - lantern: match rand::thread_rng().gen_range(0, 3) { + ))) + .lantern(match rand::thread_rng().gen_range(0, 3) { 0 => Some(Item::new_from_asset_expect("common.items.lantern.black_0")), _ => None, - }, - glider: None, - head: None, - tabard: None, - }, - Warlord => Loadout { - active_item, - second_item: None, - shoulder: Some(Item::new_from_asset_expect( + }) + .build(), + Warlord => LoadoutBuilder::new() + .active_item(active_item) + .shoulder(Some(Item::new_from_asset_expect( "common.items.armor.shoulder.warlord", - )), - chest: Some(Item::new_from_asset_expect( + ))) + .chest(Some(Item::new_from_asset_expect( "common.items.armor.chest.warlord", - )), - belt: Some(Item::new_from_asset_expect( + ))) + .belt(Some(Item::new_from_asset_expect( "common.items.armor.belt.warlord", - )), - hand: Some(Item::new_from_asset_expect( + ))) + .hands(Some(Item::new_from_asset_expect( "common.items.armor.hand.warlord", - )), - pants: Some(Item::new_from_asset_expect( + ))) + .pants(Some(Item::new_from_asset_expect( "common.items.armor.pants.warlord", - )), - foot: Some(Item::new_from_asset_expect( + ))) + .feet(Some(Item::new_from_asset_expect( "common.items.armor.foot.warlord", - )), - back: Some(Item::new_from_asset_expect( + ))) + .back(Some(Item::new_from_asset_expect( "common.items.armor.back.warlord", - )), - ring: None, - neck: None, - lantern: match rand::thread_rng().gen_range(0, 3) { + ))) + .lantern(match rand::thread_rng().gen_range(0, 3) { 0 => Some(Item::new_from_asset_expect("common.items.lantern.black_0")), _ => None, - }, - glider: None, - head: None, - tabard: None, - }, - Warlock => Loadout { - active_item, - second_item: None, - shoulder: Some(Item::new_from_asset_expect( + }) + .build(), + Warlock => LoadoutBuilder::new() + .active_item(active_item) + .shoulder(Some(Item::new_from_asset_expect( "common.items.armor.shoulder.warlock", - )), - chest: Some(Item::new_from_asset_expect( + ))) + .chest(Some(Item::new_from_asset_expect( "common.items.armor.chest.warlock", - )), - belt: Some(Item::new_from_asset_expect( + ))) + .belt(Some(Item::new_from_asset_expect( "common.items.armor.belt.warlock", - )), - hand: Some(Item::new_from_asset_expect( + ))) + .hands(Some(Item::new_from_asset_expect( "common.items.armor.hand.warlock", - )), - pants: Some(Item::new_from_asset_expect( + ))) + .pants(Some(Item::new_from_asset_expect( "common.items.armor.pants.warlock", - )), - foot: Some(Item::new_from_asset_expect( + ))) + .feet(Some(Item::new_from_asset_expect( "common.items.armor.foot.warlock", - )), - back: Some(Item::new_from_asset_expect( + ))) + .back(Some(Item::new_from_asset_expect( "common.items.armor.back.warlock", - )), - ring: None, - neck: None, - lantern: match rand::thread_rng().gen_range(0, 3) { + ))) + .lantern(match rand::thread_rng().gen_range(0, 3) { 0 => Some(Item::new_from_asset_expect("common.items.lantern.black_0")), _ => None, - }, - glider: None, - head: None, - tabard: None, - }, - Villager => Loadout { - active_item, - second_item: None, - shoulder: None, - chest: Some(Item::new_from_asset_expect( + }) + .build(), + Villager => LoadoutBuilder::new() + .active_item(active_item) + .chest(Some(Item::new_from_asset_expect( match rand::thread_rng().gen_range(0, 10) { 0 => "common.items.armor.chest.worker_green_0", 1 => "common.items.armor.chest.worker_green_1", @@ -541,167 +469,105 @@ impl LoadoutBuilder { 8 => "common.items.armor.chest.worker_orange_0", _ => "common.items.armor.chest.worker_orange_1", }, - )), - belt: Some(Item::new_from_asset_expect( + ))) + .belt(Some(Item::new_from_asset_expect( "common.items.armor.belt.leather_0", - )), - hand: None, - pants: Some(Item::new_from_asset_expect( + ))) + .pants(Some(Item::new_from_asset_expect( "common.items.armor.pants.worker_blue_0", - )), - foot: Some(Item::new_from_asset_expect( + ))) + .feet(Some(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", }, - )), - back: None, - ring: None, - neck: None, - lantern: None, - glider: None, - head: None, - tabard: None, - }, + ))) + .build(), } } else { - Loadout { - active_item, - second_item: None, - shoulder: None, - chest: None, - belt: None, - hand: None, - pants: None, - foot: None, - back: None, - ring: None, - neck: None, - lantern: None, - glider: None, - head: None, - tabard: None, - } + LoadoutBuilder::new().active_item(active_item).build() }; Self(loadout) } - /// Default animal configuration - pub fn animal(body: Body) -> ItemConfig { - ItemConfig { - item: Item::new_from_asset_expect("common.items.weapons.empty.empty"), - ability1: Some(CharacterAbility::BasicMelee { - energy_cost: 10, - buildup_duration: 500, - swing_duration: 100, - recover_duration: 100, - base_damage: body.base_dmg(), - knockback: 0.0, - range: body.base_range(), - max_angle: 20.0, - }), - ability2: None, - ability3: None, - block_ability: None, - dodge_ability: None, - } - } - - /// Get the default [ItemConfig](../comp/struct.ItemConfig.html) for a tool - /// (weapon). This information is required for the `active` and `second` - /// weapon items in a loadout. If some customisation to the item's - /// 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(item: Item, map: &AbilityMap) -> ItemConfig { - ItemConfig::from((item, map)) - } - - /// Get an item's (weapon's) default - /// [ItemConfig](../comp/struct.ItemConfig.html) - /// by string reference. This will first attempt to load the Item, then - /// 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: &str, map: &AbilityMap) -> ItemConfig { - Self::default_item_config_from_item(Item::new_from_asset_expect(item_ref), map) - } - - pub fn active_item(mut self, item: Option) -> Self { - self.0.active_item = item; - + pub fn active_item(mut self, item: Option) -> Self { + self.0.swap(EquipSlot::Mainhand, item); self } - pub fn second_item(mut self, item: Option) -> Self { - self.0.second_item = item; - + pub fn second_item(mut self, item: Option) -> Self { + self.0.swap(EquipSlot::Offhand, item); self } pub fn shoulder(mut self, item: Option) -> Self { - self.0.shoulder = item; + self.0.swap(EquipSlot::Armor(ArmorSlot::Shoulders), item); self } pub fn chest(mut self, item: Option) -> Self { - self.0.chest = item; + self.0.swap(EquipSlot::Armor(ArmorSlot::Chest), item); self } pub fn belt(mut self, item: Option) -> Self { - self.0.belt = item; + self.0.swap(EquipSlot::Armor(ArmorSlot::Belt), item); self } - pub fn hand(mut self, item: Option) -> Self { - self.0.hand = item; + pub fn hands(mut self, item: Option) -> Self { + self.0.swap(EquipSlot::Armor(ArmorSlot::Hands), item); self } pub fn pants(mut self, item: Option) -> Self { - self.0.pants = item; + self.0.swap(EquipSlot::Armor(ArmorSlot::Legs), item); self } - pub fn foot(mut self, item: Option) -> Self { - self.0.foot = item; + pub fn feet(mut self, item: Option) -> Self { + self.0.swap(EquipSlot::Armor(ArmorSlot::Feet), item); self } pub fn back(mut self, item: Option) -> Self { - self.0.back = item; + self.0.swap(EquipSlot::Armor(ArmorSlot::Back), item); self } - pub fn ring(mut self, item: Option) -> Self { - self.0.ring = item; + pub fn ring1(mut self, item: Option) -> Self { + self.0.swap(EquipSlot::Armor(ArmorSlot::Ring1), item); + self + } + + pub fn ring2(mut self, item: Option) -> Self { + self.0.swap(EquipSlot::Armor(ArmorSlot::Ring2), item); self } pub fn neck(mut self, item: Option) -> Self { - self.0.neck = item; + self.0.swap(EquipSlot::Armor(ArmorSlot::Neck), item); self } pub fn lantern(mut self, item: Option) -> Self { - self.0.lantern = item; + self.0.swap(EquipSlot::Lantern, item); self } pub fn glider(mut self, item: Option) -> Self { - self.0.glider = item; + self.0.swap(EquipSlot::Glider, item); self } pub fn head(mut self, item: Option) -> Self { - self.0.head = item; + self.0.swap(EquipSlot::Armor(ArmorSlot::Head), item); self } pub fn tabard(mut self, item: Option) -> Self { - self.0.tabard = item; + self.0.swap(EquipSlot::Armor(ArmorSlot::Tabard), item); self } diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 5425667dd0..ff3b285c68 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -1,17 +1,41 @@ -pub mod item; -pub mod slot; - -use crate::{comp::inventory::item::ItemDef, recipe::Recipe}; use core::ops::Not; -use item::Item; +use std::{collections::HashMap, convert::TryFrom, iter::once, mem, ops::Range}; + use serde::{Deserialize, Serialize}; -use specs::{Component, HashMapStorage}; +use specs::{Component, DerefFlaggedStorage}; use specs_idvs::IdvStorage; +use tracing::{debug, trace, warn}; + +use crate::{ + comp::{ + inventory::{ + item::ItemDef, + loadout::Loadout, + slot::{EquipSlot, Slot, SlotError}, + }, + slot::{InvSlotId, SlotId}, + Item, + }, + recipe::Recipe, + LoadoutBuilder, +}; + +pub mod item; +pub mod loadout; +pub mod loadout_builder; +pub mod slot; +#[cfg(test)] mod test; +#[cfg(test)] mod test_helpers; + +pub type InvSlot = Option; +const DEFAULT_INVENTORY_SLOTS: usize = 18; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Inventory { - slots: Vec>, - amount: u32, + loadout: Loadout, + /// The "built-in" slots belonging to the inventory itself, all other slots + /// are provided by equipped items + slots: Vec, } /// Errors which the methods on `Inventory` produce @@ -22,24 +46,55 @@ pub enum Error { Full(Vec), } -#[allow(clippy::len_without_is_empty)] // TODO: Pending review in #587 +/// Represents the Inventory of an entity. The inventory has 18 "built-in" +/// slots, with further slots being provided by items equipped in the Loadout +/// sub-struct. Inventory slots are indexed by `InvSlotId` which is +/// comprised of `loadout_idx` - the index of the loadout item that provides the +/// slot, 0 being the built-in inventory slots, and `slot_idx` - the index of +/// the slot within that loadout item. +/// +/// Currently, it is not supported for inventories to contain items that have +/// items inside them. This is due to both game balance purposes, and the lack +/// of a UI to show such items. Because of this, any action that would result in +/// such an item being put into the inventory (item pickup, unequipping an item +/// that contains items etc) must first ensure items are unloaded from the item. +/// This is handled in `inventory\slot.rs` impl Inventory { - pub fn new_empty() -> Inventory { + pub fn new_empty() -> Inventory { Self::new_with_loadout(LoadoutBuilder::new().build()) } + + pub fn new_with_loadout(loadout: Loadout) -> Inventory { Inventory { - slots: vec![None; 36], - amount: 0, + loadout, + slots: vec![None; DEFAULT_INVENTORY_SLOTS], } } - pub fn slots(&self) -> &[Option] { &self.slots } + /// Total number of slots in in the inventory. + pub fn capacity(&self) -> usize { self.slots().count() } - pub fn len(&self) -> usize { self.slots.len() } + /// An iterator of all inventory slots + pub fn slots(&self) -> impl Iterator { + self.slots + .iter() + .chain(self.loadout.inv_slots_with_id().map(|(_, slot)| slot)) + } - /// Total number of occupied slots in the inventory. - pub fn amount(&self) -> u32 { self.amount } + /// A mutable iterator of all inventory slots + fn slots_mut(&mut self) -> impl Iterator { + self.slots.iter_mut().chain(self.loadout.inv_slots_mut()) + } - pub fn recount_items(&mut self) { - self.amount = self.slots.iter().filter(|i| i.is_some()).count() as u32; + /// An iterator of all inventory slots and their position + pub fn slots_with_id(&self) -> impl Iterator { + self.slots + .iter() + .enumerate() + .map(|(i, slot)| ((InvSlotId::new(0, u16::try_from(i).unwrap())), slot)) + .chain( + self.loadout + .inv_slots_with_id() + .map(|(loadout_slot_id, inv_slot)| (loadout_slot_id.into(), inv_slot)), + ) } /// Adds a new item to the first fitting group of the inventory or starts a @@ -47,8 +102,7 @@ impl Inventory { pub fn push(&mut self, item: Item) -> Option { if item.is_stackable() { if let Some(slot_item) = self - .slots - .iter_mut() + .slots_mut() .filter_map(Option::as_mut) .find(|s| *s == &item) { @@ -61,21 +115,7 @@ impl Inventory { // 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 - /// item again if no free slot was found. - fn add_to_first_empty(&mut self, item: Item) -> Option { - let item = match self.slots.iter_mut().find(|slot| slot.is_none()) { - Some(slot) => { - *slot = Some(item); - None - }, - None => Some(item), - }; - self.recount_items(); - item + self.insert(item) } /// Add a series of items to inventory, returning any which do not fit as an @@ -120,24 +160,22 @@ impl Inventory { /// Replaces an item in a specific slot of the inventory. Returns the old /// item or the same item again if that slot was not found. - pub fn insert(&mut self, cell: usize, item: Item) -> Result, Item> { - match self.slots.get_mut(cell) { - Some(slot) => { - let old = core::mem::replace(slot, Some(item)); - if old.is_none() { - self.recount_items(); - } - Ok(old) - }, + pub fn insert_at(&mut self, inv_slot_id: InvSlotId, item: Item) -> Result, Item> { + match self.slot_mut(inv_slot_id) { + Some(slot) => Ok(core::mem::replace(slot, Some(item))), None => Err(item), } } /// 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> { + pub fn insert_or_stack_at( + &mut self, + inv_slot_id: InvSlotId, + item: Item, + ) -> Result, Item> { if item.is_stackable() { - match self.slots.get_mut(cell) { + match self.slot_mut(inv_slot_id) { Some(Some(slot_item)) => { Ok(if slot_item == &item { slot_item @@ -150,46 +188,77 @@ impl Inventory { Some(old_item) }) }, - Some(None) => self.insert(cell, item), + Some(None) => self.insert_at(inv_slot_id, item), None => Err(item), } } else { - self.insert(cell, item) + self.insert_at(inv_slot_id, item) } } - pub fn is_full(&self) -> bool { self.slots.iter().all(|slot| slot.is_some()) } + /// Attempts to equip the item into a compatible, unpopulated loadout slot. + /// If no slot is available the item is returned. + #[must_use = "Returned item will be lost if not used"] + pub fn try_equip(&mut self, item: Item) -> Result<(), Item> { self.loadout.try_equip(item) } - /// O(n) count the number of items in this inventory. - pub fn count(&self) -> usize { self.slots.iter().filter_map(|slot| slot.as_ref()).count() } + pub fn populated_slots(&self) -> usize { self.slots().filter_map(|slot| slot.as_ref()).count() } - /// O(n) check if an item is in this inventory. + fn free_slots(&self) -> usize { self.slots().filter(|slot| slot.is_none()).count() } + + /// Check if an item is in this inventory. pub fn contains(&self, item: &Item) -> bool { - self.slots.iter().any(|slot| slot.as_ref() == Some(item)) + self.slots().any(|slot| slot.as_ref() == Some(item)) } /// Get content of a slot - pub fn get(&self, cell: usize) -> Option<&Item> { - self.slots.get(cell).and_then(Option::as_ref) + pub fn get(&self, inv_slot_id: InvSlotId) -> Option<&Item> { + self.slot(inv_slot_id).and_then(Option::as_ref) + } + + /// Returns a reference to the item (if any) equipped in the given EquipSlot + pub fn equipped(&self, equip_slot: EquipSlot) -> Option<&Item> { + self.loadout.equipped(equip_slot) + } + + pub fn loadout_items_with_persistence_key( + &self, + ) -> impl Iterator)> { + self.loadout.items_with_persistence_key() + } + + /// Returns the range of inventory slot indexes that a particular equipped + /// item provides (used for UI highlighting of inventory slots when hovering + /// over a loadout item) + pub fn get_slot_range_for_equip_slot(&self, equip_slot: EquipSlot) -> Option> { + // The slot range returned from `Loadout` must be offset by the number of slots + // that the inventory itself provides. + let offset = self.slots.len(); + self.loadout + .slot_range_for_equip_slot(equip_slot) + .map(|loadout_range| (loadout_range.start + offset)..(loadout_range.end + offset)) } /// Swap the items inside of two slots - pub fn swap_slots(&mut self, a: usize, b: usize) { - if a.max(b) < self.slots.len() { - self.slots.swap(a, b); + pub fn swap_slots(&mut self, a: InvSlotId, b: InvSlotId) { + if self.slot(a).is_none() || self.slot(b).is_none() { + warn!("swap_slots called with non-existent inventory slot(s)"); + return; } + + let slot_a = mem::take(self.slot_mut(a).unwrap()); + let slot_b = mem::take(self.slot_mut(b).unwrap()); + *self.slot_mut(a).unwrap() = slot_b; + *self.slot_mut(b).unwrap() = slot_a; } /// Remove an item from the slot - pub fn remove(&mut self, cell: usize) -> Option { - let item = self.slots.get_mut(cell).and_then(|item| item.take()); - self.recount_items(); - item + pub fn remove(&mut self, inv_slot_id: InvSlotId) -> Option { + self.slot_mut(inv_slot_id).and_then(|item| item.take()) } /// 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) { + pub fn take(&mut self, inv_slot_id: InvSlotId) -> Option { + if let Some(Some(item)) = self.slot_mut(inv_slot_id) { let mut return_item = item.duplicate(); if item.is_stackable() && item.amount() > 1 { @@ -197,20 +266,25 @@ impl Inventory { 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) + self.remove(inv_slot_id) } } else { None } } + /// Takes all items from the inventory + pub fn drain(&mut self) -> impl Iterator + '_ { + self.slots_mut() + .filter(|x| x.is_some()) + .filter_map(mem::take) + } + /// Determine how many of a particular item there is in the inventory. pub fn item_count(&self, item_def: &ItemDef) -> u64 { self.slots() - .iter() .flatten() .filter(|it| it.is_same_item_def(item_def)) .map(|it| u64::from(it.amount())) @@ -225,17 +299,18 @@ impl Inventory { pub fn contains_ingredients<'a>( &self, recipe: &'a Recipe, - ) -> Result, Vec<(&'a ItemDef, u32)>> { - let mut slot_claims = vec![0; self.slots.len()]; + ) -> Result, Vec<(&'a ItemDef, u32)>> { + let mut slot_claims = HashMap::::new(); let mut missing = Vec::<(&ItemDef, u32)>::new(); for (input, mut needed) in recipe.inputs() { let mut contains_any = false; - for (i, slot) in self.slots().iter().enumerate() { + for (inv_slot_id, slot) in self.slots_with_id() { if let Some(item) = slot.as_ref().filter(|item| item.is_same_item_def(&*input)) { - let can_claim = (item.amount() - slot_claims[i]).min(needed); - slot_claims[i] += can_claim; + let claim = slot_claims.entry(inv_slot_id).or_insert(0); + let can_claim = (item.amount() - *claim).min(needed); + *claim += can_claim; needed -= can_claim; contains_any = true; } @@ -252,24 +327,312 @@ impl Inventory { Err(missing) } } -} -impl Default for Inventory { - fn default() -> Inventory { - let mut inventory = Inventory { - slots: vec![None; 36], - amount: 0, - }; - inventory.push(Item::new_from_asset_expect( - "common.items.consumable.potion_minor", - )); - inventory.push(Item::new_from_asset_expect("common.items.food.cheese")); - inventory + /// Adds a new item to the first empty slot of the inventory. Returns the + /// item again if no free slot was found. + fn insert(&mut self, item: Item) -> Option { + match self.slots_mut().find(|slot| slot.is_none()) { + Some(slot) => { + *slot = Some(item); + None + }, + None => Some(item), + } + } + + fn slot(&self, inv_slot_id: InvSlotId) -> Option<&InvSlot> { + match SlotId::from(inv_slot_id) { + SlotId::Inventory(slot_idx) => self.slots.get(slot_idx), + SlotId::Loadout(loadout_slot_id) => self.loadout.inv_slot(loadout_slot_id), + } + } + + fn slot_mut(&mut self, inv_slot_id: InvSlotId) -> Option<&mut InvSlot> { + match SlotId::from(inv_slot_id) { + SlotId::Inventory(slot_idx) => self.slots.get_mut(slot_idx), + SlotId::Loadout(loadout_slot_id) => self.loadout.inv_slot_mut(loadout_slot_id), + } + } + + /// Returns the number of free slots in the inventory ignoring any slots + /// granted by the item (if any) equipped in the provided EquipSlot. + pub fn free_slots_minus_equipped_item(&self, equip_slot: EquipSlot) -> usize { + if let Some(mut equip_slot_idx) = self.loadout.loadout_idx_for_equip_slot(equip_slot) { + // Offset due to index 0 representing built-in inventory slots + equip_slot_idx += 1; + + self.slots_with_id() + .filter(|(inv_slot_id, slot)| { + inv_slot_id.loadout_idx() != equip_slot_idx && slot.is_none() + }) + .count() + } else { + // TODO: return Option and evaluate to None here + warn!( + "Attempted to fetch loadout index for non-existent EquipSlot: {:?}", + equip_slot + ); + 0 + } + } + + pub fn equipped_items(&self) -> impl Iterator { self.loadout.items() } + + /// Replaces the loadout item (if any) in the given EquipSlot with the + /// provided item, returning the item that was previously in the slot. + pub fn replace_loadout_item( + &mut self, + equip_slot: EquipSlot, + replacement_item: Option, + ) -> Option { + self.loadout.swap(equip_slot, replacement_item) + } + + /// Equip an item from a slot in inventory. The currently equipped item will + /// go into inventory. If the item is going to mainhand, put mainhand in + /// offhand and place offhand into inventory. + #[must_use = "Returned items will be lost if not used"] + pub fn equip(&mut self, inv_slot: InvSlotId) -> Option> { + let mut leftover_items = None; + self.get(inv_slot) + .map(|x| x.kind().clone()) + .map(|item_kind| { + self.loadout + .get_slot_to_equip_into(&item_kind) + .map(|equip_slot| { + // Special case when equipping into main hand - swap with offhand first + if equip_slot == EquipSlot::Mainhand { + self.loadout + .swap_slots(EquipSlot::Mainhand, EquipSlot::Offhand); + } + + leftover_items = self.swap_inventory_loadout(inv_slot, equip_slot); + }) + }); + + leftover_items + } + + /// Determines how many free inventory slots will be left after equipping an + /// item (because it could be swapped with an already equipped item that + /// provides more inventory slots than the item being equipped) + pub fn free_after_equip(&self, inv_slot: InvSlotId) -> i32 { + let (inv_slot_for_equipped, slots_from_equipped) = self + .get(inv_slot) + .map(|x| x.kind().clone()) + .and_then(|item_kind| self.loadout.get_slot_to_equip_into(&item_kind)) + .and_then(|equip_slot| self.equipped(equip_slot)) + .map_or((1, 0), |item| (0, item.slots().len())); + + let slots_from_inv = self + .get(inv_slot) + .map(|item| item.slots().len()) + .unwrap_or(0); + + i32::try_from(self.capacity()).expect("Inventory with more than i32::MAX slots") + - i32::try_from(slots_from_equipped) + .expect("Equipped item with more than i32::MAX slots") + + i32::try_from(slots_from_inv).expect("Inventory item with more than i32::MAX slots") + - i32::try_from(self.populated_slots()) + .expect("Inventory item with more than i32::MAX used slots") + + inv_slot_for_equipped // If there is no item already in the equip slot we gain 1 slot + } + + /// Handles picking up an item, unloading any items inside the item being + /// picked up and pushing them to the inventory to ensure that items + /// containing items aren't inserted into the inventory as this is not + /// currently supported. + pub fn pickup_item(&mut self, mut item: Item) -> Result<(), Item> { + if self.free_slots() < item.populated_slots() + 1 { + return Err(item); + } + + // Unload any items contained within the item, and push those items and the item + // itself into the inventory. We already know that there are enough free slots + // so push will never give us an item back. + item.drain() + .collect::>() + .into_iter() + .chain(once(item)) + .for_each(|item| { + self.push(item).unwrap_none(); + }); + + Ok(()) + } + + /// Unequip an item from slot and place into inventory. Will leave the item + /// equipped if inventory has no slots available. + #[must_use = "Returned items will be lost if not used"] + pub fn unequip(&mut self, equip_slot: EquipSlot) -> Result>, SlotError> { + // Ensure there is enough space in the inventory to place the unequipped item + if self.free_slots_minus_equipped_item(equip_slot) == 0 { + return Err(SlotError::InventoryFull); + } + + Ok(self + .loadout + .swap(equip_slot, None) + .and_then(|mut unequipped_item| { + let unloaded_items: Vec = unequipped_item.drain().collect(); + self.push(unequipped_item) + .expect_none("Failed to push item to inventory, precondition failed?"); + + // Unload any items that were inside the equipped item into the inventory, with + // any that don't fit to be to be dropped on the floor by the caller + match self.push_all(unloaded_items.into_iter()) { + Err(Error::Full(leftovers)) => Some(leftovers), + Ok(_) => None, + } + })) + } + + /// Determines how many free inventory slots will be left after unequipping + /// an item + pub fn free_after_unequip(&self, equip_slot: EquipSlot) -> i32 { + let (inv_slot_for_unequipped, slots_from_equipped) = self + .equipped(equip_slot) + .map_or((0, 0), |item| (1, item.slots().len())); + + i32::try_from(self.capacity()).expect("Inventory with more than i32::MAX slots") + - i32::try_from(slots_from_equipped) + .expect("Equipped item with more than i32::MAX slots") + - i32::try_from(self.populated_slots()) + .expect("Inventory item with more than i32::MAX used slots") + - inv_slot_for_unequipped // If there is an item being unequipped we lose 1 slot + } + + /// Swaps items from two slots, regardless of if either is inventory or + /// loadout. + #[must_use = "Returned items will be lost if not used"] + pub fn swap(&mut self, slot_a: Slot, slot_b: Slot) -> Option> { + match (slot_a, slot_b) { + (Slot::Inventory(slot_a), Slot::Inventory(slot_b)) => { + self.swap_slots(slot_a, slot_b); + None + }, + (Slot::Inventory(inv_slot), Slot::Equip(equip_slot)) + | (Slot::Equip(equip_slot), Slot::Inventory(inv_slot)) => { + self.swap_inventory_loadout(inv_slot, equip_slot) + }, + (Slot::Equip(slot_a), Slot::Equip(slot_b)) => { + self.loadout.swap_slots(slot_a, slot_b); + None + }, + } + } + + /// Determines how many free inventory slots will be left after swapping two + /// item slots + pub fn free_after_swap(&self, equip_slot: EquipSlot, inv_slot: InvSlotId) -> i32 { + let (inv_slot_for_equipped, slots_from_equipped) = self + .equipped(equip_slot) + .map_or((0, 0), |item| (1, item.slots().len())); + let (inv_slot_for_inv_item, slots_from_inv_item) = self + .get(inv_slot) + .map_or((0, 0), |item| (1, item.slots().len())); + + // Return the number of inventory slots that will be free once this slot swap is + // performed + i32::try_from(self.capacity()) + .expect("inventory with more than i32::MAX slots") + - i32::try_from(slots_from_equipped) + .expect("equipped item with more than i32::MAX slots") + + i32::try_from(slots_from_inv_item) + .expect("inventory item with more than i32::MAX slots") + - i32::try_from(self.populated_slots()) + .expect("inventory with more than i32::MAX used slots") + - inv_slot_for_equipped // +1 inventory slot required if an item was unequipped + + inv_slot_for_inv_item // -1 inventory slot required if an item was equipped + } + + /// Swap item in an inventory slot with one in a loadout slot. + #[must_use = "Returned items will be lost if not used"] + pub fn swap_inventory_loadout( + &mut self, + inv_slot_id: InvSlotId, + equip_slot: EquipSlot, + ) -> Option> { + if !self.can_swap(inv_slot_id, equip_slot) { + return None; + } + + let mut unloaded_items = None; + + // Take the item from the inventory + let from_inv = self.remove(inv_slot_id); + + // Swap the equipped item for the item from the inventory + let from_equip = self.loadout.swap(equip_slot, from_inv); + if let Some(mut from_equip) = from_equip { + // Unload any items held inside the previously equipped item + let items: Vec = from_equip.drain().collect(); + if items.iter().len() > 0 { + unloaded_items = Some(items); + } + + // Attempt to put the unequipped item in the same slot that the inventory item + // was in - if that slot no longer exists (because a large container was + // swapped for a smaller one) then push the item to the first free + // inventory slot instead. + if let Err(returned) = self.insert_at(inv_slot_id, from_equip) { + self.push(returned) + .expect_none("Unable to push to inventory, no slots (bug in can_swap()?)"); + } + } + + // Attempt to put any items unloaded from the unequipped item into empty + // inventory slots and return any that don't fit to the caller where they + // will be dropped on the ground + if let Some(unloaded_items) = unloaded_items { + let leftovers = match self.push_all(unloaded_items.into_iter()) { + Err(Error::Full(leftovers)) => leftovers, + Ok(_) => vec![], + }; + return Some(leftovers); + } + + None + } + + /// Determines if an inventory and loadout slot can be swapped, taking into + /// account whether there will be free space in the inventory for the + /// loadout item once any slots that were provided by it have been + /// removed. + pub fn can_swap(&self, inv_slot_id: InvSlotId, equip_slot: EquipSlot) -> bool { + // Check if loadout slot can hold item + if !self + .get(inv_slot_id) + .map_or(true, |item| equip_slot.can_hold(&item.kind())) + { + trace!("can_swap = false, equip slot can't hold item"); + return false; + } + + // If we're swapping an equipped item with an empty inventory slot, make + // sure that there will be enough space in the inventory after any + // slots granted by the item being unequipped have been removed. + if let Some(inv_slot) = self.slot(inv_slot_id) { + if inv_slot.is_none() && self.free_slots_minus_equipped_item(equip_slot) == 0 { + // No free inventory slots after slots provided by the equipped + //item are discounted + trace!("can_swap = false, no free slots minus item"); + return false; + } + } else { + debug!( + "can_swap = false, tried to swap into non-existent inventory slot: {:?}", + inv_slot_id + ); + return false; + } + + true } } impl Component for Inventory { - type Storage = HashMapStorage; + type Storage = DerefFlaggedStorage>; } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -306,5 +669,3 @@ impl InventoryUpdate { impl Component for InventoryUpdate { type Storage = IdvStorage; } - -#[cfg(test)] mod test; diff --git a/common/src/comp/inventory/slot.rs b/common/src/comp/inventory/slot.rs index 1d55dff848..4a5ad3b97a 100644 --- a/common/src/comp/inventory/slot.rs +++ b/common/src/comp/inventory/slot.rs @@ -1,21 +1,84 @@ -use crate::{ - comp, - comp::{ - item::{self, armor, tool::AbilityMap}, - ItemConfig, - }, -}; -use comp::{Inventory, Loadout}; use serde::{Deserialize, Serialize}; -use tracing::warn; +use std::{cmp::Ordering, convert::TryFrom}; -#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] -pub enum Slot { - Inventory(usize), - Equip(EquipSlot), +use crate::comp::{ + inventory::{ + item::{armor, armor::ArmorKind, ItemKind}, + loadout::LoadoutSlotId, + }, + item, +}; + +#[derive(Debug, PartialEq)] +pub enum SlotError { + InventoryFull, } #[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] +pub enum Slot { + Inventory(InvSlotId), + Equip(EquipSlot), +} + +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] +pub struct InvSlotId { + // The index of the loadout item that provides this inventory slot. 0 represents + // built-in inventory slots + loadout_idx: u16, + // The index of the slot within its container + slot_idx: u16, +} + +impl InvSlotId { + pub const fn new(loadout_idx: u16, slot_idx: u16) -> Self { + Self { + loadout_idx, + slot_idx, + } + } + + pub fn idx(&self) -> u32 { (u32::from(self.loadout_idx) << 16) | u32::from(self.slot_idx) } + + pub fn loadout_idx(&self) -> usize { usize::from(self.loadout_idx) } + + pub fn slot_idx(&self) -> usize { usize::from(self.slot_idx) } +} + +impl From for InvSlotId { + fn from(loadout_slot_id: LoadoutSlotId) -> Self { + Self { + loadout_idx: u16::try_from(loadout_slot_id.loadout_idx + 1).unwrap(), + slot_idx: u16::try_from(loadout_slot_id.slot_idx).unwrap(), + } + } +} + +impl PartialOrd for InvSlotId { + fn partial_cmp(&self, other: &InvSlotId) -> Option { Some(self.cmp(other)) } +} + +impl Ord for InvSlotId { + fn cmp(&self, other: &InvSlotId) -> Ordering { self.idx().cmp(&other.idx()) } +} + +pub(super) enum SlotId { + Inventory(usize), + Loadout(LoadoutSlotId), +} + +impl From for SlotId { + fn from(inv_slot_id: InvSlotId) -> Self { + match inv_slot_id.loadout_idx { + 0 => SlotId::Inventory(inv_slot_id.slot_idx()), + _ => SlotId::Loadout(LoadoutSlotId { + loadout_idx: inv_slot_id.loadout_idx() - 1, + slot_idx: inv_slot_id.slot_idx(), + }), + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] pub enum EquipSlot { Armor(ArmorSlot), Mainhand, @@ -24,25 +87,26 @@ pub enum EquipSlot { Glider, } -#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] pub enum ArmorSlot { Head, Neck, Shoulders, Chest, Hands, - Ring, + Ring1, + Ring2, Back, Belt, Legs, Feet, Tabard, + Bag1, + Bag2, + Bag3, + Bag4, } -//const ALL_ARMOR_SLOTS: [ArmorSlot; 11] = [ -// Head, Neck, Shoulders, Chest, Hands, Ring, Back, Belt, Legs, Feet, Tabard, -//]; - impl Slot { pub fn can_hold(self, item_kind: &item::ItemKind) -> bool { match (self, item_kind) { @@ -53,11 +117,9 @@ impl Slot { } impl EquipSlot { - fn can_hold(self, item_kind: &item::ItemKind) -> bool { - use armor::Armor; - use item::ItemKind; + pub fn can_hold(self, item_kind: &item::ItemKind) -> bool { match (self, item_kind) { - (Self::Armor(slot), ItemKind::Armor(Armor { kind, .. })) => slot.can_hold(kind), + (Self::Armor(slot), ItemKind::Armor(armor::Armor { kind, .. })) => slot.can_hold(kind), (Self::Mainhand, ItemKind::Tool(_)) => true, (Self::Offhand, ItemKind::Tool(_)) => true, (Self::Lantern, ItemKind::Lantern(_)) => true, @@ -69,7 +131,6 @@ impl EquipSlot { impl ArmorSlot { fn can_hold(self, armor: &item::armor::ArmorKind) -> bool { - use item::armor::ArmorKind; matches!( (self, armor), (Self::Head, ArmorKind::Head(_)) @@ -77,414 +138,17 @@ impl ArmorSlot { | (Self::Shoulders, ArmorKind::Shoulder(_)) | (Self::Chest, ArmorKind::Chest(_)) | (Self::Hands, ArmorKind::Hand(_)) - | (Self::Ring, ArmorKind::Ring(_)) + | (Self::Ring1, ArmorKind::Ring(_)) + | (Self::Ring2, ArmorKind::Ring(_)) | (Self::Back, ArmorKind::Back(_)) | (Self::Belt, ArmorKind::Belt(_)) | (Self::Legs, ArmorKind::Pants(_)) | (Self::Feet, ArmorKind::Foot(_)) | (Self::Tabard, ArmorKind::Tabard(_)) + | (Self::Bag1, ArmorKind::Bag(_)) + | (Self::Bag2, ArmorKind::Bag(_)) + | (Self::Bag3, ArmorKind::Bag(_)) + | (Self::Bag4, ArmorKind::Bag(_)) ) } } - -/// 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( - equip_slot: EquipSlot, - item: Option, - loadout: &mut Loadout, - map: &AbilityMap, -) -> Option { - use std::mem::replace; - match equip_slot { - EquipSlot::Armor(ArmorSlot::Head) => replace(&mut loadout.head, item), - EquipSlot::Armor(ArmorSlot::Neck) => replace(&mut loadout.neck, item), - EquipSlot::Armor(ArmorSlot::Shoulders) => replace(&mut loadout.shoulder, item), - EquipSlot::Armor(ArmorSlot::Chest) => replace(&mut loadout.chest, item), - EquipSlot::Armor(ArmorSlot::Hands) => replace(&mut loadout.hand, item), - EquipSlot::Armor(ArmorSlot::Ring) => replace(&mut loadout.ring, item), - EquipSlot::Armor(ArmorSlot::Back) => replace(&mut loadout.back, item), - EquipSlot::Armor(ArmorSlot::Belt) => replace(&mut loadout.belt, item), - EquipSlot::Armor(ArmorSlot::Legs) => replace(&mut loadout.pants, item), - EquipSlot::Armor(ArmorSlot::Feet) => replace(&mut loadout.foot, item), - EquipSlot::Armor(ArmorSlot::Tabard) => replace(&mut loadout.tabard, item), - EquipSlot::Lantern => replace(&mut loadout.lantern, item), - EquipSlot::Glider => replace(&mut loadout.glider, item), - EquipSlot::Mainhand => replace( - &mut loadout.active_item, - item.map(|item| ItemConfig::from((item, map))), - ) - .map(|i| i.item), - EquipSlot::Offhand => replace( - &mut loadout.second_item, - item.map(|item| ItemConfig::from((item, map))), - ) - .map(|i| i.item), - } -} - -/// Insert an item into a loadout. If the specified slot is already occupied -/// the old item is returned. -#[must_use] -fn loadout_insert( - equip_slot: EquipSlot, - item: item::Item, - loadout: &mut Loadout, - map: &AbilityMap, -) -> Option { - loadout_replace(equip_slot, Some(item), loadout, map) -} - -/// Remove an item from a loadout. -/// -/// ``` -/// use veloren_common::{ -/// assets::AssetExt, -/// comp::{ -/// item::tool::AbilityMap, -/// slot::{loadout_remove, EquipSlot}, -/// Inventory, -/// }, -/// LoadoutBuilder, -/// }; -/// -/// let mut inv = Inventory::new_empty(); -/// -/// let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest"); -/// -/// let mut loadout = LoadoutBuilder::new() -/// .defaults() -/// .active_item(Some(LoadoutBuilder::default_item_config_from_str( -/// "common.items.weapons.sword.zweihander_sword_0", -/// &map, -/// ))) -/// .build(); -/// -/// let slot = EquipSlot::Mainhand; -/// -/// loadout_remove(slot, &mut loadout, &map); -/// assert_eq!(None, loadout.active_item); -/// ``` -pub fn loadout_remove( - equip_slot: EquipSlot, - loadout: &mut Loadout, - map: &AbilityMap, -) -> Option { - loadout_replace(equip_slot, None, loadout, map) -} - -/// Swap item in an inventory slot with one in a loadout slot. -fn swap_inventory_loadout( - inventory_slot: usize, - equip_slot: EquipSlot, - inventory: &mut Inventory, - loadout: &mut Loadout, - map: &AbilityMap, -) { - // Check if loadout slot can hold item - if inventory - .get(inventory_slot) - .map_or(true, |item| equip_slot.can_hold(&item.kind())) - { - // Take item from loadout - let from_equip = loadout_remove(equip_slot, loadout, map); - // Swap with item in the inventory - let from_inv = if let Some(item) = from_equip { - // If this fails and we get item back as an err it will just be put back in the - // loadout - inventory.insert(inventory_slot, item).unwrap_or_else(Some) - } else { - inventory.remove(inventory_slot) - }; - // Put item from the inventory in loadout - if let Some(item) = from_inv { - loadout_insert(equip_slot, item, loadout, map).unwrap_none(); // Can never fail - } - } -} - -/// Swap items in loadout. Does nothing if items are not compatible with their -/// new slots. -fn swap_loadout(slot_a: EquipSlot, slot_b: EquipSlot, loadout: &mut Loadout, map: &AbilityMap) { - // Ensure that the slots are not the same - if slot_a == slot_b { - warn!("Tried to swap equip slot with itself"); - return; - } - - // Get items from the slots - let item_a = loadout_remove(slot_a, loadout, map); - let item_b = loadout_remove(slot_b, loadout, map); - // 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())) - { - // Swap - loadout_replace(slot_b, item_a, loadout, map).unwrap_none(); - loadout_replace(slot_a, item_b, loadout, map).unwrap_none(); - } else { - // Otherwise put the items back - loadout_replace(slot_a, item_a, loadout, map).unwrap_none(); - loadout_replace(slot_b, item_b, loadout, map).unwrap_none(); - } -} - -// TODO: Should this report if a change actually occurred? (might be useful when -// minimizing network use) - -/// Swap items from two slots, regardless of if either is inventory or loadout. -pub fn swap( - slot_a: Slot, - slot_b: Slot, - inventory: Option<&mut Inventory>, - loadout: Option<&mut Loadout>, - map: &AbilityMap, -) { - match (slot_a, slot_b) { - (Slot::Inventory(slot_a), Slot::Inventory(slot_b)) => { - inventory.map(|i| i.swap_slots(slot_a, slot_b)); - }, - (Slot::Inventory(inv_slot), Slot::Equip(equip_slot)) - | (Slot::Equip(equip_slot), Slot::Inventory(inv_slot)) => { - if let Some((inventory, loadout)) = loadout.and_then(|l| inventory.map(|i| (i, l))) { - swap_inventory_loadout(inv_slot, equip_slot, inventory, loadout, map); - } - }, - - (Slot::Equip(slot_a), Slot::Equip(slot_b)) => { - loadout.map(|l| swap_loadout(slot_a, slot_b, l, map)); - }, - } -} - -/// Equip an item from a slot in inventory. The currently equipped item will go -/// into inventory. If the item is going to mainhand, put mainhand in -/// offhand and place offhand into inventory. -/// -/// ``` -/// use veloren_common::{ -/// assets::AssetExt, -/// comp::{ -/// item::tool::AbilityMap, -/// slot::{equip, EquipSlot}, -/// Inventory, Item, -/// }, -/// LoadoutBuilder, -/// }; -/// -/// let boots = Item::new_from_asset_expect("common.items.testing.test_boots"); -/// -/// let mut inv = Inventory::new_empty(); -/// inv.push(boots.duplicate()); -/// -/// let mut loadout = LoadoutBuilder::new().defaults().build(); -/// -/// let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest"); -/// -/// equip(0, &mut inv, &mut loadout, &map); -/// assert_eq!(Some(boots), loadout.foot); -/// ``` -pub fn equip(slot: usize, inventory: &mut Inventory, loadout: &mut Loadout, map: &AbilityMap) { - use armor::Armor; - use item::{armor::ArmorKind, ItemKind}; - - 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, - ArmorKind::Neck(_) => ArmorSlot::Neck, - ArmorKind::Shoulder(_) => ArmorSlot::Shoulders, - ArmorKind::Chest(_) => ArmorSlot::Chest, - ArmorKind::Hand(_) => ArmorSlot::Hands, - ArmorKind::Ring(_) => ArmorSlot::Ring, - ArmorKind::Back(_) => ArmorSlot::Back, - ArmorKind::Belt(_) => ArmorSlot::Belt, - ArmorKind::Pants(_) => ArmorSlot::Legs, - ArmorKind::Foot(_) => ArmorSlot::Feet, - ArmorKind::Tabard(_) => ArmorSlot::Tabard, - })), - ItemKind::Lantern(_) => Some(EquipSlot::Lantern), - ItemKind::Glider(_) => Some(EquipSlot::Glider), - _ => None, - }); - - if let Some(equip_slot) = equip_slot { - // If item is going to mainhand, put mainhand in offhand and place offhand in - // inventory - if let EquipSlot::Mainhand = equip_slot { - swap_loadout(EquipSlot::Mainhand, EquipSlot::Offhand, loadout, map); - } - - swap_inventory_loadout(slot, equip_slot, inventory, loadout, map); - } -} - -/// Unequip an item from slot and place into inventory. Will leave the item -/// equipped if inventory has no slots available. -/// -/// ``` -/// use veloren_common::{ -/// assets::AssetExt, -/// comp::{ -/// item::tool::AbilityMap, -/// slot::{unequip, EquipSlot}, -/// Inventory, -/// }, -/// LoadoutBuilder, -/// }; -/// -/// let mut inv = Inventory::new_empty(); -/// -/// let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest"); -/// -/// let mut loadout = LoadoutBuilder::new() -/// .defaults() -/// .active_item(Some(LoadoutBuilder::default_item_config_from_str( -/// "common.items.weapons.sword.zweihander_sword_0", -/// &map, -/// ))) -/// .build(); -/// -/// let slot = EquipSlot::Mainhand; -/// -/// unequip(slot, &mut inv, &mut loadout, &map); -/// assert_eq!(None, loadout.active_item); -/// ``` -pub fn unequip( - slot: EquipSlot, - inventory: &mut Inventory, - loadout: &mut Loadout, - map: &AbilityMap, -) { - loadout_remove(slot, loadout, map) // Remove item from loadout - .and_then(|i| inventory.push(i)) // Insert into inventory - .and_then(|i| loadout_insert(slot, i, loadout, map)) // If that fails put back in loadout - .unwrap_none(); // Never fails -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{assets::AssetExt, comp::Item, LoadoutBuilder}; - - #[test] - fn test_unequip_items_both_hands() { - let mut inv = Inventory { - slots: vec![None], - amount: 0, - }; - - let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest"); - - let sword = LoadoutBuilder::default_item_config_from_str( - "common.items.weapons.sword.zweihander_sword_0", - &map, - ); - - let mut loadout = LoadoutBuilder::new() - .defaults() - .active_item(Some(sword.clone())) - .second_item(Some(sword.clone())) - .build(); - - assert_eq!(Some(sword.clone()), loadout.active_item); - unequip(EquipSlot::Mainhand, &mut inv, &mut loadout, &map); - // We have space in the inventory, so this should have unequipped - assert_eq!(None, loadout.active_item); - - unequip(EquipSlot::Offhand, &mut inv, &mut loadout, &map); - // There is no more space in the inventory, so this should still be equipped - assert_eq!(Some(sword.clone()), loadout.second_item); - - // Verify inventory - assert_eq!(inv.slots[0], Some(sword.item)); - assert_eq!(inv.slots.len(), 1); - } - - #[test] - fn test_equip_item() { - let boots: Option = Some(Item::new_from_asset_expect( - "common.items.testing.test_boots", - )); - - let starting_sandles: Option = Some(Item::new_from_asset_expect( - "common.items.armor.starter.sandals_0", - )); - - let mut inv = Inventory { - slots: vec![boots.clone()], - amount: 1, - }; - - let mut loadout = LoadoutBuilder::new().defaults().build(); - - let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest"); - - // We should start with the starting sandles - assert_eq!(starting_sandles, loadout.foot); - equip(0, &mut inv, &mut loadout, &map); - - // We should now have the testing boots equiped - assert_eq!(boots, loadout.foot); - - // Verify inventory - assert_eq!(inv.slots[0], starting_sandles); - assert_eq!(inv.slots.len(), 1); - } - - #[test] - fn test_loadout_replace() { - let boots: Option = Some(Item::new_from_asset_expect( - "common.items.testing.test_boots", - )); - - let starting_sandles: Option = Some(Item::new_from_asset_expect( - "common.items.armor.starter.sandals_0", - )); - - let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest"); - - let mut loadout = LoadoutBuilder::new().defaults().build(); - - // We should start with the starting sandles - assert_eq!(starting_sandles, loadout.foot); - - // The swap should return the sandles - assert_eq!( - starting_sandles, - loadout_replace( - EquipSlot::Armor(ArmorSlot::Feet), - boots.clone(), - &mut loadout, - &map, - ) - ); - - // We should now have the testing boots equiped - assert_eq!(boots, loadout.foot); - } - - #[test] - fn test_loadout_remove() { - let map = AbilityMap::load_expect_cloned("common.abilities.weapon_ability_manifest"); - - let sword = LoadoutBuilder::default_item_config_from_str( - "common.items.weapons.sword.zweihander_sword_0", - &map, - ); - - let mut loadout = LoadoutBuilder::new() - .defaults() - .active_item(Some(sword.clone())) - .build(); - - // The swap should return the sword - assert_eq!( - Some(sword.item), - loadout_remove(EquipSlot::Mainhand, &mut loadout, &map) - ); - - // We should now have nothing equiped - assert_eq!(None, loadout.active_item); - } -} diff --git a/common/src/comp/inventory/test.rs b/common/src/comp/inventory/test.rs index 0b6aae5464..26f9d9abce 100644 --- a/common/src/comp/inventory/test.rs +++ b/common/src/comp/inventory/test.rs @@ -1,4 +1,8 @@ use super::*; +use crate::comp::{ + inventory::{slot::ArmorSlot, test_helpers::get_test_bag}, + Item, +}; use lazy_static::lazy_static; lazy_static! { static ref TEST_ITEMS: Vec = vec![ @@ -12,7 +16,7 @@ lazy_static! { fn push_full() { let mut inv = Inventory { slots: TEST_ITEMS.iter().map(|a| Some(a.clone())).collect(), - amount: 0, + loadout: LoadoutBuilder::new().build(), }; assert_eq!( inv.push(TEST_ITEMS[0].clone()).unwrap(), @@ -25,7 +29,7 @@ fn push_full() { fn push_all_full() { let mut inv = Inventory { slots: TEST_ITEMS.iter().map(|a| Some(a.clone())).collect(), - amount: 0, + loadout: LoadoutBuilder::new().build(), }; let Error::Full(leftovers) = inv .push_all(TEST_ITEMS.iter().cloned()) @@ -39,7 +43,7 @@ fn push_all_full() { fn push_unique_all_full() { let mut inv = Inventory { slots: TEST_ITEMS.iter().map(|a| Some(a.clone())).collect(), - amount: 0, + loadout: LoadoutBuilder::new().build(), }; inv.push_all_unique(TEST_ITEMS.iter().cloned()) .expect("Pushing unique items into an inventory that already contains them didn't work!"); @@ -51,7 +55,7 @@ fn push_unique_all_full() { fn push_all_empty() { let mut inv = Inventory { slots: vec![None, None], - amount: 0, + loadout: LoadoutBuilder::new().build(), }; inv.push_all(TEST_ITEMS.iter().cloned()) .expect("Pushing items into an empty inventory didn't work!"); @@ -63,9 +67,324 @@ fn push_all_empty() { fn push_all_unique_empty() { let mut inv = Inventory { slots: vec![None, None], - amount: 0, + loadout: LoadoutBuilder::new().build(), }; inv.push_all_unique(TEST_ITEMS.iter().cloned()).expect( "Pushing unique items into an empty inventory that didn't contain them didn't work!", ); } + +#[test] +fn free_slots_minus_equipped_item_items_only_present_in_equipped_bag_slots() { + let mut inv = Inventory::new_empty(); + + let bag = get_test_bag(18); + let bag1_slot = EquipSlot::Armor(ArmorSlot::Bag1); + inv.loadout.swap(bag1_slot, Some(bag.clone())); + + inv.insert_at(InvSlotId::new(15, 0), bag) + .unwrap() + .unwrap_none(); + + let result = inv.free_slots_minus_equipped_item(bag1_slot); + + // All of the base inventory slots are empty and the equipped bag slots are + // ignored + assert_eq!(18, result); +} + +#[test] +fn free_slots_minus_equipped_item() { + let mut inv = Inventory::new_empty(); + + let bag = get_test_bag(18); + let bag1_slot = EquipSlot::Armor(ArmorSlot::Bag1); + inv.loadout.swap(bag1_slot, Some(bag.clone())); + inv.loadout + .swap(EquipSlot::Armor(ArmorSlot::Bag2), Some(bag.clone())); + + inv.insert_at(InvSlotId::new(16, 0), bag) + .unwrap() + .unwrap_none(); + + let result = inv.free_slots_minus_equipped_item(bag1_slot); + + // All of the base 18 inventory slots are empty, the first equipped bag is + // ignored, and the second equipped bag has 17 free slots + assert_eq!(35, result); +} + +#[test] +fn get_slot_range_for_equip_slot() { + let mut inv = Inventory::new_empty(); + let bag = get_test_bag(18); + let bag1_slot = EquipSlot::Armor(ArmorSlot::Bag1); + inv.loadout.swap(bag1_slot, Some(bag)); + + let result = inv.get_slot_range_for_equip_slot(bag1_slot).unwrap(); + + assert_eq!(18..36, result); +} + +#[test] +fn can_swap_equipped_bag_into_empty_inv_slot_1_free_slot() { + can_swap_equipped_bag_into_empty_inv_slot(1, InvSlotId::new(0, 17), true); +} + +#[test] +fn can_swap_equipped_bag_into_empty_inv_slot_0_free_slots() { + can_swap_equipped_bag_into_empty_inv_slot(0, InvSlotId::new(0, 17), false); +} + +#[test] +fn can_swap_equipped_bag_into_empty_inv_slot_provided_by_equipped_bag() { + can_swap_equipped_bag_into_empty_inv_slot(1, InvSlotId::new(15, 0), true); +} + +fn can_swap_equipped_bag_into_empty_inv_slot( + free_slots: u16, + inv_slot_id: InvSlotId, + expected_result: bool, +) { + let mut inv = Inventory::new_empty(); + + inv.replace_loadout_item(EquipSlot::Armor(ArmorSlot::Bag1), Some(get_test_bag(18))); + + fill_inv_slots(&mut inv, 18 - free_slots); + + let result = inv.can_swap(inv_slot_id, EquipSlot::Armor(ArmorSlot::Bag1)); + + assert_eq!(expected_result, result); +} + +#[test] +fn can_swap_equipped_bag_into_only_empty_slot_provided_by_itself_should_return_false() { + let mut inv = Inventory::new_empty(); + + inv.replace_loadout_item(EquipSlot::Armor(ArmorSlot::Bag1), Some(get_test_bag(18))); + + fill_inv_slots(&mut inv, 35); + + let result = inv.can_swap(InvSlotId::new(15, 17), EquipSlot::Armor(ArmorSlot::Bag1)); + + assert_eq!(false, result); +} + +#[test] +fn unequip_items_both_hands() { + let mut inv = Inventory::new_empty(); + + let sword = Item::new_from_asset_expect("common.items.weapons.sword.zweihander_sword_0"); + + inv.replace_loadout_item(EquipSlot::Mainhand, Some(sword.clone())); + inv.replace_loadout_item(EquipSlot::Offhand, Some(sword.clone())); + + // Fill all inventory slots except one + fill_inv_slots(&mut inv, 17); + + let result = inv.unequip(EquipSlot::Mainhand); + // We have space in the inventory, so this should have unequipped + assert_eq!(None, inv.equipped(EquipSlot::Mainhand)); + assert_eq!(18, inv.populated_slots()); + assert_eq!(true, result.is_ok()); + + let result = inv.unequip(EquipSlot::Offhand).unwrap_err(); + assert_eq!(SlotError::InventoryFull, result); + + // There is no more space in the inventory, so this should still be equipped + assert_eq!(&sword, inv.equipped(EquipSlot::Offhand).unwrap()); + + // Verify inventory + assert_eq!(inv.slots[17], Some(sword)); + assert_eq!(inv.free_slots(), 0); +} + +#[test] +fn equip_replace_already_equipped_item() { + let boots = Item::new_from_asset_expect("common.items.testing.test_boots"); + + let starting_sandles = Some(Item::new_from_asset_expect( + "common.items.armor.starter.sandals_0", + )); + + let mut inv = Inventory::new_empty(); + inv.push(boots.clone()); + inv.replace_loadout_item(EquipSlot::Armor(ArmorSlot::Feet), starting_sandles.clone()); + + inv.equip(InvSlotId::new(0, 0)).unwrap_none(); + + // We should now have the testing boots equipped + assert_eq!( + &boots, + inv.equipped(EquipSlot::Armor(ArmorSlot::Feet)).unwrap() + ); + + // Verify inventory + assert_eq!( + inv.slots[0].as_ref().unwrap().item_definition_id(), + starting_sandles.unwrap().item_definition_id() + ); + assert_eq!(inv.populated_slots(), 1); +} + +/// Regression test for a panic that occurred when swapping an equipped bag +/// for a bag that exists in an inventory slot that will no longer exist +/// after equipping it (because the equipped bag is larger) +#[test] +fn equip_equipping_smaller_bag_from_last_slot_of_big_bag() { + let mut inv = Inventory::new_empty(); + + const LARGE_BAG_ID: &str = "common.items.testing.test_bag_18_slot"; + let small_bag = get_test_bag(9); + let large_bag = Item::new_from_asset_expect(LARGE_BAG_ID); + + inv.loadout + .swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(large_bag)) + .unwrap_none(); + + inv.insert_at(InvSlotId::new(15, 15), small_bag).unwrap(); + + let result = inv.swap( + Slot::Equip(EquipSlot::Armor(ArmorSlot::Bag1)), + Slot::Inventory(InvSlotId::new(15, 15)), + ); + + assert_eq!( + inv.get(InvSlotId::new(0, 0)).unwrap().item_definition_id(), + LARGE_BAG_ID + ); + assert_eq!(result, None); +} + +#[test] +fn unequip_unequipping_bag_into_its_own_slot_with_no_other_free_slots() { + let mut inv = Inventory::new_empty(); + let bag = get_test_bag(9); + + inv.loadout + .swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(bag)) + .unwrap_none(); + + // Fill all inventory built-in slots + fill_inv_slots(&mut inv, 18); + + inv.swap_inventory_loadout(InvSlotId::new(15, 0), EquipSlot::Armor(ArmorSlot::Bag1)) + .unwrap_none(); +} + +#[test] +fn equip_one_bag_equipped_equip_second_bag() { + let mut inv = Inventory::new_empty(); + + let bag = get_test_bag(9); + inv.loadout + .swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(bag.clone())) + .unwrap_none(); + + inv.push(bag); + + inv.equip(InvSlotId::new(0, 0)).unwrap_none(); + + assert_eq!( + true, + inv.equipped(EquipSlot::Armor(ArmorSlot::Bag2)).is_some() + ); +} + +#[test] +fn free_after_swap_equipped_item_has_more_slots() { + let mut inv = Inventory::new_empty(); + + let bag = get_test_bag(18); + inv.loadout + .swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(bag)) + .unwrap_none(); + + let small_bag = get_test_bag(9); + inv.push(small_bag); + + // Fill all remaining slots + fill_inv_slots(&mut inv, 35); + + let result = inv.free_after_swap(EquipSlot::Armor(ArmorSlot::Bag1), InvSlotId::new(0, 0)); + + // 18 inv slots + 9 bag slots - 36 used slots - + assert_eq!(-9, result); +} + +#[test] +fn free_after_swap_equipped_item_has_less_slots() { + let mut inv = Inventory::new_empty(); + + let bag = get_test_bag(9); + inv.loadout + .swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(bag)) + .unwrap_none(); + + let small_bag = get_test_bag(18); + inv.push(small_bag); + + // Fill all slots except the last one + fill_inv_slots(&mut inv, 27); + + let result = inv.free_after_swap(EquipSlot::Armor(ArmorSlot::Bag1), InvSlotId::new(0, 0)); + + // 18 inv slots + 18 bag slots - 27 used slots + assert_eq!(9, result); +} + +#[test] +fn free_after_swap_equipped_item_with_slots_swapped_with_empty_inv_slot() { + let mut inv = Inventory::new_empty(); + + let bag = get_test_bag(9); + inv.loadout + .swap(EquipSlot::Armor(ArmorSlot::Bag1), Some(bag)) + .unwrap_none(); + + // Add 5 items to the inventory + fill_inv_slots(&mut inv, 5); + + let result = inv.free_after_swap(EquipSlot::Armor(ArmorSlot::Bag1), InvSlotId::new(0, 10)); + + // 18 inv slots - 5 used slots - 1 slot for unequipped item + assert_eq!(12, result); +} + +#[test] +fn free_after_swap_inv_item_with_slots_swapped_with_empty_equip_slot() { + let mut inv = Inventory::new_empty(); + + inv.push(get_test_bag(9)); + + // Add 5 items to the inventory + fill_inv_slots(&mut inv, 5); + + let result = inv.free_after_swap(EquipSlot::Armor(ArmorSlot::Bag1), InvSlotId::new(0, 0)); + + // 18 inv slots + 9 bag slots - 5 used slots + assert_eq!(22, result); +} + +#[test] +fn free_after_swap_inv_item_without_slots_swapped_with_empty_equip_slot() { + let mut inv = Inventory::new_empty(); + + let boots = Item::new_from_asset_expect("common.items.testing.test_boots"); + inv.push(boots); + + // Add 5 items to the inventory + fill_inv_slots(&mut inv, 5); + + let result = inv.free_after_swap(EquipSlot::Armor(ArmorSlot::Feet), InvSlotId::new(0, 0)); + + // 18 inv slots - 5 used slots + assert_eq!(13, result); +} + +fn fill_inv_slots(inv: &mut Inventory, items: u16) { + let boots = Item::new_from_asset_expect("common.items.testing.test_boots"); + for _ in 0..items { + inv.push(boots.clone()); + } +} diff --git a/common/src/comp/inventory/test_helpers.rs b/common/src/comp/inventory/test_helpers.rs new file mode 100644 index 0000000000..651d763081 --- /dev/null +++ b/common/src/comp/inventory/test_helpers.rs @@ -0,0 +1,24 @@ +use crate::comp::{ + inventory::item::{ + armor, + armor::{ArmorKind, Protection}, + ItemDef, ItemKind, Quality, + }, + Item, +}; +use std::sync::Arc; + +pub(super) fn get_test_bag(slots: u16) -> Item { + let item_def = ItemDef::new_test( + "common.items.testing.test_bag".to_string(), + None, + ItemKind::Armor(armor::Armor::test_armor( + ArmorKind::Bag("Test Bag".to_string()), + Protection::Normal(0.0), + )), + Quality::Common, + slots, + ); + + Item::new_from_item_def(Arc::new(item_def)) +} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 22ee92c660..a16b104c79 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -13,7 +13,7 @@ pub mod group; mod health; pub mod home_chunk; mod inputs; -mod inventory; +pub mod inventory; mod last; mod location; mod misc; @@ -26,7 +26,7 @@ mod stats; pub mod visual; // Reexports -pub use ability::{CharacterAbility, CharacterAbilityType, ItemConfig, Loadout}; +pub use ability::{CharacterAbility, CharacterAbilityType}; pub use admin::Admin; pub use agent::{Agent, Alignment}; pub use aura::{Aura, AuraChange, AuraKind, Auras}; @@ -54,7 +54,7 @@ pub use home_chunk::HomeChunk; pub use inputs::CanBuild; pub use inventory::{ item, - item::{Item, ItemDrop}, + item::{Item, ItemConfig, ItemDrop}, slot, Inventory, InventoryUpdate, InventoryUpdateEvent, }; pub use last::Last; diff --git a/common/src/event.rs b/common/src/event.rs index 85b29aac5a..a4f13a97b8 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -88,7 +88,6 @@ pub enum ServerEvent { comp::Body, comp::Stats, comp::Inventory, - comp::Loadout, Option, ), }, @@ -100,7 +99,7 @@ pub enum ServerEvent { pos: comp::Pos, stats: comp::Stats, health: comp::Health, - loadout: comp::Loadout, + loadout: comp::inventory::loadout::Loadout, body: comp::Body, agent: Option, alignment: comp::Alignment, diff --git a/common/src/generation.rs b/common/src/generation.rs index 7f163f8224..43d22b02fb 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -1,6 +1,5 @@ use crate::{ - comp::{self, humanoid, Alignment, Body, Item}, - loadout_builder::LoadoutConfig, + comp::{self, humanoid, inventory::loadout_builder::LoadoutConfig, Alignment, Body, Item}, npc::{self, NPC_NAMES}, }; use vek::*; diff --git a/common/src/lib.rs b/common/src/lib.rs index 3550085ab8..f762badc07 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -6,15 +6,17 @@ #![feature( arbitrary_enum_discriminant, associated_type_defaults, + bool_to_option, const_checked_int_methods, const_generics, fundamental, - option_unwrap_none, - bool_to_option, + iter_map_while, label_break_value, + option_expect_none, + option_unwrap_none, + option_zip, trait_alias, - type_alias_impl_trait, - option_zip + type_alias_impl_trait )] pub mod assets; @@ -31,7 +33,6 @@ pub mod explosion; pub mod figure; pub mod generation; pub mod grid; -pub mod loadout_builder; pub mod lottery; pub mod metrics; pub mod npc; @@ -54,5 +55,5 @@ pub mod vol; pub mod volumes; pub use combat::{Damage, DamageSource, GroupTarget, Knockback}; +pub use comp::inventory::loadout_builder::LoadoutBuilder; pub use explosion::{Explosion, RadiusEffect}; -pub use loadout_builder::LoadoutBuilder; diff --git a/common/src/recipe.rs b/common/src/recipe.rs index 24ae29396f..de712ab227 100644 --- a/common/src/recipe.rs +++ b/common/src/recipe.rs @@ -22,15 +22,14 @@ impl Recipe { // Get ingredient cells from inventory, inv.contains_ingredients(self)? .into_iter() - .enumerate() - .for_each(|(i, n)| { + .for_each(|(pos, n)| { (0..n).for_each(|_| { - inv.take(i).expect("Expected item to exist in inventory"); + inv.take(pos).expect("Expected item to exist in inventory"); }) }); for i in 0..self.output.1 { - let crafted_item = Item::new(Arc::clone(&self.output.0)); + let crafted_item = Item::new_from_item_def(Arc::clone(&self.output.0)); if let Some(item) = inv.push(crafted_item) { return Ok(Some((item, self.output.1 - i))); } diff --git a/common/src/states/behavior.rs b/common/src/states/behavior.rs index 0af73ec436..8ec1837d32 100644 --- a/common/src/states/behavior.rs +++ b/common/src/states/behavior.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ Attacking, Beam, Body, CharacterState, ControlAction, Controller, ControllerInputs, Energy, - Health, Loadout, Ori, PhysicsState, Pos, StateUpdate, Vel, + Health, Inventory, Ori, PhysicsState, Pos, StateUpdate, Vel, }, resources::DeltaTime, uid::Uid, @@ -52,7 +52,7 @@ pub struct JoinData<'a> { pub inputs: &'a ControllerInputs, pub health: &'a Health, pub energy: &'a Energy, - pub loadout: &'a Loadout, + pub inventory: &'a Inventory, pub body: &'a Body, pub physics: &'a PhysicsState, pub attacking: Option<&'a Attacking>, @@ -76,7 +76,7 @@ pub type JoinTuple<'a> = ( &'a mut Vel, &'a mut Ori, RestrictedMut<'a, Energy>, - RestrictedMut<'a, Loadout>, + RestrictedMut<'a, Inventory>, &'a mut Controller, &'a Health, &'a Body, @@ -95,7 +95,7 @@ impl<'a> JoinData<'a> { vel: j.4, ori: j.5, energy: j.6.get_unchecked(), - loadout: j.7.get_unchecked(), + inventory: j.7.get_unchecked(), controller: j.8, inputs: &j.8.inputs, health: j.9, diff --git a/common/src/states/glide.rs b/common/src/states/glide.rs index 5fa8b267e6..2a7b729722 100644 --- a/common/src/states/glide.rs +++ b/common/src/states/glide.rs @@ -1,11 +1,12 @@ use super::utils::handle_climb; use crate::{ - comp::{CharacterState, EnergySource, StateUpdate}, + comp::{inventory::slot::EquipSlot, CharacterState, EnergySource, StateUpdate}, states::behavior::{CharacterBehavior, JoinData}, util::Dir, }; use serde::{Deserialize, Serialize}; use vek::Vec2; + // Gravity is 9.81 * 4, so this makes gravity equal to .15 const GLIDE_ANTIGRAV: f32 = crate::consts::GRAVITY * 0.90; const GLIDE_ACCEL: f32 = 12.0; @@ -31,7 +32,7 @@ impl CharacterBehavior for Data { { update.character = CharacterState::Idle; } - if data.loadout.glider.is_none() { + if data.inventory.equipped(EquipSlot::Glider).is_none() { update.character = CharacterState::Idle }; // If there is a wall in front of character and they are trying to climb go to diff --git a/common/src/states/glide_wield.rs b/common/src/states/glide_wield.rs index 170bb6f1fb..1535239e28 100644 --- a/common/src/states/glide_wield.rs +++ b/common/src/states/glide_wield.rs @@ -1,6 +1,6 @@ use super::utils::*; use crate::{ - comp::{CharacterState, StateUpdate}, + comp::{slot::EquipSlot, CharacterState, StateUpdate}, states::behavior::{CharacterBehavior, JoinData}, }; @@ -27,7 +27,7 @@ impl CharacterBehavior for Data { { update.character = CharacterState::Idle; } - if data.loadout.glider.is_none() { + if data.inventory.equipped(EquipSlot::Glider).is_none() { update.character = CharacterState::Idle }; diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index ea4f812fbd..ed89a63059 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -1,5 +1,6 @@ use crate::{ comp::{ + inventory::slot::EquipSlot, item::{Hands, ItemKind, Tool}, quadruped_low, quadruped_medium, theropod, Body, CharacterState, StateUpdate, }, @@ -290,7 +291,11 @@ 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 + .inventory + .equipped(EquipSlot::Mainhand) + .map(|i| i.kind()) + { update.character = CharacterState::Equipping(equipping::Data { static_data: equipping::StaticData { buildup_duration: tool.equip_time(), @@ -341,14 +346,14 @@ pub fn handle_climb(data: &JoinData, update: &mut StateUpdate) { /// Checks that player can Swap Weapons and updates `Loadout` if so pub fn attempt_swap_loadout(data: &JoinData, update: &mut StateUpdate) { - if data.loadout.second_item.is_some() { + if data.inventory.equipped(EquipSlot::Offhand).is_some() { update.swap_loadout = true; } } /// Checks that player can wield the glider and updates `CharacterState` if so pub fn attempt_glide_wield(data: &JoinData, update: &mut StateUpdate) { - if data.loadout.glider.is_some() + if data.inventory.equipped(EquipSlot::Glider).is_some() && !data .physics .in_liquid @@ -376,14 +381,12 @@ pub fn handle_jump(data: &JoinData, update: &mut StateUpdate) { } } -/// Will attempt to go into `loadout.active_item.ability1` pub fn handle_ability1_input(data: &JoinData, update: &mut StateUpdate) { if data.inputs.primary.is_pressed() { if let Some(ability) = data - .loadout - .active_item - .as_ref() - .and_then(|i| i.ability1.as_ref()) + .inventory + .equipped(EquipSlot::Mainhand) + .and_then(|i| i.item_config_expect().ability1.as_ref()) .filter(|ability| ability.requirements_paid(data, update)) { update.character = (ability, AbilityKey::Mouse1).into(); @@ -391,15 +394,22 @@ 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 + .inventory + .equipped(EquipSlot::Mainhand) + .map(|i| i.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 + .inventory + .equipped(EquipSlot::Offhand) + .map(|i| i.kind()) + { Some(ItemKind::Tool(Tool { kind, .. })) => Some(kind), _ => None, }; @@ -410,10 +420,9 @@ pub fn handle_ability2_input(data: &JoinData, update: &mut StateUpdate) { ) { (Some(Hands::TwoHand), _) => { if let Some(ability) = data - .loadout - .active_item - .as_ref() - .and_then(|i| i.ability2.as_ref()) + .inventory + .equipped(EquipSlot::Mainhand) + .and_then(|i| i.item_config_expect().ability2.as_ref()) .filter(|ability| ability.requirements_paid(data, update)) { update.character = (ability, AbilityKey::Mouse2).into(); @@ -421,10 +430,9 @@ pub fn handle_ability2_input(data: &JoinData, update: &mut StateUpdate) { }, (_, Some(Hands::OneHand)) => { if let Some(ability) = data - .loadout - .second_item - .as_ref() - .and_then(|i| i.ability2.as_ref()) + .inventory + .equipped(EquipSlot::Offhand) + .and_then(|i| i.item_config_expect().ability2.as_ref()) .filter(|ability| ability.requirements_paid(data, update)) { update.character = (ability, AbilityKey::Mouse2).into(); @@ -435,14 +443,12 @@ pub fn handle_ability2_input(data: &JoinData, update: &mut StateUpdate) { } } -/// Will attempt to go into `loadout.active_item.ability3` pub fn handle_ability3_input(data: &JoinData, update: &mut StateUpdate) { if data.inputs.ability3.is_pressed() { if let Some(ability) = data - .loadout - .active_item - .as_ref() - .and_then(|i| i.ability3.as_ref()) + .inventory + .equipped(EquipSlot::Mainhand) + .and_then(|i| i.item_config_expect().ability3.as_ref()) .filter(|ability| ability.requirements_paid(data, update)) { update.character = (ability, AbilityKey::Skill1).into(); @@ -451,14 +457,13 @@ pub fn handle_ability3_input(data: &JoinData, update: &mut StateUpdate) { } /// Checks that player can perform a dodge, then -/// attempts to go into `loadout.active_item.dodge_ability` +/// attempts to perform their dodge ability pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) { if data.inputs.roll.is_pressed() && data.body.is_humanoid() { if let Some(ability) = data - .loadout - .active_item - .as_ref() - .and_then(|i| i.dodge_ability.as_ref()) + .inventory + .equipped(EquipSlot::Mainhand) + .and_then(|i| i.item_config_expect().dodge_ability.as_ref()) .filter(|ability| ability.requirements_paid(data, update)) { if data.character.is_wield() { @@ -479,8 +484,12 @@ 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()) { - Some(tool) + if let Some(ItemKind::Tool(tool)) = data + .inventory + .equipped(EquipSlot::Mainhand) + .map(|i| i.kind()) + { + Some(&tool) } else { None } diff --git a/common/src/terrain/sprite.rs b/common/src/terrain/sprite.rs index 2f91924b28..aa8ed81ab9 100644 --- a/common/src/terrain/sprite.rs +++ b/common/src/terrain/sprite.rs @@ -177,7 +177,7 @@ impl SpriteKind { SpriteKind::BlueFlower => false, SpriteKind::PinkFlower => false, SpriteKind::PurpleFlower => false, - SpriteKind::RedFlower => false, + SpriteKind::RedFlower => true, SpriteKind::WhiteFlower => false, SpriteKind::YellowFlower => false, SpriteKind::Sunflower => true, diff --git a/common/sys/src/agent.rs b/common/sys/src/agent.rs index 76bc5319fd..e5a456febc 100644 --- a/common/sys/src/agent.rs +++ b/common/sys/src/agent.rs @@ -4,12 +4,13 @@ use common::{ agent::Activity, group, group::Invite, + inventory::slot::EquipSlot, item::{ tool::{ToolKind, UniqueKind}, ItemKind, }, Agent, Alignment, Body, CharacterState, ControlAction, ControlEvent, Controller, Energy, - GroupManip, Health, LightEmitter, Loadout, MountState, Ori, PhysicsState, Pos, Scale, + GroupManip, Health, Inventory, LightEmitter, MountState, Ori, PhysicsState, Pos, Scale, UnresolvedChatMsg, Vel, }, event::{EventBus, ServerEvent}, @@ -52,7 +53,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Ori>, ReadStorage<'a, Scale>, ReadStorage<'a, Health>, - ReadStorage<'a, Loadout>, + ReadStorage<'a, Inventory>, ReadStorage<'a, PhysicsState>, ReadStorage<'a, Uid>, ReadStorage<'a, group::Group>, @@ -82,7 +83,7 @@ impl<'a> System<'a> for Sys { orientations, scales, healths, - loadouts, + inventories, physics_states, uids, groups, @@ -108,7 +109,7 @@ impl<'a> System<'a> for Sys { &velocities, &orientations, alignments.maybe(), - &loadouts, + &inventories, &physics_states, bodies.maybe(), &uids, @@ -130,7 +131,7 @@ impl<'a> System<'a> for Sys { vel, ori, alignment, - loadout, + inventory, physics_state, body, uid, @@ -157,7 +158,7 @@ impl<'a> System<'a> for Sys { let mut event_emitter = event_bus.emitter(); // Light lanterns at night // TODO Add a method to turn on NPC lanterns underground - let lantern_equipped = loadout.lantern.as_ref().map_or(false, |item| { + let lantern_equipped = inventory.equipped(EquipSlot::Lantern).as_ref().map_or(false, |item| { matches!(item.kind(), comp::item::ItemKind::Lantern(_)) }); let lantern_turned_on = light_emitter.is_some(); @@ -370,8 +371,8 @@ impl<'a> System<'a> for Sys { Theropod, } - let tactic = match loadout.active_item.as_ref().and_then(|ic| { - if let ItemKind::Tool(tool) = &ic.item.kind() { + let tactic = match inventory.equipped(EquipSlot::Mainhand).as_ref().and_then(|item| { + if let ItemKind::Tool(tool) = &item.kind() { Some(&tool.kind) } else { None diff --git a/common/sys/src/beam.rs b/common/sys/src/beam.rs index 49844c75b7..3ab6cc6612 100644 --- a/common/sys/src/beam.rs +++ b/common/sys/src/beam.rs @@ -1,7 +1,7 @@ use common::{ comp::{ group, Beam, BeamSegment, Body, Energy, EnergyChange, EnergySource, Health, HealthChange, - HealthSource, Last, Loadout, Ori, Pos, Scale, + HealthSource, Inventory, Last, Ori, Pos, Scale, }, event::{EventBus, ServerEvent}, resources::{DeltaTime, Time}, @@ -29,7 +29,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Scale>, ReadStorage<'a, Body>, ReadStorage<'a, Health>, - ReadStorage<'a, Loadout>, + ReadStorage<'a, Inventory>, ReadStorage<'a, group::Group>, ReadStorage<'a, Energy>, WriteStorage<'a, BeamSegment>, @@ -51,7 +51,7 @@ impl<'a> System<'a> for Sys { scales, bodies, healths, - loadouts, + inventories, groups, energies, mut beam_segments, @@ -166,7 +166,7 @@ impl<'a> System<'a> for Sys { } // Modify damage - let change = damage.modify_damage(loadouts.get(b), beam_segment.owner); + let change = damage.modify_damage(inventories.get(b), beam_segment.owner); match target { Some(GroupTarget::OutOfGroup) => { diff --git a/common/sys/src/buff.rs b/common/sys/src/buff.rs index 3570493e98..39d66689e2 100644 --- a/common/sys/src/buff.rs +++ b/common/sys/src/buff.rs @@ -1,11 +1,11 @@ use common::{ comp::{ BuffCategory, BuffChange, BuffEffect, BuffId, BuffSource, Buffs, Health, HealthChange, - HealthSource, Loadout, ModifierKind, + HealthSource, Inventory, ModifierKind, }, event::{EventBus, ServerEvent}, resources::DeltaTime, - DamageSource, + Damage, DamageSource, }; use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; use std::time::Duration; @@ -17,14 +17,14 @@ impl<'a> System<'a> for Sys { Entities<'a>, Read<'a, DeltaTime>, Read<'a, EventBus>, - ReadStorage<'a, Loadout>, + ReadStorage<'a, Inventory>, WriteStorage<'a, Health>, WriteStorage<'a, Buffs>, ); fn run( &mut self, - (entities, dt, server_bus, loadouts, mut healths, mut buffs): Self::SystemData, + (entities, dt, server_bus, inventories, mut healths, mut buffs): Self::SystemData, ) { let mut server_emitter = server_bus.emitter(); // Set to false to avoid spamming server @@ -51,8 +51,8 @@ impl<'a> System<'a> for Sys { } } - if let Some(loadout) = loadouts.get(entity) { - let damage_reduction = loadout.get_damage_reduction(); + if let Some(inventory) = inventories.get(entity) { + let damage_reduction = Damage::compute_damage_reduction(inventory); if (damage_reduction - 1.0).abs() < f32::EPSILON { for (id, buff) in buff_comp.buffs.iter() { if !buff.kind.is_buff() { diff --git a/common/sys/src/character_behavior.rs b/common/sys/src/character_behavior.rs index 337b49bd57..60eae9dde0 100644 --- a/common/sys/src/character_behavior.rs +++ b/common/sys/src/character_behavior.rs @@ -1,7 +1,10 @@ +use specs::{Entities, Join, LazyUpdate, Read, ReadExpect, ReadStorage, System, WriteStorage}; + use common::{ comp::{ - Attacking, Beam, Body, CharacterState, Controller, Energy, Health, Loadout, Mounting, Ori, - PhysicsState, Pos, StateUpdate, Vel, + inventory::slot::{EquipSlot, Slot}, + Attacking, Beam, Body, CharacterState, Controller, Energy, Health, Inventory, Mounting, + Ori, PhysicsState, Pos, StateUpdate, Vel, }, event::{EventBus, LocalEvent, ServerEvent}, metrics::SysMetrics, @@ -14,8 +17,6 @@ use common::{ uid::{Uid, UidAllocator}, }; -use specs::{Entities, Join, LazyUpdate, Read, ReadExpect, ReadStorage, System, WriteStorage}; - fn incorporate_update(tuple: &mut JoinTuple, state_update: StateUpdate) { // TODO: if checking equality is expensive use optional field in StateUpdate if tuple.2.get_unchecked() != &state_update.character { @@ -29,9 +30,14 @@ fn incorporate_update(tuple: &mut JoinTuple, state_update: StateUpdate) { *tuple.6.get_mut_unchecked() = state_update.energy }; if state_update.swap_loadout { - let mut loadout = tuple.7.get_mut_unchecked(); - let loadout = &mut *loadout; - std::mem::swap(&mut loadout.active_item, &mut loadout.second_item); + let mut inventory = tuple.7.get_mut_unchecked(); + let inventory = &mut *inventory; + inventory + .swap( + Slot::Equip(EquipSlot::Mainhand), + Slot::Equip(EquipSlot::Offhand), + ) + .unwrap_none(); // Swapping main and offhand never results in leftover items } } @@ -55,7 +61,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Vel>, WriteStorage<'a, Ori>, WriteStorage<'a, Energy>, - WriteStorage<'a, Loadout>, + WriteStorage<'a, Inventory>, WriteStorage<'a, Controller>, ReadStorage<'a, Health>, ReadStorage<'a, Body>, @@ -82,7 +88,7 @@ impl<'a> System<'a> for Sys { mut velocities, mut orientations, mut energies, - mut loadouts, + mut inventories, mut controllers, healths, bodies, @@ -106,7 +112,7 @@ impl<'a> System<'a> for Sys { &mut velocities, &mut orientations, &mut energies.restrict_mut(), - &mut loadouts.restrict_mut(), + &mut inventories.restrict_mut(), &mut controllers, &healths, &bodies, diff --git a/common/sys/src/lib.rs b/common/sys/src/lib.rs index f58c67e279..88daef5fa5 100644 --- a/common/sys/src/lib.rs +++ b/common/sys/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(label_break_value, bool_to_option)] +#![feature(label_break_value, bool_to_option, option_unwrap_none)] pub mod agent; mod aura; diff --git a/common/sys/src/melee.rs b/common/sys/src/melee.rs index accc6ab276..ebf116965d 100644 --- a/common/sys/src/melee.rs +++ b/common/sys/src/melee.rs @@ -1,5 +1,5 @@ use common::{ - comp::{buff, group, Attacking, Body, CharacterState, Health, Loadout, Ori, Pos, Scale}, + comp::{buff, group, Attacking, Body, CharacterState, Health, Inventory, Ori, Pos, Scale}, event::{EventBus, LocalEvent, ServerEvent}, metrics::SysMetrics, span, @@ -28,7 +28,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Scale>, ReadStorage<'a, Body>, ReadStorage<'a, Health>, - ReadStorage<'a, Loadout>, + ReadStorage<'a, Inventory>, ReadStorage<'a, group::Group>, WriteStorage<'a, Attacking>, ReadStorage<'a, CharacterState>, @@ -47,7 +47,7 @@ impl<'a> System<'a> for Sys { scales, bodies, healths, - loadouts, + inventories, groups, mut attacking_storage, char_states, @@ -125,7 +125,7 @@ impl<'a> System<'a> for Sys { } } - let change = damage.modify_damage(loadouts.get(b), Some(*uid)); + let change = damage.modify_damage(inventories.get(b), Some(*uid)); server_emitter.emit(ServerEvent::Damage { entity: b, change }); // Apply bleeding buff on melee hits with 10% chance diff --git a/common/sys/src/projectile.rs b/common/sys/src/projectile.rs index 4fe3d4f63c..7ff5a2e670 100644 --- a/common/sys/src/projectile.rs +++ b/common/sys/src/projectile.rs @@ -1,7 +1,7 @@ use common::{ comp::{ buff::{Buff, BuffChange, BuffSource}, - projectile, EnergyChange, EnergySource, Group, HealthSource, Loadout, Ori, PhysicsState, + projectile, EnergyChange, EnergySource, Group, HealthSource, Inventory, Ori, PhysicsState, Pos, Projectile, Vel, }, event::{EventBus, ServerEvent}, @@ -32,7 +32,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Vel>, WriteStorage<'a, Ori>, WriteStorage<'a, Projectile>, - ReadStorage<'a, Loadout>, + ReadStorage<'a, Inventory>, ReadStorage<'a, Group>, ); @@ -49,7 +49,7 @@ impl<'a> System<'a> for Sys { velocities, mut orientations, mut projectiles, - loadouts, + inventories, groups, ): Self::SystemData, ) { @@ -116,9 +116,9 @@ impl<'a> System<'a> for Sys { if let Some(other_entity) = uid_allocator.retrieve_entity_internal(other.into()) { - let other_entity_loadout = loadouts.get(other_entity); + let other_entity_inventory = inventories.get(other_entity); let change = - damage.modify_damage(other_entity_loadout, projectile.owner); + damage.modify_damage(other_entity_inventory, projectile.owner); server_emitter.emit(ServerEvent::Damage { entity: other_entity, change, diff --git a/common/sys/src/shockwave.rs b/common/sys/src/shockwave.rs index 04ace531ba..8db1047a44 100644 --- a/common/sys/src/shockwave.rs +++ b/common/sys/src/shockwave.rs @@ -1,7 +1,7 @@ use common::{ comp::{ - group, Body, Health, HealthSource, Last, Loadout, Ori, PhysicsState, Pos, Scale, Shockwave, - ShockwaveHitEntities, + group, Body, Health, HealthSource, Inventory, Last, Ori, PhysicsState, Pos, Scale, + Shockwave, ShockwaveHitEntities, }, event::{EventBus, LocalEvent, ServerEvent}, resources::{DeltaTime, Time}, @@ -31,7 +31,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Scale>, ReadStorage<'a, Body>, ReadStorage<'a, Health>, - ReadStorage<'a, Loadout>, + ReadStorage<'a, Inventory>, ReadStorage<'a, group::Group>, ReadStorage<'a, PhysicsState>, WriteStorage<'a, Shockwave>, @@ -54,7 +54,7 @@ impl<'a> System<'a> for Sys { scales, bodies, healths, - loadouts, + inventories, groups, physics_states, mut shockwaves, @@ -199,7 +199,7 @@ impl<'a> System<'a> for Sys { } let owner_uid = shockwave.owner.unwrap_or(*uid); - let change = damage.modify_damage(loadouts.get(b), Some(owner_uid)); + let change = damage.modify_damage(inventories.get(b), Some(owner_uid)); server_emitter.emit(ServerEvent::Damage { entity: b, change }); shockwave_hit_list.hit_entities.push(*uid_b); diff --git a/common/sys/src/state.rs b/common/sys/src/state.rs index 1559b64c06..5d95686747 100644 --- a/common/sys/src/state.rs +++ b/common/sys/src/state.rs @@ -109,7 +109,6 @@ impl State { // Uids for sync ecs.register_sync_marker(); // Register server -> all clients synced components. - ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); @@ -280,9 +279,6 @@ impl State { /// Get the current delta time. pub fn get_delta_time(&self) -> f32 { self.ecs.read_resource::().0 } - /// Get a reference to this state's ability map. - pub fn ability_map(&self) -> Fetch { self.ecs.read_resource() } - /// Get a reference to this state's terrain. pub fn terrain(&self) -> Fetch { self.ecs.read_resource() } diff --git a/server-cli/Cargo.toml b/server-cli/Cargo.toml index 6269f04932..66fd1cf960 100644 --- a/server-cli/Cargo.toml +++ b/server-cli/Cargo.toml @@ -9,6 +9,7 @@ worldgen = ["server/worldgen"] default = ["worldgen"] tracy = ["common/tracy", "tracing-tracy"] plugins = ["server/plugins"] +tweak = ["server/tweak"] [dependencies] server = { package = "veloren-server", path = "../server", default-features = false } diff --git a/server-cli/src/logging.rs b/server-cli/src/logging.rs index 6a738ed2c5..72909db2dd 100644 --- a/server-cli/src/logging.rs +++ b/server-cli/src/logging.rs @@ -16,6 +16,11 @@ pub fn init(basic: bool) { let base_exceptions = |env: EnvFilter| { env.add_directive("veloren_world::sim=info".parse().unwrap()) .add_directive("veloren_world::civ=info".parse().unwrap()) + .add_directive( + "veloren_common::comp::inventory::slot=info" + .parse() + .unwrap(), + ) .add_directive("uvth=warn".parse().unwrap()) .add_directive("tiny_http=warn".parse().unwrap()) .add_directive("mio::sys::windows=debug".parse().unwrap()) diff --git a/server/Cargo.toml b/server/Cargo.toml index 0b97156e82..31af7389b8 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" worldgen = [] simd = ["vek/platform_intrinsics"] plugins = ["common-sys/plugins"] +tweak = ["const-tweaker"] default = ["worldgen", "plugins", "simd"] @@ -47,6 +48,7 @@ diesel = { version = "1.4.3", features = ["sqlite"] } diesel_migrations = "1.4.0" dotenv = "0.15.0" slab = "0.4" +const-tweaker = {version = "0.3.1", optional = true} # Plugins plugin-api = { package = "veloren-plugin-api", path = "../plugin/api"} diff --git a/server/src/character_creator.rs b/server/src/character_creator.rs index 6fdeadc53a..32199d2c6a 100644 --- a/server/src/character_creator.rs +++ b/server/src/character_creator.rs @@ -1,8 +1,5 @@ use crate::persistence::character_loader::CharacterLoader; -use common::{ - comp::{item::tool::AbilityMap, Body, Inventory, Stats}, - loadout_builder::LoadoutBuilder, -}; +use common::comp::{inventory::loadout_builder::LoadoutBuilder, Body, Inventory, Item, Stats}; use specs::{Entity, ReadExpect}; pub fn create_character( @@ -12,25 +9,30 @@ pub fn create_character( character_tool: Option, body: Body, character_loader: &ReadExpect<'_, CharacterLoader>, - map: &AbilityMap, ) { let stats = Stats::new(character_alias.to_string(), body); let loadout = LoadoutBuilder::new() .defaults() - .active_item(Some(LoadoutBuilder::default_item_config_from_str( + .active_item(Some(Item::new_from_asset_expect( character_tool.as_deref().unwrap(), - map, ))) .build(); - let inventory = Inventory::default(); + let mut inventory = Inventory::new_with_loadout(loadout); + + // Default items for new characters + inventory.push(Item::new_from_asset_expect( + "common.items.consumable.potion_minor", + )); + inventory.push(Item::new_from_asset_expect("common.items.food.cheese")); + let waypoint = None; character_loader.create_character( entity, player_uuid, character_alias, - (body, stats, inventory, loadout, waypoint), + (body, stats, inventory, waypoint), ); } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index fc91fc18b3..579fac55a9 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -13,7 +13,7 @@ use common::{ self, aura::{Aura, AuraKind}, buff::{BuffCategory, BuffData, BuffKind, BuffSource}, - ChatType, Item, LightEmitter, WaypointArea, + ChatType, Inventory, Item, LightEmitter, WaypointArea, }, effect::Effect, event::{EventBus, ServerEvent}, @@ -82,6 +82,7 @@ fn get_handler(cmd: &ChatCommand) -> CommandHandler { ChatCommand::Campfire => handle_spawn_campfire, ChatCommand::Debug => handle_debug, ChatCommand::DebugColumn => handle_debug_column, + ChatCommand::DropAll => handle_drop_all, ChatCommand::Dummy => handle_spawn_training_dummy, ChatCommand::Explosion => handle_explosion, ChatCommand::Faction => handle_faction, @@ -126,6 +127,50 @@ fn get_handler(cmd: &ChatCommand) -> CommandHandler { } } +fn handle_drop_all( + server: &mut Server, + client: EcsEntity, + _target: EcsEntity, + _args: String, + _action: &ChatCommand, +) { + let pos = server + .state + .ecs() + .read_storage::() + .get(client) + .cloned(); + + let mut items = Vec::new(); + if let Some(mut inventory) = server + .state + .ecs() + .write_storage::() + .get_mut(client) + { + items = inventory.drain().collect(); + } + + let mut rng = rand::thread_rng(); + + let pos = pos.expect("expected pos for entity using dropall command"); + for item in items { + let vel = Vec3::new(rng.gen_range(-0.1, 0.1), rng.gen_range(-0.1, 0.1), 0.5); + + server + .state + .create_object(Default::default(), comp::object::Body::Pouch) + .with(comp::Pos(Vec3::new( + pos.0.x + rng.gen_range(5.0, 10.0), + pos.0.y + rng.gen_range(5.0, 10.0), + pos.0.z + 5.0, + ))) + .with(item) + .with(comp::Vel(vel)) + .build(); + } +} + #[allow(clippy::useless_conversion)] // TODO: Pending review in #587 fn handle_give_item( server: &mut Server, @@ -146,7 +191,7 @@ fn handle_give_item( .ecs() .write_storage::() .get_mut(target) - .map(|inv| { + .map(|mut inv| { if inv.push(item).is_some() { server.notify_client( client, @@ -167,7 +212,7 @@ fn handle_give_item( .ecs() .write_storage::() .get_mut(target) - .map(|inv| { + .map(|mut inv| { for i in 0..give_amount { if inv.push(item.duplicate()).is_some() { server.notify_client( @@ -768,10 +813,9 @@ fn handle_spawn( let body = body(); - let map = server.state().ability_map(); - let loadout = - LoadoutBuilder::build_loadout(body, None, &map, None).build(); - drop(map); + let loadout = LoadoutBuilder::build_loadout(body, None, None).build(); + + let inventory = Inventory::new_with_loadout(loadout); let mut entity_base = server .state @@ -779,7 +823,7 @@ fn handle_spawn( pos, comp::Stats::new(get_npc_name(id), body), comp::Health::new(body, 1), - loadout, + inventory, body, ) .with(comp::Vel(vel)) @@ -892,7 +936,7 @@ fn handle_spawn_training_dummy( server .state - .create_npc(pos, stats, health, comp::Loadout::default(), body) + .create_npc(pos, stats, health, Inventory::new_empty(), body) .with(comp::Vel(vel)) .with(comp::MountState::Unmounted) .build(); @@ -2093,7 +2137,7 @@ fn handle_debug( .ecs() .write_storage::() .get_mut(target) - .map(|inv| inv.push_all_unique(items.into_iter())); + .map(|mut inv| inv.push_all_unique(items.into_iter())); let _ = server .state .ecs() diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 7af70eccd4..e2c64e92b4 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -6,8 +6,10 @@ use common::{ aura::{Aura, AuraKind}, beam, buff::{BuffCategory, BuffData, BuffKind, BuffSource}, - group, shockwave, Agent, Alignment, Body, Gravity, Health, HomeChunk, Item, ItemDrop, - LightEmitter, Loadout, Ori, Pos, Projectile, Scale, Stats, Vel, WaypointArea, + group, + inventory::loadout::Loadout, + shockwave, Agent, Alignment, Body, Gravity, Health, HomeChunk, Inventory, Item, ItemDrop, + LightEmitter, Ori, Pos, Projectile, Scale, Stats, Vel, WaypointArea, }, outcome::Outcome, rtsim::RtSimEntity, @@ -32,7 +34,6 @@ pub fn handle_loaded_character_data( comp::Body, comp::Stats, comp::Inventory, - comp::Loadout, Option, ), ) { @@ -66,9 +67,11 @@ pub fn handle_create_npc( Alignment::Owned(_) => None, }; + let inventory = Inventory::new_with_loadout(loadout); + let entity = server .state - .create_npc(pos, stats, health, loadout, body) + .create_npc(pos, stats, health, inventory, body) .with(scale) .with(alignment); diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index f0a382f31c..d0c13d4479 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1,6 +1,6 @@ use crate::{ client::Client, - comp::{biped_large, quadruped_medium, quadruped_small, theropod, PhysicsState}, + comp::{biped_large, quadruped_low, quadruped_medium, quadruped_small, theropod, PhysicsState}, rtsim::RtSim, Server, SpawnPoint, StateExt, }; @@ -10,7 +10,7 @@ use common::{ self, aura, buff, chat::{KillSource, KillType}, object, Alignment, Body, Energy, EnergyChange, Group, Health, HealthChange, HealthSource, - Item, Player, Pos, Stats, + Inventory, Item, Player, Pos, Stats, }, effect::Effect, lottery::Lottery, @@ -323,7 +323,18 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc 6 => "common.loot_tables.loot_table_weapon_rare", _ => "common.loot_tables.loot_table_cave_large", }, - _ => match rng.gen_range(0, 8) { + biped_large::Species::Troll => match rng.gen_range(0, 10) { + 0 => "common.loot_tables.loot_table_food", + 1 => "common.loot_tables.loot_table_cave_large", + 3 => "common.loot_tables.loot_table_armor_heavy", + 5 => "common.loot_tables.loot_table_weapon_uncommon", + 6 => "common.loot_tables.loot_table_weapon_rare", + _ => "common.loot_tables.loot_table_troll", + }, + biped_large::Species::Occultsaurok + | biped_large::Species::Mightysaurok + | biped_large::Species::Slysaurok => "common.loot_tables.loot_table_saurok", + _ => match rng.gen_range(0, 10) { 0 => "common.loot_tables.loot_table_food", 1 => "common.loot_tables.loot_table_armor_nature", 3 => "common.loot_tables.loot_table_armor_heavy", @@ -351,10 +362,17 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc _ => "common.loot_tables.loot_table_animal_parts", }, Some(common::comp::Body::Dragon(_)) => "common.loot_tables.loot_table_weapon_rare", - Some(common::comp::Body::QuadrupedLow(_)) => match rng.gen_range(0, 3) { - 0 => "common.loot_tables.loot_table_food", - 1 => "common.loot_tables.loot_table_animal_parts", - _ => "common.loot_tables.loot_table", + Some(common::comp::Body::QuadrupedLow(quadruped_low)) => { + match quadruped_low.species { + quadruped_low::Species::Maneater => { + "common.loot_tables.loot_table_maneater" + }, + _ => match rng.gen_range(0, 3) { + 0 => "common.loot_tables.loot_table_food", + 1 => "common.loot_tables.loot_table_animal_parts", + _ => "common.loot_tables.loot_table", + }, + } }, _ => "common.loot_tables.loot_table", }) @@ -438,8 +456,8 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3) source: DamageSource::Falling, value: falldmg, }; - let loadouts = state.ecs().read_storage::(); - let change = damage.modify_damage(loadouts.get(entity), None); + let inventories = state.ecs().read_storage::(); + let change = damage.modify_damage(inventories.get(entity), None); health.change_by(change); } } diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index d52ae57eee..8d2e85f337 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -1,20 +1,18 @@ +use specs::{world::WorldExt, Entity as EcsEntity}; +use tracing::error; + +use common::{ + comp::{self, inventory::slot::EquipSlot, item, slot::Slot, Inventory, Pos}, + consts::MAX_MOUNT_RANGE, + uid::Uid, +}; +use common_net::{msg::ServerGeneral, sync::WorldSyncExt}; + use crate::{ client::Client, presence::{Presence, RegionSubscription}, Server, }; -use common::{ - comp::{ - self, - item::{self, tool::AbilityMap}, - Pos, - }, - consts::MAX_MOUNT_RANGE, - uid::Uid, -}; -use common_net::{msg::ServerGeneral, sync::WorldSyncExt}; -use specs::{world::WorldExt, Entity as EcsEntity}; -use tracing::error; pub fn handle_lantern(server: &mut Server, entity: EcsEntity, enable: bool) { let ecs = server.state_mut().ecs(); @@ -32,10 +30,10 @@ pub fn handle_lantern(server: &mut Server, entity: EcsEntity, enable: bool) { .write_storage::() .remove(entity); } else { - let loadout_storage = ecs.read_storage::(); - let lantern_opt = loadout_storage + let inventory_storage = ecs.read_storage::(); + let lantern_opt = inventory_storage .get(entity) - .and_then(|loadout| loadout.lantern.as_ref()) + .and_then(|inventory| inventory.equipped(EquipSlot::Lantern)) .and_then(|item| { if let comp::item::ItemKind::Lantern(l) = item.kind() { Some(l) @@ -108,7 +106,6 @@ pub fn handle_unmount(server: &mut Server, mounter: EcsEntity) { #[allow(clippy::nonminimal_bool)] // TODO: Pending review in #587 pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) { let ecs = &server.state.ecs(); - let ability_map = ecs.fetch::(); if let (Some(possessor), Some(possesse)) = ( ecs.entity_from_uid(possessor_uid.into()), ecs.entity_from_uid(possesse_uid.into()), @@ -173,18 +170,22 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) { .map(|c| c.send_fallible(ServerGeneral::SetPlayerEntity(possesse_uid))); // Put possess item into loadout - let mut loadouts = ecs.write_storage::(); - let mut loadout = loadouts + let mut inventories = ecs.write_storage::(); + let mut inventory = inventories .entry(possesse) - .expect("Could not read loadouts component while possessing") - .or_insert(comp::Loadout::default()); + .expect("Could not read inventory component while possessing") + .or_insert(Inventory::new_empty()); - let item = comp::Item::new_from_asset_expect("common.items.debug.possess"); - if let item::ItemKind::Tool(_) = item.kind() { - let debug_item = comp::ItemConfig::from((item, &*ability_map)); - let loadout = &mut *loadout; - std::mem::swap(&mut loadout.active_item, &mut loadout.second_item); - loadout.active_item = Some(debug_item); + let debug_item = comp::Item::new_from_asset_expect("common.items.debug.possess"); + if let item::ItemKind::Tool(_) = debug_item.kind() { + inventory + .swap( + Slot::Equip(EquipSlot::Mainhand), + Slot::Equip(EquipSlot::Offhand), + ) + .unwrap_none(); // Swapping main and offhand never results in leftover items + + inventory.replace_loadout_item(EquipSlot::Mainhand, Some(debug_item)); } // Remove will of the entity diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 6efd49f8d5..95a521b478 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -1,4 +1,8 @@ -use crate::{client::Client, Server, StateExt}; +use rand::Rng; +use specs::{join::Join, world::WorldExt, Builder, Entity as EcsEntity, WriteStorage}; +use tracing::{debug, error}; +use vek::{Rgb, Vec3}; + use common::{ comp::{ self, item, @@ -13,10 +17,8 @@ use common::{ use common_net::{msg::ServerGeneral, sync::WorldSyncExt}; use common_sys::state::State; use comp::LightEmitter; -use rand::Rng; -use specs::{join::Join, world::WorldExt, Builder, Entity as EcsEntity, WriteStorage}; -use tracing::{debug, error}; -use vek::{Rgb, Vec3}; + +use crate::{client::Client, Server, StateExt}; pub fn swap_lantern( storage: &mut WriteStorage, @@ -60,7 +62,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv match manip { comp::InventoryManip::Pickup(uid) => { let picked_up_item: Option; - let item_entity = if let (Some((item, item_entity)), Some(inv)) = ( + let item_entity = if let (Some((item, item_entity)), Some(mut inv)) = ( state .ecs() .entity_from_uid(uid.into()) @@ -96,10 +98,16 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv } } - // Attempt to add the item to the entity's inventory - match inv.push(item) { - None => Some(item_entity), - Some(_) => None, // Inventory was full + // First try to equip the picked up item + if let Err(returned_item) = inv.try_equip(item) { + // If we couldn't equip it (no empty slot for it or unequippable) then attempt + // to add the item to the entity's inventory + match inv.pickup_item(returned_item) { + Ok(_) => Some(item_entity), + Err(_) => None, // Inventory was full + } + } else { + Some(item_entity) } } else { // Item entity/component could not be found - most likely because the entity @@ -146,7 +154,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv }; if let Some(item) = comp::Item::try_reclaim_from_block(block) { - let (event, item_was_added) = if let Some(inv) = state + let (event, item_was_added) = if let Some(mut inv) = state .ecs() .write_storage::() .get_mut(entity) @@ -198,7 +206,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv comp::InventoryManip::Use(slot) => { let mut inventories = state.ecs().write_storage::(); - let inventory = if let Some(inventory) = inventories.get_mut(entity) { + let mut inventory = if let Some(inventory) = inventories.get_mut(entity) { inventory } else { error!( @@ -213,29 +221,32 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv let event = match slot { Slot::Inventory(slot) => { use item::ItemKind; + let (is_equippable, lantern_opt) = - inventory - .get(slot) - .map_or((false, None), |i| match i.kind() { - ItemKind::Tool(_) - | ItemKind::Armor { .. } - | ItemKind::Glider(_) => (true, None), - ItemKind::Lantern(lantern) => (true, Some(lantern)), - _ => (false, None), - }); + inventory.get(slot).map_or((false, None), |i| { + (i.kind().is_equippable(), match i.kind() { + ItemKind::Lantern(lantern) => Some(lantern), + _ => None, + }) + }); if is_equippable { - if let Some(mut loadout) = - state.ecs().write_storage::().get_mut(entity) - { - if let Some(lantern) = lantern_opt { - swap_lantern(&mut state.ecs().write_storage(), entity, &lantern); - } - let ability_map = state.ability_map(); - slot::equip(slot, inventory, &mut loadout, &ability_map); - Some(comp::InventoryUpdateEvent::Used) - } else { - None + if let Some(lantern) = lantern_opt { + swap_lantern(&mut state.ecs().write_storage(), entity, &lantern); } + if let Some(pos) = state.ecs().read_storage::().get(entity) { + if let Some(leftover_items) = inventory.equip(slot) { + dropped_items.extend(leftover_items.into_iter().map(|x| { + ( + *pos, + state + .read_component_copied::(entity) + .unwrap_or_default(), + x, + ) + })); + } + } + Some(comp::InventoryUpdateEvent::Used) } else if let Some(item) = inventory.take(slot) { match item.kind() { ItemKind::Consumable { kind, effect, .. } => { @@ -346,13 +357,13 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv }; if reinsert { - let _ = inventory.insert_or_stack(slot, item); + let _ = inventory.insert_or_stack_at(slot, item); } Some(comp::InventoryUpdateEvent::Used) }, _ => { - inventory.insert_or_stack(slot, item).unwrap(); + inventory.insert_or_stack_at(slot, item).unwrap(); None }, } @@ -361,23 +372,31 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv } }, Slot::Equip(slot) => { - if let Some(mut loadout) = - state.ecs().write_storage::().get_mut(entity) - { - if slot == slot::EquipSlot::Lantern { - snuff_lantern(&mut state.ecs().write_storage(), entity); - } - let ability_map = state.ability_map(); - slot::unequip(slot, inventory, &mut loadout, &ability_map); - Some(comp::InventoryUpdateEvent::Used) - } else { - error!(?entity, "Entity doesn't have a loadout, can't unequip..."); - None + if slot == slot::EquipSlot::Lantern { + snuff_lantern(&mut state.ecs().write_storage(), entity); } + + if let Some(pos) = state.ecs().read_storage::().get(entity) { + // Unequip the item, any items that no longer fit within the inventory (due + // to unequipping a bag for example) will be dropped on the floor + if let Ok(Some(leftover_items)) = inventory.unequip(slot) { + dropped_items.extend(leftover_items.into_iter().map(|x| { + ( + *pos, + state + .read_component_copied::(entity) + .unwrap_or_default(), + x, + ) + })); + } + } + Some(comp::InventoryUpdateEvent::Used) }, }; drop(inventories); + if let Some(effects) = maybe_effect { for effect in effects { state.apply_effect(entity, effect, None); @@ -390,20 +409,23 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv comp::InventoryManip::Swap(a, b) => { let ecs = state.ecs(); - let mut inventories = ecs.write_storage::(); - let mut loadouts = ecs.write_storage::(); - let inventory = inventories.get_mut(entity); - let mut loadout = loadouts.get_mut(entity); - let ability_map = state.ability_map(); - slot::swap(a, b, inventory, loadout.as_deref_mut(), &ability_map); - // slot::swap(a, b, inventory, loadout.as_mut().map(|x| &mut **x), - // &ability_map); - - // :/ - drop(loadouts); - drop(inventories); - drop(ability_map); + if let Some(pos) = ecs.read_storage::().get(entity) { + if let Some(mut inventory) = ecs.write_storage::().get_mut(entity) + { + if let Some(leftover_items) = inventory.swap(a, b) { + dropped_items.extend(leftover_items.into_iter().map(|x| { + ( + *pos, + state + .read_component_copied::(entity) + .unwrap_or_default(), + x, + ) + })); + } + } + } state.write_component( entity, @@ -412,20 +434,18 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv }, comp::InventoryManip::Drop(slot) => { - let ability_map = state.ability_map(); let item = match slot { Slot::Inventory(slot) => state .ecs() .write_storage::() .get_mut(entity) - .and_then(|inv| inv.remove(slot)), + .and_then(|mut inv| inv.remove(slot)), Slot::Equip(slot) => state .ecs() - .write_storage::() + .write_storage::() .get_mut(entity) - .and_then(|mut ldt| slot::loadout_remove(slot, &mut ldt, &ability_map)), + .and_then(|mut inv| inv.replace_loadout_item(slot, None)), }; - drop(ability_map); // FIXME: We should really require the drop and write to be atomic! if let (Some(mut item), Some(pos)) = @@ -447,13 +467,15 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv }, comp::InventoryManip::CraftRecipe(recipe) => { - if let Some(inv) = state + if let Some(mut inv) = state .ecs() .write_storage::() .get_mut(entity) { let recipe_book = default_recipe_book().read(); - let craft_result = recipe_book.get(&recipe).and_then(|r| r.perform(inv).ok()); + let craft_result = recipe_book + .get(&recipe) + .and_then(|r| r.perform(&mut inv).ok()); // FIXME: We should really require the drop and write to be atomic! if craft_result.is_some() { @@ -483,15 +505,11 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv // Drop items for (pos, ori, item) in dropped_items { - let vel = *ori.0 * 5.0 - + Vec3::unit_z() * 10.0 - + Vec3::::zero().map(|_| rand::thread_rng().gen::() - 0.5) * 4.0; - state .create_object(Default::default(), comp::object::Body::Pouch) - .with(comp::Pos(pos.0 + Vec3::unit_z() * 0.25)) + .with(comp::Pos(pos.0 + *ori.0 + Vec3::unit_z())) .with(item) - .with(comp::Vel(vel)) + .with(comp::Vel(Vec3::zero())) .build(); } @@ -572,10 +590,12 @@ fn within_pickup_range>( #[cfg(test)] mod tests { - use super::*; + use vek::Vec3; + use common::comp::Pos; use find_dist::*; - use vek::Vec3; + + use super::*; // Helper function #[allow(clippy::unnecessary_wraps)] diff --git a/server/src/events/player.rs b/server/src/events/player.rs index fa9cbc1b8b..03ac7dde10 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -162,11 +162,10 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event } fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity { - if let (Some(presences), Some(stats), Some(inventory), Some(loadout), updater) = ( + if let (Some(presences), Some(stats), Some(inventory), updater) = ( state.read_storage::().get(entity), state.read_storage::().get(entity), state.read_storage::().get(entity), - state.read_storage::().get(entity), state .ecs() .read_resource::(), @@ -174,7 +173,7 @@ fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity { if let PresenceKind::Character(character_id) = presences.kind { let waypoint_read = state.read_storage::(); let waypoint = waypoint_read.get(entity); - updater.update(character_id, stats, inventory, loadout, waypoint); + updater.update(character_id, stats, inventory, waypoint); } } diff --git a/server/src/lib.rs b/server/src/lib.rs index 9eb1f84565..13cf84f689 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -1,7 +1,7 @@ #![deny(unsafe_code)] #![allow(clippy::option_map_unit_fn)] #![deny(clippy::clone_on_ref_ptr)] -#![feature(bool_to_option, drain_filter, option_zip)] +#![feature(bool_to_option, drain_filter, option_unwrap_none, option_zip)] #![cfg_attr(not(feature = "worldgen"), feature(const_panic))] pub mod alias_validator; @@ -46,10 +46,13 @@ use crate::{ state_ext::StateExt, sys::sentinel::{DeletedEntities, TrackedComps}, }; +#[cfg(not(feature = "worldgen"))] +use common::grid::Grid; use common::{ assets::AssetExt, cmd::ChatCommand, comp, + comp::CharacterAbility, event::{EventBus, ServerEvent}, outcome::Outcome, recipe::default_recipe_book, @@ -86,6 +89,7 @@ use test_world::{IndexOwned, World}; use tracing::{debug, error, info, trace}; use uvth::{ThreadPool, ThreadPoolBuilder}; use vek::*; + #[cfg(feature = "worldgen")] use world::{ sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP}, @@ -168,13 +172,13 @@ impl Server { .ecs_mut() .insert(CharacterUpdater::new(&persistence_db_dir)?); - let ability_map = comp::item::tool::AbilityMap::load_expect_cloned( + let ability_map = comp::item::tool::AbilityMap::::load_expect_cloned( "common.abilities.weapon_ability_manifest", ); + state.ecs_mut().insert(ability_map); state .ecs_mut() - .insert(CharacterLoader::new(&persistence_db_dir, &ability_map)?); - state.ecs_mut().insert(ability_map); + .insert(CharacterLoader::new(&persistence_db_dir)?); state.ecs_mut().insert(Vec::::new()); // System timers for performance monitoring @@ -256,10 +260,10 @@ impl Server { let map = WorldMapMsg { dimensions_lg: Vec2::zero(), max_height: 1.0, - rgba: vec![0], + rgba: Grid::new(Vec2::new(1, 1), 1), horizons: [(vec![0], vec![0]), (vec![0], vec![0])], sea_level: 0.0, - alt: vec![30], + alt: Grid::new(Vec2::new(1, 1), 1), sites: Vec::new(), }; diff --git a/server/src/migrations/2020-11-28-205542_item-storage/down.sql b/server/src/migrations/2020-11-28-205542_item-storage/down.sql new file mode 100644 index 0000000000..291a97c5ce --- /dev/null +++ b/server/src/migrations/2020-11-28-205542_item-storage/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` \ No newline at end of file diff --git a/server/src/migrations/2020-11-28-205542_item-storage/up.sql b/server/src/migrations/2020-11-28-205542_item-storage/up.sql new file mode 100644 index 0000000000..559d2c2a51 --- /dev/null +++ b/server/src/migrations/2020-11-28-205542_item-storage/up.sql @@ -0,0 +1,94 @@ +-- +-- Step 1 - Update renamed ring loadout position +-- +UPDATE item +SET position = 'ring1' +WHERE position = 'ring'; + +-- +-- Step 2 - Give every existing player 3x 6 slot bags in their bag1-3 loadout slots +-- + +CREATE TEMP TABLE _temp_loadout_containers +AS +SELECT item_id +FROM item +WHERE item_definition_id = 'veloren.core.pseudo_containers.loadout'; + +-- Insert an entity ID for each new bag item (3 per existing loadout) +WITH loadout_containers AS ( + SELECT 1 + FROM item + WHERE item_definition_id = 'veloren.core.pseudo_containers.loadout') +INSERT +INTO entity +SELECT NULL FROM loadout_containers +UNION ALL +SELECT NULL FROM loadout_containers +UNION ALL +SELECT NULL FROM loadout_containers; + +CREATE TEMP TABLE _temp_new_bag_item_ids AS +SELECT item_id AS loadout_container_item_id, + ROW_NUMBER() OVER(ORDER BY item_id) AS temp_id_bag1, + ROW_NUMBER() OVER(ORDER BY item_id) + (SELECT COUNT(1) FROM _temp_loadout_containers) AS temp_id_bag2, + ROW_NUMBER() OVER(ORDER BY item_id) + (SELECT COUNT(1) * 2 FROM _temp_loadout_containers) AS temp_id_bag3 +FROM item +WHERE item_definition_id = 'veloren.core.pseudo_containers.loadout'; + +INSERT INTO item +SELECT (SELECT MAX(entity_id) - temp_id_bag1 + 1 from entity) as new_item_id, + loadout_container_item_id, + 'common.items.armor.bag.tiny_leather_pouch', + 1, + 'bag1' +FROM _temp_new_bag_item_ids; + +INSERT INTO item +SELECT (SELECT MAX(entity_id) - temp_id_bag2 + 1 from entity) as new_item_id, + loadout_container_item_id, + 'common.items.armor.bag.tiny_leather_pouch', + 1, + 'bag2' +FROM _temp_new_bag_item_ids; + +INSERT INTO item +SELECT (SELECT MAX(entity_id) - temp_id_bag3 + 1 from entity) as new_item_id, + loadout_container_item_id, + 'common.items.armor.bag.tiny_leather_pouch', + 1, + 'bag3' +FROM _temp_new_bag_item_ids; + +-- +-- Step 3 - Update the position column for all existing inventory items, putting the first 18 +-- items in the inventory's built in slots, and the next 18 items inside the 3 new bags +-- + +WITH inventory_items AS ( + SELECT i2.item_id, + i2.parent_container_item_id, + CAST(i2.position AS NUMBER) AS position + FROM item i + JOIN item i2 ON (i2.parent_container_item_id = i.item_id) + WHERE i.item_definition_id = 'veloren.core.pseudo_containers.inventory' +), + new_positions AS ( + SELECT item_id, + parent_container_item_id, + position, + -- Slots 0 - 17 have loadout_idx 0 (built-in inventory slots) + -- Slots 18 - 23 have loadout_idx 15 (bag1 loadout slot) + -- Slots 24 - 29 have loadout_idx 16 (bag2 loadout slot) + -- Slots 30 - 35 have loadout_idx 17 (bag3 loadout slot) + (position / 18) * ((position / 6) + 12) as loadout_idx, + -- Slots 0-17 have their existing position as their slot_idx + -- Slots 18-35 go into slots 0-5 of the 3 new bags + CASE WHEN position < 18 THEN position ELSE (position % 18) % 6 END as slot_idx + FROM inventory_items + ) +UPDATE item +SET position = ( SELECT '{"loadout_idx":' || CAST(loadout_idx as VARCHAR) || ',"slot_idx":' || CAST(slot_idx as VARCHAR) || '}' + FROM new_positions + WHERE item_id = item.item_id) +WHERE item_id IN (SELECT item_id FROM new_positions); \ No newline at end of file diff --git a/server/src/persistence/character.rs b/server/src/persistence/character.rs index eb4c4fc52e..b9281ab569 100644 --- a/server/src/persistence/character.rs +++ b/server/src/persistence/character.rs @@ -9,6 +9,7 @@ extern crate diesel; use super::{error::Error, models::*, schema, VelorenTransaction}; use crate::{ comp, + comp::Inventory, persistence::{ character::conversions::{ convert_body_from_database, convert_body_to_database_json, @@ -22,10 +23,7 @@ use crate::{ PersistedComponents, }, }; -use common::{ - character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER}, - comp::item::tool::AbilityMap, -}; +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; @@ -59,7 +57,6 @@ pub fn load_character_data( requesting_player_uuid: String, char_id: CharacterId, connection: VelorenTransaction, - map: &AbilityMap, ) -> CharacterDataResult { use schema::{body::dsl::*, character::dsl::*, item::dsl::*}; @@ -106,8 +103,7 @@ pub fn load_character_data( 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, map)?, + convert_inventory_from_database_items(&inventory_items, &loadout_items)?, char_waypoint, )) } @@ -122,7 +118,6 @@ pub fn load_character_data( pub fn load_character_list( player_uuid_: &str, connection: VelorenTransaction, - map: &AbilityMap, ) -> CharacterListResult { use schema::{body::dsl::*, character::dsl::*, item::dsl::*, stats::dsl::*}; @@ -155,13 +150,13 @@ pub fn load_character_list( .filter(parent_container_item_id.eq(loadout_container_id)) .load::(&*connection)?; - let loadout = convert_loadout_from_database_items(&loadout_items, map)?; + let loadout = convert_loadout_from_database_items(&loadout_items)?; Ok(CharacterItem { character: char, body: char_body, level: char_stats.level as usize, - loadout, + inventory: Inventory::new_with_loadout(loadout), }) }) .collect() @@ -172,7 +167,6 @@ pub fn create_character( character_alias: &str, persisted_components: PersistedComponents, connection: VelorenTransaction, - map: &AbilityMap, ) -> CharacterCreationResult { use schema::item::dsl::*; @@ -180,7 +174,7 @@ pub fn create_character( use schema::{body, character}; - let (body, stats, inventory, loadout, waypoint) = persisted_components; + let (body, stats, inventory, waypoint) = persisted_components; // Fetch new entity IDs for character, inventory and loadout let mut new_entity_ids = get_new_entity_ids(connection, |next_id| next_id + 3)?; @@ -277,7 +271,6 @@ pub fn create_character( get_new_entity_ids(connection, |mut next_id| { let (inserts_, _deletes) = convert_items_to_database_items( - &loadout, loadout_container_id, &inventory, inventory_container_id, @@ -303,7 +296,7 @@ pub fn create_character( ))); } - load_character_list(uuid, connection, map).map(|list| (character_id, list)) + load_character_list(uuid, connection).map(|list| (character_id, list)) } /// Delete a character. Returns the updated character list. @@ -311,7 +304,6 @@ pub fn delete_character( requesting_player_uuid: &str, char_id: CharacterId, connection: VelorenTransaction, - map: &AbilityMap, ) -> CharacterListResult { use schema::{body::dsl::*, character::dsl::*, stats::dsl::*}; @@ -392,7 +384,7 @@ pub fn delete_character( ))); } - load_character_list(requesting_player_uuid, connection, map) + load_character_list(requesting_player_uuid, connection) } /// Before creating a character, we ensure that the limit on the number of @@ -534,7 +526,6 @@ pub fn update( char_id: CharacterId, char_stats: comp::Stats, inventory: comp::Inventory, - loadout: comp::Loadout, char_waypoint: Option, connection: VelorenTransaction, ) -> Result>, Error> { @@ -548,7 +539,6 @@ pub fn update( // 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, diff --git a/server/src/persistence/character/conversions.rs b/server/src/persistence/character/conversions.rs index 0ffb0c6910..f0e7416ae3 100644 --- a/server/src/persistence/character/conversions.rs +++ b/server/src/persistence/character/conversions.rs @@ -9,8 +9,14 @@ use crate::persistence::{ }; use common::{ character::CharacterId, - comp::{item::tool::AbilityMap, Body as CompBody, Waypoint, *}, - loadout_builder, + comp::{ + inventory::{ + loadout::{Loadout, LoadoutError}, + loadout_builder::LoadoutBuilder, + slot::InvSlotId, + }, + Body as CompBody, Waypoint, *, + }, resources::Time, }; use core::{convert::TryFrom, num::NonZeroU64}; @@ -24,42 +30,29 @@ pub struct ItemModelPair { /// The left vector contains all item rows to upsert; the right-hand vector /// contains all item rows to delete (by parent ID and position). +/// +/// NOTE: This method does not yet handle persisting nested items within +/// inventories. Although loadout items do store items inside them this does +/// not currently utilise `parent_container_id` - all loadout items have the +/// loadout pseudo-container as their parent. 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()), - ("glider", loadout.glider.as_ref()), - ]; - - let loadout = loadout - .iter() - .map(|&(slot, item)| (slot.to_string(), item, loadout_container_id)); + let loadout = inventory + .loadout_items_with_persistence_key() + .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)); + let inventory = inventory.slots_with_id().map(|(pos, item)| { + ( + serde_json::to_string(&pos).expect("failed to serialize InventorySlotPos"), + item.as_ref(), + inventory_container_id, + ) + }); // Construct new items. inventory.chain(loadout) @@ -209,9 +202,23 @@ pub fn convert_stats_to_database( }) } -pub fn convert_inventory_from_database_items(database_items: &[Item]) -> Result { - let mut inventory = Inventory::new_empty(); - for db_item in database_items.iter() { +pub fn convert_inventory_from_database_items( + inventory_items: &[Item], + loadout_items: &[Item], +) -> Result { + // Loadout items must be loaded before inventory items since loadout items + // provide inventory slots. Since items stored inside loadout items actually + // have their parent_container_item_id as the loadout pseudo-container we rely + // on populating the loadout items first, and then inserting the items into the + // inventory at the correct position. When we want to support items inside the + // player's inventory containing other items (such as "right click to + // unwrap" gifts perhaps) then we will need to refactor inventory/loadout + // persistence to traverse the tree of items and load them from the root + // down. + let loadout = convert_loadout_from_database_items(loadout_items)?; + let mut inventory = Inventory::new_with_loadout(loadout); + + for db_item in inventory_items.iter() { let mut item = get_item_from_asset(db_item.item_definition_id.as_str())?; // NOTE: Since this is freshly loaded, the atomic is *unique.* @@ -237,17 +244,20 @@ pub fn convert_inventory_from_database_items(database_items: &[Item]) -> Result< // Insert item into inventory // Slot position - let slot = &db_item.position.parse::().map_err(|_| { + let slot: InvSlotId = serde_json::from_str(&db_item.position).map_err(|_| { Error::ConversionError(format!( - "Failed to parse item position: {}", + "Failed to parse item position: {:?}", &db_item.position )) })?; - let insert_res = inventory.insert(*slot, item).map_err(|_| { + let insert_res = inventory.insert_at(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()) + Error::ConversionError(format!( + "Error inserting item into inventory, position: {:?}", + slot + )) })?; if insert_res.is_some() { @@ -263,11 +273,10 @@ pub fn convert_inventory_from_database_items(database_items: &[Item]) -> Result< Ok(inventory) } -pub fn convert_loadout_from_database_items( - database_items: &[Item], - map: &AbilityMap, -) -> Result { - let mut loadout = loadout_builder::LoadoutBuilder::new(); +pub fn convert_loadout_from_database_items(database_items: &[Item]) -> Result { + let loadout_builder = LoadoutBuilder::new(); + let mut loadout = loadout_builder.build(); + for db_item in database_items.iter() { let item = get_item_from_asset(db_item.item_definition_id.as_str())?; // NOTE: item id is currently *unique*, so we can store the ID safely. @@ -276,32 +285,17 @@ pub fn convert_loadout_from_database_items( |_| 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, map)))), - "second_item" => loadout = loadout.second_item(Some(ItemConfig::from((item, map)))), - "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)), - "glider" => loadout = loadout.glider(Some(item)), - _ => { - return Err(Error::ConversionError(format!( - "Unknown loadout position on item: {}", - db_item.position.as_str() - ))); - }, - } + loadout + .set_item_at_slot_using_persistence_key(&db_item.position, item) + .map_err(|err| match err { + LoadoutError::InvalidPersistenceKey => Error::ConversionError(format!( + "Invalid persistence key: {}", + &db_item.position + )), + })?; } - Ok(loadout.build()) + Ok(loadout) } pub fn convert_body_from_database(body: &Body) -> Result { diff --git a/server/src/persistence/character_loader.rs b/server/src/persistence/character_loader.rs index c73a8425af..c1b4d8ffcf 100644 --- a/server/src/persistence/character_loader.rs +++ b/server/src/persistence/character_loader.rs @@ -3,10 +3,7 @@ use crate::persistence::{ error::Error, establish_connection, PersistedComponents, }; -use common::{ - character::{CharacterId, CharacterItem}, - comp::item::tool::AbilityMap, -}; +use common::character::{CharacterId, CharacterItem}; use crossbeam_channel::{self, TryIter}; use std::path::Path; use tracing::error; @@ -70,14 +67,12 @@ pub struct CharacterLoader { } impl CharacterLoader { - pub fn new(db_dir: &Path, map: &AbilityMap) -> diesel::QueryResult { + pub fn new(db_dir: &Path) -> diesel::QueryResult { let (update_tx, internal_rx) = crossbeam_channel::unbounded::(); let (internal_tx, update_rx) = crossbeam_channel::unbounded::(); let mut conn = establish_connection(db_dir)?; - let map = map.clone(); - std::thread::spawn(move || { for request in internal_rx { let (entity, kind) = request; @@ -97,7 +92,6 @@ impl CharacterLoader { &character_alias, persisted_components, txn, - &map, ) }, )), @@ -105,19 +99,19 @@ impl CharacterLoader { player_uuid, character_id, } => CharacterLoaderResponseKind::CharacterList(conn.transaction( - |txn| delete_character(&player_uuid, character_id, txn, &map), + |txn| delete_character(&player_uuid, character_id, txn), )), CharacterLoaderRequestKind::LoadCharacterList { player_uuid } => { - CharacterLoaderResponseKind::CharacterList(conn.transaction( - |txn| load_character_list(&player_uuid, txn, &map), - )) + CharacterLoaderResponseKind::CharacterList( + conn.transaction(|txn| load_character_list(&player_uuid, txn)), + ) }, CharacterLoaderRequestKind::LoadCharacterData { player_uuid, character_id, } => { let result = conn.transaction(|txn| { - load_character_data(player_uuid, character_id, txn, &map) + load_character_data(player_uuid, character_id, txn) }); if result.is_err() { error!( diff --git a/server/src/persistence/character_updater.rs b/server/src/persistence/character_updater.rs index ca58c0ea96..902b207d42 100644 --- a/server/src/persistence/character_updater.rs +++ b/server/src/persistence/character_updater.rs @@ -5,12 +5,7 @@ use crate::persistence::{establish_connection, VelorenConnection}; use std::{path::Path, sync::Arc}; use tracing::{error, trace}; -pub type CharacterUpdateData = ( - comp::Stats, - comp::Inventory, - comp::Loadout, - Option, -); +pub type CharacterUpdateData = (comp::Stats, comp::Inventory, Option); /// A unidirectional messaging resource for saving characters in a /// background thread. @@ -51,21 +46,15 @@ impl CharacterUpdater { CharacterId, &'a comp::Stats, &'a comp::Inventory, - &'a comp::Loadout, Option<&'a comp::Waypoint>, ), >, ) { let updates = updates - .map(|(character_id, stats, inventory, loadout, waypoint)| { + .map(|(character_id, stats, inventory, waypoint)| { ( character_id, - ( - stats.clone(), - inventory.clone(), - loadout.clone(), - waypoint.cloned(), - ), + (stats.clone(), inventory.clone(), waypoint.cloned()), ) }) .collect::>(); @@ -81,16 +70,9 @@ impl CharacterUpdater { character_id: CharacterId, stats: &comp::Stats, inventory: &comp::Inventory, - loadout: &comp::Loadout, waypoint: Option<&comp::Waypoint>, ) { - self.batch_update(std::iter::once(( - character_id, - stats, - inventory, - loadout, - waypoint, - ))); + self.batch_update(std::iter::once((character_id, stats, inventory, waypoint))); } } @@ -101,12 +83,11 @@ fn execute_batch_update( let mut inserted_items = Vec::>::new(); if let Err(e) = connection.transaction::<_, super::error::Error, _>(|txn| { - for (character_id, (stats, inventory, loadout, waypoint)) in updates { + for (character_id, (stats, inventory, waypoint)) in updates { inserted_items.append(&mut super::character::update( character_id, stats, inventory, - loadout, waypoint, txn, )?); diff --git a/server/src/persistence/mod.rs b/server/src/persistence/mod.rs index 89feb78ace..17f59c89c5 100644 --- a/server/src/persistence/mod.rs +++ b/server/src/persistence/mod.rs @@ -25,7 +25,6 @@ pub type PersistedComponents = ( comp::Body, comp::Stats, comp::Inventory, - comp::Loadout, Option, ); diff --git a/server/src/rtsim/entity.rs b/server/src/rtsim/entity.rs index eb517d9b48..e5737de144 100644 --- a/server/src/rtsim/entity.rs +++ b/server/src/rtsim/entity.rs @@ -1,5 +1,5 @@ use super::*; -use common::{comp::item::tool::AbilityMap, store::Id, terrain::TerrainGrid, LoadoutBuilder}; +use common::{comp::inventory::loadout_builder::LoadoutBuilder, store::Id, terrain::TerrainGrid}; use world::{ civ::{Site, Track}, util::RandomPerm, @@ -62,7 +62,7 @@ impl Entity { (self.rng(PERM_LEVEL).gen::().powi(2) * 15.0).ceil() as u32 } - pub fn get_loadout(&self, ability_map: &AbilityMap) -> comp::Loadout { + pub fn get_loadout(&self) -> comp::inventory::loadout::Loadout { let mut rng = self.rng(PERM_LOADOUT); let main_tool = comp::Item::new_from_asset_expect( (&[ @@ -114,7 +114,7 @@ impl Entity { "common.items.armor.shoulder.leather_0", )); - LoadoutBuilder::build_loadout(self.get_body(), Some(main_tool), ability_map, None) + LoadoutBuilder::build_loadout(self.get_body(), Some(main_tool), None) .back(back) .lantern(lantern) .chest(chest) diff --git a/server/src/rtsim/tick.rs b/server/src/rtsim/tick.rs index 583c036874..5302ecdd6c 100644 --- a/server/src/rtsim/tick.rs +++ b/server/src/rtsim/tick.rs @@ -3,6 +3,7 @@ use super::*; use common::{ comp, + comp::inventory::loadout_builder::LoadoutBuilder, event::{EventBus, ServerEvent}, resources::DeltaTime, terrain::TerrainGrid, @@ -25,7 +26,6 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, comp::Pos>, ReadStorage<'a, RtSimEntity>, WriteStorage<'a, comp::Agent>, - ReadExpect<'a, comp::item::tool::AbilityMap>, ); fn run( @@ -40,7 +40,6 @@ impl<'a> System<'a> for Sys { positions, rtsim_entities, mut agents, - ability_map, ): Self::SystemData, ) { let rtsim = &mut *rtsim; @@ -101,8 +100,8 @@ impl<'a> System<'a> for Sys { stats: comp::Stats::new(entity.get_name(), body).with_level(entity.get_level()), health: comp::Health::new(body, 10), loadout: match body { - comp::Body::Humanoid(_) => entity.get_loadout(&ability_map), - _ => comp::Loadout::default(), + comp::Body::Humanoid(_) => entity.get_loadout(), + _ => LoadoutBuilder::new().build(), }, body, agent: Some(comp::Agent::new( diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 318b94f7d6..21d99a02d1 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -5,6 +5,7 @@ use crate::{ use common::{ character::CharacterId, comp, + comp::Inventory, effect::Effect, uid::{Uid, UidAllocator}, util::Dir, @@ -31,7 +32,7 @@ pub trait StateExt { pos: comp::Pos, stats: comp::Stats, health: comp::Health, - loadout: comp::Loadout, + inventory: comp::Inventory, body: comp::Body, ) -> EcsEntityBuilder; /// Build a static object entity @@ -90,8 +91,8 @@ impl StateExt for State { .map(|mut stats| stats.exp.change_by(xp)); }, Effect::Damage(damage) => { - let loadouts = self.ecs().read_storage::(); - let change = damage.modify_damage(loadouts.get(entity), source); + let inventories = self.ecs().read_storage::(); + let change = damage.modify_damage(inventories.get(entity), source); self.ecs() .write_storage::() .get_mut(entity) @@ -118,7 +119,7 @@ impl StateExt for State { pos: comp::Pos, stats: comp::Stats, health: comp::Health, - loadout: comp::Loadout, + inventory: comp::Inventory, body: comp::Body, ) -> EcsEntityBuilder { self.ecs_mut() @@ -147,7 +148,7 @@ impl StateExt for State { .with(comp::Energy::new(body.base_energy())) .with(comp::Gravity(1.0)) .with(comp::CharacterState::default()) - .with(loadout) + .with(inventory) .with(comp::Buffs::default()) } @@ -258,7 +259,7 @@ impl StateExt for State { } fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents) { - let (body, stats, inventory, loadout, waypoint) = components; + let (body, stats, inventory, waypoint) = components; if let Some(player_uid) = self.read_component_copied::(entity) { // Notify clients of a player list update @@ -281,7 +282,6 @@ impl StateExt for State { ); self.write_component(entity, stats); self.write_component(entity, inventory); - self.write_component(entity, loadout); self.write_component( entity, comp::InventoryUpdate::new(comp::InventoryUpdateEvent::default()), diff --git a/server/src/sys/msg/character_screen.rs b/server/src/sys/msg/character_screen.rs index ab901c8bf0..b3dd3ca57a 100644 --- a/server/src/sys/msg/character_screen.rs +++ b/server/src/sys/msg/character_screen.rs @@ -4,7 +4,7 @@ use crate::{ persistence::character_loader::CharacterLoader, presence::Presence, EditableSettings, }; use common::{ - comp::{item::tool::AbilityMap, ChatType, Player, UnresolvedChatMsg}, + comp::{ChatType, Player, UnresolvedChatMsg}, event::{EventBus, ServerEvent}, span, uid::Uid, @@ -28,7 +28,6 @@ impl Sys { editable_settings: &ReadExpect<'_, EditableSettings>, alias_validator: &ReadExpect<'_, AliasValidator>, msg: ClientGeneral, - map: &AbilityMap, ) -> Result<(), crate::error::Error> { match msg { // Request spectator state @@ -103,7 +102,6 @@ impl Sys { tool, body, character_loader, - map, ); } }, @@ -137,7 +135,6 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Presence>, ReadExpect<'a, EditableSettings>, ReadExpect<'a, AliasValidator>, - ReadExpect<'a, AbilityMap>, ); fn run( @@ -153,7 +150,6 @@ impl<'a> System<'a> for Sys { presences, editable_settings, alias_validator, - map, ): Self::SystemData, ) { span!(_guard, "run", "msg::character_screen::Sys::run"); @@ -176,7 +172,6 @@ impl<'a> System<'a> for Sys { &editable_settings, &alias_validator, msg, - &map, ) }); } diff --git a/server/src/sys/persistence.rs b/server/src/sys/persistence.rs index ac7053e061..2aecd80f75 100644 --- a/server/src/sys/persistence.rs +++ b/server/src/sys/persistence.rs @@ -4,7 +4,7 @@ use crate::{ sys::{SysScheduler, SysTimer}, }; use common::{ - comp::{Inventory, Loadout, Stats, Waypoint}, + comp::{Inventory, Stats, Waypoint}, span, }; use common_net::msg::PresenceKind; @@ -18,7 +18,6 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Presence>, ReadStorage<'a, Stats>, ReadStorage<'a, Inventory>, - ReadStorage<'a, Loadout>, ReadStorage<'a, Waypoint>, ReadExpect<'a, character_updater::CharacterUpdater>, Write<'a, SysScheduler>, @@ -31,7 +30,6 @@ impl<'a> System<'a> for Sys { presences, player_stats, player_inventories, - player_loadouts, player_waypoint, updater, mut scheduler, @@ -46,15 +44,12 @@ impl<'a> System<'a> for Sys { &presences, &player_stats, &player_inventories, - &player_loadouts, player_waypoint.maybe(), ) .join() .filter_map( - |(presence, stats, inventory, loadout, waypoint)| match presence.kind { - PresenceKind::Character(id) => { - Some((id, stats, inventory, loadout, waypoint)) - }, + |(presence, stats, inventory, waypoint)| match presence.kind { + PresenceKind::Character(id) => Some((id, stats, inventory, waypoint)), PresenceKind::Spectator => None, }, ), diff --git a/server/src/sys/sentinel.rs b/server/src/sys/sentinel.rs index 98a8c096a2..f3c5daee58 100644 --- a/server/src/sys/sentinel.rs +++ b/server/src/sys/sentinel.rs @@ -2,7 +2,7 @@ use super::SysTimer; use common::{ comp::{ Auras, BeamSegment, Body, Buffs, CanBuild, CharacterState, Collider, Energy, Gravity, - Group, Health, Item, LightEmitter, Loadout, Mass, MountState, Mounting, Ori, Player, Pos, + Group, Health, Inventory, Item, LightEmitter, Mass, MountState, Mounting, Ori, Player, Pos, Scale, Shockwave, Stats, Sticky, Vel, }, span, @@ -62,7 +62,7 @@ pub struct TrackedComps<'a> { pub collider: ReadStorage<'a, Collider>, pub sticky: ReadStorage<'a, Sticky>, pub gravity: ReadStorage<'a, Gravity>, - pub loadout: ReadStorage<'a, Loadout>, + pub inventory: ReadStorage<'a, Inventory>, pub character_state: ReadStorage<'a, CharacterState>, pub shockwave: ReadStorage<'a, Shockwave>, pub beam_segment: ReadStorage<'a, BeamSegment>, @@ -145,7 +145,7 @@ impl<'a> TrackedComps<'a> { .get(entity) .copied() .map(|c| comps.push(c.into())); - self.loadout + self.inventory .get(entity) .cloned() .map(|c| comps.push(c.into())); @@ -181,6 +181,7 @@ pub struct ReadTrackers<'a> { pub health: ReadExpect<'a, UpdateTracker>, pub can_build: ReadExpect<'a, UpdateTracker>, pub light_emitter: ReadExpect<'a, UpdateTracker>, + pub inventory: ReadExpect<'a, UpdateTracker>, pub item: ReadExpect<'a, UpdateTracker>, pub scale: ReadExpect<'a, UpdateTracker>, pub mounting: ReadExpect<'a, UpdateTracker>, @@ -190,7 +191,6 @@ pub struct ReadTrackers<'a> { pub collider: ReadExpect<'a, UpdateTracker>, pub sticky: ReadExpect<'a, UpdateTracker>, pub gravity: ReadExpect<'a, UpdateTracker>, - pub loadout: ReadExpect<'a, UpdateTracker>, pub character_state: ReadExpect<'a, UpdateTracker>, pub shockwave: ReadExpect<'a, UpdateTracker>, pub beam_segment: ReadExpect<'a, UpdateTracker>, @@ -228,7 +228,7 @@ impl<'a> ReadTrackers<'a> { .with_component(&comps.uid, &*self.collider, &comps.collider, filter) .with_component(&comps.uid, &*self.sticky, &comps.sticky, filter) .with_component(&comps.uid, &*self.gravity, &comps.gravity, filter) - .with_component(&comps.uid, &*self.loadout, &comps.loadout, filter) + .with_component(&comps.uid, &*self.inventory, &comps.inventory, filter) .with_component( &comps.uid, &*self.character_state, @@ -263,7 +263,7 @@ pub struct WriteTrackers<'a> { collider: WriteExpect<'a, UpdateTracker>, sticky: WriteExpect<'a, UpdateTracker>, gravity: WriteExpect<'a, UpdateTracker>, - loadout: WriteExpect<'a, UpdateTracker>, + inventory: WriteExpect<'a, UpdateTracker>, character_state: WriteExpect<'a, UpdateTracker>, shockwave: WriteExpect<'a, UpdateTracker>, beam: WriteExpect<'a, UpdateTracker>, @@ -290,7 +290,7 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) { trackers.collider.record_changes(&comps.collider); trackers.sticky.record_changes(&comps.sticky); trackers.gravity.record_changes(&comps.gravity); - trackers.loadout.record_changes(&comps.loadout); + trackers.inventory.record_changes(&comps.inventory); trackers .character_state .record_changes(&comps.character_state); @@ -355,7 +355,7 @@ pub fn register_trackers(world: &mut World) { world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); - world.register_tracker::(); + world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 8e1ed95b41..5371387c9e 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -3,7 +3,7 @@ use crate::{ chunk_generator::ChunkGenerator, client::Client, presence::Presence, rtsim::RtSim, Tick, }; use common::{ - comp::{self, bird_medium, item::tool::AbilityMap, Alignment, Pos}, + comp::{self, bird_medium, Alignment, Pos}, event::{EventBus, ServerEvent}, generation::get_npc_name, npc::NPC_NAMES, @@ -14,7 +14,7 @@ use common::{ use common_net::msg::ServerGeneral; use common_sys::state::TerrainChanges; use rand::Rng; -use specs::{Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect}; +use specs::{Join, Read, ReadStorage, System, Write, WriteExpect}; use std::sync::Arc; use vek::*; @@ -38,7 +38,6 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Pos>, ReadStorage<'a, Presence>, ReadStorage<'a, Client>, - ReadExpect<'a, AbilityMap>, ); fn run( @@ -54,7 +53,6 @@ impl<'a> System<'a> for Sys { positions, presences, clients, - map, ): Self::SystemData, ) { span!(_guard, "run", "terrain::Sys::run"); @@ -162,7 +160,7 @@ impl<'a> System<'a> for Sys { let config = entity.config; - let loadout = LoadoutBuilder::build_loadout(body, main_tool, &map, config).build(); + let loadout = LoadoutBuilder::build_loadout(body, main_tool, config).build(); let health = comp::Health::new(stats.body_type, stats.level.level()); @@ -195,7 +193,10 @@ impl<'a> System<'a> for Sys { Some(entity.pos), can_speak, &body, - matches!(config, Some(common::loadout_builder::LoadoutConfig::Guard)), + matches!( + config, + Some(comp::inventory::loadout_builder::LoadoutConfig::Guard) + ), )) } else { None diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 34f846f99c..463d1554c7 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -73,6 +73,7 @@ glsl-include = "0.3.1" guillotiere = "0.6" hashbrown = {version = "0.9", features = ["rayon", "serde", "nightly"]} image = {version = "0.23.12", default-features = false, features = ["ico", "png"]} +lazy_static = "1.4.0" native-dialog = { version = "0.4.2", default-features = false, optional = true } num = "0.3.1" ordered-float = { version = "2.0.1", default-features = false } diff --git a/voxygen/src/audio/sfx/event_mapper/combat/mod.rs b/voxygen/src/audio/sfx/event_mapper/combat/mod.rs index cfdf3e8175..2f3a16cc7d 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/mod.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/mod.rs @@ -10,7 +10,10 @@ use super::EventMapper; use client::Client; use common::{ - comp::{item::ItemKind, CharacterAbilityType, CharacterState, Loadout, Pos}, + comp::{ + inventory::slot::EquipSlot, item::ItemKind, CharacterAbilityType, CharacterState, + Inventory, Pos, + }, terrain::TerrainChunk, vol::ReadVol, }; @@ -56,10 +59,10 @@ impl EventMapper for CombatEventMapper { let focus_off = camera.get_focus_pos().map(f32::trunc); let cam_pos = camera.dependents().cam_pos + focus_off; - for (entity, pos, loadout, character) in ( + for (entity, pos, inventory, character) in ( &ecs.entities(), &ecs.read_storage::(), - ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), ) .join() @@ -68,7 +71,9 @@ impl EventMapper for CombatEventMapper { if let Some(character) = character { let sfx_state = self.event_history.entry(entity).or_default(); - let mapped_event = Self::map_event(character, sfx_state, loadout); + let mapped_event = inventory.map_or(SfxEvent::Idle, |inv| { + Self::map_event(character, sfx_state, &inv) + }); // Check for SFX config entry for this movement if Self::should_emit(sfx_state, triggers.get_key_value(&mapped_event)) { @@ -138,30 +143,28 @@ impl CombatEventMapper { fn map_event( character_state: &CharacterState, previous_state: &PreviousEntityState, - loadout: Option<&Loadout>, + inventory: &Inventory, ) -> SfxEvent { - if let Some(active_loadout) = loadout { - 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), - 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(data.kind)), - (true, false, false) => Some(SfxEvent::Unwield(data.kind)), - _ => None, - } { - return wield_event; - } + if let Some(item) = inventory.equipped(EquipSlot::Mainhand) { + if let ItemKind::Tool(data) = item.kind() { + if character_state.is_attack() { + return SfxEvent::Attack( + CharacterAbilityType::from(character_state), + 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(data.kind)), + (true, false, false) => Some(SfxEvent::Unwield(data.kind)), + _ => None, + } { + return wield_event; } - // Check for attacking states } + // Check for attacking states } SfxEvent::Idle diff --git a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs index 677d942164..f71a1745bf 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs @@ -1,24 +1,22 @@ use super::*; use crate::audio::sfx::SfxEvent; use common::{ - comp::{item::tool::ToolKind, CharacterAbilityType, CharacterState, Item, ItemConfig, Loadout}, + comp::{ + inventory::loadout_builder::LoadoutBuilder, item::tool::ToolKind, CharacterAbilityType, + CharacterState, Item, + }, states, }; use std::time::{Duration, Instant}; #[test] fn maps_wield_while_equipping() { - let loadout = Loadout { - active_item: Some(ItemConfig { - item: Item::new_from_asset_expect("common.items.weapons.axe.starter_axe"), - ability1: None, - ability2: None, - ability3: None, - block_ability: None, - dodge_ability: None, - }), - ..Default::default() - }; + let loadout = LoadoutBuilder::new() + .active_item(Some(Item::new_from_asset_expect( + "common.items.weapons.axe.starter_axe", + ))) + .build(); + let inventory = Inventory::new_with_loadout(loadout); let result = CombatEventMapper::map_event( &CharacterState::Equipping(states::equipping::Data { @@ -32,7 +30,7 @@ fn maps_wield_while_equipping() { time: Instant::now(), weapon_drawn: false, }, - Some(&loadout), + &inventory, ); assert_eq!(result, SfxEvent::Wield(ToolKind::Axe)); @@ -40,17 +38,12 @@ fn maps_wield_while_equipping() { #[test] fn maps_unwield() { - let loadout = Loadout { - active_item: Some(ItemConfig { - item: Item::new_from_asset_expect("common.items.weapons.bow.starter_bow"), - ability1: None, - ability2: None, - ability3: None, - block_ability: None, - dodge_ability: None, - }), - ..Default::default() - }; + let loadout = LoadoutBuilder::new() + .active_item(Some(Item::new_from_asset_expect( + "common.items.weapons.bow.starter_bow", + ))) + .build(); + let inventory = Inventory::new_with_loadout(loadout); let result = CombatEventMapper::map_event( &CharacterState::default(), @@ -59,7 +52,7 @@ fn maps_unwield() { time: Instant::now(), weapon_drawn: true, }, - Some(&loadout), + &inventory, ); assert_eq!(result, SfxEvent::Unwield(ToolKind::Bow)); @@ -67,17 +60,12 @@ fn maps_unwield() { #[test] fn maps_basic_melee() { - let loadout = Loadout { - active_item: Some(ItemConfig { - item: Item::new_from_asset_expect("common.items.weapons.axe.starter_axe"), - ability1: None, - ability2: None, - ability3: None, - block_ability: None, - dodge_ability: None, - }), - ..Default::default() - }; + let loadout = LoadoutBuilder::new() + .active_item(Some(Item::new_from_asset_expect( + "common.items.weapons.axe.starter_axe", + ))) + .build(); + let inventory = Inventory::new_with_loadout(loadout); let result = CombatEventMapper::map_event( &CharacterState::BasicMelee(states::basic_melee::Data { @@ -100,7 +88,7 @@ fn maps_basic_melee() { time: Instant::now(), weapon_drawn: true, }, - Some(&loadout), + &inventory, ); assert_eq!( @@ -111,17 +99,12 @@ fn maps_basic_melee() { #[test] fn matches_ability_stage() { - let loadout = Loadout { - active_item: Some(ItemConfig { - item: Item::new_from_asset_expect("common.items.weapons.sword.starter_sword"), - ability1: None, - ability2: None, - ability3: None, - block_ability: None, - dodge_ability: None, - }), - ..Default::default() - }; + let loadout = LoadoutBuilder::new() + .active_item(Some(Item::new_from_asset_expect( + "common.items.weapons.sword.starter_sword", + ))) + .build(); + let inventory = Inventory::new_with_loadout(loadout); let result = CombatEventMapper::map_event( &CharacterState::ComboMelee(states::combo_melee::Data { @@ -159,7 +142,7 @@ fn matches_ability_stage() { time: Instant::now(), weapon_drawn: true, }, - Some(&loadout), + &inventory, ); assert_eq!( @@ -173,17 +156,12 @@ fn matches_ability_stage() { #[test] fn ignores_different_ability_stage() { - let loadout = Loadout { - active_item: Some(ItemConfig { - item: Item::new_from_asset_expect("common.items.weapons.sword.starter_sword"), - ability1: None, - ability2: None, - ability3: None, - block_ability: None, - dodge_ability: None, - }), - ..Default::default() - }; + let loadout = LoadoutBuilder::new() + .active_item(Some(Item::new_from_asset_expect( + "common.items.weapons.axe.starter_axe", + ))) + .build(); + let inventory = Inventory::new_with_loadout(loadout); let result = CombatEventMapper::map_event( &CharacterState::ComboMelee(states::combo_melee::Data { @@ -221,7 +199,7 @@ fn ignores_different_ability_stage() { time: Instant::now(), weapon_drawn: true, }, - Some(&loadout), + &inventory, ); assert_ne!( diff --git a/voxygen/src/hud/bag.rs b/voxygen/src/hud/bag.rs index 0e535cca69..311017dfd5 100644 --- a/voxygen/src/hud/bag.rs +++ b/voxygen/src/hud/bag.rs @@ -4,7 +4,6 @@ use super::{ slots::{ArmorSlot, EquipSlot, InventorySlot, SlotManager}, util::loadout_slot_text, Show, CRITICAL_HP_COLOR, LOW_HP_COLOR, QUALITY_COMMON, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, - XP_COLOR, }; use crate::{ hud::get_quality_col, @@ -16,13 +15,17 @@ use crate::{ }, }; use client::Client; -use common::comp::{item::Quality, Stats}; +use common::{ + combat::Damage, + comp::{item::Quality, Stats}, +}; use conrod_core::{ color, - widget::{self, Button, Image, Rectangle, Text}, + widget::{self, Button, Image, Rectangle, Scrollbar, Text}, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; +use crate::hud::slots::SlotKind; use vek::Vec2; widget_ids! { @@ -45,11 +48,13 @@ widget_ids! { inventory_title, inventory_title_bg, scrollbar_bg, + scrollbar_slots, stats_button, tab_1, tab_2, tab_3, tab_4, + bag_expand_btn, // Stats stats_alignment, level, @@ -70,13 +75,18 @@ widget_ids! { legs_slot, belt_slot, lantern_slot, - ring_slot, + ring1_slot, + ring2_slot, feet_slot, back_slot, tabard_slot, glider_slot, mainhand_slot, offhand_slot, + bag1_slot, + bag2_slot, + bag3_slot, + bag4_slot, // ??? end_ico, fit_ico, @@ -140,7 +150,7 @@ pub struct State { } pub enum Event { - Stats, + BagExpand, Close, } @@ -163,30 +173,34 @@ impl<'a> Widget for Bag<'a> { let widget::UpdateArgs { state, ui, .. } = args; let mut event = None; - - let invs = self.client.inventories(); - let inventory = match invs.get(self.client.entity()) { - Some(i) => i, - None => return None, - }; - let loadouts = self.client.loadouts(); - let loadout = match loadouts.get(self.client.entity()) { + let bag_tooltip = Tooltip::new({ + // Edge images [t, b, r, l] + // Corner images [tr, tl, br, bl] + let edge = &self.rot_imgs.tt_side; + let corner = &self.rot_imgs.tt_corner; + ImageFrame::new( + [edge.cw180, edge.none, edge.cw270, edge.cw90], + [corner.none, corner.cw270, corner.cw90, corner.cw180], + Color::Rgba(0.08, 0.07, 0.04, 1.0), + 5.0, + ) + }) + .title_font_size(self.fonts.cyri.scale(15)) + .parent(ui.window) + .desc_font_size(self.fonts.cyri.scale(12)) + .font_id(self.fonts.cyri.conrod_id) + .desc_text_color(TEXT_COLOR); + let inventories = self.client.inventories(); + let inventory = match inventories.get(self.client.entity()) { Some(l) => l, None => return None, }; - let exp_percentage = (self.stats.exp.current() as f64) / (self.stats.exp.maximum() as f64); - let exp_threshold = format!( - "{}/{} {}", - self.stats.exp.current(), - 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.populated_slots(); + let space_max = inventory.slots().count(); 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(); - let currency = 0; // TODO: Add as a Stat + let currency = 0; // TODO: Add as a Stat // Tooltips let item_tooltip = Tooltip::new({ @@ -209,6 +223,8 @@ impl<'a> Widget for Bag<'a> { // BG Image::new(if self.show.stats { self.imgs.inv_bg_stats + } else if self.show.bag_inv { + self.imgs.inv_bg_bag } else { self.imgs.inv_bg_armor }) @@ -216,11 +232,15 @@ impl<'a> Widget for Bag<'a> { .bottom_right_with_margins_on(ui.window, 60.0, 5.0) .color(Some(UI_MAIN)) .set(state.ids.bg, ui); - Image::new(self.imgs.inv_frame) - .w_h(424.0, 708.0) - .middle_of(state.ids.bg) - .color(Some(UI_HIGHLIGHT_0)) - .set(state.ids.bg_frame, ui); + Image::new(if self.show.bag_inv { + self.imgs.inv_frame_bag + } else { + self.imgs.inv_frame + }) + .w_h(424.0, 708.0) + .middle_of(state.ids.bg) + .color(Some(UI_HIGHLIGHT_0)) + .set(state.ids.bg_frame, ui); // Title Text::new( &self @@ -244,12 +264,36 @@ impl<'a> Widget for Bag<'a> { .font_size(self.fonts.cyri.scale(20)) .color(TEXT_COLOR) .set(state.ids.inventory_title, ui); - // Scrollbar-BG - Image::new(self.imgs.scrollbar_bg) - .w_h(9.0, 173.0) - .bottom_right_with_margins_on(state.ids.bg_frame, 42.0, 3.0) - .color(Some(UI_HIGHLIGHT_0)) - .set(state.ids.scrollbar_bg, ui); + // Slots Scrollbar + if space_max > 45 && !self.show.bag_inv { + // Scrollbar-BG + Image::new(self.imgs.scrollbar_bg) + .w_h(9.0, 173.0) + .bottom_right_with_margins_on(state.ids.bg_frame, 42.0, 3.0) + .color(Some(UI_HIGHLIGHT_0)) + .set(state.ids.scrollbar_bg, ui); + // Scrollbar + Scrollbar::y_axis(state.ids.inv_alignment) + .thickness(5.0) + .h(123.0) + .color(UI_MAIN) + .middle_of(state.ids.scrollbar_bg) + .set(state.ids.scrollbar_slots, ui); + } else if space_max > 135 { + // Scrollbar-BG + Image::new(self.imgs.scrollbar_bg_big) + .w_h(9.0, 592.0) + .bottom_right_with_margins_on(state.ids.bg_frame, 42.0, 3.0) + .color(Some(UI_HIGHLIGHT_0)) + .set(state.ids.scrollbar_bg, ui); + // Scrollbar + Scrollbar::y_axis(state.ids.inv_alignment) + .thickness(5.0) + .h(542.0) + .color(UI_MAIN) + .middle_of(state.ids.scrollbar_bg) + .set(state.ids.scrollbar_slots, ui); + }; // Char Pixel-Art Image::new(self.imgs.char_art) .w_h(40.0, 37.0) @@ -280,71 +324,136 @@ impl<'a> Widget for Bag<'a> { }) .set(state.ids.space_txt, ui); // Alignment for Grid - Rectangle::fill_with([362.0, 200.0], color::TRANSPARENT) - .bottom_left_with_margins_on(state.ids.bg_frame, 29.0, 44.0) - .scroll_kids_vertically() - .set(state.ids.inv_alignment, ui); + Rectangle::fill_with( + [362.0, if self.show.bag_inv { 600.0 } else { 200.0 }], + color::TRANSPARENT, + ) + .bottom_left_with_margins_on(state.ids.bg_frame, 29.0, 46.5) + .scroll_kids_vertically() + .set(state.ids.inv_alignment, ui); + // Button to expand bag + let txt = if self.show.bag_inv { + "Show Loadout" + } else { + "Expand Bag" + }; + let expand_btn = Button::image(if self.show.bag_inv { + self.imgs.collapse_btn + } else { + self.imgs.expand_btn + }) + .w_h(30.0, 17.0) + .hover_image(if self.show.bag_inv { + self.imgs.collapse_btn_hover + } else { + self.imgs.expand_btn_hover + }) + .press_image(if self.show.bag_inv { + self.imgs.collapse_btn_press + } else { + self.imgs.expand_btn_press + }); + // Only show expand button when it's needed... + if space_max > 45 && !self.show.bag_inv { + if expand_btn + .top_left_with_margins_on(state.ids.bg_frame, 460.0, 211.5) + .with_tooltip(self.tooltip_manager, &txt, "", &bag_tooltip, TEXT_COLOR) + .set(state.ids.bag_expand_btn, ui) + .was_clicked() + { + event = Some(Event::BagExpand); + } + } else if self.show.bag_inv { + //... but always show it when the bag is expanded + if expand_btn + .top_left_with_margins_on(state.ids.bg_frame, 53.0, 211.5) + .with_tooltip(self.tooltip_manager, &txt, "", &bag_tooltip, TEXT_COLOR) + .set(state.ids.bag_expand_btn, ui) + .was_clicked() + { + event = Some(Event::BagExpand); + } + } - if !self.show.stats { - // Title - Text::new( - &self - .localized_strings - .get("hud.bag.inventory") - .replace("{playername}", &self.stats.name.to_string().as_str()), - ) - .mid_top_with_margin_on(state.ids.bg_frame, 9.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(22)) - .color(Color::Rgba(0.0, 0.0, 0.0, 1.0)) - .set(state.ids.inventory_title_bg, ui); - Text::new( - &self - .localized_strings - .get("hud.bag.inventory") - .replace("{playername}", &self.stats.name.to_string().as_str()), - ) - .top_left_with_margins_on(state.ids.inventory_title_bg, 2.0, 2.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(22)) - .color(TEXT_COLOR) - .set(state.ids.inventory_title, ui); - // Armor Slots - let mut slot_maker = SlotMaker { - empty_slot: self.imgs.armor_slot_empty, - filled_slot: self.imgs.armor_slot, - selected_slot: self.imgs.armor_slot_sel, - background_color: Some(UI_HIGHLIGHT_0), - content_size: ContentSize { - width_height_ratio: 1.0, - max_fraction: 0.75, /* Changes the item image size by setting a maximum - * fraction - * of either the width or height */ - }, - selected_content_scale: 1.067, - amount_font: self.fonts.cyri.conrod_id, - amount_margins: Vec2::new(-4.0, 0.0), - amount_font_size: self.fonts.cyri.scale(12), - amount_text_color: TEXT_COLOR, - content_source: loadout, - image_source: self.item_imgs, - slot_manager: Some(self.slot_manager), - }; - let i18n = &self.localized_strings; - let filled_slot = self.imgs.armor_slot; + // Title + Text::new( + &self + .localized_strings + .get("hud.bag.inventory") + .replace("{playername}", &self.stats.name.to_string().as_str()), + ) + .mid_top_with_margin_on(state.ids.bg_frame, 9.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(22)) + .color(Color::Rgba(0.0, 0.0, 0.0, 1.0)) + .set(state.ids.inventory_title_bg, ui); + Text::new( + &self + .localized_strings + .get("hud.bag.inventory") + .replace("{playername}", &self.stats.name.to_string().as_str()), + ) + .top_left_with_margins_on(state.ids.inventory_title_bg, 2.0, 2.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(22)) + .color(TEXT_COLOR) + .set(state.ids.inventory_title, ui); + // Armor Slots + let mut slot_maker = SlotMaker { + empty_slot: self.imgs.armor_slot_empty, + filled_slot: self.imgs.armor_slot, + selected_slot: self.imgs.armor_slot_sel, + background_color: Some(UI_HIGHLIGHT_0), + content_size: ContentSize { + width_height_ratio: 1.0, + max_fraction: 0.75, /* Changes the item image size by setting a maximum + * fraction + * of either the width or height */ + }, + selected_content_scale: 1.067, + amount_font: self.fonts.cyri.conrod_id, + amount_margins: Vec2::new(-4.0, 0.0), + amount_font_size: self.fonts.cyri.scale(12), + amount_text_color: TEXT_COLOR, + content_source: inventory, + image_source: self.item_imgs, + slot_manager: Some(self.slot_manager), + }; + let i18n = &self.localized_strings; + let filled_slot = self.imgs.armor_slot; + if !self.show.bag_inv { + let damage_reduction = (100.0 * Damage::compute_damage_reduction(inventory)) as i32; + Button::image(self.imgs.protection_ico) + .w_h(20.0, 20.0) + .top_left_with_margins_on(state.ids.bg_frame, 51.0, 5.0) + .color(UI_HIGHLIGHT_0) + .label(&format!("{}%", damage_reduction)) + .label_y(conrod_core::position::Relative::Scalar(2.0)) + .label_x(conrod_core::position::Relative::Scalar(25.0)) + .label_color(TEXT_COLOR) + .label_font_size(self.fonts.cyri.scale(12)) + .label_font_id(self.fonts.cyri.conrod_id) + .with_tooltip( + self.tooltip_manager, + "Protection", + "Damage reduction through armor", + &bag_tooltip, + TEXT_COLOR, + ) + .set(state.ids.prot_ico, ui); // Head - let (title, desc) = - loadout_slot_text(loadout.head.as_ref(), || (i18n.get("hud.bag.head"), "")); - let head_q_col = loadout - .head - .as_ref() + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Head)), + || (i18n.get("hud.bag.head"), ""), + ); + let head_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Head)) .map(|item| get_quality_col(item)) .unwrap_or(QUALITY_COMMON); slot_maker .fabricate(EquipSlot::Armor(ArmorSlot::Head), [45.0; 2]) .mid_top_with_margin_on(state.ids.bg_frame, 60.0) .with_icon(self.imgs.head_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN)) - .with_background_color(TEXT_COLOR) .filled_slot(filled_slot) .with_tooltip( self.tooltip_manager, @@ -355,11 +464,12 @@ impl<'a> Widget for Bag<'a> { ) .set(state.ids.head_slot, ui); // Necklace - let (title, desc) = - loadout_slot_text(loadout.neck.as_ref(), || (i18n.get("hud.bag.neck"), "")); - let neck_q_col = loadout - .neck - .as_ref() + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Neck)), + || (i18n.get("hud.bag.neck"), ""), + ); + let neck_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Neck)) .map(|item| get_quality_col(item)) .unwrap_or(QUALITY_COMMON); slot_maker @@ -377,11 +487,12 @@ impl<'a> Widget for Bag<'a> { .set(state.ids.neck_slot, ui); // Chest //Image::new(self.imgs.armor_slot) // different graphics for empty/non empty - let (title, desc) = - loadout_slot_text(loadout.chest.as_ref(), || (i18n.get("hud.bag.chest"), "")); - let chest_q_col = loadout - .chest - .as_ref() + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Chest)), + || (i18n.get("hud.bag.chest"), ""), + ); + let chest_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Chest)) .map(|item| get_quality_col(item)) .unwrap_or(QUALITY_COMMON); slot_maker @@ -398,12 +509,12 @@ impl<'a> Widget for Bag<'a> { ) .set(state.ids.chest_slot, ui); // Shoulders - let (title, desc) = loadout_slot_text(loadout.shoulder.as_ref(), || { - (i18n.get("hud.bag.shoulders"), "") - }); - let shoulder_q_col = loadout - .shoulder - .as_ref() + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Shoulders)), + || (i18n.get("hud.bag.shoulders"), ""), + ); + let shoulder_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Shoulders)) .map(|item| get_quality_col(item)) .unwrap_or(QUALITY_COMMON); slot_maker @@ -420,11 +531,12 @@ impl<'a> Widget for Bag<'a> { ) .set(state.ids.shoulders_slot, ui); // Hands - let (title, desc) = - loadout_slot_text(loadout.hand.as_ref(), || (i18n.get("hud.bag.hands"), "")); - let chest_q_col = loadout - .hand - .as_ref() + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Hands)), + || (i18n.get("hud.bag.hands"), ""), + ); + let chest_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Hands)) .map(|item| get_quality_col(item)) .unwrap_or(QUALITY_COMMON); slot_maker @@ -441,11 +553,12 @@ impl<'a> Widget for Bag<'a> { ) .set(state.ids.hands_slot, ui); // Belt - let (title, desc) = - loadout_slot_text(loadout.belt.as_ref(), || (i18n.get("hud.bag.belt"), "")); - let belt_q_col = loadout - .belt - .as_ref() + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Belt)), + || (i18n.get("hud.bag.belt"), ""), + ); + let belt_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Belt)) .map(|item| get_quality_col(item)) .unwrap_or(QUALITY_COMMON); slot_maker @@ -462,11 +575,12 @@ impl<'a> Widget for Bag<'a> { ) .set(state.ids.belt_slot, ui); // Legs - let (title, desc) = - loadout_slot_text(loadout.pants.as_ref(), || (i18n.get("hud.bag.legs"), "")); - let legs_q_col = loadout - .pants - .as_ref() + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Legs)), + || (i18n.get("hud.bag.legs"), ""), + ); + let legs_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Legs)) .map(|item| get_quality_col(item)) .unwrap_or(QUALITY_COMMON); slot_maker @@ -482,38 +596,17 @@ impl<'a> Widget for Bag<'a> { legs_q_col, ) .set(state.ids.legs_slot, ui); - // Lantern - let (title, desc) = loadout_slot_text(loadout.lantern.as_ref(), || { - (i18n.get("hud.bag.lantern"), "") - }); - let lantern_q_col = loadout - .lantern - .as_ref() - .map(|item| get_quality_col(item)) - .unwrap_or(QUALITY_COMMON); - slot_maker - .fabricate(EquipSlot::Lantern, [45.0; 2]) - .bottom_right_with_margins_on(state.ids.shoulders_slot, -55.0, 0.0) - .with_icon(self.imgs.lantern_bg, Vec2::new(24.0, 38.0), Some(UI_MAIN)) - .filled_slot(filled_slot) - .with_tooltip( - self.tooltip_manager, - title, - &*desc, - &item_tooltip, - lantern_q_col, - ) - .set(state.ids.lantern_slot, ui); // Ring - let (title, desc) = - loadout_slot_text(loadout.ring.as_ref(), || (i18n.get("hud.bag.ring"), "")); - let ring_q_col = loadout - .ring - .as_ref() + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Ring1)), + || (i18n.get("hud.bag.ring"), ""), + ); + let ring_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Ring1)) .map(|item| get_quality_col(item)) .unwrap_or(QUALITY_COMMON); slot_maker - .fabricate(EquipSlot::Armor(ArmorSlot::Ring), [45.0; 2]) + .fabricate(EquipSlot::Armor(ArmorSlot::Ring1), [45.0; 2]) .bottom_left_with_margins_on(state.ids.hands_slot, -55.0, 0.0) .with_icon(self.imgs.ring_bg, Vec2::new(36.0, 40.0), Some(UI_MAIN)) .filled_slot(filled_slot) @@ -524,18 +617,41 @@ impl<'a> Widget for Bag<'a> { &item_tooltip, ring_q_col, ) - .set(state.ids.ring_slot, ui); + .set(state.ids.ring1_slot, ui); + // Ring 2 + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Ring2)), + || (i18n.get("hud.bag.ring"), ""), + ); + let ring2_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Ring2)) + .map(|item| get_quality_col(item)) + .unwrap_or(QUALITY_COMMON); + slot_maker + .fabricate(EquipSlot::Armor(ArmorSlot::Ring2), [45.0; 2]) + .bottom_right_with_margins_on(state.ids.shoulders_slot, -55.0, 0.0) + .with_icon(self.imgs.ring_bg, Vec2::new(36.0, 40.0), Some(UI_MAIN)) + .filled_slot(filled_slot) + .with_tooltip( + self.tooltip_manager, + title, + &*desc, + &item_tooltip, + ring2_q_col, + ) + .set(state.ids.ring2_slot, ui); // Back - let (title, desc) = - loadout_slot_text(loadout.back.as_ref(), || (i18n.get("hud.bag.back"), "")); - let back_q_col = loadout - .back - .as_ref() + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Back)), + || (i18n.get("hud.bag.back"), ""), + ); + let back_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Back)) .map(|item| get_quality_col(item)) .unwrap_or(QUALITY_COMMON); slot_maker .fabricate(EquipSlot::Armor(ArmorSlot::Back), [45.0; 2]) - .down_from(state.ids.lantern_slot, 10.0) + .down_from(state.ids.ring2_slot, 10.0) .with_icon(self.imgs.back_bg, Vec2::new(33.0, 40.0), Some(UI_MAIN)) .filled_slot(filled_slot) .with_tooltip( @@ -547,16 +663,17 @@ impl<'a> Widget for Bag<'a> { ) .set(state.ids.back_slot, ui); // Foot - let (title, desc) = - loadout_slot_text(loadout.foot.as_ref(), || (i18n.get("hud.bag.feet"), "")); - let foot_q_col = loadout - .foot - .as_ref() + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Feet)), + || (i18n.get("hud.bag.feet"), ""), + ); + let foot_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Feet)) .map(|item| get_quality_col(item)) .unwrap_or(QUALITY_COMMON); slot_maker .fabricate(EquipSlot::Armor(ArmorSlot::Feet), [45.0; 2]) - .down_from(state.ids.ring_slot, 10.0) + .down_from(state.ids.ring1_slot, 10.0) .with_icon(self.imgs.feet_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN)) .filled_slot(filled_slot) .with_tooltip( @@ -567,39 +684,39 @@ impl<'a> Widget for Bag<'a> { foot_q_col, ) .set(state.ids.feet_slot, ui); - // Tabard - let (title, desc) = - loadout_slot_text(loadout.tabard.as_ref(), || (i18n.get("hud.bag.tabard"), "")); - let tabard_q_col = loadout - .tabard - .as_ref() + // Lantern + let (title, desc) = loadout_slot_text(inventory.equipped(EquipSlot::Lantern), || { + (i18n.get("hud.bag.lantern"), "") + }); + let lantern_q_col = inventory + .equipped(EquipSlot::Lantern) .map(|item| get_quality_col(item)) .unwrap_or(QUALITY_COMMON); slot_maker - .fabricate(EquipSlot::Armor(ArmorSlot::Tabard), [70.0; 2]) - .top_right_with_margins_on(state.ids.bg_frame, 80.5, 53.0) - .with_icon(self.imgs.tabard_bg, Vec2::new(60.0, 60.0), Some(UI_MAIN)) + .fabricate(EquipSlot::Lantern, [45.0; 2]) + .top_right_with_margins_on(state.ids.bg_frame, 60.0, 5.0) + .with_icon(self.imgs.lantern_bg, Vec2::new(24.0, 38.0), Some(UI_MAIN)) .filled_slot(filled_slot) .with_tooltip( self.tooltip_manager, title, &*desc, &item_tooltip, - tabard_q_col, + lantern_q_col, ) - .set(state.ids.tabard_slot, ui); + .set(state.ids.lantern_slot, ui); // Glider - let (title, desc) = - loadout_slot_text(loadout.glider.as_ref(), || (i18n.get("hud.bag.glider"), "")); - let glider_q_col = loadout - .glider - .as_ref() + let (title, desc) = loadout_slot_text(inventory.equipped(EquipSlot::Glider), || { + (i18n.get("hud.bag.glider"), "") + }); + let glider_q_col = inventory + .equipped(EquipSlot::Glider) .map(|item| get_quality_col(item)) .unwrap_or(QUALITY_COMMON); slot_maker - .fabricate(EquipSlot::Glider, [70.0; 2]) - .top_left_with_margins_on(state.ids.bg_frame, 80.5, 53.0) - .with_icon(self.imgs.glider_bg, Vec2::new(60.0, 60.0), Some(UI_MAIN)) + .fabricate(EquipSlot::Glider, [45.0; 2]) + .down_from(state.ids.lantern_slot, 5.0) + .with_icon(self.imgs.glider_bg, Vec2::new(38.0, 38.0), Some(UI_MAIN)) .filled_slot(filled_slot) .with_tooltip( self.tooltip_manager, @@ -609,15 +726,35 @@ impl<'a> Widget for Bag<'a> { glider_q_col, ) .set(state.ids.glider_slot, ui); + // Tabard + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Tabard)), + || (i18n.get("hud.bag.tabard"), ""), + ); + let tabard_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Tabard)) + .map(|item| get_quality_col(item)) + .unwrap_or(QUALITY_COMMON); + slot_maker + .fabricate(EquipSlot::Armor(ArmorSlot::Tabard), [45.0; 2]) + .down_from(state.ids.glider_slot, 5.0) + .with_icon(self.imgs.tabard_bg, Vec2::new(38.0, 38.0), Some(UI_MAIN)) + .filled_slot(filled_slot) + .with_tooltip( + self.tooltip_manager, + title, + &*desc, + &item_tooltip, + tabard_q_col, + ) + .set(state.ids.tabard_slot, ui); // Mainhand/Left-Slot - let (title, desc) = - loadout_slot_text(loadout.active_item.as_ref().map(|i| &i.item), || { - (i18n.get("hud.bag.mainhand"), "") - }); - let mainhand_q_col = loadout - .active_item - .as_ref() - .map(|item| get_quality_col(&item.item)) + let (title, desc) = loadout_slot_text(inventory.equipped(EquipSlot::Mainhand), || { + (i18n.get("hud.bag.mainhand"), "") + }); + let mainhand_q_col = inventory + .equipped(EquipSlot::Mainhand) + .map(|item| get_quality_col(item)) .unwrap_or(QUALITY_COMMON); slot_maker .fabricate(EquipSlot::Mainhand, [85.0; 2]) @@ -633,14 +770,12 @@ impl<'a> Widget for Bag<'a> { ) .set(state.ids.mainhand_slot, ui); // Offhand/Right-Slot - let (title, desc) = - loadout_slot_text(loadout.second_item.as_ref().map(|i| &i.item), || { - (i18n.get("hud.bag.offhand"), "") - }); - let offhand_q_col = loadout - .second_item - .as_ref() - .map(|item| get_quality_col(&item.item)) + let (title, desc) = loadout_slot_text(inventory.equipped(EquipSlot::Offhand), || { + (i18n.get("hud.bag.offhand"), "") + }); + let offhand_q_col = inventory + .equipped(EquipSlot::Offhand) + .map(|item| get_quality_col(item)) .unwrap_or(QUALITY_COMMON); slot_maker .fabricate(EquipSlot::Offhand, [85.0; 2]) @@ -655,130 +790,123 @@ impl<'a> Widget for Bag<'a> { offhand_q_col, ) .set(state.ids.offhand_slot, ui); - } else { - // Stats - // Title - Text::new( - &self - .localized_strings - .get("hud.bag.stats_title") - .replace("{playername}", &self.stats.name.to_string().as_str()), - ) - .mid_top_with_margin_on(state.ids.bg_frame, 9.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(22)) - .color(Color::Rgba(0.0, 0.0, 0.0, 1.0)) - .set(state.ids.inventory_title_bg, ui); - Text::new( - &self - .localized_strings - .get("hud.bag.stats_title") - .replace("{playername}", &self.stats.name.to_string().as_str()), - ) - .top_left_with_margins_on(state.ids.inventory_title_bg, 2.0, 2.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(22)) - .color(TEXT_COLOR) - .set(state.ids.inventory_title, ui); - // Alignment for Stats - Rectangle::fill_with([418.0, 384.0], color::TRANSPARENT) - .mid_top_with_margin_on(state.ids.bg_frame, 48.0) - .scroll_kids_vertically() - .set(state.ids.stats_alignment, ui); - // Level - Text::new(&level) - .mid_top_with_margin_on(state.ids.stats_alignment, 10.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(30)) - .color(TEXT_COLOR) - .set(state.ids.level, ui); - - // Exp-Bar Background - Rectangle::fill_with([170.0, 10.0], color::BLACK) - .mid_top_with_margin_on(state.ids.stats_alignment, 50.0) - .set(state.ids.exp_rectangle, ui); - - // Exp-Bar Progress - Rectangle::fill_with([170.0 * (exp_percentage), 6.0], XP_COLOR) // 0.8 = Experience percentage - .mid_left_with_margin_on(state.ids.expbar, 1.0) - .set(state.ids.exp_progress_rectangle, ui); - - // Exp-Bar Foreground Frame - Image::new(self.imgs.progress_frame) - .w_h(170.0, 10.0) - .middle_of(state.ids.exp_rectangle) - .color(Some(UI_HIGHLIGHT_0)) - .set(state.ids.expbar, ui); - - // Exp-Text - Text::new(&exp_threshold) - .mid_top_with_margin_on(state.ids.expbar, 10.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(15)) - .color(TEXT_COLOR) - .set(state.ids.exp, ui); - - // Divider - /*Image::new(self.imgs.divider) - .w_h(50.0, 5.0) - .mid_top_with_margin_on(state.ids.exp, 20.0) - .color(Some(UI_HIGHLIGHT_0)) - .set(state.ids.divider, ui);*/ - - // Stats - // Defense - let damage_reduction = (100.0 * loadout.get_damage_reduction()) as i32; - - Text::new( - &self - .localized_strings - .get("character_window.character_stats"), - ) - .top_left_with_margins_on(state.ids.stats_alignment, 120.0, 150.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(16)) - .color(TEXT_COLOR) - .set(state.ids.statnames, ui); - Image::new(self.imgs.endurance_ico) - .w_h(20.0, 20.0) - .top_left_with_margins_on(state.ids.statnames, 0.0, -40.0) - .color(Some(UI_HIGHLIGHT_0)) - .set(state.ids.end_ico, ui); - Image::new(self.imgs.fitness_ico) - .w_h(20.0, 20.0) - .down_from(state.ids.end_ico, 15.0) - .color(Some(UI_HIGHLIGHT_0)) - .set(state.ids.fit_ico, ui); - Image::new(self.imgs.willpower_ico) - .w_h(20.0, 20.0) - .down_from(state.ids.fit_ico, 15.0) - .color(Some(UI_HIGHLIGHT_0)) - .set(state.ids.wp_ico, ui); - Image::new(self.imgs.protection_ico) - .w_h(20.0, 20.0) - .down_from(state.ids.wp_ico, 15.0) - .color(Some(UI_HIGHLIGHT_0)) - .set(state.ids.prot_ico, ui); - - Text::new(&format!( - "{}\n\n{}\n\n{}\n\n{}%", - self.stats.endurance, self.stats.fitness, self.stats.willpower, damage_reduction - )) - .top_right_with_margins_on(state.ids.stats_alignment, 120.0, 130.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(16)) - .color(TEXT_COLOR) - .set(state.ids.stats, ui); } + // Bag 1 + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag1)), + || (i18n.get("hud.bag.bag"), ""), + ); + let bag1_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Bag1)) + .map(|item| get_quality_col(item)) + .unwrap_or(QUALITY_COMMON); + slot_maker + .fabricate(EquipSlot::Armor(ArmorSlot::Bag1), [35.0; 2]) + .bottom_left_with_margins_on( + state.ids.bg_frame, + if self.show.bag_inv { 600.0 } else { 167.0 }, + 3.0, + ) + .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN)) + .filled_slot(filled_slot) + .with_tooltip( + self.tooltip_manager, + title, + &*desc, + &item_tooltip, + bag1_q_col, + ) + .set(state.ids.bag1_slot, ui); + // Bag 2 + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag2)), + || (i18n.get("hud.bag.bag"), ""), + ); + let bag2_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Bag2)) + .map(|item| get_quality_col(item)) + .unwrap_or(QUALITY_COMMON); + slot_maker + .fabricate(EquipSlot::Armor(ArmorSlot::Bag2), [35.0; 2]) + .down_from(state.ids.bag1_slot, 2.0) + .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN)) + .filled_slot(filled_slot) + .with_tooltip( + self.tooltip_manager, + title, + &*desc, + &item_tooltip, + bag2_q_col, + ) + .set(state.ids.bag2_slot, ui); + // Bag 3 + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag3)), + || (i18n.get("hud.bag.bag"), ""), + ); + let bag3_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Bag3)) + .map(|item| get_quality_col(item)) + .unwrap_or(QUALITY_COMMON); + slot_maker + .fabricate(EquipSlot::Armor(ArmorSlot::Bag3), [35.0; 2]) + .down_from(state.ids.bag2_slot, 2.0) + .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN)) + .filled_slot(filled_slot) + .with_tooltip( + self.tooltip_manager, + title, + &*desc, + &item_tooltip, + bag3_q_col, + ) + .set(state.ids.bag3_slot, ui); + // Bag 4 + let (title, desc) = loadout_slot_text( + inventory.equipped(EquipSlot::Armor(ArmorSlot::Bag4)), + || (i18n.get("hud.bag.bag"), ""), + ); + let bag4_q_col = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Bag4)) + .map(|item| get_quality_col(item)) + .unwrap_or(QUALITY_COMMON); + slot_maker + .fabricate(EquipSlot::Armor(ArmorSlot::Bag4), [35.0; 2]) + .down_from(state.ids.bag3_slot, 2.0) + .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN)) + .filled_slot(filled_slot) + .with_tooltip( + self.tooltip_manager, + title, + &*desc, + &item_tooltip, + bag4_q_col, + ) + .set(state.ids.bag4_slot, ui); // Bag Slots // Create available inventory slot widgets - if state.ids.inv_slots.len() < inventory.len() { + if state.ids.inv_slots.len() < inventory.capacity() { state.update(|s| { s.ids .inv_slots - .resize(inventory.len(), &mut ui.widget_id_generator()); + .resize(inventory.capacity(), &mut ui.widget_id_generator()); }); } + + // Determine the range of inventory slots that are provided by the loadout item + // that the mouse is over + let mouseover_loadout_slots = self + .slot_manager + .mouse_over_slot + .and_then(|x| { + if let SlotKind::Equip(e) = x { + inventory.get_slot_range_for_equip_slot(e) + } else { + None + } + }) + .unwrap_or(0usize..0usize); + // Display inventory contents let mut slot_maker = SlotMaker { empty_slot: self.imgs.inv_slot, @@ -798,18 +926,25 @@ impl<'a> Widget for Bag<'a> { image_source: self.item_imgs, slot_manager: Some(self.slot_manager), }; - for (i, item) in inventory.slots().iter().enumerate() { + + for (i, (pos, item)) in inventory.slots_with_id().enumerate() { let x = i % 9; let y = i / 9; // Slot - let slot_widget = slot_maker - .fabricate(InventorySlot(i), [40.0; 2]) + let mut slot_widget = slot_maker + .fabricate(InventorySlot(pos), [40.0; 2]) .top_left_with_margins_on( state.ids.inv_alignment, 0.0 + y as f64 * (40.0), 0.0 + x as f64 * (40.0), ); + + // Highlight slots are provided by the loadout item that the mouse is over + if mouseover_loadout_slots.contains(&i) { + slot_widget = slot_widget.with_background_color(Color::Rgba(1.0, 1.0, 1.0, 1.0)); + } + if let Some(item) = item { let (title, desc) = super::util::item_text(item); let quality_col = get_quality_col(item); @@ -837,83 +972,6 @@ impl<'a> Widget for Bag<'a> { slot_widget.set(state.ids.inv_slots[i], ui); } } - - // Stats Button - if Button::image(self.imgs.button) - .w_h(92.0, 22.0) - .mid_top_with_margin_on(state.ids.bg, 435.0) - .hover_image(self.imgs.button_hover) - .press_image(self.imgs.button_press) - .label(if self.show.stats { - &self.localized_strings.get("hud.bag.armor") - } else { - &self.localized_strings.get("hud.bag.stats") - }) - .label_y(conrod_core::position::Relative::Scalar(1.0)) - .label_color(TEXT_COLOR) - .label_font_size(self.fonts.cyri.scale(12)) - .label_font_id(self.fonts.cyri.conrod_id) - .set(state.ids.stats_button, ui) - .was_clicked() - { - return Some(Event::Stats); - }; - // Tabs - if Button::image(self.imgs.inv_tab_active) - .w_h(28.0, 44.0) - .bottom_left_with_margins_on(state.ids.bg, 172.0, 13.0) - .image_color(UI_MAIN) - .set(state.ids.tab_1, ui) - .was_clicked() - {} - if Button::image(self.imgs.inv_tab_inactive) - .w_h(28.0, 44.0) - .hover_image(self.imgs.inv_tab_inactive_hover) - .press_image(self.imgs.inv_tab_inactive_press) - .image_color(UI_HIGHLIGHT_0) - .down_from(state.ids.tab_1, 0.0) - .with_tooltip( - self.tooltip_manager, - "Not yet Available", - "", - &item_tooltip, - TEXT_COLOR, - ) - .set(state.ids.tab_2, ui) - .was_clicked() - {} - if Button::image(self.imgs.inv_tab_inactive) - .w_h(28.0, 44.0) - .hover_image(self.imgs.inv_tab_inactive_hover) - .press_image(self.imgs.inv_tab_inactive_press) - .down_from(state.ids.tab_2, 0.0) - .image_color(UI_HIGHLIGHT_0) - .with_tooltip( - self.tooltip_manager, - "Not yet Available", - "", - &item_tooltip, - TEXT_COLOR, - ) - .set(state.ids.tab_3, ui) - .was_clicked() - {} - if Button::image(self.imgs.inv_tab_inactive) - .w_h(28.0, 44.0) - .hover_image(self.imgs.inv_tab_inactive_hover) - .press_image(self.imgs.inv_tab_inactive_press) - .down_from(state.ids.tab_3, 0.0) - .image_color(UI_HIGHLIGHT_0) - .with_tooltip( - self.tooltip_manager, - "Not yet Available", - "", - &item_tooltip, - TEXT_COLOR, - ) - .set(state.ids.tab_4, ui) - .was_clicked() - {} // Close button if Button::image(self.imgs.close_btn) .w_h(24.0, 25.0) diff --git a/voxygen/src/hud/buttons.rs b/voxygen/src/hud/buttons.rs index 874a83f192..fb0ae3bac2 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.populated_slots(); + let space_max = inventory.slots().count(); 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/hotbar.rs b/voxygen/src/hud/hotbar.rs index 1fbe84b101..1e29e25559 100644 --- a/voxygen/src/hud/hotbar.rs +++ b/voxygen/src/hud/hotbar.rs @@ -1,6 +1,8 @@ +use crate::hud::slots::EquipSlot; +use common::comp::{slot::InvSlotId, Inventory}; use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum Slot { One = 0, Two = 1, @@ -16,7 +18,7 @@ pub enum Slot { #[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] pub enum SlotContents { - Inventory(usize), + Inventory(InvSlotId), Ability3, } @@ -57,8 +59,8 @@ impl State { pub fn clear_slot(&mut self, slot: Slot) { self.slots[slot as usize] = None; } - pub fn add_inventory_link(&mut self, slot: Slot, inventory_index: usize) { - self.slots[slot as usize] = Some(SlotContents::Inventory(inventory_index)); + pub fn add_inventory_link(&mut self, slot: Slot, inventory_pos: InvSlotId) { + self.slots[slot as usize] = Some(SlotContents::Inventory(inventory_pos)); } // TODO: remove @@ -67,13 +69,12 @@ impl State { #[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587 pub fn maintain_ability3(&mut self, client: &client::Client) { use specs::WorldExt; - let loadouts = client.state().ecs().read_storage::(); - let loadout = loadouts.get(client.entity()); - let should_be_present = if let Some(loadout) = loadout { - loadout - .active_item - .as_ref() - .map(|i| i.item.kind()) + let inventories = client.state().ecs().read_storage::(); + let inventory = inventories.get(client.entity()); + let should_be_present = if let Some(inventory) = inventory { + inventory + .equipped(EquipSlot::Mainhand) + .map(|i| i.kind()) .filter(|kind| { use common::comp::item::{ tool::{ToolKind, UniqueKind}, diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 5a7b391c70..cabe4f0d60 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -26,10 +26,6 @@ image_ids! { flower: "voxygen.element.icons.item_flower", grass: "voxygen.element.icons.item_grass", - // Charwindow - xp_charwindow: "voxygen.element.frames.xp_charwindow", - divider: "voxygen.element.frames.divider_charwindow", - // Items potion_red: "voxygen.voxel.object.potion_red", potion_green: "voxygen.voxel.object.potion_green", @@ -59,6 +55,13 @@ image_ids! { selection_hover: "voxygen.element.frames.selection_hover", selection_press: "voxygen.element.frames.selection_press", + // Prompt Dialog + prompt_top: "voxygen.element.frames.prompt_dialog_top", + prompt_mid: "voxygen.element.frames.prompt_dialog_mid", + prompt_bot: "voxygen.element.frames.prompt_dialog_bot", + key_button: "voxygen.element.buttons.key_button", + key_button_press: "voxygen.element.buttons.key_button_press", + // Social Window social_frame_on: "voxygen.element.misc_bg.social_frame", social_bg_on: "voxygen.element.misc_bg.social_bg", @@ -235,10 +238,18 @@ image_ids! { close_button_press: "voxygen.element.buttons.close_btn_press", // Inventory + collapse_btn: "voxygen.element.buttons.inv_collapse", + collapse_btn_hover: "voxygen.element.buttons.inv_collapse_hover", + collapse_btn_press: "voxygen.element.buttons.inv_collapse_press", + expand_btn: "voxygen.element.buttons.inv_expand", + expand_btn_hover: "voxygen.element.buttons.inv_expand_hover", + expand_btn_press: "voxygen.element.buttons.inv_expand_press", coin_ico: "voxygen.element.icons.coin", inv_bg_armor: "voxygen.element.misc_bg.inv_bg_0", inv_bg_stats: "voxygen.element.misc_bg.inv_bg_1", inv_frame: "voxygen.element.misc_bg.inv_frame", + inv_frame_bag: "voxygen.element.misc_bg.inv_frame_bag", + inv_bg_bag: "voxygen.element.misc_bg.inv_bg_bag", char_art: "voxygen.element.icons.character", inv_slot: "voxygen.element.buttons.inv_slot", inv_slot_grey: "voxygen.element.buttons.inv_slot_grey", @@ -250,12 +261,11 @@ image_ids! { inv_slot_red: "voxygen.element.buttons.inv_slot_red", inv_slot_sel: "voxygen.element.buttons.inv_slot_sel", scrollbar_bg: "voxygen.element.slider.scrollbar", + scrollbar_bg_big: "voxygen.element.slider.scrollbar_1", inv_tab_active: "voxygen.element.buttons.inv_tab_active", inv_tab_inactive: "voxygen.element.buttons.inv_tab_inactive", inv_tab_inactive_hover: "voxygen.element.buttons.inv_tab_inactive", inv_tab_inactive_press: "voxygen.element.buttons.inv_tab_inactive", - inv_slots: "voxygen.element.misc_bg.inv_slots", - inv_runes: "voxygen.element.misc_bg.inv_runes", armor_slot: "voxygen.element.buttons.armor_slot", armor_slot_sel: "voxygen.element.buttons.armor_slot_selected", armor_slot_empty: "voxygen.element.buttons.armor_slot_empty", @@ -273,6 +283,7 @@ image_ids! { lantern_bg: "voxygen.element.icons.lantern", necklace_bg: "voxygen.element.icons.necklace", mainhand_bg: "voxygen.element.icons.mainhand", + bag_bg: "voxygen.element.icons.bag", offhand_bg: "voxygen.element.icons.offhand", willpower_ico: "voxygen.element.icons.willpower", endurance_ico: "voxygen.element.icons.endurance", @@ -289,7 +300,6 @@ image_ids! { banner_top: "voxygen.element.frames.banner_top", // Icons - fire_spell_1: "voxygen.element.icons.fire_spell_0", snake_arrow_0: "voxygen.element.icons.snake", heal_0: "voxygen.element.icons.heal_0", sword_whirlwind: "voxygen.element.icons.sword_whirlwind", diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 3662b0695d..6a293661e1 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -13,6 +13,7 @@ mod minimap; mod overhead; mod overitem; mod popup; +mod prompt_dialog; mod settings_window; mod skillbar; mod slots; @@ -37,6 +38,7 @@ use item_imgs::ItemImgs; use map::Map; use minimap::MiniMap; use popup::Popup; +use prompt_dialog::PromptDialog; use serde::{Deserialize, Serialize}; use settings_window::{SettingsTab, SettingsWindow}; use skillbar::Skillbar; @@ -45,7 +47,7 @@ use spell::Spell; use crate::{ ecs::{comp as vcomp, comp::HpFloaterList}, - hud::img_ids::ImgsRot, + hud::{img_ids::ImgsRot, prompt_dialog::DialogOutcomeEvent}, i18n::{LanguageMetadata, Localization}, render::{Consts, Globals, RenderMode, Renderer}, scene::camera::{self, Camera}, @@ -239,6 +241,7 @@ widget_ids! { character_window, popup, minimap, + prompt_dialog, bag, social, quest, @@ -303,6 +306,7 @@ pub struct HudInfo { pub selected_entity: Option<(specs::Entity, std::time::Instant)>, } +#[derive(Clone)] pub enum Event { ToggleTips(bool), SendMessage(String), @@ -350,8 +354,15 @@ pub enum Event { ToggleDebug(bool), UiScale(ScaleChange), CharacterSelection, - UseSlot(comp::slot::Slot), - SwapSlots(comp::slot::Slot, comp::slot::Slot), + UseSlot { + slot: comp::slot::Slot, + bypass_dialog: bool, + }, + SwapSlots { + slot_a: comp::slot::Slot, + slot_b: comp::slot::Slot, + bypass_dialog: bool, + }, DropSlot(comp::slot::Slot), ChangeHotbarState(Box), Ability3(bool), @@ -439,6 +450,7 @@ pub struct Show { crafting: bool, debug: bool, bag: bool, + bag_inv: bool, social: bool, spell: bool, group: bool, @@ -454,6 +466,7 @@ pub struct Show { stats: bool, free_look: bool, auto_walk: bool, + prompt_dialog: Option, } impl Show { fn bag(&mut self, open: bool) { @@ -604,6 +617,28 @@ impl Show { } } +pub struct PromptDialogSettings { + message: String, + affirmative_event: Event, + negative_event: Option, + outcome_via_keypress: Option, +} + +impl PromptDialogSettings { + pub fn new(message: String, affirmative_event: Event, negative_event: Option) -> Self { + Self { + message, + affirmative_event, + negative_event, + outcome_via_keypress: None, + } + } + + pub fn set_outcome_via_keypress(&mut self, outcome: bool) { + self.outcome_via_keypress = Some(outcome); + } +} + pub struct Hud { ui: Ui, ids: Ids, @@ -695,6 +730,7 @@ impl Hud { intro: true, debug: false, bag: false, + bag_inv: false, esc_menu: false, open_windows: Windows::None, map: false, @@ -712,6 +748,7 @@ impl Hud { stats: false, free_look: false, auto_walk: false, + prompt_dialog: None, }, to_focus: None, //never_show: false, @@ -728,6 +765,10 @@ impl Hud { } } + pub fn set_prompt_dialog(&mut self, prompt_dialog: PromptDialogSettings) { + self.show.prompt_dialog = Some(prompt_dialog); + } + pub fn update_fonts(&mut self, i18n: &Localization) { self.fonts = Fonts::load(&i18n.fonts, &mut self.ui).expect("Impossible to load fonts!"); } @@ -1871,6 +1912,34 @@ impl Hud { None => {}, } + if let Some(prompt_dialog_settings) = &self.show.prompt_dialog { + // Prompt Dialog + match PromptDialog::new( + &self.imgs, + &self.fonts, + &global_state.i18n, + &global_state.settings, + &prompt_dialog_settings, + ) + .set(self.ids.prompt_dialog, ui_widgets) + { + Some(dialog_outcome_event) => { + match dialog_outcome_event { + DialogOutcomeEvent::Affirmative(event) => events.push(event), + DialogOutcomeEvent::Negative(event) => { + if let Some(event) = event { + events.push(event); + }; + }, + }; + + // Close the prompt dialog once an option has been chosen + self.show.prompt_dialog = None; + }, + None => {}, + } + } + // Bag contents if self.show.bag { if let Some(player_stats) = stats.get(client.entity()) { @@ -1889,7 +1958,7 @@ impl Hud { ) .set(self.ids.bag, ui_widgets) { - Some(bag::Event::Stats) => self.show.stats = !self.show.stats, + Some(bag::Event::BagExpand) => self.show.bag_inv = !self.show.bag_inv, Some(bag::Event::Close) => { self.show.stats = false; self.show.bag(false); @@ -1911,28 +1980,26 @@ impl Hud { let entity = client.entity(); let stats = ecs.read_storage::(); let healths = ecs.read_storage::(); - let loadouts = ecs.read_storage::(); + let inventories = ecs.read_storage::(); let energies = ecs.read_storage::(); let character_states = ecs.read_storage::(); let controllers = ecs.read_storage::(); - let inventories = ecs.read_storage::(); let ability_map = ecs.fetch::(); + if let ( Some(stats), Some(health), - Some(loadout), + Some(inventory), Some(energy), Some(_character_state), Some(_controller), - Some(inventory), ) = ( stats.get(entity), healths.get(entity), - loadouts.get(entity), + inventories.get(entity), energies.get(entity), character_states.get(entity), controllers.get(entity).map(|c| &c.inputs), - inventories.get(entity), ) { Skillbar::new( global_state, @@ -1942,12 +2009,11 @@ impl Hud { &self.rot_imgs, &stats, &health, - &loadout, + &inventory, &energy, //&character_state, self.pulse, //&controller, - &inventory, &self.hotbar, tooltip_manager, &mut self.slot_manager, @@ -2381,7 +2447,11 @@ impl Hud { slot::Event::Dragged(a, b) => { // Swap between slots if let (Some(a), Some(b)) = (to_slot(a), to_slot(b)) { - events.push(Event::SwapSlots(a, b)); + events.push(Event::SwapSlots { + slot_a: a, + slot_b: b, + bypass_dialog: false, + }); } else if let (Inventory(i), Hotbar(h)) = (a, b) { self.hotbar.add_inventory_link(h, i.0); events.push(Event::ChangeHotbarState(Box::new(self.hotbar.to_owned()))); @@ -2402,12 +2472,19 @@ impl Hud { slot::Event::Used(from) => { // Item used (selected and then clicked again) if let Some(from) = to_slot(from) { - events.push(Event::UseSlot(from)); + events.push(Event::UseSlot { + slot: from, + bypass_dialog: false, + }); } else if let Hotbar(h) = from { + // Used from hotbar self.hotbar.get(h).map(|s| { match s { hotbar::SlotContents::Inventory(i) => { - events.push(Event::UseSlot(comp::slot::Slot::Inventory(i))); + events.push(Event::UseSlot { + slot: comp::slot::Slot::Inventory(i), + bypass_dialog: false, + }); }, hotbar::SlotContents::Ability3 => {}, /* Event::Ability3(true), * sticks */ @@ -2469,7 +2546,10 @@ impl Hud { hotbar.get(slot).map(|s| match s { hotbar::SlotContents::Inventory(i) => { if just_pressed { - events.push(Event::UseSlot(comp::slot::Slot::Inventory(i))); + events.push(Event::UseSlot { + slot: comp::slot::Slot::Inventory(i), + bypass_dialog: false, + }); } }, hotbar::SlotContents::Ability3 => events.push(Event::Ability3(state)), @@ -2499,6 +2579,22 @@ impl Hud { self.force_ungrab = !self.force_ungrab; true }, + WinEvent::InputUpdate(GameInput::AcceptGroupInvite, true) if !self.typing() => { + if let Some(prompt_dialog) = &mut self.show.prompt_dialog { + prompt_dialog.set_outcome_via_keypress(true); + true + } else { + false + } + }, + WinEvent::InputUpdate(GameInput::DeclineGroupInvite, true) if !self.typing() => { + if let Some(prompt_dialog) = &mut self.show.prompt_dialog { + prompt_dialog.set_outcome_via_keypress(false); + true + } else { + false + } + }, // If not showing the ui don't allow keys that change the ui state but do listen for // hotbar keys diff --git a/voxygen/src/hud/prompt_dialog.rs b/voxygen/src/hud/prompt_dialog.rs new file mode 100644 index 0000000000..3c358f08fd --- /dev/null +++ b/voxygen/src/hud/prompt_dialog.rs @@ -0,0 +1,192 @@ +use super::{img_ids::Imgs, TEXT_COLOR, UI_HIGHLIGHT_0}; +use crate::{ + hud::{Event, PromptDialogSettings}, + i18n::Localization, + settings::Settings, + ui::fonts::Fonts, + window::GameInput, + AssetHandle, +}; +use conrod_core::{ + widget::{self, Button, Image, Text}, + widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, +}; + +widget_ids! { + struct Ids { + top, + mid, + bot, + text, + accept_txt, // optional timer + accept_key, //button with label + decline_txt, + decline_key, + prompt_txt, + } +} +#[derive(WidgetCommon)] +pub struct PromptDialog<'a> { + imgs: &'a Imgs, + fonts: &'a Fonts, + #[conrod(common_builder)] + common: widget::CommonBuilder, + localized_strings: &'a AssetHandle, + settings: &'a Settings, + prompt_dialog_settings: &'a PromptDialogSettings, +} + +impl<'a> PromptDialog<'a> { + #[allow(clippy::too_many_arguments)] // TODO: Pending review in #587 + pub fn new( + imgs: &'a Imgs, + fonts: &'a Fonts, + localized_strings: &'a AssetHandle, + settings: &'a Settings, + prompt_dialog_settings: &'a PromptDialogSettings, + ) -> Self { + Self { + imgs, + fonts, + localized_strings, + common: widget::CommonBuilder::default(), + settings, + prompt_dialog_settings, + } + } +} + +pub struct State { + ids: Ids, +} + +pub enum DialogOutcomeEvent { + Affirmative(Event), + Negative(Option), +} + +impl<'a> Widget for PromptDialog<'a> { + type Event = Option; + type State = State; + type Style = (); + + fn init_state(&self, id_gen: widget::id::Generator) -> Self::State { + State { + ids: Ids::new(id_gen), + } + } + + #[allow(clippy::unused_unit)] // TODO: Pending review in #587 + fn style(&self) -> Self::Style { () } + + fn update(self, args: widget::UpdateArgs) -> Self::Event { + let widget::UpdateArgs { state, ui, .. } = args; + let _localized_strings = self.localized_strings; + let mut event: Option = None; + + let accept_key = self + .settings + .controls + .get_binding(GameInput::AcceptGroupInvite) + .map_or_else(|| "".into(), |key| key.to_string()); + let decline_key = self + .settings + .controls + .get_binding(GameInput::DeclineGroupInvite) + .map_or_else(|| "".into(), |key| key.to_string()); + + // Window + Image::new(self.imgs.prompt_top) + .w_h(276.0, 24.0) + .mid_top_with_margin_on(ui.window, 100.0) + .color(Some(UI_HIGHLIGHT_0)) + .set(state.ids.top, ui); + if !self.prompt_dialog_settings.message.is_empty() { + Image::new(self.imgs.prompt_mid) + .w(276.0) + .h_of(state.ids.prompt_txt) // height relative to content, max height 150 + .down_from(state.ids.top, 0.0) + .color(Some(UI_HIGHLIGHT_0)) + .scroll_kids_vertically() + .set(state.ids.mid, ui); + } + Image::new(self.imgs.prompt_bot) + .w_h(276.0, 35.0) + .down_from(state.ids.mid, 0.0) + .color(Some(UI_HIGHLIGHT_0)) + .set(state.ids.bot, ui); + + // Accept/Decline Buttons + if Button::image(self.imgs.key_button) + .w_h(20.0, 20.0) + .hover_image(self.imgs.close_btn_hover) + .press_image(self.imgs.close_btn_press) + .label(&accept_key) + .image_color(UI_HIGHLIGHT_0) + .label_color(TEXT_COLOR) + .label_font_size(self.fonts.cyri.scale(16)) + .label_font_id(self.fonts.cyri.conrod_id) + .label_y(conrod_core::position::Relative::Scalar(2.5)) + .label_x(conrod_core::position::Relative::Scalar(0.5)) + .bottom_left_with_margins_on(state.ids.bot, 4.0, 6.0) + .set(state.ids.accept_key, ui) + .was_clicked() + || self + .prompt_dialog_settings + .outcome_via_keypress + .map_or(false, |outcome| outcome) + { + // Primary use should be through pressing the key instead of clicking this + event = Some(DialogOutcomeEvent::Affirmative( + self.prompt_dialog_settings.affirmative_event.clone(), + )); + } + Text::new("Accept") + .bottom_right_with_margins_on(state.ids.accept_key, 5.0, -65.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(18)) + .color(TEXT_COLOR) + .set(state.ids.accept_txt, ui); + + if Button::image(self.imgs.key_button) + .w_h(20.0, 20.0) + .hover_image(self.imgs.close_btn_hover) + .press_image(self.imgs.close_btn_press) + .label(&decline_key) + .image_color(UI_HIGHLIGHT_0) + .label_color(TEXT_COLOR) + .label_font_size(self.fonts.cyri.scale(16)) + .label_font_id(self.fonts.cyri.conrod_id) + .label_y(conrod_core::position::Relative::Scalar(2.5)) + .label_x(conrod_core::position::Relative::Scalar(0.5)) + .bottom_right_with_margins_on(state.ids.bot, 4.0, 6.0) + .set(state.ids.decline_key, ui) + .was_clicked() + || self + .prompt_dialog_settings + .outcome_via_keypress + .map_or(false, |outcome| !outcome) + { + event = Some(DialogOutcomeEvent::Negative( + self.prompt_dialog_settings.negative_event.as_ref().cloned(), + )); + } + Text::new("Decline") + .bottom_left_with_margins_on(state.ids.decline_key, 4.0, -65.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(18)) + .color(TEXT_COLOR) + .set(state.ids.decline_txt, ui); + + // Prompt Description + Text::new(&self.prompt_dialog_settings.message) + .mid_top_with_margin_on(state.ids.mid,0.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(18)) + .color(TEXT_COLOR) + .w(260.0) // Text stays within frame + .set(state.ids.prompt_txt, ui); + + event + } +} diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs index 9e34396478..ecb267623c 100644 --- a/voxygen/src/hud/settings_window.rs +++ b/voxygen/src/hud/settings_window.rs @@ -317,6 +317,7 @@ pub enum Event { ChangeStopAutoWalkOnInput(bool), } +#[derive(Clone)] pub enum ScaleChange { ToAbsolute, ToRelative, diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index 60a5697764..fa0498df28 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -16,11 +16,12 @@ use crate::{ GlobalState, }; use common::comp::{ + inventory::slot::EquipSlot, item::{ tool::{AbilityMap, Tool, ToolKind}, Hands, ItemKind, }, - Energy, Health, Inventory, Loadout, Stats, + Energy, Health, Inventory, Stats, }; use conrod_core::{ color, @@ -125,11 +126,10 @@ pub struct Skillbar<'a> { rot_imgs: &'a ImgsRot, stats: &'a Stats, health: &'a Health, - loadout: &'a Loadout, + inventory: &'a Inventory, energy: &'a Energy, // character_state: &'a CharacterState, // controller: &'a ControllerInputs, - inventory: &'a Inventory, hotbar: &'a hotbar::State, tooltip_manager: &'a mut TooltipManager, slot_manager: &'a mut slots::SlotManager, @@ -151,12 +151,11 @@ impl<'a> Skillbar<'a> { rot_imgs: &'a ImgsRot, stats: &'a Stats, health: &'a Health, - loadout: &'a Loadout, + inventory: &'a Inventory, energy: &'a Energy, // character_state: &'a CharacterState, pulse: f32, // controller: &'a ControllerInputs, - inventory: &'a Inventory, hotbar: &'a hotbar::State, tooltip_manager: &'a mut TooltipManager, slot_manager: &'a mut slots::SlotManager, @@ -172,13 +171,12 @@ impl<'a> Skillbar<'a> { rot_imgs, stats, health, - loadout, + inventory, energy, common: widget::CommonBuilder::default(), // character_state, pulse, // controller, - inventory, hotbar, tooltip_manager, slot_manager, @@ -473,13 +471,7 @@ impl<'a> Widget for Skillbar<'a> { .set(state.ids.stamina_txt, ui); } // Slots - let content_source = ( - self.hotbar, - self.inventory, - self.loadout, - self.energy, - self.ability_map, - ); // TODO: avoid this + let content_source = (self.hotbar, self.inventory, self.energy, self.ability_map); // TODO: avoid this let image_source = (self.item_imgs, self.imgs); let mut slot_maker = SlotMaker { // TODO: is a separate image needed for the frame? @@ -529,10 +521,9 @@ impl<'a> Widget for Skillbar<'a> { .get(i) .map(|item| (item.name(), item.description())), hotbar::SlotContents::Ability3 => content_source - .2 - .active_item - .as_ref() - .map(|i| i.item.kind()) + .1 + .equipped(EquipSlot::Mainhand) + .map(|i| i.kind()) .and_then(|kind| match kind { ItemKind::Tool(Tool { kind, .. }) => match kind { ToolKind::Hammer => Some(( @@ -630,7 +621,11 @@ impl<'a> Widget for Skillbar<'a> { .right_from(state.ids.slot5, 0.0) .set(state.ids.m1_slot_bg, ui); Button::image( - match self.loadout.active_item.as_ref().map(|i| i.item.kind()) { + match self + .inventory + .equipped(EquipSlot::Mainhand) + .map(|i| i.kind()) + { Some(ItemKind::Tool(Tool { kind, .. })) => match kind { ToolKind::Sword => self.imgs.twohsword_m1, ToolKind::Dagger => self.imgs.onehdagger_m1, @@ -655,19 +650,19 @@ impl<'a> Widget for Skillbar<'a> { .right_from(state.ids.m1_slot_bg, 0.0) .set(state.ids.m2_slot, ui); - let active_tool = match self.loadout.active_item.as_ref().map(|i| i.item.kind()) { - Some(ItemKind::Tool(tool)) => Some(tool), - _ => None, - }; + fn get_tool(inventory: &Inventory, equip_slot: EquipSlot) -> Option<&Tool> { + match inventory.equipped(equip_slot).map(|i| i.kind()) { + Some(ItemKind::Tool(tool)) => Some(tool), + _ => None, + } + } - let second_tool = match self.loadout.second_item.as_ref().map(|i| i.item.kind()) { - Some(ItemKind::Tool(tool)) => Some(tool), - _ => None, - }; + let active_tool = get_tool(self.inventory, EquipSlot::Mainhand); + let second_tool = get_tool(self.inventory, EquipSlot::Offhand); let tool = match ( - active_tool.map(|t| t.kind.hands()), - second_tool.map(|t| t.kind.hands()), + active_tool.map(|x| x.kind.hands()), + second_tool.map(|x| x.kind.hands()), ) { (Some(Hands::TwoHand), _) => active_tool, (_, Some(Hands::OneHand)) => second_tool, diff --git a/voxygen/src/hud/slots.rs b/voxygen/src/hud/slots.rs index f14f633921..5acfd790c6 100644 --- a/voxygen/src/hud/slots.rs +++ b/voxygen/src/hud/slots.rs @@ -9,13 +9,14 @@ use common::comp::{ tool::{AbilityMap, ToolKind}, ItemKind, }, - Energy, Inventory, Loadout, + slot::InvSlotId, + Energy, Inventory, }; use conrod_core::{image, Color}; pub use common::comp::slot::{ArmorSlot, EquipSlot}; -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum SlotKind { Inventory(InventorySlot), Equip(EquipSlot), @@ -25,8 +26,8 @@ pub enum SlotKind { pub type SlotManager = slot::SlotManager; -#[derive(Clone, Copy, PartialEq)] -pub struct InventorySlot(pub usize); +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct InventorySlot(pub InvSlotId); impl SlotKey for InventorySlot { type ImageKey = ItemKey; @@ -47,32 +48,15 @@ impl SlotKey for InventorySlot { } } -impl SlotKey for EquipSlot { +impl SlotKey for EquipSlot { type ImageKey = ItemKey; - fn image_key(&self, source: &Loadout) -> Option<(Self::ImageKey, Option)> { - let item = match self { - EquipSlot::Armor(ArmorSlot::Shoulders) => source.shoulder.as_ref(), - EquipSlot::Armor(ArmorSlot::Chest) => source.chest.as_ref(), - EquipSlot::Armor(ArmorSlot::Belt) => source.belt.as_ref(), - EquipSlot::Armor(ArmorSlot::Hands) => source.hand.as_ref(), - EquipSlot::Armor(ArmorSlot::Legs) => source.pants.as_ref(), - EquipSlot::Armor(ArmorSlot::Feet) => source.foot.as_ref(), - EquipSlot::Armor(ArmorSlot::Back) => source.back.as_ref(), - EquipSlot::Armor(ArmorSlot::Ring) => source.ring.as_ref(), - EquipSlot::Armor(ArmorSlot::Neck) => source.neck.as_ref(), - EquipSlot::Armor(ArmorSlot::Head) => source.head.as_ref(), - EquipSlot::Armor(ArmorSlot::Tabard) => source.tabard.as_ref(), - EquipSlot::Mainhand => source.active_item.as_ref().map(|i| &i.item), - EquipSlot::Offhand => source.second_item.as_ref().map(|i| &i.item), - EquipSlot::Lantern => source.lantern.as_ref(), - EquipSlot::Glider => source.glider.as_ref(), - }; - + fn image_key(&self, source: &Inventory) -> Option<(Self::ImageKey, Option)> { + let item = source.equipped(*self); item.map(|i| (i.into(), None)) } - fn amount(&self, _: &Loadout) -> Option { None } + fn amount(&self, _: &Inventory) -> Option { None } fn image_id(key: &Self::ImageKey, source: &ItemImgs) -> image::Id { source.img_id_or_not_found_img(key.clone()) @@ -90,13 +74,7 @@ pub enum HotbarImage { BowJumpBurst, } -type HotbarSource<'a> = ( - &'a hotbar::State, - &'a Inventory, - &'a Loadout, - &'a Energy, - &'a AbilityMap, -); +type HotbarSource<'a> = (&'a hotbar::State, &'a Inventory, &'a Energy, &'a AbilityMap); type HotbarImageSource<'a> = (&'a ItemImgs, &'a img_ids::Imgs); impl<'a> SlotKey, HotbarImageSource<'a>> for HotbarSlot { @@ -104,7 +82,7 @@ impl<'a> SlotKey, HotbarImageSource<'a>> for HotbarSlot { fn image_key( &self, - (hotbar, inventory, loadout, energy, ability_map): &HotbarSource<'a>, + (hotbar, inventory, energy, ability_map): &HotbarSource<'a>, ) -> Option<(Self::ImageKey, Option)> { hotbar.get(*self).and_then(|contents| match contents { hotbar::SlotContents::Inventory(idx) => inventory @@ -112,7 +90,7 @@ impl<'a> SlotKey, HotbarImageSource<'a>> for HotbarSlot { .map(|item| HotbarImage::Item(item.into())) .map(|i| (i, None)), hotbar::SlotContents::Ability3 => { - let tool = match loadout.active_item.as_ref().map(|i| i.item.kind()) { + let tool = match inventory.equipped(EquipSlot::Mainhand).map(|i| i.kind()) { Some(ItemKind::Tool(tool)) => Some(tool), _ => None, }; @@ -146,7 +124,7 @@ impl<'a> SlotKey, HotbarImageSource<'a>> for HotbarSlot { }) } - fn amount(&self, (hotbar, inventory, _, _, _): &HotbarSource<'a>) -> Option { + fn amount(&self, (hotbar, inventory, _, _): &HotbarSource<'a>) -> Option { hotbar .get(*self) .and_then(|content| match content { diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index 2464663704..c427b70f69 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -3,7 +3,7 @@ use common::comp::item::{ tool::{Tool, ToolKind}, ItemDesc, ItemKind, }; -use std::borrow::Cow; +use std::{borrow::Cow, fmt::Write}; pub fn loadout_slot_text<'a>( item: Option<&'a impl ItemDesc>, @@ -20,7 +20,9 @@ pub fn loadout_slot_text<'a>( pub fn item_text<'a>(item: &'a impl ItemDesc) -> (&'_ str, Cow<'a, str>) { let desc: Cow = match item.kind() { - ItemKind::Armor(armor) => Cow::Owned(armor_desc(&armor, item.description())), + ItemKind::Armor(armor) => { + Cow::Owned(armor_desc(armor, item.description(), item.num_slots())) + }, ItemKind::Tool(tool) => Cow::Owned(tool_desc(&tool, item.description())), ItemKind::Glider(_glider) => Cow::Owned(glider_desc(item.description())), ItemKind::Consumable { .. } => Cow::Owned(consumable_desc(item.description())), @@ -51,7 +53,7 @@ fn ingredient_desc(desc: &str) -> String { format!("Crafting Ingredient\n\n{}", fn lantern_desc(desc: &str) -> String { format!("Lantern\n\n{}\n\n", desc) } -fn armor_desc(armor: &Armor, desc: &str) -> String { +fn armor_desc(armor: &Armor, desc: &str, slots: u16) -> String { // TODO: localization let kind = match armor.kind { ArmorKind::Shoulder(_) => "Shoulders", @@ -65,20 +67,26 @@ fn armor_desc(armor: &Armor, desc: &str) -> String { ArmorKind::Neck(_) => "Neck", ArmorKind::Head(_) => "Head", ArmorKind::Tabard(_) => "Tabard", + ArmorKind::Bag(_) => "Bag", }; - let armor = match armor.get_protection() { + + let protection = match armor.get_protection() { Protection::Normal(a) => a.to_string(), Protection::Invincible => "Inf".to_string(), }; + let mut description = format!("{}\n\nArmor: {}", kind, protection); + if !desc.is_empty() { - format!( - "{}\n\nArmor: {}\n\n{}\n\n", - kind, armor, desc - ) - } else { - format!("{}\n\nArmor: {}\n\n", kind, armor) + write!(&mut description, "\n\n{}", desc).unwrap(); } + + if slots > 0 { + write!(&mut description, "\n\nSlots: {}", slots).unwrap(); + } + + write!(&mut description, "\n\n").unwrap(); + description } fn tool_desc(tool: &Tool, desc: &str) -> String { diff --git a/voxygen/src/logging.rs b/voxygen/src/logging.rs index e0b4a962df..e7ee26dac7 100644 --- a/voxygen/src/logging.rs +++ b/voxygen/src/logging.rs @@ -45,6 +45,11 @@ pub fn init(settings: &Settings) -> Vec { .add_directive("uvth=warn".parse().unwrap()) .add_directive("tiny_http=warn".parse().unwrap()) .add_directive("mio::sys::windows=debug".parse().unwrap()) + .add_directive( + "veloren_common::comp::inventory::slot=info" + .parse() + .unwrap(), + ) .add_directive( "veloren_server::persistence::character=info" .parse() diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index cc836594a5..7219fc1d87 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -38,19 +38,22 @@ impl CharSelectionState { } } - fn get_humanoid_body_loadout<'a>( + fn get_humanoid_body_inventory<'a>( char_selection_ui: &'a CharSelectionUi, client: &'a Client, - ) -> (Option, Option<&'a comp::Loadout>) { + ) -> ( + Option, + Option<&'a comp::inventory::Inventory>, + ) { char_selection_ui - .display_body_loadout(&client.character_list().characters) - .map(|(body, loadout)| { + .display_body_inventory(&client.character_list().characters) + .map(|(body, inventory)| { ( match body { comp::Body::Humanoid(body) => Some(body), _ => None, }, - Some(loadout), + Some(inventory), ) }) .unwrap_or_default() @@ -137,7 +140,7 @@ impl PlayState for CharSelectionState { { let client = self.client.borrow(); let (humanoid_body, loadout) = - Self::get_humanoid_body_loadout(&self.char_selection_ui, &client); + Self::get_humanoid_body_inventory(&self.char_selection_ui, &client); // Maintain the scene. let scene_data = scene::SceneData { @@ -222,7 +225,7 @@ impl PlayState for CharSelectionState { fn render(&mut self, renderer: &mut Renderer, _: &Settings) { let client = self.client.borrow(); let (humanoid_body, loadout) = - Self::get_humanoid_body_loadout(&self.char_selection_ui, &client); + Self::get_humanoid_body_inventory(&self.char_selection_ui, &client); // Render the scene. self.scene diff --git a/voxygen/src/menu/char_selection/ui/mod.rs b/voxygen/src/menu/char_selection/ui/mod.rs index f94d071af1..0345961721 100644 --- a/voxygen/src/menu/char_selection/ui/mod.rs +++ b/voxygen/src/menu/char_selection/ui/mod.rs @@ -24,7 +24,7 @@ use client::Client; use common::{ assets::AssetHandle, character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER}, - comp::{self, humanoid, item::tool::AbilityMap}, + comp::{self, humanoid, inventory::slot::EquipSlot, Inventory, Item}, LoadoutBuilder, }; //ImageFrame, Tooltip, @@ -148,7 +148,7 @@ enum Mode { Create { name: String, // TODO: default to username body: humanoid::Body, - loadout: Box, + inventory: Box, tool: &'static str, body_type_buttons: [button::State; 2], @@ -177,24 +177,21 @@ impl Mode { } } - pub fn create(name: String, map: &AbilityMap) -> Self { + pub fn create(name: String) -> Self { let tool = STARTER_SWORD; let loadout = LoadoutBuilder::new() .defaults() - .active_item(Some(LoadoutBuilder::default_item_config_from_str( - tool, map, - ))) + .active_item(Some(Item::new_from_asset_expect(tool))) .build(); - let loadout = Box::new(loadout); + let inventory = Box::new(Inventory::new_with_loadout(loadout)); Self::Create { name, body: humanoid::Body::random(), - loadout, + inventory, tool, - body_type_buttons: Default::default(), species_buttons: Default::default(), tool_buttons: Default::default(), @@ -700,7 +697,7 @@ impl Controls { Mode::Create { name, body, - loadout: _, + inventory: _, tool, ref mut scroll, ref mut body_type_buttons, @@ -1203,13 +1200,7 @@ impl Controls { .into() } - fn update( - &mut self, - message: Message, - events: &mut Vec, - characters: &[CharacterItem], - map: &AbilityMap, - ) { + fn update(&mut self, message: Message, events: &mut Vec, characters: &[CharacterItem]) { match message { Message::Back => { if matches!(&self.mode, Mode::Create { .. }) { @@ -1237,7 +1228,7 @@ impl Controls { }, Message::NewCharacter => { if matches!(&self.mode, Mode::Select { .. }) { - self.mode = Mode::create(String::new(), map); + self.mode = Mode::create(String::new()); } }, Message::CreateCharacter => { @@ -1271,10 +1262,15 @@ impl Controls { } }, Message::Tool(value) => { - if let Mode::Create { tool, loadout, .. } = &mut self.mode { + if let Mode::Create { + tool, inventory, .. + } = &mut self.mode + { *tool = value; - loadout.active_item = - Some(LoadoutBuilder::default_item_config_from_str(*tool, map)); + inventory.replace_loadout_item( + EquipSlot::Mainhand, + Some(Item::new_from_asset_expect(*tool)), + ); } }, Message::RandomizeCharacter => { @@ -1366,16 +1362,18 @@ impl Controls { } /// Get the character to display - pub fn display_body_loadout<'a>( + pub fn display_body_inventory<'a>( &'a self, characters: &'a [CharacterItem], - ) -> Option<(comp::Body, &'a comp::Loadout)> { + ) -> Option<(comp::Body, &'a comp::inventory::Inventory)> { match &self.mode { Mode::Select { .. } => self .selected .and_then(|id| characters.iter().find(|i| i.character.id == Some(id))) - .map(|i| (i.body, &i.loadout)), - Mode::Create { loadout, body, .. } => Some((comp::Body::Humanoid(*body), loadout)), + .map(|i| (i.body, &i.inventory)), + Mode::Create { + inventory, body, .. + } => Some((comp::Body::Humanoid(*body), inventory)), } } } @@ -1424,11 +1422,11 @@ impl CharSelectionUi { } } - pub fn display_body_loadout<'a>( + pub fn display_body_inventory<'a>( &'a self, characters: &'a [CharacterItem], - ) -> Option<(comp::Body, &'a comp::Loadout)> { - self.controls.display_body_loadout(characters) + ) -> Option<(comp::Body, &'a comp::inventory::Inventory)> { + self.controls.display_body_inventory(characters) } pub fn handle_event(&mut self, event: window::Event) -> bool { @@ -1497,12 +1495,8 @@ impl CharSelectionUi { } messages.into_iter().for_each(|message| { - self.controls.update( - message, - &mut events, - &client.character_list().characters, - &*client.state().ability_map(), - ) + self.controls + .update(message, &mut events, &client.character_list().characters) }); events diff --git a/voxygen/src/profile.rs b/voxygen/src/profile.rs index e1285031f9..81ea9b96af 100644 --- a/voxygen/src/profile.rs +++ b/voxygen/src/profile.rs @@ -1,5 +1,5 @@ use crate::{hud, settings}; -use common::character::CharacterId; +use common::{character::CharacterId, comp::slot::InvSlotId}; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; use std::{fs, io::Write, path::PathBuf}; @@ -13,23 +13,25 @@ pub struct CharacterProfile { pub hotbar_slots: [Option; 10], } -const DEFAULT_SLOTS: [Option; 10] = [ - None, - None, - None, - None, - None, - Some(hud::HotbarSlotContents::Inventory(0)), - Some(hud::HotbarSlotContents::Inventory(1)), - None, - None, - None, -]; +const fn default_slots() -> [Option; 10] { + [ + None, + None, + None, + None, + None, + Some(hud::HotbarSlotContents::Inventory(InvSlotId::new(0, 0))), + Some(hud::HotbarSlotContents::Inventory(InvSlotId::new(0, 1))), + None, + None, + None, + ] +} impl Default for CharacterProfile { fn default() -> Self { CharacterProfile { - hotbar_slots: DEFAULT_SLOTS, + hotbar_slots: default_slots(), } } } @@ -127,7 +129,7 @@ impl Profile { .get(server) .and_then(|s| s.characters.get(&character_id)) .map(|c| c.hotbar_slots) - .unwrap_or(DEFAULT_SLOTS) + .unwrap_or_else(default_slots) } /// Set the hotbar_slots for the requested character_id. @@ -222,6 +224,7 @@ impl Profile { #[cfg(test)] mod tests { use super::*; + use common::comp::inventory::slot::InvSlotId; #[test] fn test_get_slots_with_empty_profile() { @@ -233,8 +236,8 @@ mod tests { None, None, None, - Some(hud::HotbarSlotContents::Inventory(0)), - Some(hud::HotbarSlotContents::Inventory(1)), + Some(hud::HotbarSlotContents::Inventory(InvSlotId::new(0, 0))), + Some(hud::HotbarSlotContents::Inventory(InvSlotId::new(0, 1))), None, None, None, @@ -250,8 +253,8 @@ mod tests { None, None, None, - Some(hud::HotbarSlotContents::Inventory(0)), - Some(hud::HotbarSlotContents::Inventory(1)), + Some(hud::HotbarSlotContents::Inventory(InvSlotId::new(0, 0))), + Some(hud::HotbarSlotContents::Inventory(InvSlotId::new(0, 1))), None, None, None, diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index 9b1cd2fd08..38c90a4eaf 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -10,11 +10,15 @@ use anim::Skeleton; use common::{ assets::AssetHandle, comp::{ + inventory::{ + slot::{ArmorSlot, EquipSlot}, + Inventory, + }, item::{ armor::{Armor, ArmorKind}, ItemKind, }, - CharacterState, Loadout, + CharacterState, }, figure::Segment, vol::BaseVol, @@ -106,7 +110,7 @@ pub(super) struct CharacterCacheKey { } impl CharacterCacheKey { - fn from(cs: Option<&CharacterState>, camera_mode: CameraMode, loadout: &Loadout) -> Self { + fn from(cs: Option<&CharacterState>, camera_mode: CameraMode, inventory: &Inventory) -> Self { let is_first_person = match camera_mode { CameraMode::FirstPerson => true, CameraMode::ThirdPerson | CameraMode::Freefly => false, @@ -133,7 +137,9 @@ impl CharacterCacheKey { shoulder: if let Some(ItemKind::Armor(Armor { kind: ArmorKind::Shoulder(armor), .. - })) = loadout.shoulder.as_ref().map(|i| i.kind()) + })) = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Shoulders)) + .map(|i| i.kind()) { Some(armor.clone()) } else { @@ -142,7 +148,9 @@ impl CharacterCacheKey { chest: if let Some(ItemKind::Armor(Armor { kind: ArmorKind::Chest(armor), .. - })) = loadout.chest.as_ref().map(|i| i.kind()) + })) = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Chest)) + .map(|i| i.kind()) { Some(armor.clone()) } else { @@ -151,7 +159,9 @@ impl CharacterCacheKey { belt: if let Some(ItemKind::Armor(Armor { kind: ArmorKind::Belt(armor), .. - })) = loadout.belt.as_ref().map(|i| i.kind()) + })) = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Belt)) + .map(|i| i.kind()) { Some(armor.clone()) } else { @@ -160,7 +170,9 @@ impl CharacterCacheKey { back: if let Some(ItemKind::Armor(Armor { kind: ArmorKind::Back(armor), .. - })) = loadout.back.as_ref().map(|i| i.kind()) + })) = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Back)) + .map(|i| i.kind()) { Some(armor.clone()) } else { @@ -169,7 +181,9 @@ impl CharacterCacheKey { pants: if let Some(ItemKind::Armor(Armor { kind: ArmorKind::Pants(armor), .. - })) = loadout.pants.as_ref().map(|i| i.kind()) + })) = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Legs)) + .map(|i| i.kind()) { Some(armor.clone()) } else { @@ -179,27 +193,25 @@ impl CharacterCacheKey { }, tool: if are_tools_visible { Some(CharacterToolKey { - active: loadout - .active_item - .as_ref() - .map(|i| i.item.item_definition_id().to_owned()), - second: loadout - .second_item - .as_ref() - .map(|i| i.item.item_definition_id().to_owned()), + active: inventory + .equipped(EquipSlot::Mainhand) + .map(|i| i.item_definition_id().to_owned()), + second: inventory + .equipped(EquipSlot::Offhand) + .map(|i| i.item_definition_id().to_owned()), }) } else { None }, lantern: if let Some(ItemKind::Lantern(lantern)) = - loadout.lantern.as_ref().map(|i| i.kind()) + inventory.equipped(EquipSlot::Lantern).map(|i| i.kind()) { Some(lantern.kind.clone()) } else { None }, glider: if let Some(ItemKind::Glider(glider)) = - loadout.glider.as_ref().map(|i| i.kind()) + inventory.equipped(EquipSlot::Glider).map(|i| i.kind()) { Some(glider.kind.clone()) } else { @@ -208,7 +220,9 @@ impl CharacterCacheKey { hand: if let Some(ItemKind::Armor(Armor { kind: ArmorKind::Hand(armor), .. - })) = loadout.hand.as_ref().map(|i| i.kind()) + })) = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Hands)) + .map(|i| i.kind()) { Some(armor.clone()) } else { @@ -217,7 +231,9 @@ impl CharacterCacheKey { foot: if let Some(ItemKind::Armor(Armor { kind: ArmorKind::Foot(armor), .. - })) = loadout.foot.as_ref().map(|i| i.kind()) + })) = inventory + .equipped(EquipSlot::Armor(ArmorSlot::Feet)) + .map(|i| i.kind()) { Some(armor.clone()) } else { @@ -261,7 +277,7 @@ where // TODO: If we ever convert to using an atlas here, use this. _col_lights: &super::FigureColLights, body: Skel::Body, - loadout: Option<&Loadout>, + inventory: Option<&Inventory>, // TODO: Consider updating the tick by putting it in a Cell. _tick: u64, camera_mode: CameraMode, @@ -270,11 +286,11 @@ where // TODO: Use raw entries to avoid lots of allocation (among other things). let key = FigureKey { body, - extra: loadout.map(|loadout| { + extra: inventory.map(|inventory| { Arc::new(CharacterCacheKey::from( character_state, camera_mode, - loadout, + inventory, )) }), }; @@ -291,7 +307,7 @@ where renderer: &mut Renderer, col_lights: &mut super::FigureColLights, body: Skel::Body, - loadout: Option<&Loadout>, + inventory: Option<&Inventory>, tick: u64, camera_mode: CameraMode, character_state: Option<&CharacterState>, @@ -305,11 +321,11 @@ where let skeleton_attr = (&body).into(); let key = FigureKey { body, - extra: loadout.map(|loadout| { + extra: inventory.map(|inventory| { Arc::new(CharacterCacheKey::from( character_state, camera_mode, - loadout, + inventory, )) }), }; diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index d25306bdf6..4edd0c761e 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -27,8 +27,9 @@ use anim::{ }; use common::{ comp::{ + inventory::slot::EquipSlot, item::{ItemKind, ToolKind}, - Body, CharacterState, Health, Item, Last, LightAnimation, LightEmitter, Loadout, Ori, + Body, CharacterState, Health, Inventory, Item, Last, LightAnimation, LightEmitter, Ori, PhysicsState, Pos, Scale, Vel, }, resources::DeltaTime, @@ -550,7 +551,7 @@ impl FigureMgr { last_character, physics, health, - loadout, + inventory, item, ), ) in ( @@ -564,7 +565,7 @@ impl FigureMgr { ecs.read_storage::>().maybe(), &ecs.read_storage::(), ecs.read_storage::().maybe(), - ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), ) .join() @@ -685,18 +686,18 @@ impl FigureMgr { let mut state_animation_rate = 1.0; - let active_item_kind = loadout - .and_then(|l| l.active_item.as_ref()) - .map(|i| i.item.kind()); + let active_item_kind = inventory + .and_then(|i| i.equipped(EquipSlot::Mainhand)) + .map(|i| i.kind()); let active_tool_kind = if let Some(ItemKind::Tool(tool)) = active_item_kind { Some(tool.kind) } else { None }; - let second_item_kind = loadout - .and_then(|l| l.second_item.as_ref()) - .map(|i| i.item.kind()); + let second_item_kind = inventory + .and_then(|i| i.equipped(EquipSlot::Offhand)) + .map(|i| i.kind()); let second_tool_kind = if let Some(ItemKind::Tool(tool)) = second_item_kind { Some(tool.kind) @@ -710,7 +711,7 @@ impl FigureMgr { renderer, &mut self.col_lights, *body, - loadout, + inventory, tick, player_camera_mode, player_character_state, @@ -1338,7 +1339,7 @@ impl FigureMgr { renderer, &mut self.col_lights, *body, - loadout, + inventory, tick, player_camera_mode, player_character_state, @@ -1488,7 +1489,7 @@ impl FigureMgr { renderer, &mut self.col_lights, *body, - loadout, + inventory, tick, player_camera_mode, player_character_state, @@ -1765,7 +1766,7 @@ impl FigureMgr { renderer, &mut self.col_lights, *body, - loadout, + inventory, tick, player_camera_mode, player_character_state, @@ -2059,7 +2060,7 @@ impl FigureMgr { renderer, &mut self.col_lights, *body, - loadout, + inventory, tick, player_camera_mode, player_character_state, @@ -2165,7 +2166,7 @@ impl FigureMgr { renderer, &mut self.col_lights, *body, - loadout, + inventory, tick, player_camera_mode, player_character_state, @@ -2242,7 +2243,7 @@ impl FigureMgr { renderer, &mut self.col_lights, *body, - loadout, + inventory, tick, player_camera_mode, player_character_state, @@ -2325,7 +2326,7 @@ impl FigureMgr { renderer, &mut self.col_lights, *body, - loadout, + inventory, tick, player_camera_mode, player_character_state, @@ -2463,7 +2464,7 @@ impl FigureMgr { renderer, &mut self.col_lights, *body, - loadout, + inventory, tick, player_camera_mode, player_character_state, @@ -2550,7 +2551,7 @@ impl FigureMgr { renderer, &mut self.col_lights, *body, - loadout, + inventory, tick, player_camera_mode, player_character_state, @@ -2627,7 +2628,7 @@ impl FigureMgr { renderer, &mut self.col_lights, *body, - loadout, + inventory, tick, player_camera_mode, player_character_state, @@ -3034,7 +3035,7 @@ impl FigureMgr { renderer, &mut self.col_lights, *body, - loadout, + inventory, tick, player_camera_mode, player_character_state, @@ -3139,7 +3140,7 @@ impl FigureMgr { renderer, &mut self.col_lights, *body, - loadout, + inventory, tick, player_camera_mode, player_character_state, @@ -3200,20 +3201,20 @@ impl FigureMgr { ecs.read_storage::().maybe(), &ecs.read_storage::(), ecs.read_storage::().maybe(), - ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), ) .join() // Don't render dead entities .filter(|(_, _, _, _, health, _, _)| health.map_or(true, |h| !h.is_dead)) - .for_each(|(entity, pos, _, body, _, loadout, _)| { + .for_each(|(entity, pos, _, body, _, inventory, _)| { if let Some((locals, bone_consts, model, _)) = self.get_model_for_render( tick, camera, None, entity, body, - loadout, + inventory, false, pos.0, figure_lod_render_distance, @@ -3248,13 +3249,13 @@ impl FigureMgr { let character_state_storage = state.read_storage::(); let character_state = character_state_storage.get(player_entity); - for (entity, pos, _, body, _, loadout, _) in ( + for (entity, pos, _, body, _, inventory, _) in ( &ecs.entities(), &ecs.read_storage::(), ecs.read_storage::().maybe(), &ecs.read_storage::(), ecs.read_storage::().maybe(), - ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), ) .join() @@ -3270,7 +3271,7 @@ impl FigureMgr { character_state, entity, body, - loadout, + inventory, false, pos.0, figure_lod_render_distance, @@ -3309,8 +3310,8 @@ impl FigureMgr { return; } - let loadout_storage = ecs.read_storage::(); - let loadout = loadout_storage.get(player_entity); + let inventory_storage = ecs.read_storage::(); + let inventory = inventory_storage.get(player_entity); if let Some((locals, bone_consts, model, col_lights)) = self.get_model_for_render( tick, @@ -3318,7 +3319,7 @@ impl FigureMgr { character_state, player_entity, body, - loadout, + inventory, true, pos.0, figure_lod_render_distance, @@ -3345,7 +3346,7 @@ impl FigureMgr { character_state: Option<&CharacterState>, entity: EcsEntity, body: &Body, - loadout: Option<&Loadout>, + inventory: Option<&Inventory>, is_player: bool, pos: vek::Vec3, figure_lod_render_distance: f32, @@ -3404,7 +3405,7 @@ impl FigureMgr { model_cache.get_model( col_lights, *body, - loadout, + inventory, tick, player_camera_mode, character_state, @@ -3421,7 +3422,7 @@ impl FigureMgr { quadruped_small_model_cache.get_model( col_lights, *body, - loadout, + inventory, tick, player_camera_mode, character_state, @@ -3438,7 +3439,7 @@ impl FigureMgr { quadruped_medium_model_cache.get_model( col_lights, *body, - loadout, + inventory, tick, player_camera_mode, character_state, @@ -3455,7 +3456,7 @@ impl FigureMgr { quadruped_low_model_cache.get_model( col_lights, *body, - loadout, + inventory, tick, player_camera_mode, character_state, @@ -3472,7 +3473,7 @@ impl FigureMgr { bird_medium_model_cache.get_model( col_lights, *body, - loadout, + inventory, tick, player_camera_mode, character_state, @@ -3489,7 +3490,7 @@ impl FigureMgr { fish_medium_model_cache.get_model( col_lights, *body, - loadout, + inventory, tick, player_camera_mode, character_state, @@ -3506,7 +3507,7 @@ impl FigureMgr { theropod_model_cache.get_model( col_lights, *body, - loadout, + inventory, tick, player_camera_mode, character_state, @@ -3523,7 +3524,7 @@ impl FigureMgr { dragon_model_cache.get_model( col_lights, *body, - loadout, + inventory, tick, player_camera_mode, character_state, @@ -3540,7 +3541,7 @@ impl FigureMgr { bird_small_model_cache.get_model( col_lights, *body, - loadout, + inventory, tick, player_camera_mode, character_state, @@ -3557,7 +3558,7 @@ impl FigureMgr { fish_small_model_cache.get_model( col_lights, *body, - loadout, + inventory, tick, player_camera_mode, character_state, @@ -3574,7 +3575,7 @@ impl FigureMgr { biped_large_model_cache.get_model( col_lights, *body, - loadout, + inventory, tick, player_camera_mode, character_state, @@ -3591,7 +3592,7 @@ impl FigureMgr { golem_model_cache.get_model( col_lights, *body, - loadout, + inventory, tick, player_camera_mode, character_state, @@ -3608,7 +3609,7 @@ impl FigureMgr { object_model_cache.get_model( col_lights, *body, - loadout, + inventory, tick, player_camera_mode, character_state, diff --git a/voxygen/src/scene/simple.rs b/voxygen/src/scene/simple.rs index a4825c36a3..3075d747e7 100644 --- a/voxygen/src/scene/simple.rs +++ b/voxygen/src/scene/simple.rs @@ -20,7 +20,11 @@ use anim::{ }; use client::Client; use common::{ - comp::{humanoid, item::ItemKind, Loadout}, + comp::{ + humanoid, + inventory::{slot::EquipSlot, Inventory}, + item::ItemKind, + }, figure::Segment, terrain::BlockKind, vol::{BaseVol, ReadVol}, @@ -247,7 +251,7 @@ impl Scene { &mut self, renderer: &mut Renderer, scene_data: SceneData, - loadout: Option<&Loadout>, + inventory: Option<&Inventory>, ) { self.camera.update( scene_data.time, @@ -297,9 +301,9 @@ impl Scene { self.figure_model_cache .clean(&mut self.col_lights, scene_data.tick); - let active_item_kind = loadout - .and_then(|l| l.active_item.as_ref()) - .map(|i| i.item.kind()); + let active_item_kind = inventory + .and_then(|inv| inv.equipped(EquipSlot::Mainhand)) + .map(|i| i.kind()); let active_tool_kind = if let Some(ItemKind::Tool(tool)) = active_item_kind { Some(tool.kind) @@ -307,9 +311,9 @@ impl Scene { None }; - let second_item_kind = loadout - .and_then(|l| l.second_item.as_ref()) - .map(|i| i.item.kind()); + let second_item_kind = inventory + .and_then(|inv| inv.equipped(EquipSlot::Offhand)) + .map(|i| i.kind()); let second_tool_kind = if let Some(ItemKind::Tool(tool)) = second_item_kind { Some(tool.kind) @@ -335,7 +339,7 @@ impl Scene { renderer, &mut self.col_lights, body, - loadout, + inventory, scene_data.tick, CameraMode::default(), None, @@ -367,7 +371,7 @@ impl Scene { renderer: &mut Renderer, tick: u64, body: Option, - loadout: Option<&Loadout>, + inventory: Option<&Inventory>, ) { renderer.render_skybox( &self.skybox.model, @@ -380,7 +384,7 @@ impl Scene { let model = &self.figure_model_cache.get_model( &self.col_lights, body, - loadout, + inventory, tick, CameraMode::default(), None, diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index bc9c464650..e97bd1e594 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -1,21 +1,15 @@ -use crate::{ - audio::sfx::SfxEvent, - ecs::MyEntity, - hud::{DebugInfo, Event as HudEvent, Hud, HudInfo, PressBehavior}, - i18n::{i18n_asset_key, Localization}, - key_state::KeyState, - menu::char_selection::CharSelectionState, - render::Renderer, - scene::{camera, CameraMode, Scene, SceneData}, - settings::{ControlSettings, Settings}, - window::{AnalogGameInput, Event, GameInput}, - Direction, Error, GlobalState, PlayState, PlayStateResult, -}; +use std::{cell::RefCell, rc::Rc, time::Duration}; + +use ordered_float::OrderedFloat; +use specs::{Join, WorldExt}; +use tracing::{error, info}; +use vek::*; + use client::{self, Client}; use common::{ assets::AssetExt, comp, - comp::{ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel}, + comp::{inventory::slot::Slot, ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel}, consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE}, outcome::Outcome, span, @@ -27,11 +21,20 @@ use common::{ vol::ReadVol, }; use common_net::msg::PresenceKind; -use ordered_float::OrderedFloat; -use specs::{Join, WorldExt}; -use std::{cell::RefCell, rc::Rc, time::Duration}; -use tracing::{error, info}; -use vek::*; + +use crate::{ + audio::sfx::SfxEvent, + ecs::MyEntity, + hud::{DebugInfo, Event as HudEvent, Hud, HudInfo, PressBehavior, PromptDialogSettings}, + i18n::{i18n_asset_key, Localization}, + key_state::KeyState, + menu::char_selection::CharSelectionState, + render::Renderer, + scene::{camera, CameraMode, Scene, SceneData}, + settings::{ControlSettings, Settings}, + window::{AnalogGameInput, Event, GameInput}, + Direction, Error, GlobalState, PlayState, PlayStateResult, +}; /// The action to perform after a tick enum TickAction { @@ -923,8 +926,133 @@ impl PlayState for SessionState { let mut client = self.client.borrow_mut(); client.remove_buff(buff_id); }, - HudEvent::UseSlot(x) => self.client.borrow_mut().use_slot(x), - HudEvent::SwapSlots(a, b) => self.client.borrow_mut().swap_slots(a, b), + HudEvent::UseSlot { + slot, + bypass_dialog, + } => { + let mut move_allowed = true; + + if !bypass_dialog { + if let Some(inventory) = self + .client + .borrow() + .state() + .ecs() + .read_storage::() + .get(self.client.borrow().entity()) + { + match slot { + comp::slot::Slot::Inventory(inv_slot) => { + let slot_deficit = inventory.free_after_equip(inv_slot); + if slot_deficit < 0 { + self.hud.set_prompt_dialog(PromptDialogSettings::new( + format!( + "Equipping this item will result in \ + insufficient inventory space to hold the \ + items in your inventory and {} items will \ + drop on the floor. Do you wish to continue?", + slot_deficit.abs() + ), + HudEvent::UseSlot { + slot, + bypass_dialog: true, + }, + None, + )); + move_allowed = false; + } + }, + comp::slot::Slot::Equip(equip_slot) => { + // Ensure there is a free slot that is not provided by the + // item being unequipped + let free_slots = + inventory.free_slots_minus_equipped_item(equip_slot); + if free_slots > 0 { + let slot_deficit = + inventory.free_after_unequip(equip_slot); + if slot_deficit < 0 { + self.hud.set_prompt_dialog( + PromptDialogSettings::new( + format!( + "Unequipping this item will result \ + in insufficient inventory space to \ + hold the items in your inventory and \ + {} items will drop on the floor. Do \ + you wish to continue?", + slot_deficit.abs() + ), + HudEvent::UseSlot { + slot, + bypass_dialog: true, + }, + None, + ), + ); + move_allowed = false; + } + } else { + move_allowed = false; + } + }, + } + }; + } + + if move_allowed { + self.client.borrow_mut().use_slot(slot); + } + }, + HudEvent::SwapSlots { + slot_a, + slot_b, + bypass_dialog, + } => { + let mut move_allowed = true; + if !bypass_dialog { + if let Some(inventory) = self + .client + .borrow() + .state() + .ecs() + .read_storage::() + .get(self.client.borrow().entity()) + { + match (slot_a, slot_b) { + (Slot::Inventory(inv_slot), Slot::Equip(equip_slot)) + | (Slot::Equip(equip_slot), Slot::Inventory(inv_slot)) => { + if !inventory.can_swap(inv_slot, equip_slot) { + move_allowed = false; + } else { + let slot_deficit = + inventory.free_after_swap(equip_slot, inv_slot); + if slot_deficit < 0 { + self.hud.set_prompt_dialog( + PromptDialogSettings::new( + format!( + "This will result in dropping {} \ + item(s) on the ground. Are you sure?", + slot_deficit.abs() + ), + HudEvent::SwapSlots { + slot_a, + slot_b, + bypass_dialog: true, + }, + None, + ), + ); + move_allowed = false; + } + } + }, + _ => {}, + } + } + } + if move_allowed { + self.client.borrow_mut().swap_slots(slot_a, slot_b); + } + }, HudEvent::DropSlot(x) => { let mut client = self.client.borrow_mut(); client.drop_slot(x); diff --git a/voxygen/src/ui/widgets/slot.rs b/voxygen/src/ui/widgets/slot.rs index 74c3b2e1c9..f5b99db232 100644 --- a/voxygen/src/ui/widgets/slot.rs +++ b/voxygen/src/ui/widgets/slot.rs @@ -123,6 +123,7 @@ pub struct SlotManager { // Size to display dragged content // Note: could potentially be specialized for each slot if needed drag_img_size: Vec2, + pub mouse_over_slot: Option, } impl SlotManager @@ -137,6 +138,7 @@ where events: Vec::new(), drag_id: gen.next(), drag_img_size, + mouse_over_slot: None, } } @@ -153,11 +155,16 @@ where } } + let input = &ui.global_input().current; + self.mouse_over_slot = input + .widget_under_mouse + .and_then(|x| slot_ids.iter().position(|slot_id| *slot_id == x)) + .map(|x| slots[x]); + // If dragging and mouse is released check if there is a slot widget under the // mouse if let ManagerState::Dragging(_, slot, content_img) = &self.state { let content_img = *content_img; - let input = &ui.global_input().current; if let mouse::ButtonPosition::Up = input.mouse.buttons.left() { // Get widget under the mouse if let Some(id) = input.widget_under_mouse { @@ -430,7 +437,7 @@ where #[allow(clippy::unused_unit)] // TODO: Pending review in #587 fn style(&self) -> Self::Style { () } - /// Update the state of the Slider. + /// Update the state of the Slot. fn update(self, args: widget::UpdateArgs) -> Self::Event { let widget::UpdateArgs { id, @@ -439,6 +446,7 @@ where ui, .. } = args; + let Slot { slot_key, empty_slot, diff --git a/world/src/layer/scatter.rs b/world/src/layer/scatter.rs index 6634c02302..ceca99913f 100644 --- a/world/src/layer/scatter.rs +++ b/world/src/layer/scatter.rs @@ -57,14 +57,14 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) { }), (RedFlower, false, |c, col| { ( - close(c.temp, CONFIG.tropical_temp, 0.6).min(close( + close(c.temp, CONFIG.tropical_temp, 0.7).min(close( c.humidity, CONFIG.jungle_hum, - 0.3, + 0.4, )) * col.tree_density * MUSH_FACT * 350.0, - Some((100.0, 0.05)), + Some((100.0, 0.1)), ) }), (WhiteFlower, false, |c, col| { diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 9f4d550d1d..46c42b2c58 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -10,7 +10,10 @@ use crate::{ use common::{ assets::{AssetExt, AssetHandle}, astar::Astar, - comp::{self}, + comp::{ + inventory::loadout_builder, + {self}, + }, generation::{ChunkSupplement, EntityInfo}, lottery::Lottery, store::{Id, Store}, @@ -596,7 +599,7 @@ impl Floor { //.do_if(is_giant, |e| e.into_giant()) .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) .with_alignment(comp::Alignment::Enemy) - .with_config(common::loadout_builder::LoadoutConfig::CultistAcolyte) + .with_config(loadout_builder::LoadoutConfig::CultistAcolyte) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_level(dynamic_rng.gen_range( (room.difficulty as f32).powf(1.25) + 3.0, @@ -605,7 +608,7 @@ impl Floor { let entity = match room.difficulty { 0 => entity .with_name("Outcast") - .with_config(common::loadout_builder::LoadoutConfig::Outcast) + .with_config(loadout_builder::LoadoutConfig::Outcast) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -619,7 +622,7 @@ impl Floor { )), 1 => entity .with_name("Highwayman") - .with_config(common::loadout_builder::LoadoutConfig::Highwayman) + .with_config(loadout_builder::LoadoutConfig::Highwayman) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -633,7 +636,7 @@ impl Floor { )), 2 => entity .with_name("Bandit") - .with_config(common::loadout_builder::LoadoutConfig::Bandit) + .with_config(loadout_builder::LoadoutConfig::Bandit) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -647,7 +650,7 @@ impl Floor { )), 3 => entity .with_name("Cultist Novice") - .with_config(common::loadout_builder::LoadoutConfig::CultistNovice) + .with_config(loadout_builder::LoadoutConfig::CultistNovice) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -661,7 +664,7 @@ impl Floor { )), 4 => entity .with_name("Cultist Acolyte") - .with_config(common::loadout_builder::LoadoutConfig::CultistAcolyte) + .with_config(loadout_builder::LoadoutConfig::CultistAcolyte) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -676,14 +679,14 @@ impl Floor { 5 => match dynamic_rng.gen_range(0, 6) { 0 => entity .with_name("Cultist Warlock") - .with_config(common::loadout_builder::LoadoutConfig::Warlock) + .with_config(loadout_builder::LoadoutConfig::Warlock) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_main_tool(comp::Item::new_from_asset_expect( "common.items.npc_weapons.staff.cultist_staff", )), _ => entity .with_name("Cultist Warlord") - .with_config(common::loadout_builder::LoadoutConfig::Warlord) + .with_config(loadout_builder::LoadoutConfig::Warlord) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 5) { @@ -752,9 +755,7 @@ impl Floor { )) .with_name("Outcast Leader".to_string()) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) - .with_config( - common::loadout_builder::LoadoutConfig::Outcast, - ) + .with_config(loadout_builder::LoadoutConfig::Outcast) .with_scale(2.0) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -800,7 +801,7 @@ impl Floor { )) .with_name("Bandit Captain".to_string()) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) - .with_config(common::loadout_builder::LoadoutConfig::Bandit) + .with_config(loadout_builder::LoadoutConfig::Bandit) .with_scale(2.0) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -814,27 +815,28 @@ impl Floor { ),); 2 ], - 3 => { - vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) - .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) - .with_name("Cultist Acolyte".to_string()) - .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) - .with_config(common::loadout_builder::LoadoutConfig::CultistAcolyte) - .with_scale(2.0) - .with_main_tool( - comp::Item::new_from_asset_expect( - match dynamic_rng.gen_range(0, 6) { - 0 => "common.items.weapons.axe.malachite_axe-0", - 1 => "common.items.weapons.sword.cultist_purp_2h-0", - 2 => "common.items.weapons.sword.cultist_purp_2h-0", - 3 => "common.items.weapons.hammer.cultist_purp_2h-0", - 4 => "common.items.weapons.staff.cultist_staff", - _ => "common.items.weapons.bow.horn_longbow-0", - }, - ), - ) - ; 2] - }, + 3 => vec![ + EntityInfo::at(tile_wcenter.map(|e| e as f32)) + .with_body(comp::Body::Humanoid( + comp::humanoid::Body::random() + )) + .with_name("Cultist Acolyte".to_string()) + .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) + .with_config(loadout_builder::LoadoutConfig::CultistAcolyte) + .with_scale(2.0) + .with_main_tool(comp::Item::new_from_asset_expect( + match dynamic_rng.gen_range(0, 6) { + 0 => "common.items.weapons.axe.malachite_axe-0", + 1 => "common.items.weapons.sword.cultist_purp_2h-0", + 2 => "common.items.weapons.sword.cultist_purp_2h-0", + 3 => + "common.items.weapons.hammer.cultist_purp_2h-0", + 4 => "common.items.weapons.staff.cultist_staff", + _ => "common.items.weapons.bow.horn_longbow-0", + }, + ),); + 2 + ], 4 => vec![ EntityInfo::at(tile_wcenter.map(|e| e as f32)) .with_body(comp::Body::Golem( @@ -971,9 +973,7 @@ impl Floor { )) .with_name("Animal Trainer".to_string()) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) - .with_config( - common::loadout_builder::LoadoutConfig::CultistAcolyte, - ) + .with_config(loadout_builder::LoadoutConfig::CultistAcolyte) .with_scale(2.0) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index b583383db3..cd0a8906a4 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -15,7 +15,9 @@ use crate::{ }; use common::{ astar::Astar, - comp::{self, bird_medium, humanoid, object, quadruped_small, Item}, + comp::{ + self, bird_medium, humanoid, inventory::loadout_builder, object, quadruped_small, Item, + }, generation::{ChunkSupplement, EntityInfo}, path::Path, spiral::Spiral2d, @@ -932,7 +934,7 @@ impl Settlement { )) .with_name("Guard") .with_level(dynamic_rng.gen_range(10, 15)) - .with_config(common::loadout_builder::LoadoutConfig::Guard), + .with_config(loadout_builder::LoadoutConfig::Guard), _ => entity .with_main_tool(Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 7) { @@ -946,7 +948,7 @@ impl Settlement { //_ => "common.items.npc_weapons.bow.starter_bow", TODO: Re-Add this when we have a better way of distributing npc_weapons here }, )) - .with_config(common::loadout_builder::LoadoutConfig::Villager), + .with_config(loadout_builder::LoadoutConfig::Villager), } });