From d6a4673ca59cffe057d4b0015b4920ecd5faa3b6 Mon Sep 17 00:00:00 2001 From: Herbert Wolverson Date: Wed, 24 Nov 2021 13:27:15 -0600 Subject: [PATCH] Draft of new chapter - the dark elf plaza --- Cargo.lock | 1683 +++++----- Cargo.toml | 3 +- book/src/SUMMARY.md | 1 + book/src/beta-webBanner-old.jpg | Bin 0 -> 36071 bytes book/src/c75-altar.jpg | Bin 0 -> 130774 bytes book/src/c75-edgeroads.jpg | Bin 0 -> 99790 bytes book/src/c75-emptymap.jpg | Bin 0 -> 129901 bytes book/src/c75-pools.jpg | Bin 0 -> 124793 bytes book/src/c75-solidrock.jpg | Bin 0 -> 141200 bytes book/src/c75-vokoth.jpg | Bin 0 -> 108102 bytes book/src/chapter_74.md | 2 +- book/src/chapter_75.md | 653 ++++ chapter-74-darkcity/Cargo.toml | 2 +- chapter-75-darkplaza/Cargo.toml | 15 + chapter-75-darkplaza/raws/spawns.json | 2868 +++++++++++++++++ chapter-75-darkplaza/src/components.rs | 494 +++ chapter-75-darkplaza/src/damage_system.rs | 129 + chapter-75-darkplaza/src/effects/damage.rs | 240 ++ chapter-75-darkplaza/src/effects/hunger.rs | 10 + chapter-75-darkplaza/src/effects/mod.rs | 145 + chapter-75-darkplaza/src/effects/movement.rs | 17 + chapter-75-darkplaza/src/effects/particles.rs | 44 + chapter-75-darkplaza/src/effects/targeting.rs | 55 + chapter-75-darkplaza/src/effects/triggers.rs | 270 ++ chapter-75-darkplaza/src/gamelog/builder.rs | 65 + chapter-75-darkplaza/src/gamelog/events.rs | 43 + chapter-75-darkplaza/src/gamelog/logstore.rs | 38 + chapter-75-darkplaza/src/gamelog/mod.rs | 15 + chapter-75-darkplaza/src/gamesystem.rs | 37 + chapter-75-darkplaza/src/gui/cheat_menu.rs | 42 + .../src/gui/drop_item_menu.rs | 29 + .../src/gui/game_over_menu.rs | 51 + chapter-75-darkplaza/src/gui/hud.rs | 278 ++ chapter-75-darkplaza/src/gui/identify_menu.rs | 56 + .../src/gui/inventory_menu.rs | 32 + chapter-75-darkplaza/src/gui/item_render.rs | 49 + chapter-75-darkplaza/src/gui/main_menu.rs | 86 + chapter-75-darkplaza/src/gui/menus.rs | 88 + chapter-75-darkplaza/src/gui/mod.rs | 29 + chapter-75-darkplaza/src/gui/ranged_target.rs | 62 + .../src/gui/remove_curse_menu.rs | 52 + .../src/gui/remove_item_menu.rs | 29 + chapter-75-darkplaza/src/gui/tooltips.rs | 151 + chapter-75-darkplaza/src/gui/vendor_menu.rs | 118 + chapter-75-darkplaza/src/main.rs | 575 ++++ chapter-75-darkplaza/src/map/camera.rs | 149 + chapter-75-darkplaza/src/map/dungeon.rs | 254 ++ chapter-75-darkplaza/src/map/mod.rs | 137 + chapter-75-darkplaza/src/map/themes.rs | 169 + chapter-75-darkplaza/src/map/tiletype.rs | 44 + .../src/map_builders/area_ending_point.rs | 68 + .../src/map_builders/area_starting_points.rs | 70 + .../src/map_builders/bsp_dungeon.rs | 112 + .../src/map_builders/bsp_interior.rs | 94 + .../src/map_builders/cellular_automata.rs | 72 + .../src/map_builders/common.rs | 117 + .../src/map_builders/cull_unreachable.rs | 36 + .../src/map_builders/dark_elves.rs | 248 ++ .../src/map_builders/distant_exit.rs | 45 + chapter-75-darkplaza/src/map_builders/dla.rs | 177 + .../src/map_builders/door_placement.rs | 71 + .../src/map_builders/drunkard.rs | 168 + .../src/map_builders/dwarf_fort_builder.rs | 119 + .../src/map_builders/forest.rs | 113 + .../src/map_builders/limestone_cavern.rs | 152 + chapter-75-darkplaza/src/map_builders/maze.rs | 197 ++ chapter-75-darkplaza/src/map_builders/mod.rs | 325 ++ .../src/map_builders/mushroom_forest.rs | 42 + .../src/map_builders/prefab_builder/mod.rs | 326 ++ .../prefab_builder/prefab_levels.rs | 61 + .../prefab_builder/prefab_rooms.rs | 65 + .../prefab_builder/prefab_sections.rs | 119 + .../src/map_builders/room_based_spawner.rs | 26 + .../src/map_builders/room_based_stairs.rs | 26 + .../room_based_starting_position.rs | 25 + .../src/map_builders/room_corner_rounding.rs | 49 + .../src/map_builders/room_corridor_spawner.rs | 30 + .../src/map_builders/room_draw.rs | 65 + .../src/map_builders/room_exploder.rs | 67 + .../src/map_builders/room_sorter.rs | 45 + .../src/map_builders/rooms_corridors_bsp.rs | 41 + .../map_builders/rooms_corridors_dogleg.rs | 47 + .../src/map_builders/rooms_corridors_lines.rs | 68 + .../map_builders/rooms_corridors_nearest.rs | 60 + .../src/map_builders/simple_map.rs | 40 + chapter-75-darkplaza/src/map_builders/town.rs | 439 +++ .../src/map_builders/voronoi.rs | 113 + .../src/map_builders/voronoi_spawning.rs | 47 + .../map_builders/waveform_collapse/common.rs | 13 + .../waveform_collapse/constraints.rs | 208 ++ .../src/map_builders/waveform_collapse/mod.rs | 85 + .../map_builders/waveform_collapse/solver.rs | 228 ++ chapter-75-darkplaza/src/player.rs | 485 +++ chapter-75-darkplaza/src/random_table.rs | 83 + .../src/raws/faction_structs.rs | 13 + chapter-75-darkplaza/src/raws/item_structs.rs | 74 + chapter-75-darkplaza/src/raws/loot_structs.rs | 13 + chapter-75-darkplaza/src/raws/mob_structs.rs | 63 + chapter-75-darkplaza/src/raws/mod.rs | 53 + chapter-75-darkplaza/src/raws/prop_structs.rs | 20 + chapter-75-darkplaza/src/raws/rawmaster.rs | 870 +++++ .../src/raws/spawn_table_structs.rs | 10 + .../src/raws/spell_structs.rs | 9 + .../src/raws/weapon_traits.rs | 8 + chapter-75-darkplaza/src/rect.rs | 35 + chapter-75-darkplaza/src/rex_assets.rs | 22 + chapter-75-darkplaza/src/rng.rs | 20 + chapter-75-darkplaza/src/saveload_system.rs | 163 + chapter-75-darkplaza/src/spatial/mod.rs | 117 + chapter-75-darkplaza/src/spawner.rs | 196 ++ .../src/systems/ai/adjacent_ai_system.rs | 82 + .../src/systems/ai/approach_ai_system.rs | 43 + .../src/systems/ai/chase_ai_system.rs | 77 + .../src/systems/ai/default_move_system.rs | 92 + .../src/systems/ai/encumbrance_system.rs | 122 + .../src/systems/ai/flee_ai_system.rs | 45 + .../src/systems/ai/initiative_system.rs | 96 + chapter-75-darkplaza/src/systems/ai/mod.rs | 20 + .../src/systems/ai/quipping.rs | 33 + .../src/systems/ai/turn_status.rs | 52 + .../src/systems/ai/visible_ai_system.rs | 118 + .../src/systems/dispatcher/mod.rs | 53 + .../src/systems/dispatcher/multi_thread.rs | 43 + .../src/systems/dispatcher/single_thread.rs | 42 + .../src/systems/hunger_system.rs | 70 + .../inventory_system/collection_system.rs | 41 + .../systems/inventory_system/drop_system.rs | 48 + .../src/systems/inventory_system/equip_use.rs | 91 + .../inventory_system/identification_system.rs | 36 + .../src/systems/inventory_system/mod.rs | 43 + .../systems/inventory_system/remove_system.rs | 37 + .../systems/inventory_system/use_system.rs | 103 + .../src/systems/lighting_system.rs | 40 + .../src/systems/map_indexing_system.rs | 46 + .../src/systems/melee_combat_system.rs | 187 ++ chapter-75-darkplaza/src/systems/mod.rs | 30 + .../src/systems/movement_system.rs | 61 + .../src/systems/particle_system.rs | 86 + .../src/systems/ranged_combat_system.rs | 205 ++ .../src/systems/trigger_system.rs | 60 + .../src/systems/visibility_system.rs | 66 + 141 files changed, 17426 insertions(+), 824 deletions(-) create mode 100644 book/src/beta-webBanner-old.jpg create mode 100644 book/src/c75-altar.jpg create mode 100644 book/src/c75-edgeroads.jpg create mode 100644 book/src/c75-emptymap.jpg create mode 100644 book/src/c75-pools.jpg create mode 100644 book/src/c75-solidrock.jpg create mode 100644 book/src/c75-vokoth.jpg create mode 100644 book/src/chapter_75.md create mode 100644 chapter-75-darkplaza/Cargo.toml create mode 100644 chapter-75-darkplaza/raws/spawns.json create mode 100644 chapter-75-darkplaza/src/components.rs create mode 100644 chapter-75-darkplaza/src/damage_system.rs create mode 100644 chapter-75-darkplaza/src/effects/damage.rs create mode 100644 chapter-75-darkplaza/src/effects/hunger.rs create mode 100644 chapter-75-darkplaza/src/effects/mod.rs create mode 100644 chapter-75-darkplaza/src/effects/movement.rs create mode 100644 chapter-75-darkplaza/src/effects/particles.rs create mode 100644 chapter-75-darkplaza/src/effects/targeting.rs create mode 100644 chapter-75-darkplaza/src/effects/triggers.rs create mode 100644 chapter-75-darkplaza/src/gamelog/builder.rs create mode 100644 chapter-75-darkplaza/src/gamelog/events.rs create mode 100644 chapter-75-darkplaza/src/gamelog/logstore.rs create mode 100644 chapter-75-darkplaza/src/gamelog/mod.rs create mode 100644 chapter-75-darkplaza/src/gamesystem.rs create mode 100644 chapter-75-darkplaza/src/gui/cheat_menu.rs create mode 100644 chapter-75-darkplaza/src/gui/drop_item_menu.rs create mode 100644 chapter-75-darkplaza/src/gui/game_over_menu.rs create mode 100644 chapter-75-darkplaza/src/gui/hud.rs create mode 100644 chapter-75-darkplaza/src/gui/identify_menu.rs create mode 100644 chapter-75-darkplaza/src/gui/inventory_menu.rs create mode 100644 chapter-75-darkplaza/src/gui/item_render.rs create mode 100644 chapter-75-darkplaza/src/gui/main_menu.rs create mode 100644 chapter-75-darkplaza/src/gui/menus.rs create mode 100644 chapter-75-darkplaza/src/gui/mod.rs create mode 100644 chapter-75-darkplaza/src/gui/ranged_target.rs create mode 100644 chapter-75-darkplaza/src/gui/remove_curse_menu.rs create mode 100644 chapter-75-darkplaza/src/gui/remove_item_menu.rs create mode 100644 chapter-75-darkplaza/src/gui/tooltips.rs create mode 100644 chapter-75-darkplaza/src/gui/vendor_menu.rs create mode 100644 chapter-75-darkplaza/src/main.rs create mode 100644 chapter-75-darkplaza/src/map/camera.rs create mode 100644 chapter-75-darkplaza/src/map/dungeon.rs create mode 100644 chapter-75-darkplaza/src/map/mod.rs create mode 100644 chapter-75-darkplaza/src/map/themes.rs create mode 100644 chapter-75-darkplaza/src/map/tiletype.rs create mode 100644 chapter-75-darkplaza/src/map_builders/area_ending_point.rs create mode 100644 chapter-75-darkplaza/src/map_builders/area_starting_points.rs create mode 100644 chapter-75-darkplaza/src/map_builders/bsp_dungeon.rs create mode 100644 chapter-75-darkplaza/src/map_builders/bsp_interior.rs create mode 100644 chapter-75-darkplaza/src/map_builders/cellular_automata.rs create mode 100644 chapter-75-darkplaza/src/map_builders/common.rs create mode 100644 chapter-75-darkplaza/src/map_builders/cull_unreachable.rs create mode 100644 chapter-75-darkplaza/src/map_builders/dark_elves.rs create mode 100644 chapter-75-darkplaza/src/map_builders/distant_exit.rs create mode 100644 chapter-75-darkplaza/src/map_builders/dla.rs create mode 100644 chapter-75-darkplaza/src/map_builders/door_placement.rs create mode 100644 chapter-75-darkplaza/src/map_builders/drunkard.rs create mode 100644 chapter-75-darkplaza/src/map_builders/dwarf_fort_builder.rs create mode 100644 chapter-75-darkplaza/src/map_builders/forest.rs create mode 100644 chapter-75-darkplaza/src/map_builders/limestone_cavern.rs create mode 100644 chapter-75-darkplaza/src/map_builders/maze.rs create mode 100644 chapter-75-darkplaza/src/map_builders/mod.rs create mode 100644 chapter-75-darkplaza/src/map_builders/mushroom_forest.rs create mode 100644 chapter-75-darkplaza/src/map_builders/prefab_builder/mod.rs create mode 100644 chapter-75-darkplaza/src/map_builders/prefab_builder/prefab_levels.rs create mode 100644 chapter-75-darkplaza/src/map_builders/prefab_builder/prefab_rooms.rs create mode 100644 chapter-75-darkplaza/src/map_builders/prefab_builder/prefab_sections.rs create mode 100644 chapter-75-darkplaza/src/map_builders/room_based_spawner.rs create mode 100644 chapter-75-darkplaza/src/map_builders/room_based_stairs.rs create mode 100644 chapter-75-darkplaza/src/map_builders/room_based_starting_position.rs create mode 100644 chapter-75-darkplaza/src/map_builders/room_corner_rounding.rs create mode 100644 chapter-75-darkplaza/src/map_builders/room_corridor_spawner.rs create mode 100644 chapter-75-darkplaza/src/map_builders/room_draw.rs create mode 100644 chapter-75-darkplaza/src/map_builders/room_exploder.rs create mode 100644 chapter-75-darkplaza/src/map_builders/room_sorter.rs create mode 100644 chapter-75-darkplaza/src/map_builders/rooms_corridors_bsp.rs create mode 100644 chapter-75-darkplaza/src/map_builders/rooms_corridors_dogleg.rs create mode 100644 chapter-75-darkplaza/src/map_builders/rooms_corridors_lines.rs create mode 100644 chapter-75-darkplaza/src/map_builders/rooms_corridors_nearest.rs create mode 100644 chapter-75-darkplaza/src/map_builders/simple_map.rs create mode 100644 chapter-75-darkplaza/src/map_builders/town.rs create mode 100644 chapter-75-darkplaza/src/map_builders/voronoi.rs create mode 100644 chapter-75-darkplaza/src/map_builders/voronoi_spawning.rs create mode 100644 chapter-75-darkplaza/src/map_builders/waveform_collapse/common.rs create mode 100644 chapter-75-darkplaza/src/map_builders/waveform_collapse/constraints.rs create mode 100644 chapter-75-darkplaza/src/map_builders/waveform_collapse/mod.rs create mode 100644 chapter-75-darkplaza/src/map_builders/waveform_collapse/solver.rs create mode 100644 chapter-75-darkplaza/src/player.rs create mode 100644 chapter-75-darkplaza/src/random_table.rs create mode 100644 chapter-75-darkplaza/src/raws/faction_structs.rs create mode 100644 chapter-75-darkplaza/src/raws/item_structs.rs create mode 100644 chapter-75-darkplaza/src/raws/loot_structs.rs create mode 100644 chapter-75-darkplaza/src/raws/mob_structs.rs create mode 100644 chapter-75-darkplaza/src/raws/mod.rs create mode 100644 chapter-75-darkplaza/src/raws/prop_structs.rs create mode 100644 chapter-75-darkplaza/src/raws/rawmaster.rs create mode 100644 chapter-75-darkplaza/src/raws/spawn_table_structs.rs create mode 100644 chapter-75-darkplaza/src/raws/spell_structs.rs create mode 100644 chapter-75-darkplaza/src/raws/weapon_traits.rs create mode 100644 chapter-75-darkplaza/src/rect.rs create mode 100644 chapter-75-darkplaza/src/rex_assets.rs create mode 100644 chapter-75-darkplaza/src/rng.rs create mode 100644 chapter-75-darkplaza/src/saveload_system.rs create mode 100644 chapter-75-darkplaza/src/spatial/mod.rs create mode 100644 chapter-75-darkplaza/src/spawner.rs create mode 100644 chapter-75-darkplaza/src/systems/ai/adjacent_ai_system.rs create mode 100644 chapter-75-darkplaza/src/systems/ai/approach_ai_system.rs create mode 100644 chapter-75-darkplaza/src/systems/ai/chase_ai_system.rs create mode 100644 chapter-75-darkplaza/src/systems/ai/default_move_system.rs create mode 100644 chapter-75-darkplaza/src/systems/ai/encumbrance_system.rs create mode 100644 chapter-75-darkplaza/src/systems/ai/flee_ai_system.rs create mode 100644 chapter-75-darkplaza/src/systems/ai/initiative_system.rs create mode 100644 chapter-75-darkplaza/src/systems/ai/mod.rs create mode 100644 chapter-75-darkplaza/src/systems/ai/quipping.rs create mode 100644 chapter-75-darkplaza/src/systems/ai/turn_status.rs create mode 100644 chapter-75-darkplaza/src/systems/ai/visible_ai_system.rs create mode 100644 chapter-75-darkplaza/src/systems/dispatcher/mod.rs create mode 100644 chapter-75-darkplaza/src/systems/dispatcher/multi_thread.rs create mode 100644 chapter-75-darkplaza/src/systems/dispatcher/single_thread.rs create mode 100644 chapter-75-darkplaza/src/systems/hunger_system.rs create mode 100644 chapter-75-darkplaza/src/systems/inventory_system/collection_system.rs create mode 100644 chapter-75-darkplaza/src/systems/inventory_system/drop_system.rs create mode 100644 chapter-75-darkplaza/src/systems/inventory_system/equip_use.rs create mode 100644 chapter-75-darkplaza/src/systems/inventory_system/identification_system.rs create mode 100644 chapter-75-darkplaza/src/systems/inventory_system/mod.rs create mode 100644 chapter-75-darkplaza/src/systems/inventory_system/remove_system.rs create mode 100644 chapter-75-darkplaza/src/systems/inventory_system/use_system.rs create mode 100644 chapter-75-darkplaza/src/systems/lighting_system.rs create mode 100644 chapter-75-darkplaza/src/systems/map_indexing_system.rs create mode 100644 chapter-75-darkplaza/src/systems/melee_combat_system.rs create mode 100644 chapter-75-darkplaza/src/systems/mod.rs create mode 100644 chapter-75-darkplaza/src/systems/movement_system.rs create mode 100644 chapter-75-darkplaza/src/systems/particle_system.rs create mode 100644 chapter-75-darkplaza/src/systems/ranged_combat_system.rs create mode 100644 chapter-75-darkplaza/src/systems/trigger_system.rs create mode 100644 chapter-75-darkplaza/src/systems/visibility_system.rs diff --git a/Cargo.lock b/Cargo.lock index 22165fa4..5b4e184a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,38 +1,48 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13739d7177fbd22bb0ed28badfff9f372f8bef46c863db4e1c6248f6b223b6e" + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "adler32" -version = "1.0.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "ahash" -version = "0.3.2" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0989268a37e128d4d7a8028f1c60099430113fdbc70419010601ce51a228e4fe" -dependencies = [ - "const-random", -] +checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" [[package]] name = "aho-corasick" -version = "0.7.8" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743ad5a418686aad3b87fd14c43badd828cf26e214a00f92a384291cf22e1811" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ "memchr", ] [[package]] name = "andrew" -version = "0.2.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7f09f89872c2b6b29e319377b1fbe91c6f5947df19a25596e121cf19a7b35e" +checksum = "8c4afb09dd642feec8408e33f92f3ffc4052946f6b20f32fb99c1f58cd4fa7cf" dependencies = [ "bitflags", - "line_drawing", - "rusttype 0.7.9", + "rusttype", "walkdir", "xdg", "xml-rs", @@ -46,51 +56,33 @@ checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" [[package]] name = "anyhow" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" - -[[package]] -name = "approx" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" -dependencies = [ - "num-traits", -] - -[[package]] -name = "arrayvec" -version = "0.4.12" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -dependencies = [ - "nodrop", -] +checksum = "62e1f47f7dc0422027a4e370dd4548d4d66b26782e513e98dca1e689e058a80e" [[package]] name = "arrayvec" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "atom" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c86699c3f02778ec07158376991c8f783dd1f2f95c579ffaf0738dc984b2fe2" +checksum = "c9ff149ed9780025acfdb36862d35b28856bb693ceb451259a7164442f22fdc3" [[package]] name = "autocfg" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block" @@ -100,142 +92,76 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "bracket-algorithm-traits" -version = "0.8.1" -dependencies = [ - "bracket-geometry 0.8.1", - "smallvec 1.4.2", -] - -[[package]] -name = "bracket-algorithm-traits" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec021d386ba4c891ee752738869465aa679b5902c6bcc93ca55532d9a33ab73" -dependencies = [ - "bracket-geometry 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.4.2", -] - -[[package]] -name = "bracket-color" -version = "0.8.1" +checksum = "af23ee41725dd41febe5f614851db8e5d44c07254dd91838b5ddf817060e2e73" dependencies = [ - "byteorder", - "lazy_static", - "parking_lot 0.10.2", - "serde", + "bracket-geometry", + "smallvec", ] [[package]] name = "bracket-color" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4064968b35447f0c378010bbf5470ede41d07101bdf3b1fdf29dca5441f91b" +checksum = "3c1d1b160817fb74eebedccd678055cd688d1a73dc1a14519fa30ff4c9a5bdee" dependencies = [ "byteorder", "lazy_static", - "parking_lot 0.10.2", - "serde", -] - -[[package]] -name = "bracket-geometry" -version = "0.8.1" -dependencies = [ + "parking_lot", "serde", - "ultraviolet", ] [[package]] name = "bracket-geometry" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ec613acfe65a809f03d57e2cc8ab97f9c396ddb9929e784bc19b87dd5ccf358" +checksum = "4db22c32c68bd9330ab982f8ff7ffe7b10541d16ea7d7d51aac074499850402b" dependencies = [ "serde", "ultraviolet", ] -[[package]] -name = "bracket-lib" -version = "0.8.1" -dependencies = [ - "bracket-algorithm-traits 0.8.1", - "bracket-color 0.8.1", - "bracket-geometry 0.8.1", - "bracket-noise 0.8.1", - "bracket-pathfinding 0.8.1", - "bracket-random 0.8.0", - "bracket-terminal 0.8.1", -] - [[package]] name = "bracket-lib" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0fb005e908ce2e553277e554c32ab06d8b12bea38660da98f2e77e662dea617" dependencies = [ - "bracket-algorithm-traits 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bracket-color 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bracket-geometry 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bracket-noise 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bracket-pathfinding 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bracket-random 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bracket-terminal 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bracket-noise" -version = "0.8.1" -dependencies = [ - "bracket-random 0.8.0", + "bracket-algorithm-traits", + "bracket-color", + "bracket-geometry", + "bracket-noise", + "bracket-pathfinding", + "bracket-random", + "bracket-terminal", ] [[package]] name = "bracket-noise" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b1235bcf925e1ed541da32902abe9ea2313ed388cd6b39038d5bb4b39f611f" -dependencies = [ - "bracket-random 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bracket-pathfinding" -version = "0.8.1" +checksum = "60de9564f6a658c770666a6cf6ccf837f0669f5906f67de7f089bebe09b46723" dependencies = [ - "bracket-algorithm-traits 0.8.1", - "bracket-geometry 0.8.1", - "smallvec 1.4.2", + "bracket-random", ] [[package]] name = "bracket-pathfinding" -version = "0.8.1" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd1eeaabc58017f4708c451924db6140fd148ab64f4cb6d433fb0c193cd6733" -dependencies = [ - "bracket-algorithm-traits 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bracket-geometry 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.4.2", -] - -[[package]] -name = "bracket-random" -version = "0.8.0" +checksum = "6b713222141585b5e5cc6f0be1a0a473e1e339aa1300af91929ce6b1b2ed529c" dependencies = [ - "lazy_static", - "rand", - "rand_xorshift", - "regex", - "serde", + "bracket-algorithm-traits", + "bracket-geometry", + "smallvec", ] [[package]] name = "bracket-random" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac06539968b64bd20c9f7e1620e3a090737ecc54b850f9527a27f961284c8b97" +checksum = "66b5b977a40a6be337b2baff6911051966c61bd3987836c13e96f518ec4ba312" dependencies = [ "lazy_static", "rand", @@ -246,35 +172,13 @@ dependencies = [ [[package]] name = "bracket-terminal" -version = "0.8.1" -dependencies = [ - "bracket-color 0.8.1", - "bracket-geometry 0.8.1", - "byteorder", - "console_error_panic_hook", - "flate2", - "glow", - "glutin", - "image", - "lazy_static", - "object-pool 0.5.3", - "parking_lot 0.10.2", - "rand", - "ultraviolet", - "wasm-bindgen", - "wasm-timer", - "web-sys", - "winit", -] - -[[package]] -name = "bracket-terminal" -version = "0.8.1" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2336a503041d18d4335394f8216214593c25e072a9bbb9ae768fe6368071b86b" +checksum = "460594df0b3364cae6ce5a5de4d787b293d20df0deffed4a942c10b1e3d50b1d" dependencies = [ - "bracket-color 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bracket-geometry 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow", + "bracket-color", + "bracket-geometry", "byteorder", "console_error_panic_hook", "flate2", @@ -282,8 +186,8 @@ dependencies = [ "glutin", "image", "lazy_static", - "object-pool 0.4.4", - "parking_lot 0.10.2", + "object-pool", + "parking_lot", "rand", "ultraviolet", "wasm-bindgen", @@ -294,47 +198,37 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.2.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" [[package]] name = "bytemuck" -version = "1.2.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37fa13df2292ecb479ec23aa06f4507928bef07839be9ef15281411076629431" +checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b" [[package]] name = "byteorder" -version = "1.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" - -[[package]] -name = "c2-chacha" -version = "0.2.3" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -dependencies = [ - "ppv-lite86", -] +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "calloop" -version = "0.4.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aa2097be53a00de9e8fc349fea6d76221f398f5c4fa550d420669906962d160" +checksum = "0b036167e76041694579972c28cf4877b4f92da222560ddb49008937b6a6727c" dependencies = [ - "mio", - "mio-extras", - "nix", + "log", + "nix 0.18.0", ] [[package]] name = "cc" -version = "1.0.50" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" [[package]] name = "cfg-if" @@ -342,6 +236,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "cgl" version = "0.3.2" @@ -355,14 +255,14 @@ dependencies = [ name = "chapter-01-hellorust" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", ] [[package]] name = "chapter-02-helloecs" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "specs", "specs-derive", ] @@ -371,7 +271,7 @@ dependencies = [ name = "chapter-03-walkmap" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "specs", "specs-derive", ] @@ -380,7 +280,7 @@ dependencies = [ name = "chapter-04-newmap" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "specs", "specs-derive", ] @@ -389,7 +289,7 @@ dependencies = [ name = "chapter-05-fov" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "specs", "specs-derive", ] @@ -398,7 +298,7 @@ dependencies = [ name = "chapter-06-monsters" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "specs", "specs-derive", ] @@ -407,7 +307,7 @@ dependencies = [ name = "chapter-07-damage" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "specs", "specs-derive", ] @@ -416,7 +316,7 @@ dependencies = [ name = "chapter-08-ui" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "specs", "specs-derive", ] @@ -425,7 +325,7 @@ dependencies = [ name = "chapter-09-items" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "specs", "specs-derive", ] @@ -434,7 +334,7 @@ dependencies = [ name = "chapter-10-ranged" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "specs", "specs-derive", ] @@ -443,7 +343,7 @@ dependencies = [ name = "chapter-11-loadsave" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -454,7 +354,7 @@ dependencies = [ name = "chapter-12-delvingdeeper" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -465,7 +365,7 @@ dependencies = [ name = "chapter-13-difficulty" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -476,7 +376,7 @@ dependencies = [ name = "chapter-14-gear" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -487,7 +387,7 @@ dependencies = [ name = "chapter-16-nicewalls" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -498,7 +398,7 @@ dependencies = [ name = "chapter-17-blood" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -509,7 +409,7 @@ dependencies = [ name = "chapter-18-particles" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -520,7 +420,7 @@ dependencies = [ name = "chapter-19-food" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -531,7 +431,7 @@ dependencies = [ name = "chapter-20-magicmapping" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -542,7 +442,7 @@ dependencies = [ name = "chapter-21-rexmenu" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -553,7 +453,7 @@ dependencies = [ name = "chapter-22-simpletraps" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -564,7 +464,7 @@ dependencies = [ name = "chapter-23-generic-map" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -575,7 +475,7 @@ dependencies = [ name = "chapter-24-map-testing" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -586,7 +486,7 @@ dependencies = [ name = "chapter-25-bsproom-dungeons" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -597,7 +497,7 @@ dependencies = [ name = "chapter-26-bsp-interiors" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -608,7 +508,7 @@ dependencies = [ name = "chapter-27-cellular-automata" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -619,7 +519,7 @@ dependencies = [ name = "chapter-28-drunkards-walk" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -630,7 +530,7 @@ dependencies = [ name = "chapter-29-mazes" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -641,7 +541,7 @@ dependencies = [ name = "chapter-30-dla" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -652,7 +552,7 @@ dependencies = [ name = "chapter-31-symmetry" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -663,7 +563,7 @@ dependencies = [ name = "chapter-32-voronoi" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -674,7 +574,7 @@ dependencies = [ name = "chapter-33-wfc" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -685,7 +585,7 @@ dependencies = [ name = "chapter-34-vaults" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -696,7 +596,7 @@ dependencies = [ name = "chapter-35-vaults2" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -707,7 +607,7 @@ dependencies = [ name = "chapter-36-layers" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -718,7 +618,7 @@ dependencies = [ name = "chapter-37-layers2" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -729,7 +629,7 @@ dependencies = [ name = "chapter-38-rooms" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -740,7 +640,7 @@ dependencies = [ name = "chapter-39-halls" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -751,7 +651,7 @@ dependencies = [ name = "chapter-40-doors" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -762,7 +662,7 @@ dependencies = [ name = "chapter-41-camera" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -774,7 +674,7 @@ name = "chapter-45-raws1" version = "0.1.0" dependencies = [ "lazy_static", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -786,7 +686,7 @@ name = "chapter-46-raws2" version = "0.1.0" dependencies = [ "lazy_static", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -798,7 +698,7 @@ name = "chapter-47-town1" version = "0.1.0" dependencies = [ "lazy_static", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -810,7 +710,7 @@ name = "chapter-48-town2" version = "0.1.0" dependencies = [ "lazy_static", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -822,7 +722,7 @@ name = "chapter-49-town3" version = "0.1.0" dependencies = [ "lazy_static", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -834,7 +734,7 @@ name = "chapter-50-stats" version = "0.1.0" dependencies = [ "lazy_static", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -847,7 +747,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -860,7 +760,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -873,7 +773,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -886,7 +786,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -899,7 +799,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -912,7 +812,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -925,7 +825,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -938,7 +838,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -951,7 +851,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -964,7 +864,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -977,7 +877,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -990,7 +890,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -1003,7 +903,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -1016,7 +916,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -1029,7 +929,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -1042,7 +942,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -1055,7 +955,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -1068,7 +968,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -1081,7 +981,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -1094,7 +994,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -1107,7 +1007,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -1120,7 +1020,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -1133,7 +1033,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -1146,7 +1046,7 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "serde", "serde_json", "specs", @@ -1159,7 +1059,20 @@ version = "0.1.0" dependencies = [ "lazy_static", "regex", - "rltk 0.8.1", + "rltk", + "serde", + "serde_json", + "specs", + "specs-derive", +] + +[[package]] +name = "chapter-75-darkplaza" +version = "0.1.0" +dependencies = [ + "lazy_static", + "regex", + "rltk", "serde", "serde_json", "specs", @@ -1167,167 +1080,202 @@ dependencies = [ ] [[package]] -name = "cloudabi" -version = "0.0.3" +name = "cocoa" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +checksum = "c54201c07dcf3a5ca33fececb8042aed767ee4bfd5a0235a8ceabcda956044b2" dependencies = [ "bitflags", + "block", + "cocoa-foundation", + "core-foundation 0.9.2", + "core-graphics 0.22.3", + "foreign-types", + "libc", + "objc", ] [[package]] name = "cocoa" -version = "0.19.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29f7768b2d1be17b96158e3285951d366b40211320fb30826a76cb7a0da6400" +checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" dependencies = [ "bitflags", "block", - "core-foundation", - "core-graphics", + "cocoa-foundation", + "core-foundation 0.9.2", + "core-graphics 0.22.3", "foreign-types", "libc", "objc", ] [[package]] -name = "console_error_panic_hook" -version = "0.1.6" +name = "cocoa-foundation" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" +checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" dependencies = [ - "cfg-if", - "wasm-bindgen", + "bitflags", + "block", + "core-foundation 0.9.2", + "core-graphics-types", + "foreign-types", + "libc", + "objc", ] [[package]] -name = "const-random" -version = "0.1.8" +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "const-random-macro", - "proc-macro-hack", + "cfg-if 1.0.0", + "wasm-bindgen", ] [[package]] -name = "const-random-macro" -version = "0.1.8" +name = "core-foundation" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" dependencies = [ - "getrandom", - "proc-macro-hack", + "core-foundation-sys 0.7.0", + "libc", ] [[package]] name = "core-foundation" -version = "0.6.4" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" +checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" dependencies = [ - "core-foundation-sys", + "core-foundation-sys 0.8.3", "libc", ] [[package]] name = "core-foundation-sys" -version = "0.6.2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "core-graphics" -version = "0.17.3" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" +checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.7.0", "foreign-types", "libc", ] [[package]] -name = "core-video-sys" -version = "0.1.3" +name = "core-graphics" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dc065219542086f72d1e9f7aadbbab0989e980263695d129d502082d063a9d0" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ - "cfg-if", - "core-foundation-sys", - "core-graphics", + "bitflags", + "core-foundation 0.9.2", + "core-graphics-types", + "foreign-types", "libc", - "objc", ] [[package]] -name = "crc32fast" -version = "1.2.0" +name = "core-graphics-types" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" dependencies = [ - "cfg-if", + "bitflags", + "core-foundation 0.9.2", + "foreign-types", + "libc", ] [[package]] -name = "crossbeam" -version = "0.7.3" +name = "core-video-sys" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e" +checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" dependencies = [ - "cfg-if", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", + "cfg-if 0.1.10", + "core-foundation-sys 0.7.0", + "core-graphics 0.19.2", + "libc", + "objc", +] + +[[package]] +name = "crc32fast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3825b1e8580894917dc4468cb634a1b4e9745fddc854edad72d9c04644c0319f" +dependencies = [ + "cfg-if 1.0.0", ] [[package]] name = "crossbeam-channel" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" dependencies = [ - "crossbeam-utils", - "maybe-uninit", + "cfg-if 1.0.0", + "crossbeam-utils 0.8.5", ] [[package]] name = "crossbeam-deque" -version = "0.7.3" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ + "cfg-if 1.0.0", "crossbeam-epoch", - "crossbeam-utils", - "maybe-uninit", + "crossbeam-utils 0.8.5", ] [[package]] name = "crossbeam-epoch" -version = "0.8.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", + "cfg-if 1.0.0", + "crossbeam-utils 0.8.5", "lazy_static", - "maybe-uninit", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-queue" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" dependencies = [ - "cfg-if", - "crossbeam-utils", + "cfg-if 0.1.10", + "crossbeam-utils 0.7.2", + "maybe-uninit", ] [[package]] @@ -1337,74 +1285,165 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 0.1.10", "lazy_static", ] [[package]] -name = "deflate" -version = "0.8.4" +name = "crossbeam-utils" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e5d2a2273fed52a7f947ee55b092c4057025d7a3e04e5ecdbd25d6c3fb1bd7" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" dependencies = [ - "adler32", - "byteorder", + "cfg-if 1.0.0", + "lazy_static", ] [[package]] -name = "dispatch" -version = "0.2.0" +name = "darling" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core", + "darling_macro", +] [[package]] -name = "dlib" -version = "0.4.1" +name = "darling_core" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" dependencies = [ - "libloading", + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", ] [[package]] -name = "downcast-rs" -version = "1.1.1" +name = "darling_macro" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba6eb47c2131e784a38b726eb54c1e1484904f013e576a25354d0124161af6" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core", + "quote", + "syn", +] [[package]] -name = "either" -version = "1.5.3" +name = "deflate" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] [[package]] -name = "flate2" -version = "1.0.14" +name = "derivative" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "cfg-if", - "crc32fast", - "libc", - "miniz_oxide", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "foreign-types" -version = "0.3.2" +name = "dirs" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" dependencies = [ - "foreign-types-shared", + "dirs-sys", ] [[package]] -name = "foreign-types-shared" -version = "0.1.1" +name = "dirs-sys" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - +checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76" +dependencies = [ + "libloading 0.6.7", +] + +[[package]] +name = "dlib" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" +dependencies = [ + "libloading 0.7.2", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "miniz_oxide 0.4.4", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -1423,27 +1462,26 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.1.29" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "getrandom" -version = "0.1.14" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi", - "wasm-bindgen", ] [[package]] name = "gl_generator" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca98bbde17256e02d17336a6bdb5a50f7d0ccacee502e191d3e3d0ec2f96f84a" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" dependencies = [ "khronos_api", "log", @@ -1452,11 +1490,10 @@ dependencies = [ [[package]] name = "glow" -version = "0.4.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31aed196700daf16e1241d819ff4a4855a78ee0cddb051948d50b9213deec82f" +checksum = "945be163fdb893227410c8b44c2412dade922585b262d1daa6a7e96135217d4c" dependencies = [ - "gl_generator", "js-sys", "slotmap", "wasm-bindgen", @@ -1465,39 +1502,39 @@ dependencies = [ [[package]] name = "glutin" -version = "0.24.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611023dea5047f3e9047aecb9e361852dcfd0881129daf5d110106ca2b14f3f3" +checksum = "1ae1cbb9176b9151c4ce03f012e3cd1c6c18c4be79edeaeb3d99f5d8085c5fa3" dependencies = [ "android_glue", "cgl", - "cocoa", - "core-foundation", - "core-graphics", + "cocoa 0.23.0", + "core-foundation 0.9.2", "glutin_egl_sys", "glutin_emscripten_sys", "glutin_gles2_sys", "glutin_glx_sys", "glutin_wgl_sys", "lazy_static", - "libloading", + "libloading 0.6.7", "log", "objc", "osmesa-sys", - "parking_lot 0.10.2", + "parking_lot", "wayland-client", - "winapi 0.3.8", + "wayland-egl", + "winapi 0.3.9", "winit", ] [[package]] name = "glutin_egl_sys" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772edef3b28b8ad41e4ea202748e65eefe8e5ffd1f4535f1219793dbb20b3d4c" +checksum = "2abb6aa55523480c4adc5a56bbaa249992e2dddb2fc63dc96e04a3355364c211" dependencies = [ "gl_generator", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -1508,9 +1545,9 @@ checksum = "80de4146df76e8a6c32b03007bc764ff3249dcaeb4f675d68a06caf1bac363f1" [[package]] name = "glutin_gles2_sys" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e853d96bebcb8e53e445225c3009758c6f5960d44f2543245f6f07b567dae0" +checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103" dependencies = [ "gl_generator", "objc", @@ -1518,9 +1555,9 @@ dependencies = [ [[package]] name = "glutin_glx_sys" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c243de74d6cf5ea100c788826d2fb9319de315485dd4b310811a663b3809c3" +checksum = "7e393c8fc02b807459410429150e9c4faffdb312d59b8c038566173c81991351" dependencies = [ "gl_generator", "x11-dl", @@ -1528,37 +1565,28 @@ dependencies = [ [[package]] name = "glutin_wgl_sys" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93dba7ee3a0feeac0f437141ff25e71ce2066bcf1a706acab1559ffff94eb6a" +checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696" dependencies = [ "gl_generator", ] [[package]] name = "hashbrown" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728e7d31e63d53c436094370f1e6fa249f60a4bb318cc5dfbbbe0aa2bc5a29d7" +checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" dependencies = [ "ahash", "autocfg", ] -[[package]] -name = "heck" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "hermit-abi" -version = "0.1.8" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] @@ -1573,14 +1601,21 @@ dependencies = [ "rayon", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "image" -version = "0.23.3" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfc5483f8d5afd3653b38a196c52294dcb239c3e1a5bade1990353ea13bcf387" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" dependencies = [ "bytemuck", "byteorder", + "color_quant", "jpeg-decoder", "num-iter", "num-rational", @@ -1588,21 +1623,15 @@ dependencies = [ "png", ] -[[package]] -name = "inflate" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" -dependencies = [ - "adler32", -] - [[package]] name = "instant" -version = "0.1.2" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c346c299e3fe8ef94dc10c2c0253d858a69aac1245157a3bf4125915d528caf" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", "web-sys", ] @@ -1617,24 +1646,27 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.5" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jpeg-decoder" -version = "0.1.18" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0256f0aec7352539102a9efbcb75543227b7ab1117e0f95450023af730128451" -dependencies = [ - "byteorder", -] +checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" [[package]] name = "js-sys" -version = "0.3.35" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7889c7c36282151f6bf465be4700359318aef36baa951462382eae49e9577cf9" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" dependencies = [ "wasm-bindgen", ] @@ -1663,64 +1695,52 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" - -[[package]] -name = "lexical-core" -version = "0.4.6" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304bccb228c4b020f3a4835d247df0a02a7c4686098d4167762cfbbe4c5cb14" -dependencies = [ - "arrayvec 0.4.12", - "cfg-if", - "rustc_version", - "ryu", - "static_assertions", -] +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.67" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" +checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" [[package]] name = "libloading" -version = "0.5.2" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" dependencies = [ - "cc", - "winapi 0.3.8", + "cfg-if 1.0.0", + "winapi 0.3.9", ] [[package]] -name = "line_drawing" -version = "0.7.0" +name = "libloading" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc7ad3d82c845bdb5dde34ffdcc7a5fb4d2996e1e1ee0f19c33bc80e15196b9" +checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52" dependencies = [ - "num-traits", + "cfg-if 1.0.0", + "winapi 0.3.9", ] [[package]] name = "lock_api" -version = "0.3.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" dependencies = [ "scopeguard", ] [[package]] name = "log" -version = "0.4.8" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1740,45 +1760,60 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" -version = "2.3.3" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] -name = "memmap" -version = "0.7.0" +name = "memmap2" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" dependencies = [ "libc", - "winapi 0.3.8", ] [[package]] name = "memoffset" -version = "0.5.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" dependencies = [ - "rustc_version", + "autocfg", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" dependencies = [ "adler32", ] +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + [[package]] name = "mio" -version = "0.6.21" +version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "fuchsia-zircon", "fuchsia-zircon-sys", "iovec", @@ -1805,9 +1840,9 @@ dependencies = [ [[package]] name = "miow" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" dependencies = [ "kernel32-sys", "net2", @@ -1821,41 +1856,91 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915" +[[package]] +name = "ndk" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb167c1febed0a496639034d0c76b3b74263636045db5489eee52143c246e73" +dependencies = [ + "jni-sys", + "ndk-sys", + "num_enum", + "thiserror", +] + +[[package]] +name = "ndk-glue" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf399b8b7a39c6fb153c4ec32c72fd5fe789df24a647f229c239aa7adb15241" +dependencies = [ + "lazy_static", + "libc", + "log", + "ndk", + "ndk-macro", + "ndk-sys", +] + +[[package]] +name = "ndk-macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d" +dependencies = [ + "darling", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ndk-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" + [[package]] name = "net2" -version = "0.2.33" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] name = "nix" -version = "0.14.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" +checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" dependencies = [ "bitflags", "cc", - "cfg-if", + "cfg-if 0.1.10", "libc", - "void", ] [[package]] -name = "nodrop" -version = "0.1.14" +name = "nix" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", +] [[package]] name = "nom" -version = "4.2.3" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ "memchr", "version_check", @@ -1863,20 +1948,20 @@ dependencies = [ [[package]] name = "nom" -version = "5.1.0" +version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c433f4d505fe6ce7ff78523d2fa13a0b9f2690e181fc26168bcbe5ccc5d14e07" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" dependencies = [ - "lexical-core", "memchr", + "minimal-lexical", "version_check", ] [[package]] name = "num-integer" -version = "0.1.42" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ "autocfg", "num-traits", @@ -1884,9 +1969,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.40" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" dependencies = [ "autocfg", "num-integer", @@ -1895,9 +1980,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.2.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" dependencies = [ "autocfg", "num-integer", @@ -1906,61 +1991,69 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.11" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ "hermit-abi", "libc", ] [[package]] -name = "objc" -version = "0.2.7" +name = "num_enum" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +checksum = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4" dependencies = [ - "malloc_buf", + "derivative", + "num_enum_derive", ] [[package]] -name = "object-pool" -version = "0.4.4" +name = "num_enum_derive" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8d6b57bfd185264f6b81e1a6c65a4f2cf430bbf05454058f1ab5070d72cd69" +checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" dependencies = [ - "crossbeam", - "parking_lot 0.9.0", - "serde", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "object-pool" -version = "0.5.3" +name = "objc" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57280719d7b44758cab397e55d4a1a194d4a62575ceea8c794841742b9636e6c" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" dependencies = [ - "parking_lot 0.10.2", + "malloc_buf", ] [[package]] -name = "ordered-float" -version = "1.0.2" +name = "object-pool" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" +checksum = "ee9a3e7196d09ec86002b939f1576e8e446d58def8fd48fe578e2c72d5328d68" dependencies = [ - "num-traits", + "parking_lot", ] +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + [[package]] name = "osmesa-sys" version = "0.1.2" @@ -1971,53 +2064,37 @@ dependencies = [ ] [[package]] -name = "parking_lot" -version = "0.9.0" +name = "owned_ttf_parser" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3" dependencies = [ - "lock_api", - "parking_lot_core 0.6.2", - "rustc_version", + "ttf-parser", ] [[package]] name = "parking_lot" -version = "0.10.2" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ + "instant", "lock_api", - "parking_lot_core 0.7.2", + "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" -dependencies = [ - "cfg-if", - "cloudabi", - "libc", - "redox_syscall", - "rustc_version", - "smallvec 0.6.13", - "winapi 0.3.8", -] - -[[package]] -name = "parking_lot_core" -version = "0.7.2" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" dependencies = [ - "cfg-if", - "cloudabi", + "cfg-if 1.0.0", + "instant", "libc", "redox_syscall", - "smallvec 1.4.2", - "winapi 0.3.8", + "smallvec", + "winapi 0.3.9", ] [[package]] @@ -2028,82 +2105,61 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pkg-config" -version = "0.3.17" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" +checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" [[package]] name = "png" -version = "0.16.2" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "910f09135b1ed14bb16be445a8c23ddf0777eca485fbfc7cee00d81fecab158a" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" dependencies = [ "bitflags", "crc32fast", "deflate", - "inflate", + "miniz_oxide 0.3.7", ] [[package]] name = "ppv-lite86" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" - -[[package]] -name = "proc-macro-hack" -version = "0.5.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" -dependencies = [ - "proc-macro2 1.0.24", - "quote 1.0.2", - "syn", -] +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" [[package]] -name = "proc-macro2" -version = "0.4.30" +name = "proc-macro-crate" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "unicode-xid 0.1.0", + "toml", ] [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" dependencies = [ - "unicode-xid 0.2.0", + "unicode-xid", ] [[package]] name = "quote" -version = "0.6.13" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" dependencies = [ - "proc-macro2 0.4.30", -] - -[[package]] -name = "quote" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -dependencies = [ - "proc-macro2 1.0.24", + "proc-macro2", ] [[package]] name = "rand" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" dependencies = [ - "getrandom", "libc", "rand_chacha", "rand_core", @@ -2112,37 +2168,37 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.2.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ - "c2-chacha", + "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" -version = "0.5.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ "getrandom", ] [[package]] name = "rand_hc" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" dependencies = [ "rand_core", ] [[package]] name = "rand_xorshift" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ "rand_core", "serde", @@ -2159,10 +2215,11 @@ dependencies = [ [[package]] name = "rayon" -version = "1.3.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" dependencies = [ + "autocfg", "crossbeam-deque", "either", "rayon-core", @@ -2170,47 +2227,52 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.7.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" dependencies = [ + "crossbeam-channel", "crossbeam-deque", - "crossbeam-queue", - "crossbeam-utils", + "crossbeam-utils 0.8.5", "lazy_static", "num_cpus", ] [[package]] name = "redox_syscall" -version = "0.1.56" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +dependencies = [ + "getrandom", + "redox_syscall", +] [[package]] name = "regex" -version = "1.3.6" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.17" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" - -[[package]] -name = "rltk" -version = "0.8.1" -dependencies = [ - "bracket-lib 0.8.1", -] +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rltk" @@ -2218,54 +2280,43 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c83076f8174384edf79d3f4a91934b8c0e1feed6eed943608d54af790f0a7dd" dependencies = [ - "bracket-lib 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bracket-lib", ] [[package]] name = "rust_roguelike_tutorial" version = "0.1.0" dependencies = [ - "rltk 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rltk", "specs", "specs-derive", ] [[package]] -name = "rustc_version" -version = "0.2.3" +name = "rusttype" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +checksum = "dc7c727aded0be18c5b80c1640eae0ac8e396abf6fa8477d96cb37d18ee5ec59" dependencies = [ - "semver", + "ab_glyph_rasterizer", + "owned_ttf_parser", ] [[package]] -name = "rusttype" -version = "0.7.9" +name = "ryu" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310942406a39981bed7e12b09182a221a29e0990f3e7e0c971f131922ed135d5" -dependencies = [ - "rusttype 0.8.2", -] +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] -name = "rusttype" -version = "0.8.2" +name = "safe_arch" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14a911032fb5791ccbeec9f28fdcb9bf0983b81f227bafdfd227c658d0731c8a" +checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" dependencies = [ - "approx", - "arrayvec 0.5.1", - "ordered-float", - "stb_truetype", + "bytemuck", ] -[[package]] -name = "ryu" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" - [[package]] name = "same-file" version = "1.0.6" @@ -2276,25 +2327,16 @@ dependencies = [ ] [[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "semver" -version = "0.9.0" +name = "scoped-tls" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" [[package]] -name = "semver-parser" -version = "0.7.0" +name = "scopeguard" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "send_wrapper" @@ -2304,29 +2346,29 @@ checksum = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" [[package]] name = "serde" -version = "1.0.117" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.117" +version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" dependencies = [ - "proc-macro2 1.0.24", - "quote 1.0.2", + "proc-macro2", + "quote", "syn", ] [[package]] name = "serde_json" -version = "1.0.48" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" +checksum = "063bf466a64011ac24040a49009724ee60a57da1b437617ceb32e53ad61bfb19" dependencies = [ "itoa", "ryu", @@ -2349,11 +2391,11 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f08237e667ac94ad20f8878b5943d91a93ccb231428446c57c21c57779016d" dependencies = [ - "arrayvec 0.5.1", + "arrayvec", "hashbrown", "mopa", "rayon", - "smallvec 1.4.2", + "smallvec", "tynm", ] @@ -2365,53 +2407,44 @@ checksum = "b5752e017e03af9d735b4b069f53b7a7fd90fefafa04d8bd0c25581b0bff437f" [[package]] name = "slab" -version = "0.4.2" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" [[package]] name = "slotmap" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fd553261805f128e2900bf69ab3d034260bc338caf7f0ee54dbf035c85acd" - -[[package]] -name = "smallvec" -version = "0.6.13" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" dependencies = [ - "maybe-uninit", + "version_check", ] [[package]] name = "smallvec" -version = "1.4.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" [[package]] name = "smithay-client-toolkit" -version = "0.6.6" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "421c8dc7acf5cb205b88160f8b4cc2c5cfabe210e43b2f80f009f4c1ef910f1d" +checksum = "4750c76fd5d3ac95fa3ed80fe667d6a3d8590a960e5b575b98eea93339a80b80" dependencies = [ "andrew", "bitflags", - "dlib", + "calloop", + "dlib 0.4.2", "lazy_static", - "memmap", - "nix", + "log", + "memmap2", + "nix 0.18.0", "wayland-client", + "wayland-cursor", "wayland-protocols", ] -[[package]] -name = "sourcefile" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" - [[package]] name = "specs" version = "0.16.1" @@ -2435,44 +2468,46 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e23e09360f3d2190fec4222cd9e19d3158d5da948c0d1ea362df617dd103511" dependencies = [ - "proc-macro2 1.0.24", - "quote 1.0.2", + "proc-macro2", + "quote", "syn", ] [[package]] -name = "static_assertions" -version = "0.3.4" +name = "strsim" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" [[package]] -name = "stb_truetype" -version = "0.3.1" +name = "syn" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f77b6b07e862c66a9f3e62a07588fee67cd90a9135a2b942409f195507b4fb51" +checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" dependencies = [ - "byteorder", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] -name = "syn" -version = "1.0.48" +name = "thiserror" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ - "proc-macro2 1.0.24", - "quote 1.0.2", - "unicode-xid 0.2.0", + "thiserror-impl", ] [[package]] -name = "thread_local" -version = "1.0.1" +name = "thiserror-impl" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ - "lazy_static", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2481,7 +2516,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ - "crossbeam-utils", + "crossbeam-utils 0.7.2", "futures", ] @@ -2491,12 +2526,27 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ - "crossbeam-utils", + "crossbeam-utils 0.7.2", "futures", "slab", "tokio-executor", ] +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "ttf-parser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" + [[package]] name = "tuple_utils" version = "0.3.0" @@ -2505,112 +2555,94 @@ checksum = "44834418e2c5b16f47bedf35c28e148db099187dd5feee6367fb2525863af4f1" [[package]] name = "tynm" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e87d8ff35b1a0aea28758ec2e5959f9e5d826cebf2349a3a7fad43b3e78de28" +checksum = "a4df2caa2dc9c3d1f7641ba981f4cd40ab229775aa7aeb834c9ab2850d50623d" dependencies = [ - "nom 5.1.0", + "nom 5.1.2", ] [[package]] name = "ultraviolet" -version = "0.4.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "432f35260aff6ee992f3027a1166eaebd7c6ebc0844188b0e545f7fa2121daf7" +checksum = "16b9e3507eba17043af05c8a72fce3ec2c24b58945f45732e71dbc6646d904a7" dependencies = [ "wide", ] -[[package]] -name = "unicode-segmentation" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" - [[package]] name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - -[[package]] -name = "unicode-xid" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" - -[[package]] -name = "void" -version = "1.0.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "walkdir" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", - "winapi 0.3.8", + "winapi 0.3.9", "winapi-util", ] [[package]] name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.58" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.58" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" dependencies = [ "bumpalo", "lazy_static", "log", - "proc-macro2 1.0.24", - "quote 1.0.2", + "proc-macro2", + "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.58" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "574094772ce6921576fb6f2e3f7497b8a76273b6db092be18fc48a082de09dc3" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" dependencies = [ - "quote 1.0.2", + "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.58" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85031354f25eaebe78bb7db1c3d86140312a911a106b2e29f9cc440ce3e7668" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" dependencies = [ - "proc-macro2 1.0.24", - "quote 1.0.2", + "proc-macro2", + "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -2618,25 +2650,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.58" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e7e61fc929f4c0dddb748b102ebf9f632e2b8d739f2016542b4de2965a9601" - -[[package]] -name = "wasm-bindgen-webidl" -version = "0.2.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef012a0d93fc0432df126a8eaf547b2dce25a8ce9212e1d3cbeef5c11157975d" -dependencies = [ - "anyhow", - "heck", - "log", - "proc-macro2 1.0.24", - "quote 1.0.2", - "syn", - "wasm-bindgen-backend", - "weedle", -] +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" [[package]] name = "wasm-timer" @@ -2654,16 +2670,15 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.23.6" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1080ebe0efabcf12aef2132152f616038f2d7dcbbccf7b2d8c5270fe14bcda" +checksum = "e3ab332350e502f159382201394a78e3cc12d0f04db863429260164ea40e0355" dependencies = [ "bitflags", - "calloop", "downcast-rs", "libc", - "mio", - "nix", + "nix 0.20.0", + "scoped-tls", "wayland-commons", "wayland-scanner", "wayland-sys", @@ -2671,19 +2686,42 @@ dependencies = [ [[package]] name = "wayland-commons" -version = "0.23.6" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda" +dependencies = [ + "nix 0.20.0", + "once_cell", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-cursor" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a" +dependencies = [ + "nix 0.20.0", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-egl" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb66b0d1a27c39bbce712b6372131c6e25149f03ffb0cd017cf8f7de8d66dbdb" +checksum = "99ba1ab1e18756b23982d36f08856d521d7df45015f404a2d7c4f0b2d2f66956" dependencies = [ - "nix", + "wayland-client", "wayland-sys", ] [[package]] name = "wayland-protocols" -version = "0.23.6" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9" +checksum = "286620ea4d803bacf61fa087a4242ee316693099ee5a140796aaba02b29f861f" dependencies = [ "bitflags", "wayland-client", @@ -2693,54 +2731,44 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.23.6" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93b02247366f395b9258054f964fe293ddd019c3237afba9be2ccbe9e1651c3d" +checksum = "ce923eb2deb61de332d1f356ec7b6bf37094dc5573952e1c8936db03b54c03f1" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", + "proc-macro2", + "quote", "xml-rs", ] [[package]] name = "wayland-sys" -version = "0.23.6" +version = "0.28.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94e89a86e6d6d7c7c9b19ebf48a03afaac4af6bc22ae570e9a24124b75358f4" +checksum = "d841fca9aed7febf9bed2e9796c49bf58d4152ceda8ac949ebe00868d8f0feb8" dependencies = [ - "dlib", + "dlib 0.5.0", "lazy_static", + "pkg-config", ] [[package]] name = "web-sys" -version = "0.3.35" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf97caf6aa8c2b1dac90faf0db529d9d63c93846cca4911856f78a83cebf53b" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" dependencies = [ - "anyhow", "js-sys", - "sourcefile", "wasm-bindgen", - "wasm-bindgen-webidl", -] - -[[package]] -name = "weedle" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164" -dependencies = [ - "nom 4.2.3", ] [[package]] name = "wide" -version = "0.4.6" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5825a3b8b18b67af1c24165f8ae1feb8f691222736d14099c3483effed2ea729" +checksum = "46bbe7c604a27ca0b05c5503221e76da628225b568e6f1280b42dbad3b72d89b" dependencies = [ "bytemuck", + "safe_arch", ] [[package]] @@ -2751,9 +2779,9 @@ checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", @@ -2773,11 +2801,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.8", + "winapi 0.3.9", ] [[package]] @@ -2788,15 +2816,14 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winit" -version = "0.22.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e9092b71b48ad6a0d98835a786308d10760cc09369d02e4a166608327f1f26" +checksum = "da4eda6fce0eb84bd0a33e3c8794eb902e1033d0a1d5a31bc4f19b1b4bbff597" dependencies = [ - "android_glue", "bitflags", - "cocoa", - "core-foundation", - "core-graphics", + "cocoa 0.24.0", + "core-foundation 0.9.2", + "core-graphics 0.22.3", "core-video-sys", "dispatch", "instant", @@ -2805,15 +2832,18 @@ dependencies = [ "log", "mio", "mio-extras", + "ndk", + "ndk-glue", + "ndk-sys", "objc", - "parking_lot 0.10.2", + "parking_lot", "percent-encoding", "raw-window-handle", "smithay-client-toolkit", "wasm-bindgen", "wayland-client", "web-sys", - "winapi 0.3.8", + "winapi 0.3.9", "x11-dl", ] @@ -2829,24 +2859,35 @@ dependencies = [ [[package]] name = "x11-dl" -version = "2.18.5" +version = "2.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8" +checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59" dependencies = [ "lazy_static", "libc", - "maybe-uninit", "pkg-config", ] +[[package]] +name = "xcursor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +dependencies = [ + "nom 7.1.0", +] + [[package]] name = "xdg" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" +checksum = "3a23fe958c70412687039c86f578938b4a0bb50ec788e96bce4d6ab00ddd5803" +dependencies = [ + "dirs", +] [[package]] name = "xml-rs" -version = "0.8.0" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5" +checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" diff --git a/Cargo.toml b/Cargo.toml index b5572467..762c3ad5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,7 +83,8 @@ members = [ "chapter-71-logging", "chapter-72-textlayers", "chapter-73-systems", - "chapter-74-darkcity" + "chapter-74-darkcity", + "chapter-75-darkplaza", ] [profile.dev] diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 1b0d21cc..d8a9c434 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -78,5 +78,6 @@ - [Text Layers](./chapter_72.md) - [Systems/Dispatch](./chapter_73.md) - [Dark Elf City 1](./chapter_74.md) + - [Dark Elf Plaza](./chapter_75.md) - [Contributors](./contributors.md) - [Licensing](./license.md) diff --git a/book/src/beta-webBanner-old.jpg b/book/src/beta-webBanner-old.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9a98bcddd8388b3546858ae174459e022046bf83 GIT binary patch literal 36071 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 {8;VLFq;!;dhQ~<9#koc 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 literal 0 HcmV?d00001 diff --git a/book/src/c75-altar.jpg b/book/src/c75-altar.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cd55e3270cc6c61ab43011c373f30c7d2961c68a GIT binary patch literal 130774 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%0Dpi{6cogmt!Yx*Ff+_-PXLcHe1>&!^@BHn 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

R-p&@ZL}hut=up93%)vW%4iuJ-s{ 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

FL?9+6H5XF&!f^02 zD3VjHNGU(AO@3;$Usw(Kh@C~v#ZS-F$KorBi>Io^ND{5&pfs;8T9Kb5Xd84+=j#LUHI8iIb2u+|2AJo-gccuU4zEG&e`Ur^d( zPnQ1F;xT+lX4I#%?+7x9HHC?{z@At{R9Wxu8TObzp82-2#Nt`~Lmoy`Mo)w`^&w0i z$}?5xc$jF`1lLP!<&uKT)CP5gZIiPbu0@sLN_vqD2Y1Q30b28fen8_L3%{Y{MqcHQ*uf7En}iZeVYn zq-;!`1a)Ehv}>oj=qoaPnVBh@(C|Na_d)ja6DGxo0Gb+{f#95WcMW}taVqp~Mg!gG zZ@_V>b+}2XLO~fG&sp8OvlUF%tIwlr0%#tx{$U+EiD;?V84rc~Exb6)zw#455XfQ* z2U&|XEQ6dT%`2?K%jU~i&sN6^@M^w{=Lyp{b|Mb*>x0BE-`e%P4iUa1QwOc4VnXX4 zB1-8qw8mz$ZV@r*TCJSVe$lP8#8Yt+ZI|_giip3?`Mb7PBUFi3iGQYT@TJ1)doH26 z5wRHJ_A~Z{b3Sv)=V=Kk^f;b->IKx_*(1UzEF&90|(`;me)8I2vJ(Z|+D)cl@{++zPQD$4b)Oh|K5}Ba+=T z(6Q8XmO{EiB3{;R?9Bsm+P$Xsl*6= zHZb?P-8dhYb)I59lrUR^P~HIF{V&M#AVB7;TA46`Q-W858>!=cS2LCrEUXB1UFVwq zxdmsuBx4DXGrBO>6BA=lDmB%|Dq44JZMzB~y^bsmiXQ~o3zd{K)(`9nEp*VsisZ!m z<>Y8Abx6|-2n8d;V(;}7aI(wEVR&RnLdei{iXN-2hcwEPsaEi-?)AvMZzw=7S`%#O zsA_nm&QG4rkkB<5<1)t`t4#gM(9>h41ta6piaH02!pqMd$Lm?k9^q|tybxlpoZzcEJKmDD5fFlfecdS7km2A*)8kN5D- z1@5~@QEj8YktRX%(;2VQmr z;W>QlNDXy#4IZqlJtij19Y@`JnF_uT$8$Dd_#vuY*GUuB&{V-X=KHQEk3#Gk5kG zj4>F-+)r$6%cP0=t!(-1(MLWd@wY}$1t>PpWS1uvFi9BKY%>|QB3^?VXVOo$z|1}= zS{`|E_t2ffy^vpJ>Nm|(wvm_U!%CKhWh!>ID@mk4JS8N-kzp&GE`Dh0VK^^|iDMiK ziFUd2bNrX@$SqYhtET3;Q#=aCZokh=ZMrunkxs>L370!~{~GV5IfZ1CzDymq-7>+O zJ@R`M z<5`Q?PQ?W4hnn~37j&XSgqAm816o?r4s5LI5reT6GuTfgsA`6oOtc~@+*$F#wdv!~ z*Tc%)9J@h0&q2m3qpO~anQ%$l32WZ7LF5_`r!oWArx?lNI0@oHz5pT7Pw>4>TPYW< z{I#i}DON=VdDTR@jZlfTOE9^wM3f)?hIrKswpm{wsOTPuk1MIWVzf*CHGdfH=101N ziJp;f@M3qb_N$u#PsC?b`K`lk9GPhzWBoqR*|n6kn&?AvoJp7;uJXND{*A%`!t@eS zhI zM=@7blGHx5};LOzD@{_txg_)?`oSNOLv@s)_BgEw`ZF{GN> z!KvvV8h8vX!A&GXsSpfu!LLwvHGqzvcsO*TIcM5HRK+Ue37Zc8*je@`jiJ6)Af>@; zhPe{m3PlC^-to&L0q{y>M+ZV6|GKOh_7@~PWEFSqxig&PM4WJWB1+NqVRF(Qb$Lye*y8=L7n zv=+3Ey9+b%G{0$XD1{<{xS2^^kE6FKA@PidS8joisIhfsR5w-o9v)kNR7Pb|HaZW; z&5Wy~1mP%`Q9$!)T);2fP1Mb8mlbN02qp_74CQ~7#vDdu@v3vH@&e7yO^ERFd!i6c zn}&cDr9Q_Vzn4;_-+$W2MU|NrtjY)xr3A)^IM#Am0 ziJC>>-Xhj6bdLyL$~YPcO@;D{hJy!%ifSt+YQr)OjDoSh3C7P#q`EUNr@%R8`10DU zHxUoyqPRXlf%aRz5wz<(r>Mf6a=q}z_pKAa6^C~kK(*tttO>)a0*BAPzxO{jH{0q% zG%n;zj}iy}p41YL3px=vmD*kwWb1VGPsso8XznXBMexK^jROR5ymZ(k@Kh^Emg9if z_h+&j#q7(;7bl~zcw7~^C zgVXK;DQDd9CEbOnO)3VwSJS3PhD=`JPmNaU6rk#tji$d5CLY@+zi zE)BxQPiz@W4#|>`Y;m3s<3A#6NgEtY&uy1u-uA-QF96Be+s?s!=K*9E7VA>F>W(WX z^LQw#l(>8{&5I}mX&X9_s`>;fV?{V{wHR7kSA((8N_WsCdC&>cTj8q%%`~n2t&k6& z5IRVcv6Qux*cZI91R(}d566eu;b2Spkz;Yu=pLl~>?+V}vr-6WsCtM7K$DFcuz1+3 zI|@q&^zm>aH5=|P=;{zOy(Tz4C@U|bf}GzaghPqR6L3mK9E&|AK^3mV<~SpV2yhl> zAJrO&6_TSg)8#q-L#AAAkLs z-$%idrhY=10kwEhu`O~v{Nk|tgwm+ud@(pw*&knqrFK~sd)vO}1!)SAeuT_2LOTdW zvHag3{y<$KtCA`BEVDrE`W$-H-)U6J zQU~1l)rpT(aw<#2D9Brwx5TFG>%uC(m4-{q@V!DOs%WJp!Qw`%b#H=8e@Je~^g=oT z$-ELy8mx4C>=xcPIp`S?Zf1NP6QZ_q8Ei;TfQik(>Q}ufOxNfrM3su*xG>{nc@)MN z`YN{r0H(F$6sRtg6&oq-S-PUHgfr`sMD^9&`3%c6vuQa2x5H;A4~oD_J%q7PH<3mX zb~AN>hcTW?-&zyjC2vt{m%K;#6fvS~OY8_^`~L8T%5G=ZrfO?;Eb{efrc#6{Cpubc z&n`(GhRR_Vy6?xy;SHP7TD_xCn9p!_zg2BbYL9e>x7>0!d*V+dG||kG%sj2nqrn2$ z?}7vD=(hv)jbOhu7-ZkD&mZV&1+G0EFRw9O7(N`oa~a*O{6m??dRNxrTqgar`kW>~ zpx0F@x!uwZn!Q~~mvVk~+KKQsMz)wdK$gq38H3s*J{)6t22a0_f~D#J9}U5@pTa5VIu2RyT~ygHdmukJl_ zT>!%Yj;+1nU^N{yy<@R$xPip?Xr6J&^=|&gJIU(o>Jdx(bd;oe-2fhE4zsLDUs{67 z!;nfz5)g6&I6cs!fgV||v7Wx+9$3?3Ks^YF*QX|V`^KNOQg`&=W(L12?IHj?mJT%c zbV*_=fwF7WbM1X?m>MR^6?lcj{4hYExnE)FAHs^_oLkfT0X!BVcoE8jq)~at>rG9c zG-Ud443vtTDEOJBK?OVW24n?A?0@_>_LIn)y2!AquOKc)S11$fE`Bt6O zP!u1h^vomNFrG*5>L{f|FRK2Gzl2FJ;i@wr(#xJC?}rx#p<5~Ak{TbDaX8zu1zftT z!AwyNo_k=+x2zH70P`NW#p(S?nX%x+Zg}bXd7fr2G{2Aiqm-k7w!^7(n?^9K%XYM0 zZSYTN-fr<=&9{5oBu7US<`(w3Qzn&^9lw1$}7 z|Ldn-uF+wNvF^ns3k{HT0V?i;k) zu0|46tV*VLp{JM4M|c;QaNzQl%el)D>tX24D#LPO$h}?vU>mRO^H{4P@o25UucF%s zQdT2ybAaM-FNLvlBQ5;(9nIrUKPOcAOcKGAD|W-~d2v!`C0lFt?05U|FvR2_tTsCR zuFHI#&v&1}SlhrkYmqj#UsxTx#F7CjPU6B|29DHMkW({ zEMHNg$cSj-Ju@_vpA=9@3HQS4549C>fJ&;#5ncdbB!~)xyKAmVx(~dATU~~G3A;@p zknBMHZEE8sfJFJ`7x9vwsZQh8={Cy2zw%|}Orea>ghSmV75G|uR4udsH?lKgJ=?a zaDY7kv~*zt)lLcteOH?M%-B4V`^HVA16ux7{SosN>O0tb4+>~Cuf#0hyU;6B(p#9`RzfOXr3UUrztp>L9kGvKUZ zXu{0Fwag^eI8J{UWQpg4!O0KKbGu?z}Mr9)=d~AoVhhH%ufaQAI*bONw3^;e7oI zje->Sj-=OmTFmcq_f>7M0zMUkmF~|>43v;d%6gJ>bu{1h*8`v;fge6R-j+Q+VXVM=H69F0&ntv2 zVu@KEhTL)E_C8Qf#A%|r&t%=sl9le}@<>}d3io z3JQ-=a^$>6y-OG_V?5!yI@(~5aDp{|Fu~7VB%vDN7>o`>ob}PCp;&}1tXa&t9hH)Q|d7b}q z#Kcl`>)sSw^kDf?lU58)ura8|KWFFZbQo%HivKUj8wpIn9vTO+_M`rZ2|6qKlL8Yk zwla))fC$qd3<OIhVDn0&t8jjxrRTZb?+~DUVjo5|;wJ#;OPj|GhUp>zM?}Wor z0;(*E-<5DZay{MJC~GG^+Ieh#rGuJXII|kbT#=SPxKvEe0_CylUn~_JA3!FqHiJK% z_b5t6(3u8N*!HvE=ETAH3`4+I|vPRfQI@hYoc1wUbK@pbQUP{QE{(B&I26JGd^B zpN7zvqW@I6;nCIFxH<4{XcS3)-q{G@P%CZEJCRV|_=qK6g`7zTR>(sooMwCaL7q-)LRLd6 z+PVu-A(}FtufOoT{`3A}?0H4hC8QX+XY7s_UVmpc&R16%fcx_IxhlNPhf Wwc!B`yl5s6J)TfWN#(`g<^KV;}#GgAh4D;G-!IRzEe(KM_?K}yO1rLHEcpezGMLl7*@#>Cbc?=l40+POHX z%Skfm+|*^jpM{8_GtepMEQD)h;_RTJA*%`fx7YR{1SmmJ9~Xw!e>?0yKOiwRb2fn> z90u@M+{D4j1+)!8o8R5V0n?5LZG?%Xktt{wf;OiUU=Xy2G5yBJ?PW~c0^5dxCWLU( zRF?v>AwZkK;;**xUu_dhCtEOv7mQ&swY3BEVLHccQ%w5-rfqBE3V6d_7zt0B*=cEj zI}7-wh2$UwNCiSc43IJ83RyumkPE~G?slNZ8Bzz&CI6fG=dklpV3skMWd)gl8PbqF zWD6N#=R+79fHC0uN4L)AS9!2qI430`2>$u#Xpy`i$css4@V8&Wt!1IT>Ns!3FOyb8`sV$ble| zn-E0#5rT;Hj`aqfVV*-7LlC41bfw%0K?#Wv#AX3#-}-OlCIA!u=g5>5>j$fVq!9CGIB};6&2OVQ>V{R zBhFADs1O(;IJjUA9-I&kCq$4ClOXVs1RnnV6b2+Xfq(P~I*AKrlHrm8D9`>p^>+IA{Qr1AE4)`) z;j}%0`(1URb$Q3W&r9G%Yng!PZM^_W^D;HNKKE1n!fTf_TQ+A47G+gZx&YaKyGXoV zblH^8T6CSLzhQysj-4tQZ8^bBcka#g(bZHIv&H3>k@n%&2Gz?o2VxxCN09K_)|}JY zhi6~T%_UCNPRv=CyRTI^k3T$P7F8=dVUhB0vRlla#Bbx6#r~qiC=U5Epn$_-#@3c?9zmUl zWzPo&;o`^=6iH_L$05aM3a<>f+tqSsY9$=H4XW>j3AC$a&D1J64j18Ts<^fQOasU^ zm@5Yb7`~5t+-2qH6Pw{L6kVn8Awba-4~3_r4wpOMWfOgXzm*!b)7^cT(C@GSOG8k> z@`+$592AZuu{sxq1(-KP^T5p`dSsCM@?hzE{V(kqv&GV_eWl~C&3f$K6madgw>b6G zZ;GM54ox7`!LoGDz5Cr}si)r@T=;UxH|dqo&$so_*E^c}tpo~39f2T*LyXokxfl*1 zk~$GjFWZG120Uxo!83z)IF|}c>T~ryvn^(;t&m+mN7v^~HlxXbG6;;l?w2_>m3^*@ z+IVI5 zCf(QgZPqWFv|C_EGNn`EP1&6(_sgD$*L4@vzS~|4LWWc){Os=aQk(I@Ur3fSkfB0p zrm@0dZGd42MXm|KhyXzXNWVhFDb%l>dZwMHx#}r%X=mcAFM^RT5~uR3s0p2`>Qb3g zhx$G0aS>9Ev!2_j13O50AsAla08TZLjPk@d z@&FShtJg|{%%$JQ_X}k`*5gJrEBl{m@DAy45J!3kGnOa(to+5IryOZ8ktSYR1%NEd z*G~&#h2wERa7If80*JVx)!xZWO}Jgnw$;KkiTCvRnAnt&r(?5waiv!`#$MUac?Mf# zsV2u3qno@^rg;~JRs}7Y0;zEwpLXBdQ)|0skFl50M|KpE?})e}SRgo!6-r}3pnyhw zIKuJx``nHCQoKfT2Rrob#_wCDc0K#9S8lhpIx`xOGMmv`I`PD*nCOnp*SHd%`MBG` zGxJren@RcP;yy-Itrq{-tz^A@6+}0NX z<<~jf4;5Q8&hkfzy;`a~|MqJCm}BRY_Kuf5I0*4{m=XX3|NYjULyegf`~`hAQXdrB zj8H-n6`YGiDk6P-(-43p!W)7$CMmmnu!(rg>Ap1AO?tX&5yvxK*0J$*b$GSQl-Hf) zS$o=St^_9@im5?Cnz$ zpqP4$V(LyU=qugc)70X?QGYZKi)~OV2!eBv-b)p@aDn@C*YyX2*ORqCO!jAB$B~9U z8Pyx`NrecNCM>iW=pP`>*`%Qg1E>H1Jh)DqyuY%%Es%$Hj1(9 zCH^SfiwyzsNF7*M$>HnV4?&Lyj1gC9Pm_ELn>BHm z*pI}ds`Aq|h1q@9*S%Y@oZbLIOh|IK_-_NV>2PmfG;m2@opf>xn8>C1cUFGbXY3cW zzNYW>cv0tLgTTX>fr{OZc9X|pjLX-rbFOQuWn`kWb9UWj@4@TOW_djej#F>_hkElN z0u%#Yw9WcNVY{@B3`{~=hajDU5h3FX5Cs3j^^Zq;fa_i*!z^(QB0ac-vbV#y$-)f^ zjlR0m1Dj#hn@;;jkZ2C|_}Hhdb(_)7V$}(;%*8jKWeAWE8Jyx+DP+OKI8H1TwrYr=f@EXfU-daFzsxNJSx4QXgez=sj(osdw)%d-QBsva4AG4Ni5zI*3xEzigM zbrsU>Nl|f0w-fpn9Bsybq>57YBOwMjNJ(W)88=e=Wc*M%ARxgS35^gk1R-Gk(V5C| zFW?X7*B&Y?#@m0MDf3${vrz3$@tcy@&5IzK0yTzziRRaYP3xg|l@=~TLp^S5M5ac4 zo!zqu{O-ms&OT^O7^p2mNK)v|z1||^Bqm5xkY)-j;q+~g@dY^G2B2NScml@!noXV( z6PFj3Zd$igJzy22e3k=`fh!U_Q~*a;o!St)*e_bUSeso}7gFi12j}7Y?(Dp)70-Oy_7R>aX+)#%J83J4R8wAP%QPopD{*Jw7`SA-V(?`B- zzFCAyv9z1QZfdD(CBpn`E29Y$i{B4wkN_)r5;jy28E3R45@QT3akyW!jE@l&8M$#F z24??8{Kqwcsaq}y*0cm1M5e=nWSVIertBh@y6@`Lef#J-USq8e(!J($IDjAwI4(gg zr!Zf_ystqx@U>_ez%;n)u+nqj>4V~O2xJD1^>6rFDW2DNZji7J*|aL6d`FZY`M?!V z`VN^lWspt{mCfbi_kz-i9V8?i9BCf}H4UJ7Z=OPjpcE(rxNj`nkB9{*+{IvrK^2CTFN|OqKWB~q;YZf3|d>xZmOUTi%MkJ6w zm?0L%`7byea@~sZZ6opp(wR~Tp7c#lBH3|2|0b2M1m&BSRZ5)?DiH5p>w^`Jb!Ryk2L6-Z7@ z4Klzch21HNphcmBkns*-0Kx)O`RTX(t-IRd%xBan-)L59^(%X5v=NffCM}m8rAKZ} z1&64Zycu+`@O(vpQ5A#*k2eU?oOu5tU{cP<#j}911bE9o<4>w=}12`6&WYPO>&SUL;tqqGZOv#yOlX+U4opTf~*Rr7?6-8il%Ph zK!F0qpHW^NK}Lb%2VvMO9m$CoEe?3XBo2)E(3t(kC>9c!Vj-{CYulne$;0i|;-sQC zH&a@k45)SqC2U}6X}M4ZtByZJjv%nTGCyFJMhzpBMpMHm&<3~SrWz;pz$*P$v&oOmOdK)SlFFhYeTtXa&2#1IP`eahC6&ub1 zQVb^`;_atN4X}w9XU~7Xvgc3>TS2JZUI+H!Kyjw!NfK*8&*6khh6sOf=AaG{q}g7c zC}@|?#LQ;^dhx*;%ru?m@64^c2XGT4fdX%eWN0lop(KvDk~oB9R7XOD+(UGq3SR{x z#AV178FUCR+7M5tKvyPt{!@nK6yHU#rUNfUV(hEKE=dG74jf3ZCsoWO$rky3(-KKW zF0CmpgYjS)GLR@qUXQLGL2|olPw!&%>4}?!Pwi~d9*ch)_uJ_%^#omR{(Z?dibu>^ zlcQn$2v6gmaWr1EpB4nSRaM->Oc|{qh?pV|A|QDIs9Sj>y(9A_3>kD7Fjj}XCwHf` zs09IER0M342Kl!7SLsE1dcDHp`s*&%>x~qj$~bGj**QA*(Bkccs6mP6^oWzi^R2B_ zL4G5bskaq2Le85LFE)Q(2<;MW_Rk3>l6`jDx^o^+x)kSTd}x{!jyzdF!=Pz28OU=IUMBWnt0bTnvY9G+>Bggy&BLY;d;H%0+3tw$<)wp5!5_Z-DB!$ZpEA5#Z?%~! z(Dh)y#*_Zp)|C6cufI5w0L}=OWgv#|pvYdhABrJbyo8LCnBi0mO;F`$(Xfp$+BGqy zrPl^>>!||VbS7bZ+Wn;ZFu}y-?(02Iu|jtf zm!0ZoDMwJ3Ze8i9#-of6JKeE9(rGYn0QaZlU)2+KI$ ze{9z2^$xjC#ijSH3aj;O?6o%TUAOr3^=D`*b+^#oA-(EO-C_NbW%q1%x~JUfJd^qt zx2_J62z%+iQJZ>mzJ6(b#L;HJeevs%$BkYmkK#rTedm#{EfZlkehAh6_~3p=q|&JKo_yJ(q-W3LzJY(A&rmV5s=o8^*A=hRs`bSr0c>}Z&ukbjrF4aE zb(?Y5%%qug=JZcZ%(|a+w=>tLYK?rg2bR16hjWltDq#{!wtm*Hqr^xIZF8AAK zJ$UFHF-RTsI{k6!{E*mZuGI70!a#W{JM~hXqeA%*Eon8;!+UV|EG5i;w zCR}`ye0yqSUwBDG#tvg zUFDWg#xNrsGc?TR={5CRHR?#kGpt4~HCWCkyMGzP5ec=1YldG28fA zVHPyB`IBB`dG5i)S?7(#Epy$=B8T$ilHNq&;3y1_2!eq#2(Tn4%PT(=9-0tIlTh~A z>(`kSa4!YmUTk$Wj?1&H6SL+UKgaHEc$!qwZC4sXE z>{-%Pu~|K^~o-Kr;faLWsx~-bb_9@&szvouEz;{7OBggNGkYx{Tj{d*P)`R zgCeCh*KFzxJj@D~=rZUr0XUuva|#DB^?M`92r!A19oC>A?#+qDsY&=bggF|!4NmX$ zZroC^Xgs}uKeFbHITwVeAXo^L1|~8fAsj{pZB`aE%m;*UaukCOtbwGX`|}~dF~ho; z#O}bzwuY8NQ`PF++cv8g^x}AFkS2s9PCz9NrhBJB5!RC7M0gOnxHp_koD(9KRs0!O zzZ(XQ`nk!#pJZ@hcqjh+F#rdg^pk@#ISQ&1ln6dDY9>~36ax$MWr-_>;PjsW{80c0 z4kPafmGA$*@WS7x-?OlMDKq}2gkjy8-*9k-bqqDjvNYNz0g{zzN zC82(gmS;~rCp`t(y>8cSjxpUd7rL~iwW@qmu7h*scAD*@`?q}4rRmR2KZK5;N8qG+ zi8soWQs3+O( z$&!nOUWPE}{PIy!Dhk`lv49|~E=z}U(Jn!#v5~>Ku1bE_>ax5;#=k%FlKu0js59Bl zX35z?FG=|SdnaHd3B$;L+f4H5!o5NV&$8o zH@Pgi%H;<83v{>XO1o%raBTSyK}%7hvoEkueK{6 zE&e>ww6b@zu99PhTeAwCOBo#@@W^Q$E`|vy@QGi=I<>i z&Y=re63?@w;=*|(&!KqSzVrwk;+j)ldqxbS4%_<(P0C6d9{FF~=$tf9JrUNizBLbiZd!->31g;$@n!d5*6#fvRIR?_aYubH|_M)3nsH z45o3*p0OKzI&yGPjQJ_z=K3v%oT)i}5i!iff!`Bd32m4be4-RK?lV1&f5iQqDJ-eBD& z=ZgXoMUh_MDE%^7(M7X7r~Q!Yjn@r%uM~|8238r_>7YNLu??LwCpycs$_Kam(q^-I zzu5|(3LG{bw>~y`>z3QsTg4=}Id5e{GUMiw%(O54_?wQ-7~0BMhO*T$M?S|F-N!e`kF|H3lFU;1KSs`jSt>zK;?NmGMs&q%L9?msUFWta8jh$G! zmfs-%59tRlJM|Q7rwb&enncUbQUwolRxJ4!MG21+&-x0#I+N{bEetETGF47D3J++o z5j|#d{T|z_v9R-oW43zFdeW0C_;Lb-ge$YJHqV`VZ!wsWQ7JNOXSUH8X8U1eFG*)q ze(|I6cg_?2Wk1ojiq|VIm@n#(6ifnFz?c?)$D{Ad^l8mty;9N}DtELQ#2aHMWKIQ+ zJ(S7%o^SQt@}Bm%L-|V!?e8}QdFi9ubD6M*ls{!$u)W*gB7tlAFlK9OZ6KQz8_ z26GQ=R@7De?!=-%J?l|Txf9yDCBYp3EMsHI&b94@!mehLt2UQQ$c8A5(e_WQKAON3 zFgI)yd)Fzn3R4ACqF=S?-ZT2Z5{#$+k$I*~FyO77ScY|N{$O){fPw$QBi~oAa<|8l zpJ`g!(cb)C$mNFB9M^bh5dd23n4I>b(|N{sG%x_FBzAI(N!9foI4BAjUBURr{%=)ld~ivxl2K7Oe< zI0u&!hdt{CM(M(eTiV&o1~148v*jsC18wDT2}kw;N~JnafI$wiOi4W6h)p`XRk^SH zP~mo=<{=FCrPmf&zGaTav#}xQmK2%wLTICAe_}dU8hHi#4fRk zI%D<+(u;C{2SE3D#xXNIv60n`iWqJ-!~cTCcjgRr;olwrCICOWvIT+w(C=~7s+A;o zp#a!*`MrL#CkIyj$55?^)8Mteqi~upkM3MV&+(daeGHe|=PJ%%{p(SmQeGl|dFG?U zejvsIhN-X|@rQE1pMT%stx3qRD;4gQA8Au zfPflDfWGHA7GSG@s>85jXkzP`)-4ceudLioZ?9`%}hxy>J9Dl~? zTvPsLZmWB)JbK%Ub64)!Ef#r_r;MHqhTe);px)aSJsbY+>edw`XG2+j+0)jtH|B2S zAgckn7p>vmSkH@%A^RorFZnwKYt!}=vaQ-#J*sU?&pvP{s4GTlMrOva}ulcMjrBpTwKn93xJ?o)<>EWdb`8Es>ec!`st&|rT8ZM;7| z8^qD5juIjR^^WKRf7}Qnjv7jQCP=G8ax)W|Kd)S1MT8Hd>I%m{0pa6{Tb`=;mBE)DIXAG|iU{We)e zWu|$Np<~%^dHYMA8-CT(dIhr^b4avRQzaS*BNdo>HGn3iw<*(Aljv3$>8aICbogXX zxyRPDGWcR}+0)V>R)N1#gSWD#CSm-|K!4ZD{Q9~1ozsCs3W>L5YfN2TuT~I%9*Xek zt7%bsd5I;UCmS&uOv_6h=X|xI>2*osNf?s%N zkEu?j`R~X6o_*M2{&mp?*Er=l@XU0`8LHnr+n zN<-6u1=F&{PjscrJ4!uXcvYVe$(u?hsIXMic2Rvvc(gM`sMl<?~WtC*Y`o&n@?x0(x&+8MUA0L{L174C4 z$xey;=D%QNYJ-MVBBLUS(zJVH{q}Sji91isQUuE&(RkT-4e)BcHpwTn9SwDONFv(E zjRh9(%oA@jX%-b&ya{5TrmiH0RqA4V`B4-sKu_1y#TY?%1_~i0lyNf5;3^XJLej<~ zgXfjOdq}+h*Y{}c-h>+;5v0<%sSKpj;2}f;|7o6ajBtBW|7V4I*;)QH#ICv-Ms@|7 zg06gO+H*xIyix(x+s!sy1J?J|Rja(BuDVr-cv$5%Eya{^(`PQfA1~70{*c%7hN`yB z^dA5=^)fcQQ$1Tzz$qC6J;tW}CF2SQ zm6@j$DtQZUbxWoy_IVRqr2>*T1|w&mZ6znul;@?Au&j%v|JsiZ`tO=(z1;TQHL$b*LKA#23=r}MR633N$KgW^3kmRcqHt)AL7uH-|vxx1# zo7{-NG(K_Ez>)x_9+NVtPM9kUu@`?l-n*MDN&8u^LSTYG!#VBM2pizE3zZf2w{1+s9pkul0r0`RNWK+$wwIwovjslPaw+0c!&H$kof$&bm1#RGOyUQUa`rsUGNsVOxu1(EtTO08<+E! zlNGP#3oMlj=7~Uz{`ww7W)PN7_#Q+ZpI%&ckdYuoZ_V?k5}fhrOZ)V2ty6B)_Iloo zh4=hQGA^0IGzm`GizFN8o|Q|v%b)XXMFjHoG_2=~8VBe2EBVT+i;6mJ?Jn9l>*)oL z(hd&JRkZR|2w^&(-obPxj?!umPghh@fzHxjohG1jaQN5A|6S+5OvfzrFVpY*v-%&F z?qE6-KaC9zg4MBnzO%IhoigU@{M(JeZWG{WRDm3P9}EwMM+`rKi;oAs^$fn=3nRzH zqhP!ufnY*Mz{x0icr}b1oqS5Kic3CUV_-(98~0p}d`o5Go8CCJenRSHFExvyGxpRJ ze4APf=c)jyQHM~#c<2A~O-`I?)?K92M}2rFXDuvE;*xhIp{2|@BeEEugwq{z zQ8gNbY>EN{+Wg(HO0T!iTV6c=u+C=p!#;(doYlC|qy5D0*kgbCo9#(9N}PK3QGOp- zrk|fSNnbS}7u$zL2b(wRSH;rRUR%eDZVa?~su#9b8uY=XG4Q3YoFd5!v(aHa*p4U% zV`SL5P%euyw}X43C$39Bez(coL{p0!qnE{M@hP)(tgSVmFYx7lG=+Ngz*+hz@MW?f zCRKBeI{_y<%O?r3`a`eb{*)#Vz$BPjIY4T!}iH9NNs@b}qA4=%L@nVJ4%jvc{L7b5DlQqF&``a$~0m z68BAk>r)zmfw?*4)HmydCdyu2^wWRvob5{)+Tf1P%E&O0BiRb!N!{V3&7cZuy2g`7 z5Q_p^L&E#v-R_Wkgb8mL5|VBzex9hD-dMj!@IJU(A!3#4!*}aL<0I&!vdruZ_eDNg zi~41WKvc}ltZnxr==QgrbL!DCOx$G?RojO@-RaL7^0LR=VWUW@YI!6uaM3aJgnhpD z%(;^jnnf+VXY2Cxo>*S>RIfbq(R?qEZiCiaY@UViInAo`Ky^bC=T_Nq`(6jkpIuq8~ z?atsvKaIPqSiW`y89GJuek0Vb!~glaPi&Yr)1|gitM=43K06)vTFjlP!-iSrtj`oh z5B_vbJ}eQEUvPh1V1Rqz(P2E8co(mS>vsuk|DHtU>uws7Tv+#-ty(>3<3eVJEQ>4+E>lit!N0h+7|2Z*4Q>BrbQ41X%V>S<+_VFXy^x)ScY_|yGGFfLswNlO)-P|Mg}AjJ<=oNpqPMKhz96iuM!pxs9T-OBDMqe# zMWXF7X=*qt)k!+u0FE<4sK$PnIcY5Daw%~EDCtnhl$lJA4Wb)VbYda^5AC|A#-_ct>g6@2nkKVr(Lr_}qwoq6w2@IhpA3js$Cr<@vZ_mb3iEdgee&~&Jujvdx1>-JW4PdJ(j90&p8*`FS!I+s;Sw!NT4N)wf)v5fqVLPmy7}MMh>sN4* zzN-NoAQ;b|gE9hNmCn2OL5wnmY_9n`?{d1d+&jLC)feu#1AVghzX)H*39eu=T*wr5 zScWTIc6#n}QR{JmV0++`zhC^vg(npWepK z$KrW_dn#&lfD(8X%3{!pMGqx5lmhfLYjmJ?xCv!s7(x-HA?QzZ^q1nx&)_A>SEZt# z35!bfZBS>t64o12N;G!PY+k3&jhqi$%jp}hmL8Id+J86Zlv^C--Hf`-+OAOl8^2ET9G`x5acSG&MNJcufDb@&jd? z!}-7U@8s1biJ=+({-%FCc$tE>`^gv1I{NR%^jmUEW*M)a@>*|~%#PtWf|RLDhBck4;F!!d-d^dX&y^ z=m=XK^86)C7}4F0vnL-*frU+ZsExwQPGXiHlbX^gnTm!fIN7u$y{uLJrhS^3?apN& z>A<=4RYq%-cMpRqO!XR{E72-O4+z=6=TJWv)0XJWJnn5WZ97I(g!m1jz6h@gqcEAXxQAT|Ez5Y9_r*^D{+%=kM(?rO4|1gR(HaP8uTo{UnFL4j~%^h7(`-dV2fM*RgW%0LEX z?I9nFz=(yF4`Zz37uIAXX!p}z%unJkR|}p=6$CCczBI-?h01!$s%ZVJ@(UF^iQ=q^ z$f=GJ+H``EroyOUA!+imr)bhH_A70S!`)+)%LB9D-u=4}e^&dgN9zy_J^(<1son43 z#`1h%{;CiJ61h@3b2T&8bU;SQ4q!k4$jbmik8^87g0;HtV9}}ENY>W;KJanwAH9JU z1Y_9qohHIHGp`iVmT?_xCX=hj;APdes~8RW2*PeZ9_2-xa596{P=@B3a) zeEnwk8a8%;Qh_`0>6Z`;h|JGo-~%|pAi0bLrQV~diW>O-Zn1 zmmwwg=p%@HeBuNTk9ipx`%V8_&y(EG@ThCHNX}?-u&|8CZQ^sml?warpI?Ukftk}< zUY25AxA-5%$X|;*ihi8s^f+mz78H*pa&3uj=a;X)J>Plz_WrSd{pRn)0}N|Yj2IYT z09FPn)yq+-ifGcadAuKQ6ee%;8Hj}^YiXdBOPs&&bpXdh=v=r&N@ps2^YajMd0j;i zVQ)5qg2$S8rgk~IIQ6}1eqL@qC+&e(*|`i&hlFb~JH$s2)AE&kQeC3Bo7vmfhyPeo z0+rQytXQx;)iGd`)f7-F9@0Pqu*Y4MTcD6EjavdGb#Yu}K+F6}<1J5ji`zo3L(;)k=19 zz?=?TGVT|euu*B-1145GNShv*G)9iW@Q1m0P|k>SV{)4Ng`kVZRMZWzsFIQrtcouG zz8?p<0iqy6e)N7z?js+tX;dq*R-voc_nuA5(@L~A8TS^Fw-(jxEBRqa$}6m!UNDCK zuTC-?;GJ9HX?Fr_9y&NVIl&wD#mzl)C7(}@Fsj+WyMB|q>0B)yEph#xXNJ0E%MqmS zqy8~Ezf+)ewdp_6JV*E*Axlk-H7igYHi7~Sa?fa52i4vBM@O(s1$#Z3QScqi4qn$o z`~CWDvT0N#SISzLdHr8bB?p}9nmiK^oE&U*uz?XU+JuQq;FZrOJ;9Auo{C7&_|$cf z>`SXiAyf9b=f7x@h2fbG*YSK~%sg|z9_DsZnUhbNB_gK-q20dZUdKFA&^{6Jcf<-; zG6DejCu;p(^`AKzBgO3#QGbUX`H~WB^fq@qc0l0%WhCf)#>`NL^a=yLH@jGONWqkR zlwd;u(!G<+v1B}FPq1Nq7PG0NitSb#27FEY2XSo+9+=F{l6pp8arMEL+q7bA`%Ns&SOSQ|0@VNj0sJ!_Iz))Bl+*424t+> zY(})r-q@8|;*sTpZ(gq`|H@M@7TMpv4dj$U%N!2 zFg4My^{;`Nz6R9ve~tQGF0Pl(ypv2AT{)*5)KTxPaJ4BYjI5=LknJuJVt}tIe&lV$ zOmk~k&4u`4uRpaBz)@qWgppyC8cRBN7fZ7wDza-j_u-}ZJcOs$L_bA)WSZwV-}$af zwd!Os7jZN>9@H7<+Ki`OzLiXRU*rmDu!vW!SgvB4b?&wem1Iu)90{uJ?9C2FRAiF5 z!R=eJ>4MV9sh6#pA4%jN8WHgKye%lPUOrcQ;>Fv1Fc6e@EJ6Z zj{_PKql}L0W6ah?W1e+~ji`r6=Y8EepB7p(%7Teyz-hF6!|0T68aDzN9Hxep?UKH6 z!Ckme=gPEw=ed(YBWH~tze9YmHRi~>*12r^C}unG7MY`&>UHIGg}kTFi1)SJtp##> zzSO_GXCa(#ky)CP)IE2Y?bU@OW3Y0H%=Y_OW#`7J9FtVv;U`MuU4=^LxPI+9jS%^* zXZqV%65|&d>fdRf0n7xw%?#XFyR)4cwOY`9 z=@KK&VM+kbt!E0p%yqov=G{MH3`*I?U#pj(UXUh)Sf~ib**CiCRcbcgfeUS7)o5@PO5f3D(9O7c?E{FK(=+wQcq3Cxpu zSuKy3R6VzqZk4qR+cvE`VI2+wc+4i2VCwFC*a^eHlyg>`4klc0sxOCK%iZ`m8;U3W zsao!o{2Ki}&)%mcNmI9XPK`TbdRa@%YjM$Ez1- z0vtps)x&**NH}=2Bd-zN3BQkb#A|@g2YndId&dcDB-OF0f+yMVJz~KQhixEx%EUdy} zzGG&QJz~8so~lV``7+a&C+O)Vo69^GO#<%vGzP~hF`cu{?EO)FzLz<3zt^+4`RUmw zewm>xHi`7Q^ZVIt7u~On(i;3kD=HGP@aFa=%6G1{b%XzgB>c}f(Tf=yNywtHRvSl` z|HNH7A6XocuW;gSl+Cy9e>24vk)=x%Ufg)@TS;an;ZiuklfcDh#A)OEsT343taxT2 zzP<~&2qqrp0y2jWsK`RW!gwpKdIw)-zj_9jbPC&$bqzEb)4=6-Qo%O+3cgWHs$*{@ z6f&O{%CVstpO;o4NrPV>)=#QGl`WhIVpp-0(s_?SB#;jT3Mwd2$%Rpna4==Qt{;

)bPK+#d%J9yY2$eEe7ZkVU8P+HLUW13%)03l%fK#omWcK_Bn^{bA+pAxOiSo#XB zO`HqnbZ5uosHi9;q}*2aPgei4YYHQhkm1=N)Mw{iA<|c1slZXnV@dV~|CK=R^z4(s zz?bkd?H}1)St5sFPfXvH1!g4hl@JCgs&EBrDn=^U2o~@dDjH*7c1%yw(H9Ltx4aF^ zB#%W}#v8dfGV+BI(VKWTSU4#3g#?;IfwhFTbfle>>1n#jop5DEbxRuwIkXuVbyi-5 z6?`S9DONs zj|QS3Xg>{y_fDooVKi!*t8jxViiD$bl}wG$OMF3@W%TrPde@m;2Vfn}=}1mx^#x|c z$W9cTO**vN7Z^*plotGD_i@W)nlQ)S-UcEgqCj9;QvKC)Hl5#=`(-Ua`{w9*rmV}6 z6_>^s0;ev$r$hRt3{7We#`b*J6Eyv}uNh%%_`F&WX*Zr;q$|Z1Fr67f2|opclYE+d zUhDQ$%_>;F6u{)?+kZV5fd!j=A#};?9rowWjMRg&`Xf~v&`*-A9GzMem>AGudvI(& z`bm5UH`9|26>p$iD;vpl%}6DqcQ<`CC}A3uL>-zJFDw2>Lmj3_Tp?l435i3qMBaow zP2bue3Q|`f0VY(UG-{5Yla4el`9o34s9A}J5ku5b`{HFK9=k54Kg|a4@hz&5B0gTM zHV(TV!k$c$PpPHFO~zl8W;bE_**u^ng9g!`tsr*1IEvr_6^_I6W=)TKlKf6g)gsjc z#&5$Yy(~W(b-iy#@FTO8?vQJ?CcFtPajNSf_7%FR5vGBRv^L3s9YIt&M!BCe(_qvdmgkk)q+uYirCas zto4(^cJ$pvIML)ne5^}fklX$Rty)**3y%!vKg4Tl)K11JoPS5tfAfLR$WA5;W24Vs zxx@8(Sjp=~L2}weDheT1MU@-Riyzf|F!;j1LRLjPt@z9%K{`rv{rdAH3NGR@ToyOn#-F=Kq>{V*W}!{B?rvr&jpb z36SBv84BvM_K&qaY-5WNqGv-%{nfS7@)XOT1s_5ANupirFi#X~x)KG!hr55Lo~(aQ zJq=R|36|G_PgQLy%_w}?J)GQ8`PWSGa$wr$S}sKoYY?6pCGG8DLsRWm(sf*aD(+Zm z-u{-jH}AJ2=q}7ft$4Bgr{s}Jm?tkG{W#{FhQispvo{yYMtaGcu7pQNWQIn`y>s!> zFr%uCUwO*?AG48cI>i)9PUF@(+sH)f^z84`!Q*lb@o}LLzH98mQGJjM<#_`!r|>xVrNS>(V$5-5WVMa_D)4wJbY#6tRXmGdEiN6wkYcf249JyG(3)a z(s?YgO~Dx@%plv`Axa9&Kt?MLQ&C`IVSJkcIkk8O@(s-RoQ4{?ERSs__5n`YdO=G+HSQsWndKLT_D0EZyev&CW-T?Jgg1%( zWyZ#*@ffZB@uoe$NcU@VIPNxA*ggqNk|HU2q;MUUgv;{ z-uLr=pYK`k_kC-$nV`Jr+`bkFny$yF+GMNuPeW}EV~H4iJH>~`(W+mx7(wAGloz!M5@tENQ` z2?Y*s4AA;h?_2+YlaoE4bgB}e;%nV0PN21-xf$P*sm)5f$G^S@AynuZp(;ocqwyRi z#U7n(aU$XTCAHsO>h>Ub8kYHunR+g>xuR@O?8GG+mv;U&%C?8z&9mZlYDdAIM1gNy zVGKn=j_i9{^Cb&;*PwhT0Oe^*w|#=w1q8Y7bh>~epIq~ZVM&O|&d$wT4X?itn2rV@;tvngS)9}ro7}V(Yet!R}Bpw`Gj-K6UhN(B0 zAK*f06<&n|{lw_&i_};lv{Jd6iPkLg`a?3llSyW)UNpGMQluM|Ce_?G1;K%#}@MAM3!s^r2P(>#HM$A{P%)Hgf%f!FF=2ff0m)3`m zCF9teOKZN@c%AU$3K$8|cBarUhNLNw6Zakc?c$ZHJYF?XqLj)*y&Cuu{#!Ma1i#|U zAxFi%=ejfWy~f8u7OBftT~&>`P&Te0auL1a>)b6hiic`zx&5TbVZ^3KZ}7NYEMk6U z!t3+Dg{vDQtMG=XftXBp7Xs>Slci`#jp7v+R=wn+;}7Hy+i?Xfxzyu$Vr#9xS3T{s z&uetKQPVRVG*OU~Ck2jTz8qm@dlN`;-u@%Sxr6@tAHk=YRt?3mgtqiqD&G)r2@f7% z(O)ku8kaJt4UX7&(W3oK(Ad-i>Y(B?lBJ*Xlu;Y&C3O~q8|#(#NnJ~cpnuA}GuF=m`zsB1w= z5n9Z3z4+1ZVjbwy%D;l60HC6z||HHVM z*I9v(^Gx^NfRy4T|JTG~0p$={%m*!=fe!J{VyMWW#aA7BhE0AFYTGsnOgIMvoK*ReJ4L$>3|b)(5~{354t)SZYm?FK1&ewNou8jh2x zLErHopeD`q&TQW~WTmOEN6q6t^uiyCb}6w=gBX@2J-kqrmWGLqu{DxwB3R8eX-V&; z$nvyvMA|$yAs##H;w(H4GE+D}ex1CI6U9F8->x>gY;AEIWZHj#+yk_ZxYk+^=)4x_ z9H@qL{KdLTk= zPTjq_n&{UK)bh=S+1vU7QqxaJ zHs;ki{z6Id>>EKT$vlCZZ#Mf=os;nEPYKm?P3BDA+vxCq)IOnIr2m|>{Omgx^r>8Z z46O@qq&MZ~GPbfFGKTjy`d20|^8Skk$CIT=m561)<#%wvQ*O4Z*FMbPgA@weVS zU#Z}+YrHoWH2>TPWjy5MHExaGMIG>c*=O4yZW3gF63srjcz>xMJ}Xu8zRK7dqTiK` zV*R=Cf;Wy=wwKuPPO~?jHeO1G((@DP>zWEH;N{uJ_eR#O>4Vz~L{;rTwIX0pt!`0f z0`@Bc?|7r!ts2Fwsfv8QWVF$KJm3^C^34(*9$hzeZ2bYc?eoHL_M!B`m(%X42amaQ z+*8sQAU*F)O~2!vp5r~_j{E^qxav~LM58d*1XUIOwIc)7Koh1{t_F56C~uhbwNnH; z9_E*}7Y9*hgobCrU56V(+|#2ORwMv%n}r@WVQn8acJXx6EuSmAK~7+}$DVF^yC^gB z<|Z-Uww24Ahj-P3`xn{s#N1-cX7gL`n~xnH`|`ZI#ph*zoC_YHH};>j%TMuo-|tjl znEJJ4=6y>$qtBob`)1v~Me!#8%PRVFlE%?po9`P$MW*jtCT(A~r0+fLP0B84pX_eW z=Q|PE5hFk2soLS>Zx!9+-P><@-VI^-9dG1{?$q?~^bq$<_U5-R!&y@YPoHHw(pM15 zGwevV-*HE-+(WwLKcpx5(c0&Ron!^EFy1IL-FMtifLEYthSkyM`NpT1-j>E@emlN4xo~YfdhnVGdvBPC^1Julk zV{6W9ew$`#H|BS_+qf5BJ#Zj>?Le|^J;WVlCX#xQuA4Yt#BlVaGZls5V55CD{SVBFItsA1C@ZvHv00fQa(fQ0@P9e!zMKr|jxeruh5A7~Ww8DJh_{KeeUZf) z?*{1>GjS-YsY%`R0{VpK&K09L?t5AQ5TWvLA6%)fZ%Y+r8#;{=)cDQH)4*+g^0D^inHyGY@ut!;h3kFl6~H zvI0{omQ9Wqd{SKdX$Mhm_&^OwFxYO0cS0S#8-e!&YOS359;%C@kIZeDlD*k0CWzQg z9i;ScA({s>6|(3j#$!uZI>q)jQg{(D^jR8d7&%qt#k}Ct_}QnPIkRW5V3X*7S@?7G zh#k)G`eTB#tFgKO<@Cn*x_d~#rpF&%cba#?e~OwGM5yi;tVj3HE(1AXlss>!l|v)Un;&gdarccUH*B%d$QAEG-`P#1^2Sh`57Mh zPAK%kIVfNaJv&DF0Wu5e`M2R9qPo<(0t*t^m!MzMcH>Ex5s1ruE>VO#EC~fhtaaGy z33B>qkt0<(cYF=Qv$E)c55LsB;5WTg5HrE4;P>OD5Wdn!GbxELwrN4`35Y#j7#Y83 zF&Gf3M;~NbXmMb4z^y31=cY5{iy^C+Q2Q$7c{CZp4xeK=T`)U#F+b9TMn~Gy8x`Pf z20+nmLcVCPsVFX61c~DfKag{Bzu&*f0T)5GYsxd4#ljI`TE}>WtoOF%uSGl8KJX@^Dd^Q$-L&T_>@>7aa zNwEATd>mZ%kkJnM=-LlZell@qJ(||i8h<_OtKdni4t%T9um-{Ggy2%@akm!5nfA`- zxxBE|g?Asv(e<;?5-e5-H5c>Dx17&x8Uk1TdMTB8r?NlZc|V?G|84ENERviL_4}jf zqvodjvWw^SC$k-OcgI@qut%)-V7@a;s_Fl=XoAYrsbA!YgK7oto?~&mSbV8aY>BjcZa=vSGsO#+xg@JJ<*RO#DZ>N8}U|be%4|iV%EVQf~@R9kfxACy5 z8j%fgk-=oU6}uc?h~c{r2k|~Nk1a*!$@bQ2z}n5;3t{VOWzV6{{YQL zpvv<Z(L>~xWx;5jOG4%q6 zAv~6?zJ=;?A#Hhv_8>AL<>vMZ7FG3U+kCFcSg~W!+L{0uRyp4+FN{q3SF5hU;MVHK zf~zMb3CrL706lySg0G>zc(|=u-jDW3vH&5Y_xRNP@)2W@0xso71{oKm=gT7eW`AGr zOF7}<@hwWA%(yMqdQXbXxyDI%feHB2x56#T6 z4npu;)sT%>{mjnePbb^iqQ0Z|wPWgh@Q5v~QC%%h(G@r4p)4Bx!J^Dd?=^NG?N(qXQayMwoG~C?wl|}RJr&1Je{_sbHBL`w(OrtQ6i?_b<^-Lg z>jH|?qvF#C2cO#KkvCyq@P$nY=j9IV0(TwPFcz0z)wGsTZfM`S(>v|5{^ez8Tp?31_E!5hXPp!Q!2_pEW?WYJSnE_9G`gzi z_NC&->aELG((-Ag6{rtk9lTRh!# z=Zl^mcAe0G6Y}t-fZS=a*T{HjWAL?Wfl4)v_Xq1%^&-C+%Ho5m;4u%IF!T&=EBY6H9<{8c)US(@pI)(=p5Tx)0NaC+pXT=r;^ zm-KtBXfqjJc8t4nXK4wgvIaeLObb-JZiCOPs=V~cObhRcm?iB*VSJW{CJhg}g9aN@ zKXIdiHPw@HT3+c)E(XdJ*^WEZ^$$jEQIJNoV^)V{G792ZDk!E;n+xMf4s?CCtYRXo z#}iU^W*A6H<*$D0&Jdg${C?0tmEV@ZwRR-6E~tI5Uu!(IEx)?u&ES_AyKK>6p(!E# z;q^Xb%)6DUgUXttc~%N0vq2G6>78usnm<7I7ksQv|J`Xw-mjx`jXdQtlunq3Ps%Un zV-*$ln=Tm2evoYmPD0WOWh|d$$dJ4LUc$P*(q@y4NuITQw}mrDK(LP zH&fns^iV=ZSRH*B z(I4W(dyq<7U^NrBMOXBZuKtEzjmh47#2yCMQ}0tQ&1#_v2@xH6ErFQ1rhtywch6m; z66lh&j^<Sdo$ zmA?!d_qy6@)9J?3jBcbz^linku&B%t8?l4^y8uy|n>j5xEXr4Y$#d^hY6M|6~JOj|;RY35z-L80m2ZX@e; zNXYca$Q+rO6!f2G>ZLh%ZIs})&G4UJLKaTN7ZHz#jAHHs2<6oV!>Q@nfLAmBO227B zgIaJ!!z zZ&c5W8HnDIgTfYx%O?w{f$M9JGK~EouqCSp3U*k;^#-!D8%>~3yhejIMI4H+diLq^ z6@g9SC!is8{jdGq&i@|JbpP!H^{f?Vy6&y7m2D0zj7}S_w123b67|V)D|4;S9!$U{ zZ`99C%P7qUkk6}n6e2_ZpB6t1SbRLh;)|5Fxo(8zTAFxp>)>O|2F+Q@ZIT?CH-|Ad zqujcqZLjO?0aGKNDLOAYkjRQzj;*3orUbS4VLSctU(ucohiUlv*T~fcbxF2*l6v^Z zhi7j;sF>;cBt<7?1P?ri_2nyXe2`usYF_sFj{ExLvK3evg3oCFz-M>HEp>Mq;u^X9 zrPh_END&7_bk~|0zBb$_k!#*;#a;fUayY*+<=ZS8YkyWA(6uuwSonF$s{ZuZr&@0S zEXdUBOAdZjG)c{29!=lo{8zk`OSpNE(2Am*z_M!-t+-9nzLwPwX6#kpD`G-mGStoU zWahC??GHGPC9JO$p|wDvPxLu5=lby9!ZJnw8!Yn_3ljAptA+=F&%1Tq=ZOGd<{jN% zV8-+hpQn={SHtiRPjxN4Q}Qj=7~;^oBnz2_Kf{*Ws<9PSewEGtcc?x!umVd*;Qr}^ z6agos#cQkP7IUv`c?aSYX87~)cr~=bbHirsUjEbPiMm$&?KrpZR|4LS{y%#=<^Q&~ zLpci998AicZ#*OW3&#MA{sSSc?AxjrlDOc<jiJmd8wWpy2>tLfhpV*8B6c_EADTPP#YM zEHZ*pjF^xtb26vHO#f9PHz7TN=~ynIv|W18VhVMneA&d@6ZkT-;%S9=Zi29jVyB2U z2`@r&c*x|PdyB=seTVwlanlLuRZOSI@-=EPvD@xSX_^J|`OkqzU=3dYtK_dSa+BR^ zi-(jByJrf)O0%$|!gx1e7}*~IDOMk_h?7Akq$$tPnlm%zHhFFWb$_UJh0<|$h(mE7 z_J!;S$|!i^N7T zl{z~Bs>+AF)Gl+w%bMxSHu7kK@dt-M2AS#RtJ`U+8w0GTFU^c80iQ0*n4Qs1$F5fY ze}K+{AdCCfwu*_F zuy?hT+;dHWrkDBr5D}T8Uin2ovuDkZ8}X}7NQnB1CLWq16Ov^EZ{UX51lONY==FXT z=PZL7#$ffiZc;EaGPT>>9RuQ=r4%~U*)VCC;6sV0df^CGaM&IEA6z6ua5(*nK&Ay& zw>tQPh=a}O&1F!H2NySwVq)bJGz3gF$U(;T+Dh8513`_S6-y+{Vrhwy?Cz+f95Ma? zr6j2?ZwSe7M87+KBi(@n<4%rIOM-N} z1Tvcy0e2>m3c)M|gDOdCO}a|jRxat*Y)d?(;!nkVU zkF%KBN9kG;%_H5aiq&R&YH)8qfL;$iEUlm~&kbaM!?ROk9S$k|7WL;aRV8D%oXkvF z+9tzBb3OeR7;MO7=|01rUQ03-etpPs`?3DvR#T`L7T7{b2~6OKd}S;U??f*SD;ZMz z!RfrYQMx1wrAClzn6IDMX^`5Zins7S_LMKgnV9{a3f5px*5V0?!prqEvEgp5Aag45 zgek&y!R8p=xP=vpGdA+w|Nqe&*xRLqSjVupyvMbSl&XaEDY0n_bN678EeL5W1O(w< z+cw@&kXnX^2`9jx@(Xd)3O0phAVh$FKYI8EQttX(lnVL)=}l{I@#v%{-N=wP{uG)~i~E1J`PKOUjyDpDYH;`x9g+fS8pzFdXdq;xb>b0z zyJjAY+0RjM?k)M^^Hm>5e-_|f6+oX*y7oi{lZ&)G51XS{gF2XR$TFK*Z#&UgNm`Pn z9N{XfA`ASW!?Uxm)664r}*47Q7X9-b?dRWgG- z&#r9V)jGX?o|Iw7L#Ca<=+#M<2z)4p5BT!)ks`#w8_ zXQ^ITTMtDKq1|s!939=6Vcz}xv{qwrp3=Oyy;-zZ#n;s*F6-jsDZR+~=poqaG=Dqc zKRh-fX4g!l%EBC9=qJtY8UKy$_zni#@y_FvozvHjl%-koU*sL7muoYyj5TJ;^&)4f zFei4bsd1C!w)Tyw!yR!m2tN5$x7#$xgcrk=$(~!$cEho^jxw-H4|6 z4WxE&n-JQp^O_>!Xs^T9DW-zEbEGDr8}0W0C_b5jRycezfL66vIzii9)L*vxN3~<| zGI18tmQ|y-{7Ia5e#oldhqsX=IqgnHN9bHapqF8jLcxq-eUQu!f;%;bom?i%r z$v;gav_^Bu4PU%tq*`z-;W}`1YN#R~QjmUE)OiWlUzSEgut!DyieK2;T z3Z_huCp2x;)EAF~T0VGBj(zm_bMP8wT3pFKcd?``#!aSMLu55KOqWR8owlPp2t)+! z{7})qsSjqA+{Bm7HMW0^+JjS~OU4;cjXc3hCJX(<7fIj;)Xj2eGet>u%tR7|)2deU znIV{lPOxI6bn3#}5+V*5SHSY3!CC(GOpl1KhDgh@%0&DcIS+nm!GDx8E0@;#*=T6xP5BePZ{B(^rVXrnmbt8pUI~))mO8#uC^w}&lu`_ zEA#fr)|EqJ01vamW2@$A=(~03-g`cn;C$Bst8b^IU|M~8Y0qluScj>6JEWQU(@U&) zr#N{d?B@m%`Uy{-lgdk}l02?C-{jg4>Z2mo=SHI(L%dk*<`l3~Pl0!;d{3S(11xNO z2|~*d9eDcK2+MlwRpxg|cN`<>Ztay)cOMX9^w0+p_ z>l=p!ZohNI!`(1A!7e2pP_cTTi%f6_`;_BRQo|lF`O?jbrRP=-&P$d8&RqN2#@rd`GA9Jwv09`RNY!C*)gqf&Guj|Vjd-@k-LaYYXUMI1G~il<0Wr(*A24rhs~%biaegzLl}pNkx}8 zCX)5X!XZZf zFwy670F5$+YSi+?QW&P5SJ;4XY3%c@VHiYYX1PDwhcnYU^PS$@X}kykp`twb@-<9` z8gKR!aLa+{TV@UE5RoVbIFO(J$ztvw0ywh9Yw~;P)G(CU-U4}P6T+Gpe(?P=lHI%V zDve*+d}{1W3)9DqRLYSXSNnB;fHZoKo&(VrL0451m$$RyzeGG%tbh9LcD9#nSc~C^RxpAD z9vWqyO zeFtO75j~i$&v~b!bl+vmKe_%(C;xpJ2Ft>l&XqtEzP`TxIRevEIMQ4W0T_&!Q-u&G z@#(x8HRBp^jbIx~g!>o1C4X)VD%LLn68d~-q&WrF26y&L%jp9 z$cm~lNNF!uR1x%IEx@24CACIGMug^r;{qGZ}O{3}X!A!wGOf24A3s3zk|?=7Ls z5I9J`je*vO(&J`Hz}(44s_Jml^Fxb3V~zT&lY}VXmWtJ(#b3&VzW6Id0gkMrKcUM) z()K+Vo*M)SEar6J?3qDl^xMUc#g>tm#M9RZU@D~HYP9L8_OAE3Akyi zE2=Vusn{ysCgjoE0WKE#OTyK@itgttd{7gP#Z$V;fXqOLTku)!6XMyIkb+FoG}hz8 zd8^&SJB;;T-simwAtz~+2$g`np=ZY65=v3_W_F^7y#LZUI_&kRWUhK=$VA=U>M0#Q zlX1^?xT~kJ`pL6+XrE1a+jdd=ma^|kkCntp>dxZ7nboa_T<`8{xuWsC^r zCB%ZmZR~f28p2 zk^Quk080Xc>MD84klz7OdzR?7(+oaF_)P*rH2iYB^s$mg2e=2w>bjq0Q}AFaSeRd= zh!Ti;Ha25$m0FCc(y{te;SX(@dp*^<_pK!&AV%GKO<8$l95#)S7i8iy(&Hatu3yqjY=o8`U^7U;&y3p=XN+^4?%bY zLbz7g;HKu?M*l{5xnWifKbokx(EU4#TOhQimoZI12+b7@mdOmAb$&8luG2V!y9%$G zlFr4Tq)inAW?SW6r_O4t27ZSkEgtudjEo|xHY#QVsr4(6Pxf$T`!5JW@)Lr1KpYqP zP4wR3w;z>EX`IP(&p991#1Fo?KT;4jESb|(0%N8nq2RT6`|dm;I0MrSn?rEaM9H8U z>3%)d2S0GZ3r}{f-YD(+!FPAhdGu5h#p+T!5h*mv#Kb;%Ro029-e|C*FzDXP~{@^I{Wlj>~C>j>{V^&acJxW`nL^*A^a{kzGrSDrymI3Av* zTyI%7E{e?i5TDdfb8t(&a`H1__OdV4Pqb_ge}LWr?BkYVD%0cNNo)Tm`~$?4W3Q%L zQmtF4o3W56)8a|yc;!7VQ};(ptn9P*W7$+?P{Qiy%qLAz;PXC9iB=CAP5+~zXXFkP zb&9F0Bsjhm%jyI&q2;JS=F)wM_h$Cs7_4D%@l8xKGE*fEjV{`|G2$-7bMTyCD-#!v z58bbXyv;j@oLhCK0&RvbTU+% zc_bQBl#2i>`ON<$`@$xA5?U)mC4g=B43%)YO}fDrWLw zsrT}URYLj7!n!Rxo*~rK1Xn zA+6<(hQUcwCDawUn+`q;e;26N##=vJ9=fi?ol(%*rQRm|y>eT#`l9gmqHnnJwSW}} z&EwDCU#Ertk|f{{-)jdlGk`QW;I~#kj)*8l`HUKdS5A4oRQ)Y%kb2m52?xT0vTxJZ z2-BTq4dZiz!`)~c>0<1W92ovvD?{GZQ}9aifhBa>BYCDJ^sr+SFNuGEUb0~c?nLU@ zxP6lC0TWbxVT#81I)u$c6VyE#_iIIo@9eNs!xb=NotY?2J_3jKuOpK5Y_Suo6U4G7AXlRG&=Hy|a40-!j*F4IDAzB=p{LIG+=Bc3?IO;z@-K;#R!gX`5x@C@n6VSZ) zGHQv1K&)9-Dn1mIcJSUw;J33pfBe7HSthO+E>jvE(^XePhZSb-zEpJN-4c2831w9q zh3h^t0EgK%`WZvcwZJNtDf~Nu<%R<2h1>rwYlt$~b0dftIstm%zcqEw)taKx?MQYy z(dvtqaDoe^D-4*iHm5UqVgr=I!~@lZl}9o)?tY2{9N4Jz_XD4XDFs6Gp0TjfhO@Mm z9uv-26BNSa#rlJM#VNHDd;4A&jZEg$6kVYEP!u)z8sc!X#= z50-}6F9zpuyWtMTO8eMTI`H$nSkQ?d3U;EaXHFXo2*@ySMxwK-bs#Nb$)^)p!*^=H zfb#U+F+~}9eRV`y&K_OMTuCnNilo(YFC93i|8IW)Wwxd$dJWNKz|0^Pw|C!<+N^mPm!;QXA zfducD2M1kv-769{`)xVbO#7-3I&#(TkzY^G5$1&FDcZui-1UYv&hcl)ZRmWBY?|=7 zYu%&^RERe-V*ILi2q+l(SukeYwdDe(HwB=s_U+T97l2Jrv62YHVibV9 zk%zpgLqPOnN^HR7Kw!!mibteScCti8hALzvps{rYoloL32!{+X`B>T1Fx@TBvFf@R z{2>8tWxVkp5)}W&$9p9E@3TkJ`g8J>q6|kN-V#xKSpuDzkQg|b0*JTL&EpO)fueu_ z#_(rgGD$&sfJh)JC8P+b)GLhK2)R&%+aq(o$>NwLeje(_&m#tKqXck+{)8CdCLHQ! zQF0(|_WSh=m;<^3b)~#6s+zAmJEp{%uOxdZ#A~|ezh!R+EWkrt=in0i+3Av7E1>#J zQ6&V@i(Q;s3K?*NeV{@YGF9KE8xEvM<%Wi%Igjcm=aGRMtXDA~`|SX2zqjPzA;6=^ zT^Fz8PdPiH1QY==Az_=`7!N@jP*y%bf9RPrg9m0jgGR)l_5od=-D8HV>=0kVDqQmQ z3#J8)hc#aVe$G3@B7pCkT+zC6r5Kk}0rf->uBIVEz7MDa=>&U-4$K)$-NrSd;o;N~ zxw;5Akn7nqa6&DaCY8iN+cQXnKyw{ey?{U&hwk79*Uj$uH&lq+(gYwiRnHjQ}d)q zr8Up~oSI8_dZk)WZlEQ zkisu^C*sDWpyk23PUfYIFUdR&O(5i=?YonqhJ=Om+-%zy*sT~qp2QcWcN+*TLWZz*iR<0ecbOsZ+X)tSSrxah1X|F&Zd$V&2+9pk~NYmhkIvR5O_Js_q^2q`Ek z3ok{o^z!?2imrLiziBEr^trTIK>eXz>DW?o2B3g#!L}u({xh{$FGS(vnL5nXIL#Upia0n{dsAK$SJ;5kwqNrXdgv;w6BB96wvI85@0N_>R;% zhXk{dypk3HhcKTwg)r~q%Mfr6vMg~a^2-qLm%0BjyMXaunhk`d^lSJ-0yL5E+Cbqh z!R(U(;{f%vxU@1BV|V+&H$_)I{4)5fKoEO}7-WSIz_1XY_^&bek{77DL+3IFcJNf} z_?pB(d&fDCE1}^P?!O2Q5Wm{8b}hbN#DpMj<6Ka~L}OmhdX) zPeLF%HVtWc1y~k8+x+48q58sg>Gxr1CSXzZVcxovlm2u&OP6j3!Y|a0epB@WvZgD_Fu!rTm4S*8ZF!2=2RdROh3Bmw9oxyLWwU z-28HFoS07EPeMUl5Oapd=jjg+<#8Z+$<6P653u@v#iH|ZzMoF&8W9p_hwX~)7h85T zrFbkdOmHO#xSb7*)Ds<6ytq4Ddq%$~9wV!{8wv5T#iyO+9s4F~aG zZ3{lQfX2>i+1wx$AwAwpzcPqx0Oc|VS>fympks^mW5c|utXP?@r3E|qEK>3P$cyiN zxub#_d`P(O#oh{rrbeq9nJ@`E5ihCX63KNC!RxE@IVdT)bVf=KIL95JNNh&f(a!j) zr?;JHCq$)>1DR1yF>WaSEDg2e9;8t+&1u8=uZiUzn*EK|nsXD#UMpm^cC@QE zaa{HA_IMfjg}a2u@p7yjfz5=5cJQV!jhVgok=IflRVsyShqyY0?fhe49e|mPE(utI zlz+#_Hw#@24eTZV%7ljMN2sNV-`I9A`8iOLMH5dWW(L-nId$Ug5k*=1Cl4a{es*DV z&0Du@sDmDM9a)?Vkp{SenQFfz?G|c3bEswgsGKD}Mf0kUf8~N^MzX)i8Myo8J1>h4 zt<7W>EIP&89Q!y;T;p2h8o`DM=+caQRk{C|WFq~saKbtW`{fz1DaB-qGRlD!ohhtDH?4_zBfHh zno*bvcJjKlM+$jOdvZ(9y_UME{PFcwdv8d=e1eN^AS}kHewTm$?L%HgyE{JQHu(qP zg6M6U_4%dR=L}~#ab?Q0tTOjeHMCjv^|Pm55+>oHma*m#qj=FQXz#u9AVR~@u$kcm zeGPjtJ755|gF|%t{pF z68r!yjm4eNomi1lJ~vq0*J>(EVt|YZPU|yTQ}=B;@5>90UA7f`%cdIczY^8N0~1)x@0bp^XSKh;R~eor8YP z30p=a63$;+9WL=k^U%O!X9Tk~g6}#v3v{H|xZ(4dC2F-y$(9QlZ0g z5P{}ZYGIeiN)N>Y5suGyuQ`b;pDDoO&l1^Xs}RsPizKjYpUu#lCZJ?wT#`~;khw(g zkfD6hRe{Cki?k(}&p1~cHs?_pX%#yA;FmBhB~j$aUfBVBCXgoPHw+y_qV@06d2l@R z>8Y~Q!8xp7a6ouk?xamkHxN|se6MBG!iV)EXC5W;27sfZO@tk<1hxL#uW$)Sds^o; ztfj@BZ0c%t2&fs1 zlVF-xeVOxSgPf(2LrYO(T=@$Of1GV&+bcLu{frz-=Bl?hK0f04>cem-sc|PmKp+2M zx9szAe$7`JAx6p%_rQIjbhJEC)dS&$2zZ2!AbOoAQ|1uy$o&+LM~Q6{M>~I)00&;K z;7X#tI5Lc#+!BLV)eK@vL$0cvcC83#4dzVC*Z%H=m)9e^I;2NDu3z2v9HmS0LUUSk zZiwRsh6NSvXZ`?jgjp!vMnUhqF5;ao>@~a)C5zYwCIXgF%xzd+v9<2tpjy;ao=7zL z5_Ah6nrx!B*=@FHg6mT0EV@;qiAzxNR9sNpm*CzUIeApwel5eJ6v0HyAD{xg(k_%( z2^ic7W`7r}FHeu1hHq4MBUTT%^a7UrduB950^#XmFpw%bcn7r9sBkN zbY@TyhMrLlztB?Wll{}^KNjAHjW1Gmf^6mK--(kDOgagf#2ZbS@9{Dr(>W{hT5_a* zeg$Kfr>&|c7nJ-6T|ev0iSQ-81p@qAd5@f)(%m;Wb|2to=(w5a%@aDpJ~2CdO&2kc z%ZZbx0q&nm>#3?0jd>#UfmpFfo*uqr--$Emvuks}>sRh2i3#``)btC1gvIUTGS9-K z-^OV>uL`+)ud8B28SFaXrwWqS95c?KRI=f8yXM)d;wY}B*bbO*&xgQHNk7lYOGRP( zN?b4UefgFz5fZ9go{=OHeCM!_ZoBa6C_f#9_D;oolRdhBzX*62>?=+mj%Z zUC%;6d1LLjY6K}A3{0OuytF%*%z`=0HxuP>s9aJcg4o;b6~L&>F>$*R5gLopG*jKR z=JG6S9fu0YX?a>t6BZK~5P$MBbj8o@`ZNVPhUXwnig<1Tk9J(dS~*K>mWJk?Z3SrSQXOrgph~Vw({lO#-D-D;2Yb&bC1tmQ~ zSL24V@M2q?XP{Y|Sur%Lmhf;tZDSHQ&NM)pu5QFM3^J?j6gT}q^Y-7Hga+{=-ij}! zxa^j&KlbR&4@6@zpU6x`kj#A!1AbY#gO+a^yJPbu1k;JE{0M*NTp{~v3KYK||34kXxj zO8Oy)f79eOAikIW0XGzVBv?G|Rls!6!-DO4D@UrIlmZEKlK!3>yn z_ylti}$`|jT*tWW)69dSagL5 zaq=4XYk9YkMkWH%cqDfiUX^5R0Y1k==UxV_hmyT9c}^Rgu*t1HBMGA$n1sO;PS}+^ zgOdUiAClv=`d(9qwOsj6{Wfnc>fecYvM0fs|4Tf&hAG0M`NjE-5dqk0@V?wuDdeazBEtVHF`1*)f^aA;R(nrH6q8P@#1dAl-(_pL@*mN4HP*u zpyV+xR5oV*B)1t@ISji}jdZ?9&SHig;0i2_`)Oj@?S|n^=hUpDmXOPAc{GW;KhCCL zLxB(GDV|`N@Wal%FSleS(ly&9qywDbUz??#9y_=7PpMQ^>jOdtMHvq?WH>48r;gJ# zf4rN38YK-#ku>|%_<-*nWNt14-Q<&eTuHqexFPJW7!nTKzHcxurVw+_axy63P3P{W zEZfVZBLkDyND^*W3&e%1PMuR%t~!+uYym~7oYye2ahvM(7TkJ!m!l3XNF09BFr*|7 zcQBN~@d>_PZ=kkL3C2(pFV-5aCZsF`-%kUv;+ZO0rlT!oA2wGjRUB$XVyXDLA~IHB zbE2=kg7Yu3FGon${;e@1Uu`~~C~fbB%|Q;8TpskgTqmEFEr>HThlw@R!7C!+-EmjJ zQ}+wEqI;!nrg>!IS3i1_iK$3Zp$(BK72=kc0iUh-+h=2o#L1>g2-6k8tLQ0sJygo7 zqEnTpi^WH!S2CT_qm+}%=%7VOP?wirxt*eD`+oD%#r)N!8W&v*Bb!vQ)sO{fdUEJ+ zPd+}jpE6D)CTr}&fNP%RpI*A+%1h^2L0tT@Vv!7ibcwSXBnCWNMU>} zIl>gY47K=0OX3wn3s4lWH)jquHL(hHgy-;!nORY1P-*Ij;d^cvBNq7NCgW#}U?=t! zAjiuG5lfyPcK-|_m*j<+zm3!C_wS_7n8dB{E;`HQz>iY}KV`%tEfzAAFf(bIzAdECda9aJ<>)TKE0XtW9~)(K>8 z;=j{Gr$K0|DofAS1SA>(4+y^5&_}7Mx(l}`nl$cmTm)( zQN+a*bUOt^5JnS5XCScg^P;|vl0GItvE%MsLP5v2qWqu-RdTf$h*7tpU-M3&a z@#jo=g#U;Zyl(`}+8^O05_yj}%ea0mQ?eA&VgI(m<-vwuoa3)Q2NKT->WZb?XLu>wggJjC!TN&+Og z<>vlF6n%6>d_boVUY4L%0mzT=c{Hq5^G?iP=~VSiXl?N9@ zS8$zI_~Vzu;AICq^^Gtqlk_r)wE8oj6(vt^Z%ty?0y_&G$c=gaDz3UZeyFy;l)M zLa3og@4ZS>5Csts5}GvWO+=*kUIj%$m0|%DX%;|55D-K_r9Hn*;CY_U{e8dpe((M3 z-uq%nW@l!1=gc|hecm(Ko&5ns9YI1MlQ0ZdN_2#AjPUChW~G~w*t<(?d{KB+6VJqe zLF(f)s%mxD_*Y@qbD_?%2^e7k?F@>$wx?hSj8T~4{u!ez5h9nsY2=zSzux`C#O<@m zv(G7RLamRq=_li>P@4Sd&NVX8ul^71Ibp_$QJV6`QV5brr>{?I9Up9a-cN@zoD;NZ zVY~Fp8JD;2^bcZkM@>$F0LT)#WMakK%%tfdO_89Cr_ni3mjp%_yb=P&P#w-?xHHw} z{E5s@aS4K7>o1&1KuS((nl!jC@G`HJucJ!T0-q% zIGmyD>ACbO?h?ltm;d)@gQOfobj1UYaha3AZ~>jGox9f%KFGV$kwF-J8uQGKeS?C^ zdznzvbAlW#92JkLN$aEWe_vDXv=_y7oI2MtxsZ;!xRFJKg*E9L%y~Z1LLdm7j4^;4 z(+Fb>TEZAZ(C0_|^_fzysvET?_vlsJh`Op4Kn_CVFqaDl2_ewq-$BO6$spt0W)17t zuskL4<_ZJM>gnBfvLD8~6h*k-K7=`arSIywX80cy->4IVB8Pl5jdGANZEVw*fS%E+ zA&<7ksYUq0+kQT$Wwjr;r;FwqGkT-cOXYY0zZf`9oMnosCnxhXyXx;-n1ApRsATf~ z<-c(Qm~7Igiy2QQmF{V$Q<=Wr)gEG$LbkZC+SX&|lI9VXtFhQD1h6 zQ&u#lX2?niv;SA}57(#vot(EM&iO`yn4Y{NJDpwv-YI7SBJHCDfxZNB8QDe_M}Zr{ z`QAZX7MzEu{LPG<6#b1rs*iGeDZNRYC3q(Y1ojy54tXW zIyXfs!24oJhV9(MHCk6s@8V3elQaH zo&ldj5Nv;Cwv)qL{*SuW|Uj%8dazT8WRdrSv?bG(PQF-er zlgiL(xA3%a!N>eN$vA~sp3)l>^}|Q}CzZ*FmE{PP)rgg;iIqJFm7|E2+5i7t`J^}g zdjbEmHw^xtblv|{IVN2^dQ22}V~P8EJUSOz4o!fNWK95%+KRzJ5HN(4goKp17&HiY z)RvwXB%=mAWSfTD`T_5qKuDvCnih@zewY@l$}5NI_7StT8GUQf+?G{a=l=h~PR$w1 zP2*(`yL-sBGLX&lD4d`l8h*w`$E!H@&GXJk3Q!Fvz4evGn=ZOnyxL^!be*Q6aC|r% zPJ~k$6hG^lg&dK?X?Euh_SL|J$R3fVzt(wdEIvV9U&z6yyNy0B$DT108|8>M0yM_y4^zjc?EXvnC~gy-_qs!>R%H_;202n+Nz z*-bofL3pA0Ilq}ReY1cqt!QCwukBEJ35<=(@x8;O3mdHuJ+jd8dQVHEy0#)|=`>w- zg>-$hGJ46NHC`0gDd9VRKGc;A1LJ)As9-<&V*AXZ;|0}EXO-G0t7*1CWSMhlmKf5wbK1;D_uX}4 z^)&?frRg*4-*KrwSLh!r5h}wu2Ogm@~5t0ovAJ2znd%~@At*>eeN*YVB{|t zbdWHFRz#WhS1r8x;T^1Sfun%NkPOJ^1v0k0g9T19iZVPuk^13P#1=ojbM@ZxR{c@A z5^RLQk&!2W(ebX(R_^{mE9D9(hTZPE1C=3N#iL6CX~7NG^7Z(Bs1IFoJwq8mc3nJi zFoNuo0+3HvDvA7H19}I}Z$(@My|kfMgtAvQ;-eJcKw=C{2BcWnQZGxu&TvqFDI#%I z%P)?mxl0x>j%zA>X^a*eiaO_sni;<=K_LT$+R+y&X2N7Vg9B%qCl}dN z6Q~WmMa{oD_N@1PRA~kj?sE5j0duOXaz@UbqJl{FbmlD+tLQ=+4K}zx0&#zGHYq`; zij7Q@0aBW&&qXSTcYkAsRyixb|ku3gt+j=b63A@A>h z=~w%5GA+f2y>}D6z2`s7=P4-(h0b5sCWmf@UiDm9c(8fpjkDF+(D^`4&NJN`UBtTY zwaGz1aY-K+P)a<i;O4Q`zAR8m6A;b>-R!GDI${9x#orV?VFy|I6 zo&={l+oczsewC6-O}xp4)_5?&W7b&7>oNuM9ibQUF+$l;ImFzIADy{|w&1iN2>lsk zRQWmxOEx(0ZYV*{v!4V)#gq$jRHxdR!L>D0uj-`hSPg%?5%;z^@O+^{+HlSOQ~~y_ zwnF%J(>TZQa{ZbGH(@v8WVd0>Vui--y5(+-3b-~|)3Ln{sPq|FNNw~L%7;I%=g5|L zj^n8=5^K(>Bg*$cEpPXvNlkKJ?l)oFS@W`!E?kWvu-_^d6K`h$FXNR?xkk{f+k0C} z7Kk9IJM&pmJelTozD$;<{;ES)uX!_V>qICAU*XeJYlVjy>7C&VV(n{sPK9s_6q2Gd zTic0aLc^mX9gW7$Vp|@@PYO6D@>_*vQHKph(Y|A&6SAMNxeA)4q6MYpjib5))?@BK zs5IpuEcdaB=zi>W;QN^Jg>1Cq!iB6LXRk*jZsJP7F}Wwl;6ASpom4EuPuQIvgKG|Q zr82;u;dRE}Ip^Z4E0H5Zbs0mp$D1=*2Sx4i-(Ajrv{U@rib6hH;lAJFx??DEL)_ml|W~ zWCDtsNlc7}tCx2)fF)9Q9KD>O_ytn7XW{3?b6zLi{M0;h8aS<>j_CdAcN!!uECWFIyDzAH!DD)v?(9%%^HRpUQ1*@Fk$x$6tGF_2P1RR9AnX6O zj>f6WOHo<$Cx>$^81&sVL7IkZx{7uBZ&(_q-w zI+wn(D7Gt^mWvSN zej*xi4V8;EX{V-QsPs{?JA*(;oku4)N#$FJU-9*?A56@*mq=z#gyU?i1@#=C2+RK= z&z)u98Bf#B0TxFlWqnYj$w~hm<~_2zsEuY<^0A0*w3ol7WOx7ML=k$b#X?6slZaj; z@yIr7KjUmmy@`-{;`f{VdYO50LeJeyYHd`TO;qC{uQ+tq+ki3%&sdHItBCzd>I;an zvizC9WX+nem6D($d-XFuie!-F>n!db0mZ?F(BGA$%V(VATpK>j(~L3nb=SBABv?)V z;3OWRtN4V8!AT9b6!U9+A)a3>VN3TkTCfz(JYc(D641a>a;$J6q=3heydvdw9}o(+ z_U)nsx!X&xpiP|6_HY$B7u!^P6E>Mcmp zfPRTi>sn#mMqOBv*6;&dzVO93UmE;96M6coGRurzjd;&!SrJiZmiuMM z4R8tuBwnlZ3%A9Bp6Yxi69om`B1?iRsU;vAD$9 zx(TG-6^GB#NyiJ&FYxrG?6r>5RwHi+9jszI7Q?@^FJ6Io=RIb|gmrPB>9BPCa~ zPx+$}!G)qX45QA^Hv~lo5b0DD=5mM|<>VUfB(d_JixwK+ab@R;!Jk7=^5v88rwM(F zv>K17ixZ&=x8$`1a-mo+gHm2EH@sa*!pdXc%F!>9vVbsw&<;N}9$R1k(j_m@B~&7s z#Zz3syrAFULt5hp{x;WK7S!;!OAg#gQHx+{xyDOs2)ehGll`a2{7!ZGOLRph z2P|lp=Rt67VH?#qN6FWo{G7&sQgNng98 zeBaFskvif@s1FxK+d$9Hi;{RO4@pZkhHn&~N9>g>?9?x2X8{OE=t`^o2HSA9!$2*d z1VG1zt6lH1vM&}E=i=C)t*Y8lD^OV(xqkHX<07!BA{;w57h;h0k$UobUx|czD!4I&b6)O?+Jc0#1#Qv@ zH?DL$QRh*IcBj{lB0lXOfXHZSk-zvq=DAs`shk$ zuVNsqHhbd4)UA!ZCrc#kHiybJACdK`2dKwJSl6O$vME?@04-VB>{0ZTRwJ}p4Rv{3syERuD z_RA73yHmFL#8ZV&RRXDYMpBih4kep()nfp30(fb%n&Eo4n#5RH6x@qo*BF@zs7Bkj($IlP1$Uts(m@iveTr$USv`AAXAjk@G6&k z)@Xt>=6=rD0-rBvFr#tc9UD^iRR(G<6aV3H9n@~Ea>JkoNjsg(MLJ!RZYNt0|9ZW^ zJL1z7%~qE%0x1>ZF7w$wbZWan3)GWuFN8#R2WJDA_Oxa9p=JW@b6Y@_mQG{3*b{29 zf9s5}{i*{C%lA&sqN^(&m`sWH0iEc2bz$4CBzu@`*Js&e`(QTPX4Dm%xfTD4pQ%hNXg-AiX5Y8hvM&yUEy^kv&Z& zzWqk6O^ubn>&wbSo@v+W#waQC3vbu zQ7!($3G(&??fi!>fg~{m7uQ+)7F}J-YL0olM@ahF)f;uxYYW>>^Jhx>q>Zxd4!cv` zbby>+b%3yZe*%^C!D~y+J%L8};L#0Wo;ebxm!szj>7!(A?ux6637ZqVtj^b;asG?# zHEh90K>rp-9W@zxO2U7E5)7T-Qm`?PN)$FEo6B5EgZ)i0%x?S>W8WT^NyukJW*d4BOpHNEl1gn)`SZ4qNNguq`0mMxK_?9!wrcI`?vKVP4 zqI>Nq|Kdc6Rfr(($sp@ro7H6={X!M7-DwM0z?XoS$MsqS$z}nY<T;Kxl^$ojZqs|5r&aN$bo7fXxFBNXgG&7G~TGe>Q(kZ(*#va3rg|(!dr;J(2bTPRCN}=oPuH95QPs9Z z=c{XJqs(EDn3IMQRHgcKiq4s5?z3#lhBiKV-x8a=f8vUs*^Vq5Fv%0rCOM+Fc+D0yKTW`MLvyRRQ?22!(gnD9FZGc)K$Z@WjvJ08;R# zqY-cDm*ve4=-@tpCAT@CZ#2#YFldZK#3R0KD!zQoQ!&PHD6Jas#3w^)vh078cS5ytx_u+uO< zk)6$UUd9v96VY(0BmqGPAHW_nU^_%~+0^|k3mBHVu-$=~gdN~hsiWtLj;SSpZ{}c_ z`I)35OKd{ROhU&D0ZY1*A)#RaFal!S6B9NA<&Oz36ZKJ*m3iH^xj6>?dSO&qCvFty z-3CbPEmTZNuqRs$a~9PaGjTYen>vAFQ9>RmjN7Df4TDN325_iKRA`)$0@T*%^w6ek zkC}}KM6bt>UCg*ri32zQC6i9bpGEEsijALjm{X_!3nT`(I{`6)cPks!;QN+4sf+xM zEpahDe+Zfb%JI+ko=_7!f4O##uXYbm(PJaP$D8Ju_Wqory@jzJ)95HO_Zf|=ZPo68 zZuD!5vw4AdRToPc8dhnSJ2)fz`2krfq5Xo9#P);G`#Pyk-!fvg($knVQ@%|}biGEh zCD$ybm1+(`Xpih6SkR|DuJCUqx>;-+gTM&8;;Sg{Y$734Y_NM7Cllj+mQM%8#&(gQ zPB&GD{95KH%jR60z~5E|q?m0s`T)!K#t=N6pd>)B$9t8(r8_G_;aR~jAmb+iu-cml z_5}omak=VP2Db$O6W*W3f^)@okM1sfV-a>cJCtUsVW>A0#Ghkp90?rN*4PUi<(xdW zdwwW3)82Fr>qgS|mdjkAPnaGszf~=UA6jx9Y6V!GvtCkA zFM;sw@(k;{HWS0(#yu~!sAf9CK&iMu_%VM=jO`UbgvF`)H-Pir`7PZM2zbp=^~-4j z3;{5mx7An~fujV#`F0H+y{X$V7frCVdce|Z@S5m7{sWs>0FCQR#e@m?#eAxtA~Oe5 zH6=cewX+hUZ~Fm)WbWlmkcov{B*{CFy6ZJ~2;0o4jw@2|1M=TMLEv57l!+Z5j$nyU z-oM$0l?teJAfUgfu(#@o${k0%UQ`ERm|oDnPdD>yvlN&$AjoH5A|VP)ffc4iy&eyF zSPvbF;~Q95YZL+VJc~>1fA3Y##A~}Cl=sKH;5i^%_^|ah<^x4+jcpM0ct-3YuwM7H{&OYkmc8}>g9C8a0!z>B`4#= zi}7eH8om(@+j{01rrKPXG)ZH_Pk*4}i2ZKKpDEhXiZ@i)t~qZ9csyWkFM?s4W!CHH z8kM-?X(AR_0g!A1Fr;c300?SqshRf5x&c2iHFKaF-{d4}qq4%c8~1K;7upJ(Cmdj;ML-KRrS8fl&jA#KC^$r>@P&#DqUjgog`__BhwYt(58dg2;&G;g$rVlV+ zAQ(SxAmRDw-7=H1LEvdm-w;fryBx6S>Q^qRZ64*uN0qa0VDx<@P_re z_Ah`YNqT?iCwON^uE>V=2pGqphe_&M)bLP3IS9tJM#vZ?zawy0_aJ)ciG=h?MYaR4IaKqSs5uF32tT(!sdUOe zX0x+_$pS*Wt1);402`nf06PQPfv^We?YuWR!hq4l*rb1?)GopoNId@Pj zKQ(u5XPEzDS4X%K3(x^Ig2Y&8S^{t_7DAL!27tKp0uVI;sz`_|Z@QzU^i<-20SH93 z?Q#>(N5A%q_h0Wnoavw7Kf%O@`Axv6ig+<<;9|s4l`h@zeJ7NN9E*xUHPPIv`w2$3 zb`wpEzs6$?(L`cSKtsD&qs~17N$=W{?%X>(`#WgnBrcXz{2`x`{uc=FCrT{x4u8bR zp<%tSav_3}Pn|BX{E>Mq(UoGJ5`u#Xfv%X?mE-|tMPR)SWCR{iaXtJ-_xc=XN5 zU^zUn{B#DXL_*?p{7_hS){f#XTr<9R-11Nu{+s~e%+76ueslF9#8U#>-mANmB3&m1hrR&d>SD)U%KJuuG!mvj0MfW{;)yD1RR;@<9EijVTtTGBsewjJ zS9lwsj*V@=?Fc=~@)syo3AK3_r;4&dlgXO=~%U#n({0(@c@*+l)|u} zhO}GUt&spMI_Hfl4^Q2Z1?q$!ZSvr4PVs$4c5>i^xZYmerF)@+IH~dD)my?T3Dg#- z(Mx`!GoBl_&mL^9L|mrRh@>TY^6lwguZm-U;?tUgCp8WCMdE^%fHC3gZ>pD61NVE( z696q$0&v~vUgLB!YTaSFV}oyhzPP$^wLiUimmEF<7KhmIlsv1hc5TG>DLs3mLXyCa zx_-`TU*N;A;`2qj_S%7;Rz|A*!ep1lIF6@f>R3|@1uC<>3mUexRz9t?MIh$o?#zX! ztRwwT*@g166@|Mzz3r!>yptVg8?(A!^?lVr%8;zcl?I9ATvc4z;UUS1$%R9rV=3sJ z25T28Vf^Z+PNF&QN($tmlR@s>TC=8qTw4) ztM|;@%RNoDYAyFdg^?!6_(1yf2O^lXum^#_XqB`G{SXeuQ#hay^BmU>g1TyEjk6uM@oeLN1 z`+LI^6Tl}SaZK1wa)1i>i(&&82{nm1qDOWYg*p(^(TA2XVU|h2c4!$LM%aM#N#gX= z6gY6`q8MIe;UV*4<_}7P4UvUR;#{-|@KYvBMWX4mD&R-h5RsAQWx~ueF!6V4^}=Oj zjxruimDECuUzrk9Mb z?+P4*oI_OeS8{J?$PQDc$$w*can+6n83_4x6+ZeQZ+wq8H92AlJUn(3 z!eixvEy7*hH`c_VP2cZV+$y&Ca$NZvl`Bli#x}y56o_;pb8;C^T*}a@k zxM-QTokHi^xM0YFpPdg=yzhH+C}oOJVfVRWlO2=rnnQUPH4MBf8Lv-^yf+ep z;=}5+ZOgH+(b4po1j=@bZMg!FL&e1pq zI1{~G5a?c%eLhc;#0)qnAxT3zT)uJ(vcW9UIf7?O#^_N!o8UuQQpCWcd)*W0XD+61 z$wfyReML+>_j-6&^aALyrY3kiS5M;}CM#ZOLj0Z-35B$dxV{w|{9)FUxR?D7$ymHm zYSj=Q1Zy3uE0>+(l#A6P8;(V)#~bP5Md1dCD1AKCi0kQXoqRo~(sK;zz%o|NUmG)4 zIK!l>7%5F;WTP?g+?7dwJXI@`eqdo}7C6#f?k?9%IH4&~BoFR_I2)PMwQH3e(k{gd zh{)m;NpWeZ#Z`8+cLip^*CvZ9nLtQ}L~{8bJLVNg{IaYV1U*s0*w>uKKFqOm!*U}O z#@?rfd&Lpeq@Qm6wxakRr|dzd$++k1!4UB9A5wiK5FbUZ+tu&@NZN?VV(=vGR|n4; zd}8a36^+5#Ug1y2`yhKy) zciJOHBh`505H2Rg6ZVHJpd>45!S4Y9wrg(b*a=wS4`qr(%CoWvcyI`foW{1G{9uM+ zl)LTog?pyRSlU`m{bjw|zUf9Byxdr62Z-=b>iqspi^H=PW*9Q+doIGG2IfId^x7~~ z0s=v&z%o|6rq+Y*EQq0EwW~!U)au^oii;_c@Y# zQESo^+8|VlhRWSvSd=xi;HybzLzLBmA(con&7wgBuL2>xd z2Ywc=mwnSwI?;R&&bX3)-{Gb7*DZN2d!5!;?{%-BmXmCPIQ$K}Jj1}ity?4gO;J4R z`;IdmMF)4Lc%w#%LF9>hS>H*hFJt*I#*)57^n^4p1KTUAe~t`` zh~D`5O0CXXeJOw~OnboY!8b%hadZxaP_If_pg zWkgE!6aCv4OZ7!G*zTbP;+}_j)#Is>V;JpzlV~e|2#!MkTQv{$l7X7;JC@I>HhYbh zwM;qBQWGr zCDJIskV7L>Hm)>4H`qb`LIeXtwiOEFL1|cWI1U`npeJn#{rMIe57CjOU`!}9`b;>; zc;td7M40yD#Zvn*r-QHSwf?<>r@y1NoYD|R4wA++5Kfm;f!A81O;*S0gH^)E2Eh}@ z6ZPM|^_A!(ObIfBad0XGHuPc9*_`d7NGlu(Ll#5cSajlvxjrE?c+iYNc}AQ5&SbPG z@kgQ%Tm6&UO4k^5T}EifeQVP>#Z%aC00S4rXg)Gng&j6n@$Q%JhfA zcCygN<2pzgD7CJc#d>!!o{8YN-ycT;&SbTmiRoPbAq#@HDpi1i>}@;CJnMIs2TCmWc15V0dgb2eY4N}3z~o>}wG zJvdR6kbXLLr|ZcjJG>dp)^P-cNzu&B@2(-#5>N17fP(m>xxd;>k`rsy-HJ+TJon6a zQe4tuDwj^|Mm%Fqn;=QwnGy;a^IROE!-`~^I2;1}*PWhy=wGj=pC<<0O?EH;laYL; z3%T#W84$-C`^ZrcII(%bHV>m=1rcIc&G-5gm9t;^1xf=@gK|<{1V)=x>zy2kL2?ZD z)8I=OId$1V$a$tJ{>q>>Hl0UI@#O%Sc-J#U#YkRL2iN3d2bcRzmukNxk^>=<%Oq(z z`#WxNgJ5;Y`<-Ii9I!~a-;`2JX5HA4SsuJljPLzUk=$ZbchjqNRqd7IoV|zh{ZmJu zhk#k^5@2@p2VvrR5SZPWY};dax2B=fK(;%S>mKUyR5Tj8wS9Fg#LKR52=iyhJG1~5 zP&pXm7P~u?pM1@hBx`>sB=DoC5L7jk)1bAGBIryj^f{=PV*c5iu})vg^c@|9 zAlSEahhm4ei|0*zjBpeKu=Mnh>nEKQkmR^A-&ZjaZ_Y=VAV1dCUBH9p_JYNUgV)M3l4 z%=}=7w1UVuN#E}rjrXeqf~jw`X=5~_MHCUQcAl1VY7FHzdjN2i^oRgg08a=YO(!A> zZ0C#|h=%)GhI4*#hrmI4u@*JE?11sWOl2pWFBuNFq7m0A9LWyyq6eqN;PMsVf~wQ2 zoCkB?1nDU)6lYj%YhsC*C-nX+%UdUgWDt%!uEr33!fOH_l~WLbG=~57qp$azE-)Oy z#X}Pag@)IwqI<8LkfXR+_1P23vp#>!G}2c)CqMr@ zo6K*&@%^6G+&ll=uC~CYLAYf}*R73|xAk{p5MC4N%Z?>O1l3MR+JsweYJ}OP1ogs3b zcK5PaY8S%vco|?LbRM1E)D(-K{{rbI#v{*6a}0!9UkNk6pz;dzB}CCfCrWI7IiGve z^PG95TZ<#J28}<%hun!MJ7`C_0)$0oY;EPvvOs5v(*$q#wF=s6vxtDo>Z+xjFt7f6 zGab%qS%j5($fp&LaRN3a-!D#P>PkHS_7jZ1H=W^>0`bNT7 zZPq_}Lo!+MW@KEfHV~%Mgf3+YMasIv6}2dt*MEU%H10y$y=Tj=PKjB>J9OJj)mYTp zG`S3lS=3pzc@y{ALV&LZn>LrJ8nbHa5{J@%iJ2s~Qb;wgnIW=h`PVG$Y;~ z1b)*g$nh0g7+e4CV-~L>Ouqsm8NOu1` zhGf*W-l-A1(qgcK%=G<&Fza|P<&{3&j`6r}(B1y#Q!2CgZg_nBRyeY~zNv68%Aqq32=A-jt>)OLyu1~%y&8puv)4CoiumVh}-blx^kEP)k zn{`JYhK5PKvTFh_&lTEC{s{-Qb|`wu&&`@%t{HB9o_Y7q&SS0gRovAddpGeOo7^Uqh98$ zZx=o2Iy4Hf53dtsN2aSANW6Khd?|bW5zEJG^ix-MAx{c+OfDY@8>GUn$fqGAfg3%= zQbPQD6j;yosE@%K%seon{AKAgZoRR&0Fd{hNNCpzq04K+Q)W-d==M}z?Lm`^PS7leB%=x^J-VyLZ=(ydvRhz zs}jG~0uPip#Nqv_LzF)SE$_x}_}<#ZNQnPQJ2Pb8(J~JV{EUWmL*m6Z*_79;u5<+I zgdVy1iT^D4dEEa+LUD307+4|>=Ul~4YWs;MwZ}6@vG$(850W8NFt zF_5JHs@CiZvd61CQ7UX;tXja4jpL6=krOR{Yi|feoOJ7 zbp}&W77f+vFI$wv$P#)}lt$kCemLiA|Cede7d;!~JO=Cw=T*2jTTqp)PPa`+&*CWGzjPY(e=|vAaxjNx z1SX|8Nf&6p!9_P2r7+zI;*$=3FVz$pp*csM-C^1``Znno)x>^lQdR1!N_mM zBMtoPcE*JfxvE{A?1(n7$Gv;59%~61V+p3;*nqjv?vZ8`Z+hQNpHkl}$tLECw%D%} zeo|sDf_O)2oSLLQ%%4qVq3S=;;AZeTUAvVz+(?bpH06*GtqEa)iz^6pf$2|$FEjRDK8F*~H`_}Eqk`%$euN^Qw@n3zdd^MW zwKArl5csb5WP_~Xg9bV+g-7FzYA9GYOe|D~ejW%TZdkg>S5L06O0_s~&LKIY#Xdoq zhj;GnWLqSFAPEvZJ1qzN;tE$fT!2Nb6}yG_whQKc3w5UL4cgnuzoRi%9?v`m_o!qv zANI5x9&dVXIto+t^H=MH^E~;~rP9`N2`90-m(_T9+^%y2lR+wOy_USPu0wbsn^X;R%c%1&eyRDUKfA)zw z;psYh;ORONFgfAjIxrXr?k7*z8LPp8$Lo3*w-C~}qNWMdkJG2?NWfrKI9qwg^8c6r z%9$hX!j~vilGOvLDP3xhO_fL!RY)3gBYovGMPVQ* zX`Kc7N1|nUvg#5D-pF{}y|Q%!3PBB!M!GHv1)33#RuhEEb3&mMh#b!}4Gw| zIfF(-D4@m~!&=HBKi$6a?4zLvaHkuQkS7)kYDx9EG%_o&q+9GzFumen#6&3{?}8Hq zq(6T#3LTfh`i=~2C9MuOvet6q@S%?ymec^ZcJwv;D3;|wq#C0uKO2_}WP7%LxyS_K z`XQ{N$P|wpPw%DId^9@RMYc%ASj5ky!4~j{EQybX!^w5Nd;p84r2LaOwX7zQ@!qRA zgE4Vg4GJBX7XZJR)bANbz1$Hlg;m3`BI11rC&nUXmo5!zd|DGcP?LiUs#b341Y=;3 zeI8AgdPz7bUq%s+p;b2<{kHhMuDs5^XLk$6;!7EjH`JR8r79Ot^7=F$2F3-(MxvYD zq2+iag5tAV7xhQY$Q7(_M_)a@6z-(fL~8S-rtFrb?sk3F7kjbMF3YSN4Wg?K3X?!~ z@U-ra`m9g(Vz#4;#yuo4#QjV)q0#q1`2+re_r^+3S{pc49Rwzy=#>TM=-&NpCo066 zowZ~y_PopT-i-?KFa8Y-s}Amj@>@B&KP9u4?ZpNNMGdmV>@h<24`Md)h<~zyvjN$_ ziGbSiHwY*B`ywYz8tSsVdxLOoprt9i|OC| zuB29bvK~9BH1L|Wtm}zMPKY$>^=6I@k}61HrmOJA;Of-`wXEd_;i50@^tZPfGgf?* z>|p|_jGmsZBL3@KfzU(mVD8&h3&4d4yX3&`$;JLD{cm?mK|{xHny+sdlDulWYIt$V zEiuL0$MsbkoI&rOGnA%Foi(9o{uxLn4AerV-Rep;kvePf`R4{ImIBP8|Js}HZh}%} zsg^s$Z3XT&z{!!I;Qjko%(Ent+<0vu;)iU7MjCGQRd1Y_FWL;=!H2LJP z`VSz~xlsxKd0_j09O(A<`35y*Pb$yRii)J=pI*@HI=>QhWE?RPr&L+~?_pO_kT_{I zR;Vx^mXBZe$2rTi{`VcNd#a|zK`JSedp(Aqj>;x3n7QD@fZi51yp!XY@TRN_^7W6- zW^%D|kB~E&#L(}RN_x`Cj|PYzDoREg&&%d1pN81d{D=lQ3A!55z?uS$UUH+dmVTGo zvkp=(ex`7cN>G~!`#Y2CB=cK8AZq@D`j zq(fkyI5I^QAN_M7V#WJ|KXjC(#?QfUq>&VSRN|b?+de-m<42ziopJoLX&2GA`g%6x zFrt-vUuOB6Fy?jIm+7U1>~$YaimmOn9~k9nu8=D?cq8F@@n1spWBvkV%D|1EZ|^X) zBiFCow~^|Kqhb`WwMulnIYpXl2kVTZzeT^R=D`Y)bdfjeytm*xM)O73LsdxO}B8L-ktTHSQ{xg-qCkeG1uwDDRaFMYua=4t3^)2}H9IyL= z%^*F@xAJTHoLBs2!(Z>bDn9Vr3|F0V&tD1rT_Q}w`il8&15;_&1O+S4tqvO(T6-B9 z$Bb6ojegp+F2GVjrK-If}1=ZoXd@3sRxO~+>f<%p|)v7F!jAqzAi&1VFxm$I!hab7_32) zz!G_*sBx*pLkvp%U#_bqT7sD7)wQ(X%G zxzl60bX0t&Pxoh_Qs50puk)iFOwK?&Jjj#&DDv zuy|BVzjqL?@`Fyedw|Yxy0hBqq4{z^!9nK5pne&=i$qDKrvHnEN_X zZu^SMbGF9C5%?{IiHrIU-Zb*iM+?F5^iN=ou+LcX6VXwQ?N1GqgiB7>Nxp(Tq6B5a zoj`g+*^OLZP%svxsaP7FE{lRY3Tt&wPM8Y2%QN9{S@dUa$j7P!!`X<%L&sK(jgQQQ z4&bT1-BIlP%=@o{%r_K@H7@alS=vOU?S{M7oeS~VR^BS-t=SFEPK;_}emx37bP7ecDYXSFqTo z;+jo#2YONBb(G~@;h16gS@xsfX0Ct>E3RquhAWh~)WwH|9vUZ;Jq+u54WIgRF_XFi zP72b6NrNaJ>!NRFfe?TgV#%P_Ga@>c1OWy0M?Ycb6pwX(c3MXF&vkSiO}FiTm;}E~ znQPa4*5*z6tnC8b#W(3dnqEfa<6Oa|+co~T_6iqU{WdJx?Ltk;*Ip1y3AM0_cA>@~ zcFBLC7(ixn05^fcDR%9DK=uCumJw_X=d*4-SIqmgu4=o-{p{EfP-v!*Wj#ZOHzXBCbSTi|y7 zBHtRL>{TrG<4e((fU@iOpnOGf6d0Qctw5fzu0s>>dzXvXzpQ}79!{a&gpgNkTRs?l zvP|B7M0eOQzNZ|X=9saUM2TS0A%)6jAI!xMKBNizU5HI(B;_{XuWuEWc~m&=Xm=^Q z+;iY*<)R2b%Z08(Q<8HI&+Wzjl*#EgRfvXu&d_vzeDxEP7FPG*Mc2CaB)9XCaT2uQ zsl8W!Q61BUHvd?|pf|&N@XJGCIp*w~myUxnT#gxyMfZ697Gu^zRV4tk7;@g^2*1;G z@OAPiW#qu1)JYog8I)Kg@zq|~)0$19{(}`VBO%kwmv<1lfeH1wOxM9uzhf742KCsypW%kf22TVdo0y+Wv$in3(-XA z{0}bA`n^4BcyXYX?V|w?c1p?fwhVs%d1*=-!`Afq_!Xr`lT^KEd}PXP^_ zi=&&r%UyX*S$f;^zTRe_y2w*DSK|A~v7SK)Ifq1jSYBgn z(-Bv&u@%wFe`uiK0)__i(+bWsuKUVcz$!CDp=)VU4#{0VutT>Wppfxc1nq;X{2N>L z;bM9fzK{WX$X_5xKIO$fff3B*$>1p5~)cfPo!A!p_x80{pfj&e1W1)$Pe5O~rhzX>0BM zk`IuP9cfHl;JSZwr)|IB)vE&(9>)6KM&Ej6Pm^!4i5XTi&W})N(N9tT3q=0qsDUc$ zRxQmPZ-(o-+Dc{o`Noh-*K{y1MJUb<((|7-jEm6B==@K0BAiRbm#!J2Fa7~2B`pk% zlY_R3mqIi~WtjAVUIa3Ch`kH6{iDG2Ql8aQm8xpZ9~+L@B`wXNzm4c=ie#Y2Nt;UY zHzacu68#6m+Hi(c<^C-?Ad(4Ehe5wsJbzd*{5EFR#)$TzTkY&KiqqV5*2 zOhQ9TZ!BCX@BDa4QQ)ppwKOVg`{&gjrcA&>KmNGAPL5&V;AZ1X5!MpFrP|VmV{Wyz zo9~9f->Q~0P(FvcJj*!vO>`aA9w@JtG{e)MSD)TlpX;FswySyWTFS?Y)U?SQ+D?H-E$L>(1x7*0HJ|` zrpI1%&Fk{RaiS+((|%m%eLP%bvwj%3xZR+D(@F)*>r}aU=u2LJUI8thWG3(tRSpy< znEag`SOEd7Yh!AJsKHJizsNO1^o5DQ-{sj-Dc%H;>b3I#MZ^00h0=i+MJ9}2)5D8v zj!SI`uP^}XOSZk2SNNzNXXU%xH_Zo4$-9uSB@?wj_FJBoBZUi$7)XhVxU$a!h&TQh z2=pt0D>$s?g%hC#2tu#RN9Uim?bHTEI4A${4UL!APHdD=ecbrOA$SPhA>ixC{bq9T z#|9B*-YyYMajcc7o@S9L=;P@5s!o5B;e*4qAKIw2zdIl4n?8cKimhx+bP_zH$a(2r z5qv?j^=zIv-wZ|KeOe*4I=H+Vp@>J>BTgOB@V}V*@^~n}_wReg3^NR7#=bVjHukZF zQf9_rtPLrYYOL83QE4^yvDH{YD0?Xsk!Z6^ktmf)*_WguB^7$^L47{I-|zYU@w{Ho z>v=sb-1j;6ea>}V=epkSYtA`y-y&$m09cE6OZ>B~m*1a|*>Z8`^4b0kV@n|RuG*TW zGyxiRErQlv2n?G)#s)+CuTraJ=T_Z{6EUCXY}G*xp5qr6E`yl}sL@G5y*;N@KfdFH z`^eVcz@{Z9`Om2z=15+K@518`h91(!Kdi$qa>qdda&-=$D+{~Tr<`N|?)T+ALou=4 z0UC#Jv>!#4#7msAf(n^PYFGZoJC|bbOabk^oFa57*0F_MHoV$HD=<6I*+CH>u`P$F7 znocz&_{j0Xy0AS&5GQUVnQi6aAVZBFCnDBVB6CB5N-|2=9{WY5Byse00tD|(m)i5p zbeAD4{yZtdPa!9e(qRFbmVGd8 z@e_jmv$uXl^}qdideiI=r|iZ{wZ^|dw^7E6+raapO*%p&SUojU36h4;==vnLZ@SZDGnMEet>#cgWzHutCuo*OEl(dzzZ~ z%HG=EI<>MMHhvM14*th#hCf4t*2nEk1gNAmh<`nh_R;4B5Yt%^?{fPMD8Y=+i)8t^jSFer8YUzUHR@0{T`?M+j-tuMk8& zaPAsHe&>&SKE9j?t7u&%!{_c*Z(AvJbLuN8Ij3`SfOsq{OHEN1m(m2U-C2iA-aYZP z7xZ2Z+!dA~KW#{9h^?_w0DYPvhkrbjI3%gIuAFxw8Mmf+UqDH+;6j0yx=(nGwNiwa zum2E!c>k-=<-Oo0a8m!qdqqq5hBbT$xcQ(&pW|2@4?`<(G_>V}JV=Q3K8@tkVNQnjykI6BfTFieJ*-paj<%`d#d9Oh zs8>b%;IgPErFy?FX{=PO@Er%=5%X~s;{qag-Oth=+xJLVTqe=+)4;fJ|ht2`^Rroa}EZ$M|X7JG1O*Iysw z0CnKa?|IiLBt-Osm(7}lR*G3plUSpk`$NUFBk+E9C&@Vu21;tx+D2{xb2m;;7YH~F-SRIBV zA8a$$p^KB9h!t)fgulqdt`06pz3chXzl*@3U$+t{B#2v}RO7F2 zt)@(WX{-2b1Vm5ig*7(Efs%a)&qeeiCZ!Vvx^G8~)LZsn^vq4G{5U$j_06l1g9RUC z-z=CvdGKrR2Gu!dDej{e`t$J{F2Mh~;6EO>PNb`2wjOO@<+^!jtAf0#Sj9wekZz(A zBOp5Do2jk&yZBWuIiGY=*(}tRmU^maYP0C`nXorg$F(X?l)YXGU*i*spiKWD85cMC zCHr;Q!m9Dt7#!@PEKYCD`$6AX9ir8<2fH}o?mBW!`p)6i>p6s-k29MUKORLUmR~UR zkwX3I9GJV+^G%Sx5O;lkt6}snr5rPz(J8U z-(1B>D^36*6NiZ3xS5PZWjo2^7Vq}HjA$5;?ffw!Mvutp7pLL zS)dlHw&Wxm4o2-W&p2k|*pJS}PjU%5k%;BPG5G<57Vh7!&xP$?PBC>a`whg+t?Qv* zHTjywf%*SQzjYxA@vC~R56-Ip{BchbL=s9YA2RUasrC8A({oR4e@w$G;Vtt0vJIB-oTSBSQubm05sliCY5CW)w|-_W zKDhixOaRzfW)sBWq#gh85@?MzKmJQfXXmcZE$)6FUV30PnbVH2w{{w%7gy}e3R`t+ zj-~#x^dIh`(k5jqPVu-eh~2nyVk-!x^MW4ptE0@_;Z^oNijsPBr})snB8rojB%spGHq7l*AzfSYtw^|5=dbF<4zFe?(w<(1{ez_CZF$axhUK41PrQqaoMJ?c`gf1 zB>{UMeLWpMICu8oA6MMq(tnEQ*@46X$J(oVV?Kb%)g7_gdvn!beGE8B$@Am-^xl|{ zTKguKzX^V+SpU?yQ4$7&@n?^F$&_Bc)YAfNzGuPEA+fdYFmO2B&NR{Ebk5nXv||=q zvQaKmQ#b^M7F)(5CM=)4-<0IV$VC}OqmUB~!7+e6S`s-v(w zKi*G@z>_S~jOB-9ZoL#A-1xEY_TaAIiBFrhp5v;7&ni^BIVDt*imAUY4E88D~^HKwU z%C`2+g9`zxRe63s4mTgP{r5WMt$nk`V6&XrEU<+LaG}9&ZK8{F$XRn(UxN6Lr)5lV zQAB8UHmq+BY^rtrblcXx)+%tQ=EUuP(2|kJw=WPu#!fDVe8Np*DBWkMrR z;B!wl!ftGTgYveqW9Z<_AyhObQ9j6YbF6w4uQyZpSci5hmX>MBP^dwR7Ti?8Yg{6; zv9F*hnAijWA!kV-_4hLu__%>aX=n$m6PBVBALNds5}FVI20tsIOj`2LaSusB%L9eH zW|!IbQ-cm$o-&gihap`b;n1i^DwBR8tT8il~Ie+RHQ4x6@&v^&<6dGn=Ag znehDd)M%^o03rPz1Sr5eu%ILa8|O25OZ;)LQ4}7=5GHS-NCKjf4ptad2DA{9N_!!V zPQA@9XYt}p^Tr2Y1T~ke$kiNQo$_jCI7n7j;F|6g))}~pm1jte?!l5+mD-kR!0j{u z=xWi1M5({}lA=%%N~8ph5|U8#N74_aNXisJT8Ti zU>9!07F1SCXw2B}e)qE!4hCS+sPFtLJG2L}Q&B1gh@uCNs2$ynKobn7BP`WSo0V4eD|sfoyZM^|rW<=KwzCRAjvSvv6%_<8)ux51V3e?-?T!EdeXo zyApzv-A1fbJP$VL`KLjo>Ay~dQ7=lNdqaGD7|suVRNOov@3nB&Z{>RD-lM8>S@sBk zjJlKAU{<*MEovJ2>~ze0f<~%kvS~n_xPfNhuHrPw*jGWsgBuG(DqGEOX&u=JQ2^4V zo$szpPsMqXe9A(?trA01J?L*V;VJb;3gJKiQ>KTtzxfC>5YY+aLx>G17i8nm;<27x z@1j$Ml=eo;^j8g@Vsce!O-FBQt}A-)-yuErl}i#8%%A6xQ!qo3B83hkPLv>~<)`l~Y8Z`*-fd zN{CvP(3zUQijN?STYeU!ofe#`!mSp}2md%2^pAs0?ROB(`Kpy`?~`idC*{@H1o}&_ zLRR0NOfDWLKa4CFMsna@e?K=HN)L8lI(}R3t#I$o4O;K*mDjvbJ-PEHngF~4HWx@P zE88T5^b`n0NaeVabWl2GuxUq(- zs-%`%dFiBbV9*L$d^$h)H;^NM!;gt?0;H41r%EqdD(Qn;e_f<9{pch&oYkh>GHoN! z0Z&C-7UM85iSa<2OW|k#nY3PSRmG zxA)88j#M;O=I+up0XZ&>5c*stjD#bBZh0YmB%cK1lAyKy29!?;vmkAt2TPNf$zE@tbhjj22H($k z-Em@qJdQax>@G1Tce2@a5P&BIY)1}EcI0?Z0 zab?+6zqJM54oQlD@Y){@nNJXgNm(Bs7<^2KXiU6bQXV=?b%l1;-@h6b)Q<+L_vs0X zi|!M4W+ryGNxDEIMQ>V8ionMz@A@&n_ zj%{mKSBmE|W5P*awZhjHYJ>wFWKkg@S#XKdHZu16mC6??gDa-toy zYV6f_9h|^9%c$y|EVg0Iz*gC^Fysf=jd5gShIjG?6(Rg)fNNDkpC7dIn0^F@ijm~( zUWk@aWeri?u3w+Dp{(~dC3U^>if<*nXBQm6GOrscPqvI0T~<}asxO@(Vv@vwPG?5lFXkQ zW*?(jC{rCDE1b3QJ-)YzGT-|n`ZMj6c}LEcK+J!h&HGU~57i_`ZugF1Vm<`q#eq}r zi~P<2a1elsFn~`(_w8O9v+m~o+kI$sM8?(^b^TDBNK)nJYYkr_05r-=UhK0Op11SE zsl)m15D2f=+wLaS3t}rjN@NINtb>&3C??i@dj`t9sb}2_1roT?+#3NBk@6dO`<0Rp z&ph$%WelA8O2Tb$$2T$uMN$$ugcuDo*d&~{d_sx|@*!9DOJT}T`5GOL0hSQxo|@RA z*bWd8)sgRRwxK~j{91cE{51jsjoluR*Lw7&)&u&dGiP@l!YIIyN1jTa5x#yPh+23`Jp1oiJC2{lu2=I6Od)`hC4xXk{7QmEF&NMJG) z0C4OSWWJmqiA10Y7XoYs)+{!*k)CkPZ{CO=mQ7G41vXKz1F8NB_EBSaPp5ULd70XBat%>v zYMkU9wwk{N3pT(bapL4Q*O<6^FHVOMI$J7+sz7b4idY5*L9J($a<1YNt z@nPxfgwW*oY4(**e*^f}z9zd$K5CS?FF4gNmbH$P;&GlBG>M$8!hE9P3*Dbl^UC>4 z_N>^)V=s3pQ9f(@wBS1`xFIlbf9(1uT0jnb zMFnRQZ9&x>i5}5}>zySIuf3uI%GpE<5Mq1z3#F(VSqIRsU>J>Y@(&P$MGErTG0GYX!dQV>@Olc<<^m?U=955jx=V zSB)3n@i0;)gpvL#d|a@E!%!dk5plUqRPSpaXmeC%2Rbg~$-Y)pqi~Ib+{b>;_gD#q zU9{S4%XjC(#Q5x5@DVD37Y$nFy3qUBD`TY*{V5W18*#q@R0%-)xD_v9g%f(Sh}3qT zITrr8RY#TY%5Na!M4+K`77;gqNIi6Ti(=9blPl@!VVctZj7!PM6y}hEOF#>{QXvPz zK=C2)Uu!M*M>iJ$6JK%KZvC6xaS)61$#Apip0`qx-HkKVCTF!tj95UFpJ^%ym7@bU z9{MJ$io}>6i0)l@ba!z6c=zPu%3sol}PDmM?k_d(R0u&Nk~o5Tua{c zn10C4gFRX%N2{=yfo<8gtR8`uMG2VXr`qwC6Hwrjv6?&&p&caz66pP@bW3H}Xp}Si?C0Oa30LRsFr@|LEX92=PC~ zBFKx16MvmOG~Su1=~~bErNJTxMUH6V2VpL{p=PbF7LlIyw?#)Z(LIU0t5>i(GclZt zgC!0PD@I7XlgDJ6@H)%C?Ny!|A+Iv+nY7UfJ5@i4!d?Cw_D zXHe!v7*Wm2^3&&^er08tpdTMwM1NP2Uc@SL{=ZdtNl7x<^mwhq8T^&MtzZ4yIpGcy zh{V8PWMNjHoB(xJ_VUH=--V*pzsKAXO9v+eSdk-MS(Xh$hhi8#00QzDLU;ttPq2XE zg=P6eKIpr>l?3v}27iekHD@aSFgR8wFx3MCUxWC0z2@yi`Q{>$&{EFb=W&ic(D1aQ z*_EG?fo)#`=Xc8V<|9{Z-}$xN)`OTlyz|)kKes`kr=y*5=OQ~#RQI){81r12ytmvs zECEZw1dDMg1!gRkXMPc2D(q(a?BOL45hIaP9b7huDkpUvR#i4~TTjV;?dS{x=ZEl^ zb|7W_7>1VtXw_Lrq{rbTw%_R(HKQbjs@v^8iUyQ(x$Rizw;l@EoC2$E52SivRVv?V zBg_naYYAMjX+Bak1R}>NM{axir*YWipMD<0E=}ReCy1%-A0+aNmw9)Fh+Bfa4DjH? z1h+pq@hiSr`c`HypC8%yuYLRt$gkQfFFe?gq@js;q@{Aaq^-T-%QTS)|;7*t6aQp}92=7=~JhV&YqY1{K=#bQ;n ztt+FEd*?Fp;KQApI^Vh88~?n)DD2SqXRddv(fB8Z!4ZT^ADaZ@M4E6n$h;@=5$nEU zD(iOAZ$LYFWMi}_jaBzGdY2Rhg&*)(QO1XH_02zNs2fTsMqnZ8`B7fkA4FY;NyVn> z;%)5yj?eJ&QV0&~FK8srYU1B6^Hfq+s%=yZbJmGpkbmT-t8H^@UA7h>0Bl)`@;ZAK z*RYryKeRd!XAp1Y;)8mS3WV;vHDNb`KYsN;dfV^9sKy1!!>=H)xaK~^`c#rG{n@N` z-@)zaS6{ZM+z>oztpUJdK5u(+z+YWZEYQm92>EloS3bbL_qhZdDKLUgEkA&HgpW1- z2BNrDCjZJGt&>+{j_BTPdRi12Uvl>YpAXg52qHvv#hqR5{(IwCbOpj&@vB*;3FZPl z(pc0=i*UE3t)bvg(;H)_dE#Q{!rC5fH&rV+nqE}1sQtALpYt2g%S=hsJdJm2OJy}5 zT)=-})nzNO{<%>;JHb=*MWg)-AEAbkYDUZ|SjHFnSoLlwkyd7I*=GFfH*l%irTmsgyXy;w z^b!>-@JDlHd}X(=OV5T+CXLR>*ly^tP5hzJwo<5JTy?(5;4=sxiejqzNP^(y?z?M@ zU+=~C%W+M_MeQ>v0hIpNI3#CSz?~*%Zj9HLzwp=SJT!1iKl`y4;nikN2d?!qG-G3f z#CG`^I5R6?NI5P4C2iXUyAq63de6(eS&t3oaZ?{e@*TcOLIZ-Fgb+aTmH%A%$r24?!8RLW_UPlNI=dj|cEPytEyBO@5sIbp&-EhO#`9fSk{q(az1)p0V8d$TRYrPd(LYKOq zt5RM29BKuo+MjFw9Nbq{3k>J}jAF(a-$a%zb7IjNr}{vX!GpXD^fkv?!Yr@dPm zj~z=?tTRD#L3~EJzQ26wQfzQfrLQRQk&k~>jGK^Ku7YSN&o35^$Qhu4DgCE-;}OK) z6I>JknpUGbYL7eyB@S!Z4+#vFeE(JU<;y?P2l{L7UNaXsM324dI@3!!(=Vl#*y3+h zqS}Lm0xBcxsF;o5V;}aDwtW)Hhc?=541j$(#C@A`a{4eK;V!rVNZc-x4?=uZDfF;I z@;zd{zOPXAOSa^Uw|U>Ry~r_J{v+BeoZEtg9`^VbtA_RON5U^aOd9eR?$-^kqQ}4s z&XC<0SQlYcb4}M0Rb!b)4keVrfCC2#o5$hm`d)vjL*G6;0C(OItY5TTuJ^lVR+z=C)Y^GF5R6n#e>h;gj=-Id{lX-_G z?&@bufd2AgcgCoOEol27W}(UFE*QEgy@ZmREGp@vqFzZ3sC5&;oT7rI=QO1hcszN1 zU$tDmtLn3^P2IK&-6hg{x0~)s+8@FEqpaMc$v^V?#tg5G7AU7->mM83l8>GR88GkA zsEZD$zJ4Ys$_;SX!PKY;Res6(==%xY{RD8+hp&niJ)(-o>NfuKa*ck02vq-0;9w?P z*GzEVN5#*Yn;P1F<$cu87^YMI3UoZ=3UvoZ@n+*sYa?}cL>?m>vi-rYyq**LT!StG z-;C!!#~vC7Z9iTg$Z_FJFicm&zo2h4?dSXZ%Xn=u?k@@ei7k+7jlHOW8+x2Cf`-C{ zq=Fs^t=m}WSM;%gbsIlep1#>*&pZk6%c3i zGEwaO&A9D$-7;IoyPj+toQ3zUO!B_osI}@Rb$hiz_eK8?=C1XD;6E_n!5`=JvaK^^_Khq*S;+1@e=)CNjL$5*zBwO6G%O$3 zRJiHKQ^Cz)rXU&JS0KlXcV>*P-n|M_UZ4%?gAU?JFeLC^q3Uv?)le6U*@`GYJQQX= znb*dlI)Kf~sV7p8<(01bupEa0p}6KRpd~>^dTyNwhSI7iol(cI0FK6EsTjXalpVy> z#Q~oeEI(OJoWB0o0fSO9GBy46Ge$Kzy>p}oRX8-v4oBBEeEJQz{FStBmn#QJIUrge zs59Q_2VU42OJ1V<1lP2e58nLtU=4G}J2_W?{+xZK9MlE0^mq$yMebDi+m$F3`(a8zSCZdLSEw>{%p_X)%e zFm}BBNni_~_Uk$zYn`_%T9wN^YL z1%>2t6jEcfOdE_Wc{U;ug^lX+7I{m&3_2E~nlE6R6ljQYcE?KWq(3xO6QVuu*L&t- zcTrj;f<%L5uhuJ9D8*eFnM)il_M4IPEx)`?0Cmh1h?R-ekv(h7W2}utl@UA(ygWXU zR-tVFKz)!_lsdh{oWBQN#eD|m`-Xv-V$&3eFenAx1KSb_RlsN0IC~eE9uKXW@sIW) zLE&ty{`h^SR~9TwG=TCA*BQ<56sto*Rb*8Pxv!Wa#4tDb+&mKuu48!?-O5x{uiI9K z1~z7rD45|>Jy1D!oLga2UApe@HB3Ge0_{Lb&h|3WXmmgu>8wSBFM`kgwHeBw#g0XzPWDN;~4HcaBlFbsy? z#6oPLdxh@5k{CLDVf$5$XIBr~U3tFgvLokg^{@a>4p}=YnRNv}(1A%M=z)Je7HYcI z?G{+g6zecTC*e`l^rG5MC%8dSuTla{&Bz1dy|Etp!ZtR-`Ivcaq)a2bX>6ryeA;Ie zzhAPxUjIqtSZSgjgbOMApj`TG3pI89rrovq$x4y?Zt++_2X7D8Tq_A`kK z$re3)GqolR@rsL8a;!tCZGUl)W#m;>KRYi{q@=L`7gbmm#2DPEC2j*=i@ts2hSMoI z=sztHXt)KnwF`FAly`|W*lzuFXf1s_^SuX0AO~@s$xXnu9Te1qnxYqR+sBj=P`AnP zuy^qZNPD(jVvRUEq|hVM(NieDKplZc2$7h+=3L6uI_^SAd5jK-xI~s(vNw+LB9B2N~!Bw)w63M7iilWZ-2oAjd#Laj<*j6_7+h=}a!11DV1WvQ%z#CmV2N@DpI_^(n-bkF^v*@DRY& zUz?A+Mh{@RpwJ4uLQO;gKM?(WT$Er&IIsuEE{sNfhMn71F04SPO%Nzh=ye+*d=4xO zbqS$^bVVrwBUBoN4T-GM0qOzR&GMKCrKwQ@0IDO$Q3nmGBa&j%t@KWkpcIQn;5T*A__a=q zq=sUeC4#DgZgCsAdm|wUZV@5AxYhzGs;#h>g?w;@FAs@g$tt4+6so{A0ceDa5FVk1 z;OAi^D?u#V&?wIyTA2MMy`A(#g&8RQi#@j@;x$KlBdLhDxvkjUB1CC6`WnA>;f-h) zWd=5`P^UG2T%yJWj^b~++z8Os@SbG28zkQ-BmMS)i!&S50o>8g+OUi^8y*Fi8WWKw z3R9RB>7CMgtb1G87$Mf}oXIjmwQ#s?gC%-lFAhiuv z+M#S}?u@3Ql_G$rN!)hvLHtxbbG+v}D$@<^B-ur3s^ABRqNXrDy}#~U5P6|-pc}F= z3d?Ma)j=rMuX!g#4oU|g8vZb6Y~UI7d`KXLF?)tdPbEUqfN_*}O17z&_>eivCKKR} z0@86nhcKI740~!hyWTM9qr^U}7h? zqY%O|O~6BZwitX%ZjVBTI4*4nAc&;ssEb6yBT-he_CjV7_O*fN4N_L!&TRg(#D_|v z-%8meB_mRxr@g2L3iQv)L|g^;$e)Wl24OLY(K;hRaxrzHd2rmp6k)f#=&w(X*fyT zvmrpmj@nVV6D^9UT2;ZbsbgRws@y`I#{D46GmZav9}OoYoo}E+NYu_y>xN8 zpxx1JLjK*i_!O^m(V%9mQG-UIW=jC#Qv zI9zBe%-(sZ9olv409n!&cH3|0BS``GQm)CtYmM4su8KJSCCEOKt$|hD4vDhiEv4Fl1HXLW_$U2R28@MLk?T-`NK@ z{L6j+k=>4rmqiLw$77ruxnuU18?}}@s7aa-L5k_GXo@=JeAi8|3a)xdYWJjP*xp!O zs_mg8x8B3lx^g`{{~LZNKvXzff?-(R zHhct#dMgPcrr=0hlzLTZrZ%8tdEL#a@p?uEN3 zzmE?DxFL?C2`dF<@#sT^`6v&GkcV&p+-uSkf1v9|2(ofrAjznr#s9)?UZlV51|a}I z#?44|q=AJ53G#2+EKl$b-ZT|LjVU%hZWr%$p{S?TAx$6p0`1zvG&IyU-;zxv zD>niTx$)^7f?%XU|KLszb6)gND>DH6o{Npf2YVdzPpHQJ_fUP5TzwKEy;I@7$3Z30VR(gcEnxssfdu0_NiBTs|DUULay2y%E+aV7jyhBh;1&COq6Hk7FUiVb`B3diAN>k%>D+nXsjK9}qC6%Ph z`t@(tYSya5iW}#>%DqRn9niBZOy%u%ZggSRcZl8y(Bo}+q+f$pn|gH|8tXE63ziN; zVBtEz&X2buU}=4`z>z%Z|-x^a#TC#9Z7({ z#0S)zns7q<*{*t&H!FMW53p6Va-XFpNnDWd zbtBk2-KU6NuGlp&Nu4${4pm)P#6}kdct!2#bb=A$yBx|HT$Jh;hU^;ms_lTD+Lkb|DG-+X7Ki(`}=?b5b>ZACI3fiNuADdZ&JN>agxFBIi(ERfEM+B#G^ry9y_361kbsLs$PN2E zA{WwNrUx$YYHJCI7os7soADh4ziJljg9NyzIhp~{{&eqHp9 z`}VCQ9Kk38S%>eLF5BeKubVg9* zp~znR9%qEA;f4rp9dDVlLWm-T<$aag>L^ZM4*@u?H^gR_%4F^&?W2-|#~_!Uq5Yx> zI=15ccL_=7h@+b`uSnb&r{I);=OT^8inR(@rj0pUmyPWdkUI9{)*Dy8v|;!mMj>Xd z+c_fQRB*?9`zD`&kCu``T%L4GNPswn$EqFoMPS6)Y&_=zsi3lETrQu1X!A?TRbey< zVIy6*xRG-7?l7VEJKJ9V1}f&C=X!JJ*lazWxU#7_sX5#C+>;fkOMD4`mV#v>4o79d zOT?6k9m2As>crDUGido{dn61V0Yj621LO33YJ#XwZ$Y@Dcuyg^n!Cl&=!!d05PF-epoxT-#ZfelOj1!SHloxcIMomRtahTU4R z_-~*T&2YrpbGhZ3{KAnUpfCiQ0si9t4SeGUk8xxV&M!6aQIcf$rbN_lU^I#wCEi

1 z(zryWx5E<76I47$eqbVv)$Nz`3Zt){=Tg3G_!5QM1VEdqVWAh$ZvFFU2GGraxI2j3 zciF8}Ik@pTeJkc=EH5rlc`n&C>-x#4@kNv5pTpcf*B7?Y(PR~+VpGx0{~|$ z1_OOS5&dk_R{NqUi_BhyZ&;7?l}A>?vUZjm5IqIjl{8*Ap7JzTT)1)cr4qGYSe`Kl zNO#lS8*rfo?1)K6_>+&|vk;FqOPoX%Xt-}8zlnlTcPRu*GHHfVfhe!d5?5g>;B=zRj=v%wN;vtQJX(;!o3e&)rS`ZG&jjarO^ zC@~Rx^0v?Jmm~1*xnQ<|1#W06;ar(`48flPHlceV2C%N4K8+f819V)#JNO51P|lT6 zM*XCxGjm0=ZV#UVxbK^9Tj~>ofcLKii^F$dud}v<6Y@njbn*4w>&?c)+b>Tbul|&B zwYhkgBBRC(yW1tfyv1`Y@!$YV4b{yJ<6;H*{LB%r7kV_p@bBAj%xa|_VlR{;@`~xL z)<%-O>Wy}NVt+hQMAGo5@DBdlt@9U?xUMW@{|3_LzBMfh?t27}wu?!vcl2&6jHA^~ z93-udHOY^$5I2PgLaQr(-6C60-1~~QZKtCS7C;WO*v7jqup1#L!Vfd!=PN`!urWVm z%Qc2le*~EUkp&VzmG+pkH3?L=Al|EUqHZ`W?fq%W#e@_VL-=Zo&-+@Y4R(mC7>=nJ z#+CbxC)$1A=Tn1@@35B=%Xu4`dJl#_B6|M!rr)k<& zp=EI6!vP;TA$Fl+;^UdByJ!eQ@{QKmlNK4p71YW5=U2K@Pm(|UFl+7DA=bL@41-Tw z>pe*^g_YO}NZM;U3KxhgU7kn3^X85gX@$;h#9KYdDr1#(x8nPm;Ysp!YQggK*1K&J z^$D6!M}QAXJbFqKN9|3tWdlzYCMKqDG3&z@h+n(}8QAc~d|Tu375W@gBKGQT6q5_@ zgg%t<8{iT^N4Uu`xqQgRh@KOd0xAo$#x+);(l9>cW-M~5OI&n>&4`;rkbVO}%x{Ra zi<-!i81yEA68lI5z{CfM(tz_^!IjvS0vNg(6FFtZx9hzE;xf=o;M+qX%lrZW?!Ic( zL$bT%qY^Rqv`>@1qg|r=4%+N75{%YEXl8}T!^q?sF6 z*W40c`Y!ACA=yzrBB$~TPd)c)Q3LKo@Ce3rESf>wmoBi zm+B9`nZv297WOa^?Gs~uTe_d{_3&X?+_$BoTYz+*3mVef*XW{!*=%ga%RTa79<3}} zSd^u9qBX$g%wm*D-?xyh_yM=DghlbJm0{+NlWoEqv`=Pjk9M@%U*|k0(nYnnXFKXA zO;T^T>&)CUfjJCR-7k<}o-U-%j_kT;CbQSL0Nfq0fdH>F&5txnK^sBprg*AZ33Mx{ zk0Rdr=*s@Bb><$Ygxr&gV`}uE^yU+CC~JhAfw#hM;Kbv7lTch$Y0eUvv0*f99Q-;T zpMoMu+uBjt2=CA_EIsncVC(^f$T}z3X1>cY&bxv8;F3DWj zoMNzvw%wZncuKfQox^8&7;|B~l%D2XZSRk&r6wny6#oSTcBItXpAz>#9{~^Oq#R#> z3oYR*b&jCumPCGoH=nfqI_%0*7a?^)E_-;!j8kG^E6Y%Sg$3I#3z@4ThY9ZMoVxBZ zXg>nRJAq4?^-2j2fyN03tyde{bh47 zDD*`h0PTIK@pcE{1d&DJstd!r$69Y=LVU(DK3z?hPg)k#);Zg%^_`x`$(!>U2i0q$H44d$Vo_soK$D-koO^RAV1n!Hwbz=UT280jeF$F zh-5-yZ(>i}fz$5yz3nuEgHH8|=nPqnBud{8ktWm3Hyd?YB1j~{Nj^N4mV_^6oHtNR$)b-G7_^k5QjS(J%w zO$1C7qT~yKjFNBD#LDUeUL}tX@CJ4XqcYUZAKaYL!KFfxG=VD}Y3Ikt;5-9lqtdUB zYAfwqL_}PWmtJcB0xwIgr5I9}jpzACOApqS z@pUyOWgj2XHjbUi&$5TPP3ZTXB>%{ZWLbmwvyXf{=w}`p1R`CP-qty_lnJ$!F5n>R z>bI{lrn+~AY^%#LaDv?}w4V|~{wPpdRtFeF@U%9qxN~j5L*RMSsw=1CFP=CrRF@E* z1)t+bnyYDt0N1Ap&|s(F{qc)^G{f`fbIZ2t&ILnONeCtsZ0#S0MtgXkcYDm=4`3d# zef<=WcKMiIy*djU@Fg#epFndQsSSj=J~*uChJ*DesF;fE^6KJ?z~<*TN@<*c^FE{J zUoDELf2{UP{Hr5=as*v9kD%I6)H3^Si3KfDZf$%a-&(`O;Zo71r|q5VDRdG^!pd=L zBrAu7wb6Wa&UoX9x>|=~ucm@eh=6J7orEEw*&UXg=y`w8?&8(OsFz<7@_Kl47yG!f zGcKc%k<$SGCm^O2{>gbuFqUV#th;8Q0J!x|UUciZQj%g@xKH77qxSCP_TvdZ5~K%Q z@mGrXjx@9uS*$dgw4Z1-IqfENr|xPs;=#o6I-h2!T63O^+4fo4hV;JU-@Cn9#+ub) z>>lFwR=j>yjZl^c)6X3MTPj+q@>-jL5-+#4GS5fz`@6OF2jtS{uBUhUy{$ap06Z=S z9vDjQiihp`g%fDDpAbkxUcD`PaXXw#F)b2GAQ)IezXiT{oTW7MxWCbjYDk!E(|>wt zrit{>b7;wK_WDdn@Ak(6Bcm9Dpg7hxQ?UrTGW2U^2vqYU2535e;%bS3xLHN>pohDA z+dEexN=)IxbBBb6Qgc6`F4nD7t0pv)1LPTBeWy8@j7pS9qr@JYut+|%o3c*$`4FS) zM6MXVO4p)37zW*{PZ43|3$w!jfgj(c__-hRI#3k4PSB*T>604VqEVx(ZasQ zCkRPx`lnD@-;s{g6cNhrJ#vx!oMy1e#smrxgQhWMcnR{U5Bb^2&I;vYhgFM z0vFKr&wIK#Gahoeb8h7}T3NiTfF>jvpYcfApFClgO}TH$>t;~%YSS@iduOAJM{)=V zVhVagiBFgOuJHlRf{25>2+c^|_ujjz_E2137zjA5@aVBd1H*l+^6I+VxyB^J4BHqH zI$cm&{19JRSCrqf~yii_ND=V^2NFF14OX@dZsQRr7 zC$kuzuxw^Qzrc;hWd&!Ir47g@&1;JoQyVG%Spl+Mw^$Z>`13zUN>79RMmJBJX22Av z5QPf)oz3^~5OOV*Uu&TgY9#76Bnjzg<7N3rO1Lyhk7u_^jg-QTB$e@QYWP*-@p(6;bIXpO@t*z%DIgrqzG^=yu=0q&`DZOVb)r_m5U(&Zw z7g^rW;VY=T?4;Lc}{7``9zcCFl-BHl#7^>fAh z5$Q0VhY%xl2$k07Z%D^Mikt;d(clzu0Ss4)FhNP+zg<+PyA*>qiHnG*-C}NEIkw- zfxQn?5X!xZRrKJFB62Mmh%=r}<6V9o@wQ{)BA@!1ZO=U6dlMUcr5WP3W%vUeQOWF51>asY2sPID-+wZRARkJtdoJ5L#YxW74srtiLFZ-Ps4{EJaS(8 z68qW$sFQzJx;)iTe_J)`9;P8TXm5WN+kh4`&3a{{+;}(q*UqnwFArTpju1bhy$|G5 z^>UQDqh@NqA+XXNEj8rxwGpUCrh8iG{pZggOP0(wJme=yOmQzy`vKT9Mm;<{GR6 zu2geRAfj<|Vh`wiS!(B*eD<37<4lhwMHp9h@JT{qHG0_a8sKPa+-Q{hoTNQ3GAHy7 zCw>0IM}Dav(XP0Q)0nph%&>FZ?(e>t$cAnJvGVe-V@AJZOza)zdW~3AyeTWgap}3AL(BHQKl;nL3W6RJ==4?5}7cVxL7 zJP^3sc&~ADJZn*i*Spa7^Hk~StxWx8^1ZeX13OCKXqYqFVX_X#745@iRR1i#KP zQcnF1Z1;wlyWeVsjF`lqe?X60er(gitK7O|{*(hhXHG+T;5Y&le5sg)f28NkVf&Uw z1oXSluPzC)tV)vbX2qH7MkE&WWAMAT<~S}j(F-#V)q-=n!!?2lk@*H!4vGty1YwJ{n5$DJ4mwKEL;S zeSiPG%+BZY^?W=Y&&T`qdc85s3dSui_UEH_cR)et+?u{-VIwpTzA(pE|6px=T2N1n zyT?;YN`P@td>3l=oX%nP|LnEp769g^C6NU`!UEyI}#pmu2%U|?D>~6{F^&qRW9x?~~;QD3c zhdgq|;p_q!y~lIH{W;yEBw{EQK$Z9FaSdRrT%OrR$Eg?SN1$N(?25v{;mf*kRsWL~ zrpYkaft82b)KG7Uxo~9xL%JFTL^eFLieCdF(SyUX`&+y#OIG0ph=k7+=9g*w7S2$9 z1Ezk$DD~gqS=aoA3tL>rF{k{d^b*D|GfOI@xVv^f!qt(WkbK{GR*v@-9H7DlEHj7P z@?b9=MFr7j4G+a(Sc)R5TS2Je>@m}zuAmYm#X&{zXB^E%b>+y;8O!RhHCAu-_>O8K zT%%q)%m0|sNz`}sdh;=Z^>Js~pOofH)#BZAMABtO{a(Rcic2h>4lfds7nJRb=j@b} zFb9Qxc^5rA8u>Sudn~$K3l)BHr?NkkK`J2U<(yH!^+w9N zl>icFC|ki&MMO@*bKCjmXWdy;ti5%7ZrYqh=^bA?PENSDdyWZfX1Ou3(;`*&+s@XL zj`%-b3|IN?>Nn}z#COhrJ^HS!hL37$NBIv$~W6LPZt4xq_HHRlP7z!pov+t%@OSTYxt9xvM~YeX3OWWskB4xZIT!pYnLyN{sc_1kf2Cce zr7caQNjKqF&Y1SN+Gj5xkoawmC9hEWe(}yTt1Kq1mD+6^uwtXT-+vWW-QS#lNr9H- zirU?=QGR~7X&`8Q7!s~)VosLE5y$@IcX{oil(?6r<>tn2cFb=;sg*_EUZ%SuZ9ImY za)CLK=jcYPv?wIv?y_4C8KzS6HRHJ(q>c1H_RKhg16m%(EO^Q`qlllKDl0XW*csgE zd0k{NrM>Pz>l*>N*g@NO?Ce|QUG5=^bdHtSu;%(07;400HvdlPNAm?>)_%Ll?#%6B zMV9whpUiS_gou(=_@bV^ZcmWX63vYQB_z;DC#u>vKyd_keGG|AzE-+e7-J zgTv3>CEjS7g-?($!)tdxX_|!zhnGyDJQ%t}=9*pq-YGPf3k{QC#@ylV`D0ESo{h#S zF*Dx>ynK-IqIi2ovm9D{d%LiZhYCY_D*N8?^5NtPqR+LXk}`^ake9}>L@QIY+OW@r zr9(i6eS%pxrz_I5Qff4cA*!1Yd}!(9bVY&2r>vF(6d3;#x)UX8A;WM**LBRR9|;^5 zeBCC`L%o&mYj#onB3q4O7MpjojrIqyRRKhi-c+cbH>BKPuA5G(SI|&LkI)TnSy4-} zStD2G3`O@3X^uW&**wRf&*1|=6((R%j@+sl&(8P80b4G+>($BJBuRjSdW#YD12ps? zsk@fu9knDKy!$O?!)CwM|+w7fAX^la`@!E>kKW`Xvd#E<5a{|L6<*#qY2To-)l!W%Fl z(z~x(JcKf1vK{6+cLV&_r*VrZb?|}BPMbC@C3a?B14fb*=4GHHp}Ka2834fe5`{{e)2|Cfvr9mKGUgl2mh8Bwa%O%)i1qF%TP5{P*HsawAy@~**c zD%_(LcoMF+X8-OaDq-_|vEByrPv8jVV8Gt~`*THJ*_)wU0I_W!<^$2Zt3bifb^NR) zWq0pKnNUCC&*ITDYniB;$8PZj$K|9cL-l*lHfYuQE{Rq`QXu{F%iWxGtNyF%l863` z^o6VD<_{MF7Mu>k`rbkL0xX?TeYH>(X>>}d#6qREOJ9%`*$E=nzDy*T)Q~R>JD$1u z=0m}qw9x9NmELsUoBs#`Pdo-+1EkXW-<3koRdiy)@9L)eg0;M2V7_~-b*$l?*ieFi z)t{J`2Ed~9X*BG{wQYtzwe4xJq|*B0|8-Ua6T>gPCtsolm1WFNU-afZO|x5`@}c}l z-JEdC1vBDAH~{gu$oMol7h;>-7e?Mg4^N>F>!PRoe2R`W6rXOFsa+Fk%qPSE zICI<`)z;de+fkm@RXDKyJcbXGeZ;d@DMj>>uyZK$8yMl1|A3L4@%BYd+m=S<8*`>5 z{@G=tw#6@l_F-msSiem+PEh+9AO`QMRem&~XuQL5H0&gfv`m?3JW+IK)JF&}arL09 zbfXP&YCU{~yL5JGouxe%?mrhak8vIEPwxFeVc}P3-~Q&{*|EPp`0IS>sR1y`L3f@u zt5ibRmtih7FEdk|a#J~P`QBL7tjSyKVF`w5Pp?|)03+&gFq{oVy_5Dpu)XFw$SEA_ z8*4>xz`Bq!gk|l5!0^F+@F`Hmyh)M=nW#Cv_9XMzeC+qCF&@549^@Sm0sD?iZ#U~| z9pl-29+wPGcih_+`|W$D=*(BetzhAZ4ON&~gXzyQN+*s&W%4Y8wROB!k{{15p!=ll z_s*UmSJl6yl_#jYT&`23I}B{@RorrY?D)-$Gdv%RiM!@Lc-8+HiLm>JgN|d0HwF7F z54njUhiBCjjDC_&TSa-x!yL2Aj#jE!>Z!&B=@t`023KDIlvpYIoR&Q|amG%k@!d-g z5KfP1jZ3$bnX^MVRAKI@fQaenX75q>nvxAQyc#JwS~PYBTevH_Fjuxrj9)s{UO!X9e5gCHT-ygv7o|K$-5i3SeVld5fuQUB&RLVa zNB_c`)S*C0Y2g15{wgzI_)$>`R4h9G==h|(Ib^|Xp~&o4GUiy&)N_nuFv#9+m@D~E z{$>5YY7JqepUTU*3WDC{=ne93k@-~MwD|YhHV+S_hT=w{R_`^82W^w;^aVOW59B>K zUcmX3vm6Vs3}i8Kt(6y|V9eN#c9A!>R^Y0$DDNinrt^s=EmXsKQ;ld&C&%k{VO&~_ zq>{FsUglCFMct_?Rv8@CwU3$}88HSq}w;I_vX~)jn{_^FU%8nXJiVofQTD#^3_z~fo0&=<}cm~An!GY8Tru~-5+D9P^;zh)* zK&eL|pEGZ`!89bmwxg>f*X%>ntV(i@KI6RU3l%R6W^1vTXvWe$E8s&%)8~eL;i` z`Ht^s>kNRL$Ox7J^82Ru9w3c93Cml$DP3%dI_4W;GRMETyMGwoTiw}X=|DxZZQc0p zcnnJT?;M~^#_x?|xFy9ylEnSM3Vo8f!eLIN97%AQb0hSM9)95*_{YywfVsbqyNxMM1$mrHtnO3&Ml9q;s8j8*wt9=2bPJd7P{ z9i<)P`hhsroL;Cbf&^c4`pKe3^T*0}>5SGtK^%kWT*Y zzf{CZtNN?snEOQhmG=S9uP(~VR{dojIT+s;Qhy?L=-NsU&-l*XrnNBi6@LOv6{n|- zKd27UlF1#*fX$%lV3&^R??onfDfJKlSV0ky?4?-3&1p&8;3Q)UH2 z|1&;4m{&icekx190h1&+`8fDC`%v>gy^Ol#VTbbt;@+f&%dTWX3D;Y_tU+WnOZ;O! zUM{g{k5+zclZbPwCB4n9_T%fPb>j_#05t{uWMK2CMy)^pAwP*Pjfk)S4TPCrgNBEi3O*1IFo zv;JLGvxL~>d$y1s?&0Sv-EYDUm>go72$w#o~O+2iNO zM+U*}DVNu!ElE`|`)&H4z4p6(6gS=j<$d6on5zbm4Aec4KdYx!okA)#l$8?{sTP zkK@-EtK_M%+%(jt!9^4SVJH5b-krBxIx1zkBANv(WwKS6QxR}$83voaOGMHkgUfBs zt-7S!V_2A$l?uuzJ~pl=P$H#a_zOLm z51}Obb6i!X5~1SzEY-8|VSUIf-s24$O5Wz-%OqcQDi}JEPI3}ySe|%CQa7B$HGf;!4QbH>W8043?C=kipqeSO z@S80NTyyncYwO~ck#}6R9aSOQOy0v>cnN*Gz-E4Np{dfEwKB5Wlq9f?(c9U}@lXqg z(<<8cDu#A{x(u+mX(YK8(jqml#1u3g-IQgk-lDq!cyaC=5|P<6EEk@A)4I>kt`b8i z6~bz8#987AW|e1A`BbL;b7}CpE`vQU9C4=62GQe#!p9u1eo-bJmWxxW5rvC;5DxVS z(ZGbdYxH4mCKorJw9;eN{iA^6@s>EN_aDH|SfK3e4}oOcYU{+%eNb8OK71h(*1J&6 z$`a6YfvtTNqD74kE_#PYoDZZHohv8v#=!#V+FWC5s145M&7FeaX$y$V#c~Q?LCL$6 zgnrUo3?k2!F|iX+t}SV9FlD2l1Oqd1>iSpE)X}@qvO4@HSSO&|a4_rH_yB^vy`NwG z9o+w@8?YIi=?Z~t%8u*5wu!Zs5fV+d>2f9{+ZJ!8t{VrFS7*mRnT-!|`#?vWbyccu z{rG*+9xzpuMB6EcBieE2;T9#_`4X6cm9lv@ybadFLZW-SfkU)9-G1u{LiXrXl-_dR z8@4XT9X#KP9B9Za61#)9p1wZ>wdDo=z46q=pDd)%Q;Sa+`&D=(uO(GBLE{eHNiWq> zHY@+K5IjK5kZ>o{{g|_Mto*{9>ri&f?iOF&Ah{{&d5Ymbh1qX4{$kF;_I*eQz~mGo zj4_upPv>NtmieSv!49tT-a_eKg(_mblArrCxc_1^KYg$G_Zbi*9+tDXw{0OvDYwj9 zx&U@Fh^y+*yar>2_j}d{&bTy}ZvWC%T`A3=9B?yXFJ1WM+Zk9BUm$t|G0fcdzW{dB zpYOAj2sWy4lN*4^dq{1`ue*_#IRd_SHF``*;_bj9{{^WV>>e#FSN>MhQ+jK`qmb{v zXVF6LX_6yMtAB^|?buc;2HJ1b*FXj7W0MeZo0enW&OoQV;Yjy0(78toR|NZw;UV6K z6@ptUrHSvCP82O*3XfqEwA2WvesL+_9o$CWT7TUFU4S19cOnF_ty7=o!8plpDHB?; za~QCTWDI$AH6Ip{dpUlMvpR+B|A2OxZAdpm;>JZ|`2#_F7UfbmL%e9zv0duX-h~~Vf`XWp zT~m;kB0c&Ow*=`U^8>qjy1=%ck9lSw4-Poo?Q& zEa}a*mi6y{kqk!8(*ryVrcn#Ch5VC!yoDN(1oI-e?30PoOvY)&@`EsOLMaIve2+VQ zz6H8Q0NdmmL_ZD1G->@*RRupX$3|KSX0KcD!7=@M-NXf>Nia4|E`mTSL^Gr?Z}#}o zS6=M#9c$hL%Zi15ey>iVwnWw-0arNqFR*)V*2Vrcp3FX4rb7OeY>PcOi!ZL8WmNkC zVAR2uk>bRxUjyL-7U~B&KI(URZM22RvURMn5wx-(J);u#2huv-vr*IkV>eCYJ0k#G z#CJasnXMbtS{$RQo73k}qBIqguQAkuKoszS|A5MrJKcG((AI-1Le?-7KJl9x-PRa% z;c+U1JEGY$7ArcR)1}+g`XRpwa^-iL*)au*P|5xustkkGnTYfxKwZ=wi1?GDnpWwcYQOcSvPg zMW_JDe_n%DOr4^AQfhOi;-bYsMER6D)ISMcZMXGW2oO(oVE^{7=5y>m2~^5F8Z|KA z3~GVR7a)-V1t&HT#0C&o%U;GoQOX#W0u6P^+(bCWX@6?o^`1|R|EUXgU7h}s?7wP* z5wV|n?mgswuyB3Jpmqnz6E1F(0+qzGjO?FVjshh4fvGx#kuZL^?=+s+9mV_d1)cRZ z7DJ>&W)9Uf$Qn=5%CEYHyd7$+(O z8^Z^x>(-3+PepaeT7G}26*tjwM4}MtP9}yxGt)T+{N{lm|C7YBZX;0pVW~}%be{fS zSY2^Y|C7B(?6&44IKRw5WdKda)@I&c*QcJXFfRwrAei5g@hr&oDOu9ehAu5MI=$~X zGzkuxzshw&LGyHy0io7uyIJ=Wi@#kcYy@gh!MCq73I03%jBK>zMgq6VF{286s02(* zO0Zxb=#Fr&N9Nd@hG75WDYIAeJ-o16~tck(1rF@7Wz~k(mAB6m4;GB(dGekA59$yteZ%uzRBkeDnfD=ty zWCs>#_iYOQrbP1Ag!f7;X6@JiE9=|*zee?5O4gchkhg}o6$i#{A<<c)ONF?)npj_YTLZ7!H%`45rpyK3(9%el~iTFps_elH=*Jh&}vcSFRl8<)ZWG z-d1QGsRd}SoeYJpw0JS4W}F|U3soRE~}>Ux-~#A-z6q^$#etg5lbE9<^;CDAN-rShsg z-c>bL!nT9#lKA=wNAPZ8ZSZF% z+SWdfB?xBS!E8Qd_^_tCXix4cSdahOnj>6UwHXc6yqc@)-zBoU4+j;1XOphk9v?`k z4;~#j)l0S3nmWw97Ao7wvo>Cu(VVx#Y^AHC-IAZX&&#;uf?JQ+<*FGSCj50_pM*+W z*qTbq4tt%O`r*>D=j;zBx`YRhf=9fT|4_KLBao^2@>bRyWB^)&#wp~H8$J5REe*1i zc3c_iiiM`e>Qmh9P%|Zd_ZnFSi|+Pyqlcek~IGFDD2Fl)jI2|&Tia|T#M^lpp~FqB@6FonpvQ*^O>c-_Vd5k zzDCZ*zn#{4^i{A<1oG-5uvOW#CsjXta%u1s=rTrsg^H+iCX3*+YzIaGFDqJo9`f@8 z-Z1ZiNVN#_gqlsz4RuJ9Tw)t*-28*Lb<2Cduu-KG6lKls#TnO54`(Nt z1X*7*({gzDdCmEWv^jO;rA%trtlOy56au9^8xzIUE-YXu#uhzJe#xku2_v?LuDCzY zs}E{etpe^yTUcr6iku5hm99CiH6Gn4ce3EK=---;J+uiebe#5kIm#=&?$-)0;#%(B zi|8%*Q9hwg8P%Q8XvvB+esA{ixn!Ld@n5ay+rdjBUiZcwIf$JM2XzI;dBGdTjJ~bn z05{6A*6m6iC~MW8K;1J)@d$UZ;YnRkpmLv=SkyowG*?8qoA!3Q+1fxVIMKjP+h#3X zT4gp`)G?}RkYymtCX18{S;?tr@tWU)4Q$Q+khm$=c{^DrR>S>SoEmE~l@b+2>Ch@X z@h(~EhH|8-X2SYCquu$J=49obOg35wlS9RW&>F_(TjkuB!Hnsw*g^g#jRazXM$f** z`uOSL-R>e=cVpD>4)>yZp+TJGb_ErofY?!Z*ZR<+V||Iah+-n!jlZKUbk@(+&tua13)gIT6PD*V*-aj*i;+|E_ZicQu~ z)95}LD7q4%%+p^`|HEz{MOA7>DOhSZ^gp?>PkkyOEoU+LVt?nOx*c~W>>cx~BVEgb zZf~La_1_gqc_EA}6bMHc)g@{MDRI5^5}oFZ3Ah21FTTV9+m5}FdR61|*ikY!fXFUh zVxNA^?n!9Z?vPVulRV_=ITy)T%j4MnWSbi@ek9r1?JacsTG|RtLJs9rZ3#J0`Y9@R z-DoA3C{F}ZV`OWr{Cz-8@xxkXr3iqjZBVC3aqPh~?()gV`Ea8}Mpl>j?-oD)ADw+L zSpC)L;#qV+dW)b%eXi5%BjD`I2pKGynf$$$6)|a@mK*dh^Q3U&KFt>CSwx^VRZl%H zA;M2zHqMRfz45ghM@F6RtHhAs*7$L`o@EY+dq(X!Yx6xd+;P1mhnq#BDDn3YH*NZZ zM(qyR7l+nc`7DJyem#-RdDh^ffJMAbYFp%leB;Xe#FI>$qQpu%pug)v&3k#@~-8z#Vg+ zkZ?PZlo0)L!jW)lUdwgpkY=hnLCa%z3(R(~AgDhSR$~3#z_r1Rax^nQ`VR@h6q`R= z4!nrQx#1Mb7Hyu*YcpO%ev6ismd=Hx3C%7F!Zx!9FZKm!Y!sSu28A9IYVT_%w34+C ziY(B^Z4nf@(qAX!saC1EWZlGCp=xRD=(t{3#0b|0k5R?yPGj=6_>Gim-OlOf zVwjmcfrE=+Pgk^Ylzr-oAY`{!IdjqEh9>tV7cY*uIU`@d2nN?tN_XC4NA?f9s8yVr z$Cm2UHFtjqc}xSzZQQ0qTVyz)kg9g6!6sm8vv9+>T^@E@8cRNFJPd5X4v8W}x7ia~ zYw#8cPJ|QN8b(`>QX+MYd5Y-B?`Jo!Fd<4fo_AdNcOV3Yl+icbCUj$qi{If?Yr|H7 z{mIR_&Z2#*M0L+Uf-)x{qUd_!Rs|$=XF$u8SbEAH~6ICOBzm(iTfy9ZS1e zNE9vBb}U&#Dj7T-S~W4?x99_*_GL?Nto*#Iv12p1g@@OVjxq9!CJytM$CCSxGq$g= z4VHWMSnCuB4--kz1p@XN4FebZeWF!);t$hy+PCtYMVUPjU2nf`7xzE-XyLm(2ogky z0uQhEem4!szDOZK%WrSoSmhzl_N#3Oq3SKBnz09u!^BD<61#I(4A0lRqTGp6Kww_U z%JCYG!I+LoN`lQh)Yguk5nhGEh;?#=YS21Ot;R-hhYk*F7u@@mVfuV9CYhwqBVbA{ zXpt_0XL`B6i{(Ai&Aih-eX-d1{neRl_K>%aiIla*gP%jHdO6Xwcm2XX|AQ>f&tZM8 z&#bFl%l%Zr&9GM!2G%gu(v6RYZU}xU#5V3po}ZHhzW8lRvUM%re@_cWD~-Y{zm}?* zxKDj@)P_K~`Z~Ypd^S#FpD=TV(w4F#SZJfM-|yujUuG~lI$PF@ytmn5>4dbx7dN#` z3858<0Cm;lH04e=qtQdeq4j&Ej7hZ0C{I%HhIL~ZrK*igJ)pW!SzkrMbvR4K zl%n<+ncD$_S$k9%=_Q-f8j1{H%Kd6`(c~h)9Ht&#DU{J0ijmbtz&(rdlRrLw?J5>i zX@at|esCMj2eBgk*1{dRfZ@jbqB&Y5i$W{x&7VskYE%4R5X@WFpHT;@pBt(u!8$&ibcuW z;Mu^AL^*iUvYI{FhrAW{ib2NxQ^o43&0m9V+}>?&(Hb>=DjoEQJ_D6^9-rt^}9dr zuz$YC^wqTBvLD9njD^;gL78D}kQi@ES}yv@4L)P&h^t{_4SaU%i2?YM!V?Z&*k=1?{M#B_+r3y;Vs zZlf|Ic}omi@c&vK;Z4%g%dmBUt{s{ava4SSh7`Xt_Ros%kGLKMxR=u}?xtlM0f z<8vzQz#7;l(Lk9sA^%HjmHq+P?6`A(;UcTudRBo^Ekmbe?I>;un6;i-(_u@kwsu|V z9n}{k!0-5{kQ>t3JfZ%_C3gF!Jm;Rn6mCEgF^*A(AeN9}4A1-~Q)SW-zA#;POR070r+j?*q zE=RKQaYBY*ZRoO_i>nvEAoJ@B3_R)QHa?0N%9k4i?}`dQ%_B^@y# z>$R;05BKsW5U}X`_QO2=ovp*!uu6l7h=aqa*`8!1p!1Q&x_Py>^8zM=}kbGyrO8%Wd^~yTani?yXIi=u@>HrT;C_XDsg$r0b+abMd z9Tmq}Cg}$`K&zh|f}0G|FyFSS;J z6>eh7!!u7+npi-_v70wdsV43$3@JP4rBDVPbW@%Qe=AOYSE9R~6r=D(sB~<}{RW?W z9e)lLMIt_rIY~qT=F938Ulo4=epZpPttxOlg|b1|oBKOCeUM5Ag7*O|%AWRStz677 zM-AzBmVfke+y)#ak;P+FAdw2Bl$~&jFT!|)elCQ>jTt*p`uux20qJQa@9U)C-Csq) zJ|!`_ecK*Uy{^E*6{#CipXIV^H+gyL)S#;#)dZ5_tp;A}iD4reSQyp-c=r@Z68(5P zDd3%}3)4G8jH@V(fnPBqm>AjxY&w8-*`U^nARWCyXz5=sps$NN7#_0_7R$!x?3*Ds z5#GC$phEFfpFe)sPdTq#x@9i!V5p-%Tusw5v!`}As}lE8x9*CAC?kV zP8l{le6TP-!Ex3u^~4X|c8!>_NW0O*-ji^p+3^|IEZxIDEU{f@o#hITF-|V`?!p~D z2QNF|>_Xoq%<6pHec^w}-TF&nOyX2o8Y)cIdu0P(GKa%%EwLnsx!pLl4a__Dm3=Nf zs<%*YJ?XkEY3P=T+0h)C20a)8Wj=3`vikKw0o4)~X}&tBg^hmAW>vxou}Svr9;b8O z1j%5JgzN$;tB^=6T0BHtWAf0wwF;r*C%8P%|DcxbO~ZIT!>%G0BJIu$Q221hpl05H z65jn7mPf>+nR#kv7M<__v1Iv{cMpHDExY>9XGc&dUG$3 zLQ_M*5w+@a_UyEB{x51a0hKR%V$bloOhN$%P0gu7!vIxhD$-pZNNH%)Dr8rY$p^3_ z#J0mN36qOhw;y(+hH`}YvdLisUZYU2TKJRj!eifxe;TW@K`u5k_>_c8ebcpnm4ll~ zgJM0DE?p&;C7eO6`aOPME%u!+V6Gt6%Eg1_huXKzXe=M9d5m{Wi#JPcFIlf6#v@EG zqL1MXXI;wQN24N|pt6tuFz3o-r@g^3+KIhb+R37}Q7>*%@JnELx z+o4`*3S4u;3MpkVXqp=*?))t=u=Wb-q==aF*}+8)7RW>P8tl4i=Qu7I*_jlHVOs8p zBO+p67YzsF^=!;zQL8wX7w`#Y@5O)?G3=*&J`i=qK*MmtJk+m*1o#ibf$lrWoN!$b zKNPL>bwhn9W~L3fyG@Ek;C9m*#1U^ou02+1KxtD0ve)qSXs-EGCFssy2u!%TP zi6GTXHJtw%(^u64i;s9?_aBfLL5duKzI*P$_SNNof7`@PaW60wX#y9R=-;^12H+j= z(smWPx_m7yI|Zr&ebqlQqBP7QzyOxm9y;oqsVK(b^_gKvwLWLX7mn3%jJN;y>g9Xi z;&IuNkJazm z=l<>?{fc&N{Ec>H+##J9kk6Se-E7aud74*1wgwPK&gjkHkV66zPH(%@@eS=*P=6LK z_uJC`kz-E(VntcvOfY$dB(vS-Bu*ur;$TY!92`AwCX=PFUS-LN?!%YjTn|P7+_ zXLs4d=m~wz=K!a9ev0+p0A4sl>t}JDT3>FIdqKePfMs*UK9S`zz5VDkV#u^bj=BD6 ze0$C_{E+LMHGjs5J7tp~)(^0aMxImHGWe@h_H2E;X&uQf=&5th_O@8*D20m2Tcxwn z@jpoO_&q+hODC$0ojwTTADJseC(j-<^*RvLh%Kli=Nt|yeO=(uuYf*ho6hdX_7xU) zxLH1-Oupcm_JmzC)|xOG=Sh^;^_H}SjY4ptC1yzAQ-RzT@QSog`h~&#?t$TBVDi)CdBYA+$Y#>H zlTwkWhL`!#5ubdw%gcbz=d^Hy?S<4eso6S5DF5kC#fx;jE80#reiJ7{!bFGu2mDh z-3to?-_JDNnz(KRwBzG{KU~26?pJrCnRM~2$5oRQ;=NW5$*pA7i-NDs!+_WwN!ptI zT4d|PqXV*s#-k^!v4lg*<;f#Aw~1Zo(Kzj^nKABJ(Vly_K4>c`%1iFs1+__LJ*TQJ zE+|*DOSJs6KK~r%JGk29>DxE|u7wU~k$sSp{1W5fX)q%8141p)#u^H$@>WlqXi}Gx z(d?olTJMul;3#j=#u-UW#F8+Y`8zC{oB}BrG|5z7Xnr*(cQu{r&7xxlVe*MV&~w>u zl3c{p`*1k2!CZDnv`M(^S#NxyHT=lzWF40$AqeNV7nnWk^AJqr>11;OCfs~i|MyAg_&r781z?nFc zaXlG5hK*@h9%}~nSGvNe)a(b|r*XPyEKvtx1N3P8Ugdm=6${SmE{m*)8CBSBRT6`e4kgrwjx6W&$ z4Z*M~;IVc5%6`gr7&~$Vu8#xs5pNt~;rMaJtS zi${e!nEIT6o%6q|dv%?{2EvmmDNew+i@mJI(6R#7?VCMlyxk+)g#|xJ{xyf?KkqH+ z*}kBi{FYIEb$~6uiQTy3K#eLV4#!Qle!jQvPvhOGgw(eR+$HX($Gr>skZe8OdEmDs zvv+k7uV#UMatNz=Po0^&>1d%nEN036yK3wi>VV1q^nPBiF$13y{vxE3;B7$n_P_4; zFv_u|g@l4_mrasqV=xBPj)SP;IR3qd;A;@13Utu5{Hmv~03c3CObFrfHh?D~VMw9- zU?4A@tTY5HuFYzB_pH%D(-1JMZPnP2dtx5LctTGa+-6qDLM^-hsn*e^u48S6J8bxW z)`|ZQ7)TxF7EwcQv3L<9>U!;sm|<27v8R1?6Z|R0sQdb^y;OioqevN4kWn~mTtny# z!M}rrQN&bMNH<{hPtE@hAm=A|;8`0NLs$~YO4>kM7Q`j!dJpIY>tg($*n4SIRwsGX z0&c&u0TPLeI5I#eW91E5(Lm&qxMA7mTJzmYoKuS*m@=@~BFkU!&t>MYv?k)Q{4Xh0|QeEVa z$+cldppl~WL4|56d+S2hP??8!kt*sKY!>KhGA&W&#}+&yxup$g&dcJHlxLKf>QnNQ zUy_H)oQ4gJaa;N+O2UVa1?+E;7o&D5%xT$KrBY5rO@@~Z5hfbJQ#7dmiiXD5%*4NJ zSz^8P($*%_QKCEDeU{zJ_;pFz!>QiARt|UE57EIR&q@yzN%WrQ;iQ9Z)0W?IO@Bog zsdm(wUz6J#D;SA^p(&-(5A;UWoj~@vg=kWU$^P~N2+vqmWUCQGAgIDf8;`?gc2O#) z#FS{ofl|DhYF-cw#*#n(Anzlf@jlEvKnLyHBkch%edTh;v-bF0JS*sO_o-`$D=!nx zN2S0uGX(iFFqI=6IZ}+=w1bZ$yu)v;YQhWt&byz> zH^~^eP3YL`>?fTAUw(C!YV=R(7s8OGD&Rc?{7^=uUM(S&7W;!n%g0qJ$2T=7Tn1mX zRCc-e)6UB0Z|DT@iba~NT=JAo0 zz`Du(mM90>sm}`g?sc*Ya*I zb+Md zxiGt{k7_{|o0bzQ4pWpU$unE0kdgXNmP+d_@6>aBdu}e~_hkwg*}8}QV;%W^1`U*B zT;V;jkCScWCibjr!v*>_&s0-OY=mm64dpF&RpW;*FuG-9to%~_2ch0vl>B5#*9M_s zd4t0*3U-<6yKCy;mcEIg8S_+`rHSOeWC`~? z7hI$#wPm!*-(xnARs&Mh7(&1RzRM4P%u1$ye{v7~67R`#sp&uY6;(eCQvG1qulHd+ zi^Mi>)FGHj`!5b~O%33{s&=CO5ruf8?DY--02NiGQGuSuU7-fu2M?(tuxdyH=vd%* z!F0BAOz!2v%@GJrpC9Vs*DVp2ekO;9HoM*Lf7Mi6d4bhhmtuNGCLfzj3H8)GY@4JG7rg^xr`N(A+so%k~A2L zs1RlLt$hyF^SsaV|K9hyzU%t0OM9=g_Fnh8@89np*4{^UU+<1X)T&AP$#aVuPZ0q)yj`}m*> zAzd&!XMt@<&}Xsxqi^*`-}<7V zEd(`R`91$?HUx>Ug7I6w$JnzVh$rmbZ1YrPH>dzr4Eggb5?SO34f3cewO!$S{|K~k>`0b8C zXQ2c5`0x*C1mK5|n2?ZwfROayK_X&uQgU)KQZh0MN*ZbkN(3bt88tmMf)+_fM@LR| zh=Cr-K!c=1!b0#4fH?$&B!q+{ND49vLFZ^(l z`wt;P8+93@m%i9_9$+p_YZsZRy>N1|`SV0q``jnHI_Yy?K7Vw7UM3Gdv7Fj;hkhxL6UBX9dxxuh3t&tI5-O_&S^9^67e%m&Fp zP$1|NUFqxFxjHX?)U<*JPw{%74X0UXEoM?ZciqF-Px|2&U)Xy$e<_;Kv=LA_EjV2urwA#Auwa}QraW|4nD4I3 z-dx{>3LL~;j&T_lkw z!)`EBtz4b-hOQ@p?*#puthwahi=slB;Cp_G#-^j8!Gy|Jd?i z*qR~0fEYZgmATOC{6pnUdRv!{ucF>(g66A)+p?Dya914$44hRHIxOU;)VP`JW zg))v9o~i4#(VIsf6TV_$-K6T z*4M=TX1Y5s#cMj zm=}t=HNMd}a-_b+V;7R3KHQi!TPyS7lM`X6t>Z7T#h0dU`4w^CmuS;m&&Ka3JT34iBntii`R$Kpz#tXykq*9Ynp#F5)p=W5Bq$% z^k7jQs!PjyvZ0me`Qgk;gR(j6jX4KGOg0CUgQ6#eR3V6N4~uGBZ@aKskC93~vja0i zMwU_5ct~Q7ux!ijYP-yZiSR?CtFBMXuM(&znD+L)_v7l`x|+RVn^zvE=+{z$r>#wT zJ&F)20u>U{L6YI?fMOD=@A%~~Ns9hdkML(xWkluk)Cbez@nwt?k+t+BK5qrIUQhj4 ziy3Kdr|xw(+oXE4hB}Ap)yVt~dxIbF20q^)#|>keVEiQL5r+{#SRU^j+Cm<{i$_X= z>e09(8O!U=yPzKND(5+`q+_ja(VdW^7IKOo#aWPYiqy|~x5oy2vpP1Y@;{5apk(qN zXAjh7=+4GRae%nD6%gPMxOcAI>)(^%QlWBH0gBG+aQ) zMY4qF@)B7eZCrx&y94WY>ZM;GhW@F%wsxR)B#Q4&!W%I4O<~eojex!ftv`Cx0BJIj1{GIm!UNI}gK;-q@ z5FX%QCcwLjPEs*9 zIRq65xhOmiYJMxnp8?9tSx0C6lR$8hW~phTE%N1f@X#1KzGiKY#c6lfDH7oaP>~Lh zjo)k|H1?qUip_{twQ7(4^OJr(Z_xdkEsI%1vB{~qBo?gU^dy$}A36K3tpso{y$_o+ zPHt3G^#hT5=8QaPWOfbd_27bc?cW|AGdOdRM5KrtPY%MzzjavM$Hg7)*|8P!12 zNIHC5JTe_*;9Uq6s(Z9{y@biA_h#Qw=NU8Pg8JiE9v{z35`^bMRb+TbMe6vQPsYCP z#Lmb_zT7^cJJJ{>lmAegS|>aA8NQ$CIc{w-3nWW+0uSMFJ=UBv_)winjrDh?7>#T4 zs~J>wBhQC~hhwdi(|(LR7n$-_V#+Lj=B8UwmzGT5C)=Z7$cjQ(pjlyTuPQH>!`PNw zdSA)xP<1_<4pnkJxQ3Kko6LrokVHkq&^Qoc9irwT(!AWa-mrcD@T^Qt`RaY&Cr3M& zIHQvR0K7p!T?I5!@j5F2)?+1r2McUL!#uamBT@_ zo3oG01wxA5Q#FT@!?Q_HQTWYrRRn0{IbJeiMM!;E>YCMZKYsHWROE5w8D2Comhs%fz8;%3 zdFuwTZn7#viyu@oss*C(xV6zXgsLWiZ^YldsO+Be@pO@>7a!uf7M-P)a8KK2qp^wh z&G&ZpXMrIctbqii!iHZuY;t*7gIIET3D9)Zk*rS<3TXV+eMP^9%}Hc7DpnPEIJTGq zpVxWKLuwL)I=Lg@Q|z)qqdWLS;L=B#`Qm*1XQc7As?c4C0F9T>=^?;J9Z7}<&`Lr#Ir;uPVFd_<)yY7nP6(3DXBb zgtrLLEm6=}$TYxhjmyz^sz_QWS=L$f$^ zNq0`=4AwkM4W1+N&DKcqu%AE@9bvWY)HUxBuDcvO2ae0(LA-dDY+Nx5X5I*T#2HHt zYCtPTFdE6q%Nl+NfaVUn2@G1nk#4Pgj)Md8!)J7YvunsKim_^)(eBc|9taX1N2uD- z*H2bR!2E0#1=jEi-s{bZ*Fnhfn;&hQpWV@$dPgH^=1_-f%ZF>NM zu1hfHsoODAuw%}^c(5lD(!sCeD`zeRSucN#2msZz`tD8 z_54~4vZzEQiN~0OP>x3tAHvD;nx!!$u9S-pS41cz69$vAS0EJrK}->f?|OC-HyXo1 zq6lR2kSXL4Le#u?7B_(|Ub70Y-$aJfBNW(kdC?&ITKr80olGJK<*L9exgrvWY=F!4 zB#_f9v;(u{2r3Tqg1{TZqlYTu!|4G|EQ*K=KomNq^XlUb*(A@OXIJj%QcM;O#0O$> z_%KKgdTs3_LOS4#AUlA(ttb~2-{~PDBbBHa7|C7%NTQdEWQ{-tqlxQodTf^e5xiNd zXH;pTP&}lGbVzUr8x90Qpa*#DkFtaCTVangCSVQERfB+wU|oeZ8UHUX3>VGZ;Cy0k zM>;~1GiW`q8wP1F8|;1nHMl?uvM?lpQ;4Dh88N+rwjpuepB|feWL7IbyNHLB%%X?~ zw{$?KZh6UV=n9z=sh&D7=6VG&$gP4Cd9FIo~1u=e@$AQf{1| z0S{^B!P5z{$j)W~&(ieJg{60qJ#xGRG@wuuu;-$JEUvepqFCy@s^2w$D7==lp+Vj?g?-WWeSn*#}UggK-v5QyV1?K1@s2`?#YGjSCu2xkk{AbJv)?{@Xx zfQxTuSMF<>ZQS^xPl_SPeh~k%h+CWHB^@Cilx>J&9g5Py1auAxPE zhEq0AhWVm01bebdeyw! zW`5@DiiPmj=?Y)lE@zinmkMU*?}>$1mCm1EBfTDEXRT>^r1Im4zMr-Kl6%d>^AIMZ zsh&_%qp6GV0~3@BfjvfKk2; zt^q&hLqkstWRLWY3{G3~zHqPi?HgS@)hE!)>{(^1z9^lU(Rsj5Eg_6(^w#93PmT5E zqY_s%mBX@pqO(rc7i?%A3n=~&J2U@kvrJ~{lb3N#K&}mRT&{>UP(JXWmEEgaBr);;W$^4C8H!M>!B{XSvt?|y(>DMu>`e!3D zk5mpXWqe(#8ff669hQ={F86pNF}BpVYVz1OLt?CQu;CExkkj;~x=Y`B2CKCmH=Jtw zVf@XxbEd<{N5W-%EauMaTIP+=og*@|=Xx}iOMbroI?}x1&y;b#^=yv@6vzuvAsc1Ky?VxSqPN~-&MiN@uQ7Q{U6Hl382ZNdkETUbEA%L1`t6N)Tmp)ndHD88cAFi0k+W0 z*e$eqXXZ+j_$m95djCSp&yrfjUaOx-5Ly0%D1nMp-5x55g(ji2TPe^^nf&lm|sCken zc;1-Wd)}Dv-s!8!bH91$(yLF88+(eIlutIK`gfMUaBm2Eb?V@o)jIw|0(G4qmXc3j zIVel@diAZ3*wpDK(ouz9)*d;0{?@O4RWdAL!e!lkVP+(g>ft;O-Z^a)L`}~D@yHQU zgN?Wvsc#WAA$2_1Nmle)SA*w0oTWZJ9)@4djy4>os)gP1m9K#1y=$fcPmE-a^+~L5 zF>5UOg*J^{{I=xMFRe^J%9LvUx=|ASyHJv$)B%teKypW(8X=Pbl7*DK$2Z7Hsfy z6e1B<2&^ zH}LJVi{Q5<56W0Ba%TIyOS5%sIX$gSZGC#*Jjbq`>F#iKA0!!d7<){qrR6UjZA97l z)_b$E#lyIU+~W|l^T^jDGLn)fIvh@4dTs0doua_;llgU~3lbe-5{GEQIRX(1WOy9C z&vDPa;4O&cdxNS@m&Gsk&kEo3aSb>(*tL8I{;d3-H;YR4^y*sy%_ZM$pGW<-rtEUP zW0;F4noN4knMZLzAo2)2zqnYwZWmw<%P1cQ$qo z7I>Dmntyq4ZEbWmGcTlOeaGB2*8BF-vqg{9YZD{yL_a9Dt?*u3EPR?fQ+j;Fhd01_ z$`3sHmd1o77lXV-3_K!!03ioze73D^pVvwj*HYPyxjR=9?u?8P3rm@<%KVIdv|`dB zn-Z!Vm0bA#;#+S@C4t*z1yt3;b=K8KoHr!NO5gb?t98w}2zEKUR=%9Eys@zI#gCaa zbw~d&CBJu}SLfIUrRLroprMv-T&&E!Fc8I?&Mh9^)q_%rwPS*ZC_m7?bU$qt;C$Ov zgpZ2vJTefdbq?NlC~(t5Di%266V^t~ti0HTk`^kTJn`|qxY-V15xDPZ)@tyzb{F!j z%8<@H3Cy%CK(x z{!rB`pE6lM3YlHT#tv1%S|bkCI)G}}tmoH>>hb<78DZxambPPWuJU>@l*WX(C4#q` zkx-Bv*fVAmE7Fj%uzs}lUxgo{0r3*=m?g{WsR4u!Wxgwo^?zg7F*|&W(>Tkg^Q{D> zlF02KLBHgN7k1IL+eZ`hYaO?_*owP>AHKLlsm~zOr#aH^a`0?Q=-H^u;`h#vc5Wm@ zeVk{|?9unjR5iVvN`JIWAYgdL?MU!wItUi~*=(o+B1RGu&{N+9N$nh{kN~d+Z!Iid z&wD<->&Sb*w6j#FdCYUij8QpnawF{KC(BI=qhp+&W}ODdbQ?N*&aGsa>o0BrcN`r4 z5c72S%AAW6$fas4rk6k3y~qoa_~9rbMbWDHcw4sZ2jeH_yo5tT4fjvoe`KE-74@0n zYn!Q;&C9C6n&%nwef$khAU^1gAI)WXUpOcfRo%oNlw3hT6ORg#3$h?cB&LUezj^QZ zTb7w?#aegF%tYMew3y8I*as{1>q5HBJ=-9E<8Cr)}aspTYN; zD?bYxr04#B_+YXN3G703M+k0l4)z!xxG`-Qq<5&(`9LDc_`$|$vufQwni1`TOfG%L zn>A0THWQQ)=Y0)X_mI8;xn^G4MqpHfQi+s(N)2Bjp``^v(e%ljcA}GYEaOpE0rwm!i^_Rdkq7cmv~``P3hB0v@#Wivu5%-sHC`rN2`+WxFv*d4NAhY` zvqs+iG41`Acdi7_yK#ZRw~=OjS2W>qH|}h?R#z@V<4J2*xk;jrnl-}ibqV)-@5pYI zU1XAb+x-eRglyQqaOJ|hE0;-{#IH;6-T(U_&skgcr3hDZo&egh242EeQ75|shON)>+{{#uA`5kwc zpKVl>r`mLW9ykP)m=l@XP~UL6@hM`!Zg3@SC_Hsd>5i92%W{qW;OC-x#t>z+DEh5r}zd0RiC=uUZ+-EG!yjb`{esX@$~CuSxOJ7 z*tYxIb$f6Nz67lwVE*4UU>ZP6XiE-N8E!7WW@&3H;wtaz%<+Z}*{9B{@;{v(aE|krkg-`cgz|BI_ zveLAl{034!wc!2u<@c;VHJ5HY3P8k;TdR`$&@$>Sem{IPYY9-Mx>kK{b?JX_AW@8v z&rX;&eXFa2Fge@Ph3kxLR_EnRsKAJC&pQTQmGR8uxhL6NRn`yQn)XIhAS#&|B<&`g zr4%1$oHh?bhn-mB`|p-Lg=O#x4EM{I?{XsFg@YtKGw`X)muK9ZT2a8s8Dy1v@=IYo zyHN6b*%NpB3Z3Rz1j1Pl)r#+@2=`n5(Az(<51fk*@On&_N5Rqje{+$&wc*usqCTt; z(TXriiP2;ixVX-^J+*m0NPI(c6DO zXu6a`?;#P%Y%8;Bl|Be~f>aRj?iOC!kBejs$wl8)gHThXQ%ljr%@1# z_@4)0Kd#lEJ$qJB5&rQA4UL2&K){2AWU|#jJ8NJS&ynB-HLpnr{fNn^=8fb-Y7L3A zGCBVj`%YbS2rUEh^MZkm7^lcD;CBarC+F2L&NwZaw>Hb)2tM@ju%%MxCVf*4hp$on z##$>-;_pon3@8(q^`Fnl&CSgVHn><+ZwRDu`6ba-PS0kjMFx?wUn%ZjYTATFsT>rQrSPNJm`h|k- z&5;Kf48}P!2BAGnNB8DLrO!?bn>oAi=fL#~sm0=jZ*@`z(~hZjAu9PUU}gxa6z1JJ zdl(qc^)SV9*b=q_zK%cWxwY}H$oFdl7WyYD&$zTOGUk*%5XA(DPaw?}lsc+=RLn)U zbi6`@6}p!7ExvTDn`FuuQh#>3xUG7A&`C5dSz3iGpWN>RGKaE|D0=ADV}q9<8NuQ1 z_;&zd>_D?}A}hppoFijfcw~vEkbs9=v|Ewos)d){=`*}>66z+b>S2ZnFWn=g@!aKn ztB2wqn-A@Yg|o=-_T`VvgzdiJkws0=F$bf4cA~HM@t!tXp1&gYtW49lguFJ-`aH{4 zY=QC4`=lk?Z}Zp;4ko=}1dWGBO7KrteEF7b^H9@l{)tFEVzdaeTK@rtL>1WXpVS4$ zPS)KXYA|`4l?HtADnjwjn#}`BhO6u@s+_`_+%tgcbDi<`lD5^%#|9erQtZBOW6yuh zx9GLbWr2Wxb(5}i{)17$St_L$BH|d9&A6?PsZ|+<>9#6ww3mOG`!i~Ap|UTgk8Q9O ziz)D!DdrCGlzPNwep&ZlX290L0f5c)SeqG-B&F~i$yAP`*zWnT(l~c%)7zlqNK<)q zMZ@ARh=F&9hJK2ho4BWKS^-AeQb+LsXyoFx#90(?j;t$B0{ZfQVl5Q}#Y9Pzq5BqC zY8a;D4I>Z0_6Tj@c?m{FhNDWR`8jZCpH(~?;3TkN?)Gq0`UG}&0q)vsJSQHUl8V)W zXJgyggHu=DHPs&47I=&$F7$82?aw<>bLCwhwy zPQAyc_-T*OFJm=7@fXy-CO6gO`&#}P^@?x;!hs4OJ;0s)_vYz?bOh}DVb9OAjrGv6k9>~I^`WUoLaF(8O!w-EZW-d^1?1| zS$__Km4}`8Pvv0~JwQ4xnqXpEzzZ1I%!i$l;m=h54gdkbs8=T5RQv`L3~2ZZ!ABmP z0*1ZpN2AY|@Q9!FljCpu?RbCICTtO!qN8B8Agct_FJvYyg?=6VOYj57oSH7maaYQF7#g!4;u3W#TRCb;U!! zqvuQ3U#s_(m%|_#JH;T*gX|xG+5+X;9~(FSTv`pTyvqv~Q&eWyl2o2xj&%S*jsxwV z>mNwM+5j2tw^q5i_3$jVU<^oPz`XsGYDMp5N%4|ly7jDQz$XfdG_*Vs?AVMv41ywu zh#lp7Sk{ePZX6JDnhTj$bg7y#m8zuVc{+2;x%GiF$hH8Cc4hpZ+srsXZL1yzgUwB@ z64_K?QGCB}aq3~!o@3cbgODy+PtJH&je%f9+t*@&+^)~IazR#j1?~i8>4wjkqe|6h zgHOtd<{nH?MdoM{2vZkUFl$mvWoVZz8(f{on~wBBtc=^?6H>;95r~?+J>|>2SlE(9 z`fsOucr zF;w`EgP$6M4h7wibt1o>0c-J$o(oT=u68O{v$VX0NI{bDN%?s%TWZE|mzu}#je_lU z*%VNXg@QUWbjzIS8W(kfBLzOHGl~|+bKzph5reKt zCyCT%(aYv+_CpLAjfAU0*VBvw2v(e;U7`Xajd$K+Yv4ir4#CG4Z4bV=-z-{Y$ufx7 z%Y>+?6~)&-Fv+NY_7`yBvhd3o2aiZa7ZG zr{ksb@fj5KLIb9~T8(&s!txd&`Wz8MINPLYJR!Gr$gLZLmTzl`nWl#xGzYRDyG=P0 zbe8|P!y9>w!t#5uel9Pa5M>^17SRvVsDgCzaWC!CKbvs>?oQC9O?b;*Z%exP)B-b#uS5pev7aD46#Q1064@lw3QEvCAF@o01L{-& zbugr908-OPu5>-{B`N(d5vgfA)l~H3sewr5fUy2TBDG`sL%E$Jf&@*}oe9xtjD^P2zMPfi)B^}TV z<#)@#RJH`#fAzLNakQUE*H(J4eK5|Y$bKS}GmDv~?ZGzDP0}hIFN?Op2AXS$iwb;? z`b8lE0wn8X@|zYNfFocVG1j2pd^tvtC7YV78$Y! z7o{Fc16c$vNdg6rgUf(N`vGiw=E`zVAV1(eXJPZcN+3UW`A}ClNyJ20IwTR0kxHLc z5s7tP82bC!91600*5|>rSvaK}0EsVRF8js7`1~Jc5fX1S;haRXaX~1Ut3Y1-u(a^u zmn{Q9rbR_Fss}*I()vQzRKvano`ai*TInIabK;(i`dx3F&!0Oj+zissxGtLEgdFJP zKECB*16&X-RZ^x%`^`Ce-*;1 za8%oZS&k9?f9A>Lx3BP7tIkq zF4c=`!i$TSYHkQnfuCK7Tz}_H!jdu>Q{}xL#>yiEPWZ{ZmkP$nSA0}H_onsJ`W{%> zx*;tc-}~s5?W1b3hiUikwOGW|{OtJk)VTH(Qp=kTD2iDl#%x&A9FF!<;& zZqN}7rjL>1?)>it|1uq3=&#dlf35yYrRSHypoobPP)Q#*n0o&nJV;}AfIls|RRetQ zat{2z20VNMyn`e}ga-&9BJkfk5P0~<4-n9t5u!%2ilTMnXjz0Uon3+|7e(acbDs7S zvRTD132IN!=~%bWv!Ata9k`xbh5bebe03v>mzI2*DMpzsu4~%m|8*yZU`cpKjf(I3 zY^>7BRXPic&hgj-d?5^@k%A=?B8c_Wb;&_ZIuSax+2M|{#V8*iO1tQC`O&4|hWq}$ zj#f6#PMNjiO_EQ0D>pQyk2puymVLOv-K{JB0PQZpKecpr7b36`vc)Ku-*j62;p-Wv zA#RwUDdnm4`R)swxt6vvK`Xa@p6|z+FdlTf5LJ5~B~PYAL6V)#{Ki^FCo@L7wqerK zxn5!BlG`A?-$R{(@AR3>>g_%2ybLAfYM8dIL*I60l(a;mG@nSFPW_=0mD15{rXP|2 zqvgA>n{HHEYioIMukQ=jkhi+K(9d1yhe)DsNLpKKUaKx zUfz!(CCM29TXt7pk@Gq@+jT1*B`C)zEcqvnHW^USiln#PR9$>k;w{DHZRxBt6(QTs zSyU!KY3nV^0Tmju87W z#qXZl^?RHm;`9tH&pH~qXd6cg)s5$49Ir)W{W32~e zr*l(HkBcYCBuq9E*`0cEoMWJ*oNhCqerCxxRE(i`E;Z|bnO1%2X5KFJve#`r;<-@3SQ`6Pp<<0|XwhnW;Av!-)dR_g7&1hVi zKDZkMXLO!=r#HbR^!%ru=RaS#_Q!=0>c!>|G*v zo|}*TTI7$rI{%a<;`jWwTG$K1&#}|1aWioG{&@knATM^op8x6sP84niZt=gY@eiW* z7XJs+|IH4ZR!sj-X88RNO#S?Wso&gkrWNKH(6TXal`+>dv$jXk^BPuK!Kb?s!AgnZ zXG6Ls*cNjJ!c>n+r6?FWlfiAB3bV-)9Q#uc$X@m21qrt6Z_!roLZav= zV}q>TqUX=vxNXg6rm5q5^2Pk(BUU^G^)!%y!x|_6#o%v6A z?)x(@YU&{>1?&Qqt zu@vinZdP5d$Ktb;lxvFIaz%4G5@jM;iyo-dusey#pRA-M9wtj*%V;j-x}lXAp0o?O zDGmn=t7e%4!COgJPcJ-rUD;9I&J>WgtzEk`d+NvkKE}K*TXN`_G||q${ZG=#WAx&~ zYaqJ0R^_KUvp>Aid-ADj%&L>I3F*9J_a9>c(>q0`o-0Q-RckVSm^d0v8BE4*Wetum3%>=W-O2!xp-8{qJMvQBzxepF=uh&on(K_3E>E)E*<|kJ^R0|0^^8PjJ6)4y$kkfuLngc z3+$}8_d3?4LxmkB^L@8M6GB%bkY)j7xl@>TVq^w@OI{)=4oiccWH^0CA znl-I|=8)3MUFgw=r2r4&r)|sr`u2BDHhu^T_zrGU`aZe~mA~3qKwvz61kA5C)f4C) zZOGc#z6xL5DIc5%lUn{hX>Yvb)T)1$$!3KjdU;!>=BGtup4S50v5ps4$(R>!T-oZ4 z#pD5KKVI&s;Ritez2Sg`aRto?1{;iM<)P`E7WnRl8tOX2}A9=P55#Quh zHxSZ|)ljfsG1eMxedW5vZ#g5XHG0vZV2x{b?nCsKO9H2|#=lc|b1gg&)AglYar@Ts z<6+7jCiWNZ*f3KrL4WP+dGUHI;fh9dbadUxle~^bSdOu(!TMa-|BO}c0$?1YI5qC( znfy}xY`y|zR+rlxlRcBYEY$|xNBj02y2Fm@b{Ndmzyo_@Pk}Hhqg_b4F>=g4OV~!ZxlGfhu1Jfkg4!;^O7sNa4lKVaZ zV$et!tQGxh#VYO<9@!1+kk0rw2wFzVm^)G#7n#opwbYd%3VaSSBItB-*_~pIXf?tI z!qb)e<+E0-`x8#Ta+aQ3Pvb9EuGhM~ANBiZc*S2i2ZuSlrVUsc#vdGHLKM(?hMFvH zi>tKi>wM=5IPaZCH$RFsVx;RTC}$sT2zlWBT$;jLSu<0^;W(fOxJEQLr6?=UY+i0! z4ZFHRK;E&NAOS`EN%8;<22UJRyO3C=~omIggx80M3(I>-$=q?M&Xu0yG2 zs{VRsjX%+orUdiBQNQ!$JN`tEy~@HodU@4JxzPg|LXo8 ztm2LIBaIgG@Ooyo3(X3wPW=_^G&r^58ZNT;AXSGc<=n>?HWyI-%V>bNYQaIRX$iMD zX#zLh%ol2U>fT|hytR%>iC`>u_}tvtLKRwd<&CA;ZjkdbLC#BobKY~HI?x^4fVb{z zzVAP2)xd)8WUx5)N(eEoVon7KN+x@1CEou%yANqh`kH}FATfsLOp*^mGEf96ZDBuj!_R_VghzGm_OK^diFvG z_G9Md7cp6791q@5V4PO{ZPONjBM;tno7(A0oKNBJ zfr$iGg3~^wgGF% zuLD3!8z&dw_j?`TSou6Mdi$qkB(5pCvNEcd)z8fhm?+;HR*Fx{WlO-=KZ(oXhM5~s z=+9SS)SesMc__0Vfbi09e4k^fgUQ1t`lu=_alZ~JHrS<)%k!oI%CJ5IY=YAQvmH7l zHjlJ~`PH<60@|Uh!JjE{L|wr<8e=bp2Apj+;8yzbCwy>F;k*!lhch!%mv36H;TY*g zy}4cII1D4K!aBbZDJ|AZP1QJ!gs}?)KedTd9v%Mq8Ar?*wu=1F{fMm`0BD?RRqmm- zYzkAem8o;bo5??abjGDo(WHO8y!7D@Uw=Z_mOZY(2}@G~Z>0Z=@~{tixX`WMb2F>z zy_yHS9FShm-^ZQ2tolBd5M-O`sYU*-9$Ox)lLNi`P2U;T^PAR%gq=T}q56?eXW@;M z+OlaCZ)%QkcOBvrVm2FvtBzt_=I~!gAVG_NKz?jb$UFOKW>^1QRjS1bE*gz$O97X>@XrjI;~SWc~py z8=N2G0unIt%Y&bwv$xK0~c;50olg}3p@Y@0hYi?E5@Z3M{FJ&)dD+HP?dg{ z0$fytopx{%8wP*8V{x`mttO^CxFt5o!?dohZbtYIikVVCz;dEAB59GFU^(m=SRy2E zlVBD6uj|1Z-+f1dWA|4a@}IxG{s`!ND@RD)vC1lV6orV+VZGymv$;p&kD$`R7%Df4S%Qtz6TUX`rQejY61@taH$wwPdiquT$P2bb#S%Y zFAgDAuzt5e;HL<5WJW`nCagT%5eGvhzy>&4Q`m=pa5UTo16!ncqd>v~|A42Wu+a*1 z#cI6|FQ6?o=Oi!#9)R5e$0SHji<_%|%X`@LfyC~f_k?5X1QJ+)gfl7t7zL~c5doyZ zX#$QiAUfn1FeHh7cg+7}4hsi}ZCs{^#ujikE0EDZ1_TnQb4aPNegI6;4#B|+RNTiG zt2VepxC`g9|G^z@6S7xTu_X-$2h2DC1rC9~p#eUcD&q82yp=;ri`tZEWcDxoGn0Dk zxf-qnzyJfa>ELrJtS;;zSPl17-@_J%EY>Hif>Db9>=v*{?7jnd1$L(f^n?p0PBtyP z5Lg5o7}y?m33yprV9vj)`lnRD6iy!C3MdM*Lkw64ya8v$p7%k8y&eRb`F|P@<0s+8%V7WjiY|RH4 z{6)F_M;4w;sZ7oLd?2;-zJ8s~$vw;owt($GJA_&o;jPEKgZ)E_{7j5onz>z|t07H~ zQp${iV+^JYfQi)|$$0|g7uY1lxIX{@Yk+)Muhms)l~&lThxj~QNE3On;8a~H)$-PJ z%*BoECY=+p9N9Z&cXUU)w;mVerk9L6v>b)%@GROd!G-Ip9i;)UI*;2Y?me2bVIR$ry0jWpuaw9N(F|xI z+R}*W$;aHLSCq(A!@Q9~46V zh`XWQrdh^5F{nB$rNzaNh(7Z6<6_Qm=X}iy8&O&Tm%%X&iQIU$JYm$!=hU~SqY;X< z=J#@0ol4gbfwRL)8{nH23yHb>rBEd^q(tqu$4;=tplI9YscxQhmYXt&H(ugsfrPcI zi*IYh+R_HkyIb~dU{)lLJ6!cDvCeC)Nl+@WXQLI$fFCDEM_ zz0U>QrligVso~M#9jWIlyEVly$-g|wCv@Y5Z-r+PanWJP}U zWK^ff4seC>aOsy{E!ipW8)=K37@8}%)E1tev(R{TS$NdgX|Z(qiETv5ai@ip4bNTZ z@n0SqJrbyd#69&aQF=ZBE*YzCbHz#}mI@z!n#XDS(6F*valW}WG1|G~#ocG6prEcW zrnx044&L-eq-Ln|I6df89^1_LYRf&WuHJ@9+l5Ym$n3MS^b$k7yfmqs*;c5bDyB=y z2^QU|6Z|d`7Gg%3ItMSb!y(~-E=fh7+8&vqBgFiJvBa1^^v{&(H zJuV?|CEQe(ICLZq#Q5s!qG@6ub6BTC%XQH<=Xlfvfp$MqY+fqs9#B`2k@@Z4 zUXyj*G>Q$Pn}ZWT>9D#^EuBG0YGK7q`;alFOk=Cma5lyO`I1##WEp|AIx4!3?Jx>t z6wyJ^G7iJ^;z$EZ4HrdK1RYALPKYa&S)MH-- z`2U6aMs`aDIDK0@r2O8kC_0Q(=%oK$48)!F>BL-&Gb9{pzM_43b_nJQNpCy*rn@U3 zjvhu{w7o5(>SUi9>2r;ZC<(3|Q?!&J9n%(%#r*tyDMY=Fnl!3%7nEEqu#RHG>o1*J z-64KjU>}iQn-E54scSk0C=b(6-J@JxSBdWA z`}z9Udo0}hUN(A*$YB7nhIetK56S&OET{ahr5yAgT7fMFP`Vt^pzq=x9+^zy2r5Q2%gvvg&R&dLE4 zA5cD!9xh5!Z3?JLP0n=Nq&95LnCRA&YE#VZOu8*T4*0Dp1xNG3=H(|$@HCX^Y0g68 z5%!@87x0Dp;f~g2cl&5J-GcPTnykCfa6w=77$BWZr+|8A{TIc!q5&w@h_nWE8duX9 zv_TxR5f)v`1auVTa*9_8o|g!3iP(?6()gi?LA@prdaZEiITf!i+YeJKxLrW!q&u#2 zKaB`4tZ@;vPmLIETXr@(dLp{(shh*b#v0?rWr-SJkT5X)>ag478H|iH-(<99-On0K zG3icoB_HY(6tH=N0nQsq$TLW%8%p024z2m>g#{%&n;SmQSU#uzeEUS3!U-I01ll8; z>`w4|Y`Xb{!^yf8X=kn+H z42EL!SPpR^c#joAi;w_Oqj3VWB$Z)>R=(VKob;#yHgu)ZdH*)K+Nr#ILjXhY1K zZywStFE0;b;4iWFJ=YvUStfh_u02cC6{flU>_-#$F1MacG$ql*&nK!bv{I{$0j*oi^) z!8f{qfXsmm<6^V2a~&l-k@ck5+tkI6x~%iC3qj^y<|$sq%x^yaxhtFYjTK9sjvv6L@BsB?8kT#u#2~7~ma4 zW9R)s0Q&`$;P&fvQUw7avR$ZhDmd*u_IuIpPr@rU6?3ID;z~=eT^C9=jeRXXjT**2 zEbO54OESNtb(85QPvfgBhSMKSo`5l*q`@B#)}HoNm42~bx{+mL@Mdu8p!Bn!aq#gP zI7(srQvceA@7%u!*T>EJ&*T1`+b>;v%e{^X?jH;H`pobCQP9sg+=M?aU)vk{yNtg` z!rk9pN76IWM0Dpb=AOR5xA@(l|fWO6yfP@HWjuXRMS}&u!@!ba8#UII(0AkNwRyeE2upcA<4HNmcfx`xA#m zCnFK5I;Jm^j!B8XS{0-?NTYD$lhL4%$zZ~%9q`wJH0dt%Mv?}-l5ua;GWOJ86*VQH zvFC};>3Od>#Qs!|!`}Qt-;i5kj)K0TXWzsQcP=J4VU;dj;z7!(!qS#qXzFwQb?0_k zv6X{fk;blte8|JMJp0zy`!&Daqijsrh3=t5?**#c6V*ujzRx(}JlrG2wv^7R?&akK z{;w+Xnef0EXJ;__;(WP4>irKUr<>NYzN^{^MtoWupiTBa_S4w)ZsDD?PddR@`wxPr z&uIx1-TjCVtB0=NkttZM%=tguy#-uU+t)wrwLwHF1q6|9>8?vV#2^d|-5moWpoGA^ z(j7ymbceJ$!lk=QTDnz0P}27d0}AN9zj&VaeLw&Ie1^lBIs5Fr_S$=W*LSUb=A4Il z8!n{kb9*A??O#2tV%N|oxTf$bt?KBXO?X4Mt8V_=hwTr~9xtE5dkdWH2%Non;_N)2 za`iQIo)SH90qJRaz1m6W%R^B9~V4m;>On6zxsX(ksp zWlPrAH$LNiCQ>kFlj=fSiZz{`onN3|;7;u@3V(sq&)M7B+S#StWetjJr~jQg8o9sjhq_{~Cgjw}mPhloD(f&webC9 z;CkhHl|APD$F_6=Nqe$RA_9)^U2$uu5QdOOJbKXo5tUiDT&sWm!Z_De@Q1QUavGPc zciy$JW!LlO4GCdl2aPEL*XQiw)L!oC^)ThppKQ226Lk>uCC3M*aRtxSHF$f|bw(K* z&V1qnM++0(0T7rcDj$ejGv@p;Rq%10M6h7DzpgC*f^St-)3cqbb^6m4cswFf+CLDT z@Bel#MB~9A4I*cEU(i{58}IMV8PBII{c(HzZel2J$+J z)c47m ze1z$@bK|bckKWF2$Q^9#tUfjUoZ(da@h(S0#USAT@Hc%B=ijXgj}gq*jb+$>@}{Ks z?IbYarq%k))qtZKjvR}F%NKF0;;7|3oQc0zxbVS>zxZU0I4<$dFY{N~0g0y@0C{)f z%!9;it#@Jus+LN}(gEW|$S%rpdbrd6j-%FPeW~q_<(9Cin=f$n%md8T2foB2YPo0! zDh4(YyN7$F3%&!TdFBDu-_EI`s&$^S0#ClY{;`G-)0i#SjGt;O;r3VPJaJ{Tz^mw9 zYMS{^2WF$McBEY+bKne%9nP+s6$8{a0-U?j%&-f8UuuwFqUOp9;)5y+M#gGHJ1nxb zFwF~L26aazrq1oYY1<>?8MXm@!&`H<*wJXmmk!e{LLaIm^jzRVPaP87CHcUXBwq@O zIga|W{}v_Hl$D0L+O&pCdKE-%FhzUwYp>?8>G-GJy77ub4FX+yOJuG#P_cUlbpUF+ zv1Q{ptpRi!g50QmHdi}7rby4ivNAW744i|dHc(19NMblG`_vRUVmQ{~b(F#vmtd;h zu}^u&d8vmhj$0aq3K^Fjbl1L?3Bz z)-YGib0kBm)x5i4XVmnl3zqc6MZG?aFTGpuf%OG7Ri<6!rD`hYC}?Fi&(Hn#fMlHn zp){M3wQz5sSgqY$u(KiNLofJ?*D$F=rI1%iY>?%VkgFpD8#fAa?7Ue%b}3u6ix0Mi7$UtvQn5^*Vy+|UytQd18#Cy zmKiiy_%fM<*Mk{n=NF?+PYKNnW1b=e>#w!1?Fk?bzjz~yocSlJXyx`0dIO@s)6V5Z zyy0=V*Zg(gM9V5@awUCwM#`f7 zv)#Av6XkoOz*=FIJp<}ef0B3ZUwS=ahNkvMti$Kq*x?s(vP!LR5`j4ru>s4ZG&>KQ zSKlg+%>~SDaJCx(b{<`J#H5s>xgy`CA*FZ|oIc)k*x9l4gpW7Clo=h}wG2xk2{psk zyx-1QNl+W>CD!v#$_>#ZL;9D0I}i{HBn#{40^I}c%8Vst^9$|qIt6e_kG zKJ|jFY>D?ZA%96AoQ0Ok?}#qqP$Qz-`^V~264xg_S|@onra3jUy=b4Gp4x<-*tpen>dph ze1Y@4ep69mbD+Nef!$w_#lAi>3-tR#@`nb_lQ<32kymq}cSyYLX&v3Uaj!sGgXCkm zvH~T>uGUtJcd5@9N&SUW&r;hp0p{oKVQw`yjgum9533eXpNkv)cJ5=#OBD+j()Vdg zH{U-bR%sECuozG0xud+WBH3fir0kUFsp9u_WBZA5OJ(5PhTyh*Yu4TP=sn|(5Knb= znJIN6Y{Lg?up#mm^2<6QSi z2nka19Ff2Yz{i9&xCJaZ5UCa%lQLc#_9zi_RXUJfd z@gEdwjU3Dq3g;;q7tQC4u(524qG}+!tSLLAB576`Gk4>T4}EACO+2WT{Eq;!am=`^ z_S_Q6K{r>U;i7o3B@SBa{M5EBJzk>A@`tRO$zikT_Y4EiNfhUs#-cwpXGF7mKQUC6 zn*oV+uX((^v8~*zQ9W%<+01y}r!TIE`)Z%;`^A=*=4wv@aVesp4zn+MTVCGJ;}gXk zFSJqFwkB}V)RkK`EX=UsN-+SZw|JB1WX*Bg2;c`~uiu)xhqGnY8cEwCwj@=w;P5z* z4GfbEUJgj}A1R_jd+GgWB*fts)^MtRqJ#7(2dzla){8})^W;0FY5JqpdM~B!WuZ0K zEpi3$#HzQMN{`TwGcpz_hGnz23qBE++l0i*&+ty0>T|C6Jh#xhXV^C`?*8O86@7qp z)`G3xSpAe*E#3o~)`4!etg0J;PJvEve(!`d82Y+=&spu!$G8jcccl*4PVDQ{q}EY(I`v*g`qP58Z-ZOHl5xdygvPqH zNXZV0I7+kp(N*ere6CUCZv`;w@~VB_YBKynq}HeFV1t=UR5mFTz*9tE!)rt2$K&f* z2u^_H+AO#l6dhPoP(D3>FA=X``>*H>PSN|uVEqtd7C9(dZ;M@^?N$@8(Br4(&vak6 z-z{R4dM)D<>7J;4Xc0Q2$-oz!yaKuR3J0VJkm{vdCFV{(1A`e`(%bf( zuj%V3h@B10&7o{Y+y_ta(WNvLvmYC7aMH2e>Ye;qrk}*_#=|4VEPd<4x!@aQdx+=P zxyy$X-o1MKIJ#Vr&~VZrDL`Eo9--hC7H{wl2kYB8yU!c%cejOht@b;U?Dv8^i<+Y1 zt$+YKXen)4Q)=Lk(OHCSK#Hs!OAV9MuvEtj&*pQDZ(|4}Y+3@JrCAVuE~DY{txO6teTmU6dg1%B(nK0cuC z#;V4$*E|1(e2oO%SqjYZ2c{SMUe@>47liFr^xar~<8kJ1Y@Ct|&r$~on_aaC#&BNT z*nPRLw->*+gh)e%$UnV>%8pVK>8V_T4PF-!c~JsgUw-_GF_ z6o^(_9v`0*sEB>^)x}17XtjVD9xm~q=f_RGU?`(??R5Ql93I#C%Zmw=Giu!zyXIPTYtqaI_NdklKCrO| zBzQXJ(WG(xNiyYns$cOnURt8{5|G8UN#4&VE6^4}Q}Dg@JJkcM18QwGXvc{Te>6IT z_04qb5B!xylOQQy$Ss0r6`cf$G`Z3_V4)Mp35IbY;V|wL9A>j-ZH$1#xhXhU7l7U&Qn@$ z|Dr(^tP@R;?;6QepyoAz*F{&g- zg<0^~9}5avu0#g}bSlIu_Yw*eV;w?>5Y@i*d8WhM4?}fT=Of(Y=uE17hMA~&L$M+l zvFfmPM>ok^#&7`sEGrAZZXHZHxZL_&VlhMygl*j#LCx7mB;>-~D~ z6u_r5lb*tF8J*Au(zXYxlut`c&Cz-WfAS&D=tCHJvxq6*;+qeas{O$53xfluPNd`dK+_0RFWZJh4B39QX6gFS zB%N)i-hX}GjP+CD%AOFf>-osF>r+xqG^Hu(qkuPBl+jT*CC%o7Z{VSC&}!DT;PaxW z^=*SWT#1Y%NN1+izd1+25c+1Az0`ftymyy0a@5*f-eb0nxA5_Z`vLnY7kh7cnOt2h z@h{HZTCAuvc|vO(md@ELN$RBGi1Ijjs2&=z3X5rvj^j={lL2cQjRzK8=NDUL?+3g) zO#=gGRkjSa@YIClCo`PoXp7_^gg3{@{GlQW$m`q2ViI?5U~I~bz>w#;fzhi2jmo#Cd#L(KiesOMRdcf3WL>f%tKn0OGxvwx z{?r_f4GF{6>&eskYkU1d63AiC=Bq)Lj-AeP$F~ZwIkCqZm#J@+W|&NhfIN&7)UNYq zB_B|mAmUmcNT`%Z-jw`k(-%bV4CeTcsAxg@E4PLzshJXVYK6y5@5$x4+%o8F%q`c1 z{fpwNS=ltcox88Cg*Z_9>~FlN*@p4Th+>4xym)wj=<8>_30KT5#<>BPwika=@eX3$ z`dofBgR|@-E`BR*8xA=~=lbmCa_8)?De!chzDbp*OlD}@3;3;Y^UGH{gNe~^2nD%f zgfn2~-jq|$Rck-7Inwpu)O+E2z?nrev^b@nb=<-G2!Fss_ zM&>Gpf|NhT%d7yg#QklBt(Gr-(iY*AbOCH~@xhRN#j_*fu$My|XgxTL@jyyL3KUAQ zhD8{}_9<;O{Nt^NAv!4~631sN&l8t2I8%_Gso|7ovbE02T97VOEN#1RR}%-a+KwW> z77Y<;BEAZhJz^rSI^cQ!SnLN^g}kk>p~B*MgyU0orHlSoPg(L#FFwUfZyW4eAk9@m zwHo05Tc+*7tW1ws_V^h zirnXZY8-Ftmd$?>#*uufz@Vs%xHeR`-iQ<7*4padi;sIo;ML&P58S1hHS2T4Dm*ZN z3H?Gix%YRRC%xMgG6T4MqUP7#y7Gcs#??D$a#$_~=HizuG7C5x{etg8ergQRPPUzy zeE9zuj3ECXfl)dyn3zgz!QNI}vK+;{b$kAvB3Z9bk49VwL42kt5Lz;jD z$Vrf!Av|7rU)+x=nr(R;!W)|V`mRQbfA6oN*qp_DowbV4SEqvT_%k_y0-ins#3N}bbBS00Vk+J=ASR%yyVBkyTgz!RK3 zN{p-77;%=;(|t#V0(>&l6>RO(F7qnOX&~^&J{=>^H5s-owmoK;qg|@zbpXHY{MQ~` z$UgvPrhoBNr1=&It?j^hQmjQ7n+87v!zbcIulciq@>v4%SyIN9>0bcD;=hXv?+^6C z#Z2pD8im4a=?z%RBQ|{L+Jwn-E;I7V+|XA26)iYRFVt)dSeN3}!VC=vmYP~EUO(fG zT6k(e+0UD|w_!8O=kjRV=FvCYr85#c2#_ z+^BH4dR4E4(NBV5n&UKVKW5edl66}F84`@s(S|bzlI$x}zL4jIM@arg=z}2U`6;9k zbVjc?o_M{REk={ZBjQb<{|8p?a*VMDPichv>rYlIBTz_IdyS6a#{Bv({q$)wtb1KD z{b_ai3Kz+Ja;DZ?+Wp)z+?DcwFG=-_sO(oss<^*TQbqN$Zx3A(>i=bgw-7U}Vdlu) z_nDW)a_S|cu5T)?;j&3|--H*SQkBxYJCf+wGW7iGU8b|()t^i^vecKkbBfD(xTr4P z4#*-m?ezTY;QNrMsd5T6^P}^TVMBLgx*6~?4=Oua%+2F@IqYD`XijmT4Ko+NaHaEX zo(e^xqpB%S1rTx;p4!&+SrWG+nU|_tErC!xmNb$rgQclBDkJCO;w*2Ypw||JX&zfd z`4Jh~n%cK>acD8XU^9a}% z7@>KT^}`r820r+4NFaV1jr%9JC64mb9djEa#aTRoV-PP0u6&_p#)A@$g8Xz+Ert z1WJz8usJ5$a!u(-5ZM*!2jbUE-1L`a@&tK24oS zFEt2nOPBF3=<@Z=6qXqyN*dva7%RcT)G)X~H(!h?D-F*m?(4o#84xWx`JhoSk$g-T zNFJ9mP0E(M?|E<(MD>9+9evww)w+Zi3qx7aa5#f^^$cRv76>@=#^j9pX(ahTTB>Q*?X3X!4_BBqJ|}wCjJ`; zOgshwrwb1l#dMh@ym+mVoUMerUy|Upd)`6r*@2-FshhlocWUW7f+vWKwA$HZTZIgR z1*Z#F7%E_85uH3SWg4Yjj68@LM1qyY{MJ>o62d!5vS|%VWTVbbBI+}^x5v^$H!)~A z*RbDN-A^MkD4D1*G-|=~qQYw)1ogeuX`lB26Qn36qhn_+V-lOPsz--$`@-8Z44(5Y zc?J(O4eW zWN*e_6O~gAdmfq#p_L~hxa09+(ZVGOGoo?n=6|%qHZ4=_x%2s7eYW17>jO zxT)!85>suu+8~?);fZXe_tyj;v2K?d8t@jXT10pwLi53IlHWeu7pjA}j)P zI+iwNzxO=8YtOE*XfgdUM_>PmsNcTwLR7)+zigDIzGCVjw0}D+l6|5GeqAI*N5FM; zy92wx>y%SVPy&Bp9?!H#n@%^M1XeElM@C3nxAFQy+wOo@@0-e4Wo~Uq5MOM?k5h|= zSk_#>438E`pHp5Q=N7_P_OZFy%A}B1mR8o&_gF#sU`%r6{U|jd3?a%P{rQbL_9UIW zY420}DTmnx?r|4IdPDXv589jLh^7$AezT^^`~BbvNd)P2-57O*ng^wUsVX1;IycXn z#AKL>S>oq+p?Y`?PFuh#G4&;C^z`^f-R5xxpJt=Vj*>vy1P%Rw`B{-Nh@34_WOaX< zv<98wCnM$eHDJ!PYY1Nrwd@v7|%9N3I z{37|)6scsp1C>S&qzZG6R2XxtLNh>x?MN*nRrntixxv#yZpofmN=)#<2v}qh$cz1c zHk$oA+i3Y}o-MiFbAu7DAH45y`3>FLD>ytXwLeXB81uMEwy(w8BwNmj8W;x*2a@}V z#)IdAd+&1>zYB{?EHqv-UKd)~6|5lGQ{4;N73|DEtbd0~!1cG!V8??C3vru!SGswY zzuri;8T>>dHec{w(5F-TFK3*Dsy}w9|DhO<-`cYaAMijB+iHS{B4*dnAf3tN#LDH3 zPS>WlBI`_Ko7b8Ue!#o)(OXgb!#&jJe|Ot}yTw$6Ajg!+9k=;&9UD<@u8UlOw)|{Q3EMVjOvN-e6eVkh+4(UEC8=M2SN?BOugM^XL?#PX2=Ke~bB{gxk z<_(7Vql&2~>R#0a#59-22DmPdNN9QGcgd6tz4BAOyatxKWf}9@PbCAm*a>!i=zSe; zDPs}85lygxf#eq_`05Az=UOb5h&haRpcv(tfJBZm|EdXj3E($Pi-?CEx1*sgPkO<< z2g43`W^WfDY+6kxs_f?@daGR4IvF!u_NNon!TY<7{AKaGUjiLqd|y5itq(u_II*|< zpZ>>mA;+IesQ>cCpJXQG9k;nQYLt*;^5tldjt3=R3K+Tn;LQ@Vn!LN)c!99J;i~|? zBIk6Ap(7o9CS;R{+}OZVI_In<5lDDueLE-XLq)1#TxGRIa6dO<8n8)=>XOwm+=iVX zD|SkWAspYj1%w-1R%pw@r@YBIym0F_T0OM0J$J!jnyl~h{M~J`B_j3nW^E26AFMAc zX3m?77jTV?lGHGkGv}^xl#5ytGjUtD2yiBzbRIej*Y+d=6^Vgo z8ZkZZQzJLpM4zzvrw<}-L?Gv!@4 z^=d+>VIwn5oi^YNh2h==;WVBz)}Xk+t0B%HI%e=EPyGca4jL`p9$&KO$S5O z8g?K%xW8>6FpJ0#R^qFY>KFX#Nn=O!sNswEqk?3SH^+@E(j;Y8$Oeb{xQk0UQ*2mR z`$BzVop#=e1cF^`H+U2q&0I~soePC))Rb>p;E+6(soskYdXuhm0A)XH^vX8P7A7I> z?uRP=FMCML?K?7J^8W^S0bD>~FX8&yCGV+cN;FA|1a_UDo@*r0f+inR3=7;{edel5 zl)x${(`6%L5iAyJQ8m(I%IQBCJIJ=be*wMKmy&nHQaI~!YLdibHCH}=c`0(A#%}ZY ziM@MaWi7af7)2>^a>}hLuLax$bxWK_swSCzd7HNq+&-hrF=n7uFq9~oL3jM*gm*3x z#qBN5J6@oQYHVhx4*8OK4@Z`*?suBC6^H2HHMUY2kJs>N+Of^oaj6Oh1;I; zF*5Nv3o}9R)dNdh1@9^A&ua5efjiofKB&cNWhcm@(&iZ^uPW%e+80*3VVfpfs$@29 z#-ux<1D8@`6{moeD@jktn=T0bo&ryd=kHKr8BQD)nv%d8bPley!ZZ@n7(cCH4!CQC6lKG*NQLDm*Q$#csUXE)ibw{y?P7HE*+F;x>de3K$3{%0LKms2-L~ewHMCJR9Db zy^HbUxq|1zw3(h{c>)5a(mkQylfxu&f|3XMs=og2Lowu9p4tN=DB#LB-rYH`FhZlS zZYodX?;kmQK=MMqxw9j|m@aBnZk2?}O(U9U)T%YZwRs{E3_PbbfObXO)xP>bO2Eyt z^oP~_2UBGHtd|5V&0k|X2D}yHy4++U! zq-`J;QO=fCQyP15#3Y{&M8vU^$0fhhE^KeHp)<|z?JqQ1}@g>UZ9F$nB4b=|x7FiU-;p5{@J*|3FQV0l+bP zz03}$Z0qW;+8#hry3iv^_s<6OlRAFIAy^Cr055sfbW}R<;g*@KscjeV+eM1ZfT|f< z+Q|!0N_#d_Fn1(w(YJFwp)nx7FaE=L;p~Hiau4|htvYWp@%k!Z$-sul-LU+;9#uS> zL4-4>DVoueiR8M3tyH{NS9M^hHYioi)3O8gBEm<1@4o^fdZ3$}7v~i&uV5=^>@XyM zdj-qwsXH5q=uno(1}0!ncvZ0?egr-Y20H68Fco|248dk>zm-VW7xL+@J5O=hJm!>& z;!Aol^k8qcmj)PYvcLPNFP#ifHG+q5di`^+VpCQTI)lgx* zuk~WNaB_A@&Wh#1?cY{zrIw9z+3FRscNs45RKD9hZNOC!iLdCJztocaGONEEIRD zP%TP)iaL7#FRKT&l(V6u)*iTdQJiCCHBl4tt8=H~M=$1Unwapp$y>5Ycv#D^t3eN; zlrxjVwiQA&G32nvvysV5Ip5C3U)FA4%V$yUCbor)#KTS2_0a6=UvEUQ=~9_$nQ=ZF z7-s#lI(R^2`GYZbR5=$t1KiYENcoiwohtK_cOOTY7(Nb^c9ej5=tWOoi(HB3mv>}g zwq_=+v-3|frWcUFThVZc+|PsEC>TdsW7XOsm5djC#fkh;t`>L%Cgcl3&M&$qCn4{p zEHL>C*f*B_vt2qhd|kfySUY$q3#jYG0uguiw3O3rz%3p|fBz0toSJ+;$^RW>>cfz+ zpAmq2Q`rmLfmO8Gn4w*rEs@YxZEe%dJAtu{R!9 zLR*`Su2E*Un{8x+<7e`xFm)qo{Dm{aLll&nhWO}EGp28H_)#H&SoaJMh=WAk>Bp(^-5U1y`Y3JyCK?7^XHQkw17o%#_u77_E z;|rE2tWFm*;=r^ezUb8^7||GQhc0|6fsP2___BdE9CA zQTrNy9pMS}NUd7tp2>RC{IQGYPqhWxM6RnvW|>(*8WR?do{${Myarr_GMy1r7U!!l z$7y&VP~?!?u?4YQIZO)acyLswS8b(Aag?M+Gab|@Q42g<@Vk^$Of_1kGte5C8J8O; zSix&DRP_|@w^nLh$)d?$X{3q8$LTWu!j0ARa_*JetekS8-_GqPlDsn{B+S_7rHFEy z7T9$ylvd(sY~HE#Uuq2qf4aQ)N7##Kxm~I2rMhjSoe6f3eIT8>6Ud$cJ82<9f@hR= ztU!d;AuB^z-D&tFAXqMc_@P=r5d%QnfGhk zM)uNYARB(uNR|vx7keyqv+F@;&Gk!*KDlYa9@l~=VCeD|=|5*s{?h^FKblQsBg&a$GHNg?Z=t@=R!3sT^`?tXe0~DdaeP={f8+E%)@pdM{*~9sz>D+U6k^b zP_UQGJ_~xZP;Fr&Zs&cLhT`=}b=r&yq;*Vx}8>7ii;=A62$*)MoouW9Cl=%#=NC_k*cQ zx$GAFL$42ed}8z6;rlg>-#-b4fRl**_z_)|q8)9X#!rMGEJ34QK6TT9&AxcZTFJo} zYjCDLN!3Qy8u;#3`iNQ&aI;aF-ievcW*hQ@76-wqZ2f;%s^ft;E!HpRHA*TXvKFXe z+Y6sp;Ea}7i{-1ZHjz~te?L*K%DlfK2rfI5sve=2$?>9U&3iH67>Ew2!uRv z!Y!?;e;liToywcIFL^~OwrBS}2LhclvxNJ&Ba}Tq|c0cY^p2$)VoXAp~jki!n3&0+&oMd`jCDclzb0hPxYXCN$uw4zHf5<=7 zTd#Cw!qs)~Wg0TNK;__?zvtk7a!7|2om7@4U0_9*6_%8v0DE^!`*QSw-Sw$yRNCZ( z1yvyY<-x=f90fq)QBc&It zO@A1uUPhw3>mGpa)AsFDn(VO5kmf+t2zm-LMf5AgTk0d>YYO0lQ&Y(8I$*t1z=j+b zQU)0A&ejQruYbhwVaf7E>77t1Q=85`k)N0($_%e|m{K;(&y(75wIm#ZA^tOwJE9-C zJYi9fQ}ZrEm>oAak*J9>R%GW7UriMihrG1k>W zBq6L1Ux(QUKw=4`I{`_Y?>qx{|BH5l)4}m*e;;;W=X7;PMsQr`aqZtL#Xqdn z{gAwv|4d$h=8?|fXnsT`!|13=Xq!p9fgj z#OBHgK3e_vlkt~bum&VY)&^`*uMl5a)C_-(B*nTyC#PM=Ve4nNXYl}Vd-e0pC|w_I zkL!&2Q77TwYx)<;K9upeN~FXNxAF6y^g16}$l=n>zauFi{wZPW&%S0f^h!r$e@qJ0 z`2Av1oX%YL-#tsekK0Vp^`wZ$TiBr!W%DA$69Oo0EBYxy2o{@ubFQ~feP ztbvE7=%fEYy7<3kU*DDU%RV7h0dxt_#^E2J6kNy0|9p#53M|DGQ^Cx!>BuEZ@>_1BN&oX~M zivRW}fc{^t{%73)(F!tBUe7XcW2cTwX1uB;VSL-sJ3F^HofkQ7gfBjGM|A-^l=UT3 z3VFWlkL7xf%nq11q-LV#nuI`>gN)PxU6oP-G*58LosCwBO;)~n(}VVUo&l1bhdwYi zZ`go! z!^RPzx$5Km@Re(v(XjfNb?D;bZ78Eq75scRAO3*)6R$-L)-=o5FOD@b%_ZO~_nL+U zrN^p*R%rRt6Mz^{cEB9E1J7`vuXz4RS`!neVe*!e5+2KP?55-MmzZguqf2McTi&vcYE-v zZNMBFCk$F{4teEZ(P~Z@hITo`7I1(orr7zy)gFomgWOv}mK=wAh3L8@F8sjLjIXv}O zpC)vCq`pSlZ?m3s@wKc*j;q@hG({_A;G&&nqJGCl4y~8%%DJaM$PMshoH{rKEC+af zFNs+O;(iP~N0=vjGtl%a@IC2a4M&B558l89L8vo;4~Lht0N0s_9<3&vT3Psh{mDP9 zD;!^wY%)zt>$>h`l~!w-VRV?iq%;22AMi5s}4^;4=HA|?z) zU2Wd|&{$qX+}(~lXdd;-Uexi@%TmzEY+G^;HO57J=-IL#lBx~Hu1`lqnvx0Mf>9Ut z^O{yX$_{fU9BE0C5SN!K?Rx}AxLFrl#f_&zw!j5iZfIbdX8RJF5prR1P>hd8-Zj_N znVLzN7`A#=xw{l&Anu{M5Nx`>x4WJM{rb%*b*bykbs>1-Z1Vj{76TiFEDbEau=(f? z5M_2SgjvE)ZiSna7Gj~NDP2$Td^Xx5M{~qE$%8Kt7O82@>?36nstKV_T*Nn43Zx3c zhPP5XkqHU%ulnNvM;MA3C#OOte}Z&hxJY z*4*Wfx->I6p+$K=w5@45dL}cdee>xmz6{S)J9is|ph@;3EW*=3BD-X$k2ycb9SvSj z0q7134^Z<_he=o1*f&?!AF*Pm7kn#rpHqjkS&8S9~eR5cCu+*TOG9 zG#o_e79I&`7XM_GyN?|_8Rgs%(;}LW?cdDVW7^~Bd4VzA&nML9Qp-SIam&`_&<_M& zqo%p3ypuF-a2QoQb>{afP<`Cy<{mLjUBg!8RCsGP!e3y3p@k7;YEoAZ+*sLmsBl^~ z$zCQs(O@tt)ZTwVLtIQ<;4!Tgniuus0JtNrL~5ccc9sxGz1g$o4r3C&LlTGs{N_oV z84I_9VQC5$UX~I#mXpZj4!LQ@b%r$?g#d-JH`M4uV}1B_deI;f(~b z10y|-mpNrFC}vs2E~pe(H>}ZV_YC%D?@Eoy(0l{tSikFTlU@nNY(3RQObiCPSerXu z)KV#J5jiW%zikz%X~BfJlV{2JguYsw2;@*%1q7SVOxZY*ccNv&Q>D#d_c=oWZ_sPc zp3o#d8Q<)x!X4l`zT5s5OwfsW<>2wQv>M@8B3XS1$nIsRefIU4cn&Nq5DXrF&A31Z zCS6Zjnuixc7;>kw?_gYlbc0e5{vv>~gE944-i7P2b8@f8^`t^GdhA@SsJQ#WV&LM` zgb_&*?Hkl%RxFeth;xSW7KZ4Ylryz3p>&-3gKf2o(CyHo;y$rZZaF{8_rsAsqULIJ zWNc!VyMO?S_A%G{;wh5JXc>!ak{pHS1ZpV7FDz|c%D}c^g#g02n zm*z)6E{)q&ILB_{2xdf-$caLG{(uip`#SaxG3&MyV)~>afY>?yodjVHf;SN+r zX0znQwS;t9FApC!Xj6G00Y{VPl+RsHi52)OU@q3V zN0uiCE@a_SJ;&OW`lf@8Eq?F@ON0jw&?BBPS8|3sCLoS>Ru1s4JN6K)pA?s}zB&4K zzK{;8Lj=(|VSybJNX0b3s}QH%V09(~J-6p*Exgm{Hmy;PY;yMn_pzBf0kO~KHpy~U z;x#v3)V$7hV6_iRP~{FK%VInBgC2G&>jrDI{6*Fj;_|Xe#A>(Nt@tiuxz!U|+eg?0 z$6RA5i^j^%-XA8B4#a*Cp|}AywoII%$2s9^>HgN&el6|xf|a~VSh}f{ zNQW)YyUopaEB%q5Jf&U9v>}s7Gv8BSEiPX28tnt&JVZ9Gff8Uf!4P|= z*nYDYceB=??O^c+Wg{AeG@U~RTe;jio_BkzZ46gY~|pZvBx9t*kCIS!&r77WTH4Vs%vV!0yNqXUc~tD&*PW zT=AH)DH@liI*<;-O$}P> zFK)-Dvo?}$AeHF8V~Ce-M)|npe#-uHZXU)Oo8rD=Xs`D>?s`jWw>pTHyi7N{&32`& zBY%_&TOdTPvcVC=&^IJgO_&|~MB)afeKD8~x4{uxHbT|JNX#rpHeU1+m>D1i3@1rJ zVKu~$Rg2{sr;ltHG&(c9rR#Oj7QWrvn(NHVZF8v+>CKdU6zwg5QB-tr$O`TfEK(Fh z4B0N4Wy6N9c;|JnG|(2%q?^PXc92BAVY%#k>KPSGerlIx-)+NX-7PY=$Rlis2=T4K zVa&t#&XYRyqrx7iJtT95s9_2D4R%_T!#rdbg~!Z-#Ehwb#?Ujy6s}7ZF)nMD?9iZ* zNzObhn;yqrTpu8n4;|E*ROfr`dHbqGnY-?s2Sq>c>acZY4X0-e)u#n@hi93O!priN zGn(BbacY@-!SV%cqOUeQsjteEmFSU`()9CMyt95$GkHCt%b)+dv*ZkCZB38h&dQQ_ zrLMMq$cUKG!dh2iHn8PzyDEIW%N%EbFM3z99DJy~J3Q(X#ft|?OR^#~*V`X6t+yL1cvu9!;&I=a#?SUYu}FqJzILE6(Vgf!@?J4 z5!;o_L9x>m4%(3#m$^$5?E5JXhh|gMI1bXD7) zMm!FNL3(p7VWbip68m~~&HKr5lxs^)48?pmP_!Rs22LdA+QVr=+qznvph z@n0zir8v>rA{~O#q3@YF>wA#O%4S|ox^Mj2J>0m(eGx@Q3IQ@=R2FBE_Cb}}a!P0; zb5e=+Do1&^h`(=gxOwJZ1(aTq-qNYXJlV#gHpe@0!t@EeCD^2mGglzCi1&ymJn84uH0p*%i8jFP=M9bg4?hv9B05bjn!Iv(6( z&qm29uV$32OfNEYW!L%XiE#HiYMrE(gT41d;ltJs&3Q#7=7eez^jBp{kkC(;)oWB6 zaQUp`06al3(i5Qkh^_-Nh*dodVj;QCnN`Xmo`k09iZ!x~h?r%u4gXha1fETYK&H}{ z{ZsUe3Ql#^MP&_jyUNzLA5PN8E7SMRRHyjpM%Gi)3`Qp<^v+{IZ$<-wrub2ydB#DS z&%Ez7jeysI42B+WW|k%>7hPt$D8pW&r&pRe2zal|UK0QpB9C!F2I-2(B0fdwJ*64q zN)j^zZHCZ=JEA;_f7@{co&TocOwVfpmsx-zO_ji#Ev}9P(qs2P_ex#U&BNlu(zRN{ zk`0O68fqyMMX_(^qSsk#W!bxn;L=tU{c6;4hAqS2UCQ@!4nx-;c%6biOlZ#N&iI-K zPz6k*Kn{L6klIrlH!cNg?o15k9Xe_J+0b@NelpjxZ}3X1vKQJ1$%?RNiT&Kv&t4y}1D7hVIHF99f%8pVY4q z`#9UCi_b5gaofk32kk{st0*hElKN{z9A<4U+rjFMv{Qt=qt*U9VJAK$>^~Zz%}&V? z8Ood2dB3N&P+K;v(P?*;t~2~9P5iSsVm}DCcxGs^I+!!s@fK*LB4}@kTUAQrI&Ah~ z3Rz~F+7dH;v+nE^_98=vd30CP-o(u6NsIn>;96Eumgr>WPVq-pPK& ziLwsjY?54w?ZCw#Pl$CBzdUfh&kZ6|g|zd;zjplzB7fl;5GzC^ewz4Cx{jTj6ty<> zm8re~EHjGSf!=_yx7SWZHv#DE;o@|I@_$05Z!q1A1R2>Vc6ds@+2(r|cNgK$w_H!z zqZ&(ohB(DUOvpMIMo*EFVddO*+P$+O2I(Z5a-0|7V1|*2sVhU;Kn74 z%9WTAdWn~589Md4KiJ<%27Sc8C$D=R66zxuJ@|{( zDq=;!sE)Fb%1Am2uxHV0?wo2O%o`%8G*`;iVM3Fb+9cTQdQy!tFN)M-ACr24j1K%< zQr4am`9$D!b>!)xM|NGEKEjaUjon-nz6>3jb-JhbNvu{{f{I=tz$U%jH-OV)k~PLT zoQZ{EP4-$k9a+?7QyPK_KfZ-ic0ryn)Y(8Pb)V9T>IUuu2_w8v!%Ho0G^~S~)2LSV z>W?CipJ22k7RAiN%)?7jtQNtkF2`=x2I7T?BvVwf9mY3ltUqA1tL-}z4tQMl<_a=R z_&rKO(Q66367~W0lhphW8bK`pG#Zy|*PcnGo=tT!1D9l{l3!;(OepqCWMuw=vf|62 zl8m?D8q4KoDgOn;sgW0dJLlGTcs;~X+DH6wDfU;ZAIOXhtJUl;S3jb^<4>{`Bf`e# z|Do}Hr}v7H@z=m1Caa|r3cQYUVc*8@hD#2UW!&JcF1BM#ynpB4F1a* z<9DX~hco`GNk!s}K%ztC68E1}AM`bnvW&=%P^doV*g>=B($OvN?6(QCOFpW_+WGX` zo}cz?`eI#byx%z`FO@U`xgct`F#-|YKs2|DL01-Tru4R@%%bjwzvmaN8HrPgTLls2 zj4LS^_);?TkFLZ9jJ6yMINa;}ve);|sOi?=u8m6n@J6N2!<}ev&sje5kt4WXcdBVjm>4MDk2QOEZP+c=yBKca~j`LSIKZv^amgC=Vf;v3^ z&9cK^TmJFc+c9jTz$YSXr!>DJZ@E`hqKfr|x*rt8m!wc@fGhnEDWaDqAo4OmYgCQM ziv{rn!w!3n%oJ2YBj(q|*ixPz=XfRK2DRNWs9%eJ*_)_To_KP7Rq^w?nr#aw#&3?1 z+B9P@0J>UV0hB1T=}obcZ!cl>1-P9b>odIw*lXpafRSlC%(Do#qrZ zx2=?ql=6S^U*Jwhc#)g&XJSK2jGYK4iR&?LR+>1bW|(0pZQpPLNHMwYBKvt%&uqGJ6DmU3uw#l8|YZ&CmR>mKFA^^RDBQD1Md+fIr`Ss{RuYjS< zr1ouD()QyPR}YT0U__X{Y(bQp@LtNf@k?omVujU6wO@Ww(J{NITH(}Av17uGALvtWd8#fdj_u?1PIEPCOhpZ)e|6A~zwWGmCUq!UY*S|9 zlYJ6s&;}yZVH zFKiLrFH>7KCnCd?w~@5F6;ejN9Q)Cexs7RjbxirceMmUTspQsP+N4{o`~1HCa8-Pd zZriI-S5l*m2zPLws{wtih<7juw7+86)h8E*tb zt}tvr%5uD~Fg>N#A~?@7KIqbT!^5HNPAd0w&MhZ0OB~-)R_4#deSE0Ip(1W(`^=C1 zX-&Ge)x~ z$n3cal|*Og^pKoA^LIwlVjgVq3|dT$EmlU0wXnsE*kYgme=jC!<9`FU6vs73&obHVaFFhKsPGfz-VJ<3aBvn^0pvhIRqhm5v?V+RJO(mRrYucG)liRtp zzMhAf@#f*mitVFD6x52Q*2SJlMS038Y8hlzIOo!wr=`ZX6sW2OuKV?vQr)QALI~^@ zjk%)MiGRtgPazn{g+FKh@YiBa_2CqS`n|awc@4c6KvA2$m`jo<*K+UBu@><7ErixOC+du}JnR9LkUna^$%afDYuBG!vXg3N#xOE$sPUF$@}oX=292-F?Ar>3 zW*Q@AStvs3cTv2s`cc<9{?fYcJoDYj0*3K*4eMPICdSQT>9cFsWM+fUNj&^vAK3Th z&Y|<4TTZ6GI&+PCHmE@2?hliv8W!{GWAZO6`YWjt13B^>}cBME?(ax4zHn<_Awca~ z55;|MF-xDp1b7Jo*zfH7bmvgs=N9L`_gfK&g%+aBj{35-mU!i;VMd-9-@5Hfa;@_W~>+E*+F2lSudWJC5F=TPq8n&{%6 z(YPU*yf>9VHy9i~$g|7sqP72@NiVC)5(ecy*k8z!wdUn0rHy!A+ z3w}wFQYFTnlYWKrUGIF{eDB-d`Chp9U;o8nNA7<<#^c!Y&29cp{y8Bo8GW5KXGJw} zU&q3ZZzg>zBgDbiayPzNGFJ7+o$9M;TDDwXel140e??l~hd#dZBI4G_xWNo?c@}XCySrx#P!I zL%CASHE-LD3~9D~I&Q2*_9ptW2nAiuF?W93r3R70aQ|yk6m{pLutJAf7cJX({q)Ol^679^GHaSqikeTU-R6mv zcb4}sE(;0IRhp1l?cP>4K$bf)G94JmjNEMhJ)xKyPj8*@w^>hY+g~e+OsDfm$ThR{ zQA*Fa(2OKs9EUc9>%O1jyPFU@7Wwn4p3S2rUTf&WNCVNaF-1o~5@w2AX06gJ4?4 z?x_h}K!zstz%o$&L3mKy>91v@wohKSkrORDfcuba-#6(S?t7C5y@Wh=N6Ivj zuEsQxVdZd=IXJ;>&8FvQ1-z4*)Dz?;_r%#HM%tU;d8azbW!uBAGd>(vN+^_YVfVSm zzofJ4^kb3E#hIfz{=Bs~65q&7k5_Y4px2vPAx(tUkR|5-97k06|>82#*US;>Las!1x? z&U@f%2`}9G+kaM?SE_wh-86h-=!DD71HW-XUXjP8ny%Gw7fw9zy8K>Wh2cu$!sTcB zXj$#G9~{{7-?-r>?D$XYc=ak1`tzOFU2~i8a;hO!4ZTBu&u=uEGI&?W`pVR-zeZJ}KV$MYJ}E2+pzA1uv=xXTgTxh@4Z?pwZ&?9K3` z2+nj_mc0nxnTv)UGbDuT`=NHQy)oGRi8!54AGoC~ai$$)P4z!v%YlEvd0AWNu5Nfh zxih=wp%>KO9wb`us-^G^=I@sFX6Xrk8gym4A+vs=Qoa$1`Q;;lsr@*G!McksWXK=u#%$l5$Ye#X5 z$->6rU=>$uJdOK5PuuzTY2KBxkI3D53yMEwn@2H_!jecb)@Nj_zd-sM z*DWj$M=&E`m=`7If-DbLe(0Wm6_^Y&D?-gN^cpBYHlC0&q}lX%7-1$dq7A?`DMlcC z&dorykm863ENI0pG1T6)EGx5im}tJ!rj-Li3!+L*Mu*)0)+~7_8^kQwA8ZTH32PAP zqZ~taw$A{Fh&@ZsFh;VmZ-X!x5m~Zt6aZXYmKe(o{>pZ+;TNw%07pRHQGFL`MIDjZ zhsxV%D~~>{*_Bu2tF308sB#jYa3esWZgf*_TqeI$AVsse_0VG81C3(x=NjZ|L}ee2 zj%<#*Yl@tk?{T~&Mhg6(l6@3Uii#plHeJRr-hzth8sxvEG<78nWg7^MzQ=Gigxwqa z^JibP3Zx(@YlIiz_(xVQDM92&?6aZahExY9kD}r4y_21fv19};>JM_i*CLy@17M2o z0@Oj0t};O?su~V&V&^IJYL%p%cQ~@rDoIy!PyBgJ7v|P|d^+Tz>?rDZ3WJL=@S-oY zy{5yR4!ZT9cd+x?1xYsqPQj;7^_mVDn4N}PUjj78${rar+i`V;8sD5f!im#eG#!*J zcHs;@mhJbp$|v<8S)$IVh*HOfu(w(_#x8eni|EN7&!3Ugy|k;rIph-a*wMY0Bm>-b zjdbE2OM2gZl3$87*}ZGGJ^Ae*U9o_N^I_jNzlm_~#MLbu=#BU4@NQEJ-QEA%G5H+7 zK2K3SplDEkr8!kBFHTj%tjq3=7CJM`0g39dfa{8wyTR2T2itJfCZsTJP}FZ+4`Kob z;V!7?KfqCgW&HP$s;huwq&FcCD+RaJxgN_dF|SJpdO^><$|mZ@KVd?ufu~bl$*!o?k`>D8epHKpg;}CLXQ( z$SIkHyXx$f@7|WDCgyCH6&N4j=nc;IHhn49l{DunEVsihVFzy~SMfk@$6bS~-4l5g znPU9Aw929cSgpdvG($3pQ8k-h^6>GiwFolbKDn%UI%bV{C%2uM2;f8E^F^G4j!5l} zUx}cIK0;x&wD+FKE8JQn$FXpv*5JfS6V2fSO~!|hmPi4L;(mkK{E2srSs_wIPRh4f z(nawOyJbgW**9nt@Gt! zf<1H(9!6;s9g{!*8yC-&SmNl)Jb(gt zG^}zeylI)RQ#-c3#{rP+bI|Rc^V^bW99aydj#P@==gC*-cJ z?ydBoYf(4?iWn=stv?&WK{up18N$I05e>E}0;Qeo1l`yZRhJ;*y;>>gu7MG6xp1em z@6w#Koj(uha4x~Sk7`W@J%g}HGifk{(4nMhSq@Caj+77Sa;L?ma>T)g?#{~h3~mj} zdsMf4q{Wpfjiw{u%^4lR>#ag>HB1t!WQQDnimIRG=zsqCvR5m9H}hLf8GXAauh_uI@KF<$&1Z;(C z2=?$cx*_dldf*pwZ}e`t(5feT9V!HeltX3!zlUd0&I=(2J)C6mMvqThb*Yu&D*P?#sS zKj(dIXwF!E(cbK_vTCxGk~VG2@PNCUFURuPJllpShwn)vun_lxeGztORi$k4D=AeVQ-1ozIAY1UKU zJCOgnXVh^`Q>=7%{-DjtOW^l!*b&pP$5wh#R8hgwl0_)?W}Yg8e1~3GR2}htaP-wh z-WG^^Pu`>o%oIJQzN{DkG4-DT`~Psw1U-O#d787?fvN@s)zy8>l&n_Dca_ zmpYUWh$ z3DMv2U3M44rXUx_X`=YU%woz z)&T~IhCsQd*)9X|WZeNkl#{*~~3Y;>R^ zQG)Vp#HXWBE1UbL=p?4qsSY(9I%E18-&?hUt11R{xw&+V-9KnnJk0IVQTMm+b) zP%LN;l^3W~#Ih9tf_EPXjey}UN_dd5z+Gt&7;s-s0a^v_c$jE$d+(@x(mQE+aA!b| za7<|O2-V7gVcyZRGyogmx@z;4Q%UcrDZod;gSD3ll~d6M6yiLDo#4-TEN7{BAM=1N z4bWOb3CUNbRo=_9aIDqU*!zSN0|na5>eeWD-Q-QIc5l^af5|hauycp_PqCGIpU(BH zQ}n}gNX|e@PDV{)0e@L4;;V>&{wd55qd|cSRZ{v}NG!m=L1I)3V~mo%#P?2^K5(JE zYhct3*h6PW4*&>}*KQv{RuVx4;sj1xbdV3QoTd3vj1H(r!RfQ|FulmF=M1 zvG%}8=6NZ>8HLE(l#$MYhAXl$3i45u9cijjg0reE{JB=SI%dRu%#XwC^pC1`DYnuL za+WpIBv$ie-78Gxc_jBbdx0&_uWtIIH!oT0{f$>5MmUU@h;PyIrjE^$du1{*8Kr-~ zjCR*4LihDeraLtDDAkS=c>5TROvelsNJsIzlNc6#rDyvehJ`eW)C#F|AJnTJehw$G zzQwgNf;3kB0)?No&BM}Fn%BYUw9Sh4L^3E>5ru>tVtLTE1pEp}=g#e*Hqi8QUMw7zbRB)AF;KhT`xpm?#cCC40vxq8oRFpKB~8}hMq>y#qxY(kWx@~^5E|>(d*kk71rM@_uoIsh^%z&J zT=s|6Zr0Ll$Xf1~w65;H%N^zf{oqVWM^0M$L&4|2I#}&X)$J@7m@Et1<5TVrKcDer z*Mx1-s`WF$;Lf*fpul~LpU1mL==yFYPAzIV7Uq&!d2e!9E>8}(OcBkLW9)Uk8aWv)A%ht8dL6)E63V|AHEphu zRKhO+E*FJV!d61kr8dU}6&jms+rj?~{7EqhFbLjdt}N69%Anc}U`f8C1Ogd6jg$fY zsXT<8ftt{AMeXINYlgerk5JzBDs;(OnU)md$K87sAZPMGE~e|O?d`<=Z3fR?wVDxz zt=6dLe&a5!F;tRhX$zI5IQd3D1JSgxS6bu)Py}Ng(enOOjFmyn7VcZZt_`)GJTNF( zX{g+60g?c+2@niKArOcIi0+f zt1Ru3w(XB|XvvA-=bWTpn-Ogfx0m-R7J{9k`5G&-ug0+@< zcxSApIOXnCXP*A&uFdU(8TRBrxiTBxTG2K6KWhT%^uIL7p_JtkRN_!}; zm$o!|e~N8MfX{HfX5-p4I7wQNQ(Rag*QoD5@-U{g-H%MAUv_Fs$J@Qq?tEXX>C6>1 zv7R`UC0n6;52|IB)Dag`lR`rbptPhe0(52|@4g3C(YFkCpXUb!L$d}kR3>0V;3Iwm z@m~th5A~oZWSZ5&LyUn_fv%&F05A9%YkO>#w8mozU+m5X3S^vJdF>%I)^u`Z?(IW_#W z)zg=zn*!gIFjD-W=8`iV7VQv;9LeDs+V%n;Ly@t0kNYXj>iyibvD5(>-XhAsarZWV z_ItlMtece5?f(Yf@XkThoPnyX={bUX)CxrHvbh95PzN+wnl2Q#+l3fH8pL{o&=5dG zzG181-(VsE6AXt0JX$INdO-d*A3f95N;^;qWhHo9!x7Es06#*5aACABC}^v-rbQfl zXByKo4Y=2aA`r_GCtubM&lxH%(2fs{1ysrzo=QpLLTk{j?AYD_enfZFgnma~@0pecfi8^BsXMsJ}eC{KVK1p|5f zQjA%N>5Ue55wa^$Vh}SR8OmEt3~|Wj>fv}0P{0oCtl)-xppsw8Ae}-g@aOkKiZn-R zi4{hVz5KK#6>M1OLj(+#u#>h}13i+tQ8O3pGfKeq1G)bn&}w;6?R&BxZt5M zL+%NkV@Sa4AH}bYh*?1L;F6N$S@i5fGTB@!1jn4EEjKBNxSDfeJB`f+$s*e!0eXWW zeo4EE6S%J0+lAT;Io8^v;KJb-bOSBc1lZOIg|79%!)#4M*e#M#Ba)Kk7oqsQtTlj# zg?|x%3GRUVmtw$@;9)WaDk>m3W-AY0L-*J!ycipA%*Yqjk?TvLR%83+jQ*n7J2A52 z;-ujO@;nrX0DEn;R48i#?q6ELa8E`HuXh)?Fn9_AdMphhLy>W&6o%zzTfAq>+NJE7 zvr>cn4p8p=<8gD%y~mc8k_gnEl^#-uGW9si(nJT&k`a2&lCchuv6U?Azl(3FjgsRl zMue@nz#x>#gREX0lMk+o-y8UYbRcY?x%k0ozD~9I!p%@W>|d5OSFlm7gaqm*PNE%|0Lj2+y}e;LK{#E+Fq4!aR_FVE+SgXdtony3i{4XOsYdbjOBNt4u`8s1UogVD%t1;whB74uLh)d!mH#%r#Uu{c(+ z0;4MUPRoxGR6VekJ{LbWjAAe%l_($qw0PN3Yipkg>T`p!=+J=20D9vwlwqj(0>l?O z1y2f--XD0QMF$r=1P)yd*v*S5(f5bZEQ8+_YFgx2!VE0#O`0fYnHG)GF>~$235xC8 zKlhP=58^NAN~H9$fXWvW_FdLasG(`tUdK4!FiDmGV$hF8vsCx9oTCBv)MT}e0Qg9- zbLAbibBFk1gw7z~Lyl^T#Pa|}{Hr#)@^c@-^Q2^v zY)F2v_OJRKDyBt8nU^&ICXjJl=c75BFo2*sF1uL#URx^geN$o+Jy^mV^EV`?l%^fo zg=tz`_3$KH*+IuiWI?^E14VieW?J5gGcfslG*=M$8%NYwWP?%&O+tebE6s@;J$xAQ zctN*(tU@1w1fmW*p;ZIDF|VM#YaAaTwPAIwr8l&Gp{BuFG)X|Bz^yjI2LnL>K$6oP z@&i*Lm?+e~Jxr@$EN&6EDxfb0qLihH19wQc($$nuLkn;~z9UO;+)t<}{(!q)t>ltk z8z2Pbszrbki}OK;;&cd`GH^ucdcn?U_r-*BG>g$R$FRKIrLj8$Dj&&ra(;z$nCu$uF!NXQ4*)87GuQs1x zw`#GcjvkZay{zd6|Exd@&4Wqv4P!~~s7^ldC*M)DPoNwjo?e8;*BPS2hFSV6q1q$K zg2v*g8UPVcHu6W)Bxuv1nFY}T>#zX{gvuSdKG25YAwZkL0NR_R&{v^Bob7Msp{!hm z{JKg3;X&qkZwRx-DUy4EO*oqv=dUjdjv@ylyEfu>90krV!BNVgK#PlRrfUWGv)%lx+f=_IiJQzDKfb?TgYyy#_B>Pi`G` z#-c0)iIx^XIhuTHtRC^Wtc8C`8#?pAX}vMWH@a4|BsrdhUi3KN$~)|FAS>#yV84N% zP1uzVw)^No2$ZY8;DT-uQfTOc7KVt9kyybEId-aI)k3QT!Cb;!7o9{>KPnV)hC@TK zXj+0Ubo08DFs=d%B_UjR_eNJn2Xz9dkc>e8r%^i;fgBJNly;8}^Ny7O?JIdpVC^;6 z#Nq`L2JBTE4a8+J0gu3UjYFKXW~(2Z+GZ8;bwY8vb%c~Z9M?SP0C5j;yhj&psXc9W z!^i8onxw!1o4>x%wRw-QLe^pr1ezq}!6#fY;80%Ad`!e?Ja2nVxwBQ zHEF)Fm1$CM3>a)Xl4<`kR8XBv$h*d(;2ggcOYNy+nq(WDP%R$4{PiUq19=w1N%*xY zG#J}377c;I1|=pN@I4M9BN9X7&AEOQB#71lL_hm8$U$RM$TcWjV9tyxLX4bO!4Rcn z9`Sbv_EOZk0W_P3rVQ3Im`4p3RLmyeR|RBOp-~WJ1>76N!BQR7aRWb1IQ5eDK)o1> zK)tYE^i0P=*$#lL;BXNB9-*;t6wVI=-j}6^1?wnm(viafU|}{YC}y!+1rGx*RCX}x z@Cww?cJ7N+*B>DlGy4Re+Wkzmkw0I_7is^=pHE;=jB0<>%tZ7QCctl^LWyL2*w_-e z6-+=NlfsY)8nh^6wkv~Fs6a;*AL1SmKpQQP;;smQBod*dxjyAMDa%7h2x5E(+!9L^ zV-8>(WDhf7EO6GyOdbJbko{>;NIdaF!Q&1>py)_IZ2(EAe&Nza;}au}+P}YX@FP9m zog3wwl((=h1|rrIKtB;QPstbE z-7pAU6E>cHmF9J51ly|uoCX6gIyCNwlnFupymFPmUIamoa17!evW&$t6crntQ|tyB z%BXW7KZ@zAT5ETj5aQf?l(o*(I=Ej0zF4laa0P=s7xl6syLP2fWg33l!uJEhEq zI@s2kUPf5~;{*@`VEJp9@-8gi)oC@BHSRlX6bYQ^RJe^IVNxDV!O<*dpKS?jH6RtS z4ydGCgv}ZNTkT);(4-7%S`K!-J5dP146tEapf1+-0L`JW0s9nV?~7F^s4ZNK)ZZ1s zZpjb&Xh0~iR&c8&^bcn&*kucH90=3Rk)9x2XZ$SJHs2%|6_ zfqM4bq!E!c!D32h_%k3!n=#OSG1Tk_S?1d(p@9qCaoDKwkj}f0ItQzC9PC06e?h5n z+TwyP3=t#P3(u39H*GY(V46VyXM(m3Bnwj5FM$%V&Q5y=*apPlUBMg-tH3}UBobU$ zZ6KcLA(yLB16aWQ&8V1Dtz1V&Ky(^HZ{w9Dfm{+^Kzr=%kAlzVz7|H3rW@TZI$?jZ zNs_tre*au3K_TFMf&Ifc271sT*k}!H7l8~9A_Ds&k3h@}yi7W1qt6WfqPOh^?p$vkKoijgkB*R7c6A)td*Xb^zp9>z=vZ^^<) z#XIE0pnL@jbD`fj@^u#Tzbw+{&rT3DmQj;2CiiFXg4z>nouDD%e(1h6xnNs0@F>=I zF2S^x9$LV#-;fR{GoLnsh9Uh~6yd)p0#U$}kis6}zUEXIoD@V8niSfd#Q{R?FG5U`lxlz;Lt;mNLvlwA0vLlXW-)SbFTn@^rkgOp zC-nt4&K4jR0)O~q5yECtB+({eXv*(}#Doh#7$kkHzk~k5Fpp{d&z-+^iDKhFhKT&5 z^}w}zgbi!JctaB-kMoz__@gle+H!+-Fp;iE!273v1%We{;M2ELVn`n=Gl2qN!|$<-DUFfbg9mDD6<~Pzj0(9RM-YF_OL$U=s!2YZEW?$_Mtryhux{9 z{Xp10AgHzg&`_{o!#&gnhN2AZo>krl*kvtUy6^WRk|aP_z3em7pzY zQ}Wdgq017*F48s^Sa)`x-FWPk8ScCiR57rA)?^)o}Pmi=n8T7S$Ip?CK=sVx}R=eGt zyytTa7muD5X;SB&`k}mFUFb#~AUX9xUnRI_$A}4AXUgaCs8Ko7rZ?2S+CsXcf*DNGk*+?8RRUa(nNSjOQJ=Tcx;LG#J#Te>c~W7^(V48AFe8t)U%t>*de z{Z==dy7!6g)S{+iVNP2;2M2l6O^$`-yY!)xSF5L)(&cTko@e0}M+PY>ws2iJ#WSx2 zGYYnAUE5g)QZABrKXHgSZ!i#kb%7IhZ3r)4b@k&=RT#3l+%b}yA;@+0yDiy{8y&?I z{+pFVo3FdU#;&sot#$kOgSZyd8T;ai3uc0?@f9C;D!bZ^U(FYL8)7!HTu}B_!+0n( zv-FpY)~bEva;wbSkRv0@eCY59)a6!cwEEg1>ey!i{^+4QnODxrSg+b& zT!vjeA@@d>FO5dPVl9Wgtk zwM)^m{-#&-Yx$yf5|v@*Se>pDiF%r)*xKzqeqlhaj?4P_))*E+rh-I2#(s}_qZiy) zI}&oTuYcSo6VDyY{?wasaZ9Jq3hx8kPF;FJLW2?hN9uyIL4c2>7|SBPJIAi7ON^gW zRw~Z_tSTi_JQ2bgCeIZ}Hb@A}xjvwQjC#7sYH~U5Yq8Wu;rx z=*!K*cdbRb6o(WA-_WqQ_K)A1(yBIdI!fgsIpw9naX)fsaKMd_A^DJT2zQdl{Aou{ z!mh06mgghrT?Z~FQ3iXa(l}>Gu4Wuc@4Zema(YltXve*}$Z;P3A_GrrfdisiB@6HW zL~2v#yOYlQIOxnDZmLqgzTm<*c#Fy{Impu^=G$q}{_6}qghBtkk}1=dG+(lRth0>atV~uk%q=;y6N&KA2NCj|q(E%6B*& z!)X0l=RtYyi}qiG^0MwrfIDq#B~Fk&G#7rcC-+6uxMRC&OslfJP3M9YtXs)k_4#dl z`2&dSnQ@zQpG;>C^Ybxqoh~<5<5r90lHF}b=HSR>sM;LlzY>to5HWI5n?pI(q~~JK zSXc87R>_rMsoP^F(mc8Hp|OYP?L2qo#21W&!ovQ2;yTpbk9Jmde`6JAq)KExQ|h_* z)LzEL=Q+%~^rSiDg{Uv*oKb!uZbL&p+tB6L6C--8X!2Qm0nXL2bf%i`^}4|)jq@5qzj5Jf59gGi zakQ~{)H?=iy%I)~=-rR%^w+w(CiC-V(dsRk?O;-}rn;U>-sQ2sTh}2&>xvj&f3)cf zkJad0W|m9bjy<%p^k>9M_@o6@smkQ#9g?YC$u2&a4v)>$(c#+alB(dBf?H_cH&h-; zTEe?eP&C`#f7s~)vvwjmjZX1lDbr)Y#1+cq)BA3X-jgH~A9Xlxm`IjLFrccJE1I*G zcTnRRx*L;yGBR5YdsF9??-Tq&xX#_9`n|$jo!%@b%vK9NX~g};(Vo#*)wpjR-iO&R zt1pHI>r%`|9X{* zia!}URc{$@x;I59Es-P{!CCYm!+?QfDV;cY%*{K5$J||CU7l(D{J}V7f!XWCh8I)p zWOgqVKZ-h@R!mThk&@GARx_L2%g&l&Ij+nJo0pN(Dd#$TC`^zb&6TE0|jn}&Q1V&2EGaH4C(URZdB#%GsL0>_55=6F? z8Q4!{YRm84Z+uD>ufcfq?blP;MT`%b#c^_+7tV*&WIa}o2)w#3>o8aDtlH`J(vBBbGn&F1|{RF2lCR4TqS)%WfX5H2UC-*9zWo+1= zdowO?V$PMLS(!)M@7JA8R-SViMPGX46S{TUJzA{41k+b=pJY~T=%1Ayq$WgE$bGtc zppL7Cy6aBGwlb;+s;*vd~k<^Bw?J8vs`1=gB$iZ7waPJ!4i4Jk;LB^G+?XTO9lb)v-krVX& z>(2gi*jo09+q4y^<&D`wdfg8t_sV4$c@Gr2uY_o2_s5f< zGFTVBvzvG-rt`kLS*)C8_QqXz1@Si&vTQ~9%=^g6Gk1vMJG+b3uLhD)jShaGqW3Nl z6DoYZU7+^rHnLD&Ij_9-iH*Oi(8op<4)2&W!;RotvMJJmhjyY%2Pf%Tmk7o~f6Xfmk0tBvpTW}%UfhVfM zsOm?pJ1N;U?KS(5PCAS7>Ypo+h&n{PuxB$k41eOMzj3inu`X8otlp*mSdvRa z^Gq+*a;Z}|_#B9J~=GWVB85sW{2=|=Zk5xcL*r*t(@d6TzU!ovBmTZP(nhmEk{_mkJBdB?k#azm+nXKu~h7s%nc?d*NKU_hWQ$9ig6 zxhOezjRan%u|b{!Qh~Q9r=>sU#9R_t7?E7$sqf)1S=m|?g}4>V$aolTe{^xbKyC{` zOnvz@MSK4iC%eMQ2|i6uAEFO=E3L^R+5Qf?ms{7%=gMUHckK!=*-TGDo8zzkq76A` z{`nryyH(4~w?7pE*m$ZwP}^V8zp|$bfz6kmO^;04x4h@uG@?#X{Y`mZi+*VKfpEUt z7ehdVAay@K0nViJ0f1KSsbQD+Hr<+B#CwZOu2WN{eDnNs%1d|r6~pw;U(?RkVZp_F z*IP#^WJXY4O1T_ztSqTv@7WtZ3eC3tQwVFke-vO?6G;85g5tOwL*)*8$saAmeY{T) z9=TDD11mF`Q`ylDP=9ta(U9^wOI&zPgc@NBf`w)T3(Ai~FDU9Rj9oTZ>U#1l?#22Y zZtdaLB@_B~QIUyFlLY~HTv9S@$eT06QrCmd6nvw1Bz=MovnaMLT9k8jr=zg6+kGCD zv|IT>I>I;HM(ZV}owE=Clu|{D`$&EwMXYAEi<5(bg_{ERg|58F9|r}Vw;LlYkf?c4 zwBgr&pRHd3y~%OsV&1A76SQ=~WO2yh-ezK|Il_=FI9O`aC%Wov*+K6WYlclz^E=~XJ*(n13u0RAgu(@@ z(s?iG1t+F*Vxbph8n)*x1Wyvhw7-VD9zLvrekmuj@#JY^ZZ43f1Uj(u;UJ=R%65;Z%t?Xkmvd>Hg^CQlgU87N zAecN+Rkj^&B(}KH%=Q?)M6QMs5X`Zq@XZeKXLgyK|}groue)(qNOu zyEh8jy!pcUIGVdhU)dr!Aq!+vq~{qOhzT~CDLb{n30Bhv_V%~4YgnzAURB z+df)$OdS7*>TF8Gd*THhTmvA+*xLT2-d&gU|HN32{d0r1z3QAc!@nXNT=8?SYVbyvLxvrTe z{vU%&L=luI8`C=wGl1Y~nKL4>4}+fh^#$i{a>aV8C`P~O_I6iiGuNH0p{iA!!pW{V zR2L$B4`>gxbx~D@p~7fLCT-i$y#5*T=gPi=-3RRyU$2WTT3n1Yh`I1%bJtqIXL_x_ zCTS;qJhIr?jM$&S?Bp==ovHJ#xQip!3m_)NWSkmi3`Y!EHI;z~I}T$~jAtUA|MrZ^QJPSb3e{H);4UGF5+ATa&jRg{o?J|@`c%>KJ;3kB!*KGOqy)?~J|>+#F$dS&zXkDCxr~g@}cz zkFi=evR9sL2X#eGPu4|^;I+sdU#F~G!xwC-R$z=bgG~G*tz&6>=^s+6=KCz-s?u>e ztV(!Wa8AD0SaPWOcF?9U%8!{r0fqh2Z`g|6DP+!^dU+X9XdWAf9V7}J$J}2(ZTDO( zY9{yKIwUmfqCnrJEweXYN!FTRui(fX?XdX7kP4&0{X0Z;ti&0ooEf)e0n~hacQV_A zZ|37V^;!fPuAq+_aHcb4v2~-5$`+qIajS$p=%F=K` zt8i+10}Aj(>;Aj1KE*|-X6FnFNLuIf7Lpf;-9i-Ednb$dw9f%0*MWOvcWjI{a>g?$ z(V<>R>#tIDhYOd5PQ`kJ^2G%uQN)*92Ap6Wsx^VoS0Zq++P*$oCrO^1Hb98h<;y-a z+Q+)LA06@8AD!}$l;u{=^=6yoK!TayYdo{_KskvSGSA8CmFr?8k-3(>&v~fbh8VQh zP*?i-W5n#@Z?(2-PGnCOb|kSl&--!D>MB|G@SAlfIRC*Pll(msoFWN8WmQjX{)01R zuUiA;Gu>>-6~>=FWH^7ocJ2#yGWb6yb0MzKG4XtMS*|wogy((dcI;h^XDALRIp$)7 zl}gTc4kN7JA{DX~W!W+y7y8fH%SWC{Dq1;%uP*IZ$W>?4F=+3Ww#Uvi$}~5KgT6m* z;qK>#kEC@rPc$&%S`=&7dRdlkzPpgi7Va>ziKq)W@7sW`lC9KNeXwvJY|GGi$3bp_ zeU9d?i{<@eSRcfs!fjG=;cIy+Kiy6GGp~-tY8|l@c}CQDuknd5b?MxcMy$hzS1_bA zjCMK9#ydFy&jI(dGd3^cc6`LDQJrW0N ztQtJ9zZyc|=9*1|)8osQ)z&4t=eH=j4>#+M|8#wDXoZQXM6RIPOL>mBi?QwZ1dg5@ zk0Zm8!}lf;s)SRF{;&V{y1wl^E(kv>{J;N`Syx>zc&mG@=5T(&|Gt{ELhszzuYd_7 zm-0|EzKpzH<&9H0CYc~fTEs%B<3!^# zV`1h!*2|YgPt@&Q7Ea>|^yQ?Wc7AE3g2&MaZ_&n$zDj15{2Ce;w-hSeSbfucf>LPr z^~U?crH$)*8>K|`eC2J%PU0wJLM78eZ-$slO08{UF?{hrt?~5k#=esWdRuVD9tYSy ztjm6{^A6wBTWUm>(KvqdH}3S=n&qOnjjKP(t_ohq{w4DGh!p zM#nF!{d=ArG4hy<_GteQ10FAg96fYZd6esH^dzrH{1e9UsQe#+Y%I{hE~ zcp1@e_2k0IcYEbEndF}Ob%vEod5#pb1s8Yd{NfTi9h&;ui0;FOJC9O4g`Tm+eQSA) zYupn#m>I4g`jl>bp-Q$h!1(HuUtb%gMw?YkGoIaId+EdU*g!VPi9wh4XViA4m-d->fzD%04)z$L*F!_9VUH%3$yW z_d{V_nUf=vsq|Zn`ENhBu@Vs}Jdp2vxJ@@N|Evw;_(x|k;cs(>dZq7-1X%+-MeVvf z4h8Q&w91_0Y3=pkq;t&Go1*tDdxcZRx4f8ISF?LXZTHIMp|tC&tc#d@IY^fJ{jz4Mv`9_3W0er);a3dFBSV==SF)wPkhgt1 zNXtI?A*JwO%gx3jqD*3&=X@;>4YgZxbj`P;B@>p~%^{M{_B>zjv|1}?9Tr@ChSPSs zvG7Fh%}0gFF>msGT#ms*iQ2>Mhhb&ZfqX?8_zC;Zg3&cxa)g8H$G)N+3AGN|O;0;Bz2bKY z+sKQ??q6PPiPkWwEAFvQWhqJycpLovu-3k}3H#^vYz?uJ3gvm^J$Nxn-Tj(^YxjQI zXZyE)tW)`D;qDMtGG0%~X`KI@=kXTl{&fG?r?JteXJvl95_#(+q0Fg&xI(+UFmE}V|^iad>d=y#S3k@oc@M8 z)Wqwj+|vwR_4?DF;W+6^+w3QJpOJ!hiO;&}Lj$cO|h<(sM)uTRO7BaNEd8k^md2eo!wr^x)}-g?FW zIwp;36UAHa%;}80+h>wY*cR0%EV4`y<(EjeJF!};K%e-tE6ewIHly)+Lur>(wXmbq zS3{!z`Bp!75ia}Nhg}~EN*K2$a)-7V3vYkFURU+iFykC-7R=5*>iX9I#OWP|Vlu<> zI=8uN^P-OqagFTG2p!v+1Pkd3M*;!rg5XsRE*lDG3sWlL97kfxGZE4G~v5 znhyljQy1*1Ok5k>C9iE%nk>w}heF(c2m6&T5s6D0-@{dI%YT@Dv-b#>@Y%Z4CsV|% zDDh%F^2}|9?fYELCR#t^<7M@t7s*LHrl~tbDQGCw`qhC|y0$j)9=T`O4g^#~2 z3l1!}TwpylX2x$c@O31`u)#>+=blTHm!;CL+kUCvYAvCsAWWt|EH0ux&#zq|m9|>H zX2Z*(HcT=4c_zP4RA=DRY2p5c$G+R$Cq^#xO@7+FUFCsPI@Proy4n}d_w@|6P92y$ z^1!6f)BJ}+dr|&{k%uiO*k$S*cqT-zjh?eitU*%EU$CjPa(-2CvH0J z`;Aj-6%+iN|ID+{@X4v*#_N2(Dn=)opEl#$cBj^^wKbZpe{}zNM0``1P@7iU+NT5Q zlMQvI;ZkqErC(oLZ!`=IFTEUCD>NYEB0UtaQ(=GKhg(kM`(M^nCNHel$8w4O8cwn9 z{5aAlZ+X_q$yvFb;^K>xhtlq1Vyk32RLco;hdCrpyS?a}z)?VdMsw3w%V$5fOO?Al z7VL7h^W74*#|VB6*yx;*s1A*1=VtZwl*!hHdb-u|79I4Zh)g&#GBP|F751fwcsve= zXTk}{S!g+S_=I09y0JvypOzi|^yfFTpKw_%1{1A(>T5dR(au0-!Q!H@ z%J3(AO^?a$Y8@xX>?7u;(RxCqsZ2oF#6R*v1)yt&2~@#|`d^L@3glL6(vUm2R_G4C zsPs4(=~7ZND49O{Kyyhri|C|p0BsEK1%IA=*3`8lWMLkx7=I>@!6|D4x=ZqyY9WoV zixHFptwcJ3;y#2wlsMx!gE~gZnydp2&M+E3*~7tCr=D%IqJ;S!eoDDIL+b$XJ+i0g zUKHd!c4erM;=2}>u4{@J4pI)e&%8AE@&Zj@TKkNI&++||K1Uog>PJ=!lnEsEQ0O~P z@3&~;vU*OFLH3-+|Dathmo22IoAiM}gR)fGQOEi| zg3Gd08m6YynbFeDbAK>1Q#7Jq7=B9RSGN(8o~C;w{(FrRbKy!r^__xpgM$UT$q8?= z9VYS|_J7aCdE1v>n7oFRcpr_?f!=bxS8l1-ekyeL`s5U^8`~`9bp=g`2)?+=xc{<}+_tJ=uZSh*7&2N#e?`D|}(;g=tSzJHx zsa)^1TdMQVeVo16)Kfnd#(RCVi`NrvmPNXLnPqS7g_~R8W^gjt+>bvA-MrN%Jo=d% zY$Ovl0yiVw`~0_ium?T(&Hs}zKM`XP8%1jg!^D+Yh_7QF&SBHL23I*!eR#WOGq4+r zF&2JfUYr(VUN^xH%gBJDh4&MeH$B85$<|&8MExJ|Gt9Akb(t_y|s%LXY^q^WYMV3o1Xb z40he@EN7Zp$;?s+qmt5}ooQ+haPuHP79o>xa{DynqhgP@_ir7V&UOcrMn$8cOdjS> zOIyG8DErIw8dq}IfmY>XG&=S4?le06D(xe?%fn3jl}JbS!gb|iP870XrUPla%Rk|l z`*UOyteju`;tqUdb-rsMj3RY3rmB*R6Ex! zHJ9oC(SA9W;W3J zZNI_k-ir4*a1zTIf?A^=*4*cVwXdC*yr2VjiK|Sty|7^XNsk`Z z*Gm%11kW5c9wuzcgJ<^QxJ+r03k>_x1t>7AKg#a3Su>yl?0(18ai^CX3mD`W)P*9ISrlV@UNEZV7G!rL5tKWAMl! z!sL**@^%I~ib}>5TJi&$P zDTi75pnDVHGy1-%RGoA(E0-h%*F~adD;aLJ%<4t|vC~Z27oOt-sqm8nbXEPO2wydS28}k7l zzW5SK9S#-!MA%c6?HR1&(=X;aGp+o#y21jKs^wi2crL1o`7Zoo}z>XrlRPh z8=z{SlCh)5LhO+F52JVZ1_21~P=Evi1`L?LKE|&oFtzgKNx!MmjqNsCTJ*o3>$*Np zUcRqZO*dszs3JY~XR5(~>`j*m*%gh0E$cp2&9C;Ik<@LLuo)Q^sv6ouA@Q&Wd;0yR-auSkZo7#yTxtasCoFj3KN(9l*44R>Uj$D^+u1A zOm+7ijU*fz*T<7an~H zg=L%NB;S+DN3SE9Y^$=!p$Gg1!oNjwRJENp@jvOdwHw`fJugq_*-^c9L>(%e}z4dz~Zq_Z;^9iWNKwR$# z3VihhBmmmJrD2d2TX_zzVOwVJjBGqVK%wyOv&W->%LhJOYPPqjVQfUTOioW`}ur}t>Vwn?7pQ%A1 zUfuq+&Qx2`<93(5(=?|v-^a5K>~hy=AMdSPj{buQ^0EAtS}fuJYIb@+%1t`>CZL_{ zs;)|(ze`6DWkc{g_tJ4@So@K)6f#**9sahdAiVx9`?Jj&N{IcyR=@=i0H`D)QtcRE z<&^+s9PZg+C$I|7k!{M){YS8DTP)k-p^gAhfdo&FS0oQ0#{kb>_dIxxQF)I?h&E`v zX9r3NRR-T4%BwD; zl2lBm^iB?rQjN70kUpS0Cvhe_`ujuSimUOp94GrQHnIFghT6DYzZ5lBR*E1IvZrAo zvrPq91_JZYqK^wH{ZLDhOt6)&cNqVP2gz!{tphx-09)MV287Ij7N8CW=Ar?S3+zLJ z1QAhi;H$nyj~8xpjCn9NU|`VNLW<}`EeP0EC`zV|h1FJ7(^67*a6aA893APX?DcsU zIt3~ReC|a_sSjKNvEQmqVS?RWZ@5&cBD{577@y#I*&MG+Okl?q^Kx(uo|OZ#|18^< zM$f?zHbWd0W;TF7)Pa2j@iMyFgYX&HF53~cKx)jzroL-Aw^|>^Jn6C7$_irFn{&5a zXd-I~bjpu2k&&aS1;KEccoId*plU^0X zH$-*%52lDZEBEIgjQ0~7a(=(beJ*8X(i9JIhy513H`P$@{YMz1)C8^~+m2e4s;KwW z5r_fG;0cg{LWMjb^*m;QD^P)&$r@>@bPpoBvgJloro32)l`X&uT8tbB99)jFw8=g1 z7r+T^-Q5X6Is4sez&$%d8gFsUhu2KKrk8$)JDRfAEc#B?ik&SjjR>kSjvHT85q0|5 z-a8w1|ETi5Qf%dDWZBv<80vEEs)=@A8oyTY@STm(Qb?FdcNOPs;+S7D4rjVpu2Mnd zFj4iCW|8yW$g1Yh%{7rt{YYRFphqOnfZ|KYo}Z)8WSxfCEeHTLdH~}fB>dg1Z;P~}ERxs935fETC;hH(u|o)R=;kat?gHg}hSAQ^? ze$<pZe=#6Ap4JvF;^g(%ZuPxtO6FChu{W*$^uB#8_9b78hJJF z=DLs>BnwSMoB_6u`bJj8douz^{0`PMR;A63bCN(#7+{SD zz^3Y)!H@3qF@cpEOnw45YCrYC_liVtQ`AfI&B%%=V{itWD+DI{34pC37Eh5}x4CZ( z2@bSpe~zvL%d`3cpYRZ%c<7Wt>%ndS3fS@<1{~ajfLP(~GK#CdKtj3rP{$ICXc$c@ z6v>{SX(hf)TD9+rZw&a&!4X^sm}mPOI<$$5UQnrm_Tl9+5?`hv>I{Mi20Tdhcqb0! zbF@^&?+*qxY*{a`A_VPvM851xQ5C-^4-K9|sGu9{GwePe9WB41_!bY#hx~0MVyc1l zqnC4L!C5e=0ke`Hhz!V<73QM0^_%XU8xYR%m@Z|^1Jaw|HC?*bu)-A}f^L$r2SBmI z)mgCJ-Ra7?Mt3lg!{H@(vF(kKAdXl=OFcsEuFcg~J#*JwsYwT!^>3>XXLw+xMj!o- zE;*DZcSc<4)x>i2rh&laK)?AtidW~k$!rO12R&xj6zA#@4mmwp@e30E1yUkfLH|=D{YQBSv)=)f94LGUQ_ANb zl2;A_&B0UYBf={wMm(>!Nap9At@3tm10uT#N)`m=s5cPnl+XbtkG||GBt)R%y7oN~ z0WJj5)eN&4KnPDNkLfa?M*XHnb6rk=Oj&U0TLVmK;2`LWU&1*(o>*T?!Jv%2?S{Fl zYO=?*h`o6+jQweO3!dHHE-ast0vRcMpU_PZbG171%~P7Bcc6a?q&(gsK3m>B09fZ$C@`2*LP~ z#7hla?N5Qs^7=LM9>`Fn-~fINDie&o1%9aHxlrjnre9-y!OwRNan4{TyM8LnsAB!d zwrUMn_amtl6ddR?>;YmsLWO7l2!tL9^d1rsxNH@m89-)Xpd6h;0&NS8**Rc42Mj&m zU62e%r=j9%`#_s@g0r#M*Hbrf?(+|+sZW0#na<^p(enf3hVF&7pnO0L0*+3G0Rt>@ zo9hDdTj36NfY<|^_*aMFQ6BIhm!CIqidaruD(8fcLf5Zi#r@p-w+ks`I%~^vxPq^; zKpFRPAblv`eRu3J zwUTHokErLBuEEi&_!huzKLNmSV7(MW=OP7T0_E!NL_GwoH9&*~{ziBNVinMiS!p34 z7Em_&wtbCK9Y}}dKd7L>6KRd;FMGeKab7Yo`448)q5v0z=P~sMe@y0VPvg{1!ySQS z0*iyTI1CGaAe^+am6|o?o2i4)roZz4=4KViFWeKshFcGuXyR#KdT0y=KpX&bw|T11 zRjss_j5{!bXP_PtRFuB@s~lkZ0y272w)GA4^aCV=WSPltuCAU$wm>o?QGNXeFAR2| zm(Y@Jj_W{qMd*<1bS@55AzL`$nZ88~cZEcYMb6)B_sd(0GLrz6c8h zJyp1PIVIK0eg57Wun{#e^KA+=9sCE`e`RsKt12GRE`-C-B-CgmT_Em8c;0>jMsG2w zD|W;(nlq-$NDV7QB-hnp4p}qvYvdeL$+gNwoX#l$Qqq{5mX_!W%$c8uwelnb>A46xyI zYj_7nbrtSWi90e4#jyEojr&9L3WqL8=t@~I0stQWH!Y*&WpjnW2}}e2{qI-k>Dh5a zm64ZTXHbD${~NZbp|I$arr0tbAuMtTDL$UTUZL&1(0JfjY*oGA49Sfm_BDvX`3+|E4Z_&7xQta=?4DWCP!gD?XPoU4GI zJT-`9-M`5du@HIL2g?nFjEI>xh-yc#BRilgUw?|ha~AMe1Ru1JSa@9CZUkvbq0~h2vFRl^ECc;V=A>cXIT`ANCfOaY_}QW? zRX7`igejD0I7;k;1%<#lAT*%wuwd9KBw&Vt)rKdq4ip?7VDo6WPjkLov zh|Iv3iF42?_ez6*O=y<}ze9~42j&mj-2J&_C#&c{pGKdS4zX_`A#THVBup!b5Ov zK=$?BSu?bnJqn6Cq{e&Q4C;QM8x?;&gDm~kQV0PMR3}?BCNkvu>U4>a=o60Uz zpfXb4ytKUHvcpYCc5H$o`cL$M*aG~6)4E;2f6%HdRd7&g%nz_<7R3218JHb$hoijK zP*}bg=gooe0ULfJmqf-TzqYT9^a9Cy+b=tIB0901D3M@8oQp)#BL|A8_On1QfLCN8 zEcmPm@_)VZ%4tQMA`(}>in&9ve{&xiUYEDt(Z-8qG#nZuqhwS`@_)ebDpXErEEr~0 z0EPo+0Vlu;9MA+AO(11v)-ZGllGa`Wy1=CFj%IQw8?Rfy&cBfrigyV`R9Q(3`!Qq( z^esGLhvEN((yz(RnN1$*Pk#P?bKQ>jY&QSS@jwQ0u^=9SlXqaO(t{-Y_t1^##3x7# z098P>fy+QM?Z7n6gmxteb}&`|JSqo+b{SN`p$F?_A(s%fg7_jJ<6q3**teE~*n!4- z!kurs{`)w0U>Y2?f;JJ4BZJsf?ff5AD~&VQztICl53NvkN*%sJ0}24N9+WE31~7k) zdaAs~^X{y{SrA%a?x@*4|3?!X*6Jc1OK22M-jhv#KVpl0h1EY zA+O^=E&bO_NH5qZ2*d$kG+62fXdP2XG^tt(ogD7S#!pqlY_v1@R4Uyc z>>1zz^H3D%glYz$)?W1ru=V!_0@Y?H3e%7){Wu9bN&IBd!joAh$g%KRaP0?k z!(bX6p%F64^ZL$KXW>8;G7ZKLs%=}u!FLHx48l1uz&Jb#XiA!e96|>K(2uX1%>e|n zX8kZALHFN)`@hz9$6yida{8NMwkh?GThK{sd@#V!J__Hw_zvU%PR)T8Fi`?0Ocnsp z4VTbiHk?>M@`)!zewby8P|z!&r@&6Y4M?bC_$@L>CPd6a=mbS!+p+K%{Pd?Rm~il` z_GPe`QsiAkb0lH_oLNEcCPAxrpk4g%Ia2{EpC%~BKcP_|txzU{qW0MxWEsse$BqJP zz$6;HFqAD|78n+KA*6?@H3#5dmlo>Lfi!}mhRhft>V`%M1v)Tp*6l$ z&=Ld+%x`l}ZiKF^`2N8lMVdIMUf?^1Xw|ayh6MgS#J|I7BvT^U9rR!S3w-O;48HvU z$Pc7=Ksd6;BhmfY-Jc_X&PUm25T%9AfRh=M{D19O0Ai!Z0w{z`XEk?T5T&5^NU}ia04h9)GK3y>t`lA#xC6Y80=QyZ8s21H-mU^N76=VE zkq4}M0|?Wj)0zmTsgOx>b!3Kb;SMU<3W^=Weo22YPAf>S-BPzn0_d<+ErPeMx53{Y z6o4V$&bOlh%heDIVJ1MjY&KKT1ki>eI)cUk6jhMc`{4}ZS@8eZp%KUn*!L~QKw50J ztAPA}7QSINQvu#Df-Zu}hyd>Gm-@wTKEI2{Fslt#?J%1$hu-et5aWkIvX$(Em1!R%FU&!uwp(ka^S;ARH!cSlCWbKfBI>>I>S|+(J%weoC5@#H4saFZ�lGD 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�bHl)ZMceF%CDIf?iZ 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

}vV= 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 CIwNNQ literal 0 HcmV?d00001 diff --git a/book/src/c75-pools.jpg b/book/src/c75-pools.jpg new file mode 100644 index 0000000000000000000000000000000000000000..99639c60ddbf9555db748b262ee5879ac016ef1c GIT binary patch literal 124793 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 literal 0 HcmV?d00001 diff --git a/book/src/c75-solidrock.jpg b/book/src/c75-solidrock.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bacb6cbb68fc6a274d1d026211cafb375897b52b GIT binary patch literal 141200 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�C0`ky3AdKw*+cYlP7qWy8%BQv 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 literal 0 HcmV?d00001 diff --git a/book/src/c75-vokoth.jpg b/book/src/c75-vokoth.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dcc35b46d6bcdc371eaa85bceb344fad230b555b GIT binary patch literal 108102 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 literal 0 HcmV?d00001 diff --git a/book/src/chapter_74.md b/book/src/chapter_74.md index 697ae38e..5e1d7d45 100644 --- a/book/src/chapter_74.md +++ b/book/src/chapter_74.md @@ -21,7 +21,7 @@ The next level of the game is a dark elven city. The design document is a bit sp ## Generating a basic city -The `level_builder` function in `map_builder/mod.rs` controls which map algorithm is called for a given level. Add a placeholder entry for a new map type: +The `level_builder` function in `map_builders/mod.rs` controls which map algorithm is called for a given level. Add a placeholder entry for a new map type: ```rust pub fn level_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { diff --git a/book/src/chapter_75.md b/book/src/chapter_75.md new file mode 100644 index 00000000..ad330617 --- /dev/null +++ b/book/src/chapter_75.md @@ -0,0 +1,653 @@ +# One Night in the Plaza + +--- + +***About this tutorial*** + +*This tutorial is free and open source, and all code uses the MIT license - so you are free to do with it as you like. My hope is that you will enjoy the tutorial, and make great games!* + +*If you enjoy this and would like me to keep writing, please consider supporting [my Patreon](https://www.patreon.com/blackfuture).* + +[![Hands-On Rust](./beta-webBanner.jpg)](https://pragprog.com/titles/hwrust/hands-on-rust/) + +--- + +The city level was deliberately messy: the hero is fighting through cramped, sprawling a Dark Elf under-city - facing different noble houses' troops who were also intent upon killing one another. It makes for fast-paced, tight combat. The last part of the city is the plaza - which is meant to offer more of a contrast. A park in the city holds a portal to the Abyss, and only the most affluent/influential dark elves can build here. So despite being underground, it's more of an outdoor city type of feeling. + +So let's think a bit about what makes up the plaza level: + +* A decent sized park, defended by some tough baddies. We can add something demonic here for the first time, since we're right next to a portal to their home. +* Some larger buildings. +* Statues, fountains and similar niceties. + +Continuing to think about dark elves, they aren't really known for their civic planning. They are, at heart, a Chaotic species. So we want to avoid the feeling that they really planned out their city, and meticulously built it to make sense. In fact, not making sense adds to the surreality. + +## Generating the Plaza + +Just like we have for other level builders, we need to add a placeholder builder for level `11`. Open `map_builders/mod.rs` and add a call to `dark_elf_plaza` for level 11: + +```rust +pub fn level_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { + rltk::console::log(format!("Depth: {}", new_depth)); + match new_depth { + 1 => town_builder(new_depth, width, height), + 2 => forest_builder(new_depth, width, height), + 3 => limestone_cavern_builder(new_depth, width, height), + 4 => limestone_deep_cavern_builder(new_depth, width, height), + 5 => limestone_transition_builder(new_depth, width, height), + 6 => dwarf_fort_builder(new_depth, width, height), + 7 => mushroom_entrance(new_depth, width, height), + 8 => mushroom_builder(new_depth, width, height), + 9 => mushroom_exit(new_depth, width, height), + 10 => dark_elf_city(new_depth, width, height), + 11 => dark_elf_plaza(new_depth, width, height), + _ => random_builder(new_depth, width, height) + } +} +``` + +Now open `map_builders/dark_elves.rs` and create the new map builder function---`dark_elf_plaza`. We'll start with generating a BSP interior map; that'll change, but it's good to get something compiling: + +```rust +pub fn dark_elf_plaza(new_depth: i32, width: i32, height: i32) -> BuilderChain { + println!("Dark elf plaza builder"); + let mut chain = BuilderChain::new(new_depth, width, height, "Dark Elven Plaza"); + chain.start_with(BspInteriorBuilder::new()); + chain.with(AreaStartingPosition::new(XStart::LEFT, YStart::CENTER)); + chain.with(CullUnreachable::new()); + chain.with(AreaEndingPosition::new(XEnd::RIGHT, YEnd::CENTER)); + chain.with(VoronoiSpawning::new()); + chain +} +``` + +### Deliberately Poor City Planning + +Now that we have the exact same map as the previous level, let's build a generator to create the plaza. We'll get started by making a boring, empty map - just to validate that our map builder is working. At the end of `dark_elves.rs`, paste in the following: + +```rust +// Plaza Builder +use super::{InitialMapBuilder, BuilderMap, TileType }; + +pub struct PlazaMapBuilder {} + +impl InitialMapBuilder for PlazaMapBuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.empty_map(build_data); + } +} + +impl PlazaMapBuilder { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(PlazaMapBuilder{}) + } + + fn empty_map(&mut self, build_data : &mut BuilderMap) { + build_data.map.tiles.iter_mut().for_each(|t| *t = TileType::Floor); + } +} +``` + +You also need to go into the `dark_elf_plaza` and change the initial builder to use it: + +```rust +pub fn dark_elf_plaza(new_depth: i32, width: i32, height: i32) -> BuilderChain { + println!("Dark elf plaza builder"); + let mut chain = BuilderChain::new(new_depth, width, height, "Dark Elven Plaza"); + chain.start_with(PlazaMapBuilder::new()); + chain.with(AreaStartingPosition::new(XStart::LEFT, YStart::CENTER)); + chain.with(CullUnreachable::new()); + chain.with(AreaEndingPosition::new(XEnd::RIGHT, YEnd::CENTER)); + chain.with(VoronoiSpawning::new()); + chain +} +``` + +If you run the game now and teleport down to the last level, the "plaza" is a giant open space full of people killing both you and one another. I found it quite entertaining, but it's not what we want. + +![](./c75-emptymap.jpg) + +The plaza needs to be divided into zones, which contain plaza content. That's similar to what we did for Voronoi maps, but we aren't looking to create cellular walls - just areas in which to place content. Let's start by making a basic Voronoi cell area. Extend your map builder to call a new function named `spawn_zones`: + +```rust +impl InitialMapBuilder for PlazaMapBuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.empty_map(build_data); + self.spawn_zones(build_data); + } +} +``` + +We'll start by taking our previous Voronoi code, and making it always have 32 seeds and use Pythagoras for distance: + +```rust +fn spawn_zones(&mut self, build_data : &mut BuilderMap) { + let mut voronoi_seeds : Vec<(usize, rltk::Point)> = Vec::new(); + + while voronoi_seeds.len() < 32 { + let vx = crate::rng::roll_dice(1, build_data.map.width-1); + let vy = crate::rng::roll_dice(1, build_data.map.height-1); + let vidx = build_data.map.xy_idx(vx, vy); + let candidate = (vidx, rltk::Point::new(vx, vy)); + if !voronoi_seeds.contains(&candidate) { + voronoi_seeds.push(candidate); + } + } + + let mut voronoi_distance = vec![(0, 0.0f32) ; 32]; + let mut voronoi_membership : Vec = vec![0 ; build_data.map.width as usize * build_data.map.height as usize]; + for (i, vid) in voronoi_membership.iter_mut().enumerate() { + let x = i as i32 % build_data.map.width; + let y = i as i32 / build_data.map.width; + + for (seed, pos) in voronoi_seeds.iter().enumerate() { + let distance = rltk::DistanceAlg::PythagorasSquared.distance2d( + rltk::Point::new(x, y), + pos.1 + ); + voronoi_distance[seed] = (seed, distance); + } + + voronoi_distance.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap()); + + *vid = voronoi_distance[0].0 as i32; + } + + // Spawning code will go here +} +``` + +At the end of the new `spawn_zones` function, we have an array named `voronoi_membership` that categorizes every tile into one of 32 zones. The zones are guaranteed to be contiguous. Let's write some quick code to count the sizes of each zone to validate our work: + +```rust +// Make a list of zone sizes and cull empty ones +let mut zone_sizes : Vec<(i32, usize)> = Vec::with_capacity(32); +for zone in 0..32 { + let num_tiles = voronoi_membership.iter().filter(|z| **z == zone).count(); + if num_tiles > 0 { + zone_sizes.push((zone, num_tiles)); + } +} +println!("{:?}", zone_sizes); +``` + +This will give different results every time, but will give a good idea of how many zones we've created and how large they are. Here's the output from a quick test run: + +``` +[(0, 88), (1, 60), (2, 143), (3, 261), (4, 192), (5, 165), (6, 271), (7, 68), (8, 151), (9, 78), (10, 45), (11, 154), (12, 132), (13, 88), (14, 162), (15, 49), (16, 138), (17, 57), (18, 206), (19, 117), (20, 168), (21, 67), (22, 153), (23, 119), (24, 41), (25, 48), (26, 78), (27, 118), (28, 197), (29, 129), (30, 163), (31, 94)] +``` + +So we know that the zone creation works: there are 32 zones, none of which are excessively tiny - although some are quite large. Let's sort the list by size, descending: + +```rust +zone_sizes.sort_by(|a,b| b.1.cmp(&a.1)); +``` + +This yields a weighted "importance" map: the big zones are first, the smaller zones last. We'll use this to spawn content in order of importance. The big "portal park" is guaranteed to be the largest area. Here's the start of our creation system: + +```rust +// Start making zonal terrain +zone_sizes.iter().enumerate().for_each(|(i, (zone, _))| { + match i { + 0 => self.portal_park(build_data, &voronoi_membership, *zone), + _ => {} + } +}); +``` + +The placeholder signature for `portal_park` is as follows: + +```rust +fn portal_park(&mut self, build_data : &mut BuilderMap, voronoi_membership: &[i32], zone: i32) { +} +``` + +We'll use this pattern to gradually populate the plaza. For now, we'll skip the portal park and add some other features first. + +### Solid Rock + +Let's start with the simplest: we're going to turn some of the smaller zones into solid rock. These might be areas the elves haven't mined yet, or - more likely - they left them in place to hold the cavern up. We're going to use a feature we haven't touched before: a "match guard". You can make `match` work for "greater than" as follows: + +```rust +// Start making zonal terrain +zone_sizes.iter().enumerate().for_each(|(i, (zone, _))| { + match i { + 0 => self.portal_park(build_data, &voronoi_membership, *zone), + i if i > 20 => self.fill_zone(build_data, &voronoi_membership, *zone, TileType::Wall), + _ => {} + } +}); +``` + +The actual `fill_zone` function is quite simple: it finds tiles in the zone and turns them into walls: + +```rust +fn fill_zone(&mut self, build_data : &mut BuilderMap, voronoi_membership: &[i32], zone: i32, tile_type: TileType) { + voronoi_membership + .iter() + .enumerate() + .filter(|(_, tile_zone)| **tile_zone == zone) + .for_each(|(idx, _)| build_data.map.tiles[idx] = tile_type); + } +``` + +This already injects a little life into our map: + +![](./c75-solidrock.jpg) + +### Pools + +Caves tend to be dank, wet places. The dark elves probably enjoy a few pools - plazas are known for magnificent pools! Let's extend the "default" matching to sometimes create zone pools: + +```rust +// Start making zonal terrain +zone_sizes.iter().enumerate().for_each(|(i, (zone, _))| { + match i { + 0 => self.portal_park(build_data, &voronoi_membership, *zone), + i if i > 20 => self.fill_zone(build_data, &voronoi_membership, *zone, TileType::Wall), + _ => { + let roll = crate::rng::roll_dice(1, 6); + match roll { + 1 => self.fill_zone(build_data, &voronoi_membership, *zone, TileType::DeepWater), + 2 => self.fill_zone(build_data, &voronoi_membership, *zone, TileType::ShallowWater), + _ => {} + } + } + } +}); +``` +See how if we aren't matching anything else, we roll a dice? If it comes up 1 or 2, we add a pool of varying depth. Actually adding the pool is just like adding solid rock - but we add water instead. + +The addition of some water features continues to bring the zone to life: + +![](./c75-pools.jpg) + +### Stalactite Parks + +Stalactites (and presumably their twin, stalagmites) can be a beautiful feature of real caves. They are a natural candidate for inclusion in a dark elf park. It would be nice to have a bit of color in the city, so let's surround them with grass. They are a carefully cultivated park, offering privacy for whatever dark elves do in their spare time (you don't want to know...). + +Add it to the "unknown" zone options: + +```rust +// Start making zonal terrain +zone_sizes.iter().enumerate().for_each(|(i, (zone, _))| { + match i { + 0 => self.portal_park(build_data, &voronoi_membership, *zone), + i if i > 20 => self.fill_zone(build_data, &voronoi_membership, *zone, TileType::Wall), + _ => { + let roll = crate::rng::roll_dice(1, 6); + match roll { + 1 => self.fill_zone(build_data, &voronoi_membership, *zone, TileType::DeepWater), + 2 => self.fill_zone(build_data, &voronoi_membership, *zone, TileType::ShallowWater), + 3 => self.stalactite_display(build_data, &voronoi_membership, *zone), + _ => {} + } + } + } +}); +``` + +And use a similar function to `fill_zone` to populate each tile in the zone with either grass or a stalactite: + +```rust +fn stalactite_display(&mut self, build_data : &mut BuilderMap, voronoi_membership: &[i32], zone: i32) { + voronoi_membership + .iter() + .enumerate() + .filter(|(_, tile_zone)| **tile_zone == zone) + .for_each(|(idx, _)| { + build_data.map.tiles[idx] = match crate::rng::roll_dice(1,10) { + 1 => TileType::Stalactite, + 2 => TileType::Stalagmite, + _ => TileType::Grass, + }; + }); + } +``` + +### Parks & Sacrifice Areas + +A few areas of vegetative cover, with seating adds to the park feel. These should be larger areas - that's the dominant theme of the zone. I don't really picture dark elves sitting around listening to a nice concert---so let's go with an altar in the middle, complete with bloodstains. Notice how we're using an "or" statement in our `match` to match both the 2nd and 3rd largest zones: + +```rust +// Start making zonal terrain +zone_sizes.iter().enumerate().for_each(|(i, (zone, _))| { + match i { + 0 => self.portal_park(build_data, &voronoi_membership, *zone), + 1 | 2 => self.park(build_data, &voronoi_membership, *zone), + i if i > 20 => self.fill_zone(build_data, &voronoi_membership, *zone, TileType::Wall), + _ => { + let roll = crate::rng::roll_dice(1, 6); + match roll { + 1 => self.fill_zone(build_data, &voronoi_membership, *zone, TileType::DeepWater), + 2 => self.fill_zone(build_data, &voronoi_membership, *zone, TileType::ShallowWater), + 3 => self.stalactite_display(build_data, &voronoi_membership, *zone), + _ => {} + } + } + } +}); +``` + +Actually populating the park is slightly more convoluted: + +```rust +fn park(&mut self, build_data : &mut BuilderMap, voronoi_membership: &[i32], zone: i32, seeds: &[(usize, rltk::Point)]) { + let zone_tiles : Vec = voronoi_membership + .iter() + .enumerate() + .filter(|(_, tile_zone)| **tile_zone == zone) + .map(|(idx, _)| idx) + .collect(); + + // Start all grass + zone_tiles.iter().for_each(|idx| build_data.map.tiles[*idx] = TileType::Grass); + + // Add a stone area in the middle + let center = seeds[zone as usize].1; + for y in center.y-2 ..= center.y+2 { + for x in center.x-2 ..= center.x+2 { + let idx = build_data.map.xy_idx(x, y); + build_data.map.tiles[idx] = TileType::Road; + if crate::rng::roll_dice(1,6) > 2 { + build_data.map.bloodstains.insert(idx); + } + } + } + + // With an altar at the center + build_data.spawn_list.push(( + build_data.map.xy_idx(center.x, center.y), + "Altar".to_string() + )); + + // And chairs for spectators + zone_tiles.iter().for_each(|idx| { + if build_data.map.tiles[*idx] == TileType::Grass && crate::rng::roll_dice(1, 6)==1 { + build_data.spawn_list.push(( + *idx, + "Chair".to_string() + )); + } + }); +} +``` + +We start by collecting a list of available tiles. Then we cover them all in nice grass. Find the center point of the Voronoi zone (it'll be the seed that generated it), and cover that area with road. Spawn an altar in the middle, some random blood stains and a bunch of chairs. It's all spawning we've done before - but pulled together to make a (not entirely pleasant) theme park. + +The park areas look sufficiently chaotic: + +![](./c75-altar.jpg) + +### Adding Walkways + +At this point, there's no guaranty that you can actually traverse the map. It's entirely possible that water and walls will coincide in just the wrong way to block your progress. That's not a good thing! Let's use the system we encountered when we created the first Voronoi builder to identify edges between voronoi zones---and replace the edge tiles with roads. This ensures that there's a pathway between zones, as well as giving a nice honeycomb effect across the map. + +Start by adding a call to the end of `spawn_zones` that calls the road builder: + +```rust +// Clear the path +self.make_roads(build_data, &voronoi_membership); +``` + +Now we actually have to build some roads. Most of this code is the same as the voronoi edge detection. Instead of placing floors inside the zone, we're placing roads on the edge: + +```rust +fn make_roads(&mut self, build_data : &mut BuilderMap, voronoi_membership: &[i32]) { + for y in 1..build_data.map.height-1 { + for x in 1..build_data.map.width-1 { + let mut neighbors = 0; + let my_idx = build_data.map.xy_idx(x, y); + let my_seed = voronoi_membership[my_idx]; + if voronoi_membership[build_data.map.xy_idx(x-1, y)] != my_seed { neighbors += 1; } + if voronoi_membership[build_data.map.xy_idx(x+1, y)] != my_seed { neighbors += 1; } + if voronoi_membership[build_data.map.xy_idx(x, y-1)] != my_seed { neighbors += 1; } + if voronoi_membership[build_data.map.xy_idx(x, y+1)] != my_seed { neighbors += 1; } + + if neighbors > 1 { + build_data.map.tiles[my_idx] = TileType::Road; + } + } + } +} +``` + +With this in place, the map is passable. Roads delineate the edges, without looking too square: + +![](./c75-edgeroads.jpg) + +### Cleaning up the Spawns + +Currently, the map is *very* chaotic --- and quite likely to kill you very fast. There's big open areas, chock full of baddies, traps (why would you build a trap in a park?), and items strewn around. Chaos is good, but there's such a thing as too much randomness. We'd like to have the map make some sense---in a random sort of way. + +Let's start by completely removing the random entity spawner from the builder chain: + +```rust +pub fn dark_elf_plaza(new_depth: i32, width: i32, height: i32) -> BuilderChain { + println!("Dark elf plaza builder"); + let mut chain = BuilderChain::new(new_depth, width, height, "Dark Elven Plaza"); + chain.start_with(PlazaMapBuilder::new()); + chain.with(AreaStartingPosition::new(XStart::LEFT, YStart::CENTER)); + chain.with(CullUnreachable::new()); + chain.with(AreaEndingPosition::new(XEnd::RIGHT, YEnd::CENTER)); + chain +} +``` + +This gives you an enemy-free map, albeit one that still has some chairs and altars. This is a "theme park" map - so we're going to retain some control over what spawns in a given area. It will be light on assistance to the player---we're nearly at the end, so hopefully they stocked up! + +Let's start by putting some monsters in the park/altar areas. One dark elf family or the other is there, leading to clusters of enemies. Now find the `park` function, and we'll extend the "add chairs" section: + +```rust +// And chairs for spectators, and the spectators themselves +let available_enemies = match crate::rng::roll_dice(1, 3) { + 1 => vec![ + "Arbat Dark Elf", + "Arbat Dark Elf Leader", + "Arbat Orc Slave", + ], + 2 => vec![ + "Barbo Dark Elf", + "Barbo Goblin Archer", + ], + _ => vec![ + "Cirro Dark Elf", + "Cirro Dark Priestess", + "Cirro Spider", + ] +}; + +zone_tiles.iter().for_each(|idx| { + if build_data.map.tiles[*idx] == TileType::Grass { + match crate::rng::roll_dice(1, 10) { + 1 => build_data.spawn_list.push(( + *idx, + "Chair".to_string() + )), + 2 => { + let to_spawn = crate::rng::range(0, available_enemies.len() as i32); + build_data.spawn_list.push(( + *idx, + available_enemies[to_spawn as usize].to_string() + )); + } + _ => {} + } + } +}); +``` + +We're doing a couple of new things here. We're randomly assigning an owner to the park - A, B or C groups of Dark Elves. Then we make a list of available spawns for each, and spawn a few in that park. This ensures that the park *starts* as owned by one faction. Since they can often see one another, carnage will commence - but at least it's themed carnage. + +We're going to leave the stalactite galleries and pools empty of enemies. They are just window dressing, and provide a quiet area to hide/rest (see? We're not being totally unfair!). + +### The Portal Park + +Now that we've got the basic shape of the map down, it's time to focus on the park. The first thing to do is to stop the exit from spawning randomly. Change the basic map builder to not include exit placement: + +```rust +pub fn dark_elf_plaza(new_depth: i32, width: i32, height: i32) -> BuilderChain { + println!("Dark elf plaza builder"); + let mut chain = BuilderChain::new(new_depth, width, height, "Dark Elven Plaza"); + chain.start_with(PlazaMapBuilder::new()); + chain.with(AreaStartingPosition::new(XStart::LEFT, YStart::CENTER)); + chain.with(CullUnreachable::new()); + chain +} +``` + +That leaves you with no exit at all. We want to place it in the middle of Portal Park. Let's extend the function signature to include the voronoi seeds, and use the seed point to place the exit---just like we did for other parks: + +```rust +fn portal_park(&mut self, build_data : &mut BuilderMap, voronoi_membership: &[i32], zone: i32, seeds: &[(usize, rltk::Point)]) { + let center = seeds[zone as usize].1; + let idx = build_data.map.xy_idx(center.x, center.y); + build_data.map.tiles[idx] = TileType::DownStairs; +} +``` + +Now, let's make the portal park stand out a bit by covering it in gravel: + +```rust +fn portal_park(&mut self, build_data : &mut BuilderMap, voronoi_membership: &[i32], zone: i32, seeds: &[(usize, rltk::Point)]) { + let zone_tiles : Vec = voronoi_membership + .iter() + .enumerate() + .filter(|(_, tile_zone)| **tile_zone == zone) + .map(|(idx, _)| idx) + .collect(); + + // Start all gravel + zone_tiles.iter().for_each(|idx| build_data.map.tiles[*idx] = TileType::Gravel); + + // Add the exit + let center = seeds[zone as usize].1; + let idx = build_data.map.xy_idx(center.x, center.y); + build_data.map.tiles[idx] = TileType::DownStairs; +} +``` + +Next, we'll add some altars around the exit: + +```rust +// Add some altars around the exit +let altars = [ + build_data.map.xy_idx(center.x - 2, center.y), + build_data.map.xy_idx(center.x + 2, center.y), + build_data.map.xy_idx(center.x, center.y - 2), + build_data.map.xy_idx(center.x, center.y + 2), +]; +altars.iter().for_each(|idx| build_data.spawn_list.push((*idx, "Altar".to_string()))); +``` + +This gives a pretty good start at the exit to Abyss. You have the exit in the right place, creepy altars and a clearly marked approach. It's also devoid of risk (other than the elves killing one another all over the map). + +Let's make the exit a little more challenging by adding a boss fight to the exit. It's the last big push before Abyss, so it's a natural spot for it. I randomly generated a demon name, and decided to name the boss "Vokoth". Let's spawn it one tile adjacent to th exit: + +```rust +let demon_spawn = build_data.map.xy_idx(center.x+1, center.y+1); +build_data.spawn_list.push((demon_spawn, "Vokoth".to_string())); +``` + +This won't do anything at all until we define Vokoth! We want a tough baddie. Let's take a quick trip down memory lane in `spawns.json` and remind ourselves how we defined the black dragon: + +```json +{ + "name" : "Black Dragon", + "renderable": { + "glyph" : "D", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1, + "x_size" : 2, + "y_size" : 2 + }, + "blocks_tile" : true, + "vision_range" : 12, + "movement" : "static", + "attributes" : { + "might" : 13, + "fitness" : 13 + }, + "skills" : { + "Melee" : 18, + "Defense" : 16 + }, + "natural" : { + "armor_class" : 17, + "attacks" : [ + { "name" : "bite", "hit_bonus" : 4, "damage" : "1d10+2" }, + { "name" : "left_claw", "hit_bonus" : 2, "damage" : "1d10" }, + { "name" : "right_claw", "hit_bonus" : 2, "damage" : "1d10" } + ] + }, + "loot_table" : "Wyrms", + "faction" : "Wyrm", + "level" : 6, + "gold" : "20d10", + "abilities" : [ + { "spell" : "Acid Breath", "chance" : 0.2, "range" : 8.0, "min_range" : 2.0 } + ] +}, +``` + +That's a really tough monster, and makes for a good template for the Abyssal demon. Let's clone it (copy/paste time!) and build an entry for Vokoth: + +```json +{ + "name" : "Vokoth", + "renderable": { + "glyph" : "&", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1, + "x_size" : 2, + "y_size" : 2 + }, + "blocks_tile" : true, + "vision_range" : 6, + "movement" : "static", + "attributes" : { + "might" : 13, + "fitness" : 13 + }, + "skills" : { + "Melee" : 18, + "Defense" : 16 + }, + "natural" : { + "armor_class" : 17, + "attacks" : [ + { "name" : "whip", "hit_bonus" : 4, "damage" : "1d10+2" } + ] + }, + "loot_table" : "Wyrms", + "faction" : "Wyrm", + "level" : 8, + "gold" : "20d10", + "abilities" : [] +} +``` + +Now if you play the game, you'll find yourself facing a nasty demon monster at the exit to Abyss. + +![](./c75-vokoth.jpg) + +## Wrap-Up + +We now have the second-to-last section done! You can battle your way down to the Dark Elf Plaza, and find the gateway to Abyss - but only if you can evade a hulking demon and a horde of elves---with very little in the way of help offered. Next up, we'll begin to build the Abyss. + +--- + +**The source code for this chapter may be found [here](https://github.com/thebracket/rustrogueliketutorial/tree/master/chapter-75-darkplaza)** + + +[Run this chapter's example with web assembly, in your browser (WebGL2 required)](https://bfnightly.bracketproductions.com/rustbook/wasm/chapter-75-darkplaza) +--- + +Copyright (C) 2019, Herbert Wolverson. + +--- \ No newline at end of file diff --git a/chapter-74-darkcity/Cargo.toml b/chapter-74-darkcity/Cargo.toml index 917cfede..43b38b4c 100644 --- a/chapter-74-darkcity/Cargo.toml +++ b/chapter-74-darkcity/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rltk = { path="c:/users/herbe/documents/learnrust/rltk_rs/rltk", features = ["serde"] } +rltk = { version = "0.8.0", features = ["serde"] } specs = { version = "0.16.1", features = ["serde"] } specs-derive = "0.4.1" serde= { version = "^1.0.44", features = ["derive"] } diff --git a/chapter-75-darkplaza/Cargo.toml b/chapter-75-darkplaza/Cargo.toml new file mode 100644 index 00000000..85249e27 --- /dev/null +++ b/chapter-75-darkplaza/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "chapter-75-darkplaza" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rltk = { version = "0.8.0", features = ["serde"] } +specs = { version = "0.16.1", features = ["serde"] } +specs-derive = "0.4.1" +serde= { version = "^1.0.44", features = ["derive"] } +serde_json = "^1.0.44" +lazy_static = "1.4.0" +regex = "1.3.6" diff --git a/chapter-75-darkplaza/raws/spawns.json b/chapter-75-darkplaza/raws/spawns.json new file mode 100644 index 00000000..03157ad4 --- /dev/null +++ b/chapter-75-darkplaza/raws/spawns.json @@ -0,0 +1,2868 @@ +{ +"spawn_table" : [ + { "name" : "Goblin", "weight" : 10, "min_depth" : 3, "max_depth" : 4 }, + { "name" : "Goblin Archer", "weight" : 10, "min_depth" : 3, "max_depth" : 4 }, + { "name" : "Orc", "weight" : 1, "min_depth" : 4, "max_depth" : 100 }, + { "name" : "Beginner's Magic", "weight" : 6, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Venom 101", "weight" : 3, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Arachnophilia 101", "weight" : 3, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Strength Potion", "weight" : 2, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Poison Potion", "weight" : 3, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Slow Potion", "weight" : 3, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Haste Potion", "weight" : 3, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Web Scroll", "weight" : 2, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Rod of Venom", "weight" : 2, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Health Potion", "weight" : 15, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Mana Potion", "weight" : 7, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Fireball Scroll", "weight" : 2, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Confusion Scroll", "weight" : 2, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Magic Missile Scroll", "weight" : 4, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Town Portal Scroll", "weight" : 4, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Remove Curse Scroll", "weight" : 4, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Identify Scroll", "weight" : 4, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Rod of Fireballs", "weight" : 1, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Gauntlets of Ogre Power", "weight" : 1, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Dagger", "weight" : 3, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Shield", "weight" : 3, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Shortbow", "weight" : 2, "min_depth" : 3, "max_depth" : 100 }, + { "name" : "Longsword", "weight" : 2, "min_depth" : 3, "max_depth" : 100 }, + { "name" : "Tower Shield", "weight" : 1, "min_depth" : 3, "max_depth" : 100 }, + { "name" : "Leather Armor", "weight" : 1, "min_depth" : 2, "max_depth" : 100 }, + { "name" : "Leather Boots", "weight" : 1, "min_depth" : 2, "max_depth" : 100 }, + { "name" : "Chainmail Armor", "weight" : 1, "min_depth" : 4, "max_depth" : 100 }, + { "name" : "Cloth Cap", "weight" : 5, "min_depth" : 4, "max_depth" : 100 }, + { "name" : "Leather Cap", "weight" : 4, "min_depth" : 4, "max_depth" : 100 }, + { "name" : "Chain Coif", "weight" : 3, "min_depth" : 4, "max_depth" : 100 }, + { "name" : "Steel Helm", "weight" : 2, "min_depth" : 4, "max_depth" : 100 }, + { "name" : "Cloth Pants", "weight" : 6, "min_depth" : 1, "max_depth" : 100 }, + { "name" : "Leather Pants", "weight" : 5, "min_depth" : 1, "max_depth" : 100 }, + { "name" : "Chain Leggings", "weight" : 4, "min_depth" : 1, "max_depth" : 100 }, + { "name" : "Steel Greaves", "weight" : 3, "min_depth" : 5, "max_depth" : 100 }, + { "name" : "Leather Boots", "weight" : 5, "min_depth" : 1, "max_depth" : 100 }, + { "name" : "Chain Boots", "weight" : 4, "min_depth" : 3, "max_depth" : 100 }, + { "name" : "Steel Boots", "weight" : 2, "min_depth" : 5, "max_depth" : 100 }, + { "name" : "Cloth Gloves", "weight" : 6, "min_depth" : 1, "max_depth" : 100 }, + { "name" : "Leather Gloves", "weight" : 5, "min_depth" : 1, "max_depth" : 100 }, + { "name" : "Chain Gloves", "weight" : 3, "min_depth" : 1, "max_depth" : 100 }, + { "name" : "Steel Gloves", "weight" : 2, "min_depth" : 5, "max_depth" : 100 }, + { "name" : "Rations", "weight" : 10, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Magic Mapping Scroll", "weight" : 2, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Bear Trap", "weight" : 5, "min_depth" : 0, "max_depth" : 100 }, + { "name" : "Battleaxe", "weight" : 1, "min_depth" : 2, "max_depth" : 100 }, + { "name" : "Kobold", "weight" : 15, "min_depth" : 3, "max_depth" : 3 }, + { "name" : "Rat", "weight" : 15, "min_depth" : 2, "max_depth" : 2 }, + { "name" : "Mangy Wolf", "weight" : 13, "min_depth" : 2, "max_depth" : 2 }, + { "name" : "Bandit", "weight" : 9, "min_depth" : 2, "max_depth" : 3 }, + { "name" : "Bandit Archer", "weight" : 9, "min_depth" : 2, "max_depth" : 3 }, + { "name" : "Bat", "weight" : 15, "min_depth" : 3, "max_depth" : 3 }, + { "name" : "Large Spider", "weight" : 3, "min_depth" : 3, "max_depth" : 3 }, + { "name" : "Gelatinous Cube", "weight" : 3, "min_depth" : 3, "max_depth" : 3 }, + { "name" : "Dragon Wyrmling", "weight" : 1, "min_depth" : 5, "max_depth" : 6 }, + { "name" : "Lizardman", "weight" : 10, "min_depth" : 5, "max_depth" : 7 }, + { "name" : "Giant Lizard", "weight" : 4, "min_depth" : 5, "max_depth" : 7 }, + { "name" : "Rock Golem", "weight" : 4, "min_depth" : 5, "max_depth" : 7 }, + { "name" : "Firecap Mushroom", "weight" : 10, "min_depth" : 7, "max_depth" : 9 }, + { "name" : "Sporecap Mushroom", "weight" : 10, "min_depth" : 7, "max_depth" : 9 }, + { "name" : "Deathcap Mushroom", "weight" : 7, "min_depth" : 7, "max_depth" : 9 }, + { "name" : "Fungus Man", "weight" : 8, "min_depth" : 7, "max_depth" : 9 }, + { "name" : "Spore Zombie", "weight" : 7, "min_depth" : 7, "max_depth" : 9 }, + { "name" : "Fungal Beast", "weight" : 9, "min_depth" : 7, "max_depth" : 9 }, + { "name" : "Stonefall Trap", "weight" : 4, "min_depth" : 5, "max_depth" : 6 }, + { "name" : "Landmine", "weight" : 1, "min_depth" : 5, "max_depth" : 6 }, + { "name" : "Breastplate", "weight" : 7, "min_depth" : 5, "max_depth" : 7 }, + { "name" : "War Axe", "weight" : 7, "min_depth" : 5, "max_depth" : 7 }, + { "name" : "Dwarf-Steel Shirt", "weight" : 1, "min_depth" : 5, "max_depth" : 7 }, + { "name" : "Hand Crossbow", "weight" : 2, "min_depth" : 9, "max_depth" : 11 }, + { "name" : "Dark Elf", "weight": 10, "min_depth": 10, "max_depth": 11 }, + { "name" : "Arbat Dark Elf", "weight": 10, "min_depth": 10, "max_depth": 11 }, + { "name" : "Arbat Dark Elf Leader", "weight": 7, "min_depth": 10, "max_depth": 11 }, + { "name" : "Arbat Orc Slave", "weight": 14, "min_depth": 10, "max_depth": 11 }, + { "name" : "Barbo Dark Elf", "weight": 9, "min_depth": 10, "max_depth": 11 }, + { "name" : "Barbo Goblin Archer", "weight": 13, "min_depth": 10, "max_depth": 11 }, + { "name" : "Cirro Dark Elf", "weight": 7, "min_depth": 10, "max_depth": 11 }, + { "name" : "Cirro Dark Priestess", "weight": 6, "min_depth": 10, "max_depth": 11 }, + { "name" : "Cirro Spider", "weight": 10, "min_depth": 10, "max_depth": 11 } +], + +"loot_tables" : [ + { "name" : "Animal", + "drops" : [ + { "name" : "Hide", "weight" : 10 }, + { "name" : "Meat", "weight" : 10 } + ] + }, + { "name" : "Wyrms", + "drops" : [ + { "name" : "Dragon Scale", "weight" : 10 }, + { "name" : "Meat", "weight" : 10 } + ] + } +], + +"faction_table" : [ + { "name" : "Player", "responses": { }}, + { "name" : "Mindless", "responses": { "Default" : "attack" } }, + { "name" : "Townsfolk", "responses" : { "Default" : "flee", "Player" : "ignore", "Townsfolk" : "ignore" } }, + { "name" : "Bandits", "responses" : { "Default" : "attack", "Bandits" : "ignore" } }, + { "name" : "Cave Goblins", "responses" : { "Default" : "attack", "Cave Goblins" : "ignore" } }, + { "name" : "Carnivores", "responses" : { "Default" : "attack", "Carnivores" : "ignore" } }, + { "name" : "Herbivores", "responses" : { "Default" : "flee", "Herbivores" : "ignore" } }, + { "name" : "Hungry Rodents", "responses": { "Default" : "attack", "Hungry Rodents" : "ignore" }}, + { "name" : "Wyrm", "responses": { "Default" : "attack", "Wyrm" : "ignore", "Fungi" : "ignore" }}, + { "name" : "Dwarven Remnant", "responses": { "Default" : "attack", "Player" : "ignore", "Dwarven Remnant" : "ignore" }}, + { "name" : "Fungi", "responses": { "Default" : "attack", "Fungi" : "ignore", "Wyrm" : "ignore" }}, + { "name" : "DarkElf", "responses" : { "Default" : "attack", "DarkElf" : "ignore" } }, + { "name" : "DarkElfA", "responses" : { "Default" : "attack", "DarkElfA" : "ignore", "DarkElfB" : "attack", "DarkElfC" : "attack" } }, + { "name" : "DarkElfB", "responses" : { "Default" : "attack", "DarkElfB" : "ignore", "DarkElfA" : "attack", "DarkElfC" : "attack" } }, + { "name" : "DarkElfC", "responses" : { "Default" : "attack", "DarkElfC" : "ignore", "DarkElfA" : "attack", "DarkElfB" : "attack" } } +], + +"items" : [ + { + "name" : "Beginner's Magic", + "renderable": { + "glyph" : "¶", + "fg" : "#FF00FF", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { "teach_spell" : "Zap" } + }, + "weight_lbs" : 0.5, + "base_value" : 50.0, + "vendor_category" : "alchemy" + }, + + { + "name" : "Arachnophilia 101", + "renderable": { + "glyph" : "¶", + "fg" : "#FF00FF", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { "teach_spell" : "Web" } + }, + "weight_lbs" : 0.5, + "base_value" : 50.0, + "vendor_category" : "alchemy" + }, + + { + "name" : "Venom 101", + "renderable": { + "glyph" : "¶", + "fg" : "#FF00FF", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { "teach_spell" : "Venom" } + }, + "weight_lbs" : 0.5, + "base_value" : 50.0, + "vendor_category" : "alchemy" + }, + + { + "name" : "Poison Potion", + "renderable": { + "glyph" : "!", + "fg" : "#FF00FF", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { "damage_over_time" : "2" } + }, + "weight_lbs" : 0.5, + "base_value" : 50.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "potion" } + }, + + { + "name" : "Slow Potion", + "renderable": { + "glyph" : "!", + "fg" : "#FF00FF", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { "slow" : "2.0" } + }, + "weight_lbs" : 0.5, + "base_value" : 50.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "potion" } + }, + + { + "name" : "Haste Potion", + "renderable": { + "glyph" : "!", + "fg" : "#FF00FF", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { "slow" : "-2.0" } + }, + "weight_lbs" : 0.5, + "base_value" : 100.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "potion" } + }, + + { + "name" : "Health Potion", + "renderable": { + "glyph" : "!", + "fg" : "#FF00FF", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { "provides_healing" : "8" } + }, + "weight_lbs" : 0.5, + "base_value" : 50.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "potion" } + }, + + { + "name" : "Mana Potion", + "renderable": { + "glyph" : "!", + "fg" : "#FF00FF", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { "provides_mana" : "4" } + }, + "weight_lbs" : 0.5, + "base_value" : 50.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "potion" } + }, + + { + "name" : "Strength Potion", + "renderable": { + "glyph" : "!", + "fg" : "#FF00FF", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { "particle" : "!;#FF0000;200.0" } + }, + "weight_lbs" : 0.5, + "base_value" : 50.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "potion" }, + "attributes" : { "might" : 5 } + }, + + { + "name" : "Magic Missile Scroll", + "renderable": { + "glyph" : ")", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { + "ranged" : "6", + "damage" : "20", + "particle_line" : "▓;#00FFFF;200.0" + } + }, + "weight_lbs" : 0.5, + "base_value" : 50.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "scroll" } + }, + + { + "name" : "Web Scroll", + "renderable": { + "glyph" : ")", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { + "ranged" : "6", + "slow" : "10.0", + "area_of_effect" : "3", + "particle_line" : "☼;#FFFFFF;200.0" + } + }, + "weight_lbs" : 0.5, + "base_value" : 500.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "scroll" } + }, + + { + "name" : "Fireball Scroll", + "renderable": { + "glyph" : ")", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { + "ranged" : "6", + "damage" : "20", + "area_of_effect" : "3", + "particle" : "▓;#FFA500;200.0" + } + }, + "weight_lbs" : 0.5, + "base_value" : 100.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "scroll" } + }, + + { + "name" : "Confusion Scroll", + "renderable": { + "glyph" : ")", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { + "ranged" : "6", + "confusion" : "4" + } + }, + "weight_lbs" : 0.5, + "base_value" : 75.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "scroll" } + }, + + { + "name" : "Magic Mapping Scroll", + "renderable": { + "glyph" : ")", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { + "magic_mapping" : "" + } + }, + "weight_lbs" : 0.5, + "base_value" : 50.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "scroll" } + }, + + { + "name" : "Town Portal Scroll", + "renderable": { + "glyph" : ")", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { + "town_portal" : "" + } + }, + "weight_lbs" : 0.5, + "base_value" : 20.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "scroll" } + }, + + { + "name" : "Remove Curse Scroll", + "renderable": { + "glyph" : ")", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { + "remove_curse" : "" + } + }, + "weight_lbs" : 0.5, + "base_value" : 50.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "scroll" } + }, + + { + "name" : "Identify Scroll", + "renderable": { + "glyph" : ")", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { + "identify" : "" + } + }, + "weight_lbs" : 0.5, + "base_value" : 50.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "scroll" } + }, + + { + "name" : "Rations", + "renderable": { + "glyph" : "%", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { + "food" : "" + } + }, + "weight_lbs" : 2.0, + "base_value" : 0.5, + "vendor_category" : "food" + }, + + { + "name" : "Meat", + "renderable": { + "glyph" : "%", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { + "food" : "" + } + }, + "weight_lbs" : 2.0, + "base_value" : 0.5, + "vendor_category" : "food" + }, + + { + "name" : "Hide", + "renderable": { + "glyph" : "ß", + "fg" : "#A52A2A", + "bg" : "#000000", + "order" : 2 + }, + "weight_lbs" : 2.0, + "base_value" : 5.0 + }, + + { + "name" : "Dragon Scale", + "renderable": { + "glyph" : "ß", + "fg" : "#FFD700", + "bg" : "#000000", + "order" : 2 + }, + "weight_lbs" : 2.0, + "base_value" : 75.0 + }, + + { + "name" : "Dried Sausage", + "renderable": { + "glyph" : "%", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { + "food" : "" + } + }, + "weight_lbs" : 2.0, + "base_value" : 0.5 + }, + + { + "name" : "Beer", + "renderable": { + "glyph" : "!", + "fg" : "#FF00FF", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { "provides_healing" : "4" } + }, + "weight_lbs" : 2.0, + "base_value" : 0.5, + "vendor_category" : "food" + }, + + { + "name" : "Rusty Longsword", + "renderable": { + "glyph" : "/", + "fg" : "#BB77BB", + "bg" : "#000000", + "order" : 2 + }, + "weapon" : { + "range" : "melee", + "attribute" : "might", + "base_damage" : "1d8-1", + "hit_bonus" : -1 + }, + "weight_lbs" : 3.0, + "base_value" : 10.0, + "initiative_penalty" : 2, + "vendor_category" : "junk" + }, + + { + "name" : "Dagger", + "renderable": { + "glyph" : "/", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 2 + }, + "weapon" : { + "range" : "melee", + "attribute" : "Quickness", + "base_damage" : "1d4", + "hit_bonus" : 0 + }, + "weight_lbs" : 1.0, + "base_value" : 2.0, + "initiative_penalty" : 0, + "vendor_category" : "weapon", + "template_magic" : { + "unidentified_name" : "Unidentified Dagger", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Shortbow", + "renderable": { + "glyph" : ")", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 2 + }, + "weapon" : { + "range" : "4", + "attribute" : "Quickness", + "base_damage" : "1d4", + "hit_bonus" : 0 + }, + "weight_lbs" : 2.0, + "base_value" : 5.0, + "initiative_penalty" : 1, + "vendor_category" : "weapon", + "template_magic" : { + "unidentified_name" : "Unidentified Shortbow", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Hand Crossbow", + "renderable": { + "glyph" : ")", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 2 + }, + "weapon" : { + "range" : "6", + "attribute" : "Quickness", + "base_damage" : "1d6", + "hit_bonus" : 0 + }, + "weight_lbs" : 2.0, + "base_value" : 5.0, + "initiative_penalty" : 1, + "vendor_category" : "weapon", + "template_magic" : { + "unidentified_name" : "Unidentified Hand Crossbow", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Shortsword", + "renderable": { + "glyph" : "/", + "fg" : "#FFAAFF", + "bg" : "#000000", + "order" : 2 + }, + "weapon" : { + "range" : "melee", + "attribute" : "might", + "base_damage" : "1d6", + "hit_bonus" : 0 + }, + "weight_lbs" : 2.0, + "base_value" : 10.0, + "initiative_penalty" : 1, + "vendor_category" : "weapon", + "template_magic" : { + "unidentified_name" : "Unidentified Shortsword", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Longsword", + "renderable": { + "glyph" : "/", + "fg" : "#FFAAFF", + "bg" : "#000000", + "order" : 2 + }, + "weapon" : { + "range" : "melee", + "attribute" : "might", + "base_damage" : "1d8", + "hit_bonus" : 0 + }, + "weight_lbs" : 3.0, + "base_value" : 15.0, + "initiative_penalty" : 2, + "vendor_category" : "weapon", + "template_magic" : { + "unidentified_name" : "Unidentified Longsword", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Scimitar", + "renderable": { + "glyph" : "/", + "fg" : "#FFAAFF", + "bg" : "#000000", + "order" : 2 + }, + "weapon" : { + "range" : "melee", + "attribute" : "might", + "base_damage" : "1d6+2", + "hit_bonus" : 1 + }, + "weight_lbs" : 2.5, + "base_value" : 25.0, + "initiative_penalty" : 1, + "vendor_category" : "weapon", + "template_magic" : { + "unidentified_name" : "Unidentified Scimitar", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Battleaxe", + "renderable": { + "glyph" : "¶", + "fg" : "#FF55FF", + "bg" : "#000000", + "order" : 2 + }, + "weapon" : { + "range" : "melee", + "attribute" : "might", + "base_damage" : "1d8", + "hit_bonus" : 0 + }, + "weight_lbs" : 4.0, + "base_value" : 10.0, + "initiative_penalty" : 2, + "vendor_category" : "weapon", + "template_magic" : { + "unidentified_name" : "Unidentified Battleaxe", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "War Axe", + "renderable": { + "glyph" : "¶", + "fg" : "#FF55FF", + "bg" : "#000000", + "order" : 2 + }, + "weapon" : { + "range" : "melee", + "attribute" : "might", + "base_damage" : "1d12", + "hit_bonus" : 0 + }, + "weight_lbs" : 4.0, + "base_value" : 100.0, + "initiative_penalty" : 2, + "vendor_category" : "weapon", + "template_magic" : { + "unidentified_name" : "Unidentified War Axe", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Shield", + "renderable": { + "glyph" : "[", + "fg" : "#00AAFF", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Shield", + "armor_class" : 1.0 + }, + "weight_lbs" : 5.0, + "base_value" : 3.0, + "initiative_penalty" : 0.5, + "vendor_category" : "armor", + "template_magic" : { + "unidentified_name" : "Unidentified Shield", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Tower Shield", + "renderable": { + "glyph" : "[", + "fg" : "#00FFFF", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Shield", + "armor_class" : 2.0 + }, + "weight_lbs" : 45.0, + "base_value" : 30.0, + "initiative_penalty" : 1.0, + "vendor_category" : "armor", + "template_magic" : { + "unidentified_name" : "Unidentified Tower Shield", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Stained Tunic", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Torso", + "armor_class" : 0.1 + }, + "weight_lbs" : 1.0, + "base_value" : 1.0, + "initiative_penalty" : 0.1, + "vendor_category" : "junk" + }, + + { + "name" : "Torn Trousers", + "renderable": { + "glyph" : "[", + "fg" : "#00FFFF", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Legs", + "armor_class" : 0.1 + }, + "weight_lbs" : 1.0, + "base_value" : 1.0, + "initiative_penalty" : 0.1, + "vendor_category" : "junk" + }, + + { + "name" : "Old Boots", + "renderable": { + "glyph" : "[", + "fg" : "#FF9999", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Feet", + "armor_class" : 0.1 + }, + "weight_lbs" : 1.0, + "base_value" : 1.0, + "initiative_penalty" : 0.1, + "vendor_category" : "junk" + }, + + { + "name" : "Cudgel", + "renderable": { + "glyph" : "/", + "fg" : "#A52A2A", + "bg" : "#000000", + "order" : 2 + }, + "weapon" : { + "range" : "melee", + "attribute" : "Quickness", + "base_damage" : "1d4", + "hit_bonus" : 0 + }, + "weight_lbs" : 2.0, + "base_value" : 0.1, + "initiative_penalty" : 2.0, + "vendor_category" : "junk", + "template_magic" : { + "unidentified_name" : "Unidentified Cudgel", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Cloth Tunic", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Torso", + "armor_class" : 0.1 + }, + "weight_lbs" : 1.0, + "base_value" : 1.0, + "initiative_penalty" : 0.1, + "vendor_category" : "clothes" + }, + + { + "name" : "Cloth Pants", + "renderable": { + "glyph" : "[", + "fg" : "#00FFFF", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Legs", + "armor_class" : 0.1 + }, + "weight_lbs" : 1.0, + "base_value" : 1.0, + "initiative_penalty" : 0.1, + "vendor_category" : "clothes" + }, + + { + "name" : "Leather Pants", + "renderable": { + "glyph" : "[", + "fg" : "#00FFFF", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Legs", + "armor_class" : 0.2 + }, + "weight_lbs" : 5.0, + "base_value" : 25.0, + "initiative_penalty" : 0.2, + "vendor_category" : "clothes", + "template_magic" : { + "unidentified_name" : "Unidentified Leather Pants", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Chain Leggings", + "renderable": { + "glyph" : "[", + "fg" : "#00FFFF", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Legs", + "armor_class" : 0.3 + }, + "weight_lbs" : 10.0, + "base_value" : 50.0, + "initiative_penalty" : 0.3, + "vendor_category" : "clothes", + "template_magic" : { + "unidentified_name" : "Unidentified Chain Leggings", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Drow Leggings", + "renderable": { + "glyph" : "[", + "fg" : "#00FFFF", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Legs", + "armor_class" : 0.4 + }, + "weight_lbs" : 10.0, + "base_value" : 50.0, + "initiative_penalty" : 0.1, + "vendor_category" : "clothes", + "template_magic" : { + "unidentified_name" : "Unidentified Drow Leggings", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Steel Greaves", + "renderable": { + "glyph" : "[", + "fg" : "#00FFFF", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Legs", + "armor_class" : 0.5 + }, + "weight_lbs" : 20.0, + "base_value" : 100.0, + "initiative_penalty" : 0.5, + "vendor_category" : "clothes", + "template_magic" : { + "unidentified_name" : "Unidentified Steel Greaves", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Slippers", + "renderable": { + "glyph" : "[", + "fg" : "#FF9999", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Feet", + "armor_class" : 0.1 + }, + "weight_lbs" : 1.0, + "base_value" : 1.0, + "initiative_penalty" : 0.1, + "vendor_category" : "clothes" + }, + + { + "name" : "Leather Armor", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Torso", + "armor_class" : 1.0 + }, + "weight_lbs" : 15.0, + "base_value" : 10.0, + "initiative_penalty" : 0.5, + "vendor_category" : "clothes", + "template_magic" : { + "unidentified_name" : "Unidentified Leather Armor", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Chainmail Armor", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Torso", + "armor_class" : 2.0 + }, + "weight_lbs" : 20.0, + "base_value" : 50.0, + "initiative_penalty" : 1.0, + "vendor_category" : "armor", + "template_magic" : { + "unidentified_name" : "Unidentified Chainmail Armor", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Drow Chain", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Torso", + "armor_class" : 3.0 + }, + "weight_lbs" : 5.0, + "base_value" : 50.0, + "initiative_penalty" : 0.0, + "vendor_category" : "armor", + "template_magic" : { + "unidentified_name" : "Unidentified Drow Chain", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Breastplate", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Torso", + "armor_class" : 3.0 + }, + "weight_lbs" : 25.0, + "base_value" : 100.0, + "initiative_penalty" : 2.0, + "vendor_category" : "armor", + "template_magic" : { + "unidentified_name" : "Unidentified Breastplate", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Dwarf-Steel Shirt", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Torso", + "armor_class" : 3.0 + }, + "weight_lbs" : 5.0, + "base_value" : 500.0, + "initiative_penalty" : 0.0, + "vendor_category" : "armor", + "template_magic" : { + "unidentified_name" : "Unidentified Dwarf-Steel Shirt", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Cloth Cap", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Head", + "armor_class" : 0.2 + }, + "weight_lbs" : 0.25, + "base_value" : 5.0, + "initiative_penalty" : 0.1, + "vendor_category" : "armor" + }, + + { + "name" : "Leather Cap", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Head", + "armor_class" : 0.4 + }, + "weight_lbs" : 0.5, + "base_value" : 10.0, + "initiative_penalty" : 0.2, + "vendor_category" : "armor", + "template_magic" : { + "unidentified_name" : "Unidentified Leather Cap", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Chain Coif", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Head", + "armor_class" : 1.0 + }, + "weight_lbs" : 5.0, + "base_value" : 20.0, + "initiative_penalty" : 0.5, + "vendor_category" : "armor", + "template_magic" : { + "unidentified_name" : "Unidentified Chain Coif", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Steel Helm", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Head", + "armor_class" : 2.0 + }, + "weight_lbs" : 15.0, + "base_value" : 100.0, + "initiative_penalty" : 1.0, + "vendor_category" : "armor", + "template_magic" : { + "unidentified_name" : "Unidentified Steel Helm", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Leather Boots", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Feet", + "armor_class" : 0.2 + }, + "weight_lbs" : 2.0, + "base_value" : 5.0, + "initiative_penalty" : 0.25, + "vendor_category" : "clothes", + "template_magic" : { + "unidentified_name" : "Unidentified Leather Boots", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Chain Boots", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Feet", + "armor_class" : 0.3 + }, + "weight_lbs" : 3.0, + "base_value" : 10.0, + "initiative_penalty" : 0.25, + "vendor_category" : "armor", + "template_magic" : { + "unidentified_name" : "Unidentified Chain Boots", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Drow Boots", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Feet", + "armor_class" : 0.4 + }, + "weight_lbs" : 2.0, + "base_value" : 10.0, + "initiative_penalty" : 0.1, + "vendor_category" : "armor", + "template_magic" : { + "unidentified_name" : "Unidentified Drow Boots", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Steel Boots", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Feet", + "armor_class" : 0.5 + }, + "weight_lbs" : 5.0, + "base_value" : 10.0, + "initiative_penalty" : 0.4, + "vendor_category" : "armor", + "template_magic" : { + "unidentified_name" : "Unidentified Steel Boots", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Cloth Gloves", + "renderable": { + "glyph" : "[", + "fg" : "#FF9999", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Hands", + "armor_class" : 0.1 + }, + "weight_lbs" : 0.5, + "base_value" : 1.0, + "initiative_penalty" : 0.1, + "vendor_category" : "clothes" + }, + + { + "name" : "Leather Gloves", + "renderable": { + "glyph" : "[", + "fg" : "#FF9999", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Hands", + "armor_class" : 0.2 + }, + "weight_lbs" : 1.0, + "base_value" : 1.0, + "initiative_penalty" : 0.1, + "vendor_category" : "clothes", + "template_magic" : { + "unidentified_name" : "Unidentified Leather Gloves", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Chain Gloves", + "renderable": { + "glyph" : "[", + "fg" : "#FF9999", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Hands", + "armor_class" : 0.3 + }, + "weight_lbs" : 2.0, + "base_value" : 10.0, + "initiative_penalty" : 0.2, + "vendor_category" : "clothes", + "template_magic" : { + "unidentified_name" : "Unidentified Chain Gloves", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Steel Gloves", + "renderable": { + "glyph" : "[", + "fg" : "#FF9999", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Hands", + "armor_class" : 0.5 + }, + "weight_lbs" : 5.0, + "base_value" : 10.0, + "initiative_penalty" : 0.3, + "vendor_category" : "clothes", + "template_magic" : { + "unidentified_name" : "Unidentified Gauntlets", + "bonus_min" : 1, + "bonus_max" : 5, + "include_cursed" : true + } + }, + + { + "name" : "Gauntlets of Ogre Power", + "renderable": { + "glyph" : "[", + "fg" : "#00FF00", + "bg" : "#000000", + "order" : 2 + }, + "wearable" : { + "slot" : "Hands", + "armor_class" : 0.1 + }, + "weight_lbs" : 1.0, + "base_value" : 300.0, + "initiative_penalty" : 0.0, + "vendor_category" : "armor", + "magic" : { "class" : "common", "naming" : "Unidentified Gauntlets" }, + "attributes" : { "might" : 5 } + }, + + { + "name" : "Rod of Fireballs", + "renderable": { + "glyph" : "/", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { + "ranged" : "6", + "damage" : "20", + "area_of_effect" : "3", + "particle" : "▓;#FFA500;200.0" + }, + "charges" : 5 + }, + "weight_lbs" : 0.5, + "base_value" : 500.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "Unidentified Rod" } + }, + + { + "name" : "Rod of Venom", + "renderable": { + "glyph" : "/", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 2 + }, + "consumable" : { + "effects" : { + "ranged" : "6", + "damage_over_time" : "1", + "particle_line" : "▓;#00FF00;200.0" + }, + "charges" : 5 + }, + "weight_lbs" : 0.5, + "base_value" : 500.0, + "vendor_category" : "alchemy", + "magic" : { "class" : "common", "naming" : "Unidentified Rod" } + } +], + +"mobs" : [ + { + "name" : "Barkeep", + "renderable": { + "glyph" : "☻", + "fg" : "#EE82EE", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "static", + "attributes" : { + "intelligence" : 13 + }, + "skills" : { + "Melee" : 2 + }, + "equipped" : [ "Cudgel", "Cloth Tunic", "Cloth Pants", "Slippers" ], + "faction" : "Townsfolk", + "gold" : "2d6", + "vendor" : [ "food" ] + }, + + { + "name" : "Shady Salesman", + "renderable": { + "glyph" : "h", + "fg" : "#EE82EE", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "static", + "attributes" : {}, + "equipped" : [ "Cudgel", "Cloth Tunic", "Cloth Pants", "Slippers" ], + "faction" : "Townsfolk", + "gold" : "2d6", + "vendor" : [ "junk" ] + }, + + { + "name" : "Patron", + "renderable": { + "glyph" : "☻", + "fg" : "#AAAAAA", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "random", + "quips" : [ "Quiet down, it's too early!", "Oh my, I drank too much.", "Still saving the world, eh?" ], + "attributes" : {}, + "equipped" : [ "Cudgel", "Cloth Tunic", "Cloth Pants", "Slippers" ], + "faction" : "Townsfolk", + "gold" : "1d4" + }, + + { + "name" : "Priest", + "renderable": { + "glyph" : "☻", + "fg" : "#EE82EE", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "static", + "attributes" : {}, + "equipped" : [ "Cudgel", "Cloth Tunic", "Cloth Pants", "Slippers" ], + "faction" : "Townsfolk", + "gold" : "2d6" + }, + + { + "name" : "Parishioner", + "renderable": { + "glyph" : "☻", + "fg" : "#AAAAAA", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "random", + "quips" : [ "Great to see a new face here!", "I hear there's going to be a good sermon on tea", "Want some cake?" ], + "attributes" : {}, + "equipped" : [ "Cudgel", "Cloth Tunic", "Cloth Pants", "Slippers" ], + "faction" : "Townsfolk", + "gold" : "1d4" + }, + + { + "name" : "Blacksmith", + "renderable": { + "glyph" : "☻", + "fg" : "#EE82EE", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "static", + "attributes" : {}, + "equipped" : [ "Cudgel", "Cloth Tunic", "Cloth Pants", "Slippers" ], + "faction" : "Townsfolk", + "gold" : "2d6", + "vendor" : [ "armor", "weapon" ] + }, + + { + "name" : "Clothier", + "renderable": { + "glyph" : "☻", + "fg" : "#EE82EE", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "static", + "attributes" : {}, + "equipped" : [ "Cudgel", "Cloth Tunic", "Cloth Pants", "Slippers" ], + "faction" : "Townsfolk", + "gold" : "2d6", + "vendor" : [ "clothes" ] + }, + + { + "name" : "Alchemist", + "renderable": { + "glyph" : "☻", + "fg" : "#EE82EE", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "static", + "attributes" : {}, + "equipped" : [ "Cudgel", "Cloth Tunic", "Cloth Pants", "Slippers" ], + "faction" : "Townsfolk", + "gold" : "2d6", + "vendor" : [ "alchemy" ] + }, + + { + "name" : "Mom", + "renderable": { + "glyph" : "☻", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "static", + "quips" : [ "Hello, dear", "Off saving the world again?", "Be careful in the dungeon!", "Your father would be so proud, were he here." ], + "attributes" : {}, + "equipped" : [ "Cudgel", "Cloth Tunic", "Cloth Pants", "Slippers" ], + "faction" : "Townsfolk", + "gold" : "2d6" + }, + + { + "name" : "Peasant", + "renderable": { + "glyph" : "☻", + "fg" : "#999999", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "random_waypoint", + "quips" : [ "Why are you in my house?" ], + "attributes" : {}, + "equipped" : [ "Cudgel", "Cloth Tunic", "Cloth Pants", "Slippers" ], + "faction" : "Townsfolk", + "gold" : "1d2" + }, + + { + "name" : "Dock Worker", + "renderable": { + "glyph" : "☻", + "fg" : "#999999", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "random_waypoint", + "quips" : [ "Lovely day, eh?", "Nice weather", "Hello" ], + "attributes" : {}, + "equipped" : [ "Cudgel", "Cloth Tunic", "Cloth Pants", "Slippers" ], + "faction" : "Townsfolk", + "gold" : "1d2" + }, + + { + "name" : "Fisher", + "renderable": { + "glyph" : "☻", + "fg" : "#999999", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "random_waypoint", + "quips" : [ "They're biting today!", "I caught something, but it wasn't a fish!", "Looks like rain" ], + "attributes" : {}, + "equipped" : [ "Cudgel", "Cloth Tunic", "Cloth Pants", "Slippers" ], + "faction" : "Townsfolk", + "gold" : "1d2" + }, + + { + "name" : "Wannabe Pirate", + "renderable": { + "glyph" : "☻", + "fg" : "#aa9999", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "random_waypoint", + "quips" : [ "Arrr", "Grog!", "Booze!" ], + "attributes" : {}, + "equipped" : [ "Cudgel", "Cloth Tunic", "Cloth Pants", "Slippers" ], + "faction" : "Townsfolk", + "gold" : "2d6" + }, + + { + "name" : "Drunk", + "renderable": { + "glyph" : "☻", + "fg" : "#aa9999", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "random", + "quips" : [ "Hic", "Need... more... booze!", "Spare a copper?" ], + "attributes" : {}, + "equipped" : [ "Cudgel", "Cloth Tunic", "Cloth Pants", "Slippers" ], + "faction" : "Townsfolk", + "gold" : "1d2" + }, + + { + "name" : "Rat", + "renderable": { + "glyph" : "r", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "static", + "attributes" : { + "might" : 7, + "fitness" : 3 + }, + "skills" : { + "Melee" : -1, + "Defense" : -1 + }, + "natural" : { + "armor_class" : 11, + "attacks" : [ + { "name" : "bite", "hit_bonus" : 0, "damage" : "1d4" } + ] + }, + "faction" : "Hungry Rodents" + }, + + { + "name" : "Mangy Wolf", + "renderable": { + "glyph" : "w", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "static", + "attributes" : { + "might" : 3, + "fitness" : 3 + }, + "skills" : { + "Melee" : -1, + "Defense" : -1 + }, + "natural" : { + "armor_class" : 12, + "attacks" : [ + { "name" : "bite", "hit_bonus" : 0, "damage" : "1d6" } + ] + }, + "loot_table" : "Animal", + "faction" : "Carnivores" + }, + + { + "name" : "Fox", + "renderable": { + "glyph" : "f", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "static", + "attributes" : { + "might" : 3, + "fitness" : 3 + }, + "skills" : { + "Melee" : -1, + "Defense" : -1 + }, + "natural" : { + "armor_class" : 11, + "attacks" : [ + { "name" : "bite", "hit_bonus" : 0, "damage" : "1d4" } + ] + }, + "loot_table" : "Animal", + "faction" : "Carnivores" + }, + + { + "name" : "Deer", + "renderable": { + "glyph" : "d", + "fg" : "#FFFF00", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "random", + "attributes" : { + "might" : 3, + "fitness" : 3 + }, + "skills" : { + "Melee" : -1, + "Defense" : -1 + }, + "natural" : { + "armor_class" : 11, + "attacks" : [ + { "name" : "bite", "hit_bonus" : 0, "damage" : "1d4" } + ] + }, + "loot_table" : "Animal", + "faction" : "Herbivores" + }, + + { + "name" : "Bandit", + "renderable": { + "glyph" : "☻", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 6, + "movement" : "random_waypoint", + "quips" : [ "Stand and deliver!", "Alright, hand it over" ], + "attributes" : {}, + "equipped" : [ "Dagger", "Shield", "Leather Armor", "Leather Boots" ], + "light" : { + "range" : 6, + "color" : "#FFFF55" + }, + "faction" : "Bandits", + "gold" : "1d6" + }, + + { + "name" : "Bandit Archer", + "renderable": { + "glyph" : "☻", + "fg" : "#FF5500", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 6, + "movement" : "random_waypoint", + "quips" : [ "Stand and deliver!", "Alright, hand it over" ], + "attributes" : {}, + "equipped" : [ "Shortbow", "Leather Armor", "Leather Boots" ], + "light" : { + "range" : 6, + "color" : "#FFFF55" + }, + "faction" : "Bandits", + "gold" : "1d6" + }, + + { + "name" : "Dark Elf", + "renderable": { + "glyph" : "e", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "random_waypoint", + "attributes" : {}, + "equipped" : [ "Hand Crossbow", "Scimitar", "Buckler", "Drow Chain", "Drow Leggings", "Drow Boots" ], + "faction" : "DarkElf", + "gold" : "3d6", + "level" : 6 + }, + + { + "name" : "Arbat Dark Elf", + "renderable": { + "glyph" : "e", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "random_waypoint", + "attributes" : {}, + "equipped" : [ "Scimitar +1", "Buckler", "Drow Chain", "Drow Leggings", "Drow Boots" ], + "faction" : "DarkElfA", + "gold" : "3d6", + "level" : 6 + }, + + { + "name" : "Arbat Dark Elf Leader", + "renderable": { + "glyph" : "E", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "random_waypoint", + "attributes" : {}, + "equipped" : [ "Scimitar +2", "Buckler +1", "Drow Chain", "Drow Leggings", "Drow Boots" ], + "faction" : "DarkElfA", + "gold" : "3d6", + "level" : 7 + }, + + { + "name" : "Arbat Orc Slave", + "renderable": { + "glyph" : "o", + "fg" : "#FFAAAA", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "static", + "attributes" : {}, + "faction" : "DarkElfA", + "gold" : "1d8" + }, + + { + "name" : "Barbo Dark Elf", + "renderable": { + "glyph" : "e", + "fg" : "#FF9900", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "random_waypoint", + "attributes" : {}, + "equipped" : [ "Hand Crossbow +1", "Dagger", "Buckler", "Drow Chain", "Drow Leggings", "Drow Boots" ], + "faction" : "DarkElfB", + "gold" : "3d6", + "level" : 6 + }, + + { + "name" : "Barbo Goblin Archer", + "renderable": { + "glyph" : "g", + "fg" : "#FF9900", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "static", + "attributes" : {}, + "faction" : "Cave Goblins", + "gold" : "1d6", + "equipped" : [ "Shortbow", "Leather Armor", "Leather Boots" ] + }, + + { + "name" : "Cirro Dark Elf", + "renderable": { + "glyph" : "e", + "fg" : "#FF00FF", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "random_waypoint", + "attributes" : {}, + "equipped" : [ "Hand Crossbow", "Scimitar", "Buckler", "Drow Chain", "Drow Leggings", "Drow Boots" ], + "faction" : "DarkElfC", + "gold" : "3d6", + "level" : 7 + }, + + { + "name" : "Cirro Dark Priestess", + "renderable": { + "glyph" : "E", + "fg" : "#FF00FF", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "random_waypoint", + "attributes" : {}, + "equipped" : [ "Hand Crossbow", "Scimitar", "Buckler", "Drow Chain", "Drow Leggings", "Drow Boots" ], + "faction" : "DarkElfC", + "gold" : "3d6", + "level" : 8, + "abilities" : [ + { "spell" : "Web", "chance" : 0.2, "range" : 6.0, "min_range" : 3.0 } + ] + }, + + { + "name" : "Cirro Spider", + "level" : 3, + "attributes" : {}, + "renderable": { + "glyph" : "s", + "fg" : "#FF00FF", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 6, + "movement" : "static", + "natural" : { + "armor_class" : 12, + "attacks" : [ + { "name" : "bite", "hit_bonus" : 1, "damage" : "1d12" } + ] + }, + "abilities" : [ + { "spell" : "Web", "chance" : 0.2, "range" : 6.0, "min_range" : 3.0 } + ], + "faction" : "DarkElfC" + }, + + { + "name" : "Orc", + "renderable": { + "glyph" : "o", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "static", + "attributes" : {}, + "faction" : "Cave Goblins", + "gold" : "1d8" + }, + + { + "name" : "Orc Leader", + "renderable": { + "glyph" : "O", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "static", + "attributes" : {}, + "faction" : "Cave Goblins", + "gold" : "3d8", + "equipped" : [ "Battleaxe", "Tower Shield", "Leather Armor", "Leather Boots" ], + "level" : 2 + }, + + { + "name" : "Goblin", + "renderable": { + "glyph" : "g", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "static", + "attributes" : {}, + "faction" : "Cave Goblins", + "gold" : "1d6" + }, + + { + "name" : "Goblin Archer", + "renderable": { + "glyph" : "g", + "fg" : "#FFFF00", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "static", + "attributes" : {}, + "faction" : "Cave Goblins", + "gold" : "1d6", + "equipped" : [ "Shortbow", "Leather Armor", "Leather Boots" ] + }, + + { + "name" : "Kobold", + "renderable": { + "glyph" : "k", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "static", + "attributes" : {}, + "faction" : "Cave Goblins", + "gold" : "1d4" + }, + + { + "name" : "Bat", + "renderable": { + "glyph" : "b", + "fg" : "#995555", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 6, + "movement" : "random", + "attributes" : { + "might" : 3, + "fitness" : 3 + }, + "skills" : { + "Melee" : -1, + "Defense" : -1 + }, + "natural" : { + "armor_class" : 11, + "attacks" : [ + { "name" : "bite", "hit_bonus" : 0, "damage" : "1d4" } + ] + }, + "faction" : "Herbivores" + }, + + { + "name" : "Large Spider", + "level" : 2, + "attributes" : {}, + "renderable": { + "glyph" : "s", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 6, + "movement" : "static", + "natural" : { + "armor_class" : 12, + "attacks" : [ + { "name" : "bite", "hit_bonus" : 1, "damage" : "1d12" } + ] + }, + "abilities" : [ + { "spell" : "Web", "chance" : 0.2, "range" : 6.0, "min_range" : 3.0 } + ], + "faction" : "Carnivores" + }, + + { + "name" : "Gelatinous Cube", + "level" : 2, + "attributes" : {}, + "renderable": { + "glyph" : "▄", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "static", + "natural" : { + "armor_class" : 12, + "attacks" : [ + { "name" : "engulf", "hit_bonus" : 0, "damage" : "1d8" } + ] + }, + "light" : { + "range" : 4, + "color" : "#550000" + } + }, + + { + "name" : "Dragon Wyrmling", + "renderable": { + "glyph" : "d", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 12, + "movement" : "random_waypoint", + "attributes" : { + "might" : 3, + "fitness" : 3 + }, + "skills" : { + "Melee" : 15, + "Defense" : 14 + }, + "natural" : { + "armor_class" : 15, + "attacks" : [ + { "name" : "bite", "hit_bonus" : 4, "damage" : "1d10+2" } + ] + }, + "loot_table" : "Wyrms", + "faction" : "Wyrm", + "level" : 3, + "gold" : "3d6" + }, + + { + "name" : "Black Dragon", + "renderable": { + "glyph" : "D", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1, + "x_size" : 2, + "y_size" : 2 + }, + "blocks_tile" : true, + "vision_range" : 12, + "movement" : "static", + "attributes" : { + "might" : 13, + "fitness" : 13 + }, + "skills" : { + "Melee" : 18, + "Defense" : 16 + }, + "natural" : { + "armor_class" : 17, + "attacks" : [ + { "name" : "bite", "hit_bonus" : 4, "damage" : "1d10+2" }, + { "name" : "left_claw", "hit_bonus" : 2, "damage" : "1d10" }, + { "name" : "right_claw", "hit_bonus" : 2, "damage" : "1d10" } + ] + }, + "loot_table" : "Wyrms", + "faction" : "Wyrm", + "level" : 6, + "gold" : "20d10", + "abilities" : [ + { "spell" : "Acid Breath", "chance" : 0.2, "range" : 8.0, "min_range" : 2.0 } + ] + }, + + { + "name" : "Lizardman", + "renderable": { + "glyph" : "l", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "random_waypoint", + "attributes" : {}, + "faction" : "Wyrm", + "gold" : "1d12", + "level" : 2 + }, + + { + "name" : "Giant Lizard", + "renderable": { + "glyph" : "l", + "fg" : "#FFFF00", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 4, + "movement" : "random", + "attributes" : {}, + "faction" : "Wyrm", + "level" : 2, + "loot_table" : "Animal" + }, + + { + "name" : "Rock Golem", + "renderable": { + "glyph" : "g", + "fg" : "#AAAAAA", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 6, + "movement" : "random_waypoint", + "attributes" : {}, + "faction" : "Dwarven Remnant", + "level" : 3 + }, + + { + "name" : "Firecap Mushroom", + "renderable": { + "glyph" : "♠", + "fg" : "#FFAA50", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 3, + "movement" : "static", + "attributes" : {}, + "faction" : "Fungi", + "level" : 1, + "abilities" : [ + { "spell" : "Explode", "chance" : 1.0, "range" : 3.0, "min_range" : 0.0 } + ], + "on_death" : [ + { "spell" : "Explode", "chance" : 1.0, "range" : 0.0, "min_range" : 0.0 } + ] + }, + + { + "name" : "Sporecap Mushroom", + "renderable": { + "glyph" : "♠", + "fg" : "#00AAFF", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 3, + "movement" : "static", + "attributes" : {}, + "faction" : "Fungi", + "level" : 1, + "abilities" : [ + { "spell" : "ConfusionCloud", "chance" : 1.0, "range" : 3.0, "min_range" : 0.0 } + ], + "on_death" : [ + { "spell" : "ConfusionCloud", "chance" : 1.0, "range" : 0.0, "min_range" : 0.0 } + ] + }, + + { + "name" : "Deathcap Mushroom", + "renderable": { + "glyph" : "♠", + "fg" : "#55FF55", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 3, + "movement" : "static", + "attributes" : {}, + "faction" : "Fungi", + "level" : 1, + "abilities" : [ + { "spell" : "PoisonCloud", "chance" : 1.0, "range" : 3.0, "min_range" : 0.0 } + ], + "on_death" : [ + { "spell" : "PoisonCloud", "chance" : 1.0, "range" : 0.0, "min_range" : 0.0 } + ] + }, + + { + "name" : "Fungus Man", + "renderable": { + "glyph" : "f", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "random_waypoint", + "attributes" : {}, + "faction" : "Fungi", + "gold" : "2d8", + "level" : 4, + "loot_table" : "Animal" + }, + + { + "name" : "Spore Zombie", + "renderable": { + "glyph" : "z", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 8, + "movement" : "random_waypoint", + "attributes" : {}, + "faction" : "Fungi", + "gold" : "2d8", + "level" : 5 + }, + + { + "name" : "Fungal Beast", + "renderable": { + "glyph" : "F", + "fg" : "#995555", + "bg" : "#000000", + "order" : 1 + }, + "blocks_tile" : true, + "vision_range" : 6, + "movement" : "random", + "attributes" : {}, + "natural" : { + "armor_class" : 11, + "attacks" : [ + { "name" : "bite", "hit_bonus" : 0, "damage" : "1d4" } + ] + }, + "faction" : "Fungi" + }, + + { + "name" : "Vokoth", + "renderable": { + "glyph" : "&", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 1, + "x_size" : 2, + "y_size" : 2 + }, + "blocks_tile" : true, + "vision_range" : 6, + "movement" : "static", + "attributes" : { + "might" : 13, + "fitness" : 13 + }, + "skills" : { + "Melee" : 18, + "Defense" : 16 + }, + "natural" : { + "armor_class" : 17, + "attacks" : [ + { "name" : "whip", "hit_bonus" : 4, "damage" : "1d10+2" } + ] + }, + "loot_table" : "Wyrms", + "faction" : "Wyrm", + "level" : 8, + "gold" : "20d10", + "abilities" : [] + } +], + +"props" : [ + { + "name" : "Bear Trap", + "renderable": { + "glyph" : "^", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : true, + "entry_trigger" : { + "effects" : { + "damage" : "6", + "single_activation" : "1" + } + } + }, + + { + "name" : "Stonefall Trap", + "renderable": { + "glyph" : "^", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : true, + "entry_trigger" : { + "effects" : { + "damage" : "12", + "single_activation" : "1" + } + } + }, + + { + "name" : "Landmine", + "renderable": { + "glyph" : "^", + "fg" : "#FF0000", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : true, + "entry_trigger" : { + "effects" : { + "damage" : "18", + "single_activation" : "1", + "area_of_effect" : "3", + "particle" : "▓;#FFA500;200.0" + } + } + }, + + { + "name" : "Door", + "renderable": { + "glyph" : "+", + "fg" : "#805A46", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false, + "blocks_tile" : true, + "blocks_visibility" : true, + "door_open" : true + }, + + { + "name" : "Keg", + "renderable": { + "glyph" : "φ", + "fg" : "#AAAAAA", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false + }, + + { + "name" : "Table", + "renderable": { + "glyph" : "╦", + "fg" : "#AAAAAA", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false + }, + + { + "name" : "Chair", + "renderable": { + "glyph" : "└", + "fg" : "#AAAAAA", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false + }, + + { + "name" : "Altar", + "renderable": { + "glyph" : "╫", + "fg" : "#5555FF", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false, + "entry_trigger" : { + "effects" : { + "provides_healing" : "100" + } + } + }, + + { + "name" : "Candle", + "renderable": { + "glyph" : "Ä", + "fg" : "#FFA500", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false + }, + + { + "name" : "Anvil", + "renderable": { + "glyph" : "╔", + "fg" : "#AAAAAA", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false + }, + + { + "name" : "Water Trough", + "renderable": { + "glyph" : "•", + "fg" : "#5555FF", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false + }, + + { + "name" : "Weapon Rack", + "renderable": { + "glyph" : "π", + "fg" : "#FFD700", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false + }, + + { + "name" : "Armor Stand", + "renderable": { + "glyph" : "⌠", + "fg" : "#FFFFFF", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false + }, + + { + "name" : "Chemistry Set", + "renderable": { + "glyph" : "δ", + "fg" : "#00FFFF", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false + }, + + { + "name" : "Dead Thing", + "renderable": { + "glyph" : "☻", + "fg" : "#AA0000", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false + }, + + { + "name" : "Cabinet", + "renderable": { + "glyph" : "∩", + "fg" : "#805A46", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false + }, + + { + "name" : "Bed", + "renderable": { + "glyph" : "8", + "fg" : "#805A46", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false + }, + + { + "name" : "Loom", + "renderable": { + "glyph" : "≡", + "fg" : "#805A46", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false + }, + + { + "name" : "Hide Rack", + "renderable": { + "glyph" : "π", + "fg" : "#805A46", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false + }, + + { + "name" : "Watch Fire", + "renderable": { + "glyph" : "☼", + "fg" : "#FFFF55", + "bg" : "#000000", + "order" : 2 + }, + "hidden" : false, + "light" : { + "range" : 6, + "color" : "#FFFF55" + }, + "entry_trigger" : { + "effects" : { + "damage" : "6" + } + } + } +], + +"spells" : [ + { + "name" : "Zap", + "mana_cost" : 1, + "effects" : { + "ranged" : "6", + "damage" : "5", + "particle_line" : "▓;#00FFFF;400.0" + } + }, + + { + "name" : "Web", + "mana_cost" : 2, + "effects" : { + "ranged" : "6", + "slow" : "10", + "area_of_effect" : "3", + "particle_line" : "☼;#FFFFFF;400.0" + } + }, + + { + "name" : "Venom", + "mana_cost" : 2, + "effects" : { + "ranged" : "6", + "damage_over_time" : "4", + "particle_line" : "▓;#00FF00;400.0" + } + }, + + { + "name" : "Acid Breath", + "mana_cost" : 2, + "effects" : { + "ranged" : "6", + "damage" : "10", + "area_of_effect" : "3", + "particle" : "☼;#00FF00;400.0" + } + }, + + { + "name" : "Explode", + "mana_cost" : 1, + "effects" : { + "ranged" : "3", + "damage" : "20", + "area_of_effect" : "3", + "particle" : "▒;#FFAA50;400.0", + "single_activation" : "1", + "target_self" : "1" + } + }, + + { + "name" : "ConfusionCloud", + "mana_cost" : 1, + "effects" : { + "ranged" : "3", + "confusion" : "4", + "area_of_effect" : "3", + "particle" : "?;#FFFF00;400.0", + "single_activation" : "1", + "target_self" : "1" + } + }, + + { + "name" : "PoisonCloud", + "mana_cost" : 1, + "effects" : { + "ranged" : "3", + "damage_over_time" : "4", + "area_of_effect" : "3", + "particle" : "*;#00FF00;400.0", + "single_activation" : "1", + "target_self" : "1" + } + } +], + +"weapon_traits" : [ + { + "name" : "Venomous", + "effects" : { "damage_over_time" : "2" } + }, + { + "name" : "Dazzling", + "effects" : { "confusion" : "2" } + } +] +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/components.rs b/chapter-75-darkplaza/src/components.rs new file mode 100644 index 00000000..42ba281b --- /dev/null +++ b/chapter-75-darkplaza/src/components.rs @@ -0,0 +1,494 @@ +use specs::prelude::*; +use specs_derive::*; +use rltk::{RGB, Point}; +use serde::{Serialize, Deserialize}; +use specs::saveload::{Marker, ConvertSaveload}; +use specs::error::NoError; +use std::collections::HashMap; + +#[derive(Component, ConvertSaveload, Clone)] +pub struct Position { + pub x: i32, + pub y: i32, +} + +#[derive(Component, ConvertSaveload, Clone)] +pub struct TileSize { + pub x: i32, + pub y: i32, +} + +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct OtherLevelPosition { + pub x: i32, + pub y: i32, + pub depth: i32 +} + +#[derive(Component, ConvertSaveload, Clone)] +pub struct Renderable { + pub glyph: rltk::FontCharType, + pub fg: RGB, + pub bg: RGB, + pub render_order : i32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Player {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Target {} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct KnownSpell { + pub display_name : String, + pub mana_cost : i32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct KnownSpells { + pub spells : Vec +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct SpecialAbility { + pub spell : String, + pub chance : f32, + pub range : f32, + pub min_range : f32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct SpecialAbilities { + pub abilities : Vec +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct OnDeath { + pub abilities : Vec +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct AlwaysTargetsSelf {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct SpellTemplate { + pub mana_cost : i32 +} + +#[derive(Component, ConvertSaveload, Clone)] +pub struct Viewshed { + pub visible_tiles : Vec, + pub range : i32, + pub dirty : bool +} + +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct LightSource { + pub color : RGB, + pub range: i32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Initiative { + pub current : i32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Vendor { + pub categories : Vec +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct MyTurn {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Faction { + pub name : String +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct ApplyMove { + pub dest_idx : usize +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct ApplyTeleport { + pub dest_x : i32, + pub dest_y : i32, + pub dest_depth : i32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct WantsToApproach { + pub idx : i32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct WantsToFlee { + pub indices : Vec +} + +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] +pub enum Movement { + Static, + Random, + RandomWaypoint{ path : Option> } +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct MoveMode { + pub mode : Movement +} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct Name { + pub name : String +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct ObfuscatedName { + pub name : String +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct IdentifiedItem { + pub name : String +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct BlocksTile {} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Pool { + pub max: i32, + pub current: i32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Pools { + pub hit_points : Pool, + pub mana : Pool, + pub xp : i32, + pub level : i32, + pub total_weight : f32, + pub total_initiative_penalty : f32, + pub gold : f32, + pub god_mode : bool +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Attribute { + pub base : i32, + pub modifiers : i32, + pub bonus : i32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Attributes { + pub might : Attribute, + pub fitness : Attribute, + pub quickness : Attribute, + pub intelligence : Attribute +} + +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] +pub enum Skill { Melee, Defense, Magic } + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Skills { + pub skills : HashMap +} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct WantsToMelee { + pub target : Entity +} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct WantsToShoot { + pub target : Entity +} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct Chasing { + pub target : Entity +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct LootTable { + pub table : String +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct EquipmentChanged {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Item { + pub initiative_penalty : f32, + pub weight_lbs : f32, + pub base_value : f32 +} + +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] +pub enum MagicItemClass { Common, Rare, Legendary } + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct MagicItem { + pub class : MagicItemClass +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct AttributeBonus { + pub might : Option, + pub fitness : Option, + pub quickness : Option, + pub intelligence : Option +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct CursedItem {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Consumable { + pub max_charges : i32, + pub charges : i32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct ProvidesRemoveCurse {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct ProvidesIdentification {} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct Ranged { + pub range : i32 +} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct InflictsDamage { + pub damage : i32 +} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct AreaOfEffect { + pub radius : i32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Confusion {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Slow { + pub initiative_penalty : f32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct DamageOverTime { + pub damage : i32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Duration { + pub turns : i32 +} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct StatusEffect { + pub target : Entity +} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct ProvidesHealing { + pub heal_amount : i32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct ProvidesMana { + pub mana_amount : i32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct TeachesSpell { + pub spell : String +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct BlocksVisibility {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Door { + pub open: bool +} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct InBackpack { + pub owner : Entity +} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct WantsToPickupItem { + pub collected_by : Entity, + pub item : Entity +} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct WantsToUseItem { + pub item : Entity, + pub target : Option +} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct WantsToCastSpell { + pub spell : Entity, + pub target : Option +} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct WantsToDropItem { + pub item : Entity +} + +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct WantsToRemoveItem { + pub item : Entity +} + +#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)] +pub enum EquipmentSlot { Melee, Shield, Head, Torso, Legs, Feet, Hands } + +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct Equippable { + pub slot : EquipmentSlot +} + +#[derive(Component, ConvertSaveload, Clone)] +pub struct Equipped { + pub owner : Entity, + pub slot : EquipmentSlot +} + +#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)] +pub enum WeaponAttribute { Might, Quickness } + +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct Weapon { + pub range : Option, + pub attribute : WeaponAttribute, + pub damage_n_dice : i32, + pub damage_die_type : i32, + pub damage_bonus : i32, + pub hit_bonus : i32, + pub proc_chance : Option, + pub proc_target : Option, +} + +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct Wearable { + pub armor_class : f32, + pub slot : EquipmentSlot +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct NaturalAttack { + pub name : String, + pub damage_n_dice : i32, + pub damage_die_type : i32, + pub damage_bonus : i32, + pub hit_bonus : i32 +} + +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct NaturalAttackDefense { + pub armor_class : Option, + pub attacks : Vec +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct ParticleAnimation { + pub step_time : f32, + pub path : Vec, + pub current_step : usize, + pub timer : f32 +} + +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct ParticleLifetime { + pub lifetime_ms : f32, + pub animation : Option +} + +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct SpawnParticleLine { + pub glyph : rltk::FontCharType, + pub color : RGB, + pub lifetime_ms : f32 +} + +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct SpawnParticleBurst { + pub glyph : rltk::FontCharType, + pub color : RGB, + pub lifetime_ms : f32 +} + +#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)] +pub enum HungerState { WellFed, Normal, Hungry, Starving } + +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct HungerClock { + pub state : HungerState, + pub duration : i32 +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct ProvidesFood {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct MagicMapper {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct TownPortal {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct TeleportTo { + pub x: i32, + pub y: i32, + pub depth: i32, + pub player_only : bool +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Hidden {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct EntryTrigger {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct EntityMoved {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct SingleActivation {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Quips { + pub available : Vec +} + +// Serialization helper code. We need to implement ConvertSaveLoad for each type that contains an +// Entity. + +pub struct SerializeMe; + +// Special component that exists to help serialize the game data +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct SerializationHelper { + pub map : super::map::Map, +} +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct DMSerializationHelper { + pub map : super::map::MasterDungeonMap, + pub log : Vec>, + pub events : HashMap +} diff --git a/chapter-75-darkplaza/src/damage_system.rs b/chapter-75-darkplaza/src/damage_system.rs new file mode 100644 index 00000000..70280329 --- /dev/null +++ b/chapter-75-darkplaza/src/damage_system.rs @@ -0,0 +1,129 @@ +use specs::prelude::*; +use super::{Pools, Player, Name, RunState, Position, + InBackpack, Equipped, LootTable}; + +pub fn delete_the_dead(ecs : &mut World) { + let mut dead : Vec = Vec::new(); + // Using a scope to make the borrow checker happy + { + let combat_stats = ecs.read_storage::(); + let players = ecs.read_storage::(); + let names = ecs.read_storage::(); + let entities = ecs.entities(); + for (entity, stats) in (&entities, &combat_stats).join() { + if stats.hit_points.current < 1 { + let player = players.get(entity); + match player { + None => { + let victim_name = names.get(entity); + if let Some(victim_name) = victim_name { + crate::gamelog::Logger::new() + .color(rltk::RED) + .append(&victim_name.name) + .append("is dead!") + .log(); + } + dead.push(entity) + } + Some(_) => { + let mut runstate = ecs.write_resource::(); + *runstate = RunState::GameOver; + } + } + } + } + } + + // Drop everything held by dead people + let mut to_spawn : Vec<(String, Position)> = Vec::new(); + { // To avoid keeping hold of borrowed entries, use a scope + let mut to_drop : Vec<(Entity, Position)> = Vec::new(); + let entities = ecs.entities(); + let mut equipped = ecs.write_storage::(); + let mut carried = ecs.write_storage::(); + let mut positions = ecs.write_storage::(); + let loot_tables = ecs.read_storage::(); + for victim in dead.iter() { + let pos = positions.get(*victim); + for (entity, equipped) in (&entities, &equipped).join() { + if equipped.owner == *victim { + // Drop their stuff + if let Some(pos) = pos { + to_drop.push((entity, pos.clone())); + } + } + } + for (entity, backpack) in (&entities, &carried).join() { + if backpack.owner == *victim { + // Drop their stuff + if let Some(pos) = pos { + to_drop.push((entity, pos.clone())); + } + } + } + + if let Some(table) = loot_tables.get(*victim) { + let drop_finder = crate::raws::get_item_drop( + &crate::raws::RAWS.lock().unwrap(), + &table.table + ); + if let Some(tag) = drop_finder { + if let Some(pos) = pos { + to_spawn.push((tag, pos.clone())); + } + } + } + } + + for drop in to_drop.iter() { + equipped.remove(drop.0); + carried.remove(drop.0); + positions.insert(drop.0, drop.1.clone()).expect("Unable to insert position"); + } + } + + { + for drop in to_spawn.iter() { + crate::raws::spawn_named_item( + &crate::raws::RAWS.lock().unwrap(), + ecs, + &drop.0, + crate::raws::SpawnType::AtPosition{x : drop.1.x, y: drop.1.y} + ); + } + } + + // Fire death events + use crate::effects::*; + use crate::Map; + use crate::components::{OnDeath, AreaOfEffect}; + for victim in dead.iter() { + let death_effects = ecs.read_storage::(); + if let Some(death_effect) = death_effects.get(*victim) { + for effect in death_effect.abilities.iter() { + if crate::rng::roll_dice(1,100) <= (effect.chance * 100.0) as i32 { + let map = ecs.fetch::(); + if let Some(pos) = ecs.read_storage::().get(*victim) { + let spell_entity = crate::raws::find_spell_entity(ecs, &effect.spell).unwrap(); + let tile_idx = map.xy_idx(pos.x, pos.y); + let target = + if let Some(aoe) = ecs.read_storage::().get(spell_entity) { + Targets::Tiles { tiles : aoe_tiles(&map, rltk::Point::new(pos.x, pos.y), aoe.radius) } + } else { + Targets::Tile{ tile_idx : tile_idx as i32 } + }; + add_effect( + None, + EffectType::SpellUse{ spell: crate::raws::find_spell_entity( ecs, &effect.spell ).unwrap() }, + target + ); + } + } + } + } + } + + for victim in dead { + ecs.delete_entity(victim).expect("Unable to delete"); + } +} diff --git a/chapter-75-darkplaza/src/effects/damage.rs b/chapter-75-darkplaza/src/effects/damage.rs new file mode 100644 index 00000000..e43c4c93 --- /dev/null +++ b/chapter-75-darkplaza/src/effects/damage.rs @@ -0,0 +1,240 @@ +use specs::prelude::*; +use super::*; +use crate::components::{Pools, Player, Attributes, Confusion, SerializeMe, Duration, StatusEffect, + Name, EquipmentChanged, Slow, DamageOverTime, Skills }; +use crate::map::Map; +use crate::gamesystem::{player_hp_at_level, mana_at_level}; +use specs::saveload::{MarkedBuilder, SimpleMarker}; + +pub fn inflict_damage(ecs: &mut World, damage: &EffectSpawner, target: Entity) { + let mut pools = ecs.write_storage::(); + let player_entity = ecs.fetch::(); + if let Some(pool) = pools.get_mut(target) { + if !pool.god_mode { + if let Some(creator) = damage.creator { + if creator == target { + return; + } + } + if let EffectType::Damage{amount} = damage.effect_type { + pool.hit_points.current -= amount; + add_effect(None, EffectType::Bloodstain, Targets::Single{target}); + add_effect(None, + EffectType::Particle{ + glyph: rltk::to_cp437('‼'), + fg : rltk::RGB::named(rltk::ORANGE), + bg : rltk::RGB::named(rltk::BLACK), + lifespan: 200.0 + }, + Targets::Single{target} + ); + if target == *player_entity { + crate::gamelog::record_event("Damage Taken", amount); + } + if let Some(creator) = damage.creator { + if creator == *player_entity { + crate::gamelog::record_event("Damage Inflicted", amount); + } + } + + if pool.hit_points.current < 1 { + add_effect(damage.creator, EffectType::EntityDeath, Targets::Single{target}); + } + } + } + } +} + +pub fn bloodstain(ecs: &mut World, tile_idx : i32) { + let mut map = ecs.fetch_mut::(); + map.bloodstains.insert(tile_idx as usize); +} + +pub fn death(ecs: &mut World, effect: &EffectSpawner, target : Entity) { + let mut xp_gain = 0; + let mut gold_gain = 0.0f32; + + let mut pools = ecs.write_storage::(); + let mut attributes = ecs.write_storage::(); + + if let Some(pos) = entity_position(ecs, target) { + crate::spatial::remove_entity(target, pos as usize); + } + + if let Some(source) = effect.creator { + if ecs.read_storage::().get(source).is_some() { + if let Some(stats) = pools.get(target) { + xp_gain += stats.level * 100; + gold_gain += stats.gold; + } + + if xp_gain != 0 || gold_gain != 0.0 { + let mut player_stats = pools.get_mut(source).unwrap(); + let mut player_attributes = attributes.get_mut(source).unwrap(); + player_stats.xp += xp_gain; + player_stats.gold += gold_gain; + if player_stats.xp >= player_stats.level * 1000 { + // We've gone up a level! + player_stats.level += 1; + crate::gamelog::Logger::new() + .color(rltk::MAGENTA) + .append("Congratulations, you are now level") + .append(format!("{}", player_stats.level)) + .log(); + + // Improve a random attribute + let attr_to_boost = crate::rng::roll_dice(1, 4); + match attr_to_boost { + 1 => { + player_attributes.might.base += 1; + crate::gamelog::Logger::new().color(rltk::GREEN).append("You feel stronger!").log(); + } + 2 => { + player_attributes.fitness.base += 1; + crate::gamelog::Logger::new().color(rltk::GREEN).append("You feel healthier!").log(); + } + 3 => { + player_attributes.quickness.base += 1; + crate::gamelog::Logger::new().color(rltk::GREEN).append("You feel quicker!").log(); + } + _ => { + player_attributes.intelligence.base += 1; + crate::gamelog::Logger::new().color(rltk::GREEN).append("You feel smarter!").log(); + } + } + + // Improve all skills + let mut skills = ecs.write_storage::(); + let player_skills = skills.get_mut(*ecs.fetch::()).unwrap(); + for sk in player_skills.skills.iter_mut() { + *sk.1 += 1; + } + + ecs.write_storage::() + .insert( + *ecs.fetch::(), + EquipmentChanged{}) + .expect("Insert Failed"); + + player_stats.hit_points.max = player_hp_at_level( + player_attributes.fitness.base + player_attributes.fitness.modifiers, + player_stats.level + ); + player_stats.hit_points.current = player_stats.hit_points.max; + player_stats.mana.max = mana_at_level( + player_attributes.intelligence.base + player_attributes.intelligence.modifiers, + player_stats.level + ); + player_stats.mana.current = player_stats.mana.max; + + let player_pos = ecs.fetch::(); + let map = ecs.fetch::(); + for i in 0..10 { + if player_pos.y - i > 1 { + add_effect(None, + EffectType::Particle{ + glyph: rltk::to_cp437('░'), + fg : rltk::RGB::named(rltk::GOLD), + bg : rltk::RGB::named(rltk::BLACK), + lifespan: 400.0 + }, + Targets::Tile{ tile_idx : map.xy_idx(player_pos.x, player_pos.y - i) as i32 } + ); + } + } + } + } + } + } +} + +pub fn heal_damage(ecs: &mut World, heal: &EffectSpawner, target: Entity) { + let mut pools = ecs.write_storage::(); + if let Some(pool) = pools.get_mut(target) { + if let EffectType::Healing{amount} = heal.effect_type { + pool.hit_points.current = i32::min(pool.hit_points.max, pool.hit_points.current + amount); + add_effect(None, + EffectType::Particle{ + glyph: rltk::to_cp437('‼'), + fg : rltk::RGB::named(rltk::GREEN), + bg : rltk::RGB::named(rltk::BLACK), + lifespan: 200.0 + }, + Targets::Single{target} + ); + } + } +} + +pub fn restore_mana(ecs: &mut World, mana: &EffectSpawner, target: Entity) { + let mut pools = ecs.write_storage::(); + if let Some(pool) = pools.get_mut(target) { + if let EffectType::Mana{amount} = mana.effect_type { + pool.mana.current = i32::min(pool.mana.max, pool.mana.current + amount); + add_effect(None, + EffectType::Particle{ + glyph: rltk::to_cp437('‼'), + fg : rltk::RGB::named(rltk::BLUE), + bg : rltk::RGB::named(rltk::BLACK), + lifespan: 200.0 + }, + Targets::Single{target} + ); + } + } +} + +pub fn add_confusion(ecs: &mut World, effect: &EffectSpawner, target: Entity) { + if let EffectType::Confusion{turns} = &effect.effect_type { + ecs.create_entity() + .with(StatusEffect{ target }) + .with(Confusion{}) + .with(Duration{ turns : *turns}) + .with(Name{ name : "Confusion".to_string() }) + .marked::>() + .build(); + } +} + +pub fn attribute_effect(ecs: &mut World, effect: &EffectSpawner, target: Entity) { + if let EffectType::AttributeEffect{bonus, name, duration} = &effect.effect_type { + ecs.create_entity() + .with(StatusEffect{ target }) + .with(bonus.clone()) + .with(Duration { turns : *duration }) + .with(Name { name : name.clone() }) + .marked::>() + .build(); + ecs.write_storage::().insert(target, EquipmentChanged{}).expect("Insert failed"); + } +} + +pub fn slow(ecs: &mut World, effect: &EffectSpawner, target: Entity) { + if let EffectType::Slow{initiative_penalty} = &effect.effect_type { + ecs.create_entity() + .with(StatusEffect{ target }) + .with(Slow{ initiative_penalty : *initiative_penalty }) + .with(Duration{ turns : 5}) + .with( + if *initiative_penalty > 0.0 { + Name{ name : "Slowed".to_string() } + } else { + Name{ name : "Hasted".to_string() } + } + ) + .marked::>() + .build(); + } +} + +pub fn damage_over_time(ecs: &mut World, effect: &EffectSpawner, target: Entity) { + if let EffectType::DamageOverTime{damage} = &effect.effect_type { + ecs.create_entity() + .with(StatusEffect{ target }) + .with(DamageOverTime{ damage : *damage }) + .with(Duration{ turns : 5}) + .with(Name{ name : "Damage Over Time".to_string() }) + .marked::>() + .build(); + } +} diff --git a/chapter-75-darkplaza/src/effects/hunger.rs b/chapter-75-darkplaza/src/effects/hunger.rs new file mode 100644 index 00000000..03139ce3 --- /dev/null +++ b/chapter-75-darkplaza/src/effects/hunger.rs @@ -0,0 +1,10 @@ +use specs::prelude::*; +use super::*; +use crate::components::{HungerClock, HungerState}; + +pub fn well_fed(ecs: &mut World, _damage: &EffectSpawner, target: Entity) { + if let Some(hc) = ecs.write_storage::().get_mut(target) { + hc.state = HungerState::WellFed; + hc.duration = 20; + } +} diff --git a/chapter-75-darkplaza/src/effects/mod.rs b/chapter-75-darkplaza/src/effects/mod.rs new file mode 100644 index 00000000..50890c83 --- /dev/null +++ b/chapter-75-darkplaza/src/effects/mod.rs @@ -0,0 +1,145 @@ +use std::sync::Mutex; +use specs::prelude::*; +use std::collections::{HashSet, VecDeque}; +use crate::map::Map; +mod damage; +mod targeting; +pub use targeting::*; +mod particles; +mod triggers; +mod hunger; +mod movement; +use crate::components::AttributeBonus; +use rltk::Point; + +lazy_static! { + pub static ref EFFECT_QUEUE : Mutex> = Mutex::new(VecDeque::new()); +} + +#[derive(Debug)] +pub enum EffectType { + Damage { amount : i32 }, + Bloodstain, + Particle { glyph: rltk::FontCharType, fg : rltk::RGB, bg: rltk::RGB, lifespan: f32 }, + ParticleProjectile { glyph: rltk::FontCharType, fg : rltk::RGB, bg: rltk::RGB, lifespan: f32, speed: f32, path: Vec }, + EntityDeath, + ItemUse { item: Entity }, + SpellUse { spell: Entity }, + WellFed, + Healing { amount : i32 }, + Mana { amount : i32 }, + Confusion { turns : i32 }, + TriggerFire { trigger: Entity }, + TeleportTo { x:i32, y:i32, depth: i32, player_only : bool }, + AttributeEffect { bonus : AttributeBonus, name : String, duration : i32 }, + Slow { initiative_penalty : f32 }, + DamageOverTime { damage : i32 } +} + +#[derive(Clone, Debug)] +pub enum Targets { + Single { target : Entity }, + TargetList { targets: Vec }, + Tile { tile_idx : i32 }, + Tiles { tiles: Vec } +} + +#[derive(Debug)] +pub struct EffectSpawner { + pub creator : Option, + pub effect_type : EffectType, + pub targets : Targets, + dedupe : HashSet +} + +pub fn add_effect(creator : Option, effect_type: EffectType, targets : Targets) { + EFFECT_QUEUE + .lock() + .unwrap() + .push_back(EffectSpawner{ + creator, + effect_type, + targets, + dedupe : HashSet::new() + }); +} + +pub fn run_effects_queue(ecs : &mut World) { + loop { + let effect : Option = EFFECT_QUEUE.lock().unwrap().pop_front(); + if let Some(mut effect) = effect { + target_applicator(ecs, &mut effect); + } else { + break; + } + } +} + +fn target_applicator(ecs : &mut World, effect : &mut EffectSpawner) { + if let EffectType::ItemUse{item} = effect.effect_type { + triggers::item_trigger(effect.creator, item, &effect.targets, ecs); + } else if let EffectType::SpellUse{spell} = effect.effect_type { + triggers::spell_trigger(effect.creator, spell, &effect.targets, ecs); + } else if let EffectType::TriggerFire{trigger} = effect.effect_type { + triggers::trigger(effect.creator, trigger, &effect.targets, ecs); + } else { + match &effect.targets.clone() { + Targets::Tile{tile_idx} => affect_tile(ecs, effect, *tile_idx), + Targets::Tiles{tiles} => tiles.iter().for_each(|tile_idx| affect_tile(ecs, effect, *tile_idx)), + Targets::Single{target} => affect_entity(ecs, effect, *target), + Targets::TargetList{targets} => targets.iter().for_each(|entity| affect_entity(ecs, effect, *entity)), + } + } +} + +fn tile_effect_hits_entities(effect: &EffectType) -> bool { + match effect { + EffectType::Damage{..} => true, + EffectType::WellFed => true, + EffectType::Healing{..} => true, + EffectType::Mana{..} => true, + EffectType::Confusion{..} => true, + EffectType::TeleportTo{..} => true, + EffectType::AttributeEffect{..} => true, + EffectType::Slow{..} => true, + EffectType::DamageOverTime{..} => true, + _ => false + } +} + +fn affect_tile(ecs: &mut World, effect: &mut EffectSpawner, tile_idx : i32) { + if tile_effect_hits_entities(&effect.effect_type) { + let content = crate::spatial::get_tile_content_clone(tile_idx as usize); + content.iter().for_each(|entity| affect_entity(ecs, effect, *entity)); + } + + match &effect.effect_type { + EffectType::Bloodstain => damage::bloodstain(ecs, tile_idx), + EffectType::Particle{..} => particles::particle_to_tile(ecs, tile_idx, &effect), + EffectType::ParticleProjectile{..} => particles::projectile(ecs, tile_idx, &effect), + _ => {} + } +} + +fn affect_entity(ecs: &mut World, effect: &mut EffectSpawner, target: Entity) { + if effect.dedupe.contains(&target) { + return; + } + effect.dedupe.insert(target); + match &effect.effect_type { + EffectType::Damage{..} => damage::inflict_damage(ecs, effect, target), + EffectType::EntityDeath => damage::death(ecs, effect, target), + EffectType::Bloodstain{..} => if let Some(pos) = entity_position(ecs, target) { damage::bloodstain(ecs, pos) }, + EffectType::Particle{..} => if let Some(pos) = entity_position(ecs, target) { particles::particle_to_tile(ecs, pos, &effect) }, + EffectType::WellFed => hunger::well_fed(ecs, effect, target), + EffectType::Healing{..} => damage::heal_damage(ecs, effect, target), + EffectType::Mana{..} => damage::restore_mana(ecs, effect, target), + EffectType::Confusion{..} => damage::add_confusion(ecs, effect, target), + EffectType::TeleportTo{..} => movement::apply_teleport(ecs, effect, target), + EffectType::AttributeEffect{..} => damage::attribute_effect(ecs, effect, target), + EffectType::Slow{..} => damage::slow(ecs, effect, target), + EffectType::DamageOverTime{..} => damage::damage_over_time(ecs, effect, target), + _ => {} + } +} + diff --git a/chapter-75-darkplaza/src/effects/movement.rs b/chapter-75-darkplaza/src/effects/movement.rs new file mode 100644 index 00000000..63b734e6 --- /dev/null +++ b/chapter-75-darkplaza/src/effects/movement.rs @@ -0,0 +1,17 @@ +use specs::prelude::*; +use super::*; +use crate::components::{ApplyTeleport}; + +pub fn apply_teleport(ecs: &mut World, destination: &EffectSpawner, target: Entity) { + let player_entity = ecs.fetch::(); + if let EffectType::TeleportTo{x, y, depth, player_only} = &destination.effect_type { + if !player_only || target == *player_entity { + let mut apply_teleport = ecs.write_storage::(); + apply_teleport.insert(target, ApplyTeleport{ + dest_x : *x, + dest_y : *y, + dest_depth : *depth + }).expect("Unable to insert"); + } + } +} diff --git a/chapter-75-darkplaza/src/effects/particles.rs b/chapter-75-darkplaza/src/effects/particles.rs new file mode 100644 index 00000000..90944ed9 --- /dev/null +++ b/chapter-75-darkplaza/src/effects/particles.rs @@ -0,0 +1,44 @@ +use specs::prelude::*; +use super::*; +use crate::systems::particle_system::ParticleBuilder; +use crate::map::Map; +use crate::components::{ParticleAnimation, ParticleLifetime, Renderable, Position}; + +pub fn particle_to_tile(ecs: &mut World, tile_idx : i32, effect: &EffectSpawner) { + if let EffectType::Particle{ glyph, fg, bg, lifespan } = effect.effect_type { + let map = ecs.fetch::(); + let mut particle_builder = ecs.fetch_mut::(); + particle_builder.request( + tile_idx % map.width, + tile_idx / map.width, + fg, + bg, + glyph, + lifespan + ); + } +} + +pub fn projectile(ecs: &mut World, tile_idx : i32, effect: &EffectSpawner) { + if let EffectType::ParticleProjectile{ glyph, fg, bg, + lifespan: _, speed, path } = &effect.effect_type + { + let map = ecs.fetch::(); + let x = tile_idx % map.width; + let y = tile_idx / map.width; + std::mem::drop(map); + ecs.create_entity() + .with(Position{ x, y }) + .with(Renderable{ fg: *fg, bg: *bg, glyph: *glyph, render_order: 0 }) + .with(ParticleLifetime{ + lifetime_ms: path.len() as f32 * speed, + animation: Some(ParticleAnimation{ + step_time: *speed, + path: path.to_vec(), + current_step: 0, + timer: 0.0 + }) + }) + .build(); + } +} diff --git a/chapter-75-darkplaza/src/effects/targeting.rs b/chapter-75-darkplaza/src/effects/targeting.rs new file mode 100644 index 00000000..bb85e302 --- /dev/null +++ b/chapter-75-darkplaza/src/effects/targeting.rs @@ -0,0 +1,55 @@ +use specs::prelude::*; +use crate::components::{Position, InBackpack, Equipped}; +use crate::map::Map; + +pub fn entity_position(ecs: &World, target: Entity) -> Option { + if let Some(pos) = ecs.read_storage::().get(target) { + let map = ecs.fetch::(); + return Some(map.xy_idx(pos.x, pos.y) as i32); + } + None +} + +pub fn aoe_tiles(map: &Map, target: rltk::Point, radius: i32) -> Vec { + let mut blast_tiles = rltk::field_of_view(target, radius, &*map); + blast_tiles.retain(|p| p.x > 0 && p.x < map.width-1 && p.y > 0 && p.y < map.height-1 ); + let mut result = Vec::new(); + for t in blast_tiles.iter() { + result.push(map.xy_idx(t.x, t.y) as i32); + } + result +} + +pub fn find_item_position(ecs: &World, target: Entity, creator: Option) -> Option { + let positions = ecs.read_storage::(); + let map = ecs.fetch::(); + + // Easy - it has a position + if let Some(pos) = positions.get(target) { + return Some(map.xy_idx(pos.x, pos.y) as i32); + } + + // Maybe it is carried? + if let Some(carried) = ecs.read_storage::().get(target) { + if let Some(pos) = positions.get(carried.owner) { + return Some(map.xy_idx(pos.x, pos.y) as i32); + } + } + + // Maybe it is equipped? + if let Some(equipped) = ecs.read_storage::().get(target) { + if let Some(pos) = positions.get(equipped.owner) { + return Some(map.xy_idx(pos.x, pos.y) as i32); + } + } + + // Maybe the creator has a position? + if let Some(creator) = creator { + if let Some(pos) = positions.get(creator) { + return Some(map.xy_idx(pos.x, pos.y) as i32); + } + } + + // No idea - give up + None +} diff --git a/chapter-75-darkplaza/src/effects/triggers.rs b/chapter-75-darkplaza/src/effects/triggers.rs new file mode 100644 index 00000000..f9d812d1 --- /dev/null +++ b/chapter-75-darkplaza/src/effects/triggers.rs @@ -0,0 +1,270 @@ +use super::*; +use crate::components::*; +use crate::RunState; + +pub fn item_trigger(creator : Option, item: Entity, targets : &Targets, ecs: &mut World) { + // Check charges + if let Some(c) = ecs.write_storage::().get_mut(item) { + if c.charges < 1 { + // Cancel + crate::gamelog::Logger::new() + .item_name(&ecs.read_storage::().get(item).unwrap().name) + .append("is out of charges!") + .log(); + return; + } else { + c.charges -= 1; + } + } + + // Use the item via the generic system + let did_something = event_trigger(creator, item, targets, ecs); + + // If it was a consumable, then it gets deleted + if did_something { + if let Some(c) = ecs.read_storage::().get(item) { + rltk::console::log(format!("{}", c.max_charges)); + if c.max_charges < 2 { + ecs.entities().delete(item).expect("Delete Failed"); + } + } + } +} + +pub fn spell_trigger(creator : Option, spell: Entity, targets : &Targets, ecs: &mut World) { + let mut targeting = targets.clone(); + let mut self_destruct = false; + if let Some(template) = ecs.read_storage::().get(spell) { + let mut pools = ecs.write_storage::(); + if let Some(caster) = creator { + if let Some(pool) = pools.get_mut(caster) { + if template.mana_cost <= pool.mana.current { + pool.mana.current -= template.mana_cost; + } + } + + // Handle self-targeting override + if ecs.read_storage::().get(spell).is_some() { + if let Some(pos) = ecs.read_storage::().get(caster) { + let map = ecs.fetch::(); + targeting = if let Some(aoe) = ecs.read_storage::().get(spell) { + Targets::Tiles { tiles : aoe_tiles(&map, rltk::Point::new(pos.x, pos.y), aoe.radius) } + } else { + Targets::Tile{ tile_idx : map.xy_idx(pos.x, pos.y) as i32 } + } + } + } + } + if let Some(_destruct) = ecs.read_storage::().get(spell) { + self_destruct = true; + } + } + event_trigger(creator, spell, &targeting, ecs); + if self_destruct && creator.is_some() { + ecs.entities().delete(creator.unwrap()).expect("Unable to delete owner"); + } +} + +pub fn trigger(creator : Option, trigger: Entity, targets : &Targets, ecs: &mut World) { + // The triggering item is no longer hidden + ecs.write_storage::().remove(trigger); + + // Use the item via the generic system + let did_something = event_trigger(creator, trigger, targets, ecs); + + // If it was a single activation, then it gets deleted + if did_something && ecs.read_storage::().get(trigger).is_some() { + ecs.entities().delete(trigger).expect("Delete Failed"); + } +} + +#[allow(clippy::cognitive_complexity)] +fn event_trigger(creator : Option, entity: Entity, targets : &Targets, ecs: &mut World) -> bool { + let mut did_something = false; + + // Simple particle spawn + if let Some(part) = ecs.read_storage::().get(entity) { + add_effect( + creator, + EffectType::Particle{ + glyph : part.glyph, + fg : part.color, + bg : rltk::RGB::named(rltk::BLACK), + lifespan : part.lifetime_ms + }, + targets.clone() + ); + } + + // Line particle spawn + if let Some(part) = ecs.read_storage::().get(entity) { + if let Some(start_pos) = targeting::find_item_position(ecs, entity, creator) { + match targets { + Targets::Tile{tile_idx} => spawn_line_particles(ecs, start_pos, *tile_idx, part), + Targets::Tiles{tiles} => tiles.iter().for_each(|tile_idx| spawn_line_particles(ecs, start_pos, *tile_idx, part)), + Targets::Single{ target } => { + if let Some(end_pos) = entity_position(ecs, *target) { + spawn_line_particles(ecs, start_pos, end_pos, part); + } + } + Targets::TargetList{ targets } => { + targets.iter().for_each(|target| { + if let Some(end_pos) = entity_position(ecs, *target) { + spawn_line_particles(ecs, start_pos, end_pos, part); + } + }); + } + } + } + } + + // Providing food + if ecs.read_storage::().get(entity).is_some() { + add_effect(creator, EffectType::WellFed, targets.clone()); + let names = ecs.read_storage::(); + crate::gamelog::Logger::new() + .append("You eat the") + .item_name(&names.get(entity).unwrap().name) + .log(); + did_something = true; + } + + // Magic mapper + if ecs.read_storage::().get(entity).is_some() { + let mut runstate = ecs.fetch_mut::(); + crate::gamelog::Logger::new().append("The map is revealed to you!").log(); + *runstate = RunState::MagicMapReveal{ row : 0}; + did_something = true; + } + + // Remove Curse + if ecs.read_storage::().get(entity).is_some() { + let mut runstate = ecs.fetch_mut::(); + *runstate = RunState::ShowRemoveCurse; + did_something = true; + } + + // Identify Item + if ecs.read_storage::().get(entity).is_some() { + let mut runstate = ecs.fetch_mut::(); + *runstate = RunState::ShowIdentify; + did_something = true; + } + + // Town Portal + if ecs.read_storage::().get(entity).is_some() { + let map = ecs.fetch::(); + if map.depth == 1 { + crate::gamelog::Logger::new().append("You are already in town, so the scroll does nothing.").log(); + } else { + crate::gamelog::Logger::new().append("You are telported back to town!").log(); + let mut runstate = ecs.fetch_mut::(); + *runstate = RunState::TownPortal; + did_something = true; + } + } + + // Healing + if let Some(heal) = ecs.read_storage::().get(entity) { + add_effect(creator, EffectType::Healing{amount: heal.heal_amount}, targets.clone()); + did_something = true; + } + + // Mana + if let Some(mana) = ecs.read_storage::().get(entity) { + add_effect(creator, EffectType::Mana{amount: mana.mana_amount}, targets.clone()); + did_something = true; + } + + // Damage + if let Some(damage) = ecs.read_storage::().get(entity) { + add_effect(creator, EffectType::Damage{ amount: damage.damage }, targets.clone()); + did_something = true; + } + + // Confusion + if let Some(_confusion) = ecs.read_storage::().get(entity) { + if let Some(duration) = ecs.read_storage::().get(entity) { + add_effect(creator, EffectType::Confusion{ turns : duration.turns }, targets.clone()); + did_something = true; + } + } + + // Teleport + if let Some(teleport) = ecs.read_storage::().get(entity) { + add_effect( + creator, + EffectType::TeleportTo{ + x : teleport.x, + y : teleport.y, + depth: teleport.depth, + player_only: teleport.player_only + }, + targets.clone() + ); + did_something = true; + } + + // Attribute Modifiers + if let Some(attr) = ecs.read_storage::().get(entity) { + add_effect( + creator, + EffectType::AttributeEffect{ + bonus : attr.clone(), + duration : 10, + name : ecs.read_storage::().get(entity).unwrap().name.clone() + }, + targets.clone() + ); + did_something = true; + } + + // Learn spells + if let Some(spell) = ecs.read_storage::().get(entity) { + if let Some(known) = ecs.write_storage::().get_mut(creator.unwrap()) { + if let Some(spell_entity) = crate::raws::find_spell_entity(ecs, &spell.spell) { + if let Some(spell_info) = ecs.read_storage::().get(spell_entity) { + let mut already_known = false; + known.spells.iter().for_each(|s| if s.display_name == spell.spell { already_known = true }); + if !already_known { + known.spells.push(KnownSpell{ display_name: spell.spell.clone(), mana_cost : spell_info.mana_cost }); + } + } + } + } + did_something = true; + } + + // Slow + if let Some(slow) = ecs.read_storage::().get(entity) { + add_effect(creator, EffectType::Slow{ initiative_penalty : slow.initiative_penalty }, targets.clone()); + did_something = true; + } + + // Damage Over Time + if let Some(damage) = ecs.read_storage::().get(entity) { + add_effect(creator, EffectType::DamageOverTime{ damage : damage.damage }, targets.clone()); + did_something = true; + } + + did_something +} + +fn spawn_line_particles(ecs:&World, start: i32, end: i32, part: &SpawnParticleLine) { + let map = ecs.fetch::(); + let start_pt = rltk::Point::new(start % map.width, end / map.width); + let end_pt = rltk::Point::new(end % map.width, end / map.width); + let line = rltk::line2d(rltk::LineAlg::Bresenham, start_pt, end_pt); + for pt in line.iter() { + add_effect( + None, + EffectType::Particle{ + glyph : part.glyph, + fg : part.color, + bg : rltk::RGB::named(rltk::BLACK), + lifespan : part.lifetime_ms + }, + Targets::Tile{ tile_idx : map.xy_idx(pt.x, pt.y) as i32} + ); + } +} diff --git a/chapter-75-darkplaza/src/gamelog/builder.rs b/chapter-75-darkplaza/src/gamelog/builder.rs new file mode 100644 index 00000000..f0691aca --- /dev/null +++ b/chapter-75-darkplaza/src/gamelog/builder.rs @@ -0,0 +1,65 @@ +use rltk::prelude::*; +use super::{LogFragment, append_entry}; + +pub struct Logger { + current_color : RGB, + fragments : Vec +} + +impl Logger { + pub fn new() -> Self { + Logger{ + current_color : RGB::named(rltk::WHITE), + fragments : Vec::new() + } + } + + pub fn color(mut self, color: (u8, u8, u8)) -> Self { + self.current_color = RGB::named(color); + self + } + + pub fn append(mut self, text : T) -> Self { + self.fragments.push( + LogFragment{ + color : self.current_color, + text : text.to_string() + } + ); + self + } + + pub fn log(self) { + append_entry(self.fragments) + } + + pub fn npc_name(mut self, text : T) -> Self { + self.fragments.push( + LogFragment{ + color : RGB::named(rltk::YELLOW), + text : text.to_string() + } + ); + self + } + + pub fn item_name(mut self, text : T) -> Self { + self.fragments.push( + LogFragment{ + color : RGB::named(rltk::CYAN), + text : text.to_string() + } + ); + self + } + + pub fn damage(mut self, damage: i32) -> Self { + self.fragments.push( + LogFragment{ + color : RGB::named(rltk::RED), + text : format!("{}", damage).to_string() + } + ); + self + } +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/gamelog/events.rs b/chapter-75-darkplaza/src/gamelog/events.rs new file mode 100644 index 00000000..b20efdff --- /dev/null +++ b/chapter-75-darkplaza/src/gamelog/events.rs @@ -0,0 +1,43 @@ +use std::collections::HashMap; +use std::sync::Mutex; + +lazy_static! { + static ref EVENTS : Mutex> = Mutex::new(HashMap::new()); +} + +pub fn clear_events() { + EVENTS.lock().unwrap().clear(); +} + +pub fn record_event(event: T, n : i32) { + let event_name = event.to_string(); + let mut events_lock = EVENTS.lock(); + let events = events_lock.as_mut().unwrap(); + if let Some(e) = events.get_mut(&event_name) { + *e += n; + } else { + events.insert(event_name, n); + } +} + +pub fn get_event_count(event: T) -> i32 { + let event_name = event.to_string(); + let events_lock = EVENTS.lock(); + let events = events_lock.unwrap(); + if let Some(e) = events.get(&event_name) { + *e + } else { + 0 + } +} + +pub fn clone_events() -> HashMap { + EVENTS.lock().unwrap().clone() +} + +pub fn load_events(events : HashMap) { + EVENTS.lock().unwrap().clear(); + events.iter().for_each(|(k,v)| { + EVENTS.lock().unwrap().insert(k.to_string(), *v); + }); +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/gamelog/logstore.rs b/chapter-75-darkplaza/src/gamelog/logstore.rs new file mode 100644 index 00000000..e7b62b0e --- /dev/null +++ b/chapter-75-darkplaza/src/gamelog/logstore.rs @@ -0,0 +1,38 @@ +use std::sync::Mutex; +use super::LogFragment; +use rltk::prelude::*; + +lazy_static! { + static ref LOG : Mutex>> = Mutex::new(Vec::new()); +} + +pub fn append_entry(fragments : Vec) { + LOG.lock().unwrap().push(fragments); +} + +pub fn clear_log() { + LOG.lock().unwrap().clear(); +} + +pub fn print_log(console: &mut Box, pos: Point) { + let mut y = pos.y; + let mut x = pos.x; + LOG.lock().unwrap().iter().rev().take(6).for_each(|log| { + log.iter().for_each(|frag| { + console.print_color(x, y, frag.color.to_rgba(1.0), RGBA::named(rltk::BLACK), &frag.text); + x += frag.text.len() as i32; + x += 1; + }); + y += 1; + x = pos.x; + }); +} + +pub fn clone_log() -> Vec> { + LOG.lock().unwrap().clone() +} + +pub fn restore_log(log : &mut Vec>) { + LOG.lock().unwrap().clear(); + LOG.lock().unwrap().append(log); +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/gamelog/mod.rs b/chapter-75-darkplaza/src/gamelog/mod.rs new file mode 100644 index 00000000..ed01bcd7 --- /dev/null +++ b/chapter-75-darkplaza/src/gamelog/mod.rs @@ -0,0 +1,15 @@ +use rltk::prelude::*; +mod builder; +pub use builder::*; +mod logstore; +use logstore::*; +pub use logstore::{clear_log, clone_log, restore_log, print_log}; +use serde::{Serialize, Deserialize}; +mod events; +pub use events::*; + +#[derive(Serialize, Deserialize, Clone)] +pub struct LogFragment { + pub color : RGB, + pub text : String +} diff --git a/chapter-75-darkplaza/src/gamesystem.rs b/chapter-75-darkplaza/src/gamesystem.rs new file mode 100644 index 00000000..920f1a44 --- /dev/null +++ b/chapter-75-darkplaza/src/gamesystem.rs @@ -0,0 +1,37 @@ +use super::{Skill, Skills}; + +pub fn attr_bonus(value: i32) -> i32 { + (value-10)/2 // See: https://roll20.net/compendium/dnd5e/Ability%20Scores#content +} + +pub fn player_hp_per_level(fitness: i32) -> i32 { + 15 + attr_bonus(fitness) +} + +pub fn player_hp_at_level(fitness:i32, level:i32) -> i32 { + 15 + (player_hp_per_level(fitness) * level) +} + +pub fn npc_hp(fitness: i32, level: i32) -> i32 { + let mut total = 1; + for _i in 0..level { + total += i32::max(1, 8 + attr_bonus(fitness)); + } + total +} + +pub fn mana_per_level(intelligence: i32) -> i32 { + i32::max(1, 4 + attr_bonus(intelligence)) +} + +pub fn mana_at_level(intelligence: i32, level: i32) -> i32 { + mana_per_level(intelligence) * level +} + +pub fn skill_bonus(skill : Skill, skills: &Skills) -> i32 { + if skills.skills.contains_key(&skill) { + skills.skills[&skill] + } else { + -4 + } +} diff --git a/chapter-75-darkplaza/src/gui/cheat_menu.rs b/chapter-75-darkplaza/src/gui/cheat_menu.rs new file mode 100644 index 00000000..5a2d17ac --- /dev/null +++ b/chapter-75-darkplaza/src/gui/cheat_menu.rs @@ -0,0 +1,42 @@ +use rltk::prelude::*; +use crate::State; +use super::{menu_option, menu_box}; + +#[derive(PartialEq, Copy, Clone)] +pub enum CheatMenuResult { NoResponse, Cancel, TeleportToExit, Heal, Reveal, GodMode } + +pub fn show_cheat_mode(_gs : &mut State, ctx : &mut Rltk) -> CheatMenuResult { + let mut draw_batch = DrawBatch::new(); + let count = 4; + let mut y = (25 - (count / 2)) as i32; + menu_box(&mut draw_batch, 15, y, (count+3) as i32, "Cheating!"); + draw_batch.print_color( + Point::new(18, y+count as i32+1), + "ESCAPE to cancel", + ColorPair::new(RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK)) + ); + + menu_option(&mut draw_batch, 17, y, rltk::to_cp437('T'), "Teleport to next level"); + y += 1; + menu_option(&mut draw_batch, 17, y, rltk::to_cp437('H'), "Heal all wounds"); + y += 1; + menu_option(&mut draw_batch, 17, y, rltk::to_cp437('R'), "Reveal the map"); + y += 1; + menu_option(&mut draw_batch, 17, y, rltk::to_cp437('G'), "God Mode (No Death)"); + + draw_batch.submit(6000); + + match ctx.key { + None => CheatMenuResult::NoResponse, + Some(key) => { + match key { + VirtualKeyCode::T => CheatMenuResult::TeleportToExit, + VirtualKeyCode::H => CheatMenuResult::Heal, + VirtualKeyCode::R => CheatMenuResult::Reveal, + VirtualKeyCode::G => CheatMenuResult::GodMode, + VirtualKeyCode::Escape => CheatMenuResult::Cancel, + _ => CheatMenuResult::NoResponse + } + } + } +} diff --git a/chapter-75-darkplaza/src/gui/drop_item_menu.rs b/chapter-75-darkplaza/src/gui/drop_item_menu.rs new file mode 100644 index 00000000..912c894b --- /dev/null +++ b/chapter-75-darkplaza/src/gui/drop_item_menu.rs @@ -0,0 +1,29 @@ +use rltk::prelude::*; +use specs::prelude::*; +use crate::{State, InBackpack}; +use super::{get_item_display_name, ItemMenuResult, item_result_menu}; + +pub fn drop_item_menu(gs : &mut State, ctx : &mut Rltk) -> (ItemMenuResult, Option) { + let mut draw_batch = DrawBatch::new(); + + let player_entity = gs.ecs.fetch::(); + let backpack = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + + let mut items : Vec<(Entity, String)> = Vec::new(); + (&entities, &backpack).join() + .filter(|item| item.1.owner == *player_entity ) + .for_each(|item| { + items.push((item.0, get_item_display_name(&gs.ecs, item.0))) + }); + + let result = item_result_menu( + &mut draw_batch, + "Drop which item?", + items.len(), + &items, + ctx.key + ); + draw_batch.submit(6000); + result +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/gui/game_over_menu.rs b/chapter-75-darkplaza/src/gui/game_over_menu.rs new file mode 100644 index 00000000..deb34d72 --- /dev/null +++ b/chapter-75-darkplaza/src/gui/game_over_menu.rs @@ -0,0 +1,51 @@ +use rltk::prelude::*; + +#[derive(PartialEq, Copy, Clone)] +pub enum GameOverResult { NoSelection, QuitToMenu } + +pub fn game_over(ctx : &mut Rltk) -> GameOverResult { + let mut draw_batch = DrawBatch::new(); + draw_batch.print_color_centered( + 15, + "Your journey has ended!", + ColorPair::new(RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK)) + ); + draw_batch.print_color_centered( + 17, + "One day, we'll tell you all about how you did.", + ColorPair::new(RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)) + ); + draw_batch.print_color_centered( + 18, + "That day, sadly, is not in this chapter..", + ColorPair::new(RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)) + ); + + draw_batch.print_color_centered( + 19, + &format!("You lived for {} turns.", crate::gamelog::get_event_count("Turn")), + ColorPair::new(RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)) + ); + draw_batch.print_color_centered( + 20, + &format!("You suffered {} points of damage.", crate::gamelog::get_event_count("Damage Taken")), + ColorPair::new(RGB::named(rltk::RED), RGB::named(rltk::BLACK)) + ); + draw_batch.print_color_centered( + 21, + &format!("You inflicted {} points of damage.", crate::gamelog::get_event_count("Damage Inflicted")), + ColorPair::new(RGB::named(rltk::RED), RGB::named(rltk::BLACK))); + + draw_batch.print_color_centered( + 23, + "Press any key to return to the menu.", + ColorPair::new(RGB::named(rltk::MAGENTA), RGB::named(rltk::BLACK)) + ); + + draw_batch.submit(6000); + + match ctx.key { + None => GameOverResult::NoSelection, + Some(_) => GameOverResult::QuitToMenu + } +} diff --git a/chapter-75-darkplaza/src/gui/hud.rs b/chapter-75-darkplaza/src/gui/hud.rs new file mode 100644 index 00000000..8615f6e3 --- /dev/null +++ b/chapter-75-darkplaza/src/gui/hud.rs @@ -0,0 +1,278 @@ +use rltk::prelude::*; +use specs::prelude::*; +use crate::{Pools, Map, Name, InBackpack, + Equipped, HungerClock, HungerState, Attributes, Attribute, Consumable, + StatusEffect, Duration, KnownSpells, Weapon, gamelog }; +use super::{draw_tooltips, get_item_display_name, get_item_color}; + +fn draw_attribute(name : &str, attribute : &Attribute, y : i32, draw_batch: &mut DrawBatch) { + let black = RGB::named(rltk::BLACK); + let attr_gray : RGB = RGB::from_hex("#CCCCCC").expect("Oops"); + draw_batch.print_color(Point::new(50, y), name, ColorPair::new(attr_gray, black)); + let color : RGB = + if attribute.modifiers < 0 { RGB::from_f32(1.0, 0.0, 0.0) } + else if attribute.modifiers == 0 { RGB::named(rltk::WHITE) } + else { RGB::from_f32(0.0, 1.0, 0.0) }; + draw_batch.print_color(Point::new(67, y), &format!("{}", attribute.base + attribute.modifiers), ColorPair::new(color, black)); + draw_batch.print_color(Point::new(73, y), &format!("{}", attribute.bonus), ColorPair::new(color, black)); + if attribute.bonus > 0 { + draw_batch.set(Point::new(72, y), ColorPair::new(color, black), to_cp437('+')); + } +} + +fn box_framework(draw_batch : &mut DrawBatch) { + let box_gray : RGB = RGB::from_hex("#999999").expect("Oops"); + let black = RGB::named(rltk::BLACK); + + draw_batch.draw_hollow_box(Rect::with_size(0, 0, 79, 59), ColorPair::new(box_gray, black)); // Overall box + draw_batch.draw_hollow_box(Rect::with_size(0, 0, 49, 45), ColorPair::new(box_gray, black)); // Map box + draw_batch.draw_hollow_box(Rect::with_size(0, 45, 79, 14), ColorPair::new(box_gray, black)); // Log box + draw_batch.draw_hollow_box(Rect::with_size(49, 0, 30, 8), ColorPair::new(box_gray, black)); // Top-right panel + + // Draw box connectors + draw_batch.set(Point::new(0, 45), ColorPair::new(box_gray, black), to_cp437('├')); + draw_batch.set(Point::new(49, 8), ColorPair::new(box_gray, black), to_cp437('├')); + draw_batch.set(Point::new(49, 0), ColorPair::new(box_gray, black), to_cp437('┬')); + draw_batch.set(Point::new(49, 45), ColorPair::new(box_gray, black), to_cp437('┴')); + draw_batch.set(Point::new(79, 8), ColorPair::new(box_gray, black), to_cp437('┤')); + draw_batch.set(Point::new(79, 45), ColorPair::new(box_gray, black), to_cp437('┤')); +} + +pub fn map_label(ecs: &World, draw_batch: &mut DrawBatch) { + let box_gray : RGB = RGB::from_hex("#999999").expect("Oops"); + let black = RGB::named(rltk::BLACK); + let white = RGB::named(rltk::WHITE); + + let map = ecs.fetch::(); + let name_length = map.name.len() + 2; + let x_pos = (22 - (name_length / 2)) as i32; + draw_batch.set(Point::new(x_pos, 0), ColorPair::new(box_gray, black), to_cp437('┤')); + draw_batch.set(Point::new(x_pos + name_length as i32 - 1, 0), ColorPair::new(box_gray, black), to_cp437('├')); + draw_batch.print_color(Point::new(x_pos+1, 0), &map.name, ColorPair::new(white, black)); +} + +fn draw_stats(ecs: &World, draw_batch: &mut DrawBatch, player_entity: &Entity) { + let black = RGB::named(rltk::BLACK); + let white = RGB::named(rltk::WHITE); + let pools = ecs.read_storage::(); + let player_pools = pools.get(*player_entity).unwrap(); + let health = format!("Health: {}/{}", player_pools.hit_points.current, player_pools.hit_points.max); + let mana = format!("Mana: {}/{}", player_pools.mana.current, player_pools.mana.max); + let xp = format!("Level: {}", player_pools.level); + draw_batch.print_color(Point::new(50, 1), &health, ColorPair::new(white, black)); + draw_batch.print_color(Point::new(50, 2), &mana, ColorPair::new(white, black)); + draw_batch.print_color(Point::new(50, 3), &xp, ColorPair::new(white, black)); + draw_batch.bar_horizontal( + Point::new(64, 1), + 14, + player_pools.hit_points.current, + player_pools.hit_points.max, + ColorPair::new(RGB::named(rltk::RED), RGB::named(rltk::BLACK)) + ); + draw_batch.bar_horizontal( + Point::new(64, 2), + 14, + player_pools.mana.current, + player_pools.mana.max, + ColorPair::new(RGB::named(rltk::BLUE), RGB::named(rltk::BLACK)) + ); + let xp_level_start = (player_pools.level-1) * 1000; + draw_batch.bar_horizontal( + Point::new(64, 3), + 14, + player_pools.xp - xp_level_start, + 1000, + ColorPair::new(RGB::named(rltk::GOLD), RGB::named(rltk::BLACK)) + ); +} + +fn draw_attributes(ecs: &World, draw_batch: &mut DrawBatch, player_entity: &Entity) { + let attributes = ecs.read_storage::(); + let attr = attributes.get(*player_entity).unwrap(); + draw_attribute("Might:", &attr.might, 4, draw_batch); + draw_attribute("Quickness:", &attr.quickness, 5, draw_batch); + draw_attribute("Fitness:", &attr.fitness, 6, draw_batch); + draw_attribute("Intelligence:", &attr.intelligence, 7, draw_batch); +} + +fn initiative_weight(ecs: &World, draw_batch: &mut DrawBatch, player_entity: &Entity) { + let attributes = ecs.read_storage::(); + let attr = attributes.get(*player_entity).unwrap(); + let black = RGB::named(rltk::BLACK); + let white = RGB::named(rltk::WHITE); + let pools = ecs.read_storage::(); + let player_pools = pools.get(*player_entity).unwrap(); + draw_batch.print_color( + Point::new(50, 9), + &format!("{:.0} lbs ({} lbs max)", + player_pools.total_weight, + (attr.might.base + attr.might.modifiers) * 15 + ), + ColorPair::new(white, black) + ); + draw_batch.print_color( + Point::new(50,10), + &format!("Initiative Penalty: {:.0}", player_pools.total_initiative_penalty), + ColorPair::new(white, black) + ); + draw_batch.print_color( + Point::new(50,11), + &format!("Gold: {:.1}", player_pools.gold), + ColorPair::new(RGB::named(rltk::GOLD), black) + ); +} + +fn equipped(ecs: &World, draw_batch: &mut DrawBatch, player_entity: &Entity) -> i32 { + let black = RGB::named(rltk::BLACK); + let yellow = RGB::named(rltk::YELLOW); + let mut y = 13; + let entities = ecs.entities(); + let equipped = ecs.read_storage::(); + let weapon = ecs.read_storage::(); + for (entity, equipped_by) in (&entities, &equipped).join() { + if equipped_by.owner == *player_entity { + let name = get_item_display_name(ecs, entity); + draw_batch.print_color( + Point::new(50, y), + &name, + ColorPair::new(get_item_color(ecs, entity), black)); + y += 1; + + if let Some(weapon) = weapon.get(entity) { + let mut weapon_info = if weapon.damage_bonus < 0 { + format!("┤ {} ({}d{}{})", &name, weapon.damage_n_dice, weapon.damage_die_type, weapon.damage_bonus) + } else if weapon.damage_bonus == 0 { + format!("┤ {} ({}d{})", &name, weapon.damage_n_dice, weapon.damage_die_type) + } else { + format!("┤ {} ({}d{}+{})", &name, weapon.damage_n_dice, weapon.damage_die_type, weapon.damage_bonus) + }; + + if let Some(range) = weapon.range { + weapon_info += &format!(" (range: {}, F to fire, V cycle targets)", range); + } + weapon_info += " ├"; + draw_batch.print_color( + Point::new(3, 45), + &weapon_info, + ColorPair::new(yellow, black)); + } + } + } + y +} + +fn consumables(ecs: &World, draw_batch: &mut DrawBatch, player_entity: &Entity, mut y : i32) -> i32 { + y += 1; + let black = RGB::named(rltk::BLACK); + let yellow = RGB::named(rltk::YELLOW); + let entities = ecs.entities(); + let consumables = ecs.read_storage::(); + let backpack = ecs.read_storage::(); + let mut index = 1; + for (entity, carried_by, _consumable) in (&entities, &backpack, &consumables).join() { + if carried_by.owner == *player_entity && index < 10 { + draw_batch.print_color( + Point::new(50, y), + &format!("↑{}", index), + ColorPair::new(yellow, black) + ); + draw_batch.print_color( + Point::new(53, y), + &get_item_display_name(ecs, entity), + ColorPair::new(get_item_color(ecs, entity), black) + ); + y += 1; + index += 1; + } + } + y +} + +fn spells(ecs: &World, draw_batch: &mut DrawBatch, player_entity: &Entity, mut y : i32) -> i32 { + y += 1; + let black = RGB::named(rltk::BLACK); + let blue = RGB::named(rltk::CYAN); + let known_spells_storage = ecs.read_storage::(); + let known_spells = &known_spells_storage.get(*player_entity).unwrap().spells; + let mut index = 1; + for spell in known_spells.iter() { + draw_batch.print_color( + Point::new(50, y), + &format!("^{}", index), + ColorPair::new(blue, black) + ); + draw_batch.print_color( + Point::new(53, y), + &format!("{} ({})", &spell.display_name, spell.mana_cost), + ColorPair::new(blue, black) + ); + index += 1; + y += 1; + } + y +} + +fn status(ecs: &World, draw_batch: &mut DrawBatch, player_entity: &Entity) { + let mut y = 44; + let hunger = ecs.read_storage::(); + let hc = hunger.get(*player_entity).unwrap(); + match hc.state { + HungerState::WellFed => { + draw_batch.print_color( + Point::new(50, y), + "Well Fed", + ColorPair::new(RGB::named(rltk::GREEN), RGB::named(rltk::BLACK)) + ); + y -= 1; + } + HungerState::Normal => {} + HungerState::Hungry => { + draw_batch.print_color( + Point::new(50, y), + "Hungry", + ColorPair::new(RGB::named(rltk::ORANGE), RGB::named(rltk::BLACK)) + ); + y -= 1; + } + HungerState::Starving => { + draw_batch.print_color( + Point::new(50, y), + "Starving", + ColorPair::new(RGB::named(rltk::RED), RGB::named(rltk::BLACK)) + ); + y -= 1; + } + } + let statuses = ecs.read_storage::(); + let durations = ecs.read_storage::(); + let names = ecs.read_storage::(); + for (status, duration, name) in (&statuses, &durations, &names).join() { + if status.target == *player_entity { + draw_batch.print_color( + Point::new(50, y), + &format!("{} ({})", name.name, duration.turns), + ColorPair::new(RGB::named(rltk::RED), RGB::named(rltk::BLACK)), + ); + y -= 1; + } + } +} + +pub fn draw_ui(ecs: &World, ctx : &mut Rltk) { + let mut draw_batch = DrawBatch::new(); + let player_entity = ecs.fetch::(); + + box_framework(&mut draw_batch); + map_label(ecs, &mut draw_batch); + draw_stats(ecs, &mut draw_batch, &player_entity); + draw_attributes(ecs, &mut draw_batch, &player_entity); + initiative_weight(ecs, &mut draw_batch, &player_entity); + let mut y = equipped(ecs, &mut draw_batch, &player_entity); + y += consumables(ecs, &mut draw_batch, &player_entity, y); + spells(ecs, &mut draw_batch, &player_entity, y); + status(ecs, &mut draw_batch, &player_entity); + gamelog::print_log(&mut rltk::BACKEND_INTERNAL.lock().consoles[1].console, Point::new(1, 23)); + draw_tooltips(ecs, ctx); + + draw_batch.submit(5000); +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/gui/identify_menu.rs b/chapter-75-darkplaza/src/gui/identify_menu.rs new file mode 100644 index 00000000..05592818 --- /dev/null +++ b/chapter-75-darkplaza/src/gui/identify_menu.rs @@ -0,0 +1,56 @@ +use rltk::prelude::*; +use specs::prelude::*; +use crate::{Name, State, InBackpack, Equipped, MasterDungeonMap, Item, ObfuscatedName }; +use super::{get_item_display_name, item_result_menu, ItemMenuResult}; + +pub fn identify_menu(gs : &mut State, ctx : &mut Rltk) -> (ItemMenuResult, Option) { + let mut draw_batch = DrawBatch::new(); + + let player_entity = gs.ecs.fetch::(); + let equipped = gs.ecs.read_storage::(); + let backpack = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + let item_components = gs.ecs.read_storage::(); + let names = gs.ecs.read_storage::(); + let dm = gs.ecs.fetch::(); + let obfuscated = gs.ecs.read_storage::(); + + let mut items : Vec<(Entity, String)> = Vec::new(); + (&entities, &item_components).join() + .filter(|(item_entity,_item)| { + let mut keep = false; + if let Some(bp) = backpack.get(*item_entity) { + if bp.owner == *player_entity { + if let Some(name) = names.get(*item_entity) { + if obfuscated.get(*item_entity).is_some() && !dm.identified_items.contains(&name.name) { + keep = true; + } + } + } + } + // It's equipped, so we know it's cursed + if let Some(equip) = equipped.get(*item_entity) { + if equip.owner == *player_entity { + if let Some(name) = names.get(*item_entity) { + if obfuscated.get(*item_entity).is_some() && !dm.identified_items.contains(&name.name) { + keep = true; + } + } + } + } + keep + }) + .for_each(|item| { + items.push((item.0, get_item_display_name(&gs.ecs, item.0))) + }); + + let result = item_result_menu( + &mut draw_batch, + "Inventory", + items.len(), + &items, + ctx.key + ); + draw_batch.submit(6000); + result +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/gui/inventory_menu.rs b/chapter-75-darkplaza/src/gui/inventory_menu.rs new file mode 100644 index 00000000..58d824f2 --- /dev/null +++ b/chapter-75-darkplaza/src/gui/inventory_menu.rs @@ -0,0 +1,32 @@ +use rltk::prelude::*; +use specs::prelude::*; +use crate::{State, InBackpack }; +use super::{get_item_display_name, item_result_menu}; + +#[derive(PartialEq, Copy, Clone)] +pub enum ItemMenuResult { Cancel, NoResponse, Selected } + +pub fn show_inventory(gs : &mut State, ctx : &mut Rltk) -> (ItemMenuResult, Option) { + let player_entity = gs.ecs.fetch::(); + let backpack = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + + let mut draw_batch = DrawBatch::new(); + + let mut items : Vec<(Entity, String)> = Vec::new(); + (&entities, &backpack).join() + .filter(|item| item.1.owner == *player_entity ) + .for_each(|item| { + items.push((item.0, get_item_display_name(&gs.ecs, item.0))) + }); + + let result = item_result_menu( + &mut draw_batch, + "Inventory", + items.len(), + &items, + ctx.key + ); + draw_batch.submit(6000); + result +} diff --git a/chapter-75-darkplaza/src/gui/item_render.rs b/chapter-75-darkplaza/src/gui/item_render.rs new file mode 100644 index 00000000..44dd5554 --- /dev/null +++ b/chapter-75-darkplaza/src/gui/item_render.rs @@ -0,0 +1,49 @@ +use rltk::prelude::*; +use specs::prelude::*; +use crate::{Name, Consumable, MagicItem, MagicItemClass, ObfuscatedName, CursedItem }; + +pub fn get_item_color(ecs : &World, item : Entity) -> RGB { + let dm = ecs.fetch::(); + if let Some(name) = ecs.read_storage::().get(item) { + if ecs.read_storage::().get(item).is_some() && dm.identified_items.contains(&name.name) { + return RGB::from_f32(1.0, 0.0, 0.0); + } + } + + if let Some(magic) = ecs.read_storage::().get(item) { + match magic.class { + MagicItemClass::Common => return RGB::from_f32(0.5, 1.0, 0.5), + MagicItemClass::Rare => return RGB::from_f32(0.0, 1.0, 1.0), + MagicItemClass::Legendary => return RGB::from_f32(0.71, 0.15, 0.93) + } + } + RGB::from_f32(1.0, 1.0, 1.0) +} + +pub fn get_item_display_name(ecs: &World, item : Entity) -> String { + if let Some(name) = ecs.read_storage::().get(item) { + if ecs.read_storage::().get(item).is_some() { + let dm = ecs.fetch::(); + if dm.identified_items.contains(&name.name) { + if let Some(c) = ecs.read_storage::().get(item) { + if c.max_charges > 1 { + format!("{} ({})", name.name.clone(), c.charges).to_string() + } else { + name.name.clone() + } + } else { + name.name.clone() + } + } else if let Some(obfuscated) = ecs.read_storage::().get(item) { + obfuscated.name.clone() + } else { + "Unidentified magic item".to_string() + } + } else { + name.name.clone() + } + + } else { + "Nameless item (bug)".to_string() + } +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/gui/main_menu.rs b/chapter-75-darkplaza/src/gui/main_menu.rs new file mode 100644 index 00000000..e03b1675 --- /dev/null +++ b/chapter-75-darkplaza/src/gui/main_menu.rs @@ -0,0 +1,86 @@ +use rltk::prelude::*; +use crate::{State, RunState, rex_assets::RexAssets }; + +#[derive(PartialEq, Copy, Clone)] +pub enum MainMenuSelection { NewGame, LoadGame, Quit } + +#[derive(PartialEq, Copy, Clone)] +pub enum MainMenuResult { NoSelection{ selected : MainMenuSelection }, Selected{ selected: MainMenuSelection } } + +pub fn main_menu(gs : &mut State, ctx : &mut Rltk) -> MainMenuResult { + let mut draw_batch = DrawBatch::new(); + let save_exists = crate::saveload_system::does_save_exist(); + let runstate = gs.ecs.fetch::(); + let assets = gs.ecs.fetch::(); + ctx.render_xp_sprite(&assets.menu, 0, 0); + + draw_batch.draw_double_box(Rect::with_size(24, 18, 31, 10), ColorPair::new(RGB::named(rltk::WHEAT), RGB::named(rltk::BLACK))); + + draw_batch.print_color_centered(20, "Rust Roguelike Tutorial", ColorPair::new(RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK))); + draw_batch.print_color_centered(21, "by Herbert Wolverson", ColorPair::new(RGB::named(rltk::CYAN), RGB::named(rltk::BLACK))); + draw_batch.print_color_centered(22, "Use Up/Down Arrows and Enter", ColorPair::new(RGB::named(rltk::GRAY), RGB::named(rltk::BLACK))); + + let mut y = 24; + if let RunState::MainMenu{ menu_selection : selection } = *runstate { + if selection == MainMenuSelection::NewGame { + draw_batch.print_color_centered(y, "Begin New Game", ColorPair::new(RGB::named(rltk::MAGENTA), RGB::named(rltk::BLACK))); + } else { + draw_batch.print_color_centered(y, "Begin New Game", ColorPair::new(RGB::named(rltk::WHITE), RGB::named(rltk::BLACK))); + } + y += 1; + + if save_exists { + if selection == MainMenuSelection::LoadGame { + draw_batch.print_color_centered(y, "Load Game", ColorPair::new(RGB::named(rltk::MAGENTA), RGB::named(rltk::BLACK))); + } else { + draw_batch.print_color_centered(y, "Load Game", ColorPair::new(RGB::named(rltk::WHITE), RGB::named(rltk::BLACK))); + } + y += 1; + } + + if selection == MainMenuSelection::Quit { + draw_batch.print_color_centered(y, "Quit", ColorPair::new(RGB::named(rltk::MAGENTA), RGB::named(rltk::BLACK))); + } else { + draw_batch.print_color_centered(y, "Quit", ColorPair::new(RGB::named(rltk::WHITE), RGB::named(rltk::BLACK))); + } + + draw_batch.submit(6000); + + match ctx.key { + None => return MainMenuResult::NoSelection{ selected: selection }, + Some(key) => { + match key { + VirtualKeyCode::Escape => { return MainMenuResult::NoSelection{ selected: MainMenuSelection::Quit } } + VirtualKeyCode::Up => { + let mut newselection; + match selection { + MainMenuSelection::NewGame => newselection = MainMenuSelection::Quit, + MainMenuSelection::LoadGame => newselection = MainMenuSelection::NewGame, + MainMenuSelection::Quit => newselection = MainMenuSelection::LoadGame + } + if newselection == MainMenuSelection::LoadGame && !save_exists { + newselection = MainMenuSelection::NewGame; + } + return MainMenuResult::NoSelection{ selected: newselection } + } + VirtualKeyCode::Down => { + let mut newselection; + match selection { + MainMenuSelection::NewGame => newselection = MainMenuSelection::LoadGame, + MainMenuSelection::LoadGame => newselection = MainMenuSelection::Quit, + MainMenuSelection::Quit => newselection = MainMenuSelection::NewGame + } + if newselection == MainMenuSelection::LoadGame && !save_exists { + newselection = MainMenuSelection::Quit; + } + return MainMenuResult::NoSelection{ selected: newselection } + } + VirtualKeyCode::Return => return MainMenuResult::Selected{ selected : selection }, + _ => return MainMenuResult::NoSelection{ selected: selection } + } + } + } + } + + MainMenuResult::NoSelection { selected: MainMenuSelection::NewGame } +} diff --git a/chapter-75-darkplaza/src/gui/menus.rs b/chapter-75-darkplaza/src/gui/menus.rs new file mode 100644 index 00000000..bcb4686a --- /dev/null +++ b/chapter-75-darkplaza/src/gui/menus.rs @@ -0,0 +1,88 @@ +use rltk::prelude::*; +use specs::prelude::*; +use super::ItemMenuResult; + +pub fn menu_box(draw_batch: &mut DrawBatch, x: i32, y: i32, width: i32, title: T) { + draw_batch.draw_box( + Rect::with_size(x, y-2, 31, width), + ColorPair::new(RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)) + ); + draw_batch.print_color( + Point::new(18, y-2), + &title.to_string(), + ColorPair::new(RGB::named(rltk::MAGENTA), RGB::named(rltk::BLACK)) + ); +} + +pub fn menu_option(draw_batch: &mut DrawBatch, x: i32, y: i32, hotkey: rltk::FontCharType, text: T) { + draw_batch.set( + Point::new(x, y), + ColorPair::new(RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)), + rltk::to_cp437('(') + ); + draw_batch.set( + Point::new(x+1, y), + ColorPair::new(RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK)), + hotkey + ); + draw_batch.set( + Point::new(x+2, y), + ColorPair::new(RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)), + rltk::to_cp437(')') + ); + draw_batch.print_color( + Point::new(x+5, y), + &text.to_string(), + ColorPair::new(RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK)) + ); +} + +pub fn item_result_menu( + draw_batch: &mut DrawBatch, + title: S, + count: usize, + items: &[(Entity, String)], + key: Option +) -> (ItemMenuResult, Option) { + + let mut y = (25 - (count / 2)) as i32; + draw_batch.draw_box( + Rect::with_size(15, y-2, 31, (count+3) as i32), + ColorPair::new(RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)) + ); + draw_batch.print_color( + Point::new(18, y-2), + &title.to_string(), + ColorPair::new(RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK)) + ); + draw_batch.print_color( + Point::new(18, y+count as i32+1), + "ESCAPE to cancel", + ColorPair::new(RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK)) + ); + + let mut item_list : Vec = Vec::new(); + let mut j = 0; + for item in items { + menu_option(draw_batch, 17, y, 97+j as rltk::FontCharType, &item.1); + item_list.push(item.0); + y += 1; + j += 1; + } + + match key { + None => (ItemMenuResult::NoResponse, None), + Some(key) => { + match key { + VirtualKeyCode::Escape => { (ItemMenuResult::Cancel, None) } + _ => { + let selection = rltk::letter_to_option(key); + if selection > -1 && selection < count as i32 { + return (ItemMenuResult::Selected, Some(item_list[selection as usize])); + } + (ItemMenuResult::NoResponse, None) + } + } + } + } +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/gui/mod.rs b/chapter-75-darkplaza/src/gui/mod.rs new file mode 100644 index 00000000..6a1cf892 --- /dev/null +++ b/chapter-75-darkplaza/src/gui/mod.rs @@ -0,0 +1,29 @@ +mod item_render; +pub use item_render::*; +mod hud; +pub use hud::*; +mod tooltips; +pub use tooltips::*; +mod inventory_menu; +pub use inventory_menu::*; +mod drop_item_menu; +pub use drop_item_menu::*; +mod remove_item_menu; +pub use remove_item_menu::*; +mod remove_curse_menu; +pub use remove_curse_menu::*; +mod identify_menu; +pub use identify_menu::*; +mod ranged_target; +pub use ranged_target::*; +mod main_menu; +pub use main_menu::*; +mod game_over_menu; +pub use game_over_menu::*; +mod cheat_menu; +pub use cheat_menu::*; +mod vendor_menu; +pub use vendor_menu::*; +mod menus; +pub use menus::*; + diff --git a/chapter-75-darkplaza/src/gui/ranged_target.rs b/chapter-75-darkplaza/src/gui/ranged_target.rs new file mode 100644 index 00000000..3b311963 --- /dev/null +++ b/chapter-75-darkplaza/src/gui/ranged_target.rs @@ -0,0 +1,62 @@ +use rltk::prelude::*; +use specs::prelude::*; +use crate::{State, camera, Viewshed }; +use super::ItemMenuResult; + +pub fn ranged_target(gs : &mut State, ctx : &mut Rltk, range : i32) -> (ItemMenuResult, Option) { + let (min_x, max_x, min_y, max_y) = camera::get_screen_bounds(&gs.ecs, ctx); + let player_entity = gs.ecs.fetch::(); + let player_pos = gs.ecs.fetch::(); + let viewsheds = gs.ecs.read_storage::(); + + let mut draw_batch = DrawBatch::new(); + + draw_batch.print_color( + Point::new(5, 0), + "Select Target:", + ColorPair::new(RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK)) + ); + + // Highlight available target cells + let mut available_cells = Vec::new(); + let visible = viewsheds.get(*player_entity); + if let Some(visible) = visible { + // We have a viewshed + for idx in visible.visible_tiles.iter() { + let distance = rltk::DistanceAlg::Pythagoras.distance2d(*player_pos, *idx); + if distance <= range as f32 { + let screen_x = idx.x - min_x; + let screen_y = idx.y - min_y; + if screen_x > 1 && screen_x < (max_x - min_x)-1 && screen_y > 1 && screen_y < (max_y - min_y)-1 { + draw_batch.set_bg(Point::new(screen_x, screen_y), RGB::named(rltk::BLUE)); + available_cells.push(idx); + } + } + } + } else { + return (ItemMenuResult::Cancel, None); + } + + // Draw mouse cursor + let mouse_pos = ctx.mouse_pos(); + let mut mouse_map_pos = mouse_pos; + mouse_map_pos.0 += min_x - 1; + mouse_map_pos.1 += min_y - 1; + let mut valid_target = false; + for idx in available_cells.iter() { if idx.x == mouse_map_pos.0 && idx.y == mouse_map_pos.1 { valid_target = true; } } + if valid_target { + draw_batch.set_bg(Point::new(mouse_pos.0, mouse_pos.1), RGB::named(rltk::CYAN)); + if ctx.left_click { + return (ItemMenuResult::Selected, Some(Point::new(mouse_map_pos.0, mouse_map_pos.1))); + } + } else { + draw_batch.set_bg(Point::new(mouse_pos.0, mouse_pos.1), RGB::named(rltk::RED)); + if ctx.left_click { + return (ItemMenuResult::Cancel, None); + } + } + + draw_batch.submit(5000); + + (ItemMenuResult::NoResponse, None) +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/gui/remove_curse_menu.rs b/chapter-75-darkplaza/src/gui/remove_curse_menu.rs new file mode 100644 index 00000000..01206d6f --- /dev/null +++ b/chapter-75-darkplaza/src/gui/remove_curse_menu.rs @@ -0,0 +1,52 @@ +use rltk::prelude::*; +use specs::prelude::*; +use crate::{Name, State, InBackpack, Equipped, MasterDungeonMap, CursedItem, Item }; +use super::{get_item_display_name, item_result_menu, ItemMenuResult}; + +pub fn remove_curse_menu(gs : &mut State, ctx : &mut Rltk) -> (ItemMenuResult, Option) { + let player_entity = gs.ecs.fetch::(); + let equipped = gs.ecs.read_storage::(); + let backpack = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + let item_components = gs.ecs.read_storage::(); + let cursed = gs.ecs.read_storage::(); + let names = gs.ecs.read_storage::(); + let dm = gs.ecs.fetch::(); + + let mut draw_batch = DrawBatch::new(); + + let mut items : Vec<(Entity, String)> = Vec::new(); + (&entities, &item_components, &cursed).join() + .filter(|(item_entity,_item,_cursed)| { + let mut keep = false; + if let Some(bp) = backpack.get(*item_entity) { + if bp.owner == *player_entity { + if let Some(name) = names.get(*item_entity) { + if dm.identified_items.contains(&name.name) { + keep = true; + } + } + } + } + // It's equipped, so we know it's cursed + if let Some(equip) = equipped.get(*item_entity) { + if equip.owner == *player_entity { + keep = true; + } + } + keep + }) + .for_each(|item| { + items.push((item.0, get_item_display_name(&gs.ecs, item.0))) + }); + + let result = item_result_menu( + &mut draw_batch, + "Inventory", + items.len(), + &items, + ctx.key + ); + draw_batch.submit(6000); + result +} diff --git a/chapter-75-darkplaza/src/gui/remove_item_menu.rs b/chapter-75-darkplaza/src/gui/remove_item_menu.rs new file mode 100644 index 00000000..3fd5ad00 --- /dev/null +++ b/chapter-75-darkplaza/src/gui/remove_item_menu.rs @@ -0,0 +1,29 @@ +use rltk::prelude::*; +use specs::prelude::*; +use crate::{State, Equipped }; +use super::{get_item_display_name, ItemMenuResult, item_result_menu}; + +pub fn remove_item_menu(gs : &mut State, ctx : &mut Rltk) -> (ItemMenuResult, Option) { + let mut draw_batch = DrawBatch::new(); + + let player_entity = gs.ecs.fetch::(); + let backpack = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + + let mut items : Vec<(Entity, String)> = Vec::new(); + (&entities, &backpack).join() + .filter(|item| item.1.owner == *player_entity ) + .for_each(|item| { + items.push((item.0, get_item_display_name(&gs.ecs, item.0))) + }); + + let result = item_result_menu( + &mut draw_batch, + "Remove which item?", + items.len(), + &items, + ctx.key + ); + draw_batch.submit(6000); + result +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/gui/tooltips.rs b/chapter-75-darkplaza/src/gui/tooltips.rs new file mode 100644 index 00000000..dbc064b0 --- /dev/null +++ b/chapter-75-darkplaza/src/gui/tooltips.rs @@ -0,0 +1,151 @@ +use rltk::prelude::*; +use specs::prelude::*; +use crate::{Pools, Map, Name, Hidden, camera, Attributes, StatusEffect, Duration }; +use super::get_item_display_name; + +struct Tooltip { + lines : Vec +} + +impl Tooltip { + fn new() -> Tooltip { + Tooltip { lines : Vec::new() } + } + + fn add(&mut self, line : S) { + self.lines.push(line.to_string()); + } + + fn width(&self) -> i32 { + let mut max = 0; + for s in self.lines.iter() { + if s.len() > max { + max = s.len(); + } + } + max as i32 + 2i32 + } + + fn height(&self) -> i32 { self.lines.len() as i32 + 2i32 } + + fn render(&self, draw_batch : &mut DrawBatch, x : i32, y : i32) { + let box_gray : RGB = RGB::from_hex("#999999").expect("Oops"); + let light_gray : RGB = RGB::from_hex("#DDDDDD").expect("Oops"); + let white = RGB::named(rltk::WHITE); + let black = RGB::named(rltk::BLACK); + draw_batch.draw_box(Rect::with_size(x, y, self.width()-1, self.height()-1), ColorPair::new(white, box_gray)); + for (i,s) in self.lines.iter().enumerate() { + let col = if i == 0 { white } else { light_gray }; + draw_batch.print_color(Point::new(x+1, y+i as i32+1), &s, ColorPair::new(col, black)); + } + } +} + +pub fn draw_tooltips(ecs: &World, ctx : &mut Rltk) { + let mut draw_batch = DrawBatch::new(); + + let (min_x, _max_x, min_y, _max_y) = camera::get_screen_bounds(ecs, ctx); + let map = ecs.fetch::(); + let hidden = ecs.read_storage::(); + let attributes = ecs.read_storage::(); + let pools = ecs.read_storage::(); + + let mouse_pos = ctx.mouse_pos(); + let mut mouse_map_pos = mouse_pos; + mouse_map_pos.0 += min_x - 1; + mouse_map_pos.1 += min_y - 1; + if mouse_pos.0 < 1 || mouse_pos.0 > 49 || mouse_pos.1 < 1 || mouse_pos.1 > 40 { + return; + } + if mouse_map_pos.0 >= map.width-1 || mouse_map_pos.1 >= map.height-1 || mouse_map_pos.0 < 1 || mouse_map_pos.1 < 1 + { + return; + } + if !map.in_bounds(rltk::Point::new(mouse_map_pos.0, mouse_map_pos.1)) { return; } + let mouse_idx = map.xy_idx(mouse_map_pos.0, mouse_map_pos.1); + if !map.visible_tiles[mouse_idx] { return; } + + let mut tip_boxes : Vec = Vec::new(); + crate::spatial::for_each_tile_content(mouse_idx, |entity| { + if hidden.get(entity).is_some() { return; } + let mut tip = Tooltip::new(); + tip.add(get_item_display_name(ecs, entity)); + + // Comment on attributes + let attr = attributes.get(entity); + if let Some(attr) = attr { + let mut s = "".to_string(); + if attr.might.bonus < 0 { s += "Weak. " }; + if attr.might.bonus > 0 { s += "Strong. " }; + if attr.quickness.bonus < 0 { s += "Clumsy. " }; + if attr.quickness.bonus > 0 { s += "Agile. " }; + if attr.fitness.bonus < 0 { s += "Unheathy. " }; + if attr.fitness.bonus > 0 { s += "Healthy." }; + if attr.intelligence.bonus < 0 { s += "Unintelligent. "}; + if attr.intelligence.bonus > 0 { s += "Smart. "}; + if s.is_empty() { + s = "Quite Average".to_string(); + } + tip.add(s); + } + + // Comment on pools + let stat = pools.get(entity); + if let Some(stat) = stat { + tip.add(format!("Level: {}", stat.level)); + } + + // Status effects + let statuses = ecs.read_storage::(); + let durations = ecs.read_storage::(); + let names = ecs.read_storage::(); + for (status, duration, name) in (&statuses, &durations, &names).join() { + if status.target == entity { + tip.add(format!("{} ({})", name.name, duration.turns)); + } + } + + tip_boxes.push(tip); + }); + + if tip_boxes.is_empty() { return; } + + let box_gray : RGB = RGB::from_hex("#999999").expect("Oops"); + let white = RGB::named(rltk::WHITE); + + let arrow; + let arrow_x; + let arrow_y = mouse_pos.1; + if mouse_pos.0 < 40 { + // Render to the left + arrow = to_cp437('→'); + arrow_x = mouse_pos.0 - 1; + } else { + // Render to the right + arrow = to_cp437('←'); + arrow_x = mouse_pos.0 + 1; + } + draw_batch.set(Point::new(arrow_x, arrow_y), ColorPair::new(white, box_gray), arrow); + + let mut total_height = 0; + for tt in tip_boxes.iter() { + total_height += tt.height(); + } + + let mut y = mouse_pos.1 - (total_height / 2); + while y + (total_height/2) > 50 { + y -= 1; + } + + for tt in tip_boxes.iter() { + let x = if mouse_pos.0 < 40 { + mouse_pos.0 - (1 + tt.width()) + } else { + mouse_pos.0 + (1 + tt.width()) + }; + tt.render(&mut draw_batch, x, y); + y += tt.height(); + } + + draw_batch.submit(7000); +} diff --git a/chapter-75-darkplaza/src/gui/vendor_menu.rs b/chapter-75-darkplaza/src/gui/vendor_menu.rs new file mode 100644 index 00000000..0ed20040 --- /dev/null +++ b/chapter-75-darkplaza/src/gui/vendor_menu.rs @@ -0,0 +1,118 @@ +use rltk::prelude::*; +use specs::prelude::*; +use crate::{Name, State, InBackpack, VendorMode, Vendor, Item }; +use super::{get_item_display_name, get_item_color, menu_box}; + +#[derive(PartialEq, Copy, Clone)] +pub enum VendorResult { NoResponse, Cancel, Sell, BuyMode, SellMode, Buy } + +fn vendor_sell_menu(gs : &mut State, ctx : &mut Rltk, _vendor : Entity, _mode : VendorMode) -> (VendorResult, Option, Option, Option) { + let mut draw_batch = DrawBatch::new(); + let player_entity = gs.ecs.fetch::(); + let names = gs.ecs.read_storage::(); + let backpack = gs.ecs.read_storage::(); + let items = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + + let inventory = (&backpack, &names).join().filter(|item| item.0.owner == *player_entity ); + let count = inventory.count(); + + let mut y = (25 - (count / 2)) as i32; + menu_box(&mut draw_batch, 15, y, (count+3) as i32, "Sell Which Item? (space to switch to buy mode)"); + draw_batch.print_color( + Point::new(18, y+count as i32+1), + "ESCAPE to cancel", + ColorPair::new(RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK)) + ); + + let mut equippable : Vec = Vec::new(); + let mut j = 0; + for (entity, _pack, item) in (&entities, &backpack, &items).join().filter(|item| item.1.owner == *player_entity ) { + draw_batch.set(Point::new(17, y), ColorPair::new(RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)), rltk::to_cp437('(')); + draw_batch.set(Point::new(18, y), ColorPair::new(RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK)), 97+j as rltk::FontCharType); + draw_batch.set(Point::new(19, y), ColorPair::new(RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)), rltk::to_cp437(')')); + + draw_batch.print_color( + Point::new(21, y), + &get_item_display_name(&gs.ecs, entity), + ColorPair::new(get_item_color(&gs.ecs, entity), RGB::from_f32(0.0, 0.0, 0.0)) + ); + draw_batch.print(Point::new(50, y), &format!("{:.1} gp", item.base_value * 0.8)); + equippable.push(entity); + y += 1; + j += 1; + } + + draw_batch.submit(6000); + + match ctx.key { + None => (VendorResult::NoResponse, None, None, None), + Some(key) => { + match key { + VirtualKeyCode::Space => { (VendorResult::BuyMode, None, None, None) } + VirtualKeyCode::Escape => { (VendorResult::Cancel, None, None, None) } + _ => { + let selection = rltk::letter_to_option(key); + if selection > -1 && selection < count as i32 { + return (VendorResult::Sell, Some(equippable[selection as usize]), None, None); + } + (VendorResult::NoResponse, None, None, None) + } + } + } + } +} + +fn vendor_buy_menu(gs : &mut State, ctx : &mut Rltk, vendor : Entity, _mode : VendorMode) -> (VendorResult, Option, Option, Option) { + use crate::raws::*; + let mut draw_batch = DrawBatch::new(); + + let vendors = gs.ecs.read_storage::(); + + let inventory = crate::raws::get_vendor_items(&vendors.get(vendor).unwrap().categories, &RAWS.lock().unwrap()); + let count = inventory.len(); + + let mut y = (25 - (count / 2)) as i32; + menu_box(&mut draw_batch, 15, y, (count+3) as i32, "Buy Which Item? (space to switch to sell mode)"); + draw_batch.print_color( + Point::new(18, y+count as i32+1), + "ESCAPE to cancel", + ColorPair::new(RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK)) + ); + + for (j,sale) in inventory.iter().enumerate() { + draw_batch.set(Point::new(17, y), ColorPair::new(RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)), rltk::to_cp437('(')); + draw_batch.set(Point::new(18, y), ColorPair::new(RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK)), 97+j as rltk::FontCharType); + draw_batch.set(Point::new(19, y), ColorPair::new(RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)), rltk::to_cp437(')')); + + draw_batch.print(Point::new(21, y), &sale.0); + draw_batch.print(Point::new(50, y), &format!("{:.1} gp", sale.1 * 1.2)); + y += 1; + } + + draw_batch.submit(6000); + + match ctx.key { + None => (VendorResult::NoResponse, None, None, None), + Some(key) => { + match key { + VirtualKeyCode::Space => { (VendorResult::SellMode, None, None, None) } + VirtualKeyCode::Escape => { (VendorResult::Cancel, None, None, None) } + _ => { + let selection = rltk::letter_to_option(key); + if selection > -1 && selection < count as i32 { + return (VendorResult::Buy, None, Some(inventory[selection as usize].0.clone()), Some(inventory[selection as usize].1)); + } + (VendorResult::NoResponse, None, None, None) + } + } + } + } +} + +pub fn show_vendor_menu(gs : &mut State, ctx : &mut Rltk, vendor : Entity, mode : VendorMode) -> (VendorResult, Option, Option, Option) { + match mode { + VendorMode::Buy => vendor_buy_menu(gs, ctx, vendor, mode), + VendorMode::Sell => vendor_sell_menu(gs, ctx, vendor, mode) + } +} diff --git a/chapter-75-darkplaza/src/main.rs b/chapter-75-darkplaza/src/main.rs new file mode 100644 index 00000000..2cc86ff9 --- /dev/null +++ b/chapter-75-darkplaza/src/main.rs @@ -0,0 +1,575 @@ +extern crate serde; +use rltk::{GameState, Rltk, Point}; +use specs::prelude::*; +use specs::saveload::{SimpleMarker, SimpleMarkerAllocator}; + +mod components; +pub use components::*; +mod map; +pub use map::*; +mod player; +use player::*; +mod rect; +pub use rect::Rect; +mod damage_system; +mod gui; +mod gamelog; +mod spawner; +pub mod saveload_system; +pub mod random_table; +pub mod rex_assets; +pub mod map_builders; +pub mod raws; +mod gamesystem; +pub use gamesystem::*; +pub mod effects; +#[macro_use] +extern crate lazy_static; +mod systems; +pub mod rng; +pub mod spatial; + +const SHOW_MAPGEN_VISUALIZER : bool = false; +const SHOW_FPS : bool = true; + +#[derive(PartialEq, Copy, Clone)] +pub enum VendorMode { Buy, Sell } + +#[derive(PartialEq, Copy, Clone)] +pub enum RunState { + AwaitingInput, + PreRun, + Ticking, + ShowInventory, + ShowDropItem, + ShowTargeting { range : i32, item : Entity}, + MainMenu { menu_selection : gui::MainMenuSelection }, + SaveGame, + NextLevel, + PreviousLevel, + TownPortal, + ShowRemoveItem, + GameOver, + MagicMapReveal { row : i32 }, + MapGeneration, + ShowCheatMenu, + ShowVendor { vendor: Entity, mode : VendorMode }, + TeleportingToOtherLevel { x: i32, y: i32, depth: i32 }, + ShowRemoveCurse, + ShowIdentify +} + +pub struct State { + pub ecs: World, + mapgen_next_state : Option, + mapgen_history : Vec, + mapgen_index : usize, + mapgen_timer : f32, + dispatcher : Box +} + +impl State { + fn run_systems(&mut self) { + self.dispatcher.run_now(&mut self.ecs); + self.ecs.maintain(); + } +} + +impl GameState for State { + #[allow(clippy::cognitive_complexity)] + fn tick(&mut self, ctx : &mut Rltk) { + let mut newrunstate; + { + let runstate = self.ecs.fetch::(); + newrunstate = *runstate; + } + + ctx.set_active_console(1); + ctx.cls(); + ctx.set_active_console(0); + ctx.cls(); + systems::particle_system::update_particles(&mut self.ecs, ctx); + + match newrunstate { + RunState::MainMenu{..} => {} + RunState::GameOver{..} => {} + _ => { + camera::render_camera(&self.ecs, ctx); + gui::draw_ui(&self.ecs, ctx); + } + } + + match newrunstate { + RunState::MapGeneration => { + if !SHOW_MAPGEN_VISUALIZER { + newrunstate = self.mapgen_next_state.unwrap(); + } else { + ctx.cls(); + if self.mapgen_index < self.mapgen_history.len() && self.mapgen_index < self.mapgen_history.len() { camera::render_debug_map(&self.mapgen_history[self.mapgen_index], ctx); } + + self.mapgen_timer += ctx.frame_time_ms; + if self.mapgen_timer > 250.0 { + self.mapgen_timer = 0.0; + self.mapgen_index += 1; + if self.mapgen_index >= self.mapgen_history.len() { + //self.mapgen_index -= 1; + newrunstate = self.mapgen_next_state.unwrap(); + } + } + } + } + RunState::PreRun => { + self.run_systems(); + self.ecs.maintain(); + newrunstate = RunState::AwaitingInput; + } + RunState::AwaitingInput => { + newrunstate = player_input(self, ctx); + if newrunstate != RunState::AwaitingInput { + crate::gamelog::record_event("Turn", 1); + } + } + RunState::Ticking => { + let mut should_change_target = false; + while newrunstate == RunState::Ticking { + self.run_systems(); + self.ecs.maintain(); + match *self.ecs.fetch::() { + RunState::AwaitingInput => { + newrunstate = RunState::AwaitingInput; + should_change_target = true; + } + RunState::MagicMapReveal{ .. } => newrunstate = RunState::MagicMapReveal{ row: 0 }, + RunState::TownPortal => newrunstate = RunState::TownPortal, + RunState::TeleportingToOtherLevel{ x, y, depth } => newrunstate = RunState::TeleportingToOtherLevel{ x, y, depth }, + RunState::ShowRemoveCurse => newrunstate = RunState::ShowRemoveCurse, + RunState::ShowIdentify => newrunstate = RunState::ShowIdentify, + _ => newrunstate = RunState::Ticking + } + } + if should_change_target { + player::end_turn_targeting(&mut self.ecs); + } + } + RunState::ShowInventory => { + let result = gui::show_inventory(self, ctx); + match result.0 { + gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput, + gui::ItemMenuResult::NoResponse => {} + gui::ItemMenuResult::Selected => { + let item_entity = result.1.unwrap(); + let is_ranged = self.ecs.read_storage::(); + let is_item_ranged = is_ranged.get(item_entity); + if let Some(is_item_ranged) = is_item_ranged { + newrunstate = RunState::ShowTargeting{ range: is_item_ranged.range, item: item_entity }; + } else { + let mut intent = self.ecs.write_storage::(); + intent.insert(*self.ecs.fetch::(), WantsToUseItem{ item: item_entity, target: None }).expect("Unable to insert intent"); + newrunstate = RunState::Ticking; + } + } + } + } + RunState::ShowCheatMenu => { + let result = gui::show_cheat_mode(self, ctx); + match result { + gui::CheatMenuResult::Cancel => newrunstate = RunState::AwaitingInput, + gui::CheatMenuResult::NoResponse => {} + gui::CheatMenuResult::TeleportToExit => { + self.goto_level(1); + self.mapgen_next_state = Some(RunState::PreRun); + newrunstate = RunState::MapGeneration; + } + gui::CheatMenuResult::Heal => { + let player = self.ecs.fetch::(); + let mut pools = self.ecs.write_storage::(); + let mut player_pools = pools.get_mut(*player).unwrap(); + player_pools.hit_points.current = player_pools.hit_points.max; + newrunstate = RunState::AwaitingInput; + } + gui::CheatMenuResult::Reveal => { + let mut map = self.ecs.fetch_mut::(); + for v in map.revealed_tiles.iter_mut() { + *v = true; + } + newrunstate = RunState::AwaitingInput; + } + gui::CheatMenuResult::GodMode => { + let player = self.ecs.fetch::(); + let mut pools = self.ecs.write_storage::(); + let mut player_pools = pools.get_mut(*player).unwrap(); + player_pools.god_mode = true; + newrunstate = RunState::AwaitingInput; + } + } + } + RunState::ShowDropItem => { + let result = gui::drop_item_menu(self, ctx); + match result.0 { + gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput, + gui::ItemMenuResult::NoResponse => {} + gui::ItemMenuResult::Selected => { + let item_entity = result.1.unwrap(); + let mut intent = self.ecs.write_storage::(); + intent.insert(*self.ecs.fetch::(), WantsToDropItem{ item: item_entity }).expect("Unable to insert intent"); + newrunstate = RunState::Ticking; + } + } + } + RunState::ShowRemoveItem => { + let result = gui::remove_item_menu(self, ctx); + match result.0 { + gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput, + gui::ItemMenuResult::NoResponse => {} + gui::ItemMenuResult::Selected => { + let item_entity = result.1.unwrap(); + let mut intent = self.ecs.write_storage::(); + intent.insert(*self.ecs.fetch::(), WantsToRemoveItem{ item: item_entity }).expect("Unable to insert intent"); + newrunstate = RunState::Ticking; + } + } + } + RunState::ShowRemoveCurse => { + let result = gui::remove_curse_menu(self, ctx); + match result.0 { + gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput, + gui::ItemMenuResult::NoResponse => {} + gui::ItemMenuResult::Selected => { + let item_entity = result.1.unwrap(); + self.ecs.write_storage::().remove(item_entity); + newrunstate = RunState::Ticking; + } + } + } + RunState::ShowIdentify => { + let result = gui::identify_menu(self, ctx); + match result.0 { + gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput, + gui::ItemMenuResult::NoResponse => {} + gui::ItemMenuResult::Selected => { + let item_entity = result.1.unwrap(); + if let Some(name) = self.ecs.read_storage::().get(item_entity) { + let mut dm = self.ecs.fetch_mut::(); + dm.identified_items.insert(name.name.clone()); + } + newrunstate = RunState::Ticking; + } + } + } + RunState::ShowTargeting{range, item} => { + let result = gui::ranged_target(self, ctx, range); + match result.0 { + gui::ItemMenuResult::Cancel => newrunstate = RunState::AwaitingInput, + gui::ItemMenuResult::NoResponse => {} + gui::ItemMenuResult::Selected => { + if self.ecs.read_storage::().get(item).is_some() { + let mut intent = self.ecs.write_storage::(); + intent.insert(*self.ecs.fetch::(), WantsToCastSpell{ spell: item, target: result.1 }).expect("Unable to insert intent"); + newrunstate = RunState::Ticking; + } else { + let mut intent = self.ecs.write_storage::(); + intent.insert(*self.ecs.fetch::(), WantsToUseItem{ item, target: result.1 }).expect("Unable to insert intent"); + newrunstate = RunState::Ticking; + } + } + } + } + RunState::ShowVendor{vendor, mode} => { + use crate::raws::*; + let result = gui::show_vendor_menu(self, ctx, vendor, mode); + match result.0 { + gui::VendorResult::Cancel => newrunstate = RunState::AwaitingInput, + gui::VendorResult::NoResponse => {} + gui::VendorResult::Sell => { + let price = self.ecs.read_storage::().get(result.1.unwrap()).unwrap().base_value * 0.8; + self.ecs.write_storage::().get_mut(*self.ecs.fetch::()).unwrap().gold += price; + self.ecs.delete_entity(result.1.unwrap()).expect("Unable to delete"); + } + gui::VendorResult::Buy => { + let tag = result.2.unwrap(); + let price = result.3.unwrap(); + let mut pools = self.ecs.write_storage::(); + let player_entity = self.ecs.fetch::(); + let mut identified = self.ecs.write_storage::(); + identified.insert(*player_entity, IdentifiedItem{ name : tag.clone() }).expect("Unable to insert"); + std::mem::drop(identified); + let player_pools = pools.get_mut(*player_entity).unwrap(); + std::mem::drop(player_entity); + if player_pools.gold >= price { + player_pools.gold -= price; + std::mem::drop(pools); + let player_entity = *self.ecs.fetch::(); + crate::raws::spawn_named_item(&RAWS.lock().unwrap(), &mut self.ecs, &tag, SpawnType::Carried{ by: player_entity }); + } + } + gui::VendorResult::BuyMode => newrunstate = RunState::ShowVendor{ vendor, mode: VendorMode::Buy }, + gui::VendorResult::SellMode => newrunstate = RunState::ShowVendor{ vendor, mode: VendorMode::Sell } + } + } + RunState::MainMenu{ .. } => { + let result = gui::main_menu(self, ctx); + match result { + gui::MainMenuResult::NoSelection{ selected } => newrunstate = RunState::MainMenu{ menu_selection: selected }, + gui::MainMenuResult::Selected{ selected } => { + match selected { + gui::MainMenuSelection::NewGame => newrunstate = RunState::PreRun, + gui::MainMenuSelection::LoadGame => { + saveload_system::load_game(&mut self.ecs); + newrunstate = RunState::AwaitingInput; + saveload_system::delete_save(); + } + gui::MainMenuSelection::Quit => { ::std::process::exit(0); } + } + } + } + } + RunState::GameOver => { + let result = gui::game_over(ctx); + match result { + gui::GameOverResult::NoSelection => {} + gui::GameOverResult::QuitToMenu => { + self.game_over_cleanup(); + newrunstate = RunState::MapGeneration; + self.mapgen_next_state = Some(RunState::MainMenu{ menu_selection: gui::MainMenuSelection::NewGame }); + } + } + } + RunState::SaveGame => { + saveload_system::save_game(&mut self.ecs); + newrunstate = RunState::MainMenu{ menu_selection : gui::MainMenuSelection::LoadGame }; + } + RunState::NextLevel => { + self.goto_level(1); + self.mapgen_next_state = Some(RunState::PreRun); + newrunstate = RunState::MapGeneration; + } + RunState::PreviousLevel => { + self.goto_level(-1); + self.mapgen_next_state = Some(RunState::PreRun); + newrunstate = RunState::MapGeneration; + } + RunState::TownPortal => { + // Spawn the portal + spawner::spawn_town_portal(&mut self.ecs); + + // Transition + let map_depth = self.ecs.fetch::().depth; + let destination_offset = 0 - (map_depth-1); + self.goto_level(destination_offset); + self.mapgen_next_state = Some(RunState::PreRun); + newrunstate = RunState::MapGeneration; + } + RunState::TeleportingToOtherLevel{x, y, depth} => { + self.goto_level(depth-1); + let player_entity = self.ecs.fetch::(); + if let Some(pos) = self.ecs.write_storage::().get_mut(*player_entity) { + pos.x = x; + pos.y = y; + } + let mut ppos = self.ecs.fetch_mut::(); + ppos.x = x; + ppos.y = y; + self.mapgen_next_state = Some(RunState::PreRun); + newrunstate = RunState::MapGeneration; + } + RunState::MagicMapReveal{row} => { + let mut map = self.ecs.fetch_mut::(); + for x in 0..map.width { + let idx = map.xy_idx(x as i32,row); + map.revealed_tiles[idx] = true; + } + if row == map.height-1 { + newrunstate = RunState::Ticking; + } else { + newrunstate = RunState::MagicMapReveal{ row: row+1 }; + } + } + } + + { + let mut runwriter = self.ecs.write_resource::(); + *runwriter = newrunstate; + } + damage_system::delete_the_dead(&mut self.ecs); + + rltk::render_draw_buffer(ctx); + if SHOW_FPS { + ctx.print(1, 59, &format!("FPS: {}", ctx.fps)); + } + } +} + +impl State { + fn goto_level(&mut self, offset: i32) { + freeze_level_entities(&mut self.ecs); + + // Build a new map and place the player + let current_depth = self.ecs.fetch::().depth; + self.generate_world_map(current_depth + offset, offset); + + // Notify the player + gamelog::Logger::new().append("You change level.").log(); + } + + fn game_over_cleanup(&mut self) { + // Delete everything + let mut to_delete = Vec::new(); + for e in self.ecs.entities().join() { + to_delete.push(e); + } + for del in to_delete.iter() { + self.ecs.delete_entity(*del).expect("Deletion failed"); + } + + // Spawn a new player + { + let player_entity = spawner::player(&mut self.ecs, 0, 0); + let mut player_entity_writer = self.ecs.write_resource::(); + *player_entity_writer = player_entity; + } + + // Replace the world maps + self.ecs.insert(map::MasterDungeonMap::new()); + + // Build a new map and place the player + self.generate_world_map(1, 0); + } + + fn generate_world_map(&mut self, new_depth : i32, offset: i32) { + self.mapgen_index = 0; + self.mapgen_timer = 0.0; + self.mapgen_history.clear(); + let map_building_info = map::level_transition(&mut self.ecs, new_depth, offset); + if let Some(history) = map_building_info { + self.mapgen_history = history; + } else { + map::thaw_level_entities(&mut self.ecs); + } + + gamelog::clear_log(); + gamelog::Logger::new() + .append("Welcome to") + .color(rltk::CYAN) + .append("Rusty Roguelike") + .log(); + + gamelog::clear_events(); + } +} + +fn main() -> rltk::BError { + use rltk::RltkBuilder; + let mut context = RltkBuilder::simple(80, 60) + .unwrap() + .with_title("Roguelike Tutorial") + .with_font("vga8x16.png", 8, 16) + .with_sparse_console(80, 30, "vga8x16.png") + .with_vsync(false) + .build()?; + context.with_post_scanlines(true); + let mut gs = State { + ecs: World::new(), + mapgen_next_state : Some(RunState::MainMenu{ menu_selection: gui::MainMenuSelection::NewGame }), + mapgen_index : 0, + mapgen_history: Vec::new(), + mapgen_timer: 0.0, + dispatcher: systems::build() + }; + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::>(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.insert(SimpleMarkerAllocator::::new()); + + raws::load_raws(); + + gs.ecs.insert(map::MasterDungeonMap::new()); + gs.ecs.insert(Map::new(1, 64, 64, "New Map")); + gs.ecs.insert(Point::new(0, 0)); + let player_entity = spawner::player(&mut gs.ecs, 0, 0); + gs.ecs.insert(player_entity); + gs.ecs.insert(RunState::MapGeneration{} ); + gs.ecs.insert(systems::particle_system::ParticleBuilder::new()); + gs.ecs.insert(rex_assets::RexAssets::new()); + + gs.generate_world_map(1, 0); + + rltk::main_loop(context, gs) +} diff --git a/chapter-75-darkplaza/src/map/camera.rs b/chapter-75-darkplaza/src/map/camera.rs new file mode 100644 index 00000000..0832527b --- /dev/null +++ b/chapter-75-darkplaza/src/map/camera.rs @@ -0,0 +1,149 @@ +use specs::prelude::*; +use crate::{Map,Position,Renderable,Hidden,TileSize,Target}; +use rltk::prelude::*; +use crate::map::tile_glyph; + +pub fn get_screen_bounds(ecs: &World, _ctx: &mut Rltk) -> (i32, i32, i32, i32) { + let player_pos = ecs.fetch::(); + //let (x_chars, y_chars) = ctx.get_char_size(); + let (x_chars, y_chars) = (48, 44); + + let center_x = (x_chars / 2) as i32; + let center_y = (y_chars / 2) as i32; + + let min_x = player_pos.x - center_x; + let max_x = min_x + x_chars as i32; + let min_y = player_pos.y - center_y; + let max_y = min_y + y_chars as i32; + + (min_x, max_x, min_y, max_y) +} + +const SHOW_BOUNDARIES : bool = false; + +pub fn render_camera(ecs: &World, ctx : &mut Rltk) { + let mut draw_batch = DrawBatch::new(); + let map = ecs.fetch::(); + let (min_x, max_x, min_y, max_y) = get_screen_bounds(ecs, ctx); + + // Render the Map + + let map_width = map.width-1; + let map_height = map.height-1; + + for (y,ty) in (min_y .. max_y).enumerate() { + for (x,tx) in (min_x .. max_x).enumerate() { + if tx > 0 && tx < map_width && ty > 0 && ty < map_height { + let idx = map.xy_idx(tx, ty); + if map.revealed_tiles[idx] { + let (glyph, fg, bg) = tile_glyph(idx, &*map); + draw_batch.set( + Point::new(x+1, y+1), + ColorPair::new(fg, bg), + glyph + ); + } + } else if SHOW_BOUNDARIES { + draw_batch.set( + Point::new(x+1, y+1), + ColorPair::new(RGB::named(rltk::GRAY), RGB::named(rltk::BLACK)), + to_cp437('·') + ); + } + } + } + + // Render entities + let positions = ecs.read_storage::(); + let renderables = ecs.read_storage::(); + let hidden = ecs.read_storage::(); + let map = ecs.fetch::(); + let sizes = ecs.read_storage::(); + let entities = ecs.entities(); + let targets = ecs.read_storage::(); + + let mut data = (&positions, &renderables, &entities, !&hidden).join().collect::>(); + data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order) ); + for (pos, render, entity, _hidden) in data.iter() { + if let Some(size) = sizes.get(*entity) { + for cy in 0 .. size.y { + for cx in 0 .. size.x { + let tile_x = cx + pos.x; + let tile_y = cy + pos.y; + let idx = map.xy_idx(tile_x, tile_y); + if map.visible_tiles[idx] { + let entity_screen_x = (cx + pos.x) - min_x; + let entity_screen_y = (cy + pos.y) - min_y; + if entity_screen_x > 0 && entity_screen_x < map_width && entity_screen_y > 0 && entity_screen_y < map_height { + draw_batch.set( + Point::new(entity_screen_x + 1, entity_screen_y + 1), + ColorPair::new(render.fg, render.bg), + render.glyph + ); + } + } + } + } + } else { + let idx = map.xy_idx(pos.x, pos.y); + if map.visible_tiles[idx] { + let entity_screen_x = pos.x - min_x; + let entity_screen_y = pos.y - min_y; + if entity_screen_x > 0 && entity_screen_x < map_width && entity_screen_y > 0 && entity_screen_y < map_height { + draw_batch.set( + Point::new(entity_screen_x + 1, entity_screen_y + 1), + ColorPair::new(render.fg, render.bg), + render.glyph + ); + } + } + } + + if targets.get(*entity).is_some() { + let entity_screen_x = pos.x - min_x; + let entity_screen_y = pos.y - min_y; + draw_batch.set( + Point::new(entity_screen_x , entity_screen_y + 1), + ColorPair::new(RGB::named(rltk::RED), RGB::named(rltk::YELLOW)), + to_cp437('[') + ); + draw_batch.set( + Point::new(entity_screen_x +2, entity_screen_y + 1), + ColorPair::new(RGB::named(rltk::RED), RGB::named(rltk::YELLOW)), + to_cp437(']') + ); + } + } + + draw_batch.submit(0); +} + +pub fn render_debug_map(map : &Map, ctx : &mut Rltk) { + let player_pos = Point::new(map.width / 2, map.height / 2); + let (x_chars, y_chars) = ctx.get_char_size(); + + let center_x = (x_chars / 2) as i32; + let center_y = (y_chars / 2) as i32; + + let min_x = player_pos.x - center_x; + let max_x = min_x + x_chars as i32; + let min_y = player_pos.y - center_y; + let max_y = min_y + y_chars as i32; + + let map_width = map.width-1; + let map_height = map.height-1; + + for (y,ty) in (min_y .. max_y).enumerate() { + for (x,tx) in (min_x .. max_x).enumerate() { + if tx > 0 && tx < map_width && ty > 0 && ty < map_height { + let idx = map.xy_idx(tx, ty); + if map.revealed_tiles[idx] { + let (glyph, fg, bg) = tile_glyph(idx, &*map); + ctx.set(x as i32, y as i32, fg, bg, glyph); + } + } else if SHOW_BOUNDARIES { + ctx.set(x as i32, y as i32, RGB::named(rltk::GRAY), RGB::named(rltk::BLACK), rltk::to_cp437('·')); + } + } + } +} diff --git a/chapter-75-darkplaza/src/map/dungeon.rs b/chapter-75-darkplaza/src/map/dungeon.rs new file mode 100644 index 00000000..c89a797b --- /dev/null +++ b/chapter-75-darkplaza/src/map/dungeon.rs @@ -0,0 +1,254 @@ +use std::collections::{HashMap, HashSet}; +use serde::{Serialize, Deserialize}; +use super::{Map, TileType}; +use crate::components::{Position, Viewshed, OtherLevelPosition}; +use crate::map_builders::level_builder; +use specs::prelude::*; +use rltk::Point; + +#[derive(Default, Serialize, Deserialize, Clone)] +pub struct MasterDungeonMap { + maps : HashMap, + pub identified_items : HashSet, + pub scroll_mappings : HashMap, + pub potion_mappings : HashMap +} + +impl MasterDungeonMap { + pub fn new() -> MasterDungeonMap { + let mut dm = MasterDungeonMap{ + maps: HashMap::new() , + identified_items : HashSet::new(), + scroll_mappings : HashMap::new(), + potion_mappings : HashMap::new() + }; + + for scroll_tag in crate::raws::get_scroll_tags().iter() { + let masked_name = make_scroll_name(); + dm.scroll_mappings.insert(scroll_tag.to_string(), masked_name); + } + + let mut used_potion_names : HashSet = HashSet::new(); + for potion_tag in crate::raws::get_potion_tags().iter() { + let masked_name = make_potion_name(&mut used_potion_names); + dm.potion_mappings.insert(potion_tag.to_string(), masked_name); + } + + dm + } + + pub fn store_map(&mut self, map : &Map) { + self.maps.insert(map.depth, map.clone()); + } + + pub fn get_map(&self, depth : i32) -> Option { + if self.maps.contains_key(&depth) { + let mut result = self.maps[&depth].clone(); + Some(result) + } else { + None + } + } +} + +fn make_scroll_name() -> String { + let length = 4 + crate::rng::roll_dice(1, 4); + let mut name = "Scroll of ".to_string(); + + for i in 0..length { + if i % 2 == 0 { + name += match crate::rng::roll_dice(1, 5) { + 1 => "a", + 2 => "e", + 3 => "i", + 4 => "o", + _ => "u" + } + } else { + name += match crate::rng::roll_dice(1, 21) { + 1 => "b", + 2 => "c", + 3 => "d", + 4 => "f", + 5 => "g", + 6 => "h", + 7 => "j", + 8 => "k", + 9 => "l", + 10 => "m", + 11 => "n", + 12 => "p", + 13 => "q", + 14 => "r", + 15 => "s", + 16 => "t", + 17 => "v", + 18 => "w", + 19 => "x", + 20 => "y", + _ => "z" + } + } + } + + name +} + +const POTION_COLORS: &[&str] = &["Red", "Orange", "Yellow", "Green", "Brown", "Indigo", "Violet"]; +const POTION_ADJECTIVES : &[&str] = &["Swirling", "Effervescent", "Slimey", "Oiley", "Viscous", "Smelly", "Glowing"]; + +fn make_potion_name(used_names : &mut HashSet) -> String { + loop { + let mut name : String = POTION_ADJECTIVES[crate::rng::roll_dice(1, POTION_ADJECTIVES.len() as i32) as usize -1].to_string(); + name += " "; + name += POTION_COLORS[crate::rng::roll_dice(1, POTION_COLORS.len() as i32) as usize -1]; + name += " Potion"; + + if !used_names.contains(&name) { + used_names.insert(name.clone()); + return name; + } + } +} + +fn transition_to_new_map(ecs : &mut World, new_depth: i32) -> Vec { + let mut builder = level_builder(new_depth, 80, 50); + builder.build_map(); + if new_depth > 1 { + if let Some(pos) = &builder.build_data.starting_position { + let up_idx = builder.build_data.map.xy_idx(pos.x, pos.y); + builder.build_data.map.tiles[up_idx] = TileType::UpStairs; + } + } + let mapgen_history = builder.build_data.history.clone(); + let player_start; + { + let mut worldmap_resource = ecs.write_resource::(); + *worldmap_resource = builder.build_data.map.clone(); + player_start = builder.build_data.starting_position.as_mut().unwrap().clone(); + } + + // Spawn bad guys + builder.spawn_entities(ecs); + + // Place the player and update resources + let (player_x, player_y) = (player_start.x, player_start.y); + let mut player_position = ecs.write_resource::(); + *player_position = Point::new(player_x, player_y); + let mut position_components = ecs.write_storage::(); + let player_entity = ecs.fetch::(); + let player_pos_comp = position_components.get_mut(*player_entity); + if let Some(player_pos_comp) = player_pos_comp { + player_pos_comp.x = player_x; + player_pos_comp.y = player_y; + } + + // Mark the player's visibility as dirty + let mut viewshed_components = ecs.write_storage::(); + let vs = viewshed_components.get_mut(*player_entity); + if let Some(vs) = vs { + vs.dirty = true; + } + + // Store the newly minted map + let mut dungeon_master = ecs.write_resource::(); + dungeon_master.store_map(&builder.build_data.map); + + mapgen_history +} + +fn transition_to_existing_map(ecs: &mut World, new_depth: i32, offset: i32) { + let dungeon_master = ecs.read_resource::(); + let map = dungeon_master.get_map(new_depth).unwrap(); + let mut worldmap_resource = ecs.write_resource::(); + let player_entity = ecs.fetch::(); + + // Find the down stairs and place the player + let w = map.width; + let stair_type = if offset < 0 { TileType::DownStairs } else { TileType::UpStairs }; + for (idx, tt) in map.tiles.iter().enumerate() { + if *tt == stair_type { + let mut player_position = ecs.write_resource::(); + *player_position = Point::new(idx as i32 % w, idx as i32 / w); + let mut position_components = ecs.write_storage::(); + let player_pos_comp = position_components.get_mut(*player_entity); + if let Some(player_pos_comp) = player_pos_comp { + player_pos_comp.x = idx as i32 % w; + player_pos_comp.y = idx as i32 / w; + if new_depth == 1 { + player_pos_comp.x -= 1; + } + } + } + } + + *worldmap_resource = map; + + // Mark the player's visibility as dirty + let mut viewshed_components = ecs.write_storage::(); + let vs = viewshed_components.get_mut(*player_entity); + if let Some(vs) = vs { + vs.dirty = true; + } +} + +pub fn freeze_level_entities(ecs: &mut World) { + // Obtain ECS access + let entities = ecs.entities(); + let mut positions = ecs.write_storage::(); + let mut other_level_positions = ecs.write_storage::(); + let player_entity = ecs.fetch::(); + let map_depth = ecs.fetch::().depth; + + // Find positions and make OtherLevelPosition + let mut pos_to_delete : Vec = Vec::new(); + for (entity, pos) in (&entities, &positions).join() { + if entity != *player_entity { + other_level_positions.insert(entity, OtherLevelPosition{ x: pos.x, y: pos.y, depth: map_depth }).expect("Insert fail"); + pos_to_delete.push(entity); + } + } + + // Remove positions + for p in pos_to_delete.iter() { + positions.remove(*p); + } +} + +pub fn thaw_level_entities(ecs: &mut World) { + // Obtain ECS access + let entities = ecs.entities(); + let mut positions = ecs.write_storage::(); + let mut other_level_positions = ecs.write_storage::(); + let player_entity = ecs.fetch::(); + let map_depth = ecs.fetch::().depth; + + // Find OtherLevelPosition + let mut pos_to_delete : Vec = Vec::new(); + for (entity, pos) in (&entities, &other_level_positions).join() { + if entity != *player_entity && pos.depth == map_depth { + positions.insert(entity, Position{ x: pos.x, y: pos.y }).expect("Insert fail"); + pos_to_delete.push(entity); + } + } + + // Remove positions + for p in pos_to_delete.iter() { + other_level_positions.remove(*p); + } +} + +pub fn level_transition(ecs : &mut World, new_depth: i32, offset: i32) -> Option> { + // Obtain the master dungeon map + let dungeon_master = ecs.read_resource::(); + + // Do we already have a map? + if dungeon_master.get_map(new_depth).is_some() { + std::mem::drop(dungeon_master); + transition_to_existing_map(ecs, new_depth, offset); + None + } else { + std::mem::drop(dungeon_master); + Some(transition_to_new_map(ecs, new_depth)) + } +} diff --git a/chapter-75-darkplaza/src/map/mod.rs b/chapter-75-darkplaza/src/map/mod.rs new file mode 100644 index 00000000..1b8f06ff --- /dev/null +++ b/chapter-75-darkplaza/src/map/mod.rs @@ -0,0 +1,137 @@ +use rltk::{ BaseMap, Algorithm2D, Point }; +use specs::prelude::*; +use serde::{Serialize, Deserialize}; +use std::collections::HashSet; +mod tiletype; +pub use tiletype::{TileType, tile_walkable, tile_opaque, tile_cost}; +mod themes; +pub use themes::*; +mod dungeon; +pub use dungeon::{MasterDungeonMap, level_transition, freeze_level_entities, thaw_level_entities}; +pub mod camera; + +#[derive(Default, Serialize, Deserialize, Clone)] +pub struct Map { + pub tiles : Vec, + pub width : i32, + pub height : i32, + pub revealed_tiles : Vec, + pub visible_tiles : Vec, + pub depth : i32, + pub bloodstains : HashSet, + pub view_blocked : HashSet, + pub name : String, + pub outdoors : bool, + pub light : Vec, +} + +impl Map { + pub fn xy_idx(&self, x: i32, y: i32) -> usize { + (y as usize * self.width as usize) + x as usize + } + + fn is_exit_valid(&self, x:i32, y:i32) -> bool { + if x < 1 || x > self.width-1 || y < 1 || y > self.height-1 { return false; } + let idx = self.xy_idx(x, y); + !crate::spatial::is_blocked(idx) + } + + pub fn populate_blocked(&mut self) { + crate::spatial::populate_blocked_from_map(self); + } + + pub fn populate_blocked_multi(&mut self, width : i32, height : i32) { + self.populate_blocked(); + for y in 1 .. self.height-1 { + for x in 1 .. self.width - 1 { + let idx = self.xy_idx(x, y); + if !crate::spatial::is_blocked(idx) { + for cy in 0..height { + for cx in 0..width { + let tx = x + cx; + let ty = y + cy; + if tx < self.width-1 && ty < self.height-1 { + let tidx = self.xy_idx(tx, ty); + if crate::spatial::is_blocked(tidx) { + crate::spatial::set_blocked(idx, true); + } + } else { + crate::spatial::set_blocked(idx, true); + } + } + } + } + } + } + } + + pub fn clear_content_index(&mut self) { + crate::spatial::clear(); + } + + /// Generates an empty map, consisting entirely of solid walls + pub fn new(new_depth : i32, width: i32, height: i32, name: S) -> Map { + let map_tile_count = (width*height) as usize; + crate::spatial::set_size(map_tile_count); + Map{ + tiles : vec![TileType::Wall; map_tile_count], + width, + height, + revealed_tiles : vec![false; map_tile_count], + visible_tiles : vec![false; map_tile_count], + depth: new_depth, + bloodstains: HashSet::new(), + view_blocked : HashSet::new(), + name : name.to_string(), + outdoors : true, + light: vec![rltk::RGB::from_f32(0.0, 0.0, 0.0); map_tile_count] + } + } +} + +impl BaseMap for Map { + fn is_opaque(&self, idx:usize) -> bool { + if idx > 0 && idx < self.tiles.len() { + tile_opaque(self.tiles[idx]) || self.view_blocked.contains(&idx) + } else { + true + } + } + + fn get_available_exits(&self, idx:usize) -> rltk::SmallVec<[(usize, f32); 10]> { + const DIAGONAL_COST : f32 = 1.5; + let mut exits = rltk::SmallVec::new(); + let x = idx as i32 % self.width; + let y = idx as i32 / self.width; + let tt = self.tiles[idx as usize]; + let w = self.width as usize; + + // Cardinal directions + if self.is_exit_valid(x-1, y) { exits.push((idx-1, tile_cost(tt))) }; + if self.is_exit_valid(x+1, y) { exits.push((idx+1, tile_cost(tt))) }; + if self.is_exit_valid(x, y-1) { exits.push((idx-w, tile_cost(tt))) }; + if self.is_exit_valid(x, y+1) { exits.push((idx+w, tile_cost(tt))) }; + + // Diagonals + if self.is_exit_valid(x-1, y-1) { exits.push(((idx-w)-1, tile_cost(tt) * DIAGONAL_COST)); } + if self.is_exit_valid(x+1, y-1) { exits.push(((idx-w)+1, tile_cost(tt) * DIAGONAL_COST)); } + if self.is_exit_valid(x-1, y+1) { exits.push(((idx+w)-1, tile_cost(tt) * DIAGONAL_COST)); } + if self.is_exit_valid(x+1, y+1) { exits.push(((idx+w)+1, tile_cost(tt) * DIAGONAL_COST)); } + + exits + } + + fn get_pathing_distance(&self, idx1:usize, idx2:usize) -> f32 { + let w = self.width as usize; + let p1 = Point::new(idx1 % w, idx1 / w); + let p2 = Point::new(idx2 % w, idx2 / w); + rltk::DistanceAlg::Pythagoras.distance2d(p1, p2) + } +} + +impl Algorithm2D for Map { + fn dimensions(&self) -> Point { + Point::new(self.width, self.height) + } +} + diff --git a/chapter-75-darkplaza/src/map/themes.rs b/chapter-75-darkplaza/src/map/themes.rs new file mode 100644 index 00000000..5a2d0bc1 --- /dev/null +++ b/chapter-75-darkplaza/src/map/themes.rs @@ -0,0 +1,169 @@ +use super::{Map, TileType}; +use rltk::RGB; + +pub fn tile_glyph(idx: usize, map : &Map) -> (rltk::FontCharType, RGB, RGB) { + let (glyph, mut fg, mut bg) = match map.depth { + 9 => get_mushroom_glyph(idx, map), + 8 => get_mushroom_glyph(idx, map), + 7 => { + let x = idx as i32 % map.width; + if x > map.width-16 { + get_tile_glyph_default(idx, map) + } else { + get_mushroom_glyph(idx, map) + } + } + 5 => { + let x = idx as i32 % map.width; + if x < map.width/2 { + get_limestone_cavern_glyph(idx, map) + } else { + get_tile_glyph_default(idx, map) + } + } + 4 => get_limestone_cavern_glyph(idx, map), + 3 => get_limestone_cavern_glyph(idx, map), + 2 => get_forest_glyph(idx, map), + _ => get_tile_glyph_default(idx, map) + }; + + if map.bloodstains.contains(&idx) { bg = RGB::from_f32(0.75, 0., 0.); } + if !map.visible_tiles[idx] { + fg = fg.to_greyscale(); + bg = RGB::from_f32(0., 0., 0.); // Don't show stains out of visual range + } else if !map.outdoors { + fg = fg * map.light[idx]; + bg = bg * map.light[idx]; + } + + (glyph, fg, bg) +} + +fn get_forest_glyph(idx:usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) { + let glyph; + let fg; + let bg = RGB::from_f32(0., 0., 0.); + + match map.tiles[idx] { + TileType::Wall => { glyph = rltk::to_cp437('♣'); fg = RGB::from_f32(0.0, 0.6, 0.0); } + TileType::Bridge => { glyph = rltk::to_cp437('.'); fg = RGB::named(rltk::CHOCOLATE); } + TileType::Road => { glyph = rltk::to_cp437('≡'); fg = RGB::named(rltk::YELLOW); } + TileType::Grass => { glyph = rltk::to_cp437('"'); fg = RGB::named(rltk::GREEN); } + TileType::ShallowWater => { glyph = rltk::to_cp437('~'); fg = RGB::named(rltk::CYAN); } + TileType::DeepWater => { glyph = rltk::to_cp437('~'); fg = RGB::named(rltk::BLUE); } + TileType::Gravel => { glyph = rltk::to_cp437(';'); fg = RGB::from_f32(0.5, 0.5, 0.5); } + TileType::DownStairs => { glyph = rltk::to_cp437('>'); fg = RGB::from_f32(0., 1.0, 1.0); } + TileType::UpStairs => { glyph = rltk::to_cp437('<'); fg = RGB::from_f32(0., 1.0, 1.0); } + _ => { glyph = rltk::to_cp437('"'); fg = RGB::from_f32(0.0, 0.5, 0.0); } + } + + (glyph, fg, bg) +} + +fn get_mushroom_glyph(idx:usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) { + let glyph; + let fg; + let bg = RGB::from_f32(0., 0., 0.); + + match map.tiles[idx] { + TileType::Wall => { glyph = rltk::to_cp437('♠'); fg = RGB::from_f32(1.0, 0.0, 1.0); } + TileType::Bridge => { glyph = rltk::to_cp437('.'); fg = RGB::named(rltk::GREEN); } + TileType::Road => { glyph = rltk::to_cp437('≡'); fg = RGB::named(rltk::CHOCOLATE); } + TileType::Grass => { glyph = rltk::to_cp437('"'); fg = RGB::named(rltk::GREEN); } + TileType::ShallowWater => { glyph = rltk::to_cp437('~'); fg = RGB::named(rltk::CYAN); } + TileType::DeepWater => { glyph = rltk::to_cp437('~'); fg = RGB::named(rltk::BLUE); } + TileType::Gravel => { glyph = rltk::to_cp437(';'); fg = RGB::from_f32(0.5, 0.5, 0.5); } + TileType::DownStairs => { glyph = rltk::to_cp437('>'); fg = RGB::from_f32(0., 1.0, 1.0); } + TileType::UpStairs => { glyph = rltk::to_cp437('<'); fg = RGB::from_f32(0., 1.0, 1.0); } + _ => { glyph = rltk::to_cp437('"'); fg = RGB::from_f32(0.0, 0.6, 0.0); } + } + + (glyph, fg, bg) +} + +fn get_limestone_cavern_glyph(idx:usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) { + let glyph; + let fg; + let bg = RGB::from_f32(0., 0., 0.); + + match map.tiles[idx] { + TileType::Wall => { glyph = rltk::to_cp437('▒'); fg = RGB::from_f32(0.7, 0.7, 0.7); } + TileType::Bridge => { glyph = rltk::to_cp437('.'); fg = RGB::named(rltk::CHOCOLATE); } + TileType::Road => { glyph = rltk::to_cp437('≡'); fg = RGB::named(rltk::YELLOW); } + TileType::Grass => { glyph = rltk::to_cp437('"'); fg = RGB::named(rltk::GREEN); } + TileType::ShallowWater => { glyph = rltk::to_cp437('░'); fg = RGB::named(rltk::CYAN); } + TileType::DeepWater => { glyph = rltk::to_cp437('▓'); fg = RGB::from_f32(0.2, 0.2, 1.0); } + TileType::Gravel => { glyph = rltk::to_cp437(';'); fg = RGB::from_f32(0.5, 0.5, 0.5); } + TileType::DownStairs => { glyph = rltk::to_cp437('>'); fg = RGB::from_f32(0., 1.0, 1.0); } + TileType::UpStairs => { glyph = rltk::to_cp437('<'); fg = RGB::from_f32(0., 1.0, 1.0); } + TileType::Stalactite => { glyph = rltk::to_cp437('╨'); fg = RGB::from_f32(0.7, 0.7, 0.7); } + TileType::Stalagmite => { glyph = rltk::to_cp437('╥'); fg = RGB::from_f32(0.7, 0.7, 0.7); } + _ => { glyph = rltk::to_cp437('\''); fg = RGB::from_f32(0.4, 0.4, 0.4); } + } + + (glyph, fg, bg) +} + +fn get_tile_glyph_default(idx: usize, map : &Map) -> (rltk::FontCharType, RGB, RGB) { + let glyph; + let fg; + let bg = RGB::from_f32(0., 0., 0.); + + match map.tiles[idx] { + TileType::Floor => { glyph = rltk::to_cp437('.'); fg = RGB::from_f32(0.0, 0.5, 0.5); } + TileType::WoodFloor => { glyph = rltk::to_cp437('░'); fg = RGB::named(rltk::CHOCOLATE); } + TileType::Wall => { + let x = idx as i32 % map.width; + let y = idx as i32 / map.width; + glyph = wall_glyph(&*map, x, y); + fg = RGB::from_f32(0., 1.0, 0.); + } + TileType::DownStairs => { glyph = rltk::to_cp437('>'); fg = RGB::from_f32(0., 1.0, 1.0); } + TileType::UpStairs => { glyph = rltk::to_cp437('<'); fg = RGB::from_f32(0., 1.0, 1.0); } + TileType::Bridge => { glyph = rltk::to_cp437('.'); fg = RGB::named(rltk::CHOCOLATE); } + TileType::Road => { glyph = rltk::to_cp437('≡'); fg = RGB::named(rltk::GRAY); } + TileType::Grass => { glyph = rltk::to_cp437('"'); fg = RGB::named(rltk::GREEN); } + TileType::ShallowWater => { glyph = rltk::to_cp437('~'); fg = RGB::named(rltk::CYAN); } + TileType::DeepWater => { glyph = rltk::to_cp437('~'); fg = RGB::named(rltk::BLUE); } + TileType::Gravel => { glyph = rltk::to_cp437(';'); fg = RGB::from_f32(0.5, 0.5, 0.5); } + TileType::Stalactite => { glyph = rltk::to_cp437('╨'); fg = RGB::from_f32(0.5, 0.5, 0.5); } + TileType::Stalagmite => { glyph = rltk::to_cp437('╥'); fg = RGB::from_f32(0.5, 0.5, 0.5); } + } + + (glyph, fg, bg) +} + +fn wall_glyph(map : &Map, x: i32, y:i32) -> rltk::FontCharType { + if x < 1 || x > map.width-2 || y < 1 || y > map.height-2 as i32 { return 35; } + let mut mask : u8 = 0; + + if is_revealed_and_wall(map, x, y - 1) { mask +=1; } + if is_revealed_and_wall(map, x, y + 1) { mask +=2; } + if is_revealed_and_wall(map, x - 1, y) { mask +=4; } + if is_revealed_and_wall(map, x + 1, y) { mask +=8; } + + match mask { + 0 => { 9 } // Pillar because we can't see neighbors + 1 => { 186 } // Wall only to the north + 2 => { 186 } // Wall only to the south + 3 => { 186 } // Wall to the north and south + 4 => { 205 } // Wall only to the west + 5 => { 188 } // Wall to the north and west + 6 => { 187 } // Wall to the south and west + 7 => { 185 } // Wall to the north, south and west + 8 => { 205 } // Wall only to the east + 9 => { 200 } // Wall to the north and east + 10 => { 201 } // Wall to the south and east + 11 => { 204 } // Wall to the north, south and east + 12 => { 205 } // Wall to the east and west + 13 => { 202 } // Wall to the east, west, and south + 14 => { 203 } // Wall to the east, west, and north + 15 => { 206 } // ╬ Wall on all sides + _ => { 35 } // We missed one? + } +} + +fn is_revealed_and_wall(map: &Map, x: i32, y: i32) -> bool { + let idx = map.xy_idx(x, y); + map.tiles[idx] == TileType::Wall && map.revealed_tiles[idx] +} diff --git a/chapter-75-darkplaza/src/map/tiletype.rs b/chapter-75-darkplaza/src/map/tiletype.rs new file mode 100644 index 00000000..ad3fdec6 --- /dev/null +++ b/chapter-75-darkplaza/src/map/tiletype.rs @@ -0,0 +1,44 @@ +use serde::{Serialize, Deserialize}; + +#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)] +pub enum TileType { + Wall, + Stalactite, + Stalagmite, + Floor, + DownStairs, + Road, + Grass, + ShallowWater, + DeepWater, + WoodFloor, + Bridge, + Gravel, + UpStairs +} + +pub fn tile_walkable(tt : TileType) -> bool { + match tt { + TileType::Floor | TileType::DownStairs | TileType::Road | TileType::Grass | + TileType::ShallowWater | TileType::WoodFloor | TileType::Bridge | TileType::Gravel | + TileType::UpStairs + => true, + _ => false + } +} + +pub fn tile_opaque(tt : TileType) -> bool { + match tt { + TileType::Wall | TileType::Stalactite | TileType::Stalagmite => true, + _ => false + } +} + +pub fn tile_cost(tt : TileType) -> f32 { + match tt { + TileType::Road => 0.8, + TileType::Grass => 1.1, + TileType::ShallowWater => 1.2, + _ => 1.0 + } +} diff --git a/chapter-75-darkplaza/src/map_builders/area_ending_point.rs b/chapter-75-darkplaza/src/map_builders/area_ending_point.rs new file mode 100644 index 00000000..bbcaf2a8 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/area_ending_point.rs @@ -0,0 +1,68 @@ +use super::{MetaMapBuilder, BuilderMap, TileType}; +use crate::map; + +#[allow(dead_code)] +pub enum XEnd { LEFT, CENTER, RIGHT } + +#[allow(dead_code)] +pub enum YEnd{ TOP, CENTER, BOTTOM } + +pub struct AreaEndingPosition { + x : XEnd, + y : YEnd +} + +impl MetaMapBuilder for AreaEndingPosition { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl AreaEndingPosition { + #[allow(dead_code)] + pub fn new(x : XEnd, y : YEnd) -> Box { + Box::new(AreaEndingPosition{ + x, y + }) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + let seed_x; + let seed_y; + + match self.x { + XEnd::LEFT => seed_x = 1, + XEnd::CENTER => seed_x = build_data.map.width / 2, + XEnd::RIGHT => seed_x = build_data.map.width - 2 + } + + match self.y { + YEnd::TOP => seed_y = 1, + YEnd::CENTER => seed_y = build_data.map.height / 2, + YEnd::BOTTOM => seed_y = build_data.map.height - 2 + } + + let mut available_floors : Vec<(usize, f32)> = Vec::new(); + for (idx, tiletype) in build_data.map.tiles.iter().enumerate() { + if map::tile_walkable(*tiletype) { + available_floors.push( + ( + idx, + rltk::DistanceAlg::PythagorasSquared.distance2d( + rltk::Point::new(idx as i32 % build_data.map.width, idx as i32 / build_data.map.width), + rltk::Point::new(seed_x, seed_y) + ) + ) + ); + } + } + if available_floors.is_empty() { + panic!("No valid floors to start on"); + } + + available_floors.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap()); + + build_data.map.tiles[available_floors[0].0] = TileType::DownStairs; + build_data.take_snapshot(); + } +} diff --git a/chapter-75-darkplaza/src/map_builders/area_starting_points.rs b/chapter-75-darkplaza/src/map_builders/area_starting_points.rs new file mode 100644 index 00000000..9041b75c --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/area_starting_points.rs @@ -0,0 +1,70 @@ +use super::{MetaMapBuilder, BuilderMap, Position}; +use crate::map; + +#[allow(dead_code)] +pub enum XStart { LEFT, CENTER, RIGHT } + +#[allow(dead_code)] +pub enum YStart { TOP, CENTER, BOTTOM } + +pub struct AreaStartingPosition { + x : XStart, + y : YStart +} + +impl MetaMapBuilder for AreaStartingPosition { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl AreaStartingPosition { + #[allow(dead_code)] + pub fn new(x : XStart, y : YStart) -> Box { + Box::new(AreaStartingPosition{ + x, y + }) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + let seed_x; + let seed_y; + + match self.x { + XStart::LEFT => seed_x = 1, + XStart::CENTER => seed_x = build_data.map.width / 2, + XStart::RIGHT => seed_x = build_data.map.width - 2 + } + + match self.y { + YStart::TOP => seed_y = 1, + YStart::CENTER => seed_y = build_data.map.height / 2, + YStart::BOTTOM => seed_y = build_data.map.height - 2 + } + + let mut available_floors : Vec<(usize, f32)> = Vec::new(); + for (idx, tiletype) in build_data.map.tiles.iter().enumerate() { + if map::tile_walkable(*tiletype) { + available_floors.push( + ( + idx, + rltk::DistanceAlg::PythagorasSquared.distance2d( + rltk::Point::new(idx as i32 % build_data.map.width, idx as i32 / build_data.map.width), + rltk::Point::new(seed_x, seed_y) + ) + ) + ); + } + } + if available_floors.is_empty() { + panic!("No valid floors to start on"); + } + + available_floors.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap()); + + let start_x = available_floors[0].0 as i32 % build_data.map.width; + let start_y = available_floors[0].0 as i32 / build_data.map.width; + + build_data.starting_position = Some(Position{x : start_x, y: start_y}); + } +} diff --git a/chapter-75-darkplaza/src/map_builders/bsp_dungeon.rs b/chapter-75-darkplaza/src/map_builders/bsp_dungeon.rs new file mode 100644 index 00000000..06300679 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/bsp_dungeon.rs @@ -0,0 +1,112 @@ +use super::{InitialMapBuilder, BuilderMap, Rect, TileType}; + +pub struct BspDungeonBuilder { + rects: Vec, +} + +impl InitialMapBuilder for BspDungeonBuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl BspDungeonBuilder { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(BspDungeonBuilder{ + rects: Vec::new(), + }) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + let mut rooms : Vec = Vec::new(); + self.rects.clear(); + self.rects.push( Rect::new(2, 2, build_data.map.width-5, build_data.map.height-5) ); // Start with a single map-sized rectangle + let first_room = self.rects[0]; + self.add_subrects(first_room); // Divide the first room + + // Up to 240 times, we get a random rectangle and divide it. If its possible to squeeze a + // room in there, we place it and add it to the rooms list. + let mut n_rooms = 0; + while n_rooms < 240 { + let rect = self.get_random_rect(); + let candidate = self.get_random_sub_rect(rect); + + if self.is_possible(candidate, &build_data, &rooms) { + //apply_room_to_map(&mut build_data.map, &candidate); + rooms.push(candidate); + self.add_subrects(rect); + } + + n_rooms += 1; + } + + build_data.rooms = Some(rooms); + } + + fn add_subrects(&mut self, rect : Rect) { + let width = i32::abs(rect.x1 - rect.x2); + let height = i32::abs(rect.y1 - rect.y2); + let half_width = i32::max(width / 2, 1); + let half_height = i32::max(height / 2, 1); + + self.rects.push(Rect::new( rect.x1, rect.y1, half_width, half_height )); + self.rects.push(Rect::new( rect.x1, rect.y1 + half_height, half_width, half_height )); + self.rects.push(Rect::new( rect.x1 + half_width, rect.y1, half_width, half_height )); + self.rects.push(Rect::new( rect.x1 + half_width, rect.y1 + half_height, half_width, half_height )); + } + + fn get_random_rect(&mut self) -> Rect { + if self.rects.len() == 1 { return self.rects[0]; } + let idx = (crate::rng::roll_dice(1, self.rects.len() as i32)-1) as usize; + self.rects[idx] + } + + fn get_random_sub_rect(&self, rect : Rect) -> Rect { + let mut result = rect; + let rect_width = i32::abs(rect.x1 - rect.x2); + let rect_height = i32::abs(rect.y1 - rect.y2); + + let w = i32::max(3, crate::rng::roll_dice(1, i32::min(rect_width, 20))-1) + 1; + let h = i32::max(3, crate::rng::roll_dice(1, i32::min(rect_height, 20))-1) + 1; + + result.x1 += crate::rng::roll_dice(1, 6)-1; + result.y1 += crate::rng::roll_dice(1, 6)-1; + result.x2 = result.x1 + w; + result.y2 = result.y1 + h; + + result + } + + fn is_possible(&self, rect : Rect, build_data : &BuilderMap, rooms: &[Rect]) -> bool { + let mut expanded = rect; + expanded.x1 -= 2; + expanded.x2 += 2; + expanded.y1 -= 2; + expanded.y2 += 2; + + let mut can_build = true; + + for r in rooms.iter() { + if r.intersect(&rect) { can_build = false; } + } + + for y in expanded.y1 ..= expanded.y2 { + for x in expanded.x1 ..= expanded.x2 { + if x > build_data.map.width-2 { can_build = false; } + if y > build_data.map.height-2 { can_build = false; } + if x < 1 { can_build = false; } + if y < 1 { can_build = false; } + if can_build { + let idx = build_data.map.xy_idx(x, y); + if build_data.map.tiles[idx] != TileType::Wall { + can_build = false; + } + } + } + } + + can_build + } +} diff --git a/chapter-75-darkplaza/src/map_builders/bsp_interior.rs b/chapter-75-darkplaza/src/map_builders/bsp_interior.rs new file mode 100644 index 00000000..bd16692e --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/bsp_interior.rs @@ -0,0 +1,94 @@ +use super::{InitialMapBuilder, BuilderMap, Rect, TileType, draw_corridor}; + +const MIN_ROOM_SIZE : i32 = 8; + +pub struct BspInteriorBuilder { + rects: Vec +} + +impl InitialMapBuilder for BspInteriorBuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl BspInteriorBuilder { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(BspInteriorBuilder{ + rects: Vec::new() + }) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + let mut rooms : Vec = Vec::new(); + self.rects.clear(); + self.rects.push( Rect::new(1, 1, build_data.map.width-2, build_data.map.height-2) ); // Start with a single map-sized rectangle + let first_room = self.rects[0]; + self.add_subrects(first_room); // Divide the first room + + let rooms_copy = self.rects.clone(); + for r in rooms_copy.iter() { + let room = *r; + //room.x2 -= 1; + //room.y2 -= 1; + rooms.push(room); + for y in room.y1 .. room.y2 { + for x in room.x1 .. room.x2 { + let idx = build_data.map.xy_idx(x, y); + if idx > 0 && idx < ((build_data.map.width * build_data.map.height)-1) as usize { + build_data.map.tiles[idx] = TileType::Floor; + } + } + } + build_data.take_snapshot(); + } + + // Now we want corridors + for i in 0..rooms.len()-1 { + let room = rooms[i]; + let next_room = rooms[i+1]; + let start_x = room.x1 + (crate::rng::roll_dice(1, i32::abs(room.x1 - room.x2))-1); + let start_y = room.y1 + (crate::rng::roll_dice(1, i32::abs(room.y1 - room.y2))-1); + let end_x = next_room.x1 + (crate::rng::roll_dice(1, i32::abs(next_room.x1 - next_room.x2))-1); + let end_y = next_room.y1 + (crate::rng::roll_dice(1, i32::abs(next_room.y1 - next_room.y2))-1); + draw_corridor(&mut build_data.map, start_x, start_y, end_x, end_y); + build_data.take_snapshot(); + } + build_data.rooms = Some(rooms); + } + + fn add_subrects(&mut self, rect : Rect) { + // Remove the last rect from the list + if !self.rects.is_empty() { + self.rects.remove(self.rects.len() - 1); + } + + // Calculate boundaries + let width = rect.x2 - rect.x1; + let height = rect.y2 - rect.y1; + let half_width = width / 2; + let half_height = height / 2; + + let split = crate::rng::roll_dice(1, 4); + + if split <= 2 { + // Horizontal split + let h1 = Rect::new( rect.x1, rect.y1, half_width-1, height ); + self.rects.push( h1 ); + if half_width > MIN_ROOM_SIZE { self.add_subrects(h1); } + let h2 = Rect::new( rect.x1 + half_width, rect.y1, half_width, height ); + self.rects.push( h2 ); + if half_width > MIN_ROOM_SIZE { self.add_subrects(h2); } + } else { + // Vertical split + let v1 = Rect::new( rect.x1, rect.y1, width, half_height-1 ); + self.rects.push(v1); + if half_height > MIN_ROOM_SIZE { self.add_subrects(v1); } + let v2 = Rect::new( rect.x1, rect.y1 + half_height, width, half_height ); + self.rects.push(v2); + if half_height > MIN_ROOM_SIZE { self.add_subrects(v2); } + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/cellular_automata.rs b/chapter-75-darkplaza/src/map_builders/cellular_automata.rs new file mode 100644 index 00000000..5fbc2021 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/cellular_automata.rs @@ -0,0 +1,72 @@ +use super::{InitialMapBuilder, MetaMapBuilder, BuilderMap, TileType}; + +pub struct CellularAutomataBuilder {} + +impl InitialMapBuilder for CellularAutomataBuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl MetaMapBuilder for CellularAutomataBuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.apply_iteration(build_data); + } +} + +impl CellularAutomataBuilder { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(CellularAutomataBuilder{}) + } + + fn apply_iteration(&mut self, build_data : &mut BuilderMap) { + let mut newtiles = build_data.map.tiles.clone(); + + for y in 1..build_data.map.height-1 { + for x in 1..build_data.map.width-1 { + let idx = build_data.map.xy_idx(x, y); + let mut neighbors = 0; + if build_data.map.tiles[idx - 1] == TileType::Wall { neighbors += 1; } + if build_data.map.tiles[idx + 1] == TileType::Wall { neighbors += 1; } + if build_data.map.tiles[idx - build_data.map.width as usize] == TileType::Wall { neighbors += 1; } + if build_data.map.tiles[idx + build_data.map.width as usize] == TileType::Wall { neighbors += 1; } + if build_data.map.tiles[idx - (build_data.map.width as usize - 1)] == TileType::Wall { neighbors += 1; } + if build_data.map.tiles[idx - (build_data.map.width as usize + 1)] == TileType::Wall { neighbors += 1; } + if build_data.map.tiles[idx + (build_data.map.width as usize - 1)] == TileType::Wall { neighbors += 1; } + if build_data.map.tiles[idx + (build_data.map.width as usize + 1)] == TileType::Wall { neighbors += 1; } + + if neighbors > 4 || neighbors == 0 { + newtiles[idx] = TileType::Wall; + } + else { + newtiles[idx] = TileType::Floor; + } + } + } + + build_data.map.tiles = newtiles.clone(); + build_data.take_snapshot(); + } + + #[allow(clippy::map_entry)] + fn build(&mut self, build_data : &mut BuilderMap) { + // First we completely randomize the map, setting 55% of it to be floor. + for y in 1..build_data.map.height-1 { + for x in 1..build_data.map.width-1 { + let roll = crate::rng::roll_dice(1, 100); + let idx = build_data.map.xy_idx(x, y); + if roll > 55 { build_data.map.tiles[idx] = TileType::Floor } + else { build_data.map.tiles[idx] = TileType::Wall } + } + } + build_data.take_snapshot(); + + // Now we iteratively apply cellular automata rules + for _i in 0..15 { + self.apply_iteration(build_data); + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/common.rs b/chapter-75-darkplaza/src/map_builders/common.rs new file mode 100644 index 00000000..2d85d348 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/common.rs @@ -0,0 +1,117 @@ +use super::{Map, TileType}; +use std::cmp::{max, min}; + +#[derive(PartialEq, Copy, Clone)] +#[allow(dead_code)] +pub enum Symmetry { None, Horizontal, Vertical, Both } + +pub fn apply_horizontal_tunnel(map : &mut Map, x1:i32, x2:i32, y:i32) -> Vec { + let mut corridor = Vec::new(); + for x in min(x1,x2) ..= max(x1,x2) { + let idx = map.xy_idx(x, y); + if idx > 0 && idx < map.width as usize * map.height as usize && map.tiles[idx as usize] != TileType::Floor { + map.tiles[idx as usize] = TileType::Floor; + corridor.push(idx as usize); + } + } + corridor +} + +pub fn apply_vertical_tunnel(map : &mut Map, y1:i32, y2:i32, x:i32) -> Vec { + let mut corridor = Vec::new(); + for y in min(y1,y2) ..= max(y1,y2) { + let idx = map.xy_idx(x, y); + if idx > 0 && idx < map.width as usize * map.height as usize && map.tiles[idx as usize] != TileType::Floor { + corridor.push(idx); + map.tiles[idx as usize] = TileType::Floor; + } + } + corridor +} + +pub fn draw_corridor(map: &mut Map, x1:i32, y1:i32, x2:i32, y2:i32) -> Vec { + let mut corridor = Vec::new(); + let mut x = x1; + let mut y = y1; + + while x != x2 || y != y2 { + if x < x2 { + x += 1; + } else if x > x2 { + x -= 1; + } else if y < y2 { + y += 1; + } else if y > y2 { + y -= 1; + } + + let idx = map.xy_idx(x, y); + if map.tiles[idx] != TileType::Floor { + corridor.push(idx); + map.tiles[idx] = TileType::Floor; + } + } + + corridor +} + +pub fn paint(map: &mut Map, mode: Symmetry, brush_size: i32, x: i32, y:i32) { + match mode { + Symmetry::None => apply_paint(map, brush_size, x, y), + Symmetry::Horizontal => { + let center_x = map.width / 2; + if x == center_x { + apply_paint(map, brush_size, x, y); + } else { + let dist_x = i32::abs(center_x - x); + apply_paint(map, brush_size, center_x + dist_x, y); + apply_paint(map, brush_size, center_x - dist_x, y); + } + } + Symmetry::Vertical => { + let center_y = map.height / 2; + if y == center_y { + apply_paint(map, brush_size, x, y); + } else { + let dist_y = i32::abs(center_y - y); + apply_paint(map, brush_size, x, center_y + dist_y); + apply_paint(map, brush_size, x, center_y - dist_y); + } + } + Symmetry::Both => { + let center_x = map.width / 2; + let center_y = map.height / 2; + if x == center_x && y == center_y { + apply_paint(map, brush_size, x, y); + } else { + let dist_x = i32::abs(center_x - x); + apply_paint(map, brush_size, center_x + dist_x, y); + apply_paint(map, brush_size, center_x - dist_x, y); + let dist_y = i32::abs(center_y - y); + apply_paint(map, brush_size, x, center_y + dist_y); + apply_paint(map, brush_size, x, center_y - dist_y); + } + } + } +} + +fn apply_paint(map: &mut Map, brush_size: i32, x: i32, y: i32) { + match brush_size { + 1 => { + let digger_idx = map.xy_idx(x, y); + map.tiles[digger_idx] = TileType::Floor; + } + + _ => { + let half_brush_size = brush_size / 2; + for brush_y in y-half_brush_size .. y+half_brush_size { + for brush_x in x-half_brush_size .. x+half_brush_size { + if brush_x > 1 && brush_x < map.width-1 && brush_y > 1 && brush_y < map.height-1 { + let idx = map.xy_idx(brush_x, brush_y); + map.tiles[idx] = TileType::Floor; + } + } + } + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/cull_unreachable.rs b/chapter-75-darkplaza/src/map_builders/cull_unreachable.rs new file mode 100644 index 00000000..9d35d4ff --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/cull_unreachable.rs @@ -0,0 +1,36 @@ +use super::{MetaMapBuilder, BuilderMap, TileType}; + +pub struct CullUnreachable {} + +impl MetaMapBuilder for CullUnreachable { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl CullUnreachable { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(CullUnreachable{}) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + let starting_pos = build_data.starting_position.as_ref().unwrap().clone(); + let start_idx = build_data.map.xy_idx( + starting_pos.x, + starting_pos.y + ); + build_data.map.populate_blocked(); + let map_starts : Vec = vec![start_idx]; + let dijkstra_map = rltk::DijkstraMap::new(build_data.map.width as usize, build_data.map.height as usize, &map_starts , &build_data.map, 1000.0); + for (i, tile) in build_data.map.tiles.iter_mut().enumerate() { + if *tile == TileType::Floor { + let distance_to_start = dijkstra_map.map[i]; + // We can't get to this tile - so we'll make it a wall + if distance_to_start == std::f32::MAX { + *tile = TileType::Wall; + } + } + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/dark_elves.rs b/chapter-75-darkplaza/src/map_builders/dark_elves.rs new file mode 100644 index 00000000..a4d73caa --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/dark_elves.rs @@ -0,0 +1,248 @@ +use super::{BuilderChain, XStart, YStart, AreaStartingPosition, + CullUnreachable, VoronoiSpawning, + AreaEndingPosition, XEnd, YEnd, BspInteriorBuilder }; + +pub fn dark_elf_city(new_depth: i32, width: i32, height: i32) -> BuilderChain { + println!("Dark elf builder"); + let mut chain = BuilderChain::new(new_depth, width, height, "Dark Elven City"); + chain.start_with(BspInteriorBuilder::new()); + chain.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)); + chain.with(CullUnreachable::new()); + chain.with(AreaStartingPosition::new(XStart::RIGHT, YStart::CENTER)); + chain.with(AreaEndingPosition::new(XEnd::LEFT, YEnd::CENTER)); + chain.with(VoronoiSpawning::new()); + chain +} + +pub fn dark_elf_plaza(new_depth: i32, width: i32, height: i32) -> BuilderChain { + println!("Dark elf plaza builder"); + let mut chain = BuilderChain::new(new_depth, width, height, "Dark Elven Plaza"); + chain.start_with(PlazaMapBuilder::new()); + chain.with(AreaStartingPosition::new(XStart::LEFT, YStart::CENTER)); + chain.with(CullUnreachable::new()); + chain +} + +// Plaza Builder +use super::{InitialMapBuilder, BuilderMap, TileType }; + +pub struct PlazaMapBuilder {} + +impl InitialMapBuilder for PlazaMapBuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.empty_map(build_data); + self.spawn_zones(build_data); + } +} + +impl PlazaMapBuilder { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(PlazaMapBuilder{}) + } + + fn empty_map(&mut self, build_data : &mut BuilderMap) { + build_data.map.tiles.iter_mut().for_each(|t| *t = TileType::Floor); + } + + fn spawn_zones(&mut self, build_data : &mut BuilderMap) { + let mut voronoi_seeds : Vec<(usize, rltk::Point)> = Vec::new(); + + while voronoi_seeds.len() < 32 { + let vx = crate::rng::roll_dice(1, build_data.map.width-1); + let vy = crate::rng::roll_dice(1, build_data.map.height-1); + let vidx = build_data.map.xy_idx(vx, vy); + let candidate = (vidx, rltk::Point::new(vx, vy)); + if !voronoi_seeds.contains(&candidate) { + voronoi_seeds.push(candidate); + } + } + + let mut voronoi_distance = vec![(0, 0.0f32) ; 32]; + let mut voronoi_membership : Vec = vec![0 ; build_data.map.width as usize * build_data.map.height as usize]; + for (i, vid) in voronoi_membership.iter_mut().enumerate() { + let x = i as i32 % build_data.map.width; + let y = i as i32 / build_data.map.width; + + for (seed, pos) in voronoi_seeds.iter().enumerate() { + let distance = rltk::DistanceAlg::PythagorasSquared.distance2d( + rltk::Point::new(x, y), + pos.1 + ); + voronoi_distance[seed] = (seed, distance); + } + + voronoi_distance.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap()); + + *vid = voronoi_distance[0].0 as i32; + } + + // Make a list of zone sizes and cull empty ones + let mut zone_sizes : Vec<(i32, usize)> = Vec::with_capacity(32); + for zone in 0..32 { + let num_tiles = voronoi_membership.iter().filter(|z| **z == zone).count(); + if num_tiles > 0 { + zone_sizes.push((zone, num_tiles)); + } + } + zone_sizes.sort_by(|a,b| b.1.cmp(&a.1)); + + // Start making zonal terrain + zone_sizes.iter().enumerate().for_each(|(i, (zone, _))| { + match i { + 0 => self.portal_park(build_data, &voronoi_membership, *zone, &voronoi_seeds), + 1 | 2 => self.park(build_data, &voronoi_membership, *zone, &voronoi_seeds), + i if i > 20 => self.fill_zone(build_data, &voronoi_membership, *zone, TileType::Wall), + _ => { + let roll = crate::rng::roll_dice(1, 6); + match roll { + 1 => self.fill_zone(build_data, &voronoi_membership, *zone, TileType::DeepWater), + 2 => self.fill_zone(build_data, &voronoi_membership, *zone, TileType::ShallowWater), + 3 => self.stalactite_display(build_data, &voronoi_membership, *zone), + _ => {} + } + } + } + }); + + // Clear the path + self.make_roads(build_data, &voronoi_membership); + } + + fn portal_park(&mut self, build_data : &mut BuilderMap, voronoi_membership: &[i32], zone: i32, seeds: &[(usize, rltk::Point)]) { + let zone_tiles : Vec = voronoi_membership + .iter() + .enumerate() + .filter(|(_, tile_zone)| **tile_zone == zone) + .map(|(idx, _)| idx) + .collect(); + + // Start all gravel + zone_tiles.iter().for_each(|idx| build_data.map.tiles[*idx] = TileType::Gravel); + + // Add the exit + let center = seeds[zone as usize].1; + let idx = build_data.map.xy_idx(center.x, center.y); + build_data.map.tiles[idx] = TileType::DownStairs; + + // Add some altars around the exit + let altars = [ + build_data.map.xy_idx(center.x - 2, center.y), + build_data.map.xy_idx(center.x + 2, center.y), + build_data.map.xy_idx(center.x, center.y - 2), + build_data.map.xy_idx(center.x, center.y + 2), + ]; + altars.iter().for_each(|idx| build_data.spawn_list.push((*idx, "Altar".to_string()))); + + let demon_spawn = build_data.map.xy_idx(center.x+1, center.y+1); + build_data.spawn_list.push((demon_spawn, "Vokoth".to_string())); + } + + fn fill_zone(&mut self, build_data : &mut BuilderMap, voronoi_membership: &[i32], zone: i32, tile_type: TileType) { + voronoi_membership + .iter() + .enumerate() + .filter(|(_, tile_zone)| **tile_zone == zone) + .for_each(|(idx, _)| build_data.map.tiles[idx] = tile_type); + } + + fn stalactite_display(&mut self, build_data : &mut BuilderMap, voronoi_membership: &[i32], zone: i32) { + voronoi_membership + .iter() + .enumerate() + .filter(|(_, tile_zone)| **tile_zone == zone) + .for_each(|(idx, _)| { + build_data.map.tiles[idx] = match crate::rng::roll_dice(1,10) { + 1 => TileType::Stalactite, + 2 => TileType::Stalagmite, + _ => TileType::Grass, + }; + }); + } + + fn park(&mut self, build_data : &mut BuilderMap, voronoi_membership: &[i32], zone: i32, seeds: &[(usize, rltk::Point)]) { + let zone_tiles : Vec = voronoi_membership + .iter() + .enumerate() + .filter(|(_, tile_zone)| **tile_zone == zone) + .map(|(idx, _)| idx) + .collect(); + + // Start all grass + zone_tiles.iter().for_each(|idx| build_data.map.tiles[*idx] = TileType::Grass); + + // Add a stone area in the middle + let center = seeds[zone as usize].1; + for y in center.y-2 ..= center.y+2 { + for x in center.x-2 ..= center.x+2 { + let idx = build_data.map.xy_idx(x, y); + build_data.map.tiles[idx] = TileType::Road; + if crate::rng::roll_dice(1,6) > 2 { + build_data.map.bloodstains.insert(idx); + } + } + } + + // With an altar at the center + build_data.spawn_list.push(( + build_data.map.xy_idx(center.x, center.y), + "Altar".to_string() + )); + + // And chairs for spectators, and the spectators themselves + let available_enemies = match crate::rng::roll_dice(1, 3) { + 1 => vec![ + "Arbat Dark Elf", + "Arbat Dark Elf Leader", + "Arbat Orc Slave", + ], + 2 => vec![ + "Barbo Dark Elf", + "Barbo Goblin Archer", + ], + _ => vec![ + "Cirro Dark Elf", + "Cirro Dark Priestess", + "Cirro Spider", + ] + }; + + zone_tiles.iter().for_each(|idx| { + if build_data.map.tiles[*idx] == TileType::Grass { + match crate::rng::roll_dice(1, 10) { + 1 => build_data.spawn_list.push(( + *idx, + "Chair".to_string() + )), + 2 => { + let to_spawn = crate::rng::range(0, available_enemies.len() as i32); + build_data.spawn_list.push(( + *idx, + available_enemies[to_spawn as usize].to_string() + )); + } + _ => {} + } + } + }); + } + + fn make_roads(&mut self, build_data : &mut BuilderMap, voronoi_membership: &[i32]) { + for y in 1..build_data.map.height-1 { + for x in 1..build_data.map.width-1 { + let mut neighbors = 0; + let my_idx = build_data.map.xy_idx(x, y); + let my_seed = voronoi_membership[my_idx]; + if voronoi_membership[build_data.map.xy_idx(x-1, y)] != my_seed { neighbors += 1; } + if voronoi_membership[build_data.map.xy_idx(x+1, y)] != my_seed { neighbors += 1; } + if voronoi_membership[build_data.map.xy_idx(x, y-1)] != my_seed { neighbors += 1; } + if voronoi_membership[build_data.map.xy_idx(x, y+1)] != my_seed { neighbors += 1; } + + if neighbors > 1 { + build_data.map.tiles[my_idx] = TileType::Road; + } + } + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/distant_exit.rs b/chapter-75-darkplaza/src/map_builders/distant_exit.rs new file mode 100644 index 00000000..06e17e69 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/distant_exit.rs @@ -0,0 +1,45 @@ +use super::{MetaMapBuilder, BuilderMap, TileType}; + +pub struct DistantExit {} + +impl MetaMapBuilder for DistantExit { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl DistantExit { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(DistantExit{}) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + let starting_pos = build_data.starting_position.as_ref().unwrap().clone(); + let start_idx = build_data.map.xy_idx( + starting_pos.x, + starting_pos.y + ); + build_data.map.populate_blocked(); + let map_starts : Vec = vec![start_idx]; + let dijkstra_map = rltk::DijkstraMap::new(build_data.map.width as usize, build_data.map.height as usize, &map_starts , &build_data.map, 3000.0); + let mut exit_tile = (0, 0.0f32); + for (i, tile) in build_data.map.tiles.iter_mut().enumerate() { + if *tile == TileType::Floor { + let distance_to_start = dijkstra_map.map[i]; + if distance_to_start != std::f32::MAX { + // If it is further away than our current exit candidate, move the exit + if distance_to_start > exit_tile.1 { + exit_tile.0 = i; + exit_tile.1 = distance_to_start; + } + } + } + } + + // Place a staircase + let stairs_idx = exit_tile.0; + build_data.map.tiles[stairs_idx] = TileType::DownStairs; + build_data.take_snapshot(); + } +} diff --git a/chapter-75-darkplaza/src/map_builders/dla.rs b/chapter-75-darkplaza/src/map_builders/dla.rs new file mode 100644 index 00000000..e281af8a --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/dla.rs @@ -0,0 +1,177 @@ +use super::{InitialMapBuilder, MetaMapBuilder, BuilderMap, TileType, Position, Symmetry, paint}; + +#[derive(PartialEq, Copy, Clone)] +#[allow(dead_code)] +pub enum DLAAlgorithm { WalkInwards, WalkOutwards, CentralAttractor } + +pub struct DLABuilder { + algorithm : DLAAlgorithm, + brush_size: i32, + symmetry: Symmetry, + floor_percent: f32, +} + + +impl InitialMapBuilder for DLABuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl MetaMapBuilder for DLABuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl DLABuilder { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(DLABuilder{ + algorithm: DLAAlgorithm::WalkInwards, + brush_size: 2, + symmetry: Symmetry::None, + floor_percent: 0.25, + }) + } + + #[allow(dead_code)] + pub fn walk_inwards() -> Box { + Box::new(DLABuilder{ + algorithm: DLAAlgorithm::WalkInwards, + brush_size: 1, + symmetry: Symmetry::None, + floor_percent: 0.25, + }) + } + + #[allow(dead_code)] + pub fn walk_outwards() -> Box { + Box::new(DLABuilder{ + algorithm: DLAAlgorithm::WalkOutwards, + brush_size: 2, + symmetry: Symmetry::None, + floor_percent: 0.25, + }) + } + + #[allow(dead_code)] + pub fn heavy_erosion() -> Box { + Box::new(DLABuilder{ + algorithm: DLAAlgorithm::WalkInwards, + brush_size: 2, + symmetry: Symmetry::None, + floor_percent: 0.35, + }) + } + + #[allow(dead_code)] + pub fn central_attractor() -> Box { + Box::new(DLABuilder{ + algorithm: DLAAlgorithm::CentralAttractor, + brush_size: 2, + symmetry: Symmetry::None, + floor_percent: 0.25, + }) + } + + #[allow(dead_code)] + pub fn insectoid() -> Box { + Box::new(DLABuilder{ + algorithm: DLAAlgorithm::CentralAttractor, + brush_size: 2, + symmetry: Symmetry::Horizontal, + floor_percent: 0.25, + }) + } + + #[allow(clippy::map_entry)] + fn build(&mut self, build_data : &mut BuilderMap) { + // Carve a starting seed + let starting_position = Position{ x: build_data.map.width/2, y : build_data.map.height/2 }; + let start_idx = build_data.map.xy_idx(starting_position.x, starting_position.y); + build_data.take_snapshot(); + build_data.map.tiles[start_idx] = TileType::Floor; + build_data.map.tiles[start_idx-1] = TileType::Floor; + build_data.map.tiles[start_idx+1] = TileType::Floor; + build_data.map.tiles[start_idx-build_data.map.width as usize] = TileType::Floor; + build_data.map.tiles[start_idx+build_data.map.width as usize] = TileType::Floor; + + // Random walker + let total_tiles = build_data.map.width * build_data.map.height; + let desired_floor_tiles = (self.floor_percent * total_tiles as f32) as usize; + let mut floor_tile_count = build_data.map.tiles.iter().filter(|a| **a == TileType::Floor).count(); + while floor_tile_count < desired_floor_tiles { + + match self.algorithm { + DLAAlgorithm::WalkInwards => { + let mut digger_x = crate::rng::roll_dice(1, build_data.map.width - 3) + 1; + let mut digger_y = crate::rng::roll_dice(1, build_data.map.height - 3) + 1; + let mut prev_x = digger_x; + let mut prev_y = digger_y; + let mut digger_idx = build_data.map.xy_idx(digger_x, digger_y); + while build_data.map.tiles[digger_idx] == TileType::Wall { + prev_x = digger_x; + prev_y = digger_y; + let stagger_direction = crate::rng::roll_dice(1, 4); + match stagger_direction { + 1 => { if digger_x > 2 { digger_x -= 1; } } + 2 => { if digger_x < build_data.map.width-2 { digger_x += 1; } } + 3 => { if digger_y > 2 { digger_y -=1; } } + _ => { if digger_y < build_data.map.height-2 { digger_y += 1; } } + } + digger_idx = build_data.map.xy_idx(digger_x, digger_y); + } + paint(&mut build_data.map, self.symmetry, self.brush_size, prev_x, prev_y); + } + + DLAAlgorithm::WalkOutwards => { + let mut digger_x = starting_position.x; + let mut digger_y = starting_position.y; + let mut digger_idx = build_data.map.xy_idx(digger_x, digger_y); + while build_data.map.tiles[digger_idx] == TileType::Floor { + let stagger_direction = crate::rng::roll_dice(1, 4); + match stagger_direction { + 1 => { if digger_x > 2 { digger_x -= 1; } } + 2 => { if digger_x < build_data.map.width-2 { digger_x += 1; } } + 3 => { if digger_y > 2 { digger_y -=1; } } + _ => { if digger_y < build_data.map.height-2 { digger_y += 1; } } + } + digger_idx = build_data.map.xy_idx(digger_x, digger_y); + } + paint(&mut build_data.map, self.symmetry, self.brush_size, digger_x, digger_y); + } + + DLAAlgorithm::CentralAttractor => { + let mut digger_x = crate::rng::roll_dice(1, build_data.map.width - 3) + 1; + let mut digger_y = crate::rng::roll_dice(1, build_data.map.height - 3) + 1; + let mut prev_x = digger_x; + let mut prev_y = digger_y; + let mut digger_idx = build_data.map.xy_idx(digger_x, digger_y); + + let mut path = rltk::line2d( + rltk::LineAlg::Bresenham, + rltk::Point::new( digger_x, digger_y ), + rltk::Point::new( starting_position.x, starting_position.y ) + ); + + while build_data.map.tiles[digger_idx] == TileType::Wall && !path.is_empty() { + prev_x = digger_x; + prev_y = digger_y; + digger_x = path[0].x; + digger_y = path[0].y; + path.remove(0); + digger_idx = build_data.map.xy_idx(digger_x, digger_y); + } + paint(&mut build_data.map, self.symmetry, self.brush_size, prev_x, prev_y); + } + } + + build_data.take_snapshot(); + + floor_tile_count = build_data.map.tiles.iter().filter(|a| **a == TileType::Floor).count(); + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/door_placement.rs b/chapter-75-darkplaza/src/map_builders/door_placement.rs new file mode 100644 index 00000000..a73e3c2a --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/door_placement.rs @@ -0,0 +1,71 @@ +use super::{MetaMapBuilder, BuilderMap, TileType }; + +pub struct DoorPlacement {} + +impl MetaMapBuilder for DoorPlacement { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.doors(build_data); + } +} + +impl DoorPlacement { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(DoorPlacement{ }) + } + + fn door_possible(&self, build_data : &mut BuilderMap, idx : usize) -> bool { + let mut blocked = false; + for spawn in build_data.spawn_list.iter() { + if spawn.0 == idx { blocked = true; } + } + if blocked { return false; } + + let x = (idx % build_data.map.width as usize) as i32; + let y = (idx / build_data.map.width as usize) as i32; + + // Check for east-west door possibility + if build_data.map.tiles[idx] == TileType::Floor && + (x > 1 && build_data.map.tiles[idx-1] == TileType::Floor) && + (x < build_data.map.width-2 && build_data.map.tiles[idx+1] == TileType::Floor) && + (y > 1 && build_data.map.tiles[idx - build_data.map.width as usize] == TileType::Wall) && + (y < build_data.map.height-2 && build_data.map.tiles[idx + build_data.map.width as usize] == TileType::Wall) + { + return true; + } + + // Check for north-south door possibility + if build_data.map.tiles[idx] == TileType::Floor && + (x > 1 && build_data.map.tiles[idx-1] == TileType::Wall) && + (x < build_data.map.width-2 && build_data.map.tiles[idx+1] == TileType::Wall) && + (y > 1 && build_data.map.tiles[idx - build_data.map.width as usize] == TileType::Floor) && + (y < build_data.map.height-2 && build_data.map.tiles[idx + build_data.map.width as usize] == TileType::Floor) + { + return true; + } + + false + } + + fn doors(&mut self, build_data : &mut BuilderMap) { + if let Some(halls_original) = &build_data.corridors { + let halls = halls_original.clone(); // To avoid nested borrowing + for hall in halls.iter() { + if hall.len() > 2 { // We aren't interested in tiny corridors + if self.door_possible(build_data, hall[0]) { + build_data.spawn_list.push((hall[0], "Door".to_string())); + } + } + } + } else { + // There are no corridors - scan for possible places + let tiles = build_data.map.tiles.clone(); + for (i, tile) in tiles.iter().enumerate() { + if *tile == TileType::Floor && self.door_possible(build_data, i) && crate::rng::roll_dice(1,3)==1 { + build_data.spawn_list.push((i, "Door".to_string())); + } + } + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/drunkard.rs b/chapter-75-darkplaza/src/map_builders/drunkard.rs new file mode 100644 index 00000000..fe615679 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/drunkard.rs @@ -0,0 +1,168 @@ +use super::{InitialMapBuilder, MetaMapBuilder, BuilderMap, TileType, Position, paint, Symmetry}; + +#[derive(PartialEq, Copy, Clone)] +#[allow(dead_code)] +pub enum DrunkSpawnMode { StartingPoint, Random } + +pub struct DrunkardSettings { + pub spawn_mode : DrunkSpawnMode, + pub drunken_lifetime : i32, + pub floor_percent: f32, + pub brush_size: i32, + pub symmetry: Symmetry +} + +pub struct DrunkardsWalkBuilder { + settings : DrunkardSettings +} + +impl InitialMapBuilder for DrunkardsWalkBuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl MetaMapBuilder for DrunkardsWalkBuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl DrunkardsWalkBuilder { + #[allow(dead_code)] + pub fn new(settings: DrunkardSettings) -> DrunkardsWalkBuilder { + DrunkardsWalkBuilder{ + settings + } + } + + #[allow(dead_code)] + pub fn open_area() -> Box { + Box::new(DrunkardsWalkBuilder{ + settings : DrunkardSettings{ + spawn_mode: DrunkSpawnMode::StartingPoint, + drunken_lifetime: 400, + floor_percent: 0.5, + brush_size: 1, + symmetry: Symmetry::None + } + }) + } + + #[allow(dead_code)] + pub fn open_halls() -> Box { + Box::new(DrunkardsWalkBuilder{ + settings : DrunkardSettings{ + spawn_mode: DrunkSpawnMode::Random, + drunken_lifetime: 400, + floor_percent: 0.5, + brush_size: 1, + symmetry: Symmetry::None + }, + }) + } + + #[allow(dead_code)] + pub fn winding_passages() -> Box { + Box::new(DrunkardsWalkBuilder{ + settings : DrunkardSettings{ + spawn_mode: DrunkSpawnMode::Random, + drunken_lifetime: 100, + floor_percent: 0.4, + brush_size: 1, + symmetry: Symmetry::None + }, + }) + } + + #[allow(dead_code)] + pub fn fat_passages() -> Box { + Box::new(DrunkardsWalkBuilder{ + settings : DrunkardSettings{ + spawn_mode: DrunkSpawnMode::Random, + drunken_lifetime: 100, + floor_percent: 0.4, + brush_size: 2, + symmetry: Symmetry::None + }, + }) + } + + #[allow(dead_code)] + pub fn fearful_symmetry() -> Box { + Box::new(DrunkardsWalkBuilder{ + settings : DrunkardSettings{ + spawn_mode: DrunkSpawnMode::Random, + drunken_lifetime: 100, + floor_percent: 0.4, + brush_size: 1, + symmetry: Symmetry::Both + }, + }) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + // Set a central starting point + let starting_position = Position{ x: build_data.map.width / 2, y: build_data.map.height / 2 }; + let start_idx = build_data.map.xy_idx(starting_position.x, starting_position.y); + build_data.map.tiles[start_idx] = TileType::Floor; + + let total_tiles = build_data.map.width * build_data.map.height; + let desired_floor_tiles = (self.settings.floor_percent * total_tiles as f32) as usize; + let mut floor_tile_count = build_data.map.tiles.iter().filter(|a| **a == TileType::Floor).count(); + let mut digger_count = 0; + while floor_tile_count < desired_floor_tiles { + let mut did_something = false; + let mut drunk_x; + let mut drunk_y; + match self.settings.spawn_mode { + DrunkSpawnMode::StartingPoint => { + drunk_x = starting_position.x; + drunk_y = starting_position.y; + } + DrunkSpawnMode::Random => { + if digger_count == 0 { + drunk_x = starting_position.x; + drunk_y = starting_position.y; + } else { + drunk_x = crate::rng::roll_dice(1, build_data.map.width - 3) + 1; + drunk_y = crate::rng::roll_dice(1, build_data.map.height - 3) + 1; + } + } + } + let mut drunk_life = self.settings.drunken_lifetime; + + while drunk_life > 0 { + let drunk_idx = build_data.map.xy_idx(drunk_x, drunk_y); + if build_data.map.tiles[drunk_idx] == TileType::Wall { + did_something = true; + } + paint(&mut build_data.map, self.settings.symmetry, self.settings.brush_size, drunk_x, drunk_y); + build_data.map.tiles[drunk_idx] = TileType::DownStairs; + + let stagger_direction = crate::rng::roll_dice(1, 4); + match stagger_direction { + 1 => { if drunk_x > 2 { drunk_x -= 1; } } + 2 => { if drunk_x < build_data.map.width-2 { drunk_x += 1; } } + 3 => { if drunk_y > 2 { drunk_y -=1; } } + _ => { if drunk_y < build_data.map.height-2 { drunk_y += 1; } } + } + + drunk_life -= 1; + } + if did_something { + build_data.take_snapshot(); + } + + digger_count += 1; + for t in build_data.map.tiles.iter_mut() { + if *t == TileType::DownStairs { + *t = TileType::Floor; + } + } + floor_tile_count = build_data.map.tiles.iter().filter(|a| **a == TileType::Floor).count(); + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/dwarf_fort_builder.rs b/chapter-75-darkplaza/src/map_builders/dwarf_fort_builder.rs new file mode 100644 index 00000000..dccff8eb --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/dwarf_fort_builder.rs @@ -0,0 +1,119 @@ +use super::{BuilderChain, XStart, YStart, AreaStartingPosition, RoomSorter, RoomSort, + CullUnreachable, VoronoiSpawning, BspDungeonBuilder, DistantExit, BspCorridors, + CorridorSpawner, RoomDrawer, BuilderMap, MetaMapBuilder, DLABuilder, TileType, + AreaEndingPosition, XEnd, YEnd}; + +pub fn dwarf_fort_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { + let mut chain = BuilderChain::new(new_depth, width, height, "Dwarven Fortress"); + chain.start_with(BspDungeonBuilder::new()); + chain.with(RoomSorter::new(RoomSort::CENTRAL)); + chain.with(RoomDrawer::new()); + chain.with(BspCorridors::new()); + chain.with(CorridorSpawner::new()); + chain.with(DragonsLair::new()); + + chain.with(AreaStartingPosition::new(XStart::LEFT, YStart::TOP)); + chain.with(CullUnreachable::new()); + chain.with(AreaEndingPosition::new(XEnd::RIGHT, YEnd::BOTTOM)); + chain.with(VoronoiSpawning::new()); + chain.with(DistantExit::new()); + chain.with(DragonSpawner::new()); + chain +} + +pub struct DragonsLair {} + +impl MetaMapBuilder for DragonsLair { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl DragonsLair { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(DragonsLair{}) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + build_data.map.depth = 7; + build_data.take_snapshot(); + + let mut builder = BuilderChain::new(6, build_data.width, build_data.height, "New Map"); + builder.start_with(DLABuilder::insectoid()); + builder.build_map(); + + // Add the history to our history + for h in builder.build_data.history.iter() { + build_data.history.push(h.clone()); + } + build_data.take_snapshot(); + + // Merge the maps + for (idx, tt) in build_data.map.tiles.iter_mut().enumerate() { + if *tt == TileType::Wall && builder.build_data.map.tiles[idx] == TileType::Floor { + *tt = TileType::Floor; + } + } + build_data.take_snapshot(); + } +} + +pub struct DragonSpawner {} + +impl MetaMapBuilder for DragonSpawner { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl DragonSpawner { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(DragonSpawner{}) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + // Find a central location that isn't occupied + let seed_x = build_data.map.width / 2; + let seed_y = build_data.map.height / 2; + let mut available_floors : Vec<(usize, f32)> = Vec::new(); + for (idx, tiletype) in build_data.map.tiles.iter().enumerate() { + if crate::map::tile_walkable(*tiletype) { + available_floors.push( + ( + idx, + rltk::DistanceAlg::PythagorasSquared.distance2d( + rltk::Point::new(idx as i32 % build_data.map.width, idx as i32 / build_data.map.width), + rltk::Point::new(seed_x, seed_y) + ) + ) + ); + } + } + if available_floors.is_empty() { + panic!("No valid floors to start on"); + } + + available_floors.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap()); + + let start_x = available_floors[0].0 as i32 % build_data.map.width; + let start_y = available_floors[0].0 as i32 / build_data.map.width; + let dragon_pt = rltk::Point::new(start_x, start_y); + + // Remove all spawns within 25 tiles of the drake + let w = build_data.map.width as i32; + build_data.spawn_list.retain(|spawn| { + let spawn_pt = rltk::Point::new( + spawn.0 as i32 % w, + spawn.0 as i32 / w + ); + let distance = rltk::DistanceAlg::Pythagoras.distance2d(dragon_pt, spawn_pt); + distance > 25.0 + }); + + // Add the dragon + let dragon_idx = build_data.map.xy_idx(start_x, start_y); + build_data.spawn_list.push((dragon_idx, "Black Dragon".to_string())); + } +} diff --git a/chapter-75-darkplaza/src/map_builders/forest.rs b/chapter-75-darkplaza/src/map_builders/forest.rs new file mode 100644 index 00000000..c04fd571 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/forest.rs @@ -0,0 +1,113 @@ +use super::{BuilderChain, CellularAutomataBuilder, XStart, YStart, AreaStartingPosition, + CullUnreachable, VoronoiSpawning, MetaMapBuilder, BuilderMap, TileType}; +use crate::map; + +pub fn forest_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { + let mut chain = BuilderChain::new(new_depth, width, height, "Into the Woods"); + chain.start_with(CellularAutomataBuilder::new()); + chain.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)); + chain.with(CullUnreachable::new()); + chain.with(AreaStartingPosition::new(XStart::LEFT, YStart::CENTER)); + chain.with(VoronoiSpawning::new()); + chain.with(YellowBrickRoad::new()); + chain +} + +pub struct YellowBrickRoad {} + +impl MetaMapBuilder for YellowBrickRoad { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl YellowBrickRoad { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(YellowBrickRoad{}) + } + + fn find_exit(&self, build_data : &mut BuilderMap, seed_x : i32, seed_y: i32) -> (i32, i32) { + let mut available_floors : Vec<(usize, f32)> = Vec::new(); + for (idx, tiletype) in build_data.map.tiles.iter().enumerate() { + if map::tile_walkable(*tiletype) { + available_floors.push( + ( + idx, + rltk::DistanceAlg::PythagorasSquared.distance2d( + rltk::Point::new(idx as i32 % build_data.map.width, idx as i32 / build_data.map.width), + rltk::Point::new(seed_x, seed_y) + ) + ) + ); + } + } + if available_floors.is_empty() { + panic!("No valid floors to start on"); + } + + available_floors.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap()); + + let end_x = available_floors[0].0 as i32 % build_data.map.width; + let end_y = available_floors[0].0 as i32 / build_data.map.width; + (end_x, end_y) + } + + fn paint_road(&self, build_data : &mut BuilderMap, x: i32, y: i32) { + if x < 1 || x > build_data.map.width-2 || y < 1 || y > build_data.map.height-2 { + return; + } + let idx = build_data.map.xy_idx(x, y); + if build_data.map.tiles[idx] != TileType::DownStairs { + build_data.map.tiles[idx] = TileType::Road; + } + } + + fn build(&mut self, build_data : &mut BuilderMap) { + let starting_pos = build_data.starting_position.as_ref().unwrap().clone(); + let start_idx = build_data.map.xy_idx(starting_pos.x, starting_pos.y); + + let (end_x, end_y) = self.find_exit(build_data, build_data.map.width - 2, build_data.map.height / 2); + let end_idx = build_data.map.xy_idx(end_x, end_y); + + build_data.map.populate_blocked(); + let path = rltk::a_star_search(start_idx, end_idx, &build_data.map); + if !path.success { + panic!("No valid path for the road"); + } + for idx in path.steps.iter() { + let x = *idx as i32 % build_data.map.width; + let y = *idx as i32 / build_data.map.width; + self.paint_road(build_data, x, y); + self.paint_road(build_data, x-1, y); + self.paint_road(build_data, x+1, y); + self.paint_road(build_data, x, y-1); + self.paint_road(build_data, x, y+1); + } + build_data.map.tiles[end_idx] = TileType::DownStairs; + build_data.take_snapshot(); + + // Place exit + let exit_dir = crate::rng::roll_dice(1, 2); + let (seed_x, seed_y, stream_startx, stream_starty) = if exit_dir == 1 { + (build_data.map.width-1, 1, 0, build_data.height-1) + } else { + (build_data.map.width-1, build_data.height-1, 1, build_data.height-1) + }; + + let (stairs_x, stairs_y) = self.find_exit(build_data, seed_x, seed_y); + let stairs_idx = build_data.map.xy_idx(stairs_x, stairs_y); + build_data.take_snapshot(); + + let (stream_x, stream_y) = self.find_exit(build_data, stream_startx, stream_starty); + let stream_idx = build_data.map.xy_idx(stream_x, stream_y) as usize; + let stream = rltk::a_star_search(stairs_idx, stream_idx, &mut build_data.map); + for tile in stream.steps.iter() { + if build_data.map.tiles[*tile as usize] == TileType::Floor { + build_data.map.tiles[*tile as usize] = TileType::ShallowWater; + } + } + build_data.map.tiles[stairs_idx] = TileType::DownStairs; + build_data.take_snapshot(); + } +} diff --git a/chapter-75-darkplaza/src/map_builders/limestone_cavern.rs b/chapter-75-darkplaza/src/map_builders/limestone_cavern.rs new file mode 100644 index 00000000..c7ae7502 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/limestone_cavern.rs @@ -0,0 +1,152 @@ +use super::{BuilderChain, DrunkardsWalkBuilder, XStart, YStart, AreaStartingPosition, + CullUnreachable, VoronoiSpawning, MetaMapBuilder, BuilderMap, TileType, DistantExit, + DLABuilder, PrefabBuilder, CellularAutomataBuilder, AreaEndingPosition, + BspDungeonBuilder, RoomSorter, RoomSort, NearestCorridors, RoomExploder, RoomDrawer, + RoomBasedSpawner, XEnd, YEnd}; + +pub fn limestone_cavern_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { + let mut chain = BuilderChain::new(new_depth, width, height, "Limestone Caverns"); + chain.start_with(DrunkardsWalkBuilder::winding_passages()); + chain.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)); + chain.with(CullUnreachable::new()); + chain.with(AreaStartingPosition::new(XStart::LEFT, YStart::CENTER)); + chain.with(VoronoiSpawning::new()); + chain.with(DistantExit::new()); + chain.with(CaveDecorator::new()); + chain +} + +pub fn limestone_deep_cavern_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { + let mut chain = BuilderChain::new(new_depth, width, height, "Deep Limestone Caverns"); + chain.start_with(DLABuilder::central_attractor()); + chain.with(AreaStartingPosition::new(XStart::LEFT, YStart::TOP)); + chain.with(VoronoiSpawning::new()); + chain.with(DistantExit::new()); + chain.with(CaveDecorator::new()); + chain.with(PrefabBuilder::sectional(super::prefab_builder::prefab_sections::ORC_CAMP)); + chain +} + +pub fn limestone_transition_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { + let mut chain = BuilderChain::new(new_depth, width, height, "Dwarf Fort - Upper Reaches"); + chain.start_with(CellularAutomataBuilder::new()); + chain.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)); + chain.with(CullUnreachable::new()); + chain.with(AreaStartingPosition::new(XStart::LEFT, YStart::CENTER)); + chain.with(VoronoiSpawning::new()); + chain.with(CaveDecorator::new()); + chain.with(CaveTransition::new()); + chain.with(AreaStartingPosition::new(XStart::LEFT, YStart::CENTER)); + chain.with(CullUnreachable::new()); + chain.with(AreaEndingPosition::new(XEnd::RIGHT, YEnd::CENTER)); + chain +} + +pub struct CaveDecorator {} + +impl MetaMapBuilder for CaveDecorator { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl CaveDecorator { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(CaveDecorator{}) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + let old_map = build_data.map.clone(); + for (idx,tt) in build_data.map.tiles.iter_mut().enumerate() { + // Gravel Spawning + if *tt == TileType::Floor && crate::rng::roll_dice(1, 6)==1 { + *tt = TileType::Gravel; + } else if *tt == TileType::Floor && crate::rng::roll_dice(1, 10)==1 { + // Spawn passable pools + *tt = TileType::ShallowWater; + } else if *tt == TileType::Wall { + // Spawn deep pools and stalactites + let mut neighbors = 0; + let x = idx as i32 % old_map.width; + let y = idx as i32 / old_map.width; + if x > 0 && old_map.tiles[idx-1] == TileType::Wall { neighbors += 1; } + if x < old_map.width - 2 && old_map.tiles[idx+1] == TileType::Wall { neighbors += 1; } + if y > 0 && old_map.tiles[idx-old_map.width as usize] == TileType::Wall { neighbors += 1; } + if y < old_map.height - 2 && old_map.tiles[idx+old_map.width as usize] == TileType::Wall { neighbors += 1; } + if neighbors == 2 { + *tt = TileType::DeepWater; + } else if neighbors == 1 { + let roll = crate::rng::roll_dice(1, 4); + match roll { + 1 => *tt = TileType::Stalactite, + 2 => *tt = TileType::Stalagmite, + _ => {} + } + } + } + } + build_data.take_snapshot(); + build_data.map.outdoors = false; + } +} + +pub struct CaveTransition {} + +impl MetaMapBuilder for CaveTransition { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl CaveTransition { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(CaveTransition{}) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + build_data.map.depth = 5; + build_data.take_snapshot(); + + // Build a BSP-based dungeon + let mut builder = BuilderChain::new(5, build_data.width, build_data.height, "New Map"); + builder.start_with(BspDungeonBuilder::new()); + builder.with(RoomDrawer::new()); + builder.with(RoomSorter::new(RoomSort::RIGHTMOST)); + builder.with(NearestCorridors::new()); + builder.with(RoomExploder::new()); + builder.with(RoomBasedSpawner::new()); + builder.build_map(); + + // Add the history to our history + for h in builder.build_data.history.iter() { + build_data.history.push(h.clone()); + } + build_data.take_snapshot(); + + // Copy the right half of the BSP map into our map + for x in build_data.map.width / 2 .. build_data.map.width { + for y in 0 .. build_data.map.height { + let idx = build_data.map.xy_idx(x, y); + build_data.map.tiles[idx] = builder.build_data.map.tiles[idx]; + } + } + build_data.take_snapshot(); + + // Keep Voronoi spawn data from the left half of the map + let w = build_data.map.width; + build_data.spawn_list.retain(|s| { + let x = s.0 as i32 / w; + x < w / 2 + }); + + // Keep room spawn data from the right half of the map + for s in builder.build_data.spawn_list.iter() { + let x = s.0 as i32 / w; + if x > w / 2 { + build_data.spawn_list.push(s.clone()); + } + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/maze.rs b/chapter-75-darkplaza/src/map_builders/maze.rs new file mode 100644 index 00000000..08cb1765 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/maze.rs @@ -0,0 +1,197 @@ +use super::{Map, InitialMapBuilder, BuilderMap, TileType}; + +pub struct MazeBuilder {} + +impl InitialMapBuilder for MazeBuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl MazeBuilder { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(MazeBuilder{}) + } + + #[allow(clippy::map_entry)] + fn build(&mut self, build_data : &mut BuilderMap) { + // Maze gen + let mut maze = Grid::new((build_data.map.width / 2)-2, (build_data.map.height / 2)-2); + maze.generate_maze(build_data); + } +} + +/* Maze code taken under MIT from https://github.com/cyucelen/mazeGenerator/ */ + +const TOP : usize = 0; +const RIGHT : usize = 1; +const BOTTOM : usize = 2; +const LEFT : usize = 3; + +#[derive(Copy, Clone)] +struct Cell { + row: i32, + column: i32, + walls: [bool; 4], + visited: bool, +} + +impl Cell { + fn new(row: i32, column: i32) -> Cell { + Cell{ + row, + column, + walls: [true, true, true, true], + visited: false + } + } + + fn remove_walls(&mut self, next : &mut Cell) { + let x = self.column - next.column; + let y = self.row - next.row; + + if x == 1 { + self.walls[LEFT] = false; + next.walls[RIGHT] = false; + } + else if x == -1 { + self.walls[RIGHT] = false; + next.walls[LEFT] = false; + } + else if y == 1 { + self.walls[TOP] = false; + next.walls[BOTTOM] = false; + } + else if y == -1 { + self.walls[BOTTOM] = false; + next.walls[TOP] = false; + } + } +} + +struct Grid { + width: i32, + height: i32, + cells: Vec, + backtrace: Vec, + current: usize +} + +impl Grid { + fn new(width: i32, height:i32) -> Grid { + let mut grid = Grid{ + width, + height, + cells: Vec::new(), + backtrace: Vec::new(), + current: 0 + }; + + for row in 0..height { + for column in 0..width { + grid.cells.push(Cell::new(row, column)); + } + } + + grid + } + + fn calculate_index(&self, row: i32, column: i32) -> i32 { + if row < 0 || column < 0 || column > self.width-1 || row > self.height-1 { + -1 + } else { + column + (row * self.width) + } + } + + fn get_available_neighbors(&self) -> Vec { + let mut neighbors : Vec = Vec::new(); + + let current_row = self.cells[self.current].row; + let current_column = self.cells[self.current].column; + + let neighbor_indices : [i32; 4] = [ + self.calculate_index(current_row -1, current_column), + self.calculate_index(current_row, current_column + 1), + self.calculate_index(current_row + 1, current_column), + self.calculate_index(current_row, current_column - 1) + ]; + + for i in neighbor_indices.iter() { + if *i != -1 && !self.cells[*i as usize].visited { + neighbors.push(*i as usize); + } + } + + neighbors + } + + fn find_next_cell(&mut self) -> Option { + let neighbors = self.get_available_neighbors(); + if !neighbors.is_empty() { + if neighbors.len() == 1 { + return Some(neighbors[0]); + } else { + return Some(neighbors[(crate::rng::roll_dice(1, neighbors.len() as i32)-1) as usize]); + } + } + None + } + + fn generate_maze(&mut self, build_data : &mut BuilderMap) { + let mut i = 0; + loop { + self.cells[self.current].visited = true; + let next = self.find_next_cell(); + + match next { + Some(next) => { + self.cells[next].visited = true; + self.backtrace.push(self.current); + // __lower_part__ __higher_part_ + // / \ / \ + // --------cell1------ | cell2----------- + let (lower_part, higher_part) = + self.cells.split_at_mut(std::cmp::max(self.current, next)); + let cell1 = &mut lower_part[std::cmp::min(self.current, next)]; + let cell2 = &mut higher_part[0]; + cell1.remove_walls(cell2); + self.current = next; + } + None => { + if !self.backtrace.is_empty() { + self.current = self.backtrace[0]; + self.backtrace.remove(0); + } else { + break; + } + } + } + + if i % 50 == 0 { + self.copy_to_map(&mut build_data.map); + build_data.take_snapshot(); + } + i += 1; + } + } + + fn copy_to_map(&self, map : &mut Map) { + // Clear the map + for i in map.tiles.iter_mut() { *i = TileType::Wall; } + + for cell in self.cells.iter() { + let x = cell.column + 1; + let y = cell.row + 1; + let idx = map.xy_idx(x * 2, y * 2); + + map.tiles[idx] = TileType::Floor; + if !cell.walls[TOP] { map.tiles[idx - map.width as usize] = TileType::Floor } + if !cell.walls[RIGHT] { map.tiles[idx + 1] = TileType::Floor } + if !cell.walls[BOTTOM] { map.tiles[idx + map.width as usize] = TileType::Floor } + if !cell.walls[LEFT] { map.tiles[idx - 1] = TileType::Floor } + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/mod.rs b/chapter-75-darkplaza/src/map_builders/mod.rs new file mode 100644 index 00000000..96e1b59e --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/mod.rs @@ -0,0 +1,325 @@ +use super::{Map, Rect, TileType, Position, spawner, SHOW_MAPGEN_VISUALIZER}; +use specs::prelude::*; +mod simple_map; +mod bsp_dungeon; +mod bsp_interior; +mod cellular_automata; +mod drunkard; +mod maze; +mod dla; +mod common; +mod voronoi; +mod waveform_collapse; +mod prefab_builder; +mod room_based_spawner; +mod room_based_starting_position; +mod room_based_stairs; +mod area_starting_points; +mod cull_unreachable; +mod voronoi_spawning; +mod distant_exit; +mod room_exploder; +mod room_corner_rounding; +mod rooms_corridors_dogleg; +mod rooms_corridors_bsp; +mod room_sorter; +mod room_draw; +mod rooms_corridors_nearest; +mod rooms_corridors_lines; +mod room_corridor_spawner; +mod door_placement; +mod town; +mod forest; +mod limestone_cavern; +mod dwarf_fort_builder; +mod area_ending_point; +use forest::forest_builder; +use limestone_cavern::*; +use dwarf_fort_builder::*; +use distant_exit::DistantExit; +use simple_map::SimpleMapBuilder; +use bsp_dungeon::BspDungeonBuilder; +use bsp_interior::BspInteriorBuilder; +use cellular_automata::CellularAutomataBuilder; +use drunkard::DrunkardsWalkBuilder; +use voronoi::VoronoiCellBuilder; +use waveform_collapse::WaveformCollapseBuilder; +use prefab_builder::PrefabBuilder; +use room_based_spawner::RoomBasedSpawner; +use room_based_starting_position::RoomBasedStartingPosition; +use room_based_stairs::RoomBasedStairs; +use area_starting_points::{AreaStartingPosition, XStart, YStart}; +use cull_unreachable::CullUnreachable; +use voronoi_spawning::VoronoiSpawning; +use maze::MazeBuilder; +use dla::DLABuilder; +use common::*; +use room_exploder::RoomExploder; +use room_corner_rounding::RoomCornerRounder; +use rooms_corridors_dogleg::DoglegCorridors; +use rooms_corridors_bsp::BspCorridors; +use room_sorter::{RoomSorter, RoomSort}; +use room_draw::RoomDrawer; +use rooms_corridors_nearest::NearestCorridors; +use rooms_corridors_lines::StraightLineCorridors; +use room_corridor_spawner::CorridorSpawner; +use door_placement::DoorPlacement; +use town::town_builder; +use area_ending_point::*; +mod mushroom_forest; +use mushroom_forest::*; +mod dark_elves; +use dark_elves::*; + +pub struct BuilderMap { + pub spawn_list : Vec<(usize, String)>, + pub map : Map, + pub starting_position : Option, + pub rooms: Option>, + pub corridors: Option>>, + pub history : Vec, + pub width: i32, + pub height: i32 +} + +impl BuilderMap { + fn take_snapshot(&mut self) { + if SHOW_MAPGEN_VISUALIZER { + let mut snapshot = self.map.clone(); + for v in snapshot.revealed_tiles.iter_mut() { + *v = true; + } + self.history.push(snapshot); + } + } +} + +pub struct BuilderChain { + starter: Option>, + builders: Vec>, + pub build_data : BuilderMap +} + +impl BuilderChain { + pub fn new(new_depth : i32, width: i32, height: i32, name : S) -> BuilderChain { + BuilderChain{ + starter: None, + builders: Vec::new(), + build_data : BuilderMap { + spawn_list: Vec::new(), + map: Map::new(new_depth, width, height, name), + starting_position: None, + rooms: None, + corridors: None, + history : Vec::new(), + width, + height + } + } + } + + pub fn start_with(&mut self, starter : Box) { + match self.starter { + None => self.starter = Some(starter), + Some(_) => panic!("You can only have one starting builder.") + }; + } + + pub fn with(&mut self, metabuilder : Box) { + self.builders.push(metabuilder); + } + + pub fn build_map(&mut self) { + match &mut self.starter { + None => panic!("Cannot run a map builder chain without a starting build system"), + Some(starter) => { + // Build the starting map + starter.build_map(&mut self.build_data); + } + } + + // Build additional layers in turn + for metabuilder in self.builders.iter_mut() { + metabuilder.build_map(&mut self.build_data); + } + } + + pub fn spawn_entities(&mut self, ecs : &mut World) { + for entity in self.build_data.spawn_list.iter() { + spawner::spawn_entity(ecs, &(&entity.0, &entity.1)); + } + } +} + +pub trait InitialMapBuilder { + fn build_map(&mut self, build_data : &mut BuilderMap); +} + +pub trait MetaMapBuilder { + fn build_map(&mut self, build_data : &mut BuilderMap); +} + +fn random_start_position() -> (XStart, YStart) { + let x; + let xroll = crate::rng::roll_dice(1, 3); + match xroll { + 1 => x = XStart::LEFT, + 2 => x = XStart::CENTER, + _ => x = XStart::RIGHT + } + + let y; + let yroll = crate::rng::roll_dice(1, 3); + match yroll { + 1 => y = YStart::BOTTOM, + 2 => y = YStart::CENTER, + _ => y = YStart::TOP + } + + (x, y) +} + +fn random_room_builder(builder : &mut BuilderChain) { + let build_roll = crate::rng::roll_dice(1, 3); + match build_roll { + 1 => builder.start_with(SimpleMapBuilder::new()), + 2 => builder.start_with(BspDungeonBuilder::new()), + _ => builder.start_with(BspInteriorBuilder::new()) + } + + // BSP Interior still makes holes in the walls + if build_roll != 3 { + // Sort by one of the 5 available algorithms + let sort_roll = crate::rng::roll_dice(1, 5); + match sort_roll { + 1 => builder.with(RoomSorter::new(RoomSort::LEFTMOST)), + 2 => builder.with(RoomSorter::new(RoomSort::RIGHTMOST)), + 3 => builder.with(RoomSorter::new(RoomSort::TOPMOST)), + 4 => builder.with(RoomSorter::new(RoomSort::BOTTOMMOST)), + _ => builder.with(RoomSorter::new(RoomSort::CENTRAL)), + } + + builder.with(RoomDrawer::new()); + + let corridor_roll = crate::rng::roll_dice(1, 4); + match corridor_roll { + 1 => builder.with(DoglegCorridors::new()), + 2 => builder.with(NearestCorridors::new()), + 3 => builder.with(StraightLineCorridors::new()), + _ => builder.with(BspCorridors::new()) + } + + let cspawn_roll = crate::rng::roll_dice(1, 2); + if cspawn_roll == 1 { + builder.with(CorridorSpawner::new()); + } + + let modifier_roll = crate::rng::roll_dice(1, 6); + match modifier_roll { + 1 => builder.with(RoomExploder::new()), + 2 => builder.with(RoomCornerRounder::new()), + _ => {} + } + } + + let start_roll = crate::rng::roll_dice(1, 2); + match start_roll { + 1 => builder.with(RoomBasedStartingPosition::new()), + _ => { + let (start_x, start_y) = random_start_position(); + builder.with(AreaStartingPosition::new(start_x, start_y)); + } + } + + let exit_roll = crate::rng::roll_dice(1, 2); + match exit_roll { + 1 => builder.with(RoomBasedStairs::new()), + _ => builder.with(DistantExit::new()) + } + + let spawn_roll = crate::rng::roll_dice(1, 2); + match spawn_roll { + 1 => builder.with(RoomBasedSpawner::new()), + _ => builder.with(VoronoiSpawning::new()) + } +} + +fn random_shape_builder(builder : &mut BuilderChain) { + let builder_roll = crate::rng::roll_dice(1, 16); + match builder_roll { + 1 => builder.start_with(CellularAutomataBuilder::new()), + 2 => builder.start_with(DrunkardsWalkBuilder::open_area()), + 3 => builder.start_with(DrunkardsWalkBuilder::open_halls()), + 4 => builder.start_with(DrunkardsWalkBuilder::winding_passages()), + 5 => builder.start_with(DrunkardsWalkBuilder::fat_passages()), + 6 => builder.start_with(DrunkardsWalkBuilder::fearful_symmetry()), + 7 => builder.start_with(MazeBuilder::new()), + 8 => builder.start_with(DLABuilder::walk_inwards()), + 9 => builder.start_with(DLABuilder::walk_outwards()), + 10 => builder.start_with(DLABuilder::central_attractor()), + 11 => builder.start_with(DLABuilder::insectoid()), + 12 => builder.start_with(VoronoiCellBuilder::pythagoras()), + 13 => builder.start_with(VoronoiCellBuilder::manhattan()), + _ => builder.start_with(PrefabBuilder::constant(prefab_builder::prefab_levels::WFC_POPULATED)), + } + + // Set the start to the center and cull + builder.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)); + builder.with(CullUnreachable::new()); + + // Now set the start to a random starting area + let (start_x, start_y) = random_start_position(); + builder.with(AreaStartingPosition::new(start_x, start_y)); + + // Setup an exit and spawn mobs + builder.with(VoronoiSpawning::new()); + builder.with(DistantExit::new()); +} + +pub fn random_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { + let mut builder = BuilderChain::new(new_depth, width, height, "New Map"); + let type_roll = crate::rng::roll_dice(1, 2); + match type_roll { + 1 => random_room_builder(&mut builder), + _ => random_shape_builder(&mut builder) + } + + if crate::rng::roll_dice(1, 3)==1 { + builder.with(WaveformCollapseBuilder::new()); + + // Now set the start to a random starting area + let (start_x, start_y) = random_start_position(); + builder.with(AreaStartingPosition::new(start_x, start_y)); + + // Setup an exit and spawn mobs + builder.with(VoronoiSpawning::new()); + builder.with(DistantExit::new()); + } + + if crate::rng::roll_dice(1, 20)==1 { + builder.with(PrefabBuilder::sectional(prefab_builder::prefab_sections::UNDERGROUND_FORT)); + } + + builder.with(DoorPlacement::new()); + builder.with(PrefabBuilder::vaults()); + + builder +} + +pub fn level_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { + rltk::console::log(format!("Depth: {}", new_depth)); + match new_depth { + 1 => town_builder(new_depth, width, height), + 2 => forest_builder(new_depth, width, height), + 3 => limestone_cavern_builder(new_depth, width, height), + 4 => limestone_deep_cavern_builder(new_depth, width, height), + 5 => limestone_transition_builder(new_depth, width, height), + 6 => dwarf_fort_builder(new_depth, width, height), + 7 => mushroom_entrance(new_depth, width, height), + 8 => mushroom_builder(new_depth, width, height), + 9 => mushroom_exit(new_depth, width, height), + 10 => dark_elf_city(new_depth, width, height), + 11 => dark_elf_plaza(new_depth, width, height), + _ => random_builder(new_depth, width, height) + } +} diff --git a/chapter-75-darkplaza/src/map_builders/mushroom_forest.rs b/chapter-75-darkplaza/src/map_builders/mushroom_forest.rs new file mode 100644 index 00000000..669a2425 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/mushroom_forest.rs @@ -0,0 +1,42 @@ +use super::{BuilderChain, XStart, YStart, AreaStartingPosition, + CullUnreachable, VoronoiSpawning, + AreaEndingPosition, XEnd, YEnd, CellularAutomataBuilder, PrefabBuilder, WaveformCollapseBuilder}; +use crate::map_builders::prefab_builder::prefab_sections::*; + +pub fn mushroom_entrance(new_depth: i32, width: i32, height: i32) -> BuilderChain { + let mut chain = BuilderChain::new(new_depth, width, height, "Into The Mushroom Grove"); + chain.start_with(CellularAutomataBuilder::new()); + chain.with(WaveformCollapseBuilder::new()); + chain.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)); + chain.with(CullUnreachable::new()); + chain.with(AreaStartingPosition::new(XStart::RIGHT, YStart::CENTER)); + chain.with(AreaEndingPosition::new(XEnd::LEFT, YEnd::CENTER)); + chain.with(VoronoiSpawning::new()); + chain.with(PrefabBuilder::sectional(UNDERGROUND_FORT)); + chain +} + +pub fn mushroom_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { + let mut chain = BuilderChain::new(new_depth, width, height, "Into The Mushroom Grove"); + chain.start_with(CellularAutomataBuilder::new()); + chain.with(WaveformCollapseBuilder::new()); + chain.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)); + chain.with(CullUnreachable::new()); + chain.with(AreaStartingPosition::new(XStart::RIGHT, YStart::CENTER)); + chain.with(AreaEndingPosition::new(XEnd::LEFT, YEnd::CENTER)); + chain.with(VoronoiSpawning::new()); + chain +} + +pub fn mushroom_exit(new_depth: i32, width: i32, height: i32) -> BuilderChain { + let mut chain = BuilderChain::new(new_depth, width, height, "Into The Mushroom Grove"); + chain.start_with(CellularAutomataBuilder::new()); + chain.with(WaveformCollapseBuilder::new()); + chain.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)); + chain.with(CullUnreachable::new()); + chain.with(AreaStartingPosition::new(XStart::RIGHT, YStart::CENTER)); + chain.with(AreaEndingPosition::new(XEnd::LEFT, YEnd::CENTER)); + chain.with(VoronoiSpawning::new()); + chain.with(PrefabBuilder::sectional(DROW_ENTRY)); + chain +} diff --git a/chapter-75-darkplaza/src/map_builders/prefab_builder/mod.rs b/chapter-75-darkplaza/src/map_builders/prefab_builder/mod.rs new file mode 100644 index 00000000..307aceb2 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/prefab_builder/mod.rs @@ -0,0 +1,326 @@ +use super::{InitialMapBuilder, MetaMapBuilder, BuilderMap, TileType, Position}; +pub mod prefab_levels; +pub mod prefab_sections; +pub mod prefab_rooms; +use std::collections::HashSet; + +#[derive(PartialEq, Copy, Clone)] +#[allow(dead_code)] +pub enum PrefabMode { + RexLevel{ template : &'static str }, + Constant{ level : prefab_levels::PrefabLevel }, + Sectional{ section : prefab_sections::PrefabSection }, + RoomVaults +} + +#[allow(dead_code)] +pub struct PrefabBuilder { + mode: PrefabMode +} + +impl MetaMapBuilder for PrefabBuilder { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl InitialMapBuilder for PrefabBuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl PrefabBuilder { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(PrefabBuilder{ + mode : PrefabMode::RoomVaults, + }) + } + + #[allow(dead_code)] + pub fn rex_level(template : &'static str) -> Box { + Box::new(PrefabBuilder{ + mode : PrefabMode::RexLevel{ template }, + }) + } + + #[allow(dead_code)] + pub fn constant(level : prefab_levels::PrefabLevel) -> Box { + Box::new(PrefabBuilder{ + mode : PrefabMode::Constant{ level }, + }) + } + + #[allow(dead_code)] + pub fn sectional(section : prefab_sections::PrefabSection) -> Box { + Box::new(PrefabBuilder{ + mode : PrefabMode::Sectional{ section }, + }) + } + + #[allow(dead_code)] + pub fn vaults() -> Box { + Box::new(PrefabBuilder{ + mode : PrefabMode::RoomVaults, + }) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + match self.mode { + PrefabMode::RexLevel{template} => self.load_rex_map(&template, build_data), + PrefabMode::Constant{level} => self.load_ascii_map(&level, build_data), + PrefabMode::Sectional{section} => self.apply_sectional(§ion, build_data), + PrefabMode::RoomVaults => self.apply_room_vaults(build_data) + } + build_data.take_snapshot(); + } + + fn char_to_map(&mut self, ch : char, idx: usize, build_data : &mut BuilderMap) { + // Bounds check + if idx >= build_data.map.tiles.len()-1 { + return; + } + match ch { + ' ' => build_data.map.tiles[idx] = TileType::Floor, + '#' => build_data.map.tiles[idx] = TileType::Wall, + '≈' => build_data.map.tiles[idx] = TileType::DeepWater, + '@' => { + let x = idx as i32 % build_data.map.width; + let y = idx as i32 / build_data.map.width; + build_data.map.tiles[idx] = TileType::Floor; + build_data.starting_position = Some(Position{ x:x as i32, y:y as i32 }); + } + '>' => build_data.map.tiles[idx] = TileType::DownStairs, + 'e' => { + build_data.map.tiles[idx] = TileType::Floor; + build_data.spawn_list.push((idx, "Dark Elf".to_string())); + } + 'g' => { + build_data.map.tiles[idx] = TileType::Floor; + build_data.spawn_list.push((idx, "Goblin".to_string())); + } + 'o' => { + build_data.map.tiles[idx] = TileType::Floor; + build_data.spawn_list.push((idx, "Orc".to_string())); + } + 'O' => { + build_data.map.tiles[idx] = TileType::Floor; + build_data.spawn_list.push((idx, "Orc Leader".to_string())); + } + '^' => { + build_data.map.tiles[idx] = TileType::Floor; + build_data.spawn_list.push((idx, "Bear Trap".to_string())); + } + '%' => { + build_data.map.tiles[idx] = TileType::Floor; + build_data.spawn_list.push((idx, "Rations".to_string())); + } + '!' => { + build_data.map.tiles[idx] = TileType::Floor; + build_data.spawn_list.push((idx, "Health Potion".to_string())); + } + '☼' => { + build_data.map.tiles[idx] = TileType::Floor; + build_data.spawn_list.push((idx, "Watch Fire".to_string())); + } + _ => { + rltk::console::log(format!("Unknown glyph loading map: {}", (ch as u8) as char)); + } + } + } + + #[allow(dead_code)] + fn load_rex_map(&mut self, path: &str, build_data : &mut BuilderMap) { + let xp_file = rltk::rex::XpFile::from_resource(path).unwrap(); + + for layer in &xp_file.layers { + for y in 0..layer.height { + for x in 0..layer.width { + let cell = layer.get(x, y).unwrap(); + if x < build_data.map.width as usize && y < build_data.map.height as usize { + let idx = build_data.map.xy_idx(x as i32, y as i32); + // We're doing some nasty casting to make it easier to type things like '#' in the match + self.char_to_map(cell.ch as u8 as char, idx, build_data); + } + } + } + } + } + + fn read_ascii_to_vec(template : &str) -> Vec { + let mut string_vec : Vec = template.chars().filter(|a| *a != '\r' && *a !='\n').collect(); + for c in string_vec.iter_mut() { if *c as u8 == 160u8 { *c = ' '; } } + string_vec + } + + #[allow(dead_code)] + fn load_ascii_map(&mut self, level: &prefab_levels::PrefabLevel, build_data : &mut BuilderMap) { + let string_vec = PrefabBuilder::read_ascii_to_vec(level.template); + + let mut i = 0; + for ty in 0..level.height { + for tx in 0..level.width { + if tx < build_data.map.width as usize && ty < build_data.map.height as usize { + let idx = build_data.map.xy_idx(tx as i32, ty as i32); + self.char_to_map(string_vec[i], idx, build_data); + } + i += 1; + } + } + } + + fn apply_previous_iteration(&mut self, mut filter: F, build_data : &mut BuilderMap) + where F : FnMut(i32, i32) -> bool + { + let width = build_data.map.width; + build_data.spawn_list.retain(|(idx, _name)| { + let x = *idx as i32 % width; + let y = *idx as i32 / width; + filter(x, y) + }); + build_data.take_snapshot(); + } + + #[allow(dead_code)] + fn apply_sectional(&mut self, section : &prefab_sections::PrefabSection, build_data : &mut BuilderMap) { + use prefab_sections::*; + + let string_vec = PrefabBuilder::read_ascii_to_vec(section.template); + + // Place the new section + let chunk_x; + match section.placement.0 { + HorizontalPlacement::Left => chunk_x = 0, + HorizontalPlacement::Center => chunk_x = (build_data.map.width / 2) - (section.width as i32 / 2), + HorizontalPlacement::Right => chunk_x = (build_data.map.width-1) - section.width as i32 + } + + let chunk_y; + match section.placement.1 { + VerticalPlacement::Top => chunk_y = 0, + VerticalPlacement::Center => chunk_y = (build_data.map.height / 2) - (section.height as i32 / 2), + VerticalPlacement::Bottom => chunk_y = (build_data.map.height-1) - section.height as i32 + } + + // Build the map + self.apply_previous_iteration(|x,y| { + x < chunk_x || x > (chunk_x + section.width as i32) || y < chunk_y || y > (chunk_y + section.height as i32) + }, build_data); + + let mut i = 0; + for ty in 0..section.height { + for tx in 0..section.width { + if tx > 0 && tx < build_data.map.width as usize -1 && ty < build_data.map.height as usize -1 && ty > 0 { + let idx = build_data.map.xy_idx(tx as i32 + chunk_x, ty as i32 + chunk_y); + if i < string_vec.len() { self.char_to_map(string_vec[i], idx, build_data); } + } + i += 1; + } + } + build_data.take_snapshot(); + } + + fn apply_room_vaults(&mut self, build_data : &mut BuilderMap) { + use prefab_rooms::*; + + // Apply the previous builder, and keep all entities it spawns (for now) + self.apply_previous_iteration(|_x,_y| true, build_data); + + // Do we want a vault at all? + let vault_roll = crate::rng::roll_dice(1, 6) + build_data.map.depth; + if vault_roll < 4 { return; } + + // Note that this is a place-holder and will be moved out of this function + let master_vault_list = vec![TOTALLY_NOT_A_TRAP, CHECKERBOARD, SILLY_SMILE]; + + // Filter the vault list down to ones that are applicable to the current depth + let mut possible_vaults : Vec<&PrefabRoom> = master_vault_list + .iter() + .filter(|v| { build_data.map.depth >= v.first_depth && build_data.map.depth <= v.last_depth }) + .collect(); + + if possible_vaults.is_empty() { return; } // Bail out if there's nothing to build + + let n_vaults = i32::min(crate::rng::roll_dice(1, 3), possible_vaults.len() as i32); + let mut used_tiles : HashSet = HashSet::new(); + + for _i in 0..n_vaults { + + let vault_index = if possible_vaults.len() == 1 { 0 } else { (crate::rng::roll_dice(1, possible_vaults.len() as i32)-1) as usize }; + let vault = possible_vaults[vault_index]; + + // We'll make a list of places in which the vault could fit + let mut vault_positions : Vec = Vec::new(); + + let mut idx = 0usize; + loop { + let x = (idx % build_data.map.width as usize) as i32; + let y = (idx / build_data.map.width as usize) as i32; + + // Check that we won't overflow the map + if x > 1 + && (x+vault.width as i32) < build_data.map.width-2 + && y > 1 + && (y+vault.height as i32) < build_data.map.height-2 + { + + let mut possible = true; + for ty in 0..vault.height as i32 { + for tx in 0..vault.width as i32 { + + let idx = build_data.map.xy_idx(tx + x, ty + y); + if build_data.map.tiles[idx] != TileType::Floor { + possible = false; + } + if used_tiles.contains(&idx) { + possible = false; + } + } + } + + if possible { + vault_positions.push(Position{ x,y }); + break; + } + + } + + idx += 1; + if idx >= build_data.map.tiles.len()-1 { break; } + } + + if !vault_positions.is_empty() { + let pos_idx = if vault_positions.len()==1 { 0 } else { (crate::rng::roll_dice(1, vault_positions.len() as i32)-1) as usize }; + let pos = &vault_positions[pos_idx]; + + let chunk_x = pos.x; + let chunk_y = pos.y; + + let width = build_data.map.width; // The borrow checker really doesn't like it + let height = build_data.map.height; // when we access `self` inside the `retain` + build_data.spawn_list.retain(|e| { + let idx = e.0 as i32; + let x = idx % width; + let y = idx / height; + x < chunk_x || x > chunk_x + vault.width as i32 || y < chunk_y || y > chunk_y + vault.height as i32 + }); + + let string_vec = PrefabBuilder::read_ascii_to_vec(vault.template); + let mut i = 0; + for ty in 0..vault.height { + for tx in 0..vault.width { + let idx = build_data.map.xy_idx(tx as i32 + chunk_x, ty as i32 + chunk_y); + self.char_to_map(string_vec[i], idx, build_data); + used_tiles.insert(idx); + i += 1; + } + } + build_data.take_snapshot(); + + possible_vaults.remove(vault_index); + } + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/prefab_builder/prefab_levels.rs b/chapter-75-darkplaza/src/map_builders/prefab_builder/prefab_levels.rs new file mode 100644 index 00000000..e1226d45 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/prefab_builder/prefab_levels.rs @@ -0,0 +1,61 @@ +#[derive(PartialEq, Copy, Clone)] +pub struct PrefabLevel { + pub template : &'static str, + pub width : usize, + pub height: usize +} + +#[allow(dead_code)] +pub const WFC_POPULATED : PrefabLevel = PrefabLevel{ + template : LEVEL_MAP, + width: 80, + height: 43 +}; + +#[allow(dead_code)] +const LEVEL_MAP : &str = +" +################################################################################ +#          ########################################################    ######### +#    @     ######    #########       ####     ###################        ####### +#          ####   g  #                          ###############            ##### +#          #### #    # #######       ####       #############                ### +##### ######### #    # #######       #########  ####    #####                ### +##### ######### ###### #######   o   #########  #### ## #####                ### +##                        ####       #########   ### ##         o            ### +##### ######### ###       ####       #######         ## #####                ### +##### ######### ###       ####       ####### #   ### ## #####                ### +##### ######### ###       ####       ####### #######    #####     o          ### +###          ## ###       ####       ####### ################                ### +###          ## ###   o   ###### ########### #   ############                ### +###          ## ###       ###### ###########     ###                         ### +###    %                  ###### ########### #   ###   !   ##                ### +###          ## ###              ######   ## #######       ##                ### +###          ## ###       ## ### #####     # ########################      ##### +###          ## ###       ## ### #####     # #   ######################    ##### +#### ## ####### ###### ##### ### ####          o ###########     ######    ##### +#### ## ####### ###### ####   ## ####        #   #########         ###### ###### +#    ## ####### ###### ####   ## ####        ############           ##### ###### +# g  ## ####### ###### ####   ##        %    ###########   o      o  #### #    # +#    ## ###            ####   ## ####        #   #######   ##    ##  ####   g  # +#######                  ####### ####            ######     !    !    ### #    # +######                     ##### ####        #   ######               ### ###### +#####                            #####     # ##########               ### ###### +#####           !           ### ######     # ##########      o##o     ### #   ## +#####                       ### #######   ## #   ######               ###   g ## +#   ##                     #### ######## ###   o #######  ^########^ #### #   ## +# g    #                 ###### ######## #####   #######  ^        ^ #### ###### +#   ##g####           ######    ######## ################           ##### ###### +#   ## ########## ##########    ######## #################         ######      # +#####   ######### ########## %  ######## ###################     ######## ##   # +#### ### ######## ##########    ######## #################### ##########   #   # +### ##### ######   #########    ########          ########### #######   # g#   # +### #####           ###############      ###      ########### #######   ####   # +### ##### ####       ############## ######## g  g ########### ####         # ^ # +#### ###^####         ############# ########      #####       ####      # g#   # +#####   ######       ###            ########      ##### g     ####   !  ####^^ # +#!%^## ###  ##           ########## ########  gg                 g         # > # +#!%^   ###  ###     ############### ########      ##### g     ####      # g#   # +# %^##  ^   ###     ############### ########      #####       ################## +################################################################################ +"; diff --git a/chapter-75-darkplaza/src/map_builders/prefab_builder/prefab_rooms.rs b/chapter-75-darkplaza/src/map_builders/prefab_builder/prefab_rooms.rs new file mode 100644 index 00000000..c980bc43 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/prefab_builder/prefab_rooms.rs @@ -0,0 +1,65 @@ +#[allow(dead_code)] +#[derive(PartialEq, Copy, Clone)] +pub struct PrefabRoom { + pub template : &'static str, + pub width : usize, + pub height: usize, + pub first_depth: i32, + pub last_depth: i32 +} + +#[allow(dead_code)] +pub const TOTALLY_NOT_A_TRAP : PrefabRoom = PrefabRoom{ + template : TOTALLY_NOT_A_TRAP_MAP, + width: 5, + height: 5, + first_depth: 0, + last_depth: 100 +}; + +#[allow(dead_code)] +const TOTALLY_NOT_A_TRAP_MAP : &str = " + + ^^^ + ^!^ + ^^^ + +"; + +#[allow(dead_code)] +pub const SILLY_SMILE : PrefabRoom = PrefabRoom{ + template : SILLY_SMILE_MAP, + width: 6, + height: 6, + first_depth: 0, + last_depth: 100 +}; + +#[allow(dead_code)] +const SILLY_SMILE_MAP : &str = " + + ^ ^ + ## + + #### + +"; + +#[allow(dead_code)] +pub const CHECKERBOARD : PrefabRoom = PrefabRoom{ + template : CHECKERBOARD_MAP, + width: 6, + height: 6, + first_depth: 0, + last_depth: 100 +}; + +#[allow(dead_code)] +const CHECKERBOARD_MAP : &str = " + + #^# + g#%# + #!# + ^# # + +"; diff --git a/chapter-75-darkplaza/src/map_builders/prefab_builder/prefab_sections.rs b/chapter-75-darkplaza/src/map_builders/prefab_builder/prefab_sections.rs new file mode 100644 index 00000000..17da663c --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/prefab_builder/prefab_sections.rs @@ -0,0 +1,119 @@ +#[allow(dead_code)] +#[derive(PartialEq, Copy, Clone)] +pub enum HorizontalPlacement { Left, Center, Right } + +#[allow(dead_code)] +#[derive(PartialEq, Copy, Clone)] +pub enum VerticalPlacement { Top, Center, Bottom } + +#[allow(dead_code)] +#[derive(PartialEq, Copy, Clone)] +pub struct PrefabSection { + pub template : &'static str, + pub width : usize, + pub height: usize, + pub placement : (HorizontalPlacement, VerticalPlacement) +} + +#[allow(dead_code)] +pub const UNDERGROUND_FORT : PrefabSection = PrefabSection{ + template : RIGHT_FORT, + width: 15, + height: 43, + placement: ( HorizontalPlacement::Right, VerticalPlacement::Top ) +}; + +#[allow(dead_code)] +// The padding needs to be here! +const RIGHT_FORT : &str = " +     # +  ####### +  #     # +  #     ####### +  #  g        # +  #     ####### +  #     # +  ### ### +    # # +    # # +    # ## +    ^ +    ^ +    # ## +    # # +    # # +    # # +    # # +  ### ### +  #     # +  #     # +  #  g  # +  #     # +  #     # +  ### ### +    # # +    # # +    # # +    # ## +    ^ +    ^ +    # ## +    # # +    # # +    # # +  ### ### +  #     # +  #     ####### +  #  g        # +  #     ####### +  #     # +  ####### +     # +"; + +#[allow(dead_code)] +pub const ORC_CAMP : PrefabSection = PrefabSection{ + template : ORC_CAMP_TXT, + width: 12, + height: 12, + placement: ( HorizontalPlacement::Center, VerticalPlacement::Center ) +}; + +#[allow(dead_code)] +const ORC_CAMP_TXT : &str = " + + ########## + ≈☼ ☼≈ + ≈ g ≈ + ≈ ≈ + ≈ g ≈ + o O o + ≈ ≈ + ≈ g ≈ + ≈ g ≈ + ≈☼ ☼≈ + ≈≈≈≈o≈≈≈≈≈ + +"; + +#[allow(dead_code)] +pub const DROW_ENTRY : PrefabSection = PrefabSection{ + template : DROW_ENTRY_TXT, + width: 12, + height: 10, + placement: ( HorizontalPlacement::Center, VerticalPlacement::Center ) +}; + +#[allow(dead_code)] +const DROW_ENTRY_TXT : &str = " + + ########## + # # + # > # + # # + #e # + e # + #e # + ########## + +"; diff --git a/chapter-75-darkplaza/src/map_builders/room_based_spawner.rs b/chapter-75-darkplaza/src/map_builders/room_based_spawner.rs new file mode 100644 index 00000000..93e67724 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/room_based_spawner.rs @@ -0,0 +1,26 @@ +use super::{MetaMapBuilder, BuilderMap, spawner}; + +pub struct RoomBasedSpawner {} + +impl MetaMapBuilder for RoomBasedSpawner { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl RoomBasedSpawner { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(RoomBasedSpawner{}) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + if let Some(rooms) = &build_data.rooms { + for room in rooms.iter().skip(1) { + spawner::spawn_room(&build_data.map, room, build_data.map.depth, &mut build_data.spawn_list); + } + } else { + panic!("Room Based Spawning only works after rooms have been created"); + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/room_based_stairs.rs b/chapter-75-darkplaza/src/map_builders/room_based_stairs.rs new file mode 100644 index 00000000..6529221d --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/room_based_stairs.rs @@ -0,0 +1,26 @@ +use super::{MetaMapBuilder, BuilderMap, TileType}; +pub struct RoomBasedStairs {} + +impl MetaMapBuilder for RoomBasedStairs { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl RoomBasedStairs { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(RoomBasedStairs{}) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + if let Some(rooms) = &build_data.rooms { + let stairs_position = rooms[rooms.len()-1].center(); + let stairs_idx = build_data.map.xy_idx(stairs_position.0, stairs_position.1); + build_data.map.tiles[stairs_idx] = TileType::DownStairs; + build_data.take_snapshot(); + } else { + panic!("Room Based Stairs only works after rooms have been created"); + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/room_based_starting_position.rs b/chapter-75-darkplaza/src/map_builders/room_based_starting_position.rs new file mode 100644 index 00000000..d571f040 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/room_based_starting_position.rs @@ -0,0 +1,25 @@ +use super::{MetaMapBuilder, BuilderMap, Position}; + +pub struct RoomBasedStartingPosition {} + +impl MetaMapBuilder for RoomBasedStartingPosition { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl RoomBasedStartingPosition { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(RoomBasedStartingPosition{}) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + if let Some(rooms) = &build_data.rooms { + let start_pos = rooms[0].center(); + build_data.starting_position = Some(Position{ x: start_pos.0, y: start_pos.1 }); + } else { + panic!("Room Based Staring Position only works after rooms have been created"); + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/room_corner_rounding.rs b/chapter-75-darkplaza/src/map_builders/room_corner_rounding.rs new file mode 100644 index 00000000..ebb8eaa9 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/room_corner_rounding.rs @@ -0,0 +1,49 @@ +use super::{MetaMapBuilder, BuilderMap, TileType, Rect}; + +pub struct RoomCornerRounder {} + +impl MetaMapBuilder for RoomCornerRounder { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl RoomCornerRounder { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(RoomCornerRounder{}) + } + + fn fill_if_corner(&mut self, x: i32, y: i32, build_data : &mut BuilderMap) { + let w = build_data.map.width; + let h = build_data.map.height; + let idx = build_data.map.xy_idx(x, y); + let mut neighbor_walls = 0; + if x > 0 && build_data.map.tiles[idx-1] == TileType::Wall { neighbor_walls += 1; } + if y > 0 && build_data.map.tiles[idx-w as usize] == TileType::Wall { neighbor_walls += 1; } + if x < w-2 && build_data.map.tiles[idx+1] == TileType::Wall { neighbor_walls += 1; } + if y < h-2 && build_data.map.tiles[idx+w as usize] == TileType::Wall { neighbor_walls += 1; } + + if neighbor_walls == 2 { + build_data.map.tiles[idx] = TileType::Wall; + } + } + + fn build(&mut self, build_data : &mut BuilderMap) { + let rooms : Vec; + if let Some(rooms_builder) = &build_data.rooms { + rooms = rooms_builder.clone(); + } else { + panic!("Room Rounding require a builder with room structures"); + } + + for room in rooms.iter() { + self.fill_if_corner(room.x1+1, room.y1+1, build_data); + self.fill_if_corner(room.x2, room.y1+1, build_data); + self.fill_if_corner(room.x1+1, room.y2, build_data); + self.fill_if_corner(room.x2, room.y2, build_data); + + build_data.take_snapshot(); + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/room_corridor_spawner.rs b/chapter-75-darkplaza/src/map_builders/room_corridor_spawner.rs new file mode 100644 index 00000000..53608263 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/room_corridor_spawner.rs @@ -0,0 +1,30 @@ +use super::{MetaMapBuilder, BuilderMap, spawner}; + +pub struct CorridorSpawner {} + +impl MetaMapBuilder for CorridorSpawner { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl CorridorSpawner { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(CorridorSpawner{}) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + if let Some(corridors) = &build_data.corridors { + for c in corridors.iter() { + let depth = build_data.map.depth; + spawner::spawn_region(&build_data.map, + &c, + depth, + &mut build_data.spawn_list); + } + } else { + panic!("Corridor Based Spawning only works after corridors have been created"); + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/room_draw.rs b/chapter-75-darkplaza/src/map_builders/room_draw.rs new file mode 100644 index 00000000..d7c4d152 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/room_draw.rs @@ -0,0 +1,65 @@ +use super::{MetaMapBuilder, BuilderMap, TileType, Rect}; + +pub struct RoomDrawer {} + +impl MetaMapBuilder for RoomDrawer { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl RoomDrawer { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(RoomDrawer{}) + } + + #[allow(dead_code)] + fn rectangle(&mut self, build_data : &mut BuilderMap, room : &Rect) { + for y in room.y1 +1 ..= room.y2 { + for x in room.x1 + 1 ..= room.x2 { + let idx = build_data.map.xy_idx(x, y); + if idx > 0 && idx < ((build_data.map.width * build_data.map.height)-1) as usize { + build_data.map.tiles[idx] = TileType::Floor; + } + } + } + } + + #[allow(dead_code)] + fn circle(&mut self, build_data : &mut BuilderMap, room : &Rect) { + let radius = i32::min(room.x2 - room.x1, room.y2 - room.y1) as f32 / 2.0; + let center = room.center(); + let center_pt = rltk::Point::new(center.0, center.1); + for y in room.y1 ..= room.y2 { + for x in room.x1 ..= room.x2 { + let idx = build_data.map.xy_idx(x, y); + let distance = rltk::DistanceAlg::Pythagoras.distance2d(center_pt, rltk::Point::new(x, y)); + if idx > 0 + && idx < ((build_data.map.width * build_data.map.height)-1) as usize + && distance <= radius + { + build_data.map.tiles[idx] = TileType::Floor; + } + } + } + } + + fn build(&mut self, build_data : &mut BuilderMap) { + let rooms : Vec; + if let Some(rooms_builder) = &build_data.rooms { + rooms = rooms_builder.clone(); + } else { + panic!("Room Drawing require a builder with room structures"); + } + + for room in rooms.iter() { + let room_type = crate::rng::roll_dice(1,4); + match room_type { + 1 => self.circle(build_data, room), + _ => self.rectangle(build_data, room) + } + build_data.take_snapshot(); + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/room_exploder.rs b/chapter-75-darkplaza/src/map_builders/room_exploder.rs new file mode 100644 index 00000000..0037834f --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/room_exploder.rs @@ -0,0 +1,67 @@ +use super::{MetaMapBuilder, BuilderMap, TileType, paint, Symmetry, Rect}; + +pub struct RoomExploder {} + +impl MetaMapBuilder for RoomExploder { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl RoomExploder { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(RoomExploder{}) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + let rooms : Vec; + if let Some(rooms_builder) = &build_data.rooms { + rooms = rooms_builder.clone(); + } else { + panic!("Room Explosions require a builder with room structures"); + } + + for room in rooms.iter() { + let start = room.center(); + let n_diggers = crate::rng::roll_dice(1, 20)-5; + if n_diggers > 0 { + for _i in 0..n_diggers { + let mut drunk_x = start.0; + let mut drunk_y = start.1; + + let mut drunk_life = 20; + let mut did_something = false; + + while drunk_life > 0 { + let drunk_idx = build_data.map.xy_idx(drunk_x, drunk_y); + if build_data.map.tiles[drunk_idx] == TileType::Wall { + did_something = true; + } + paint(&mut build_data.map, Symmetry::None, 1, drunk_x, drunk_y); + build_data.map.tiles[drunk_idx] = TileType::DownStairs; + + let stagger_direction = crate::rng::roll_dice(1, 4); + match stagger_direction { + 1 => { if drunk_x > 2 { drunk_x -= 1; } } + 2 => { if drunk_x < build_data.map.width-2 { drunk_x += 1; } } + 3 => { if drunk_y > 2 { drunk_y -=1; } } + _ => { if drunk_y < build_data.map.height-2 { drunk_y += 1; } } + } + + drunk_life -= 1; + } + if did_something { + build_data.take_snapshot(); + } + + for t in build_data.map.tiles.iter_mut() { + if *t == TileType::DownStairs { + *t = TileType::Floor; + } + } + } + } + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/room_sorter.rs b/chapter-75-darkplaza/src/map_builders/room_sorter.rs new file mode 100644 index 00000000..07c9abb7 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/room_sorter.rs @@ -0,0 +1,45 @@ +use super::{MetaMapBuilder, BuilderMap, Rect }; + +#[allow(dead_code)] +pub enum RoomSort { LEFTMOST, RIGHTMOST, TOPMOST, BOTTOMMOST, CENTRAL } + +pub struct RoomSorter { + sort_by : RoomSort +} + +impl MetaMapBuilder for RoomSorter { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.sorter(build_data); + } +} + +impl RoomSorter { + #[allow(dead_code)] + pub fn new(sort_by : RoomSort) -> Box { + Box::new(RoomSorter{ sort_by }) + } + + fn sorter(&mut self, build_data : &mut BuilderMap) { + match self.sort_by { + RoomSort::LEFTMOST => build_data.rooms.as_mut().unwrap().sort_by(|a,b| a.x1.cmp(&b.x1) ), + RoomSort::RIGHTMOST => build_data.rooms.as_mut().unwrap().sort_by(|a,b| b.x2.cmp(&a.x2) ), + RoomSort::TOPMOST => build_data.rooms.as_mut().unwrap().sort_by(|a,b| a.y1.cmp(&b.y1) ), + RoomSort::BOTTOMMOST => build_data.rooms.as_mut().unwrap().sort_by(|a,b| b.y2.cmp(&a.y2) ), + RoomSort::CENTRAL => { + let map_center = rltk::Point::new( build_data.map.width / 2, build_data.map.height / 2 ); + let center_sort = |a : &Rect, b : &Rect| { + let a_center = a.center(); + let a_center_pt = rltk::Point::new(a_center.0, a_center.1); + let b_center = b.center(); + let b_center_pt = rltk::Point::new(b_center.0, b_center.1); + let distance_a = rltk::DistanceAlg::Pythagoras.distance2d(a_center_pt, map_center); + let distance_b = rltk::DistanceAlg::Pythagoras.distance2d(b_center_pt, map_center); + distance_a.partial_cmp(&distance_b).unwrap() + }; + + build_data.rooms.as_mut().unwrap().sort_by(center_sort); + } + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/rooms_corridors_bsp.rs b/chapter-75-darkplaza/src/map_builders/rooms_corridors_bsp.rs new file mode 100644 index 00000000..4ade3091 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/rooms_corridors_bsp.rs @@ -0,0 +1,41 @@ +use super::{MetaMapBuilder, BuilderMap, Rect, draw_corridor }; + +pub struct BspCorridors {} + +impl MetaMapBuilder for BspCorridors { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.corridors(build_data); + } +} + +impl BspCorridors { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(BspCorridors{}) + } + + fn corridors(&mut self, build_data : &mut BuilderMap) { + let rooms : Vec; + if let Some(rooms_builder) = &build_data.rooms { + rooms = rooms_builder.clone(); + } else { + panic!("BSP Corridors require a builder with room structures"); + } + let mut corridors : Vec> = Vec::new(); + + for i in 0..rooms.len()-1 { + let room = rooms[i]; + let next_room = rooms[i+1]; + let start_x = room.x1 + (crate::rng::roll_dice(1, i32::abs(room.x1 - room.x2))-1); + let start_y = room.y1 + (crate::rng::roll_dice(1, i32::abs(room.y1 - room.y2))-1); + let end_x = next_room.x1 + (crate::rng::roll_dice(1, i32::abs(next_room.x1 - next_room.x2))-1); + let end_y = next_room.y1 + (crate::rng::roll_dice(1, i32::abs(next_room.y1 - next_room.y2))-1); + let corridor = draw_corridor(&mut build_data.map, start_x, start_y, end_x, end_y); + corridors.push(corridor); + build_data.take_snapshot(); + } + + build_data.corridors = Some(corridors); + } +} diff --git a/chapter-75-darkplaza/src/map_builders/rooms_corridors_dogleg.rs b/chapter-75-darkplaza/src/map_builders/rooms_corridors_dogleg.rs new file mode 100644 index 00000000..758890fd --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/rooms_corridors_dogleg.rs @@ -0,0 +1,47 @@ +use super::{MetaMapBuilder, BuilderMap, Rect, apply_horizontal_tunnel, apply_vertical_tunnel }; + +pub struct DoglegCorridors {} + +impl MetaMapBuilder for DoglegCorridors { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.corridors(build_data); + } +} + +impl DoglegCorridors { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(DoglegCorridors{}) + } + + fn corridors(&mut self, build_data : &mut BuilderMap) { + let rooms : Vec; + if let Some(rooms_builder) = &build_data.rooms { + rooms = rooms_builder.clone(); + } else { + panic!("Dogleg Corridors require a builder with room structures"); + } + + let mut corridors : Vec> = Vec::new(); + for (i,room) in rooms.iter().enumerate() { + if i > 0 { + let (new_x, new_y) = room.center(); + let (prev_x, prev_y) = rooms[i as usize -1].center(); + if crate::rng::range(0,2) == 1 { + let mut c1 = apply_horizontal_tunnel(&mut build_data.map, prev_x, new_x, prev_y); + let mut c2 = apply_vertical_tunnel(&mut build_data.map, prev_y, new_y, new_x); + c1.append(&mut c2); + corridors.push(c1); + } else { + let mut c1 = apply_vertical_tunnel(&mut build_data.map, prev_y, new_y, prev_x); + let mut c2 = apply_horizontal_tunnel(&mut build_data.map, prev_x, new_x, new_y); + c1.append(&mut c2); + corridors.push(c1); + } + build_data.take_snapshot(); + } + } + build_data.corridors = Some(corridors); + } +} diff --git a/chapter-75-darkplaza/src/map_builders/rooms_corridors_lines.rs b/chapter-75-darkplaza/src/map_builders/rooms_corridors_lines.rs new file mode 100644 index 00000000..a9ae12fa --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/rooms_corridors_lines.rs @@ -0,0 +1,68 @@ +use super::{MetaMapBuilder, BuilderMap, Rect, TileType }; +use std::collections::HashSet; + +pub struct StraightLineCorridors {} + +impl MetaMapBuilder for StraightLineCorridors { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.corridors(build_data); + } +} + +impl StraightLineCorridors { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(StraightLineCorridors{}) + } + + fn corridors(&mut self, build_data : &mut BuilderMap) { + let rooms : Vec; + if let Some(rooms_builder) = &build_data.rooms { + rooms = rooms_builder.clone(); + } else { + panic!("Straight Line Corridors require a builder with room structures"); + } + + let mut connected : HashSet = HashSet::new(); + let mut corridors : Vec> = Vec::new(); + for (i,room) in rooms.iter().enumerate() { + let mut room_distance : Vec<(usize, f32)> = Vec::new(); + let room_center = room.center(); + let room_center_pt = rltk::Point::new(room_center.0, room_center.1); + for (j,other_room) in rooms.iter().enumerate() { + if i != j && !connected.contains(&j) { + let other_center = other_room.center(); + let other_center_pt = rltk::Point::new(other_center.0, other_center.1); + let distance = rltk::DistanceAlg::Pythagoras.distance2d( + room_center_pt, + other_center_pt + ); + room_distance.push((j, distance)); + } + } + + if !room_distance.is_empty() { + room_distance.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap() ); + let dest_center = rooms[room_distance[0].0].center(); + let line = rltk::line2d( + rltk::LineAlg::Bresenham, + room_center_pt, + rltk::Point::new(dest_center.0, dest_center.1) + ); + let mut corridor = Vec::new(); + for cell in line.iter() { + let idx = build_data.map.xy_idx(cell.x, cell.y); + if build_data.map.tiles[idx] != TileType::Floor { + build_data.map.tiles[idx] = TileType::Floor; + corridor.push(idx); + } + } + corridors.push(corridor); + connected.insert(i); + build_data.take_snapshot(); + } + } + build_data.corridors = Some(corridors); + } +} diff --git a/chapter-75-darkplaza/src/map_builders/rooms_corridors_nearest.rs b/chapter-75-darkplaza/src/map_builders/rooms_corridors_nearest.rs new file mode 100644 index 00000000..64ebcae0 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/rooms_corridors_nearest.rs @@ -0,0 +1,60 @@ +use super::{MetaMapBuilder, BuilderMap, Rect, draw_corridor }; +use std::collections::HashSet; + +pub struct NearestCorridors {} + +impl MetaMapBuilder for NearestCorridors { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.corridors(build_data); + } +} + +impl NearestCorridors { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(NearestCorridors{}) + } + + fn corridors(&mut self, build_data : &mut BuilderMap) { + let rooms : Vec; + if let Some(rooms_builder) = &build_data.rooms { + rooms = rooms_builder.clone(); + } else { + panic!("Nearest Corridors require a builder with room structures"); + } + + let mut connected : HashSet = HashSet::new(); + let mut corridors : Vec> = Vec::new(); + for (i,room) in rooms.iter().enumerate() { + let mut room_distance : Vec<(usize, f32)> = Vec::new(); + let room_center = room.center(); + let room_center_pt = rltk::Point::new(room_center.0, room_center.1); + for (j,other_room) in rooms.iter().enumerate() { + if i != j && !connected.contains(&j) { + let other_center = other_room.center(); + let other_center_pt = rltk::Point::new(other_center.0, other_center.1); + let distance = rltk::DistanceAlg::Pythagoras.distance2d( + room_center_pt, + other_center_pt + ); + room_distance.push((j, distance)); + } + } + + if !room_distance.is_empty() { + room_distance.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap() ); + let dest_center = rooms[room_distance[0].0].center(); + let corridor = draw_corridor( + &mut build_data.map, + room_center.0, room_center.1, + dest_center.0, dest_center.1 + ); + connected.insert(i); + build_data.take_snapshot(); + corridors.push(corridor); + } + } + build_data.corridors = Some(corridors); + } +} diff --git a/chapter-75-darkplaza/src/map_builders/simple_map.rs b/chapter-75-darkplaza/src/map_builders/simple_map.rs new file mode 100644 index 00000000..b98bdba6 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/simple_map.rs @@ -0,0 +1,40 @@ +use super::{InitialMapBuilder, BuilderMap, Rect }; + +pub struct SimpleMapBuilder {} + +impl InitialMapBuilder for SimpleMapBuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build_rooms(build_data); + } +} + +impl SimpleMapBuilder { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(SimpleMapBuilder{}) + } + + fn build_rooms(&mut self, build_data : &mut BuilderMap) { + const MAX_ROOMS : i32 = 30; + const MIN_SIZE : i32 = 6; + const MAX_SIZE : i32 = 10; + let mut rooms : Vec = Vec::new(); + + for _i in 0..MAX_ROOMS { + let w = crate::rng::range(MIN_SIZE, MAX_SIZE); + let h = crate::rng::range(MIN_SIZE, MAX_SIZE); + let x = crate::rng::roll_dice(1, build_data.map.width - w - 1) - 1; + let y = crate::rng::roll_dice(1, build_data.map.height - h - 1) - 1; + let new_room = Rect::new(x, y, w, h); + let mut ok = true; + for other_room in rooms.iter() { + if new_room.intersect(other_room) { ok = false } + } + if ok { + rooms.push(new_room); + } + } + build_data.rooms = Some(rooms); + } +} diff --git a/chapter-75-darkplaza/src/map_builders/town.rs b/chapter-75-darkplaza/src/map_builders/town.rs new file mode 100644 index 00000000..b5791987 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/town.rs @@ -0,0 +1,439 @@ +use super::{BuilderChain, BuilderMap, InitialMapBuilder, TileType, Position}; +use std::collections::HashSet; + +pub fn town_builder(new_depth: i32, width: i32, height: i32) -> BuilderChain { + let mut chain = BuilderChain::new(new_depth, width, height, "The Town of Bracketon"); + chain.start_with(TownBuilder::new()); + chain +} + +pub struct TownBuilder {} + +impl InitialMapBuilder for TownBuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build_rooms(build_data); + } +} + +enum BuildingTag { + Pub, Temple, Blacksmith, Clothier, Alchemist, PlayerHouse, Hovel, Abandoned, Unassigned +} + +impl TownBuilder { + pub fn new() -> Box { + Box::new(TownBuilder{}) + } + + pub fn build_rooms(&mut self, build_data : &mut BuilderMap) { + self.grass_layer(build_data); + self.water_and_piers(build_data); + let (mut available_building_tiles, wall_gap_y) = self.town_walls(build_data); + let mut buildings = self.buildings(build_data, &mut available_building_tiles); + let doors = self.add_doors(build_data, &mut buildings, wall_gap_y); + self.add_paths(build_data, &doors); + + for y in wall_gap_y-3 .. wall_gap_y + 4 { + let exit_idx = build_data.map.xy_idx(build_data.width-2, y); + build_data.map.tiles[exit_idx] = TileType::DownStairs; + } + + let building_size = self.sort_buildings(&buildings); + self.building_factory(build_data, &buildings, &building_size); + + self.spawn_dockers(build_data); + self.spawn_townsfolk(build_data, &mut available_building_tiles); + + // Make visible for screenshot + for t in build_data.map.visible_tiles.iter_mut() { + *t = true; + } + build_data.take_snapshot(); + } + + fn grass_layer(&mut self, build_data : &mut BuilderMap) { + // We'll start with a nice layer of grass + for t in build_data.map.tiles.iter_mut() { + *t = TileType::Grass; + } + build_data.take_snapshot(); + } + + fn water_and_piers(&mut self, build_data : &mut BuilderMap) { + let mut n = (crate::rng::roll_dice(1, 65535) as f32) / 65535f32; + let mut water_width : Vec = Vec::new(); + for y in 0..build_data.height { + let n_water = (f32::sin(n) * 10.0) as i32 + 14 + crate::rng::roll_dice(1, 6); + water_width.push(n_water); + n += 0.1; + for x in 0..n_water { + let idx = build_data.map.xy_idx(x, y); + build_data.map.tiles[idx] = TileType::DeepWater; + } + for x in n_water .. n_water+3 { + let idx = build_data.map.xy_idx(x, y); + build_data.map.tiles[idx] = TileType::ShallowWater; + } + } + build_data.take_snapshot(); + + // Add piers + for _i in 0..crate::rng::roll_dice(1, 4)+6 { + let y = crate::rng::roll_dice(1, build_data.height)-1; + for x in 2 + crate::rng::roll_dice(1, 6) .. water_width[y as usize] + 4 { + let idx = build_data.map.xy_idx(x, y); + build_data.map.tiles[idx] = TileType::Bridge; + } + } + build_data.take_snapshot(); + } + + fn town_walls(&mut self, build_data : &mut BuilderMap) + -> (HashSet, i32) + { + let mut available_building_tiles : HashSet = HashSet::new(); + let wall_gap_y = crate::rng::roll_dice(1, build_data.height - 9) + 5; + for y in 1 .. build_data.height-2 { + if !(y > wall_gap_y-4 && y < wall_gap_y+4) { + let idx = build_data.map.xy_idx(30, y); + build_data.map.tiles[idx] = TileType::Wall; + build_data.map.tiles[idx-1] = TileType::Floor; + let idx_right = build_data.map.xy_idx(build_data.width - 2, y); + build_data.map.tiles[idx_right] = TileType::Wall; + for x in 31 .. build_data.width-2 { + let gravel_idx = build_data.map.xy_idx(x, y); + build_data.map.tiles[gravel_idx] = TileType::Gravel; + if y > 2 && y < build_data.height-1 { + available_building_tiles.insert(gravel_idx); + } + } + } else { + for x in 30 .. build_data.width { + let road_idx = build_data.map.xy_idx(x, y); + build_data.map.tiles[road_idx] = TileType::Road; + } + } + } + build_data.take_snapshot(); + + for x in 30 .. build_data.width-1 { + let idx_top = build_data.map.xy_idx(x, 1); + build_data.map.tiles[idx_top] = TileType::Wall; + let idx_bot = build_data.map.xy_idx(x, build_data.height-2); + build_data.map.tiles[idx_bot] = TileType::Wall; + } + build_data.take_snapshot(); + + (available_building_tiles, wall_gap_y) + } + + fn buildings(&mut self, + build_data : &mut BuilderMap, + available_building_tiles : &mut HashSet) + -> Vec<(i32, i32, i32, i32)> + { + let mut buildings : Vec<(i32, i32, i32, i32)> = Vec::new(); + let mut n_buildings = 0; + while n_buildings < 12 { + let bx = crate::rng::roll_dice(1, build_data.map.width - 32) + 30; + let by = crate::rng::roll_dice(1, build_data.map.height)-2; + let bw = crate::rng::roll_dice(1, 8)+4; + let bh = crate::rng::roll_dice(1, 8)+4; + let mut possible = true; + for y in by .. by+bh { + for x in bx .. bx+bw { + if x < 0 || x > build_data.width-1 || y < 0 || y > build_data.height-1 { + possible = false; + } else { + let idx = build_data.map.xy_idx(x, y); + if !available_building_tiles.contains(&idx) { possible = false; } + } + } + } + if possible { + n_buildings += 1; + buildings.push((bx, by, bw, bh)); + for y in by .. by+bh { + for x in bx .. bx+bw { + let idx = build_data.map.xy_idx(x, y); + build_data.map.tiles[idx] = TileType::WoodFloor; + available_building_tiles.remove(&idx); + available_building_tiles.remove(&(idx+1)); + available_building_tiles.remove(&(idx+build_data.width as usize)); + available_building_tiles.remove(&(idx-1)); + available_building_tiles.remove(&(idx-build_data.width as usize)); + } + } + build_data.take_snapshot(); + } + } + + // Outline buildings + let mut mapclone = build_data.map.clone(); + for y in 2..build_data.height-2 { + for x in 32..build_data.width-2 { + let idx = build_data.map.xy_idx(x, y); + if build_data.map.tiles[idx] == TileType::WoodFloor { + let mut neighbors = 0; + if build_data.map.tiles[idx - 1] != TileType::WoodFloor { neighbors +=1; } + if build_data.map.tiles[idx + 1] != TileType::WoodFloor { neighbors +=1; } + if build_data.map.tiles[idx-build_data.width as usize] != TileType::WoodFloor { neighbors +=1; } + if build_data.map.tiles[idx+build_data.width as usize] != TileType::WoodFloor { neighbors +=1; } + if neighbors > 0 { + mapclone.tiles[idx] = TileType::Wall; + } + } + } + } + build_data.map = mapclone; + build_data.take_snapshot(); + buildings + } + + fn add_doors(&mut self, + build_data : &mut BuilderMap, + buildings: &mut Vec<(i32, i32, i32, i32)>, + wall_gap_y : i32) + -> Vec + { + let mut doors = Vec::new(); + for building in buildings.iter() { + let door_x = building.0 + 1 + crate::rng::roll_dice(1, building.2 - 3); + let cy = building.1 + (building.3 / 2); + let idx = if cy > wall_gap_y { + // Door on the north wall + build_data.map.xy_idx(door_x, building.1) + } else { + build_data.map.xy_idx(door_x, building.1 + building.3 - 1) + }; + build_data.map.tiles[idx] = TileType::Floor; + build_data.spawn_list.push((idx, "Door".to_string())); + doors.push(idx); + } + build_data.take_snapshot(); + doors + } + + fn add_paths(&mut self, + build_data : &mut BuilderMap, + doors : &[usize]) + { + let mut roads = Vec::new(); + for y in 0..build_data.height { + for x in 0..build_data.width { + let idx = build_data.map.xy_idx(x, y); + if build_data.map.tiles[idx] == TileType::Road { + roads.push(idx); + } + } + } + + build_data.map.populate_blocked(); + for door_idx in doors.iter() { + let mut nearest_roads : Vec<(usize, f32)> = Vec::new(); + let door_pt = rltk::Point::new( *door_idx as i32 % build_data.map.width as i32, *door_idx as i32 / build_data.map.width as i32 ); + for r in roads.iter() { + nearest_roads.push(( + *r, + rltk::DistanceAlg::PythagorasSquared.distance2d( + door_pt, + rltk::Point::new( *r as i32 % build_data.map.width, *r as i32 / build_data.map.width ) + ) + )); + } + nearest_roads.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap()); + + let destination = nearest_roads[0].0; + let path = rltk::a_star_search(*door_idx, destination, &build_data.map); + if path.success { + for step in path.steps.iter() { + let idx = *step as usize; + build_data.map.tiles[idx] = TileType::Road; + roads.push(idx); + } + } + build_data.take_snapshot(); + } + } + + fn sort_buildings(&mut self, buildings: &[(i32, i32, i32, i32)]) -> Vec<(usize, i32, BuildingTag)> + { + let mut building_size : Vec<(usize, i32, BuildingTag)> = Vec::new(); + for (i,building) in buildings.iter().enumerate() { + building_size.push(( + i, + building.2 * building.3, + BuildingTag::Unassigned + )); + } + building_size.sort_by(|a,b| b.1.cmp(&a.1)); + building_size[0].2 = BuildingTag::Pub; + building_size[1].2 = BuildingTag::Temple; + building_size[2].2 = BuildingTag::Blacksmith; + building_size[3].2 = BuildingTag::Clothier; + building_size[4].2 = BuildingTag::Alchemist; + building_size[5].2 = BuildingTag::PlayerHouse; + for b in building_size.iter_mut().skip(6) { + b.2 = BuildingTag::Hovel; + } + let last_index = building_size.len()-1; + building_size[last_index].2 = BuildingTag::Abandoned; + building_size + } + + fn building_factory(&mut self, + build_data : &mut BuilderMap, + buildings: &[(i32, i32, i32, i32)], + building_index : &[(usize, i32, BuildingTag)]) + { + for (i,building) in buildings.iter().enumerate() { + let build_type = &building_index[i].2; + match build_type { + BuildingTag::Pub => self.build_pub(&building, build_data), + BuildingTag::Temple => self.build_temple(&building, build_data), + BuildingTag::Blacksmith => self.build_smith(&building, build_data), + BuildingTag::Clothier => self.build_clothier(&building, build_data), + BuildingTag::Alchemist => self.build_alchemist(&building, build_data), + BuildingTag::PlayerHouse => self.build_my_house(&building, build_data), + BuildingTag::Hovel => self.build_hovel(&building, build_data), + BuildingTag::Abandoned => self.build_abandoned_house(&building, build_data), + _ => {} + } + } + } + + fn random_building_spawn( + &mut self, + building: &(i32, i32, i32, i32), + build_data : &mut BuilderMap, + to_place : &mut Vec<&str>, + player_idx : usize) + { + for y in building.1 .. building.1 + building.3 { + for x in building.0 .. building.0 + building.2 { + let idx = build_data.map.xy_idx(x, y); + if build_data.map.tiles[idx] == TileType::WoodFloor && idx != player_idx && crate::rng::roll_dice(1, 3)==1 && !to_place.is_empty() { + let entity_tag = to_place[0]; + to_place.remove(0); + build_data.spawn_list.push((idx, entity_tag.to_string())); + } + } + } + } + + fn build_pub(&mut self, + building: &(i32, i32, i32, i32), + build_data : &mut BuilderMap) + { + // Place the player + build_data.starting_position = Some(Position{ + x : building.0 + (building.2 / 2), + y : building.1 + (building.3 / 2) + }); + let player_idx = build_data.map.xy_idx(building.0 + (building.2 / 2), + building.1 + (building.3 / 2)); + + // Place other items + let mut to_place : Vec<&str> = vec!["Barkeep", "Shady Salesman", "Patron", "Patron", "Keg", + "Table", "Chair", "Table", "Chair"]; + self.random_building_spawn(building, build_data, &mut to_place, player_idx); + } + + fn build_temple(&mut self, + building: &(i32, i32, i32, i32), + build_data : &mut BuilderMap) + { + // Place items + let mut to_place : Vec<&str> = vec!["Priest", "Altar", "Parishioner", "Parishioner", "Chair", "Chair", "Candle", "Candle"]; + self.random_building_spawn(building, build_data, &mut to_place, 0); + } + + fn build_smith(&mut self, + building: &(i32, i32, i32, i32), + build_data : &mut BuilderMap) + { + // Place items + let mut to_place : Vec<&str> = vec!["Blacksmith", "Anvil", "Water Trough", "Weapon Rack", "Armor Stand"]; + self.random_building_spawn(building, build_data, &mut to_place, 0); + } + + fn build_clothier(&mut self, + building: &(i32, i32, i32, i32), + build_data : &mut BuilderMap) + { + // Place items + let mut to_place : Vec<&str> = vec!["Clothier", "Cabinet", "Table", "Loom", "Hide Rack"]; + self.random_building_spawn(building, build_data, &mut to_place, 0); + } + + fn build_alchemist(&mut self, + building: &(i32, i32, i32, i32), + build_data : &mut BuilderMap) + { + // Place items + let mut to_place : Vec<&str> = vec!["Alchemist", "Chemistry Set", "Dead Thing", "Chair", "Table"]; + self.random_building_spawn(building, build_data, &mut to_place, 0); + } + + fn build_my_house(&mut self, + building: &(i32, i32, i32, i32), + build_data : &mut BuilderMap) + { + // Place items + let mut to_place : Vec<&str> = vec!["Mom", "Bed", "Cabinet", "Chair", "Table"]; + self.random_building_spawn(building, build_data, &mut to_place, 0); + } + + fn build_hovel(&mut self, + building: &(i32, i32, i32, i32), + build_data : &mut BuilderMap) + { + // Place items + let mut to_place : Vec<&str> = vec!["Peasant", "Bed", "Chair", "Table"]; + self.random_building_spawn(building, build_data, &mut to_place, 0); + } + + fn build_abandoned_house(&mut self, + building: &(i32, i32, i32, i32), + build_data : &mut BuilderMap) + { + for y in building.1 .. building.1 + building.3 { + for x in building.0 .. building.0 + building.2 { + let idx = build_data.map.xy_idx(x, y); + if build_data.map.tiles[idx] == TileType::WoodFloor && idx != 0 && crate::rng::roll_dice(1, 2)==1 { + build_data.spawn_list.push((idx, "Rat".to_string())); + } + } + } + } + + fn spawn_dockers(&mut self, build_data : &mut BuilderMap) { + for (idx, tt) in build_data.map.tiles.iter().enumerate() { + if *tt == TileType::Bridge && crate::rng::roll_dice(1, 6)==1 { + let roll = crate::rng::roll_dice(1, 3); + match roll { + 1 => build_data.spawn_list.push((idx, "Dock Worker".to_string())), + 2 => build_data.spawn_list.push((idx, "Wannabe Pirate".to_string())), + _ => build_data.spawn_list.push((idx, "Fisher".to_string())), + } + } + } + } + + fn spawn_townsfolk(&mut self, + build_data : &mut BuilderMap, + available_building_tiles : &mut HashSet) + { + for idx in available_building_tiles.iter() { + if crate::rng::roll_dice(1, 10)==1 { + let roll = crate::rng::roll_dice(1, 4); + match roll { + 1 => build_data.spawn_list.push((*idx, "Peasant".to_string())), + 2 => build_data.spawn_list.push((*idx, "Drunk".to_string())), + 3 => build_data.spawn_list.push((*idx, "Dock Worker".to_string())), + _ => build_data.spawn_list.push((*idx, "Fisher".to_string())), + } + } + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/voronoi.rs b/chapter-75-darkplaza/src/map_builders/voronoi.rs new file mode 100644 index 00000000..34569f74 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/voronoi.rs @@ -0,0 +1,113 @@ +use super::{InitialMapBuilder, BuilderMap, TileType}; + +#[derive(PartialEq, Copy, Clone)] +#[allow(dead_code)] +pub enum DistanceAlgorithm { Pythagoras, Manhattan, Chebyshev } + +pub struct VoronoiCellBuilder { + n_seeds: usize, + distance_algorithm: DistanceAlgorithm +} + + +impl InitialMapBuilder for VoronoiCellBuilder { + #[allow(dead_code)] + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl VoronoiCellBuilder { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(VoronoiCellBuilder{ + n_seeds: 64, + distance_algorithm: DistanceAlgorithm::Pythagoras, + }) + } + + #[allow(dead_code)] + pub fn pythagoras() -> Box { + Box::new(VoronoiCellBuilder{ + n_seeds: 64, + distance_algorithm: DistanceAlgorithm::Pythagoras, + }) + } + + #[allow(dead_code)] + pub fn manhattan() -> Box { + Box::new(VoronoiCellBuilder{ + n_seeds: 64, + distance_algorithm: DistanceAlgorithm::Manhattan, + }) + } + + #[allow(clippy::map_entry)] + fn build(&mut self, build_data : &mut BuilderMap) { + // Make a Voronoi diagram. We'll do this the hard way to learn about the technique! + let mut voronoi_seeds : Vec<(usize, rltk::Point)> = Vec::new(); + + while voronoi_seeds.len() < self.n_seeds { + let vx = crate::rng::roll_dice(1, build_data.map.width-1); + let vy = crate::rng::roll_dice(1, build_data.map.height-1); + let vidx = build_data.map.xy_idx(vx, vy); + let candidate = (vidx, rltk::Point::new(vx, vy)); + if !voronoi_seeds.contains(&candidate) { + voronoi_seeds.push(candidate); + } + } + + let mut voronoi_distance = vec![(0, 0.0f32) ; self.n_seeds]; + let mut voronoi_membership : Vec = vec![0 ; build_data.map.width as usize * build_data.map.height as usize]; + for (i, vid) in voronoi_membership.iter_mut().enumerate() { + let x = i as i32 % build_data.map.width; + let y = i as i32 / build_data.map.width; + + for (seed, pos) in voronoi_seeds.iter().enumerate() { + let distance; + match self.distance_algorithm { + DistanceAlgorithm::Pythagoras => { + distance = rltk::DistanceAlg::PythagorasSquared.distance2d( + rltk::Point::new(x, y), + pos.1 + ); + } + DistanceAlgorithm::Manhattan => { + distance = rltk::DistanceAlg::Manhattan.distance2d( + rltk::Point::new(x, y), + pos.1 + ); + } + DistanceAlgorithm::Chebyshev => { + distance = rltk::DistanceAlg::Chebyshev.distance2d( + rltk::Point::new(x, y), + pos.1 + ); + } + } + voronoi_distance[seed] = (seed, distance); + } + + voronoi_distance.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap()); + + *vid = voronoi_distance[0].0 as i32; + } + + for y in 1..build_data.map.height-1 { + for x in 1..build_data.map.width-1 { + let mut neighbors = 0; + let my_idx = build_data.map.xy_idx(x, y); + let my_seed = voronoi_membership[my_idx]; + if voronoi_membership[build_data.map.xy_idx(x-1, y)] != my_seed { neighbors += 1; } + if voronoi_membership[build_data.map.xy_idx(x+1, y)] != my_seed { neighbors += 1; } + if voronoi_membership[build_data.map.xy_idx(x, y-1)] != my_seed { neighbors += 1; } + if voronoi_membership[build_data.map.xy_idx(x, y+1)] != my_seed { neighbors += 1; } + + if neighbors < 2 { + build_data.map.tiles[my_idx] = TileType::Floor; + } + } + build_data.take_snapshot(); + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/voronoi_spawning.rs b/chapter-75-darkplaza/src/map_builders/voronoi_spawning.rs new file mode 100644 index 00000000..2bfae7b7 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/voronoi_spawning.rs @@ -0,0 +1,47 @@ +use super::{MetaMapBuilder, BuilderMap, TileType, spawner}; +use std::collections::HashMap; + +pub struct VoronoiSpawning {} + +impl MetaMapBuilder for VoronoiSpawning { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl VoronoiSpawning { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(VoronoiSpawning{}) + } + + #[allow(clippy::map_entry)] + fn build(&mut self, build_data : &mut BuilderMap) { + let mut noise_areas : HashMap> = HashMap::new(); + let mut noise = rltk::FastNoise::seeded(crate::rng::roll_dice(1, 65536) as u64); + noise.set_noise_type(rltk::NoiseType::Cellular); + noise.set_frequency(0.08); + noise.set_cellular_distance_function(rltk::CellularDistanceFunction::Manhattan); + + for y in 1 .. build_data.map.height-1 { + for x in 1 .. build_data.map.width-1 { + let idx = build_data.map.xy_idx(x, y); + if build_data.map.tiles[idx] == TileType::Floor { + let cell_value_f = noise.get_noise(x as f32, y as f32) * 10240.0; + let cell_value = cell_value_f as i32; + + if noise_areas.contains_key(&cell_value) { + noise_areas.get_mut(&cell_value).unwrap().push(idx); + } else { + noise_areas.insert(cell_value, vec![idx]); + } + } + } + } + + // Spawn the entities + for area in noise_areas.iter() { + spawner::spawn_region(&build_data.map, area.1, build_data.map.depth, &mut build_data.spawn_list); + } + } +} diff --git a/chapter-75-darkplaza/src/map_builders/waveform_collapse/common.rs b/chapter-75-darkplaza/src/map_builders/waveform_collapse/common.rs new file mode 100644 index 00000000..3c038527 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/waveform_collapse/common.rs @@ -0,0 +1,13 @@ +use super::TileType; + +#[derive(PartialEq, Eq, Hash, Clone)] +pub struct MapChunk { + pub pattern : Vec, + pub exits: [Vec; 4], + pub has_exits: bool, + pub compatible_with: [Vec; 4] +} + +pub fn tile_idx_in_chunk(chunk_size: i32, x:i32, y:i32) -> usize { + ((y * chunk_size) + x) as usize +} diff --git a/chapter-75-darkplaza/src/map_builders/waveform_collapse/constraints.rs b/chapter-75-darkplaza/src/map_builders/waveform_collapse/constraints.rs new file mode 100644 index 00000000..44e7c52e --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/waveform_collapse/constraints.rs @@ -0,0 +1,208 @@ +use super::{TileType, Map, MapChunk, tile_idx_in_chunk}; +use std::collections::HashSet; + +pub fn build_patterns(map : &Map, chunk_size: i32, include_flipping: bool, dedupe: bool) -> Vec> { + let chunks_x = map.width / chunk_size; + let chunks_y = map.height / chunk_size; + let mut patterns = Vec::new(); + + for cy in 0..chunks_y { + for cx in 0..chunks_x { + // Normal orientation + let mut pattern : Vec = Vec::new(); + let start_x = cx * chunk_size; + let end_x = (cx+1) * chunk_size; + let start_y = cy * chunk_size; + let end_y = (cy+1) * chunk_size; + + for y in start_y .. end_y { + for x in start_x .. end_x { + let idx = map.xy_idx(x, y); + pattern.push(map.tiles[idx]); + } + } + patterns.push(pattern); + + if include_flipping { + // Flip horizontal + pattern = Vec::new(); + for y in start_y .. end_y { + for x in start_x .. end_x { + let idx = map.xy_idx(end_x - (x+1), y); + pattern.push(map.tiles[idx]); + } + } + patterns.push(pattern); + + // Flip vertical + pattern = Vec::new(); + for y in start_y .. end_y { + for x in start_x .. end_x { + let idx = map.xy_idx(x, end_y - (y+1)); + pattern.push(map.tiles[idx]); + } + } + patterns.push(pattern); + + // Flip both + pattern = Vec::new(); + for y in start_y .. end_y { + for x in start_x .. end_x { + let idx = map.xy_idx(end_x - (x+1), end_y - (y+1)); + pattern.push(map.tiles[idx]); + } + } + patterns.push(pattern); + } + } + } + + // Dedupe + if dedupe { + rltk::console::log(format!("Pre de-duplication, there are {} patterns", patterns.len())); + let set: HashSet> = patterns.drain(..).collect(); // dedup + patterns.extend(set.into_iter()); + rltk::console::log(format!("There are {} patterns", patterns.len())); + } + + patterns +} + +pub fn render_pattern_to_map(map : &mut Map, chunk: &MapChunk, chunk_size: i32, start_x : i32, start_y: i32) { + let mut i = 0usize; + for tile_y in 0..chunk_size { + for tile_x in 0..chunk_size { + let map_idx = map.xy_idx(start_x + tile_x, start_y + tile_y); + map.tiles[map_idx] = chunk.pattern[i]; + map.visible_tiles[map_idx] = true; + i += 1; + } + } + + for (x,northbound) in chunk.exits[0].iter().enumerate() { + if *northbound { + let map_idx = map.xy_idx(start_x + x as i32, start_y); + map.tiles[map_idx] = TileType::DownStairs; + } + } + for (x,southbound) in chunk.exits[1].iter().enumerate() { + if *southbound { + let map_idx = map.xy_idx(start_x + x as i32, start_y + chunk_size -1); + map.tiles[map_idx] = TileType::DownStairs; + } + } + for (x,westbound) in chunk.exits[2].iter().enumerate() { + if *westbound { + let map_idx = map.xy_idx(start_x, start_y + x as i32); + map.tiles[map_idx] = TileType::DownStairs; + } + } + for (x,eastbound) in chunk.exits[3].iter().enumerate() { + if *eastbound { + let map_idx = map.xy_idx(start_x + chunk_size - 1, start_y + x as i32); + map.tiles[map_idx] = TileType::DownStairs; + } + } +} + +pub fn patterns_to_constraints(patterns: Vec>, chunk_size : i32) -> Vec { + // Move into the new constraints object + let mut constraints : Vec = Vec::new(); + for p in patterns { + let mut new_chunk = MapChunk{ + pattern: p, + exits: [ Vec::new(), Vec::new(), Vec::new(), Vec::new() ], + has_exits : true, + compatible_with: [ Vec::new(), Vec::new(), Vec::new(), Vec::new() ] + }; + for exit in new_chunk.exits.iter_mut() { + for _i in 0..chunk_size { + exit.push(false); + } + } + + let mut n_exits = 0; + for x in 0..chunk_size { + // Check for north-bound exits + let north_idx = tile_idx_in_chunk(chunk_size, x, 0); + if new_chunk.pattern[north_idx] == TileType::Floor { + new_chunk.exits[0][x as usize] = true; + n_exits += 1; + } + + // Check for south-bound exits + let south_idx = tile_idx_in_chunk(chunk_size, x, chunk_size-1); + if new_chunk.pattern[south_idx] == TileType::Floor { + new_chunk.exits[1][x as usize] = true; + n_exits += 1; + } + + // Check for west-bound exits + let west_idx = tile_idx_in_chunk(chunk_size, 0, x); + if new_chunk.pattern[west_idx] == TileType::Floor { + new_chunk.exits[2][x as usize] = true; + n_exits += 1; + } + + // Check for east-bound exits + let east_idx = tile_idx_in_chunk(chunk_size, chunk_size-1, x); + if new_chunk.pattern[east_idx] == TileType::Floor { + new_chunk.exits[3][x as usize] = true; + n_exits += 1; + } + } + + if n_exits == 0 { + new_chunk.has_exits = false; + } + + constraints.push(new_chunk); + } + + // Build compatibility matrix + let ch = constraints.clone(); + for c in constraints.iter_mut() { + for (j,potential) in ch.iter().enumerate() { + // If there are no exits at all, it's compatible + if !c.has_exits || !potential.has_exits { + for compat in c.compatible_with.iter_mut() { + compat.push(j); + } + } else { + // Evaluate compatibilty by direction + for (direction, exit_list) in c.exits.iter_mut().enumerate() { + let opposite = match direction { + 0 => 1, // Our North, Their South + 1 => 0, // Our South, Their North + 2 => 3, // Our West, Their East + _ => 2 // Our East, Their West + }; + + let mut it_fits = false; + let mut has_any = false; + for (slot, can_enter) in exit_list.iter().enumerate() { + if *can_enter { + has_any = true; + if potential.exits[opposite][slot] { + it_fits = true; + } + } + } + if it_fits { + c.compatible_with[direction].push(j); + } + if !has_any { + // There's no exits on this side, let's match only if + // the other edge also has no exits + let matching_exit_count = potential.exits[opposite].iter().filter(|a| !**a).count(); + if matching_exit_count == 0 { + c.compatible_with[direction].push(j); + } + } + } + } + } + } + + constraints +} diff --git a/chapter-75-darkplaza/src/map_builders/waveform_collapse/mod.rs b/chapter-75-darkplaza/src/map_builders/waveform_collapse/mod.rs new file mode 100644 index 00000000..44096272 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/waveform_collapse/mod.rs @@ -0,0 +1,85 @@ +use super::{MetaMapBuilder, BuilderMap, Map, TileType}; +mod common; +use common::*; +mod constraints; +use constraints::*; +mod solver; +use solver::*; + +/// Provides a map builder using the Wave Function Collapse algorithm. +pub struct WaveformCollapseBuilder {} + +impl MetaMapBuilder for WaveformCollapseBuilder { + fn build_map(&mut self, build_data : &mut BuilderMap) { + self.build(build_data); + } +} + +impl WaveformCollapseBuilder { + /// Constructor for waveform collapse. + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(WaveformCollapseBuilder{}) + } + + fn build(&mut self, build_data : &mut BuilderMap) { + const CHUNK_SIZE :i32 = 8; + build_data.take_snapshot(); + + let patterns = build_patterns(&build_data.map, CHUNK_SIZE, true, true); + let constraints = patterns_to_constraints(patterns, CHUNK_SIZE); + self.render_tile_gallery(&constraints, CHUNK_SIZE, build_data); + + let old_map = build_data.map.clone(); + + build_data.map = Map::new(build_data.map.depth, build_data.width, build_data.height, &build_data.map.name); + build_data.spawn_list.clear(); + build_data.rooms = None; + build_data.corridors = None; + let mut tries = 0; + loop { + let mut solver = Solver::new(constraints.clone(), CHUNK_SIZE, &build_data.map); + while !solver.iteration(&mut build_data.map) { + build_data.take_snapshot(); + } + build_data.take_snapshot(); + if solver.possible { break; } // If it has hit an impossible condition, try again + tries += 1; + if tries > 10 { break; } + } + + if tries > 10 { + // Restore the old one + build_data.map = old_map; + } + } + + fn render_tile_gallery(&mut self, constraints: &[MapChunk], chunk_size: i32, build_data : &mut BuilderMap) { + build_data.map = Map::new(build_data.map.depth, build_data.width, build_data.height, &build_data.map.name); + let mut counter = 0; + let mut x = 1; + let mut y = 1; + while counter < constraints.len() { + render_pattern_to_map(&mut build_data.map, &constraints[counter], chunk_size, x, y); + + x += chunk_size + 1; + if x + chunk_size > build_data.map.width { + // Move to the next row + x = 1; + y += chunk_size + 1; + + if y + chunk_size > build_data.map.height { + // Move to the next page + build_data.take_snapshot(); + build_data.map = Map::new(build_data.map.depth, build_data.width, build_data.height, &build_data.map.name); + + x = 1; + y = 1; + } + } + + counter += 1; + } + build_data.take_snapshot(); + } +} diff --git a/chapter-75-darkplaza/src/map_builders/waveform_collapse/solver.rs b/chapter-75-darkplaza/src/map_builders/waveform_collapse/solver.rs new file mode 100644 index 00000000..9599d3f9 --- /dev/null +++ b/chapter-75-darkplaza/src/map_builders/waveform_collapse/solver.rs @@ -0,0 +1,228 @@ +use super::{MapChunk, Map}; +use std::collections::HashSet; + +pub struct Solver { + constraints: Vec, + chunk_size : i32, + chunks : Vec>, + chunks_x : usize, + chunks_y : usize, + remaining : Vec<(usize, i32)>, // (index, # neighbors) + pub possible: bool +} + +impl Solver { + pub fn new(constraints: Vec, chunk_size: i32, map : &Map) -> Solver { + let chunks_x = (map.width / chunk_size) as usize; + let chunks_y = (map.height / chunk_size) as usize; + let mut remaining : Vec<(usize, i32)> = Vec::new(); + for i in 0..(chunks_x*chunks_y) { + remaining.push((i, 0)); + } + + Solver { + constraints, + chunk_size, + chunks: vec![None; chunks_x * chunks_y], + chunks_x, + chunks_y, + remaining, + possible: true + } + } + + fn chunk_idx(&self, x:usize, y:usize) -> usize { + ((y * self.chunks_x) + x) as usize + } + + fn count_neighbors(&self, chunk_x:usize, chunk_y:usize) -> i32 { + let mut neighbors = 0; + + if chunk_x > 0 { + let left_idx = self.chunk_idx(chunk_x-1, chunk_y); + match self.chunks[left_idx] { + None => {} + Some(_) => { + neighbors += 1; + } + } + } + + if chunk_x < self.chunks_x-1 { + let right_idx = self.chunk_idx(chunk_x+1, chunk_y); + match self.chunks[right_idx] { + None => {} + Some(_) => { + neighbors += 1; + } + } + } + + if chunk_y > 0 { + let up_idx = self.chunk_idx(chunk_x, chunk_y-1); + match self.chunks[up_idx] { + None => {} + Some(_) => { + neighbors += 1; + } + } + } + + if chunk_y < self.chunks_y-1 { + let down_idx = self.chunk_idx(chunk_x, chunk_y+1); + match self.chunks[down_idx] { + None => {} + Some(_) => { + neighbors += 1; + } + } + } + neighbors + } + + pub fn iteration(&mut self, map: &mut Map) -> bool { + if self.remaining.is_empty() { return true; } + + // Populate the neighbor count of the remaining list + let mut remain_copy = self.remaining.clone(); + let mut neighbors_exist = false; + for r in remain_copy.iter_mut() { + let idx = r.0; + let chunk_x = idx % self.chunks_x; + let chunk_y = idx / self.chunks_x; + let neighbor_count = self.count_neighbors(chunk_x, chunk_y); + if neighbor_count > 0 { neighbors_exist = true; } + *r = (r.0, neighbor_count); + } + remain_copy.sort_by(|a,b| b.1.cmp(&a.1)); + self.remaining = remain_copy; + + // Pick a random chunk we haven't dealt with yet and get its index, remove from remaining list + let remaining_index = if !neighbors_exist { + (crate::rng::roll_dice(1, self.remaining.len() as i32)-1) as usize + } else { + 0usize + }; + let chunk_index = self.remaining[remaining_index].0; + self.remaining.remove(remaining_index); + + let chunk_x = chunk_index % self.chunks_x; + let chunk_y = chunk_index / self.chunks_x; + + let mut neighbors = 0; + let mut options : Vec> = Vec::new(); + + if chunk_x > 0 { + let left_idx = self.chunk_idx(chunk_x-1, chunk_y); + match self.chunks[left_idx] { + None => {} + Some(nt) => { + neighbors += 1; + options.push(self.constraints[nt].compatible_with[3].clone()); + } + } + } + + if chunk_x < self.chunks_x-1 { + let right_idx = self.chunk_idx(chunk_x+1, chunk_y); + match self.chunks[right_idx] { + None => {} + Some(nt) => { + neighbors += 1; + options.push(self.constraints[nt].compatible_with[2].clone()); + } + } + } + + if chunk_y > 0 { + let up_idx = self.chunk_idx(chunk_x, chunk_y-1); + match self.chunks[up_idx] { + None => {} + Some(nt) => { + neighbors += 1; + options.push(self.constraints[nt].compatible_with[1].clone()); + } + } + } + + if chunk_y < self.chunks_y-1 { + let down_idx = self.chunk_idx(chunk_x, chunk_y+1); + match self.chunks[down_idx] { + None => {} + Some(nt) => { + neighbors += 1; + options.push(self.constraints[nt].compatible_with[0].clone()); + } + } + } + + if neighbors == 0 { + // There is nothing nearby, so we can have anything! + let new_chunk_idx = (crate::rng::roll_dice(1, self.constraints.len() as i32)-1) as usize; + self.chunks[chunk_index] = Some(new_chunk_idx); + let left_x = chunk_x as i32 * self.chunk_size as i32; + let right_x = (chunk_x as i32+1) * self.chunk_size as i32; + let top_y = chunk_y as i32 * self.chunk_size as i32; + let bottom_y = (chunk_y as i32+1) * self.chunk_size as i32; + + + let mut i : usize = 0; + for y in top_y .. bottom_y { + for x in left_x .. right_x { + let mapidx = map.xy_idx(x, y); + let tile = self.constraints[new_chunk_idx].pattern[i]; + map.tiles[mapidx] = tile; + i += 1; + } + } + } + else { + // There are neighbors, so we try to be compatible with them + let mut options_to_check : HashSet = HashSet::new(); + for o in options.iter() { + for i in o.iter() { + options_to_check.insert(*i); + } + } + + let mut possible_options : Vec = Vec::new(); + for new_chunk_idx in options_to_check.iter() { + let mut possible = true; + for o in options.iter() { + if !o.contains(new_chunk_idx) { possible = false; } + } + if possible { + possible_options.push(*new_chunk_idx); + } + } + + if possible_options.is_empty() { + rltk::console::log("Oh no! It's not possible!"); + self.possible = false; + return true; + } else { + let new_chunk_idx = if possible_options.len() == 1 { 0 } + else { crate::rng::roll_dice(1, possible_options.len() as i32)-1 }; + + self.chunks[chunk_index] = Some(possible_options[new_chunk_idx as usize]); + let left_x = chunk_x as i32 * self.chunk_size as i32; + let right_x = (chunk_x as i32+1) * self.chunk_size as i32; + let top_y = chunk_y as i32 * self.chunk_size as i32; + let bottom_y = (chunk_y as i32+1) * self.chunk_size as i32; + + + let mut i : usize = 0; + for y in top_y .. bottom_y { + for x in left_x .. right_x { + let mapidx = map.xy_idx(x, y); + let tile = self.constraints[possible_options[new_chunk_idx as usize]].pattern[i]; + map.tiles[mapidx] = tile; + i += 1; + } + } + } + } + + false + } +} diff --git a/chapter-75-darkplaza/src/player.rs b/chapter-75-darkplaza/src/player.rs new file mode 100644 index 00000000..0dbda903 --- /dev/null +++ b/chapter-75-darkplaza/src/player.rs @@ -0,0 +1,485 @@ +use rltk::{VirtualKeyCode, Rltk, Point}; +use specs::prelude::*; +use std::cmp::{max, min}; +use super::{Position, Player, Viewshed, State, Map, RunState, Attributes, WantsToMelee, Item, + WantsToPickupItem, TileType, HungerClock, HungerState, + EntityMoved, Door, BlocksTile, BlocksVisibility, Renderable, Pools, Faction, + raws::Reaction, Vendor, VendorMode, WantsToCastSpell, Target, Equipped, Weapon, + WantsToShoot, Name}; + +fn get_player_target_list(ecs : &mut World) -> Vec<(f32,Entity)> { + let mut possible_targets : Vec<(f32,Entity)> = Vec::new(); + let viewsheds = ecs.read_storage::(); + let player_entity = ecs.fetch::(); + let equipped = ecs.read_storage::(); + let weapon = ecs.read_storage::(); + let map = ecs.fetch::(); + let positions = ecs.read_storage::(); + let factions = ecs.read_storage::(); + for (equipped, weapon) in (&equipped, &weapon).join() { + if equipped.owner == *player_entity && weapon.range.is_some() { + let range = weapon.range.unwrap(); + + if let Some(vs) = viewsheds.get(*player_entity) { + let player_pos = positions.get(*player_entity).unwrap(); + for tile_point in vs.visible_tiles.iter() { + let tile_idx = map.xy_idx(tile_point.x, tile_point.y); + let distance_to_target = rltk::DistanceAlg::Pythagoras.distance2d(*tile_point, rltk::Point::new(player_pos.x, player_pos.y)); + if distance_to_target < range as f32 { + crate::spatial::for_each_tile_content(tile_idx, |possible_target| { + if possible_target != *player_entity && factions.get(possible_target).is_some() { + possible_targets.push((distance_to_target, possible_target)); + } + }); + } + } + } + } + } + + possible_targets.sort_by(|a,b| a.0.partial_cmp(&b.0).unwrap()); + possible_targets +} + +pub fn end_turn_targeting(ecs: &mut World) { + let possible_targets = get_player_target_list(ecs); + let mut targets = ecs.write_storage::(); + targets.clear(); + + if !possible_targets.is_empty() { + targets.insert(possible_targets[0].1, Target{}).expect("Insert fail"); + } +} + +fn fire_on_target(ecs: &mut World) -> RunState { + let targets = ecs.write_storage::(); + let entities = ecs.entities(); + let mut current_target : Option = None; + + for (e,_t) in (&entities, &targets).join() { + current_target = Some(e); + } + + if let Some(target) = current_target { + let player_entity = ecs.fetch::(); + let mut shoot_store = ecs.write_storage::(); + let names = ecs.read_storage::(); + if let Some(name) = names.get(target) { + crate::gamelog::Logger::new() + .append("You fire at") + .color(rltk::CYAN) + .append(&name.name) + .log(); + } + shoot_store.insert(*player_entity, WantsToShoot{ target }).expect("Insert Fail"); + + RunState::Ticking + } else { + crate::gamelog::Logger::new().append("You don't have a target selected!").log(); + RunState::AwaitingInput + } + +} + +fn cycle_target(ecs: &mut World) { + let possible_targets = get_player_target_list(ecs); + let mut targets = ecs.write_storage::(); + let entities = ecs.entities(); + let mut current_target : Option = None; + + for (e,_t) in (&entities, &targets).join() { + current_target = Some(e); + } + + targets.clear(); + if let Some(current_target) = current_target { + if !possible_targets.len() > 1 { + let mut index = 0; + for (i, target) in possible_targets.iter().enumerate() { + if target.1 == current_target { + index = i; + } + } + + if index > possible_targets.len()-2 { + targets.insert(possible_targets[0].1, Target{}).expect("Insert fail"); + } else { + targets.insert(possible_targets[index+1].1, Target{}).expect("Insert fail"); + } + } + } +} + +pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState { + let mut positions = ecs.write_storage::(); + let players = ecs.read_storage::(); + let mut viewsheds = ecs.write_storage::(); + let entities = ecs.entities(); + let combat_stats = ecs.read_storage::(); + let map = ecs.fetch::(); + let mut wants_to_melee = ecs.write_storage::(); + let mut entity_moved = ecs.write_storage::(); + let mut doors = ecs.write_storage::(); + let mut blocks_visibility = ecs.write_storage::(); + let mut blocks_movement = ecs.write_storage::(); + let mut renderables = ecs.write_storage::(); + let factions = ecs.read_storage::(); + let vendors = ecs.read_storage::(); + let mut result = RunState::AwaitingInput; + + let mut swap_entities : Vec<(Entity, i32, i32)> = Vec::new(); + + for (entity, _player, pos, viewshed) in (&entities, &players, &mut positions, &mut viewsheds).join() { + if pos.x + delta_x < 1 || pos.x + delta_x > map.width-1 || pos.y + delta_y < 1 || pos.y + delta_y > map.height-1 { return RunState::AwaitingInput; } + let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y); + + result = crate::spatial::for_each_tile_content_with_gamemode(destination_idx, |potential_target| { + if let Some(_vendor) = vendors.get(potential_target) { + return Some(RunState::ShowVendor{ vendor: potential_target, mode : VendorMode::Sell }); + } + + let mut hostile = true; + if combat_stats.get(potential_target).is_some() { + if let Some(faction) = factions.get(potential_target) { + let reaction = crate::raws::faction_reaction( + &faction.name, + "Player", + &crate::raws::RAWS.lock().unwrap() + ); + if reaction != Reaction::Attack { hostile = false; } + } + } + if !hostile { + // Note that we want to move the bystander + swap_entities.push((potential_target, pos.x, pos.y)); + + // Move the player + pos.x = min(map.width-1 , max(0, pos.x + delta_x)); + pos.y = min(map.height-1, max(0, pos.y + delta_y)); + entity_moved.insert(entity, EntityMoved{}).expect("Unable to insert marker"); + + viewshed.dirty = true; + let mut ppos = ecs.write_resource::(); + ppos.x = pos.x; + ppos.y = pos.y; + return Some(RunState::Ticking); + } else { + let target = combat_stats.get(potential_target); + if let Some(_target) = target { + wants_to_melee.insert(entity, WantsToMelee{ target: potential_target }).expect("Add target failed"); + return Some(RunState::Ticking); + } + } + let door = doors.get_mut(potential_target); + if let Some(door) = door { + door.open = true; + blocks_visibility.remove(potential_target); + blocks_movement.remove(potential_target); + let glyph = renderables.get_mut(potential_target).unwrap(); + glyph.glyph = rltk::to_cp437('/'); + viewshed.dirty = true; + return Some(RunState::Ticking); + } + None + }); + + if !crate::spatial::is_blocked(destination_idx) { + pos.x = min(map.width-1 , max(0, pos.x + delta_x)); + pos.y = min(map.height-1, max(0, pos.y + delta_y)); + entity_moved.insert(entity, EntityMoved{}).expect("Unable to insert marker"); + + viewshed.dirty = true; + let mut ppos = ecs.write_resource::(); + ppos.x = pos.x; + ppos.y = pos.y; + result = RunState::Ticking; + match map.tiles[destination_idx] { + TileType::DownStairs => result = RunState::NextLevel, + TileType::UpStairs => result = RunState::PreviousLevel, + _ => {} + } + } + } + + for m in swap_entities.iter() { + let their_pos = positions.get_mut(m.0); + if let Some(their_pos) = their_pos { + their_pos.x = m.1; + their_pos.y = m.2; + } + } + + result +} + +pub fn try_next_level(ecs: &mut World) -> bool { + let player_pos = ecs.fetch::(); + let map = ecs.fetch::(); + let player_idx = map.xy_idx(player_pos.x, player_pos.y); + if map.tiles[player_idx] == TileType::DownStairs { + true + } else { + crate::gamelog::Logger::new().append("There is no way down from here.").log(); + false + } +} + +pub fn try_previous_level(ecs: &mut World) -> bool { + let player_pos = ecs.fetch::(); + let map = ecs.fetch::(); + let player_idx = map.xy_idx(player_pos.x, player_pos.y); + if map.tiles[player_idx] == TileType::UpStairs { + true + } else { + crate::gamelog::Logger::new().append("There is no way up from here.").log(); + false + } +} + +fn get_item(ecs: &mut World) { + let player_pos = ecs.fetch::(); + let player_entity = ecs.fetch::(); + let entities = ecs.entities(); + let items = ecs.read_storage::(); + let positions = ecs.read_storage::(); + + let mut target_item : Option = None; + for (item_entity, _item, position) in (&entities, &items, &positions).join() { + if position.x == player_pos.x && position.y == player_pos.y { + target_item = Some(item_entity); + } + } + + match target_item { + None => crate::gamelog::Logger::new().append("There is nothing here to pick up.").log(), + Some(item) => { + let mut pickup = ecs.write_storage::(); + pickup.insert(*player_entity, WantsToPickupItem{ collected_by: *player_entity, item }).expect("Unable to insert want to pickup"); + } + } +} + +fn skip_turn(ecs: &mut World) -> RunState { + let player_entity = ecs.fetch::(); + let viewshed_components = ecs.read_storage::(); + let factions = ecs.read_storage::(); + + let worldmap_resource = ecs.fetch::(); + + let mut can_heal = true; + let viewshed = viewshed_components.get(*player_entity).unwrap(); + for tile in viewshed.visible_tiles.iter() { + let idx = worldmap_resource.xy_idx(tile.x, tile.y); + crate::spatial::for_each_tile_content(idx, |entity_id| { + let faction = factions.get(entity_id); + match faction { + None => {} + Some(faction) => { + let reaction = crate::raws::faction_reaction( + &faction.name, + "Player", + &crate::raws::RAWS.lock().unwrap() + ); + if reaction == Reaction::Attack { + can_heal = false; + } + } + } + }); + } + + let hunger_clocks = ecs.read_storage::(); + let hc = hunger_clocks.get(*player_entity); + if let Some(hc) = hc { + match hc.state { + HungerState::Hungry => can_heal = false, + HungerState::Starving => can_heal = false, + _ => {} + } + } + + if can_heal { + let mut health_components = ecs.write_storage::(); + let pools = health_components.get_mut(*player_entity).unwrap(); + pools.hit_points.current = i32::min(pools.hit_points.current + 1, pools.hit_points.max); + if crate::rng::roll_dice(1,6)==1 { + pools.mana.current = i32::min(pools.mana.current + 1, pools.mana.max); + } + } + + RunState::Ticking +} + +fn use_consumable_hotkey(gs: &mut State, key: i32) -> RunState { + use super::{Consumable, InBackpack, WantsToUseItem}; + + let consumables = gs.ecs.read_storage::(); + let backpack = gs.ecs.read_storage::(); + let player_entity = gs.ecs.fetch::(); + let entities = gs.ecs.entities(); + let mut carried_consumables = Vec::new(); + for (entity, carried_by, _consumable) in (&entities, &backpack, &consumables).join() { + if carried_by.owner == *player_entity { + carried_consumables.push(entity); + } + } + + if (key as usize) < carried_consumables.len() { + use crate::components::Ranged; + if let Some(ranged) = gs.ecs.read_storage::().get(carried_consumables[key as usize]) { + return RunState::ShowTargeting{ range: ranged.range, item: carried_consumables[key as usize] }; + } + let mut intent = gs.ecs.write_storage::(); + intent.insert( + *player_entity, + WantsToUseItem{ item: carried_consumables[key as usize], target: None } + ).expect("Unable to insert intent"); + return RunState::Ticking; + } + RunState::Ticking +} + +fn use_spell_hotkey(gs: &mut State, key: i32) -> RunState { + use super::KnownSpells; + use super::raws::find_spell_entity; + + let player_entity = gs.ecs.fetch::(); + let known_spells_storage = gs.ecs.read_storage::(); + let known_spells = &known_spells_storage.get(*player_entity).unwrap().spells; + + if (key as usize) < known_spells.len() { + let pools = gs.ecs.read_storage::(); + let player_pools = pools.get(*player_entity).unwrap(); + if player_pools.mana.current >= known_spells[key as usize].mana_cost { + if let Some(spell_entity) = find_spell_entity(&gs.ecs, &known_spells[key as usize].display_name) { + use crate::components::Ranged; + if let Some(ranged) = gs.ecs.read_storage::().get(spell_entity) { + return RunState::ShowTargeting{ range: ranged.range, item: spell_entity }; + }; + let mut intent = gs.ecs.write_storage::(); + intent.insert( + *player_entity, + WantsToCastSpell{ spell: spell_entity, target: None } + ).expect("Unable to insert intent"); + return RunState::Ticking; + } + } else { + crate::gamelog::Logger::new().append("You don't have enough mana to cast that!").log(); + } + } + + RunState::Ticking +} + +pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState { + // Hotkeys + if ctx.shift && ctx.key.is_some() { + let key : Option = + match ctx.key.unwrap() { + VirtualKeyCode::Key1 => Some(1), + VirtualKeyCode::Key2 => Some(2), + VirtualKeyCode::Key3 => Some(3), + VirtualKeyCode::Key4 => Some(4), + VirtualKeyCode::Key5 => Some(5), + VirtualKeyCode::Key6 => Some(6), + VirtualKeyCode::Key7 => Some(7), + VirtualKeyCode::Key8 => Some(8), + VirtualKeyCode::Key9 => Some(9), + _ => None + }; + if let Some(key) = key { + return use_consumable_hotkey(gs, key-1); + } + } + if ctx.control && ctx.key.is_some() { + let key : Option = + match ctx.key.unwrap() { + VirtualKeyCode::Key1 => Some(1), + VirtualKeyCode::Key2 => Some(2), + VirtualKeyCode::Key3 => Some(3), + VirtualKeyCode::Key4 => Some(4), + VirtualKeyCode::Key5 => Some(5), + VirtualKeyCode::Key6 => Some(6), + VirtualKeyCode::Key7 => Some(7), + VirtualKeyCode::Key8 => Some(8), + VirtualKeyCode::Key9 => Some(9), + _ => None + }; + if let Some(key) = key { + return use_spell_hotkey(gs, key-1); + } + } + + // Player movement + match ctx.key { + None => { return RunState::AwaitingInput } // Nothing happened + Some(key) => match key { + VirtualKeyCode::Left | + VirtualKeyCode::Numpad4 | + VirtualKeyCode::H => return try_move_player(-1, 0, &mut gs.ecs), + + VirtualKeyCode::Right | + VirtualKeyCode::Numpad6 | + VirtualKeyCode::L => return try_move_player(1, 0, &mut gs.ecs), + + VirtualKeyCode::Up | + VirtualKeyCode::Numpad8 | + VirtualKeyCode::K => return try_move_player(0, -1, &mut gs.ecs), + + VirtualKeyCode::Down | + VirtualKeyCode::Numpad2 | + VirtualKeyCode::J => return try_move_player(0, 1, &mut gs.ecs), + + // Diagonals + VirtualKeyCode::Numpad9 | + VirtualKeyCode::U => return try_move_player(1, -1, &mut gs.ecs), + + VirtualKeyCode::Numpad7 | + VirtualKeyCode::Y => return try_move_player(-1, -1, &mut gs.ecs), + + VirtualKeyCode::Numpad3 | + VirtualKeyCode::N => return try_move_player(1, 1, &mut gs.ecs), + + VirtualKeyCode::Numpad1 | + VirtualKeyCode::B => return try_move_player(-1, 1, &mut gs.ecs), + + // Skip Turn + VirtualKeyCode::Numpad5 | + VirtualKeyCode::Space => return skip_turn(&mut gs.ecs), + + // Level changes + VirtualKeyCode::Period => { + if try_next_level(&mut gs.ecs) { + return RunState::NextLevel; + } + } + VirtualKeyCode::Comma => { + if try_previous_level(&mut gs.ecs) { + return RunState::PreviousLevel; + } + } + + // Picking up items + VirtualKeyCode::G => get_item(&mut gs.ecs), + VirtualKeyCode::I => return RunState::ShowInventory, + VirtualKeyCode::D => return RunState::ShowDropItem, + VirtualKeyCode::R => return RunState::ShowRemoveItem, + + // Ranged + VirtualKeyCode::V => { + cycle_target(&mut gs.ecs); + return RunState::AwaitingInput; + } + VirtualKeyCode::F => return fire_on_target(&mut gs.ecs), + + // Save and Quit + VirtualKeyCode::Escape => return RunState::SaveGame, + + // Cheating! + VirtualKeyCode::Backslash => return RunState::ShowCheatMenu, + + _ => { return RunState::AwaitingInput } + }, + } + RunState::Ticking +} diff --git a/chapter-75-darkplaza/src/random_table.rs b/chapter-75-darkplaza/src/random_table.rs new file mode 100644 index 00000000..e70e88fe --- /dev/null +++ b/chapter-75-darkplaza/src/random_table.rs @@ -0,0 +1,83 @@ +use crate::raws::{SpawnTableType, spawn_type_by_name, RawMaster}; + +pub struct RandomEntry { + name : String, + weight : i32 +} + +impl RandomEntry { + pub fn new(name: S, weight: i32) -> RandomEntry { + RandomEntry{ name: name.to_string(), weight } + } +} + +#[derive(Default)] +pub struct MasterTable { + items : RandomTable, + mobs : RandomTable, + props : RandomTable +} + +impl MasterTable { + pub fn new() -> MasterTable { + MasterTable{ + items : RandomTable::new(), + mobs : RandomTable::new(), + props : RandomTable::new() + } + } + + pub fn add(&mut self, name : S, weight: i32, raws: &RawMaster) { + match spawn_type_by_name(raws, &name.to_string()) { + SpawnTableType::Item => self.items.add(name, weight), + SpawnTableType::Mob => self.mobs.add(name, weight), + SpawnTableType::Prop => self.props.add(name, weight), + } + } + + pub fn roll(&self) -> String { + let roll = crate::rng::roll_dice(1, 4); + match roll { + 1 => self.items.roll(), + 2 => self.props.roll(), + 3 => self.mobs.roll(), + _ => "None".to_string() + } + } +} + +#[derive(Default)] +pub struct RandomTable { + entries : Vec, + total_weight : i32 +} + +impl RandomTable { + pub fn new() -> RandomTable { + RandomTable{ entries: Vec::new(), total_weight: 0 } + } + + pub fn add(&mut self, name : S, weight: i32) { + if weight > 0 { + self.total_weight += weight; + self.entries.push(RandomEntry::new(name.to_string(), weight)); + } + } + + pub fn roll(&self) -> String { + if self.total_weight == 0 { return "None".to_string(); } + let mut roll = crate::rng::roll_dice(1, self.total_weight)-1; + let mut index : usize = 0; + + while roll > 0 { + if roll < self.entries[index].weight { + return self.entries[index].name.clone(); + } + + roll -= self.entries[index].weight; + index += 1; + } + + "None".to_string() + } +} diff --git a/chapter-75-darkplaza/src/raws/faction_structs.rs b/chapter-75-darkplaza/src/raws/faction_structs.rs new file mode 100644 index 00000000..6d0a51f4 --- /dev/null +++ b/chapter-75-darkplaza/src/raws/faction_structs.rs @@ -0,0 +1,13 @@ +use serde::{Deserialize}; +use std::collections::HashMap; + +#[derive(Deserialize, Debug)] +pub struct FactionInfo { + pub name : String, + pub responses : HashMap +} + +#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] +pub enum Reaction { + Ignore, Attack, Flee +} diff --git a/chapter-75-darkplaza/src/raws/item_structs.rs b/chapter-75-darkplaza/src/raws/item_structs.rs new file mode 100644 index 00000000..0b496791 --- /dev/null +++ b/chapter-75-darkplaza/src/raws/item_structs.rs @@ -0,0 +1,74 @@ +use serde::{Deserialize}; +use std::collections::HashMap; + +#[derive(Deserialize, Debug, Clone)] +pub struct Item { + pub name : String, + pub renderable : Option, + pub consumable : Option, + pub weapon : Option, + pub wearable : Option, + pub initiative_penalty : Option, + pub weight_lbs : Option, + pub base_value : Option, + pub vendor_category : Option, + pub magic : Option, + pub attributes : Option, + pub template_magic : Option +} + +#[derive(Deserialize, Debug, Clone)] +pub struct Renderable { + pub glyph: String, + pub fg : String, + pub bg : String, + pub order: i32, + pub x_size : Option, + pub y_size : Option +} + +#[derive(Deserialize, Debug, Clone)] +pub struct Consumable { + pub effects : HashMap, + pub charges : Option +} + +#[derive(Deserialize, Debug, Clone)] +pub struct Weapon { + pub range: String, + pub attribute: String, + pub base_damage: String, + pub hit_bonus: i32, + pub proc_chance : Option, + pub proc_target : Option, + pub proc_effects : Option> +} + +#[derive(Deserialize, Debug, Clone)] +pub struct Wearable { + pub armor_class: f32, + pub slot : String, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct MagicItem { + pub class: String, + pub naming: String, + pub cursed: Option +} + +#[derive(Deserialize, Debug, Clone)] +pub struct ItemAttributeBonus { + pub might : Option, + pub fitness : Option, + pub quickness : Option, + pub intelligence : Option +} + +#[derive(Deserialize, Debug, Clone)] +pub struct ItemMagicTemplate { + pub unidentified_name: String, + pub bonus_min: i32, + pub bonus_max: i32, + pub include_cursed: bool +} diff --git a/chapter-75-darkplaza/src/raws/loot_structs.rs b/chapter-75-darkplaza/src/raws/loot_structs.rs new file mode 100644 index 00000000..3e1de3d4 --- /dev/null +++ b/chapter-75-darkplaza/src/raws/loot_structs.rs @@ -0,0 +1,13 @@ +use serde::{Deserialize}; + +#[derive(Deserialize, Debug)] +pub struct LootTable { + pub name : String, + pub drops : Vec +} + +#[derive(Deserialize, Debug)] +pub struct LootDrop { + pub name : String, + pub weight : i32 +} diff --git a/chapter-75-darkplaza/src/raws/mob_structs.rs b/chapter-75-darkplaza/src/raws/mob_structs.rs new file mode 100644 index 00000000..138c89e5 --- /dev/null +++ b/chapter-75-darkplaza/src/raws/mob_structs.rs @@ -0,0 +1,63 @@ +use serde::{Deserialize}; +use super::{Renderable}; +use std::collections::HashMap; + +#[derive(Deserialize, Debug)] +pub struct Mob { + pub name : String, + pub renderable : Option, + pub blocks_tile : bool, + pub vision_range : i32, + pub movement : String, + pub quips : Option>, + pub attributes : MobAttributes, + pub skills : Option>, + pub level : Option, + pub hp : Option, + pub mana : Option, + pub equipped : Option>, + pub natural : Option, + pub loot_table : Option, + pub light : Option, + pub faction : Option, + pub gold : Option, + pub vendor : Option>, + pub abilities : Option>, + pub on_death : Option> +} + +#[derive(Deserialize, Debug)] +pub struct MobAttributes { + pub might : Option, + pub fitness : Option, + pub quickness : Option, + pub intelligence : Option +} + +#[derive(Deserialize, Debug)] +pub struct MobNatural { + pub armor_class : Option, + pub attacks: Option> +} + +#[derive(Deserialize, Debug)] +pub struct NaturalAttack { + pub name : String, + pub hit_bonus : i32, + pub damage : String +} + + +#[derive(Deserialize, Debug)] +pub struct MobLight { + pub range : i32, + pub color : String +} + +#[derive(Deserialize, Debug)] +pub struct MobAbility { + pub spell : String, + pub chance : f32, + pub range : f32, + pub min_range : f32 +} diff --git a/chapter-75-darkplaza/src/raws/mod.rs b/chapter-75-darkplaza/src/raws/mod.rs new file mode 100644 index 00000000..6e3f5546 --- /dev/null +++ b/chapter-75-darkplaza/src/raws/mod.rs @@ -0,0 +1,53 @@ +mod item_structs; +use item_structs::*; +mod mob_structs; +use mob_structs::*; +mod prop_structs; +use prop_structs::*; +mod spawn_table_structs; +use spawn_table_structs::*; +mod loot_structs; +use loot_structs::*; +mod faction_structs; +pub use faction_structs::*; +mod spell_structs; +pub use spell_structs::Spell; +mod weapon_traits; +pub use weapon_traits::*; + +mod rawmaster; +pub use rawmaster::*; +use serde::{Deserialize}; +use std::sync::Mutex; + +rltk::embedded_resource!(RAW_FILE, "../../raws/spawns.json"); + +lazy_static! { + pub static ref RAWS : Mutex = Mutex::new(RawMaster::empty()); +} + +#[derive(Deserialize, Debug)] +pub struct Raws { + pub items : Vec, + pub mobs : Vec, + pub props : Vec, + pub spawn_table : Vec, + pub loot_tables : Vec, + pub faction_table : Vec, + pub spells : Vec, + pub weapon_traits : Vec +} + +pub fn load_raws() { + rltk::link_resource!(RAW_FILE, "../../raws/spawns.json"); + + // Retrieve the raw data as an array of u8 (8-bit unsigned chars) + let raw_data = rltk::embedding::EMBED + .lock() + .get_resource("../../raws/spawns.json".to_string()) + .unwrap(); + let raw_string = std::str::from_utf8(&raw_data).expect("Unable to convert to a valid UTF-8 string."); + let decoder : Raws = serde_json::from_str(&raw_string).expect("Unable to parse JSON"); + + RAWS.lock().unwrap().load(decoder); +} diff --git a/chapter-75-darkplaza/src/raws/prop_structs.rs b/chapter-75-darkplaza/src/raws/prop_structs.rs new file mode 100644 index 00000000..a62ce51f --- /dev/null +++ b/chapter-75-darkplaza/src/raws/prop_structs.rs @@ -0,0 +1,20 @@ +use serde::{Deserialize}; +use super::{Renderable}; +use std::collections::HashMap; + +#[derive(Deserialize, Debug)] +pub struct Prop { + pub name : String, + pub renderable : Option, + pub hidden : Option, + pub blocks_tile : Option, + pub blocks_visibility : Option, + pub door_open : Option, + pub entry_trigger : Option, + pub light : Option, +} + +#[derive(Deserialize, Debug)] +pub struct EntryTrigger { + pub effects : HashMap +} diff --git a/chapter-75-darkplaza/src/raws/rawmaster.rs b/chapter-75-darkplaza/src/raws/rawmaster.rs new file mode 100644 index 00000000..a409cef9 --- /dev/null +++ b/chapter-75-darkplaza/src/raws/rawmaster.rs @@ -0,0 +1,870 @@ +use std::collections::{HashMap, HashSet}; +use specs::prelude::*; +use crate::components::*; +use super::{Raws, faction_structs::Reaction}; +use crate::random_table::{MasterTable, RandomTable}; +use crate::{attr_bonus, npc_hp, mana_at_level}; +use regex::Regex; +use specs::saveload::{MarkedBuilder, SimpleMarker}; + +pub fn parse_dice_string(dice : &str) -> (i32, i32, i32) { + lazy_static! { + static ref DICE_RE : Regex = Regex::new(r"(\d+)d(\d+)([\+\-]\d+)?").unwrap(); + } + let mut n_dice = 1; + let mut die_type = 4; + let mut die_bonus = 0; + for cap in DICE_RE.captures_iter(dice) { + if let Some(group) = cap.get(1) { + n_dice = group.as_str().parse::().expect("Not a digit"); + } + if let Some(group) = cap.get(2) { + die_type = group.as_str().parse::().expect("Not a digit"); + } + if let Some(group) = cap.get(3) { + die_bonus = group.as_str().parse::().expect("Not a digit"); + } + + } + (n_dice, die_type, die_bonus) +} + +#[derive(PartialEq, Eq, Hash, Copy, Clone)] +pub enum SpawnType { + AtPosition { x: i32, y: i32 }, + Equipped { by: Entity }, + Carried { by: Entity } +} + +pub struct RawMaster { + raws : Raws, + item_index : HashMap, + mob_index : HashMap, + prop_index : HashMap, + loot_index : HashMap, + faction_index : HashMap>, + spell_index : HashMap +} + +struct NewMagicItem { + name : String, + bonus : i32 +} + +impl RawMaster { + pub fn empty() -> RawMaster { + RawMaster { + raws : Raws{ + items: Vec::new(), + mobs: Vec::new(), + props: Vec::new(), + spawn_table: Vec::new(), + loot_tables: Vec::new(), + faction_table : Vec::new(), + spells : Vec::new(), + weapon_traits : Vec::new() + }, + item_index : HashMap::new(), + mob_index : HashMap::new(), + prop_index : HashMap::new(), + loot_index : HashMap::new(), + faction_index : HashMap::new(), + spell_index : HashMap::new() + } + } + + fn append_magic_template(items_to_build : &mut Vec, item : &super::Item) { + if let Some(template) = &item.template_magic { + if item.weapon.is_some() || item.wearable.is_some() { + if template.include_cursed { + items_to_build.push(NewMagicItem{ + name : item.name.clone(), + bonus : -1 + }); + } + for bonus in template.bonus_min ..= template.bonus_max { + items_to_build.push(NewMagicItem{ + name : item.name.clone(), + bonus + }); + } + } else { + rltk::console::log(format!("{} is marked as templated, but isn't a weapon or armor.", item.name)); + } + } + } + + fn build_base_magic_item(&self, nmw : &NewMagicItem) -> super::Item { + let base_item_index = self.item_index[&nmw.name]; + let mut base_item_copy = self.raws.items[base_item_index].clone(); + + if nmw.bonus == -1 { + base_item_copy.name = format!("{} -1", nmw.name); + } else { + base_item_copy.name = format!("{} +{}", nmw.name, nmw.bonus); + } + + base_item_copy.magic = Some(super::MagicItem{ + class : match nmw.bonus { + 2 => "rare".to_string(), + 3 => "rare".to_string(), + 4 => "rare".to_string(), + 5 => "legendary".to_string(), + _ => "common".to_string() + }, + naming : base_item_copy.template_magic.as_ref().unwrap().unidentified_name.clone(), + cursed: if nmw.bonus == -1 { Some(true) } else { None } + }); + + if let Some(initiative_penalty) = base_item_copy.initiative_penalty.as_mut() { + *initiative_penalty -= nmw.bonus as f32; + } + if let Some(base_value) = base_item_copy.base_value.as_mut() { + *base_value += (nmw.bonus as f32 + 1.0) * 50.0; + } + if let Some(mut weapon) = base_item_copy.weapon.as_mut() { + weapon.hit_bonus += nmw.bonus; + let (n,die,plus) = parse_dice_string(&weapon.base_damage); + let final_bonus = plus+nmw.bonus; + if final_bonus > 0 { + weapon.base_damage = format!("{}d{}+{}", n, die, final_bonus); + } else if final_bonus < 0 { + weapon.base_damage = format!("{}d{}-{}", n, die, i32::abs(final_bonus)); + } + } + if let Some(mut armor) = base_item_copy.wearable.as_mut() { + armor.armor_class += nmw.bonus as f32; + } + base_item_copy + } + + fn build_magic_weapon_or_armor(&mut self, items_to_build : &[NewMagicItem]) { + for nmw in items_to_build.iter() { + let base_item_copy = self.build_base_magic_item(&nmw); + + let real_name = base_item_copy.name.clone(); + self.raws.items.push(base_item_copy); + self.item_index.insert(real_name.clone(), self.raws.items.len()-1); + + self.raws.spawn_table.push(super::SpawnTableEntry{ + name : real_name.clone(), + weight : 10 - i32::abs(nmw.bonus), + min_depth : 1 + i32::abs((nmw.bonus-1)*3), + max_depth : 100, + add_map_depth_to_weight : None + }); + } + } + + fn build_traited_weapons(&mut self, items_to_build : &[NewMagicItem]) { + items_to_build.iter().filter(|i| i.bonus > 0).for_each(|nmw| { + for wt in self.raws.weapon_traits.iter() { + let mut base_item_copy = self.build_base_magic_item(&nmw); + if let Some(mut weapon) = base_item_copy.weapon.as_mut() { + base_item_copy.name = format!("{} {}", wt.name, base_item_copy.name); + if let Some(base_value) = base_item_copy.base_value.as_mut() { + *base_value *= 2.0; + } + weapon.proc_chance = Some(0.25); + weapon.proc_effects = Some(wt.effects.clone()); + + let real_name = base_item_copy.name.clone(); + self.raws.items.push(base_item_copy); + self.item_index.insert(real_name.clone(), self.raws.items.len()-1); + + self.raws.spawn_table.push(super::SpawnTableEntry{ + name : real_name.clone(), + weight : 9 - i32::abs(nmw.bonus), + min_depth : 2 + i32::abs((nmw.bonus-1)*3), + max_depth : 100, + add_map_depth_to_weight : None + }); + } + } + }); + } + + pub fn load(&mut self, raws : Raws) { + self.raws = raws; + self.item_index = HashMap::new(); + let mut used_names : HashSet = HashSet::new(); + let mut items_to_build = Vec::new(); + + for (i,item) in self.raws.items.iter().enumerate() { + if used_names.contains(&item.name) { + rltk::console::log(format!("WARNING - duplicate item name in raws [{}]", item.name)); + } + self.item_index.insert(item.name.clone(), i); + used_names.insert(item.name.clone()); + + RawMaster::append_magic_template(&mut items_to_build, item); + } + for (i,mob) in self.raws.mobs.iter().enumerate() { + if used_names.contains(&mob.name) { + rltk::console::log(format!("WARNING - duplicate mob name in raws [{}]", mob.name)); + } + self.mob_index.insert(mob.name.clone(), i); + used_names.insert(mob.name.clone()); + } + for (i,prop) in self.raws.props.iter().enumerate() { + if used_names.contains(&prop.name) { + rltk::console::log(format!("WARNING - duplicate prop name in raws [{}]", prop.name)); + } + self.prop_index.insert(prop.name.clone(), i); + used_names.insert(prop.name.clone()); + } + + for spawn in self.raws.spawn_table.iter() { + if !used_names.contains(&spawn.name) { + rltk::console::log(format!("WARNING - Spawn tables references unspecified entity {}", spawn.name)); + } + } + + for (i,loot) in self.raws.loot_tables.iter().enumerate() { + self.loot_index.insert(loot.name.clone(), i); + } + + for faction in self.raws.faction_table.iter() { + let mut reactions : HashMap = HashMap::new(); + for other in faction.responses.iter() { + reactions.insert( + other.0.clone(), + match other.1.as_str() { + "ignore" => Reaction::Ignore, + "flee" => Reaction::Flee, + _ => Reaction::Attack + } + ); + } + self.faction_index.insert(faction.name.clone(), reactions); + } + + for (i,spell) in self.raws.spells.iter().enumerate() { + self.spell_index.insert(spell.name.clone(), i); + } + + self.build_magic_weapon_or_armor(&items_to_build); + self.build_traited_weapons(&items_to_build); + } +} + +#[inline(always)] +pub fn faction_reaction(my_faction : &str, their_faction : &str, raws : &RawMaster) -> Reaction { + //println!("Looking for reaction to [{}] by [{}]", my_faction, their_faction); + if raws.faction_index.contains_key(my_faction) { + let mf = &raws.faction_index[my_faction]; + if mf.contains_key(their_faction) { + //println!(" : {:?}", mf[their_faction]); + return mf[their_faction]; + } else if mf.contains_key("Default") { + //println!(" : {:?}", mf["Default"]); + return mf["Default"]; + } else { + //println!(" : IGNORE"); + return Reaction::Ignore; + } + } + //println!(" : IGNORE"); + Reaction::Ignore +} + +fn find_slot_for_equippable_item(tag : &str, raws: &RawMaster) -> EquipmentSlot { + if !raws.item_index.contains_key(tag) { + panic!("Trying to equip an unknown item: {}", tag); + } + let item_index = raws.item_index[tag]; + let item = &raws.raws.items[item_index]; + if let Some(_wpn) = &item.weapon { + return EquipmentSlot::Melee; + } else if let Some(wearable) = &item.wearable { + return string_to_slot(&wearable.slot); + } + panic!("Trying to equip {}, but it has no slot tag.", tag); +} + +pub fn get_vendor_items(categories: &[String], raws : &RawMaster) -> Vec<(String, f32)> { + let mut result : Vec<(String, f32)> = Vec::new(); + + for item in raws.raws.items.iter() { + if let Some(cat) = &item.vendor_category { + if categories.contains(cat) && item.base_value.is_some() { + result.push(( + item.name.clone(), + item.base_value.unwrap() + )); + } + } + } + + result +} + +pub fn get_scroll_tags() -> Vec { + let raws = &super::RAWS.lock().unwrap(); + let mut result = Vec::new(); + + for item in raws.raws.items.iter() { + if let Some(magic) = &item.magic { + if &magic.naming == "scroll" { + result.push(item.name.clone()); + } + } + } + + result +} + +pub fn get_potion_tags() -> Vec { + let raws = &super::RAWS.lock().unwrap(); + let mut result = Vec::new(); + + for item in raws.raws.items.iter() { + if let Some(magic) = &item.magic { + if &magic.naming == "potion" { + result.push(item.name.clone()); + } + } + } + + result +} + +pub fn is_tag_magic(tag : &str) -> bool { + let raws = &super::RAWS.lock().unwrap(); + if raws.item_index.contains_key(tag) { + let item_template = &raws.raws.items[raws.item_index[tag]]; + item_template.magic.is_some() + } else { + false + } +} + +fn spawn_position<'a>(pos : SpawnType, new_entity : EntityBuilder<'a>, tag : &str, raws: &RawMaster) -> EntityBuilder<'a> { + let eb = new_entity; + + // Spawn in the specified location + match pos { + SpawnType::AtPosition{x,y} => eb.with(Position{ x, y }), + SpawnType::Carried{by} => eb.with(InBackpack{ owner: by }), + SpawnType::Equipped{by} => { + let slot = find_slot_for_equippable_item(tag, raws); + eb.with(Equipped{ owner: by, slot }) + } + } +} + +fn get_renderable_component(renderable : &super::item_structs::Renderable) -> crate::components::Renderable { + crate::components::Renderable{ + glyph: rltk::to_cp437(renderable.glyph.chars().next().unwrap()), + fg : rltk::RGB::from_hex(&renderable.fg).expect("Invalid RGB"), + bg : rltk::RGB::from_hex(&renderable.bg).expect("Invalid RGB"), + render_order : renderable.order + } +} + +pub fn string_to_slot(slot : &str) -> EquipmentSlot { + match slot { + "Shield" => EquipmentSlot::Shield, + "Head" => EquipmentSlot::Head, + "Torso" => EquipmentSlot::Torso, + "Legs" => EquipmentSlot::Legs, + "Feet" => EquipmentSlot::Feet, + "Hands" => EquipmentSlot::Hands, + "Melee" => EquipmentSlot::Melee, + _ => { rltk::console::log(format!("Warning: unknown equipment slot type [{}])", slot)); EquipmentSlot::Melee } + } +} + +fn parse_particle_line(n : &str) -> SpawnParticleLine { + let tokens : Vec<_> = n.split(';').collect(); + SpawnParticleLine{ + glyph : rltk::to_cp437(tokens[0].chars().next().unwrap()), + color : rltk::RGB::from_hex(tokens[1]).expect("Bad RGB"), + lifetime_ms : tokens[2].parse::().unwrap() + } +} + +fn parse_particle(n : &str) -> SpawnParticleBurst { + let tokens : Vec<_> = n.split(';').collect(); + SpawnParticleBurst{ + glyph : rltk::to_cp437(tokens[0].chars().next().unwrap()), + color : rltk::RGB::from_hex(tokens[1]).expect("Bad RGB"), + lifetime_ms : tokens[2].parse::().unwrap() + } +} + +macro_rules! apply_effects { + ( $effects:expr, $eb:expr ) => { + for effect in $effects.iter() { + let effect_name = effect.0.as_str(); + match effect_name { + "provides_healing" => $eb = $eb.with(ProvidesHealing{ heal_amount: effect.1.parse::().unwrap() }), + "provides_mana" => $eb = $eb.with(ProvidesMana{ mana_amount: effect.1.parse::().unwrap() }), + "teach_spell" => $eb = $eb.with(TeachesSpell{ spell: effect.1.to_string() }), + "ranged" => $eb = $eb.with(Ranged{ range: effect.1.parse::().unwrap() }), + "damage" => $eb = $eb.with(InflictsDamage{ damage : effect.1.parse::().unwrap() }), + "area_of_effect" => $eb = $eb.with(AreaOfEffect{ radius: effect.1.parse::().unwrap() }), + "confusion" => { + $eb = $eb.with(Confusion{}); + $eb = $eb.with(Duration{ turns: effect.1.parse::().unwrap() }); + } + "magic_mapping" => $eb = $eb.with(MagicMapper{}), + "town_portal" => $eb = $eb.with(TownPortal{}), + "food" => $eb = $eb.with(ProvidesFood{}), + "single_activation" => $eb = $eb.with(SingleActivation{}), + "particle_line" => $eb = $eb.with(parse_particle_line(&effect.1)), + "particle" => $eb = $eb.with(parse_particle(&effect.1)), + "remove_curse" => $eb = $eb.with(ProvidesRemoveCurse{}), + "identify" => $eb = $eb.with(ProvidesIdentification{}), + "slow" => $eb = $eb.with(Slow{ initiative_penalty : effect.1.parse::().unwrap() }), + "damage_over_time" => $eb = $eb.with( DamageOverTime { damage : effect.1.parse::().unwrap() } ), + "target_self" => $eb = $eb.with( AlwaysTargetsSelf{} ), + _ => rltk::console::log(format!("Warning: consumable effect {} not implemented.", effect_name)) + } + } + }; +} + +pub fn spawn_named_item(raws: &RawMaster, ecs : &mut World, key : &str, pos : SpawnType) -> Option { + if raws.item_index.contains_key(key) { + let item_template = &raws.raws.items[raws.item_index[key]]; + + let dm = ecs.fetch::(); + let scroll_names = dm.scroll_mappings.clone(); + let potion_names = dm.potion_mappings.clone(); + let identified = dm.identified_items.clone(); + std::mem::drop(dm); + let mut eb = ecs.create_entity().marked::>(); + + // Spawn in the specified location + eb = spawn_position(pos, eb, key, raws); + + // Renderable + if let Some(renderable) = &item_template.renderable { + eb = eb.with(get_renderable_component(renderable)); + if renderable.x_size.is_some() || renderable.y_size.is_some() { + eb = eb.with(TileSize{ x : renderable.x_size.unwrap_or(1), y : renderable.y_size.unwrap_or(1) }); + } + } + + eb = eb.with(Name{ name : item_template.name.clone() }); + + eb = eb.with(crate::components::Item{ + initiative_penalty : item_template.initiative_penalty.unwrap_or(0.0), + weight_lbs : item_template.weight_lbs.unwrap_or(0.0), + base_value : item_template.base_value.unwrap_or(0.0) + }); + + if let Some(consumable) = &item_template.consumable { + let max_charges = consumable.charges.unwrap_or(1); + eb = eb.with(crate::components::Consumable{ max_charges, charges : max_charges }); + apply_effects!(consumable.effects, eb); + } + + if let Some(weapon) = &item_template.weapon { + eb = eb.with(Equippable{ slot: EquipmentSlot::Melee }); + let (n_dice, die_type, bonus) = parse_dice_string(&weapon.base_damage); + let mut wpn = Weapon{ + range : if weapon.range == "melee" { None } else { Some(weapon.range.parse::().expect("Not a number")) }, + attribute : WeaponAttribute::Might, + damage_n_dice : n_dice, + damage_die_type : die_type, + damage_bonus : bonus, + hit_bonus : weapon.hit_bonus, + proc_chance : weapon.proc_chance, + proc_target : weapon.proc_target.clone() + }; + match weapon.attribute.as_str() { + "Quickness" => wpn.attribute = WeaponAttribute::Quickness, + _ => wpn.attribute = WeaponAttribute::Might + } + eb = eb.with(wpn); + if let Some(proc_effects) =& weapon.proc_effects { + apply_effects!(proc_effects, eb); + } + } + + if let Some(wearable) = &item_template.wearable { + let slot = string_to_slot(&wearable.slot); + eb = eb.with(Equippable{ slot }); + eb = eb.with(Wearable{ slot, armor_class: wearable.armor_class }); + } + + if let Some(magic) = &item_template.magic { + let class = match magic.class.as_str() { + "rare" => MagicItemClass::Rare, + "legendary" => MagicItemClass::Legendary, + _ => MagicItemClass::Common + }; + eb = eb.with(MagicItem{ class }); + + if !identified.contains(&item_template.name) { + match magic.naming.as_str() { + "scroll" => { + eb = eb.with(ObfuscatedName{ name : scroll_names[&item_template.name].clone() }); + } + "potion" => { + eb = eb.with(ObfuscatedName{ name: potion_names[&item_template.name].clone() }); + } + _ => { + eb = eb.with(ObfuscatedName{ name : magic.naming.clone() }); + } + } + } + + if let Some(cursed) = magic.cursed { + if cursed { eb = eb.with(CursedItem{}); } + } + } + + if let Some(ab) = &item_template.attributes { + eb = eb.with(AttributeBonus{ + might : ab.might, + fitness : ab.fitness, + quickness : ab.quickness, + intelligence : ab.intelligence, + }); + } + + return Some(eb.build()); + } + None +} + +#[allow(clippy::cognitive_complexity)] +pub fn spawn_named_mob(raws: &RawMaster, ecs : &mut World, key : &str, pos : SpawnType) -> Option { + if raws.mob_index.contains_key(key) { + let mob_template = &raws.raws.mobs[raws.mob_index[key]]; + + let mut eb = ecs.create_entity().marked::>(); + + // Spawn in the specified location + eb = spawn_position(pos, eb, key, raws); + + // Initiative of 2 + eb = eb.with(Initiative{current: 2}); + + // Renderable + if let Some(renderable) = &mob_template.renderable { + eb = eb.with(get_renderable_component(renderable)); + if renderable.x_size.is_some() || renderable.y_size.is_some() { + eb = eb.with(TileSize{ x : renderable.x_size.unwrap_or(1), y : renderable.y_size.unwrap_or(1) }); + } + } + + eb = eb.with(Name{ name : mob_template.name.clone() }); + + match mob_template.movement.as_ref() { + "random" => eb = eb.with(MoveMode{ mode: Movement::Random }), + "random_waypoint" => eb = eb.with(MoveMode{ mode: Movement::RandomWaypoint{ path: None } }), + _ => eb = eb.with(MoveMode{ mode: Movement::Static }) + } + + if let Some(quips) = &mob_template.quips { + eb = eb.with(Quips{ + available: quips.clone() + }); + } + + if mob_template.blocks_tile { + eb = eb.with(BlocksTile{}); + } + + let mut mob_fitness = 11; + let mut mob_int = 11; + let mut attr = Attributes{ + might: Attribute{ base: 11, modifiers: 0, bonus: attr_bonus(11) }, + fitness: Attribute{ base: 11, modifiers: 0, bonus: attr_bonus(11) }, + quickness: Attribute{ base: 11, modifiers: 0, bonus: attr_bonus(11) }, + intelligence: Attribute{ base: 11, modifiers: 0, bonus: attr_bonus(11) }, + }; + if let Some(might) = mob_template.attributes.might { + attr.might = Attribute{ base: might, modifiers: 0, bonus: attr_bonus(might) }; + } + if let Some(fitness) = mob_template.attributes.fitness { + attr.fitness = Attribute{ base: fitness, modifiers: 0, bonus: attr_bonus(fitness) }; + mob_fitness = fitness; + } + if let Some(quickness) = mob_template.attributes.quickness { + attr.quickness = Attribute{ base: quickness, modifiers: 0, bonus: attr_bonus(quickness) }; + } + if let Some(intelligence) = mob_template.attributes.intelligence { + attr.intelligence = Attribute{ base: intelligence, modifiers: 0, bonus: attr_bonus(intelligence) }; + mob_int = intelligence; + } + eb = eb.with(attr); + + let mob_level = if mob_template.level.is_some() { mob_template.level.unwrap() } else { 1 }; + let mob_hp = npc_hp(mob_fitness, mob_level); + let mob_mana = mana_at_level(mob_int, mob_level); + + let pools = Pools{ + level: mob_level, + xp: 0, + hit_points : Pool{ current: mob_hp, max: mob_hp }, + mana: Pool{current: mob_mana, max: mob_mana}, + total_weight : 0.0, + total_initiative_penalty : 0.0, + gold : if let Some(gold) = &mob_template.gold { + let (n, d, b) = parse_dice_string(&gold); + (crate::rng::roll_dice(n, d) + b) as f32 + } else { + 0.0 + }, + god_mode : false + }; + eb = eb.with(pools); + eb = eb.with(EquipmentChanged{}); + + let mut skills = Skills{ skills: HashMap::new() }; + skills.skills.insert(Skill::Melee, 1); + skills.skills.insert(Skill::Defense, 1); + skills.skills.insert(Skill::Magic, 1); + if let Some(mobskills) = &mob_template.skills { + for sk in mobskills.iter() { + match sk.0.as_str() { + "Melee" => { skills.skills.insert(Skill::Melee, *sk.1); } + "Defense" => { skills.skills.insert(Skill::Defense, *sk.1); } + "Magic" => { skills.skills.insert(Skill::Magic, *sk.1); } + _ => { rltk::console::log(format!("Unknown skill referenced: [{}]", sk.0)); } + } + } + } + eb = eb.with(skills); + + eb = eb.with(Viewshed{ visible_tiles : Vec::new(), range: mob_template.vision_range, dirty: true }); + + if let Some(na) = &mob_template.natural { + let mut nature = NaturalAttackDefense{ + armor_class : na.armor_class, + attacks: Vec::new() + }; + if let Some(attacks) = &na.attacks { + for nattack in attacks.iter() { + let (n, d, b) = parse_dice_string(&nattack.damage); + let attack = NaturalAttack{ + name : nattack.name.clone(), + hit_bonus : nattack.hit_bonus, + damage_n_dice : n, + damage_die_type : d, + damage_bonus: b + }; + nature.attacks.push(attack); + } + } + eb = eb.with(nature); + } + + if let Some(loot) = &mob_template.loot_table { + eb = eb.with(LootTable{table: loot.clone()}); + } + + if let Some(light) = &mob_template.light { + eb = eb.with(LightSource{ range: light.range, color : rltk::RGB::from_hex(&light.color).expect("Bad color") }); + } + + if let Some(faction) = &mob_template.faction { + eb = eb.with(Faction{ name: faction.clone() }); + } else { + eb = eb.with(Faction{ name : "Mindless".to_string() }) + } + + if let Some(vendor) = &mob_template.vendor { + eb = eb.with(Vendor{ categories : vendor.clone() }); + } + + if let Some(ability_list) = &mob_template.abilities { + let mut a = SpecialAbilities { abilities : Vec::new() }; + for ability in ability_list.iter() { + a.abilities.push( + SpecialAbility{ + chance : ability.chance, + spell : ability.spell.clone(), + range : ability.range, + min_range : ability.min_range + } + ); + } + eb = eb.with(a); + } + + if let Some(ability_list) = &mob_template.on_death { + let mut a = OnDeath{ abilities : Vec::new() }; + for ability in ability_list.iter() { + a.abilities.push( + SpecialAbility{ + chance : ability.chance, + spell : ability.spell.clone(), + range : ability.range, + min_range : ability.min_range + } + ); + } + eb = eb.with(a); + } + + let new_mob = eb.build(); + + // Are they wielding anyting? + if let Some(wielding) = &mob_template.equipped { + for tag in wielding.iter() { + spawn_named_entity(raws, ecs, tag, SpawnType::Equipped{ by: new_mob }); + } + } + + return Some(new_mob); + } + None +} + +pub fn spawn_named_prop(raws: &RawMaster, ecs : &mut World, key : &str, pos : SpawnType) -> Option { + if raws.prop_index.contains_key(key) { + let prop_template = &raws.raws.props[raws.prop_index[key]]; + + let mut eb = ecs.create_entity().marked::>(); + + // Spawn in the specified location + eb = spawn_position(pos, eb, key, raws); + + // Renderable + if let Some(renderable) = &prop_template.renderable { + eb = eb.with(get_renderable_component(renderable)); + if renderable.x_size.is_some() || renderable.y_size.is_some() { + eb = eb.with(TileSize{ x : renderable.x_size.unwrap_or(1), y : renderable.y_size.unwrap_or(1) }); + } + } + + eb = eb.with(Name{ name : prop_template.name.clone() }); + + if let Some(hidden) = prop_template.hidden { + if hidden { eb = eb.with(Hidden{}) }; + } + if let Some(blocks_tile) = prop_template.blocks_tile { + if blocks_tile { eb = eb.with(BlocksTile{}) }; + } + if let Some(blocks_visibility) = prop_template.blocks_visibility { + if blocks_visibility { eb = eb.with(BlocksVisibility{}) }; + } + if let Some(door_open) = prop_template.door_open { + eb = eb.with(Door{ open: door_open }); + } + if let Some(entry_trigger) = &prop_template.entry_trigger { + eb = eb.with(EntryTrigger{}); + apply_effects!(entry_trigger.effects, eb); + } + if let Some(light) = &prop_template.light { + eb = eb.with(LightSource{ range: light.range, color : rltk::RGB::from_hex(&light.color).expect("Bad color") }); + eb = eb.with(Viewshed{ range: light.range, dirty: true, visible_tiles: Vec::new() }); + } + + + return Some(eb.build()); + } + None +} + +pub fn spawn_named_spell(raws: &RawMaster, ecs : &mut World, key : &str) -> Option { + if raws.spell_index.contains_key(key) { + let spell_template = &raws.raws.spells[raws.spell_index[key]]; + + let mut eb = ecs.create_entity().marked::>(); + eb = eb.with(SpellTemplate{ mana_cost : spell_template.mana_cost }); + eb = eb.with(Name{ name : spell_template.name.clone() }); + apply_effects!(spell_template.effects, eb); + + return Some(eb.build()); + } + None +} + +pub fn spawn_all_spells(ecs : &mut World) { + let raws = &super::RAWS.lock().unwrap(); + for spell in raws.raws.spells.iter() { + spawn_named_spell(raws, ecs, &spell.name); + } +} + +pub fn find_spell_entity(ecs : &World, name : &str) -> Option { + let names = ecs.read_storage::(); + let spell_templates = ecs.read_storage::(); + let entities = ecs.entities(); + + for (entity, sname, _template) in (&entities, &names, &spell_templates).join() { + if name == sname.name { + return Some(entity); + } + } + None +} + +pub fn find_spell_entity_by_name( + name : &str, + names : &ReadStorage::, + spell_templates : &ReadStorage::, + entities : &Entities) -> Option +{ + for (entity, sname, _template) in (entities, names, spell_templates).join() { + if name == sname.name { + return Some(entity); + } + } + None +} + +pub fn spawn_named_entity(raws: &RawMaster, ecs : &mut World, key : &str, pos : SpawnType) -> Option { + if raws.item_index.contains_key(key) { + return spawn_named_item(raws, ecs, key, pos); + } else if raws.mob_index.contains_key(key) { + return spawn_named_mob(raws, ecs, key, pos); + } else if raws.prop_index.contains_key(key) { + return spawn_named_prop(raws, ecs, key, pos); + } + + None +} + +pub enum SpawnTableType { Item, Mob, Prop } + +pub fn spawn_type_by_name(raws: &RawMaster, key : &str) -> SpawnTableType { + if raws.item_index.contains_key(key) { + SpawnTableType::Item + } else if raws.mob_index.contains_key(key) { + SpawnTableType::Mob + } else { + SpawnTableType::Prop + } +} + +pub fn get_spawn_table_for_depth(raws: &RawMaster, depth: i32) -> MasterTable { + use super::SpawnTableEntry; + + let available_options : Vec<&SpawnTableEntry> = raws.raws.spawn_table + .iter() + .filter(|a| depth >= a.min_depth && depth <= a.max_depth) + .collect(); + + let mut rt = MasterTable::new(); + for e in available_options.iter() { + let mut weight = e.weight; + if e.add_map_depth_to_weight.is_some() { + weight += depth; + } + rt.add(e.name.clone(), weight, raws); + } + + rt +} + +pub fn get_item_drop(raws: &RawMaster, table: &str) -> Option { + if raws.loot_index.contains_key(table) { + let mut rt = RandomTable::new(); + let available_options = &raws.raws.loot_tables[raws.loot_index[table]]; + for item in available_options.drops.iter() { + rt.add(item.name.clone(), item.weight); + } + let result =rt.roll(); + return Some(result); + } + + None +} diff --git a/chapter-75-darkplaza/src/raws/spawn_table_structs.rs b/chapter-75-darkplaza/src/raws/spawn_table_structs.rs new file mode 100644 index 00000000..fae83096 --- /dev/null +++ b/chapter-75-darkplaza/src/raws/spawn_table_structs.rs @@ -0,0 +1,10 @@ +use serde::{Deserialize}; + +#[derive(Deserialize, Debug)] +pub struct SpawnTableEntry { + pub name : String, + pub weight : i32, + pub min_depth: i32, + pub max_depth: i32, + pub add_map_depth_to_weight : Option +} diff --git a/chapter-75-darkplaza/src/raws/spell_structs.rs b/chapter-75-darkplaza/src/raws/spell_structs.rs new file mode 100644 index 00000000..f57e2221 --- /dev/null +++ b/chapter-75-darkplaza/src/raws/spell_structs.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize}; +use std::collections::HashMap; + +#[derive(Deserialize, Debug)] +pub struct Spell { + pub name : String, + pub mana_cost : i32, + pub effects : HashMap +} diff --git a/chapter-75-darkplaza/src/raws/weapon_traits.rs b/chapter-75-darkplaza/src/raws/weapon_traits.rs new file mode 100644 index 00000000..33dd9123 --- /dev/null +++ b/chapter-75-darkplaza/src/raws/weapon_traits.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize}; +use std::collections::HashMap; + +#[derive(Deserialize, Debug)] +pub struct WeaponTrait { + pub name : String, + pub effects : HashMap +} diff --git a/chapter-75-darkplaza/src/rect.rs b/chapter-75-darkplaza/src/rect.rs new file mode 100644 index 00000000..f415726c --- /dev/null +++ b/chapter-75-darkplaza/src/rect.rs @@ -0,0 +1,35 @@ +use serde::{Serialize, Deserialize}; +use std::collections::HashSet; + +#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)] +pub struct Rect { + pub x1 : i32, + pub x2 : i32, + pub y1 : i32, + pub y2 : i32 +} + +impl Rect { + pub fn new(x:i32, y: i32, w:i32, h:i32) -> Rect { + Rect{x1:x, y1:y, x2:x+w, y2:y+h} + } + + // Returns true if this overlaps with other + pub fn intersect(&self, other:&Rect) -> bool { + self.x1 <= other.x2 && self.x2 >= other.x1 && self.y1 <= other.y2 && self.y2 >= other.y1 + } + + pub fn center(&self) -> (i32, i32) { + ((self.x1 + self.x2)/2, (self.y1 + self.y2)/2) + } + + pub fn get_all_tiles(&self) -> HashSet<(i32,i32)> { + let mut result = HashSet::new(); + for y in self.y1 .. self.y2 { + for x in self.x1 .. self.x2 { + result.insert((x,y)); + } + } + result + } +} diff --git a/chapter-75-darkplaza/src/rex_assets.rs b/chapter-75-darkplaza/src/rex_assets.rs new file mode 100644 index 00000000..70c6ae5f --- /dev/null +++ b/chapter-75-darkplaza/src/rex_assets.rs @@ -0,0 +1,22 @@ +use rltk::{rex::XpFile}; + +rltk::embedded_resource!(SMALL_DUNGEON, "../../resources/SmallDungeon_80x50.xp"); +rltk::embedded_resource!(WFC_DEMO_IMAGE1, "../../resources/wfc-demo1.xp"); +rltk::embedded_resource!(WFC_POPULATED, "../../resources/wfc-populated.xp"); + +pub struct RexAssets { + pub menu : XpFile +} + +impl RexAssets { + #[allow(clippy::new_without_default)] + pub fn new() -> RexAssets { + rltk::link_resource!(SMALL_DUNGEON, "../../resources/SmallDungeon_80x50.xp"); + rltk::link_resource!(WFC_DEMO_IMAGE1, "../../resources/wfc-demo1.xp"); + rltk::link_resource!(WFC_POPULATED, "../../resources/wfc-populated.xp"); + + RexAssets{ + menu : XpFile::from_resource("../../resources/SmallDungeon_80x50.xp").unwrap() + } + } +} diff --git a/chapter-75-darkplaza/src/rng.rs b/chapter-75-darkplaza/src/rng.rs new file mode 100644 index 00000000..4280d9d6 --- /dev/null +++ b/chapter-75-darkplaza/src/rng.rs @@ -0,0 +1,20 @@ +use std::sync::Mutex; +use rltk::prelude::*; + +lazy_static! { + static ref RNG: Mutex = + Mutex::new(RandomNumberGenerator::new()); +} + +pub fn reseed(seed: u64) { + *RNG.lock().unwrap() = RandomNumberGenerator::seeded(seed); +} + +pub fn roll_dice(n:i32, die_type: i32) -> i32 { + RNG.lock().unwrap().roll_dice(n, die_type) +} + +pub fn range(min: i32, max: i32) -> i32 +{ + RNG.lock().unwrap().range(min, max) +} diff --git a/chapter-75-darkplaza/src/saveload_system.rs b/chapter-75-darkplaza/src/saveload_system.rs new file mode 100644 index 00000000..846aa367 --- /dev/null +++ b/chapter-75-darkplaza/src/saveload_system.rs @@ -0,0 +1,163 @@ +use specs::prelude::*; +use specs::saveload::{SimpleMarker, SimpleMarkerAllocator, SerializeComponents, DeserializeComponents, MarkedBuilder}; +use specs::error::NoError; +use super::components::*; +use std::fs::File; +use std::path::Path; +use std::fs; + +macro_rules! serialize_individually { + ($ecs:expr, $ser:expr, $data:expr, $( $type:ty),*) => { + $( + SerializeComponents::>::serialize( + &( $ecs.read_storage::<$type>(), ), + &$data.0, + &$data.1, + &mut $ser, + ) + .unwrap(); + )* + }; +} + +#[cfg(target_arch = "wasm32")] +pub fn save_game(_ecs : &mut World) { +} + +#[cfg(not(target_arch = "wasm32"))] +pub fn save_game(ecs : &mut World) { + // Create helper + let mapcopy = ecs.get_mut::().unwrap().clone(); + let dungeon_master = ecs.get_mut::().unwrap().clone(); + let savehelper = ecs + .create_entity() + .with(SerializationHelper{ map : mapcopy }) + .marked::>() + .build(); + let savehelper2 = ecs + .create_entity() + .with(DMSerializationHelper{ + map : dungeon_master, + log: crate::gamelog::clone_log(), + events : crate::gamelog::clone_events() + }) + .marked::>() + .build(); + + // Actually serialize + { + let data = ( ecs.entities(), ecs.read_storage::>() ); + + let writer = File::create("./savegame.json").unwrap(); + let mut serializer = serde_json::Serializer::new(writer); + serialize_individually!(ecs, serializer, data, Position, Renderable, Player, Viewshed, + Name, BlocksTile, WantsToMelee, Item, Consumable, Ranged, InflictsDamage, + AreaOfEffect, Confusion, ProvidesHealing, InBackpack, WantsToPickupItem, WantsToUseItem, + WantsToDropItem, SerializationHelper, Equippable, Equipped, Weapon, Wearable, + WantsToRemoveItem, ParticleLifetime, HungerClock, ProvidesFood, MagicMapper, Hidden, + EntryTrigger, EntityMoved, SingleActivation, BlocksVisibility, Door, + Quips, Attributes, Skills, Pools, NaturalAttackDefense, LootTable, + OtherLevelPosition, DMSerializationHelper, LightSource, Initiative, MyTurn, Faction, + WantsToApproach, WantsToFlee, MoveMode, Chasing, EquipmentChanged, Vendor, TownPortal, + TeleportTo, ApplyMove, ApplyTeleport, MagicItem, ObfuscatedName, IdentifiedItem, + SpawnParticleBurst, SpawnParticleLine, CursedItem, ProvidesRemoveCurse, ProvidesIdentification, + AttributeBonus, StatusEffect, Duration, KnownSpells, SpellTemplate, WantsToCastSpell, TeachesSpell, + ProvidesMana, Slow, DamageOverTime, SpecialAbilities, TileSize, OnDeath, AlwaysTargetsSelf, + Target, WantsToShoot + ); + } + + // Clean up + ecs.delete_entity(savehelper).expect("Crash on cleanup"); + ecs.delete_entity(savehelper2).expect("Crash on cleanup"); +} + +pub fn does_save_exist() -> bool { + Path::new("./savegame.json").exists() +} + +macro_rules! deserialize_individually { + ($ecs:expr, $de:expr, $data:expr, $( $type:ty),*) => { + $( + DeserializeComponents::::deserialize( + &mut ( &mut $ecs.write_storage::<$type>(), ), + &$data.0, // entities + &mut $data.1, // marker + &mut $data.2, // allocater + &mut $de, + ) + .unwrap(); + )* + }; +} + +pub fn load_game(ecs: &mut World) { + { + // Delete everything + let mut to_delete = Vec::new(); + for e in ecs.entities().join() { + to_delete.push(e); + } + for del in to_delete.iter() { + ecs.delete_entity(*del).expect("Deletion failed"); + } + } + + let data = fs::read_to_string("./savegame.json").unwrap(); + let mut de = serde_json::Deserializer::from_str(&data); + + { + let mut d = (&mut ecs.entities(), &mut ecs.write_storage::>(), &mut ecs.write_resource::>()); + + deserialize_individually!(ecs, de, d, Position, Renderable, Player, Viewshed, + Name, BlocksTile, WantsToMelee, Item, Consumable, Ranged, InflictsDamage, + AreaOfEffect, Confusion, ProvidesHealing, InBackpack, WantsToPickupItem, WantsToUseItem, + WantsToDropItem, SerializationHelper, Equippable, Equipped, Weapon, Wearable, + WantsToRemoveItem, ParticleLifetime, HungerClock, ProvidesFood, MagicMapper, Hidden, + EntryTrigger, EntityMoved, SingleActivation, BlocksVisibility, Door, + Quips, Attributes, Skills, Pools, NaturalAttackDefense, LootTable, + OtherLevelPosition, DMSerializationHelper, LightSource, Initiative, MyTurn, Faction, + WantsToApproach, WantsToFlee, MoveMode, Chasing, EquipmentChanged, Vendor, TownPortal, + TeleportTo, ApplyMove, ApplyTeleport, MagicItem, ObfuscatedName, IdentifiedItem, + SpawnParticleBurst, SpawnParticleLine, CursedItem, ProvidesRemoveCurse, ProvidesIdentification, + AttributeBonus, StatusEffect, Duration, KnownSpells, SpellTemplate, WantsToCastSpell, TeachesSpell, + ProvidesMana, Slow, DamageOverTime, SpecialAbilities, TileSize, OnDeath, AlwaysTargetsSelf, + Target, WantsToShoot + ); + } + + let mut deleteme : Option = None; + let mut deleteme2 : Option = None; + { + let entities = ecs.entities(); + let helper = ecs.read_storage::(); + let helper2 = ecs.read_storage::(); + let player = ecs.read_storage::(); + let position = ecs.read_storage::(); + for (e,h) in (&entities, &helper).join() { + let mut worldmap = ecs.write_resource::(); + *worldmap = h.map.clone(); + crate::spatial::set_size((worldmap.height * worldmap.width) as usize); + deleteme = Some(e); + } + for (e,h) in (&entities, &helper2).join() { + let mut dungeonmaster = ecs.write_resource::(); + *dungeonmaster = h.map.clone(); + deleteme2 = Some(e); + crate::gamelog::restore_log(&mut h.log.clone()); + crate::gamelog::load_events(h.events.clone()); + } + for (e,_p,pos) in (&entities, &player, &position).join() { + let mut ppos = ecs.write_resource::(); + *ppos = rltk::Point::new(pos.x, pos.y); + let mut player_resource = ecs.write_resource::(); + *player_resource = e; + } + } + ecs.delete_entity(deleteme.unwrap()).expect("Unable to delete helper"); + ecs.delete_entity(deleteme2.unwrap()).expect("Unable to delete helper"); +} + +pub fn delete_save() { + if Path::new("./savegame.json").exists() { std::fs::remove_file("./savegame.json").expect("Unable to delete file"); } +} diff --git a/chapter-75-darkplaza/src/spatial/mod.rs b/chapter-75-darkplaza/src/spatial/mod.rs new file mode 100644 index 00000000..10ff88bf --- /dev/null +++ b/chapter-75-darkplaza/src/spatial/mod.rs @@ -0,0 +1,117 @@ +use std::sync::Mutex; +use specs::prelude::*; +use crate::{ Map, tile_walkable, RunState }; + +struct SpatialMap { + blocked : Vec<(bool, bool)>, + tile_content : Vec> +} + +impl SpatialMap { + fn new() -> Self { + Self { + blocked: Vec::new(), + tile_content: Vec::new() + } + } +} + +lazy_static! { + static ref SPATIAL_MAP : Mutex = Mutex::new(SpatialMap::new()); +} + +pub fn set_size(map_tile_count: usize) { + let mut lock = SPATIAL_MAP.lock().unwrap(); + lock.blocked = vec![(false, false); map_tile_count]; + lock.tile_content = vec![Vec::new(); map_tile_count]; +} + +pub fn clear() { + let mut lock = SPATIAL_MAP.lock().unwrap(); + lock.blocked.iter_mut().for_each(|b| { b.0 = false; b.1 = false; }); + for content in lock.tile_content.iter_mut() { + content.clear(); + } +} + +pub fn populate_blocked_from_map(map: &Map) { + let mut lock = SPATIAL_MAP.lock().unwrap(); + for (i,tile) in map.tiles.iter().enumerate() { + lock.blocked[i].0 = !tile_walkable(*tile); + } +} + +pub fn index_entity(entity: Entity, idx: usize, blocks_tile: bool) { + let mut lock = SPATIAL_MAP.lock().unwrap(); + lock.tile_content[idx].push((entity, blocks_tile)); + if blocks_tile { + lock.blocked[idx].1 = true; + } +} + +pub fn is_blocked(idx: usize) -> bool { + let lock = SPATIAL_MAP.lock().unwrap(); + lock.blocked[idx].0 || lock.blocked[idx].1 +} + +pub fn set_blocked(idx: usize, blocked: bool) { + let mut lock = SPATIAL_MAP.lock().unwrap(); + lock.blocked[idx] = (lock.blocked[idx].0, blocked); +} + +pub fn for_each_tile_content(idx: usize, mut f: F) +where F : FnMut(Entity) +{ + let lock = SPATIAL_MAP.lock().unwrap(); + for entity in lock.tile_content[idx].iter() { + f(entity.0); + } +} + +pub fn for_each_tile_content_with_gamemode(idx: usize, mut f: F) -> RunState +where F : FnMut(Entity)->Option +{ + let lock = SPATIAL_MAP.lock().unwrap(); + for entity in lock.tile_content[idx].iter() { + if let Some(rs) = f(entity.0) { + return rs; + } + } + + RunState::AwaitingInput +} + +pub fn get_tile_content_clone(idx:usize) -> Vec { + let lock = SPATIAL_MAP.lock().unwrap(); + lock.tile_content[idx].iter().map(|(e,_)| *e).collect() +} + +pub fn move_entity(entity: Entity, moving_from: usize, moving_to: usize) { + let mut lock = SPATIAL_MAP.lock().unwrap(); + let mut entity_blocks = false; + lock.tile_content[moving_from].retain(|(e, blocks) | { + if *e == entity { + entity_blocks = *blocks; + false + } else { + true + } + }); + lock.tile_content[moving_to].push((entity, entity_blocks)); + + // Recalculate blocks for both tiles + let mut from_blocked = false; + let mut to_blocked = false; + lock.tile_content[moving_from].iter().for_each(|(_,blocks)| if *blocks { from_blocked = true; } ); + lock.tile_content[moving_to].iter().for_each(|(_,blocks)| if *blocks { to_blocked = true; } ); + lock.blocked[moving_from].1 = from_blocked; + lock.blocked[moving_to].1 = to_blocked; +} + +pub fn remove_entity(entity: Entity, idx: usize) { + let mut lock = SPATIAL_MAP.lock().unwrap(); + lock.tile_content[idx].retain(|(e, _)| *e != entity ); + let mut from_blocked = false; + lock.tile_content[idx].iter().for_each(|(_,blocks)| if *blocks { from_blocked = true; } ); + lock.blocked[idx].1 = from_blocked; +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/spawner.rs b/chapter-75-darkplaza/src/spawner.rs new file mode 100644 index 00000000..2a594fef --- /dev/null +++ b/chapter-75-darkplaza/src/spawner.rs @@ -0,0 +1,196 @@ +use rltk::{ RGB }; +use specs::prelude::*; +use super::{Pools, Pool, Player, Renderable, Name, Position, Viewshed, Rect, + SerializeMe, random_table::MasterTable, HungerClock, HungerState, Map, TileType, raws::*, + Attribute, Attributes, Skills, Skill, LightSource, Initiative, Faction, EquipmentChanged, + OtherLevelPosition, MasterDungeonMap, EntryTrigger, TeleportTo, SingleActivation, + StatusEffect, Duration, AttributeBonus, KnownSpells }; +use specs::saveload::{MarkedBuilder, SimpleMarker}; +use std::collections::HashMap; +use crate::{attr_bonus, player_hp_at_level, mana_at_level}; + +/// Spawns the player and returns his/her entity object. +pub fn player(ecs : &mut World, player_x : i32, player_y : i32) -> Entity { + spawn_all_spells(ecs); + + let mut skills = Skills{ skills: HashMap::new() }; + skills.skills.insert(Skill::Melee, 1); + skills.skills.insert(Skill::Defense, 1); + skills.skills.insert(Skill::Magic, 1); + + let player = ecs + .create_entity() + .with(Position { x: player_x, y: player_y }) + .with(Renderable { + glyph: rltk::to_cp437('@'), + fg: RGB::named(rltk::YELLOW), + bg: RGB::named(rltk::BLACK), + render_order: 0 + }) + .with(Player{}) + .with(Viewshed{ visible_tiles : Vec::new(), range: 8, dirty: true }) + .with(Name{name: "Player".to_string() }) + .with(HungerClock{ state: HungerState::WellFed, duration: 20 }) + .with(Attributes{ + might: Attribute{ base: 11, modifiers: 0, bonus: attr_bonus(11) }, + fitness: Attribute{ base: 11, modifiers: 0, bonus: attr_bonus(11) }, + quickness: Attribute{ base: 11, modifiers: 0, bonus: attr_bonus(11) }, + intelligence: Attribute{ base: 11, modifiers: 0, bonus: attr_bonus(11) }, + }) + .with(skills) + .with(Pools{ + hit_points : Pool{ + current: player_hp_at_level(11, 1), + max: player_hp_at_level(11, 1) + }, + mana: Pool{ + current: mana_at_level(11, 1), + max: mana_at_level(11, 1) + }, + xp: 0, + level: 1, + total_weight : 0.0, + total_initiative_penalty : 0.0, + gold : 0.0, + god_mode : false + }) + .with(EquipmentChanged{}) + .with(LightSource{ color: rltk::RGB::from_f32(1.0, 1.0, 0.5), range: 8 }) + .with(Initiative{current: 0}) + .with(Faction{name : "Player".to_string() }) + .with(KnownSpells{ spells : Vec::new() }) + .marked::>() + .build(); + + // Starting equipment + spawn_named_entity(&RAWS.lock().unwrap(), ecs, "Rusty Longsword", SpawnType::Equipped{by : player}); + spawn_named_entity(&RAWS.lock().unwrap(), ecs, "Dried Sausage", SpawnType::Carried{by : player} ); + spawn_named_entity(&RAWS.lock().unwrap(), ecs, "Beer", SpawnType::Carried{by : player}); + spawn_named_entity(&RAWS.lock().unwrap(), ecs, "Stained Tunic", SpawnType::Equipped{by : player}); + spawn_named_entity(&RAWS.lock().unwrap(), ecs, "Torn Trousers", SpawnType::Equipped{by : player}); + spawn_named_entity(&RAWS.lock().unwrap(), ecs, "Old Boots", SpawnType::Equipped{by : player}); + spawn_named_entity(&RAWS.lock().unwrap(), ecs, "Shortbow", SpawnType::Carried{by : player}); + + // Starting hangover + ecs.create_entity() + .with(StatusEffect{ target : player }) + .with(Duration{ turns:10 }) + .with(Name{ name: "Hangover".to_string() }) + .with(AttributeBonus{ + might : Some(-1), + fitness : None, + quickness : Some(-1), + intelligence : Some(-1) + }) + .marked::>() + .build(); + + player +} + +const MAX_MONSTERS : i32 = 4; + +fn room_table(map_depth: i32) -> MasterTable { + get_spawn_table_for_depth(&RAWS.lock().unwrap(), map_depth) +} + +/// Fills a room with stuff! +pub fn spawn_room(map: &Map, room : &Rect, map_depth: i32, spawn_list : &mut Vec<(usize, String)>) { + let mut possible_targets : Vec = Vec::new(); + { // Borrow scope - to keep access to the map separated + for y in room.y1 + 1 .. room.y2 { + for x in room.x1 + 1 .. room.x2 { + let idx = map.xy_idx(x, y); + if map.tiles[idx] == TileType::Floor { + possible_targets.push(idx); + } + } + } + } + + spawn_region(map, &possible_targets, map_depth, spawn_list); +} + +/// Fills a region with stuff! +pub fn spawn_region(_map: &Map, area : &[usize], map_depth: i32, spawn_list : &mut Vec<(usize, String)>) { + let spawn_table = room_table(map_depth); + let mut spawn_points : HashMap = HashMap::new(); + let mut areas : Vec = Vec::from(area); + + // Scope to keep the borrow checker happy + { + let num_spawns = i32::min(areas.len() as i32, crate::rng::roll_dice(1, MAX_MONSTERS + 3) + (map_depth - 1) - 3); + if num_spawns == 0 { return; } + + for _i in 0 .. num_spawns { + let array_index = if areas.len() == 1 { 0usize } else { (crate::rng::roll_dice(1, areas.len() as i32)-1) as usize }; + + let map_idx = areas[array_index]; + spawn_points.insert(map_idx, spawn_table.roll()); + areas.remove(array_index); + } + } + + // Actually spawn the monsters + for spawn in spawn_points.iter() { + spawn_list.push((*spawn.0, spawn.1.to_string())); + } +} + +/// Spawns a named entity (name in tuple.1) at the location in (tuple.0) +pub fn spawn_entity(ecs: &mut World, spawn : &(&usize, &String)) { + let map = ecs.fetch::(); + let width = map.width as usize; + let x = (*spawn.0 % width) as i32; + let y = (*spawn.0 / width) as i32; + std::mem::drop(map); + + let spawn_result = spawn_named_entity(&RAWS.lock().unwrap(), ecs, &spawn.1, SpawnType::AtPosition{ x, y}); + if spawn_result.is_some() { + return; + } + + if spawn.1 != "None" { + rltk::console::log(format!("WARNING: We don't know how to spawn [{}]!", spawn.1)); + } +} + +pub fn spawn_town_portal(ecs: &mut World) { + // Get current position & depth + let map = ecs.fetch::(); + let player_depth = map.depth; + let player_pos = ecs.fetch::(); + let player_x = player_pos.x; + let player_y = player_pos.y; + std::mem::drop(player_pos); + std::mem::drop(map); + + // Find part of the town for the portal + let dm = ecs.fetch::(); + let town_map = dm.get_map(1).unwrap(); + let mut stairs_idx = 0; + for (idx, tt) in town_map.tiles.iter().enumerate() { + if *tt == TileType::DownStairs { + stairs_idx = idx; + } + } + let portal_x = (stairs_idx as i32 % town_map.width)-2; + let portal_y = stairs_idx as i32 / town_map.width; + + std::mem::drop(dm); + + // Spawn the portal itself + ecs.create_entity() + .with(OtherLevelPosition { x: portal_x, y: portal_y, depth: 1 }) + .with(Renderable { + glyph: rltk::to_cp437('♥'), + fg: RGB::named(rltk::CYAN), + bg: RGB::named(rltk::BLACK), + render_order: 0 + }) + .with(EntryTrigger{}) + .with(TeleportTo{ x: player_x, y: player_y, depth: player_depth, player_only: true }) + .with(SingleActivation{}) + .with(Name{ name : "Town Portal".to_string() }) + .build(); +} diff --git a/chapter-75-darkplaza/src/systems/ai/adjacent_ai_system.rs b/chapter-75-darkplaza/src/systems/ai/adjacent_ai_system.rs new file mode 100644 index 00000000..72cf15c5 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/ai/adjacent_ai_system.rs @@ -0,0 +1,82 @@ +use specs::prelude::*; +use crate::{MyTurn, Faction, Position, Map, raws::Reaction, WantsToMelee, TileSize}; + +pub struct AdjacentAI {} + +impl<'a> System<'a> for AdjacentAI { + #[allow(clippy::type_complexity)] + type SystemData = ( + WriteStorage<'a, MyTurn>, + ReadStorage<'a, Faction>, + ReadStorage<'a, Position>, + ReadExpect<'a, Map>, + WriteStorage<'a, WantsToMelee>, + Entities<'a>, + ReadExpect<'a, Entity>, + ReadStorage<'a, TileSize> + ); + + fn run(&mut self, data : Self::SystemData) { + let (mut turns, factions, positions, map, mut want_melee, entities, player, sizes) = data; + + let mut turn_done : Vec = Vec::new(); + for (entity, _turn, my_faction, pos) in (&entities, &turns, &factions, &positions).join() { + if entity != *player { + let mut reactions : Vec<(Entity, Reaction)> = Vec::new(); + let idx = map.xy_idx(pos.x, pos.y); + let w = map.width; + let h = map.height; + + if let Some(size) = sizes.get(entity) { + use crate::rect::Rect; + let mob_rect = Rect::new(pos.x, pos.y, size.x, size.y).get_all_tiles(); + let parent_rect = Rect::new(pos.x -1, pos.y -1, size.x+2, size.y + 2); + parent_rect.get_all_tiles().iter().filter(|t| !mob_rect.contains(t)).for_each(|t| { + if t.0 > 0 && t.0 < w-1 && t.1 > 0 && t.1 < h-1 { + let target_idx = map.xy_idx(t.0, t.1); + evaluate(target_idx, &map, &factions, &my_faction.name, &mut reactions); + } + }); + } else { + + // Add possible reactions to adjacents for each direction + if pos.x > 0 { evaluate(idx-1, &map, &factions, &my_faction.name, &mut reactions); } + if pos.x < w-1 { evaluate(idx+1, &map, &factions, &my_faction.name, &mut reactions); } + if pos.y > 0 { evaluate(idx-w as usize, &map, &factions, &my_faction.name, &mut reactions); } + if pos.y < h-1 { evaluate(idx+w as usize, &map, &factions, &my_faction.name, &mut reactions); } + if pos.y > 0 && pos.x > 0 { evaluate((idx-w as usize)-1, &map, &factions, &my_faction.name, &mut reactions); } + if pos.y > 0 && pos.x < w-1 { evaluate((idx-w as usize)+1, &map, &factions, &my_faction.name, &mut reactions); } + if pos.y < h-1 && pos.x > 0 { evaluate((idx+w as usize)-1, &map, &factions, &my_faction.name, &mut reactions); } + if pos.y < h-1 && pos.x < w-1 { evaluate((idx+w as usize)+1, &map, &factions, &my_faction.name, &mut reactions); } + + } + + let mut done = false; + for reaction in reactions.iter() { + if let Reaction::Attack = reaction.1 { + want_melee.insert(entity, WantsToMelee{ target: reaction.0 }).expect("Error inserting melee"); + done = true; + } + } + + if done { turn_done.push(entity); } + } + } + + // Remove turn marker for those that are done + for done in turn_done.iter() { + turns.remove(*done); + } + } +} + +fn evaluate(idx : usize, map : &Map, factions : &ReadStorage, my_faction : &str, reactions : &mut Vec<(Entity, Reaction)>) { + crate::spatial::for_each_tile_content(idx, |other_entity| { + if let Some(faction) = factions.get(other_entity) { + reactions.push(( + other_entity, + crate::raws::faction_reaction(my_faction, &faction.name, &crate::raws::RAWS.lock().unwrap()) + )); + } + }); +} diff --git a/chapter-75-darkplaza/src/systems/ai/approach_ai_system.rs b/chapter-75-darkplaza/src/systems/ai/approach_ai_system.rs new file mode 100644 index 00000000..fd88fbc0 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/ai/approach_ai_system.rs @@ -0,0 +1,43 @@ +use specs::prelude::*; +use crate::{MyTurn, WantsToApproach, Position, Map, ApplyMove}; + +pub struct ApproachAI {} + +impl<'a> System<'a> for ApproachAI { + #[allow(clippy::type_complexity)] + type SystemData = ( + WriteStorage<'a, MyTurn>, + WriteStorage<'a, WantsToApproach>, + ReadStorage<'a, Position>, + WriteExpect<'a, Map>, + Entities<'a>, + WriteStorage<'a, ApplyMove> + ); + + fn run(&mut self, data : Self::SystemData) { + let (mut turns, mut want_approach, positions, mut map, + entities, mut apply_move) = data; + + let mut turn_done : Vec = Vec::new(); + for (entity, pos, approach, _myturn) in + (&entities, &positions, &want_approach, &turns).join() + { + turn_done.push(entity); + let path = rltk::a_star_search( + map.xy_idx(pos.x, pos.y), + map.xy_idx(approach.idx % map.width, approach.idx / map.width), + &mut *map + ); + if path.success && path.steps.len()>1 { + apply_move.insert(entity, ApplyMove{ dest_idx: path.steps[1] }).expect("Unable to insert"); + } + } + + want_approach.clear(); + + // Remove turn marker for those that are done + for done in turn_done.iter() { + turns.remove(*done); + } + } +} diff --git a/chapter-75-darkplaza/src/systems/ai/chase_ai_system.rs b/chapter-75-darkplaza/src/systems/ai/chase_ai_system.rs new file mode 100644 index 00000000..26e47bf8 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/ai/chase_ai_system.rs @@ -0,0 +1,77 @@ +use specs::prelude::*; +use crate::{MyTurn, Chasing, Position, Map, ApplyMove, TileSize}; +use std::collections::HashMap; + +pub struct ChaseAI {} + +impl<'a> System<'a> for ChaseAI { + #[allow(clippy::type_complexity)] + type SystemData = ( + WriteStorage<'a, MyTurn>, + WriteStorage<'a, Chasing>, + ReadStorage<'a, Position>, + WriteExpect<'a, Map>, + Entities<'a>, + WriteStorage<'a, ApplyMove>, + ReadStorage<'a, TileSize> + ); + + fn run(&mut self, data : Self::SystemData) { + let (mut turns, mut chasing, positions, mut map, + entities, mut apply_move, sizes) = data; + + let mut targets : HashMap = HashMap::new(); + let mut end_chase : Vec = Vec::new(); + for (entity, _turn, chasing) in (&entities, &turns, &chasing).join() { + let target_pos = positions.get(chasing.target); + if let Some(target_pos) = target_pos { + targets.insert(entity, (target_pos.x, target_pos.y)); + } else { + end_chase.push(entity); + } + } + + for done in end_chase.iter() { + chasing.remove(*done); + } + end_chase.clear(); + + let mut turn_done : Vec = Vec::new(); + for (entity, pos, _chase, _myturn) in + (&entities, &positions, &chasing, &turns).join() + { + turn_done.push(entity); + let target_pos = targets[&entity]; + let path; + + if let Some(size) = sizes.get(entity) { + let mut map_copy = map.clone(); + map_copy.populate_blocked_multi(size.x, size.y); + path = rltk::a_star_search( + map_copy.xy_idx(pos.x, pos.y), + map_copy.xy_idx(target_pos.0, target_pos.1), + &mut map_copy + ); + } else { + path = rltk::a_star_search( + map.xy_idx(pos.x, pos.y), + map.xy_idx(target_pos.0, target_pos.1), + &mut *map + ); + } + if path.success && path.steps.len()>1 && path.steps.len()<15 { + apply_move.insert(entity, ApplyMove{ dest_idx: path.steps[1] }).expect("Unable to insert"); + turn_done.push(entity); + } else { + end_chase.push(entity); + } + } + + for done in end_chase.iter() { + chasing.remove(*done); + } + for done in turn_done.iter() { + turns.remove(*done); + } + } +} diff --git a/chapter-75-darkplaza/src/systems/ai/default_move_system.rs b/chapter-75-darkplaza/src/systems/ai/default_move_system.rs new file mode 100644 index 00000000..6bd00dd8 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/ai/default_move_system.rs @@ -0,0 +1,92 @@ +use specs::prelude::*; +use crate::{MyTurn, MoveMode, Movement, Position, Map, map::tile_walkable, ApplyMove}; + +pub struct DefaultMoveAI {} + +impl<'a> System<'a> for DefaultMoveAI { + #[allow(clippy::type_complexity)] + type SystemData = ( + WriteStorage<'a, MyTurn>, + WriteStorage<'a, MoveMode>, + ReadStorage<'a, Position>, + WriteExpect<'a, Map>, + WriteStorage<'a, ApplyMove>, + Entities<'a> + ); + + fn run(&mut self, data : Self::SystemData) { + let (mut turns, mut move_mode, positions, mut map, + mut apply_move, entities) = data; + + let mut turn_done : Vec = Vec::new(); + for (entity, pos, mut mode, _myturn) in + (&entities, &positions, &mut move_mode, &turns).join() + { + turn_done.push(entity); + + match &mut mode.mode { + Movement::Static => {}, + + Movement::Random => { + let mut x = pos.x; + let mut y = pos.y; + let move_roll = crate::rng::roll_dice(1, 5); + match move_roll { + 1 => x -= 1, + 2 => x += 1, + 3 => y -= 1, + 4 => y += 1, + _ => {} + } + + if x > 0 && x < map.width-1 && y > 0 && y < map.height-1 { + let dest_idx = map.xy_idx(x, y); + if !crate::spatial::is_blocked(dest_idx) { + apply_move.insert(entity, ApplyMove{ dest_idx }) + .expect("Unable to insert"); + turn_done.push(entity); + } + } + }, + + Movement::RandomWaypoint{path} => { + if let Some(path) = path { + // We have a target - go there + if path.len()>1 { + if !crate::spatial::is_blocked(path[1] as usize) { + apply_move.insert(entity, ApplyMove{ dest_idx : path[1] }) + .expect("Unable to insert"); + path.remove(0); // Remove the first step in the path + turn_done.push(entity); + } + // Otherwise we wait a turn to see if the path clears up + } else { + mode.mode = Movement::RandomWaypoint{ path : None }; + } + } else { + let target_x = crate::rng::roll_dice(1, map.width-2); + let target_y = crate::rng::roll_dice(1, map.height-2); + let idx = map.xy_idx(target_x, target_y); + if tile_walkable(map.tiles[idx]) { + let path = rltk::a_star_search( + map.xy_idx(pos.x, pos.y), + map.xy_idx(target_x, target_y), + &mut *map + ); + if path.success && path.steps.len()>1 { + mode.mode = Movement::RandomWaypoint{ + path: Some(path.steps) + }; + } + } + } + } + } + } + + // Remove turn marker for those that are done + for done in turn_done.iter() { + turns.remove(*done); + } + } +} diff --git a/chapter-75-darkplaza/src/systems/ai/encumbrance_system.rs b/chapter-75-darkplaza/src/systems/ai/encumbrance_system.rs new file mode 100644 index 00000000..1e816bb9 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/ai/encumbrance_system.rs @@ -0,0 +1,122 @@ +use specs::prelude::*; +use crate::{EquipmentChanged, Item, InBackpack, Equipped, Pools, Attributes, AttributeBonus, + gamesystem::attr_bonus, StatusEffect, Slow}; +use std::collections::HashMap; + +pub struct EncumbranceSystem {} + +impl<'a> System<'a> for EncumbranceSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + WriteStorage<'a, EquipmentChanged>, + Entities<'a>, + ReadStorage<'a, Item>, + ReadStorage<'a, InBackpack>, + ReadStorage<'a, Equipped>, + WriteStorage<'a, Pools>, + WriteStorage<'a, Attributes>, + ReadExpect<'a, Entity>, + ReadStorage<'a, AttributeBonus>, + ReadStorage<'a, StatusEffect>, + ReadStorage<'a, Slow> + ); + + fn run(&mut self, data : Self::SystemData) { + let (mut equip_dirty, entities, items, backpacks, wielded, + mut pools, mut attributes, player, attrbonus, statuses, slowed) = data; + + if equip_dirty.is_empty() { return; } + + struct ItemUpdate { + weight : f32, + initiative : f32, + might : i32, + fitness : i32, + quickness : i32, + intelligence : i32 + } + + // Build the map of who needs updating + let mut to_update : HashMap = HashMap::new(); // (weight, intiative) + for (entity, _dirty) in (&entities, &equip_dirty).join() { + to_update.insert(entity, ItemUpdate{ weight: 0.0, initiative: 0.0, might: 0, fitness: 0, quickness: 0, intelligence: 0 }); + } + + // Remove all dirty statements + equip_dirty.clear(); + + // Total up equipped items + for (item, equipped, entity) in (&items, &wielded, &entities).join() { + if to_update.contains_key(&equipped.owner) { + let totals = to_update.get_mut(&equipped.owner).unwrap(); + totals.weight += item.weight_lbs; + totals.initiative += item.initiative_penalty; + if let Some(attr) = attrbonus.get(entity) { + totals.might += attr.might.unwrap_or(0); + totals.fitness += attr.fitness.unwrap_or(0); + totals.quickness += attr.quickness.unwrap_or(0); + totals.intelligence += attr.intelligence.unwrap_or(0); + } + } + } + + // Total up carried items + for (item, carried) in (&items, &backpacks).join() { + if to_update.contains_key(&carried.owner) { + let totals = to_update.get_mut(&carried.owner).unwrap(); + totals.weight += item.weight_lbs; + totals.initiative += item.initiative_penalty; + } + } + + // Total up status effect modifiers + for (status, attr) in (&statuses, &attrbonus).join() { + if to_update.contains_key(&status.target) { + let totals = to_update.get_mut(&status.target).unwrap(); + totals.might += attr.might.unwrap_or(0); + totals.fitness += attr.fitness.unwrap_or(0); + totals.quickness += attr.quickness.unwrap_or(0); + totals.intelligence += attr.intelligence.unwrap_or(0); + } + } + + // Total up haste/slow + for (status, slow) in (&statuses, &slowed).join() { + if to_update.contains_key(&status.target) { + let totals = to_update.get_mut(&status.target).unwrap(); + totals.initiative += slow.initiative_penalty; + } + } + + // Apply the data to Pools + for (entity, item) in to_update.iter() { + if let Some(pool) = pools.get_mut(*entity) { + pool.total_weight = item.weight; + pool.total_initiative_penalty = item.initiative; + + if let Some(attr) = attributes.get_mut(*entity) { + attr.might.modifiers = item.might; + attr.fitness.modifiers = item.fitness; + attr.quickness.modifiers = item.quickness; + attr.intelligence.modifiers = item.intelligence; + attr.might.bonus = attr_bonus(attr.might.base + attr.might.modifiers); + attr.fitness.bonus = attr_bonus(attr.fitness.base + attr.fitness.modifiers); + attr.quickness.bonus = attr_bonus(attr.quickness.base + attr.quickness.modifiers); + attr.intelligence.bonus = attr_bonus(attr.intelligence.base + attr.intelligence.modifiers); + + let carry_capacity_lbs = (attr.might.base + attr.might.modifiers) * 15; + if pool.total_weight as i32 > carry_capacity_lbs { + // Overburdened + pool.total_initiative_penalty += 4.0; + if *entity == *player { + crate::gamelog::Logger::new() + .color(rltk::ORANGE) + .append("You are overburdened, and suffering an initiative penalty.") + .log(); + } + } + } + } + } + } +} diff --git a/chapter-75-darkplaza/src/systems/ai/flee_ai_system.rs b/chapter-75-darkplaza/src/systems/ai/flee_ai_system.rs new file mode 100644 index 00000000..b62b4fc8 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/ai/flee_ai_system.rs @@ -0,0 +1,45 @@ +use specs::prelude::*; +use crate::{MyTurn, WantsToFlee, Position, Map, ApplyMove}; + +pub struct FleeAI {} + +impl<'a> System<'a> for FleeAI { + #[allow(clippy::type_complexity)] + type SystemData = ( + WriteStorage<'a, MyTurn>, + WriteStorage<'a, WantsToFlee>, + WriteStorage<'a, Position>, + WriteExpect<'a, Map>, + Entities<'a>, + WriteStorage<'a, ApplyMove> + ); + + fn run(&mut self, data : Self::SystemData) { + let (mut turns, mut want_flee, positions, mut map, + entities, mut apply_move) = data; + + let mut turn_done : Vec = Vec::new(); + for (entity, pos, flee, _myturn) in + (&entities, &positions, &want_flee, &turns).join() + { + turn_done.push(entity); + let my_idx = map.xy_idx(pos.x, pos.y); + map.populate_blocked(); + let flee_map = rltk::DijkstraMap::new(map.width as usize, map.height as usize, &flee.indices, &*map, 100.0); + let flee_target = rltk::DijkstraMap::find_highest_exit(&flee_map, my_idx, &*map); + if let Some(flee_target) = flee_target { + if !crate::spatial::is_blocked(flee_target as usize) { + apply_move.insert(entity, ApplyMove{ dest_idx : flee_target }).expect("Unable to insert"); + turn_done.push(entity); + } + } + } + + want_flee.clear(); + + // Remove turn marker for those that are done + for done in turn_done.iter() { + turns.remove(*done); + } + } +} diff --git a/chapter-75-darkplaza/src/systems/ai/initiative_system.rs b/chapter-75-darkplaza/src/systems/ai/initiative_system.rs new file mode 100644 index 00000000..e4687a0d --- /dev/null +++ b/chapter-75-darkplaza/src/systems/ai/initiative_system.rs @@ -0,0 +1,96 @@ +use specs::prelude::*; +use crate::{Initiative, Position, MyTurn, Attributes, RunState, Pools, Duration, + EquipmentChanged, StatusEffect, DamageOverTime}; + +pub struct InitiativeSystem {} + +impl<'a> System<'a> for InitiativeSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( WriteStorage<'a, Initiative>, + ReadStorage<'a, Position>, + WriteStorage<'a, MyTurn>, + Entities<'a>, + ReadStorage<'a, Attributes>, + WriteExpect<'a, RunState>, + ReadExpect<'a, Entity>, + ReadExpect<'a, rltk::Point>, + ReadStorage<'a, Pools>, + WriteStorage<'a, Duration>, + WriteStorage<'a, EquipmentChanged>, + ReadStorage<'a, StatusEffect>, + ReadStorage<'a, DamageOverTime> + ); + + fn run(&mut self, data : Self::SystemData) { + let (mut initiatives, positions, mut turns, entities, attributes, + mut runstate, player, player_pos, pools, mut durations, mut dirty, + statuses, dots) = data; + + if *runstate != RunState::Ticking { return; } + + // Clear any remaining MyTurn we left by mistkae + turns.clear(); + + // Roll initiative + for (entity, initiative, pos) in (&entities, &mut initiatives, &positions).join() { + initiative.current -= 1; + if initiative.current < 1 { + let mut myturn = true; + + // Re-roll + initiative.current = 6 + crate::rng::roll_dice(1, 6); + + // Give a bonus for quickness + if let Some(attr) = attributes.get(entity) { + initiative.current -= attr.quickness.bonus; + } + + // Apply pool penalty + if let Some(pools) = pools.get(entity) { + initiative.current += f32::floor(pools.total_initiative_penalty) as i32; + } + + // TODO: More initiative granting boosts/penalties will go here later + + // If its the player, we want to go to an AwaitingInput state + if entity == *player { + // Give control to the player + *runstate = RunState::AwaitingInput; + } else { + let distance = rltk::DistanceAlg::Pythagoras.distance2d(*player_pos, rltk::Point::new(pos.x, pos.y)); + if distance > 20.0 { + myturn = false; + } + } + + // It's my turn! + if myturn { + turns.insert(entity, MyTurn{}).expect("Unable to insert turn"); + } + + } + } + + // Handle durations + if *runstate == RunState::AwaitingInput { + use crate::effects::*; + for (effect_entity, duration, status) in (&entities, &mut durations, &statuses).join() { + if entities.is_alive(status.target) { + duration.turns -= 1; + if let Some(dot) = dots.get(effect_entity) { + add_effect( + None, + EffectType::Damage{ amount : dot.damage }, + Targets::Single{ target : status.target + } + ); + } + if duration.turns < 1 { + dirty.insert(status.target, EquipmentChanged{}).expect("Unable to insert"); + entities.delete(effect_entity).expect("Unable to delete"); + } + } + } + } + } +} diff --git a/chapter-75-darkplaza/src/systems/ai/mod.rs b/chapter-75-darkplaza/src/systems/ai/mod.rs new file mode 100644 index 00000000..314b8d36 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/ai/mod.rs @@ -0,0 +1,20 @@ +mod initiative_system; +mod turn_status; +mod quipping; +mod adjacent_ai_system; +mod visible_ai_system; +mod approach_ai_system; +mod flee_ai_system; +mod default_move_system; +mod chase_ai_system; +mod encumbrance_system; +pub use initiative_system::InitiativeSystem; +pub use turn_status::TurnStatusSystem; +pub use quipping::QuipSystem; +pub use adjacent_ai_system::AdjacentAI; +pub use visible_ai_system::VisibleAI; +pub use approach_ai_system::ApproachAI; +pub use flee_ai_system::FleeAI; +pub use default_move_system::DefaultMoveAI; +pub use chase_ai_system::ChaseAI; +pub use encumbrance_system::EncumbranceSystem; diff --git a/chapter-75-darkplaza/src/systems/ai/quipping.rs b/chapter-75-darkplaza/src/systems/ai/quipping.rs new file mode 100644 index 00000000..88e5c05d --- /dev/null +++ b/chapter-75-darkplaza/src/systems/ai/quipping.rs @@ -0,0 +1,33 @@ +use specs::prelude::*; +use crate::{Quips, Name, MyTurn, Viewshed}; + +pub struct QuipSystem {} + +impl<'a> System<'a> for QuipSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + WriteStorage<'a, Quips>, + ReadStorage<'a, Name>, + ReadStorage<'a, MyTurn>, + ReadExpect<'a, rltk::Point>, + ReadStorage<'a, Viewshed>); + + fn run(&mut self, data : Self::SystemData) { + let (mut quips, names, turns, player_pos, viewsheds) = data; + + for (quip, name, viewshed, _turn) in (&mut quips, &names, &viewsheds, &turns).join() { + if !quip.available.is_empty() && viewshed.visible_tiles.contains(&player_pos) && crate::rng::roll_dice(1,6)==1 { + let quip_index = + if quip.available.len() == 1 { 0 } + else { (crate::rng::roll_dice(1, quip.available.len() as i32)-1) as usize }; + + crate::gamelog::Logger::new() + .npc_name(&name.name) + .append("says") + .item_name(&quip.available[quip_index]) + .log(); + quip.available.remove(quip_index); + } + } + } +} diff --git a/chapter-75-darkplaza/src/systems/ai/turn_status.rs b/chapter-75-darkplaza/src/systems/ai/turn_status.rs new file mode 100644 index 00000000..e866c071 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/ai/turn_status.rs @@ -0,0 +1,52 @@ +use specs::prelude::*; +use crate::{MyTurn, Confusion, RunState, StatusEffect, effects::add_effect, effects::EffectType, effects::Targets}; +use std::collections::HashSet; + +pub struct TurnStatusSystem {} + +impl<'a> System<'a> for TurnStatusSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( WriteStorage<'a, MyTurn>, + ReadStorage<'a, Confusion>, + Entities<'a>, + ReadExpect<'a, RunState>, + ReadStorage<'a, StatusEffect> + ); + + fn run(&mut self, data : Self::SystemData) { + let (mut turns, confusion, entities, runstate, statuses) = data; + + if *runstate != RunState::Ticking { return; } + + // Collect a set of all entities whose turn it is + let mut entity_turns = HashSet::new(); + for (entity, _turn) in (&entities, &turns).join() { + entity_turns.insert(entity); + } + + // Find status effects affecting entities whose turn it is + let mut not_my_turn : Vec = Vec::new(); + for (effect_entity, status_effect) in (&entities, &statuses).join() { + if entity_turns.contains(&status_effect.target) { + // Skip turn for confusion + if confusion.get(effect_entity).is_some() { + add_effect( + None, + EffectType::Particle{ + glyph : rltk::to_cp437('?'), + fg : rltk::RGB::named(rltk::CYAN), + bg : rltk::RGB::named(rltk::BLACK), + lifespan: 200.0 + }, + Targets::Single{ target:status_effect.target } + ); + not_my_turn.push(status_effect.target); + } + } + } + + for e in not_my_turn { + turns.remove(e); + } + } +} diff --git a/chapter-75-darkplaza/src/systems/ai/visible_ai_system.rs b/chapter-75-darkplaza/src/systems/ai/visible_ai_system.rs new file mode 100644 index 00000000..d2f9817e --- /dev/null +++ b/chapter-75-darkplaza/src/systems/ai/visible_ai_system.rs @@ -0,0 +1,118 @@ +use specs::prelude::*; +use crate::{MyTurn, Faction, Position, Map, raws::Reaction, Viewshed, WantsToFlee, + WantsToApproach, Chasing, SpecialAbilities, WantsToCastSpell, Name, SpellTemplate, + Equipped, Weapon, WantsToShoot}; + +pub struct VisibleAI {} + +impl<'a> System<'a> for VisibleAI { + #[allow(clippy::type_complexity)] + type SystemData = ( + ReadStorage<'a, MyTurn>, + ReadStorage<'a, Faction>, + ReadStorage<'a, Position>, + ReadExpect<'a, Map>, + WriteStorage<'a, WantsToApproach>, + WriteStorage<'a, WantsToFlee>, + Entities<'a>, + ReadExpect<'a, Entity>, + ReadStorage<'a, Viewshed>, + WriteStorage<'a, Chasing>, + ReadStorage<'a, SpecialAbilities>, + WriteStorage<'a, WantsToCastSpell>, + ReadStorage<'a, Name>, + ReadStorage<'a, SpellTemplate>, + ReadStorage<'a, Equipped>, + ReadStorage<'a, Weapon>, + WriteStorage<'a, WantsToShoot> + ); + + fn run(&mut self, data : Self::SystemData) { + let (turns, factions, positions, map, mut want_approach, mut want_flee, entities, player, + viewsheds, mut chasing, abilities, mut casting, names, spells, + equipped, weapons, mut wants_shoot) = data; + + for (entity, _turn, my_faction, pos, viewshed) in (&entities, &turns, &factions, &positions, &viewsheds).join() { + if entity != *player { + let my_idx = map.xy_idx(pos.x, pos.y); + let mut reactions : Vec<(usize, Reaction, Entity)> = Vec::new(); + let mut flee : Vec = Vec::new(); + for visible_tile in viewshed.visible_tiles.iter() { + let idx = map.xy_idx(visible_tile.x, visible_tile.y); + if my_idx != idx { + evaluate(idx, &map, &factions, &my_faction.name, &mut reactions); + } + } + + let mut done = false; + for reaction in reactions.iter() { + match reaction.1 { + Reaction::Attack => { + let range = rltk::DistanceAlg::Pythagoras.distance2d( + rltk::Point::new(pos.x, pos.y), + rltk::Point::new(reaction.0 as i32 % map.width, reaction.0 as i32 / map.width) + ); + if let Some(abilities) = abilities.get(entity) { + for ability in abilities.abilities.iter() { + if range >= ability.min_range && range <= ability.range && + crate::rng::roll_dice(1,100) <= (ability.chance * 100.0) as i32 + { + use crate::raws::find_spell_entity_by_name; + casting.insert( + entity, + WantsToCastSpell{ + spell : find_spell_entity_by_name(&ability.spell, &names, &spells, &entities).unwrap(), + target : Some(rltk::Point::new(reaction.0 as i32 % map.width, reaction.0 as i32 / map.width))} + ).expect("Unable to insert"); + done = true; + } + } + } + + if !done { + for (weapon, equip) in (&weapons, &equipped).join() { + if let Some(wrange) = weapon.range { + if equip.owner == entity { + //rltk::console::log(format!("Owner found. Ranges: {}/{}", wrange, range)); + if wrange >= range as i32 { + //rltk::console::log("Inserting shoot"); + wants_shoot.insert(entity, WantsToShoot{ target: reaction.2 }).expect("Insert fail"); + done = true; + } + } + } + } + } + + if !done { + want_approach.insert(entity, WantsToApproach{ idx: reaction.0 as i32 }).expect("Unable to insert"); + chasing.insert(entity, Chasing{ target: reaction.2}).expect("Unable to insert"); + done = true; + } + } + Reaction::Flee => { + flee.push(reaction.0); + } + _ => {} + } + } + + if !done && !flee.is_empty() { + want_flee.insert(entity, WantsToFlee{ indices : flee }).expect("Unable to insert"); + } + } + } + } +} + +fn evaluate(idx : usize, map : &Map, factions : &ReadStorage, my_faction : &str, reactions : &mut Vec<(usize, Reaction, Entity)>) { + crate::spatial::for_each_tile_content(idx, |other_entity| { + if let Some(faction) = factions.get(other_entity) { + reactions.push(( + idx, + crate::raws::faction_reaction(my_faction, &faction.name, &crate::raws::RAWS.lock().unwrap()), + other_entity + )); + } + }); +} diff --git a/chapter-75-darkplaza/src/systems/dispatcher/mod.rs b/chapter-75-darkplaza/src/systems/dispatcher/mod.rs new file mode 100644 index 00000000..3e273b5e --- /dev/null +++ b/chapter-75-darkplaza/src/systems/dispatcher/mod.rs @@ -0,0 +1,53 @@ +#[cfg(target_arch = "wasm32")] +#[macro_use] +mod single_thread; + +#[cfg(not(target_arch = "wasm32"))] +#[macro_use] +mod multi_thread; + +#[cfg(target_arch = "wasm32")] +pub use single_thread::*; + +#[cfg(not(target_arch = "wasm32"))] +pub use multi_thread::*; + +use specs::prelude::World; +use super::*; + +pub trait UnifiedDispatcher { + fn run_now(&mut self, ecs : *mut World); +} + +construct_dispatcher!( + (MapIndexingSystem, "map_index", &[]), + (VisibilitySystem, "visibility", &[]), + (EncumbranceSystem, "encumbrance", &[]), + (InitiativeSystem, "initiative", &[]), + (TurnStatusSystem, "turnstatus", &[]), + (QuipSystem, "quips", &[]), + (AdjacentAI, "adjacent", &[]), + (VisibleAI, "visible", &[]), + (ApproachAI, "approach", &[]), + (FleeAI, "flee", &[]), + (ChaseAI, "chase", &[]), + (DefaultMoveAI, "default_move", &[]), + (MovementSystem, "movement", &[]), + (TriggerSystem, "triggers", &[]), + (MeleeCombatSystem, "melee", &[]), + (RangedCombatSystem, "ranged", &[]), + (ItemCollectionSystem, "pickup", &[]), + (ItemEquipOnUse, "equip", &[]), + (ItemUseSystem, "use", &[]), + (SpellUseSystem, "spells", &[]), + (ItemIdentificationSystem, "itemid", &[]), + (ItemDropSystem, "drop", &[]), + (ItemRemoveSystem, "remove", &[]), + (HungerSystem, "hunger", &[]), + (ParticleSpawnSystem, "particle_spawn", &[]), + (LightingSystem, "lighting", &[]) +); + +pub fn new() -> Box { + new_dispatch() +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/systems/dispatcher/multi_thread.rs b/chapter-75-darkplaza/src/systems/dispatcher/multi_thread.rs new file mode 100644 index 00000000..bf73dc64 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/dispatcher/multi_thread.rs @@ -0,0 +1,43 @@ +use super::UnifiedDispatcher; +use specs::prelude::*; + +macro_rules! construct_dispatcher { + ( + $( + ( + $type:ident, + $name:expr, + $deps:expr + ) + ),* + ) => { + fn new_dispatch() -> Box { + use specs::DispatcherBuilder; + + let dispatcher = DispatcherBuilder::new() + $( + .with($type{}, $name, $deps) + )* + .build(); + + let dispatch = MultiThreadedDispatcher{ + dispatcher : dispatcher + }; + + return Box::new(dispatch); + } + }; +} + +pub struct MultiThreadedDispatcher { + pub dispatcher: specs::Dispatcher<'static, 'static> +} + +impl<'a> UnifiedDispatcher for MultiThreadedDispatcher { + fn run_now(&mut self, ecs : *mut World) { + unsafe { + self.dispatcher.dispatch(&mut *ecs); + crate::effects::run_effects_queue(&mut *ecs); + } + } +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/systems/dispatcher/single_thread.rs b/chapter-75-darkplaza/src/systems/dispatcher/single_thread.rs new file mode 100644 index 00000000..529684f4 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/dispatcher/single_thread.rs @@ -0,0 +1,42 @@ +use super::super::*; +use super::UnifiedDispatcher; +use specs::prelude::*; + +macro_rules! construct_dispatcher { + ( + $( + ( + $type:ident, + $name:expr, + $deps:expr + ) + ),* + ) => { + fn new_dispatch() -> Box { + let mut dispatch = SingleThreadedDispatcher{ + systems : Vec::new() + }; + + $( + dispatch.systems.push( Box::new( $type {} )); + )* + + return Box::new(dispatch); + } + }; +} + +pub struct SingleThreadedDispatcher<'a> { + pub systems : Vec>> +} + +impl<'a> UnifiedDispatcher for SingleThreadedDispatcher<'a> { + fn run_now(&mut self, ecs : *mut World) { + unsafe { + for sys in self.systems.iter_mut() { + sys.run_now(&*ecs); + } + crate::effects::run_effects_queue(&mut *ecs); + } + } +} diff --git a/chapter-75-darkplaza/src/systems/hunger_system.rs b/chapter-75-darkplaza/src/systems/hunger_system.rs new file mode 100644 index 00000000..d7def9b5 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/hunger_system.rs @@ -0,0 +1,70 @@ +use specs::prelude::*; +use crate::{HungerClock, HungerState, MyTurn, effects::*}; + +pub struct HungerSystem {} + +impl<'a> System<'a> for HungerSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + Entities<'a>, + WriteStorage<'a, HungerClock>, + ReadExpect<'a, Entity>, // The player + ReadStorage<'a, MyTurn> + ); + + fn run(&mut self, data : Self::SystemData) { + let (entities, mut hunger_clock, player_entity, turns) = data; + + for (entity, mut clock, _myturn) in (&entities, &mut hunger_clock, &turns).join() { + clock.duration -= 1; + if clock.duration < 1 { + match clock.state { + HungerState::WellFed => { + clock.state = HungerState::Normal; + clock.duration = 200; + if entity == *player_entity { + crate::gamelog::Logger::new() + .color(rltk::ORANGE) + .append("You are no longer well fed") + .log(); + } + } + HungerState::Normal => { + clock.state = HungerState::Hungry; + clock.duration = 200; + if entity == *player_entity { + crate::gamelog::Logger::new() + .color(rltk::ORANGE) + .append("You are hungry") + .log(); + } + } + HungerState::Hungry => { + clock.state = HungerState::Starving; + clock.duration = 200; + if entity == *player_entity { + crate::gamelog::Logger::new() + .color(rltk::RED) + .append("You are starving!") + .log(); + } + } + HungerState::Starving => { + // Inflict damage from hunger + if entity == *player_entity { + crate::gamelog::Logger::new() + .color(rltk::RED) + .append("Your hunger pangs are getting painful! You suffer 1 hp damage.") + .log(); + } + add_effect( + None, + EffectType::Damage{ amount: 1}, + Targets::Single{ target: entity } + ); + } + } + } + } + } +} diff --git a/chapter-75-darkplaza/src/systems/inventory_system/collection_system.rs b/chapter-75-darkplaza/src/systems/inventory_system/collection_system.rs new file mode 100644 index 00000000..9d319171 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/inventory_system/collection_system.rs @@ -0,0 +1,41 @@ +use specs::prelude::*; +use super::{WantsToPickupItem, Name, InBackpack, Position, EquipmentChanged, + MagicItem, ObfuscatedName, MasterDungeonMap }; + +pub struct ItemCollectionSystem {} + +impl<'a> System<'a> for ItemCollectionSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( ReadExpect<'a, Entity>, + WriteStorage<'a, WantsToPickupItem>, + WriteStorage<'a, Position>, + ReadStorage<'a, Name>, + WriteStorage<'a, InBackpack>, + WriteStorage<'a, EquipmentChanged>, + ReadStorage<'a, MagicItem>, + ReadStorage<'a, ObfuscatedName>, + ReadExpect<'a, MasterDungeonMap> + ); + + fn run(&mut self, data : Self::SystemData) { + let (player_entity, mut wants_pickup, mut positions, names, + mut backpack, mut dirty, magic_items, obfuscated_names, dm) = data; + + for pickup in wants_pickup.join() { + positions.remove(pickup.item); + backpack.insert(pickup.item, InBackpack{ owner: pickup.collected_by }).expect("Unable to insert backpack entry"); + dirty.insert(pickup.collected_by, EquipmentChanged{}).expect("Unable to insert"); + + if pickup.collected_by == *player_entity { + crate::gamelog::Logger::new() + .append("You pick up the") + .item_name( + super::obfuscate_name(pickup.item, &names, &magic_items, &obfuscated_names, &dm) + ) + .log(); + } + } + + wants_pickup.clear(); + } +} diff --git a/chapter-75-darkplaza/src/systems/inventory_system/drop_system.rs b/chapter-75-darkplaza/src/systems/inventory_system/drop_system.rs new file mode 100644 index 00000000..56a2c807 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/inventory_system/drop_system.rs @@ -0,0 +1,48 @@ +use specs::prelude::*; +use super::{Name, InBackpack, Position, WantsToDropItem, EquipmentChanged, + MagicItem, ObfuscatedName, MasterDungeonMap}; + +pub struct ItemDropSystem {} + +impl<'a> System<'a> for ItemDropSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( ReadExpect<'a, Entity>, + Entities<'a>, + WriteStorage<'a, WantsToDropItem>, + ReadStorage<'a, Name>, + WriteStorage<'a, Position>, + WriteStorage<'a, InBackpack>, + WriteStorage<'a, EquipmentChanged>, + ReadStorage<'a, MagicItem>, + ReadStorage<'a, ObfuscatedName>, + ReadExpect<'a, MasterDungeonMap> + ); + + fn run(&mut self, data : Self::SystemData) { + let (player_entity, entities, mut wants_drop, names, mut positions, + mut backpack, mut dirty, magic_items, obfuscated_names, dm) = data; + + for (entity, to_drop) in (&entities, &wants_drop).join() { + let mut dropper_pos : Position = Position{x:0, y:0}; + { + let dropped_pos = positions.get(entity).unwrap(); + dropper_pos.x = dropped_pos.x; + dropper_pos.y = dropped_pos.y; + } + positions.insert(to_drop.item, Position{ x : dropper_pos.x, y : dropper_pos.y }).expect("Unable to insert position"); + backpack.remove(to_drop.item); + dirty.insert(entity, EquipmentChanged{}).expect("Unable to insert"); + + if entity == *player_entity { + crate::gamelog::Logger::new() + .append("You drop the") + .item_name( + super::obfuscate_name(to_drop.item, &names, &magic_items, &obfuscated_names, &dm) + ) + .log(); + } + } + + wants_drop.clear(); + } +} diff --git a/chapter-75-darkplaza/src/systems/inventory_system/equip_use.rs b/chapter-75-darkplaza/src/systems/inventory_system/equip_use.rs new file mode 100644 index 00000000..ee68c765 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/inventory_system/equip_use.rs @@ -0,0 +1,91 @@ +use specs::prelude::*; +use super::{Name, InBackpack, WantsToUseItem, Equippable, Equipped, EquipmentChanged, + IdentifiedItem, CursedItem}; + +pub struct ItemEquipOnUse {} + +impl<'a> System<'a> for ItemEquipOnUse { + #[allow(clippy::type_complexity)] + type SystemData = ( ReadExpect<'a, Entity>, + Entities<'a>, + WriteStorage<'a, WantsToUseItem>, + ReadStorage<'a, Name>, + ReadStorage<'a, Equippable>, + WriteStorage<'a, Equipped>, + WriteStorage<'a, InBackpack>, + WriteStorage<'a, EquipmentChanged>, + WriteStorage<'a, IdentifiedItem>, + ReadStorage<'a, CursedItem> + ); + + #[allow(clippy::cognitive_complexity)] + fn run(&mut self, data : Self::SystemData) { + let (player_entity, entities, mut wants_use, names, equippable, + mut equipped, mut backpack, mut dirty, mut identified_item, cursed) = data; + + let mut remove_use : Vec = Vec::new(); + for (target, useitem) in (&entities, &wants_use).join() { + // If it is equippable, then we want to equip it - and unequip whatever else was in that slot + if let Some(can_equip) = equippable.get(useitem.item) { + let target_slot = can_equip.slot; + + // Remove any items the target has in the item's slot + let mut can_equip = true; + let mut to_unequip : Vec = Vec::new(); + for (item_entity, already_equipped, name) in (&entities, &equipped, &names).join() { + if already_equipped.owner == target && already_equipped.slot == target_slot { + if cursed.get(item_entity).is_some() { + crate::gamelog::Logger::new() + .append("You cannot unequip") + .item_name(&name.name) + .append("- it is cursed!") + .log(); + can_equip = false; + } else { + to_unequip.push(item_entity); + if target == *player_entity { + crate::gamelog::Logger::new() + .append("You unequip") + .item_name(&name.name) + .log(); + } + } + } + } + + if can_equip { + // Identify the item + if target == *player_entity { + identified_item.insert(target, IdentifiedItem{ name: names.get(useitem.item).unwrap().name.clone() }) + .expect("Unable to insert"); + } + + + for item in to_unequip.iter() { + equipped.remove(*item); + backpack.insert(*item, InBackpack{ owner: target }).expect("Unable to insert backpack entry"); + } + + // Wield the item + equipped.insert(useitem.item, Equipped{ owner: target, slot: target_slot }).expect("Unable to insert equipped component"); + backpack.remove(useitem.item); + if target == *player_entity { + crate::gamelog::Logger::new() + .append("You equip") + .item_name(&names.get(useitem.item).unwrap().name) + .log(); + } + + dirty.insert(target, EquipmentChanged{}).expect("Unable to insert"); + } + + // Done with item + remove_use.push(target); + } + } + + remove_use.iter().for_each(|e| { + wants_use.remove(*e).expect("Unable to remove"); + }); + } +} diff --git a/chapter-75-darkplaza/src/systems/inventory_system/identification_system.rs b/chapter-75-darkplaza/src/systems/inventory_system/identification_system.rs new file mode 100644 index 00000000..daaaf003 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/inventory_system/identification_system.rs @@ -0,0 +1,36 @@ +use specs::prelude::*; +use super::{Name, IdentifiedItem, Item, ObfuscatedName}; + +pub struct ItemIdentificationSystem {} + +impl<'a> System<'a> for ItemIdentificationSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + ReadStorage<'a, crate::components::Player>, + WriteStorage<'a, IdentifiedItem>, + WriteExpect<'a, crate::map::MasterDungeonMap>, + ReadStorage<'a, Item>, + ReadStorage<'a, Name>, + WriteStorage<'a, ObfuscatedName>, + Entities<'a> + ); + + fn run(&mut self, data : Self::SystemData) { + let (player, mut identified, mut dm, items, names, mut obfuscated_names, entities) = data; + + for (_p, id) in (&player, &identified).join() { + if !dm.identified_items.contains(&id.name) && crate::raws::is_tag_magic(&id.name) { + dm.identified_items.insert(id.name.clone()); + + for (entity, _item, name) in (&entities, &items, &names).join() { + if name.name == id.name { + obfuscated_names.remove(entity); + } + } + } + } + + // Clean up + identified.clear(); + } +} diff --git a/chapter-75-darkplaza/src/systems/inventory_system/mod.rs b/chapter-75-darkplaza/src/systems/inventory_system/mod.rs new file mode 100644 index 00000000..4de2a746 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/inventory_system/mod.rs @@ -0,0 +1,43 @@ +use crate::{WantsToPickupItem, Name, InBackpack, Position, WantsToUseItem, + WantsToDropItem, Map, AreaOfEffect, Equippable, Equipped, WantsToRemoveItem, EquipmentChanged, + IdentifiedItem, Item, ObfuscatedName, MagicItem, MasterDungeonMap, CursedItem, WantsToCastSpell }; + +mod collection_system; +pub use collection_system::ItemCollectionSystem; +mod use_system; +pub use use_system::{ItemUseSystem, SpellUseSystem}; +mod drop_system; +pub use drop_system::ItemDropSystem; +mod remove_system; +pub use remove_system::ItemRemoveSystem; +mod identification_system; +pub use identification_system::ItemIdentificationSystem; +mod equip_use; +pub use equip_use::ItemEquipOnUse; +use specs::prelude::*; + +pub fn obfuscate_name( + item: Entity, + names: &ReadStorage::, + magic_items : &ReadStorage::, + obfuscated_names : &ReadStorage::, + dm : &MasterDungeonMap, +) -> String +{ + if let Some(name) = names.get(item) { + if magic_items.get(item).is_some() { + if dm.identified_items.contains(&name.name) { + name.name.clone() + } else if let Some(obfuscated) = obfuscated_names.get(item) { + obfuscated.name.clone() + } else { + "Unidentified magic item".to_string() + } + } else { + name.name.clone() + } + + } else { + "Nameless item (bug)".to_string() + } +} diff --git a/chapter-75-darkplaza/src/systems/inventory_system/remove_system.rs b/chapter-75-darkplaza/src/systems/inventory_system/remove_system.rs new file mode 100644 index 00000000..aa4ddfac --- /dev/null +++ b/chapter-75-darkplaza/src/systems/inventory_system/remove_system.rs @@ -0,0 +1,37 @@ +use specs::prelude::*; +use super::{InBackpack, Equipped, WantsToRemoveItem, CursedItem, Name, EquipmentChanged}; + +pub struct ItemRemoveSystem {} + +impl<'a> System<'a> for ItemRemoveSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + Entities<'a>, + WriteStorage<'a, WantsToRemoveItem>, + WriteStorage<'a, Equipped>, + WriteStorage<'a, InBackpack>, + ReadStorage<'a, CursedItem>, + ReadStorage<'a, Name>, + WriteStorage<'a, EquipmentChanged> + ); + + fn run(&mut self, data : Self::SystemData) { + let (entities, mut wants_remove, mut equipped, mut backpack, cursed, names, mut dirty) = data; + + for (entity, to_remove) in (&entities, &wants_remove).join() { + if cursed.get(to_remove.item).is_some() { + crate::gamelog::Logger::new() + .append("You cannot remove") + .item_name(&names.get(to_remove.item).unwrap().name) + .append(" - it is cursed.") + .log(); + } else { + equipped.remove(to_remove.item); + backpack.insert(to_remove.item, InBackpack{ owner: entity }).expect("Unable to insert backpack"); + } + dirty.insert(entity, EquipmentChanged{}).expect("Unable to insert"); + } + + wants_remove.clear(); + } +} diff --git a/chapter-75-darkplaza/src/systems/inventory_system/use_system.rs b/chapter-75-darkplaza/src/systems/inventory_system/use_system.rs new file mode 100644 index 00000000..c5e2beb0 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/inventory_system/use_system.rs @@ -0,0 +1,103 @@ +use specs::prelude::*; +use super::{Name, WantsToUseItem,Map, AreaOfEffect, EquipmentChanged, IdentifiedItem, WantsToCastSpell}; +use crate::effects::*; + +pub struct ItemUseSystem {} + +impl<'a> System<'a> for ItemUseSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( ReadExpect<'a, Entity>, + WriteExpect<'a, Map>, + Entities<'a>, + WriteStorage<'a, WantsToUseItem>, + ReadStorage<'a, Name>, + ReadStorage<'a, AreaOfEffect>, + WriteStorage<'a, EquipmentChanged>, + WriteStorage<'a, IdentifiedItem> + ); + + #[allow(clippy::cognitive_complexity)] + fn run(&mut self, data : Self::SystemData) { + let (player_entity, map, entities, mut wants_use, names, + aoe, mut dirty, mut identified_item) = data; + + for (entity, useitem) in (&entities, &wants_use).join() { + dirty.insert(entity, EquipmentChanged{}).expect("Unable to insert"); + + // Identify + if entity == *player_entity { + identified_item.insert(entity, IdentifiedItem{ name: names.get(useitem.item).unwrap().name.clone() }) + .expect("Unable to insert"); + } + + // Call the effects system + add_effect( + Some(entity), + EffectType::ItemUse{ item : useitem.item }, + match useitem.target { + None => Targets::Single{ target: *player_entity }, + Some(target) => { + if let Some(aoe) = aoe.get(useitem.item) { + Targets::Tiles{ tiles: aoe_tiles(&*map, target, aoe.radius) } + } else { + Targets::Tile{ tile_idx : map.xy_idx(target.x, target.y) as i32 } + } + } + } + ); + + } + + wants_use.clear(); + } +} + +pub struct SpellUseSystem {} + +impl<'a> System<'a> for SpellUseSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( ReadExpect<'a, Entity>, + WriteExpect<'a, Map>, + Entities<'a>, + WriteStorage<'a, WantsToCastSpell>, + ReadStorage<'a, Name>, + ReadStorage<'a, AreaOfEffect>, + WriteStorage<'a, EquipmentChanged>, + WriteStorage<'a, IdentifiedItem> + ); + + #[allow(clippy::cognitive_complexity)] + fn run(&mut self, data : Self::SystemData) { + let (player_entity, map, entities, mut wants_use, names, + aoe, mut dirty, mut identified_item) = data; + + for (entity, useitem) in (&entities, &wants_use).join() { + dirty.insert(entity, EquipmentChanged{}).expect("Unable to insert"); + + // Identify + if entity == *player_entity { + identified_item.insert(entity, IdentifiedItem{ name: names.get(useitem.spell).unwrap().name.clone() }) + .expect("Unable to insert"); + } + + // Call the effects system + add_effect( + Some(entity), + EffectType::SpellUse{ spell : useitem.spell }, + match useitem.target { + None => Targets::Single{ target: *player_entity }, + Some(target) => { + if let Some(aoe) = aoe.get(useitem.spell) { + Targets::Tiles{ tiles: aoe_tiles(&*map, target, aoe.radius) } + } else { + Targets::Tile{ tile_idx : map.xy_idx(target.x, target.y) as i32 } + } + } + } + ); + + } + + wants_use.clear(); + } +} diff --git a/chapter-75-darkplaza/src/systems/lighting_system.rs b/chapter-75-darkplaza/src/systems/lighting_system.rs new file mode 100644 index 00000000..cb01684d --- /dev/null +++ b/chapter-75-darkplaza/src/systems/lighting_system.rs @@ -0,0 +1,40 @@ +use specs::prelude::*; +use crate::{Viewshed, Position, Map, LightSource}; +use rltk::RGB; + +pub struct LightingSystem {} + +impl<'a> System<'a> for LightingSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( WriteExpect<'a, Map>, + ReadStorage<'a, Viewshed>, + ReadStorage<'a, Position>, + ReadStorage<'a, LightSource>); + + fn run(&mut self, data : Self::SystemData) { + let (mut map, viewshed, positions, lighting) = data; + + if map.outdoors { + return; + } + + let black = RGB::from_f32(0.0, 0.0, 0.0); + for l in map.light.iter_mut() { + *l = black; + } + + for (viewshed, pos, light) in (&viewshed, &positions, &lighting).join() { + let light_point = rltk::Point::new(pos.x, pos.y); + let range_f = light.range as f32; + for t in viewshed.visible_tiles.iter() { + if t.x > 0 && t.x < map.width && t.y > 0 && t.y < map.height { + let idx = map.xy_idx(t.x, t.y); + let distance = rltk::DistanceAlg::Pythagoras.distance2d(light_point, *t); + let intensity = (range_f - distance) / range_f; + + map.light[idx] = map.light[idx] + (light.color * intensity); + } + } + } + } +} diff --git a/chapter-75-darkplaza/src/systems/map_indexing_system.rs b/chapter-75-darkplaza/src/systems/map_indexing_system.rs new file mode 100644 index 00000000..839e5783 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/map_indexing_system.rs @@ -0,0 +1,46 @@ +use specs::prelude::*; +use crate::{Map, Position, BlocksTile, Pools, spatial, TileSize}; + +pub struct MapIndexingSystem {} + +impl<'a> System<'a> for MapIndexingSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( ReadExpect<'a, Map>, + ReadStorage<'a, Position>, + ReadStorage<'a, BlocksTile>, + ReadStorage<'a, Pools>, + ReadStorage<'a, TileSize>, + Entities<'a>,); + + fn run(&mut self, data : Self::SystemData) { + let (map, position, blockers, pools, sizes, entities) = data; + + spatial::clear(); + spatial::populate_blocked_from_map(&*map); + for (entity, position) in (&entities, &position).join() { + let mut alive = true; + if let Some(pools) = pools.get(entity) { + if pools.hit_points.current < 1 { + alive = false; + } + } + if alive { + if let Some(size) = sizes.get(entity) { + // Multi-tile + for y in position.y .. position.y + size.y { + for x in position.x .. position.x + size.x { + if x > 0 && x < map.width-1 && y > 0 && y < map.height-1 { + let idx = map.xy_idx(x, y); + spatial::index_entity(entity, idx, blockers.get(entity).is_some()); + } + } + } + } else { + // Single tile + let idx = map.xy_idx(position.x, position.y); + spatial::index_entity(entity, idx, blockers.get(entity).is_some()); + } + } + } + } +} diff --git a/chapter-75-darkplaza/src/systems/melee_combat_system.rs b/chapter-75-darkplaza/src/systems/melee_combat_system.rs new file mode 100644 index 00000000..f48ff4bf --- /dev/null +++ b/chapter-75-darkplaza/src/systems/melee_combat_system.rs @@ -0,0 +1,187 @@ +use specs::prelude::*; +use crate::{Attributes, Skills, WantsToMelee, Name, + HungerClock, HungerState, Pools, skill_bonus, + Skill, Equipped, Weapon, EquipmentSlot, WeaponAttribute, Wearable, NaturalAttackDefense, + effects::*}; + +pub struct MeleeCombatSystem {} + +impl<'a> System<'a> for MeleeCombatSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( Entities<'a>, + WriteStorage<'a, WantsToMelee>, + ReadStorage<'a, Name>, + ReadStorage<'a, Attributes>, + ReadStorage<'a, Skills>, + ReadStorage<'a, HungerClock>, + ReadStorage<'a, Pools>, + ReadStorage<'a, Equipped>, + ReadStorage<'a, Weapon>, + ReadStorage<'a, Wearable>, + ReadStorage<'a, NaturalAttackDefense> + ); + + fn run(&mut self, data : Self::SystemData) { + let (entities, mut wants_melee, names, attributes, skills, + hunger_clock, pools, equipped_items, weapon, wearables, natural) = data; + + for (entity, wants_melee, name, attacker_attributes, attacker_skills, attacker_pools) in (&entities, &wants_melee, &names, &attributes, &skills, &pools).join() { + // Are the attacker and defender alive? Only attack if they are + let target_pools = pools.get(wants_melee.target).unwrap(); + let target_attributes = attributes.get(wants_melee.target).unwrap(); + let target_skills = skills.get(wants_melee.target).unwrap(); + if attacker_pools.hit_points.current > 0 && target_pools.hit_points.current > 0 { + let target_name = names.get(wants_melee.target).unwrap(); + + // Define the basic unarmed attack - overridden by wielding check below if a weapon is equipped + let mut weapon_info = Weapon{ + range: None, + attribute : WeaponAttribute::Might, + hit_bonus : 0, + damage_n_dice : 1, + damage_die_type : 4, + damage_bonus : 0, + proc_chance : None, + proc_target : None + }; + + if let Some(nat) = natural.get(entity) { + if !nat.attacks.is_empty() { + let attack_index = if nat.attacks.len()==1 { 0 } else { crate::rng::roll_dice(1, nat.attacks.len() as i32) as usize -1 }; + weapon_info.hit_bonus = nat.attacks[attack_index].hit_bonus; + weapon_info.damage_n_dice = nat.attacks[attack_index].damage_n_dice; + weapon_info.damage_die_type = nat.attacks[attack_index].damage_die_type; + weapon_info.damage_bonus = nat.attacks[attack_index].damage_bonus; + } + } + + let mut weapon_entity : Option = None; + for (weaponentity,wielded,melee) in (&entities, &equipped_items, &weapon).join() { + if wielded.owner == entity && wielded.slot == EquipmentSlot::Melee { + weapon_info = melee.clone(); + weapon_entity = Some(weaponentity); + } + } + + let natural_roll = crate::rng::roll_dice(1, 20); + let attribute_hit_bonus = if weapon_info.attribute == WeaponAttribute::Might + { attacker_attributes.might.bonus } + else { attacker_attributes.quickness.bonus}; + let skill_hit_bonus = skill_bonus(Skill::Melee, &*attacker_skills); + let weapon_hit_bonus = weapon_info.hit_bonus; + let mut status_hit_bonus = 0; + if let Some(hc) = hunger_clock.get(entity) { // Well-Fed grants +1 + if hc.state == HungerState::WellFed { + status_hit_bonus += 1; + } + } + let modified_hit_roll = natural_roll + attribute_hit_bonus + skill_hit_bonus + + weapon_hit_bonus + status_hit_bonus; + //println!("Natural roll: {}", natural_roll); + //println!("Modified hit roll: {}", modified_hit_roll); + + let mut armor_item_bonus_f = 0.0; + for (wielded,armor) in (&equipped_items, &wearables).join() { + if wielded.owner == wants_melee.target { + armor_item_bonus_f += armor.armor_class; + } + } + let base_armor_class = match natural.get(wants_melee.target) { + None => 10, + Some(nat) => nat.armor_class.unwrap_or(10) + }; + let armor_quickness_bonus = target_attributes.quickness.bonus; + let armor_skill_bonus = skill_bonus(Skill::Defense, &*target_skills); + let armor_item_bonus = armor_item_bonus_f as i32; + let armor_class = base_armor_class + armor_quickness_bonus + armor_skill_bonus + + armor_item_bonus; + + //println!("Armor class: {}", armor_class); + if natural_roll != 1 && (natural_roll == 20 || modified_hit_roll > armor_class) { + // Target hit! Until we support weapons, we're going with 1d4 + let base_damage = crate::rng::roll_dice(weapon_info.damage_n_dice, weapon_info.damage_die_type); + let attr_damage_bonus = attacker_attributes.might.bonus; + let skill_damage_bonus = skill_bonus(Skill::Melee, &*attacker_skills); + let weapon_damage_bonus = weapon_info.damage_bonus; + + let damage = i32::max(0, base_damage + attr_damage_bonus + + skill_damage_bonus + weapon_damage_bonus); + + /*println!("Damage: {} + {}attr + {}skill + {}weapon = {}", + base_damage, attr_damage_bonus, skill_damage_bonus, + weapon_damage_bonus, damage + );*/ + add_effect( + Some(entity), + EffectType::Damage{ amount: damage }, + Targets::Single{ target: wants_melee.target } + ); + crate::gamelog::Logger::new() + .npc_name(&name.name) + .append("hits") + .npc_name(&target_name.name) + .append("for") + .damage(damage) + .append("hp.") + .log(); + + // Proc effects + if let Some(chance) = &weapon_info.proc_chance { + let roll = crate::rng::roll_dice(1, 100); + //println!("Roll {}, Chance {}", roll, chance); + if roll <= (chance * 100.0) as i32 { + //println!("Proc!"); + let effect_target = if weapon_info.proc_target.unwrap() == "Self" { + Targets::Single{ target: entity } + } else { + Targets::Single { target : wants_melee.target } + }; + add_effect( + Some(entity), + EffectType::ItemUse{ item: weapon_entity.unwrap() }, + effect_target + ) + } + } + + } else if natural_roll == 1 { + // Natural 1 miss + crate::gamelog::Logger::new() + .color(rltk::CYAN) + .append(&name.name) + .color(rltk::WHITE) + .append("considers attacking") + .color(rltk::CYAN) + .append(&target_name.name) + .color(rltk::WHITE) + .append("but misjudges the timing!") + .log(); + add_effect( + None, + EffectType::Particle{ glyph: rltk::to_cp437('‼'), fg: rltk::RGB::named(rltk::BLUE), bg : rltk::RGB::named(rltk::BLACK), lifespan: 200.0 }, + Targets::Single{ target: wants_melee.target } + ); + } else { + // Miss + crate::gamelog::Logger::new() + .color(rltk::CYAN) + .append(&name.name) + .color(rltk::WHITE) + .append("attacks") + .color(rltk::CYAN) + .append(&target_name.name) + .color(rltk::WHITE) + .append("but can't connect.") + .log(); + add_effect( + None, + EffectType::Particle{ glyph: rltk::to_cp437('‼'), fg: rltk::RGB::named(rltk::CYAN), bg : rltk::RGB::named(rltk::BLACK), lifespan: 200.0 }, + Targets::Single{ target: wants_melee.target } + ); + } + } + } + + wants_melee.clear(); + } +} diff --git a/chapter-75-darkplaza/src/systems/mod.rs b/chapter-75-darkplaza/src/systems/mod.rs new file mode 100644 index 00000000..43a5ec43 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/mod.rs @@ -0,0 +1,30 @@ +mod dispatcher; +pub use dispatcher::UnifiedDispatcher; + +// System imports +mod map_indexing_system; +use map_indexing_system::MapIndexingSystem; +mod visibility_system; +use visibility_system::VisibilitySystem; +mod ai; +use ai::*; +mod movement_system; +use movement_system::MovementSystem; +mod trigger_system; +use trigger_system::TriggerSystem; +mod melee_combat_system; +use melee_combat_system::MeleeCombatSystem; +mod ranged_combat_system; +use ranged_combat_system::RangedCombatSystem; +mod inventory_system; +use inventory_system::*; +mod hunger_system; +use hunger_system::HungerSystem; +pub mod particle_system; +use particle_system::ParticleSpawnSystem; +mod lighting_system; +use lighting_system::LightingSystem; + +pub fn build() -> Box { + dispatcher::new() +} \ No newline at end of file diff --git a/chapter-75-darkplaza/src/systems/movement_system.rs b/chapter-75-darkplaza/src/systems/movement_system.rs new file mode 100644 index 00000000..dcc5d56d --- /dev/null +++ b/chapter-75-darkplaza/src/systems/movement_system.rs @@ -0,0 +1,61 @@ +use specs::prelude::*; +use crate::{Map, Position, BlocksTile, ApplyMove, ApplyTeleport, OtherLevelPosition, EntityMoved, + Viewshed, RunState}; + +pub struct MovementSystem {} + +impl<'a> System<'a> for MovementSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( WriteExpect<'a, Map>, + WriteStorage<'a, Position>, + ReadStorage<'a, BlocksTile>, + Entities<'a>, + WriteStorage<'a, ApplyMove>, + WriteStorage<'a, ApplyTeleport>, + WriteStorage<'a, OtherLevelPosition>, + WriteStorage<'a, EntityMoved>, + WriteStorage<'a, Viewshed>, + ReadExpect<'a, Entity>, + WriteExpect<'a, RunState>); + + fn run(&mut self, data : Self::SystemData) { + let (mut map, mut position, blockers, entities, mut apply_move, + mut apply_teleport, mut other_level, mut moved, + mut viewsheds, player_entity, mut runstate) = data; + + // Apply teleports + for (entity, teleport) in (&entities, &apply_teleport).join() { + if teleport.dest_depth == map.depth { + apply_move.insert(entity, ApplyMove{ dest_idx: map.xy_idx(teleport.dest_x, teleport.dest_y) }) + .expect("Unable to insert"); + } else if entity == *player_entity { + *runstate = RunState::TeleportingToOtherLevel{ x: teleport.dest_x, y: teleport.dest_y, depth: teleport.dest_depth }; + } else if let Some(pos) = position.get(entity) { + let idx = map.xy_idx(pos.x, pos.y); + let dest_idx = map.xy_idx(teleport.dest_x, teleport.dest_y); + crate::spatial::move_entity(entity, idx, dest_idx); + other_level.insert(entity, OtherLevelPosition{ + x: teleport.dest_x, + y: teleport.dest_y, + depth: teleport.dest_depth }) + .expect("Unable to insert"); + position.remove(entity); + } + } + apply_teleport.clear(); + + // Apply broad movement + for (entity, movement, mut pos) in (&entities, &apply_move, &mut position).join() { + let start_idx = map.xy_idx(pos.x, pos.y); + let dest_idx = movement.dest_idx as usize; + crate::spatial::move_entity(entity, start_idx, dest_idx); + pos.x = movement.dest_idx as i32 % map.width; + pos.y = movement.dest_idx as i32 / map.width; + if let Some(vs) = viewsheds.get_mut(entity) { + vs.dirty = true; + } + moved.insert(entity, EntityMoved{}).expect("Unable to insert"); + } + apply_move.clear(); + } +} diff --git a/chapter-75-darkplaza/src/systems/particle_system.rs b/chapter-75-darkplaza/src/systems/particle_system.rs new file mode 100644 index 00000000..0e4faa9d --- /dev/null +++ b/chapter-75-darkplaza/src/systems/particle_system.rs @@ -0,0 +1,86 @@ +use specs::prelude::*; +use crate::{ Rltk, ParticleLifetime, Position, Renderable }; +use rltk::RGB; + +pub fn update_particles(ecs : &mut World, ctx : &Rltk) { + let mut dead_particles : Vec = Vec::new(); + { + // Age out particles + let mut particles = ecs.write_storage::(); + let entities = ecs.entities(); + for (entity, mut particle) in (&entities, &mut particles).join() { + if let Some(animation) = &mut particle.animation { + animation.timer += ctx.frame_time_ms; + if animation.timer > animation.step_time && animation.current_step < animation.path.len()-2 { + animation.current_step += 1; + + if let Some(pos) = ecs.write_storage::().get_mut(entity) { + pos.x = animation.path[animation.current_step].x; + pos.y = animation.path[animation.current_step].y; + } + } + } + + particle.lifetime_ms -= ctx.frame_time_ms; + if particle.lifetime_ms < 0.0 { + dead_particles.push(entity); + } + } + } + for dead in dead_particles.iter() { + ecs.delete_entity(*dead).expect("Particle will not die"); + } +} + +struct ParticleRequest { + x: i32, + y: i32, + fg: RGB, + bg: RGB, + glyph: rltk::FontCharType, + lifetime: f32 +} + +pub struct ParticleBuilder { + requests : Vec +} + +impl ParticleBuilder { + #[allow(clippy::new_without_default)] + pub fn new() -> ParticleBuilder { + ParticleBuilder{ requests : Vec::new() } + } + + pub fn request(&mut self, x:i32, y:i32, fg: RGB, bg:RGB, glyph: rltk::FontCharType, lifetime: f32) { + self.requests.push( + ParticleRequest{ + x, y, fg, bg, glyph, lifetime + } + ); + } +} + +pub struct ParticleSpawnSystem {} + +impl<'a> System<'a> for ParticleSpawnSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + Entities<'a>, + WriteStorage<'a, Position>, + WriteStorage<'a, Renderable>, + WriteStorage<'a, ParticleLifetime>, + WriteExpect<'a, ParticleBuilder> + ); + + fn run(&mut self, data : Self::SystemData) { + let (entities, mut positions, mut renderables, mut particles, mut particle_builder) = data; + for new_particle in particle_builder.requests.iter() { + let p = entities.create(); + positions.insert(p, Position{ x: new_particle.x, y: new_particle.y }).expect("Unable to inser position"); + renderables.insert(p, Renderable{ fg: new_particle.fg, bg: new_particle.bg, glyph: new_particle.glyph, render_order: 0 }).expect("Unable to insert renderable"); + particles.insert(p, ParticleLifetime{ lifetime_ms: new_particle.lifetime, animation: None }).expect("Unable to insert lifetime"); + } + + particle_builder.requests.clear(); + } +} diff --git a/chapter-75-darkplaza/src/systems/ranged_combat_system.rs b/chapter-75-darkplaza/src/systems/ranged_combat_system.rs new file mode 100644 index 00000000..7cbc904e --- /dev/null +++ b/chapter-75-darkplaza/src/systems/ranged_combat_system.rs @@ -0,0 +1,205 @@ +use specs::prelude::*; +use crate::{Attributes, Skills, WantsToShoot, Name, + HungerClock, HungerState, Pools, skill_bonus, + Skill, Equipped, Weapon, EquipmentSlot, WeaponAttribute, + Wearable, NaturalAttackDefense, + effects::*, Map, Position}; +use rltk::{to_cp437, RGB, Point}; + +pub struct RangedCombatSystem {} + +impl<'a> System<'a> for RangedCombatSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( Entities<'a>, + WriteStorage<'a, WantsToShoot>, + ReadStorage<'a, Name>, + ReadStorage<'a, Attributes>, + ReadStorage<'a, Skills>, + ReadStorage<'a, HungerClock>, + ReadStorage<'a, Pools>, + ReadStorage<'a, Equipped>, + ReadStorage<'a, Weapon>, + ReadStorage<'a, Wearable>, + ReadStorage<'a, NaturalAttackDefense>, + ReadStorage<'a, Position>, + ReadExpect<'a, Map> + ); + + fn run(&mut self, data : Self::SystemData) { + let (entities, mut wants_shoot, names, attributes, skills, + hunger_clock, pools, equipped_items, weapon, wearables, natural, + positions, map) = data; + + for (entity, wants_shoot, name, attacker_attributes, attacker_skills, attacker_pools) in (&entities, &wants_shoot, &names, &attributes, &skills, &pools).join() { + // Are the attacker and defender alive? Only attack if they are + let target_pools = pools.get(wants_shoot.target).unwrap(); + let target_attributes = attributes.get(wants_shoot.target).unwrap(); + let target_skills = skills.get(wants_shoot.target).unwrap(); + if attacker_pools.hit_points.current > 0 && target_pools.hit_points.current > 0 { + let target_name = names.get(wants_shoot.target).unwrap(); + + // Fire projectile effect + let apos = positions.get(entity).unwrap(); + let dpos = positions.get(wants_shoot.target).unwrap(); + add_effect( + None, + EffectType::ParticleProjectile{ + glyph: to_cp437('*'), + fg : RGB::named(rltk::CYAN), + bg : RGB::named(rltk::BLACK), + lifespan : 300.0, + speed: 50.0, + path: rltk::line2d( + rltk::LineAlg::Bresenham, + Point::new(apos.x, apos.y), + Point::new(dpos.x, dpos.y) + ) + }, + Targets::Tile{tile_idx : map.xy_idx(apos.x, apos.y) as i32} + ); + + // Define the basic unarmed attack - overridden by wielding check below if a weapon is equipped + let mut weapon_info = Weapon{ + range: None, + attribute : WeaponAttribute::Might, + hit_bonus : 0, + damage_n_dice : 1, + damage_die_type : 4, + damage_bonus : 0, + proc_chance : None, + proc_target : None + }; + + if let Some(nat) = natural.get(entity) { + if !nat.attacks.is_empty() { + let attack_index = if nat.attacks.len()==1 { 0 } else { crate::rng::roll_dice(1, nat.attacks.len() as i32) as usize -1 }; + weapon_info.hit_bonus = nat.attacks[attack_index].hit_bonus; + weapon_info.damage_n_dice = nat.attacks[attack_index].damage_n_dice; + weapon_info.damage_die_type = nat.attacks[attack_index].damage_die_type; + weapon_info.damage_bonus = nat.attacks[attack_index].damage_bonus; + } + } + + let mut weapon_entity : Option = None; + for (weaponentity,wielded,melee) in (&entities, &equipped_items, &weapon).join() { + if wielded.owner == entity && wielded.slot == EquipmentSlot::Melee { + weapon_info = melee.clone(); + weapon_entity = Some(weaponentity); + } + } + + let natural_roll = crate::rng::roll_dice(1, 20); + let attribute_hit_bonus = if weapon_info.attribute == WeaponAttribute::Might + { attacker_attributes.might.bonus } + else { attacker_attributes.quickness.bonus}; + let skill_hit_bonus = skill_bonus(Skill::Melee, &*attacker_skills); + let weapon_hit_bonus = weapon_info.hit_bonus; + let mut status_hit_bonus = 0; + if let Some(hc) = hunger_clock.get(entity) { // Well-Fed grants +1 + if hc.state == HungerState::WellFed { + status_hit_bonus += 1; + } + } + let modified_hit_roll = natural_roll + attribute_hit_bonus + skill_hit_bonus + + weapon_hit_bonus + status_hit_bonus; + //println!("Natural roll: {}", natural_roll); + //println!("Modified hit roll: {}", modified_hit_roll); + + let mut armor_item_bonus_f = 0.0; + for (wielded,armor) in (&equipped_items, &wearables).join() { + if wielded.owner == wants_shoot.target { + armor_item_bonus_f += armor.armor_class; + } + } + let base_armor_class = match natural.get(wants_shoot.target) { + None => 10, + Some(nat) => nat.armor_class.unwrap_or(10) + }; + let armor_quickness_bonus = target_attributes.quickness.bonus; + let armor_skill_bonus = skill_bonus(Skill::Defense, &*target_skills); + let armor_item_bonus = armor_item_bonus_f as i32; + let armor_class = base_armor_class + armor_quickness_bonus + armor_skill_bonus + + armor_item_bonus; + + //println!("Armor class: {}", armor_class); + if natural_roll != 1 && (natural_roll == 20 || modified_hit_roll > armor_class) { + // Target hit! Until we support weapons, we're going with 1d4 + let base_damage = crate::rng::roll_dice(weapon_info.damage_n_dice, weapon_info.damage_die_type); + let attr_damage_bonus = attacker_attributes.might.bonus; + let skill_damage_bonus = skill_bonus(Skill::Melee, &*attacker_skills); + let weapon_damage_bonus = weapon_info.damage_bonus; + + let damage = i32::max(0, base_damage + attr_damage_bonus + + skill_damage_bonus + weapon_damage_bonus); + + /*println!("Damage: {} + {}attr + {}skill + {}weapon = {}", + base_damage, attr_damage_bonus, skill_damage_bonus, + weapon_damage_bonus, damage + );*/ + add_effect( + Some(entity), + EffectType::Damage{ amount: damage }, + Targets::Single{ target: wants_shoot.target } + ); + crate::gamelog::Logger::new() + .npc_name(&name.name) + .append("hits") + .npc_name(&target_name.name) + .append("for") + .damage(damage) + .append("hp.") + .log(); + + // Proc effects + if let Some(chance) = &weapon_info.proc_chance { + let roll = crate::rng::roll_dice(1, 100); + //println!("Roll {}, Chance {}", roll, chance); + if roll <= (chance * 100.0) as i32 { + //println!("Proc!"); + let effect_target = if weapon_info.proc_target.unwrap() == "Self" { + Targets::Single{ target: entity } + } else { + Targets::Single { target : wants_shoot.target } + }; + add_effect( + Some(entity), + EffectType::ItemUse{ item: weapon_entity.unwrap() }, + effect_target + ) + } + } + + } else if natural_roll == 1 { + // Natural 1 miss + crate::gamelog::Logger::new() + .npc_name(&name.name) + .append("considers attacking") + .npc_name(&target_name.name) + .append("but misjudges the timing!") + .log(); + add_effect( + None, + EffectType::Particle{ glyph: rltk::to_cp437('‼'), fg: rltk::RGB::named(rltk::BLUE), bg : rltk::RGB::named(rltk::BLACK), lifespan: 200.0 }, + Targets::Single{ target: wants_shoot.target } + ); + } else { + // Miss + crate::gamelog::Logger::new() + .npc_name(&name.name) + .append("attacks") + .npc_name(&target_name.name) + .color(rltk::WHITE) + .append("but can't connect.") + .log(); + add_effect( + None, + EffectType::Particle{ glyph: rltk::to_cp437('‼'), fg: rltk::RGB::named(rltk::CYAN), bg : rltk::RGB::named(rltk::BLACK), lifespan: 200.0 }, + Targets::Single{ target: wants_shoot.target } + ); + } + } + } + + wants_shoot.clear(); + } +} diff --git a/chapter-75-darkplaza/src/systems/trigger_system.rs b/chapter-75-darkplaza/src/systems/trigger_system.rs new file mode 100644 index 00000000..9131758a --- /dev/null +++ b/chapter-75-darkplaza/src/systems/trigger_system.rs @@ -0,0 +1,60 @@ +use specs::prelude::*; +use crate::{EntityMoved, Position, EntryTrigger, Map, Name, + effects::*, AreaOfEffect}; + +pub struct TriggerSystem {} + +impl<'a> System<'a> for TriggerSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( ReadExpect<'a, Map>, + WriteStorage<'a, EntityMoved>, + ReadStorage<'a, Position>, + ReadStorage<'a, EntryTrigger>, + ReadStorage<'a, Name>, + Entities<'a>, + ReadStorage<'a, AreaOfEffect>); + + fn run(&mut self, data : Self::SystemData) { + let (map, mut entity_moved, position, entry_trigger, + names, entities, area_of_effect) = data; + + // Iterate the entities that moved and their final position + for (entity, mut _entity_moved, pos) in (&entities, &mut entity_moved, &position).join() { + let idx = map.xy_idx(pos.x, pos.y); + crate::spatial::for_each_tile_content(idx, |entity_id| { + if entity != entity_id { // Do not bother to check yourself for being a trap! + let maybe_trigger = entry_trigger.get(entity_id); + match maybe_trigger { + None => {}, + Some(_trigger) => { + // We triggered it + let name = names.get(entity_id); + if let Some(name) = name { + crate::gamelog::Logger::new() + .item_name(&name.name) + .append("triggers!") + .log(); + } + + // Call the effects system + add_effect( + Some(entity), + EffectType::TriggerFire{ trigger : entity_id }, + if let Some(aoe) = area_of_effect.get(entity_id) { + Targets::Tiles{ + tiles : aoe_tiles(&*map, rltk::Point::new(pos.x, pos.y), aoe.radius) + } + } else { + Targets::Tile{ tile_idx: idx as i32 } + } + ); + } + } + } + }); + } + + // Remove all entity movement markers + entity_moved.clear(); + } +} diff --git a/chapter-75-darkplaza/src/systems/visibility_system.rs b/chapter-75-darkplaza/src/systems/visibility_system.rs new file mode 100644 index 00000000..ee691f34 --- /dev/null +++ b/chapter-75-darkplaza/src/systems/visibility_system.rs @@ -0,0 +1,66 @@ +use specs::prelude::*; +use crate::{Viewshed, Position, Map, Player, Hidden, BlocksVisibility, Name}; +use rltk::{field_of_view, Point}; + +pub struct VisibilitySystem {} + +impl<'a> System<'a> for VisibilitySystem { + #[allow(clippy::type_complexity)] + type SystemData = ( WriteExpect<'a, Map>, + Entities<'a>, + WriteStorage<'a, Viewshed>, + ReadStorage<'a, Position>, + ReadStorage<'a, Player>, + WriteStorage<'a, Hidden>, + ReadStorage<'a, Name>, + ReadStorage<'a, BlocksVisibility>); + + fn run(&mut self, data : Self::SystemData) { + let (mut map, entities, mut viewshed, pos, player, + mut hidden, names, blocks_visibility) = data; + + map.view_blocked.clear(); + for (block_pos, _block) in (&pos, &blocks_visibility).join() { + let idx = map.xy_idx(block_pos.x, block_pos.y); + map.view_blocked.insert(idx); + } + + for (ent,viewshed,pos) in (&entities, &mut viewshed, &pos).join() { + if viewshed.dirty { + viewshed.dirty = false; + viewshed.visible_tiles = field_of_view(Point::new(pos.x, pos.y), viewshed.range, &*map); + viewshed.visible_tiles.retain(|p| p.x >= 0 && p.x < map.width && p.y >= 0 && p.y < map.height ); + + // If this is the player, reveal what they can see + let _p : Option<&Player> = player.get(ent); + if let Some(_p) = _p { + for t in map.visible_tiles.iter_mut() { *t = false }; + for vis in viewshed.visible_tiles.iter() { + if vis.x > 0 && vis.x < map.width-1 && vis.y > 0 && vis.y < map.height-1 { + let idx = map.xy_idx(vis.x, vis.y); + map.revealed_tiles[idx] = true; + map.visible_tiles[idx] = true; + + // Chance to reveal hidden things + crate::spatial::for_each_tile_content(idx, |e| { + let maybe_hidden = hidden.get(e); + if let Some(_maybe_hidden) = maybe_hidden { + if crate::rng::roll_dice(1,24)==1 { + let name = names.get(e); + if let Some(name) = name { + crate::gamelog::Logger::new() + .append("You spotted:") + .npc_name(&name.name) + .log(); + } + hidden.remove(e); + } + } + }); + } + } + } + } + } + } +}