├── .cargo └── config.toml ├── .github └── workflows │ └── deploy-page.yaml ├── .gitignore ├── .vscode └── launch.json ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── assets ├── actors │ ├── amoeba.actor.ron │ ├── cyclops.actor.ron │ ├── ettin.actor.ron │ ├── frost_giant.actor.ron │ ├── gnoll.actor.ron │ ├── goblin.actor.ron │ ├── hobgoblin.actor.ron │ ├── human.actor.ron │ ├── kobold.actor.ron │ ├── orc.actor.ron │ └── stone_giant.actor.ron ├── combat │ └── rogue.combat.ron ├── fonts │ └── pixeled.ttf ├── inventory_themes │ └── basic.inventorytheme.ron ├── items │ ├── body │ │ ├── chainmail.item.ron │ │ ├── ringmail.item.ron │ │ ├── robe.item.ron │ │ └── scalemail.item.ron │ ├── feet │ │ └── boots_green.item.ron │ ├── finger │ │ └── ring_precious.item.ron │ ├── head │ │ ├── hat_wizard.item.ron │ │ └── helmet.item.ron │ ├── mainhand │ │ ├── club.item.ron │ │ ├── elven_broadsword.item.ron │ │ ├── orcish_dagger.item.ron │ │ ├── spear.item.ron │ │ └── staff.item.ron │ ├── neck │ │ └── amulet_stone.item.ron │ └── offhand │ │ ├── buckler.item.ron │ │ └── shield_large.item.ron ├── map_themes │ ├── cobalt_mosaic.maptheme.ron │ ├── pebble_brick.maptheme.ron │ └── snake.maptheme.ron └── sprites │ ├── actors │ ├── cyclops.png │ ├── ettin.png │ ├── frost_giant.png │ ├── giant_amoeba.png │ ├── gnoll.png │ ├── goblin.png │ ├── hobgoblin.png │ ├── human_male.png │ ├── jacket_2.png │ ├── kobold.png │ ├── orc.png │ ├── pants_black.png │ └── stone_giant.png │ ├── floor │ ├── mosaic_0.png │ ├── mosaic_1.png │ ├── mosaic_10.png │ ├── mosaic_11.png │ ├── mosaic_12.png │ ├── mosaic_13.png │ ├── mosaic_14.png │ ├── mosaic_15.png │ ├── mosaic_2.png │ ├── mosaic_3.png │ ├── mosaic_4.png │ ├── mosaic_5.png │ ├── mosaic_6.png │ ├── mosaic_7.png │ ├── mosaic_8.png │ ├── mosaic_9.png │ ├── pebble_brown_0.png │ ├── pebble_brown_1.png │ ├── pebble_brown_2.png │ ├── pebble_brown_3.png │ ├── pebble_brown_4.png │ ├── pebble_brown_5.png │ ├── pebble_brown_6.png │ ├── pebble_brown_7.png │ ├── pebble_brown_8.png │ ├── snake-a_0.png │ ├── snake-a_1.png │ ├── snake-a_2.png │ ├── snake-a_3.png │ ├── snake-c_0.png │ ├── snake-c_1.png │ ├── snake-c_2.png │ ├── snake-c_3.png │ ├── snake-d_0.png │ ├── snake-d_1.png │ ├── snake-d_2.png │ ├── snake-d_3.png │ ├── snake_0.png │ ├── snake_1.png │ ├── snake_2.png │ └── snake_3.png │ ├── gui │ ├── inventory │ │ ├── body_wear.png │ │ ├── feet_wear.png │ │ ├── finger_wear.png │ │ ├── head_wear.png │ │ ├── main_hand_gear.png │ │ ├── neck_wear.png │ │ ├── off_hand_gear.png │ │ ├── potion.png │ │ ├── scroll.png │ │ └── slot.png │ └── tooltip │ │ ├── cursor.png │ │ ├── cursor_green.png │ │ └── cursor_red.png │ ├── item │ ├── boots_4_green.png │ ├── buckler_1.png │ ├── chain_mail_1.png │ ├── club.png │ ├── elven_broadsword.png │ ├── gold_green.png │ ├── helmet_1.png │ ├── large_shield_1_new.png │ ├── orcish_dagger.png │ ├── ring_mail_1.png │ ├── robe_1_new.png │ ├── scale_mail_1_new.png │ ├── spear.png │ ├── staff_8.png │ ├── stone_3_blue.png │ ├── two_handed_sword.png │ └── wizard_hat_1.png │ ├── item_equiped │ ├── buckler_round_3.png │ ├── chainmail.png │ ├── club_slant.png │ ├── dagger_slant.png │ ├── heavy_sword.png │ ├── helm_gimli.png │ ├── lshield_long_red.png │ ├── middle_green.png │ ├── ringmail.png │ ├── robe_brown_2.png │ ├── scalemail.png │ ├── spear.png │ ├── staff_large.png │ └── wizard_white.png │ └── walls │ ├── brick_brown_0.png │ ├── brick_brown_1.png │ ├── brick_brown_2.png │ ├── brick_brown_3.png │ ├── brick_brown_4.png │ ├── brick_brown_5.png │ ├── brick_brown_6.png │ ├── brick_brown_7.png │ ├── cobalt_rock_1.png │ ├── cobalt_rock_2.png │ ├── cobalt_rock_3.png │ ├── cobalt_rock_4.png │ ├── cobalt_stone_1.png │ ├── cobalt_stone_10.png │ ├── cobalt_stone_11.png │ ├── cobalt_stone_12.png │ ├── cobalt_stone_2.png │ ├── cobalt_stone_3.png │ ├── cobalt_stone_4.png │ ├── cobalt_stone_5.png │ ├── cobalt_stone_6.png │ ├── cobalt_stone_7.png │ ├── cobalt_stone_8.png │ ├── cobalt_stone_9.png │ ├── snake_0.png │ ├── snake_1.png │ ├── snake_2.png │ ├── snake_3.png │ ├── snake_4.png │ ├── snake_5.png │ ├── snake_6.png │ ├── snake_7.png │ ├── snake_8.png │ └── snake_9.png ├── bevy_inventory ├── Cargo.toml └── src │ ├── events.rs │ └── lib.rs ├── bevy_inventory_ui ├── Cargo.toml └── src │ ├── assets.rs │ ├── draggable_ui.rs │ ├── lib.rs │ ├── plugin.rs │ └── systems.rs ├── bevy_roguelike_combat ├── Cargo.toml └── src │ ├── components │ ├── action_cost.rs │ ├── action_points.rs │ ├── attributes.rs │ ├── block.rs │ ├── bundles.rs │ ├── damage.rs │ ├── evasion.rs │ ├── formula.rs │ ├── hit_points.rs │ ├── mod.rs │ ├── protection.rs │ ├── rate.rs │ ├── resistance.rs │ └── stats_computed.rs │ ├── events.rs │ ├── lib.rs │ ├── plugin.rs │ ├── rng.rs │ └── systems.rs ├── bevy_roguelike_plugin ├── Cargo.toml └── src │ ├── components │ ├── actor │ │ └── mod.rs │ ├── damage.rs │ ├── environment.rs │ ├── fov.rs │ ├── item │ │ ├── from_template.rs │ │ ├── mod.rs │ │ └── quality.rs │ ├── mod.rs │ ├── render_info.rs │ └── vector2d.rs │ ├── events │ └── mod.rs │ ├── lib.rs │ ├── resources │ ├── actor_template.rs │ ├── combat_settings.rs │ ├── inventory_assets.rs │ ├── item_template.rs │ ├── map_info.rs │ ├── map_options.rs │ ├── map_theme.rs │ ├── mod.rs │ └── rogue_map.rs │ └── systems │ ├── action.rs │ ├── actor_stats.rs │ ├── camera.rs │ ├── fov.rs │ ├── input.rs │ ├── inventory.rs │ ├── map.rs │ ├── mod.rs │ ├── render.rs │ └── turns.rs ├── credits └── CREDITS.md ├── example.png ├── index.html ├── inventory.png ├── map_generator ├── Cargo.toml └── src │ ├── drunkard.rs │ ├── empty.rs │ ├── lib.rs │ ├── life.rs │ ├── map.rs │ ├── rect.rs │ └── rooms.rs ├── rust-toolchain.toml ├── src └── main.rs ├── vec_walk_dir ├── Cargo.toml └── src │ ├── lib.rs │ └── vec_walk_dir.rs └── web ├── sounds.js └── styles.css /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.wasm32-unknown-unknown] 2 | runner = "wasm-server-runner" 3 | 4 | [alias] 5 | serve = "run --target wasm32-unknown-unknown" 6 | -------------------------------------------------------------------------------- /.github/workflows/deploy-page.yaml: -------------------------------------------------------------------------------- 1 | name: deploy-github-page 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build-web: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@v2 13 | - name: Install rust toolchain 14 | uses: actions-rs/toolchain@v1 15 | with: 16 | toolchain: stable 17 | override: true 18 | - name: Install Dependencies 19 | run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev 20 | - name: Install trunk 21 | uses: jetli/trunk-action@v0.1.0 22 | with: 23 | version: 'latest' 24 | - name: Add wasm target 25 | run: | 26 | rustup target add wasm32-unknown-unknown 27 | - name: Build Release 28 | run: | 29 | trunk build --release --public-url "${GITHUB_REPOSITORY#*/}" 30 | - name: optimize Wasm 31 | uses: NiklasEi/wasm-opt-action@v2 32 | with: 33 | file: dist/*.wasm 34 | - name: Deploy to GitHub Pages 35 | uses: JamesIves/github-pages-deploy-action@v4.2.5 36 | with: 37 | branch: gh-pages 38 | folder: dist 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | /dist -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug executable 'rogue-tiny'", 11 | "cargo": { 12 | "args": [ 13 | "build", 14 | "--bin=rogue-tiny", 15 | "--package=rogue-tiny", 16 | "--features", 17 | "debug" 18 | ], 19 | "filter": { 20 | "name": "rogue-tiny", 21 | "kind": "bin" 22 | } 23 | }, 24 | "env": { 25 | "CARGO_MANIFEST_DIR": "${workspaceFolder}" 26 | }, 27 | "args": [], 28 | "cwd": "${workspaceFolder}" 29 | }, 30 | ] 31 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rogue-tiny" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [features] 9 | default = [] 10 | debug = ["bevy_roguelike_plugin/debug", "bevy-inspector-egui"] 11 | 12 | [dependencies] 13 | bevy_roguelike_plugin = { path = "bevy_roguelike_plugin" } 14 | bevy_inventory_ui = { path = "bevy_inventory_ui" } 15 | bevy-inspector-egui = { version = "~0.14", optional = true } 16 | 17 | [dependencies.bevy] 18 | version = "~0.9" 19 | default-features = false 20 | features = ["render", "bevy_winit", "png"] 21 | 22 | # Dependencies for native only. 23 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies.bevy] 24 | version = "~0.9" 25 | default-features = false 26 | features = ["x11"] 27 | 28 | [workspace] 29 | members = [ 30 | "bevy_roguelike_plugin", 31 | "bevy_roguelike_combat", 32 | "map_generator", 33 | "bevy_inventory", 34 | "bevy_inventory_ui", 35 | "vec_walk_dir", 36 | ] 37 | 38 | # Enable optimizations for dependencies (incl. Bevy), but not for our code: 39 | [profile.dev.package."*"] 40 | opt-level = 3 41 | 42 | # Maybe also enable only a small amount of optimization for our code: 43 | # [profile.dev] 44 | # opt-level = 1 45 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /assets/actors/amoeba.actor.ron: -------------------------------------------------------------------------------- 1 | ( 2 | render: ( 3 | name: "Amoeba", 4 | texture_path: "sprites/actors/giant_amoeba.png", 5 | texture_path_cosmetics: [], 6 | ), 7 | attributes: ( 8 | list: { 9 | Inteligence: 1, 10 | Willpower: 1, 11 | Perception: 2, 12 | Toughness: 7, 13 | Dexterity: 6, 14 | Strength: 3, 15 | }, 16 | ), 17 | protection: ( 18 | amounts: [ 19 | ( 20 | kind: Blunt, 21 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 22 | amount: 1, 23 | ), 24 | ( 25 | kind: Pierce, 26 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 27 | amount: 6, 28 | ), 29 | ( 30 | kind: Slash, 31 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 32 | amount: 3, 33 | ), 34 | ], 35 | ), 36 | resistance: ( 37 | amounts: [ 38 | ( 39 | kind: Slash, 40 | percent: 5, 41 | ), 42 | ( 43 | kind: Blunt, 44 | percent: 1, 45 | ), 46 | ( 47 | kind: Pierce, 48 | percent: 10, 49 | ), 50 | ], 51 | ), 52 | evasion: ( 53 | cost: ( 54 | cost: 128, 55 | multiplier_inverted: ( 56 | scale: 100, 57 | multipliers: [(multiplier:70,attribute:Dexterity)], 58 | ), 59 | ), 60 | chance: ( 61 | amount: 10, 62 | multiplier: ( 63 | scale: 100, 64 | multipliers: [(multiplier:90,attribute:Dexterity)], 65 | ), 66 | ), 67 | ), 68 | damage: ( 69 | list: [ 70 | ( 71 | kind: Blunt, 72 | amount: (start:2,end:4), 73 | amount_multiplier: (scale:100,multipliers:[(multiplier:80,attribute:Strength)]), 74 | hit_cost: (cost:128,multiplier_inverted:(scale:100,multipliers:[(multiplier:80,attribute:Dexterity)])), 75 | hit_chance: (amount:128,multiplier:(scale:100,multipliers:[(multiplier:128,attribute:Dexterity)])), 76 | ), 77 | ], 78 | ), 79 | equipment_display: ( 80 | items: { 81 | (OffHand, 0): (152, 48), 82 | }, 83 | ), 84 | inventory_capacity: 8, 85 | ) -------------------------------------------------------------------------------- /assets/actors/cyclops.actor.ron: -------------------------------------------------------------------------------- 1 | ( 2 | render: ( 3 | name: "Cyclops", 4 | texture_path: "sprites/actors/cyclops.png", 5 | texture_path_cosmetics: [], 6 | ), 7 | attributes: ( 8 | list: { 9 | Inteligence: 4, 10 | Willpower: 8, 11 | Perception: 8, 12 | Toughness: 14, 13 | Dexterity: 9, 14 | Strength: 15, 15 | }, 16 | ), 17 | protection: ( 18 | amounts: [ 19 | ( 20 | kind: Blunt, 21 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 22 | amount: 2, 23 | ), 24 | ( 25 | kind: Pierce, 26 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 27 | amount: 2, 28 | ), 29 | ( 30 | kind: Slash, 31 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 32 | amount: 2, 33 | ), 34 | ], 35 | ), 36 | resistance: ( 37 | amounts: [ 38 | ( 39 | kind: Slash, 40 | percent: 5, 41 | ), 42 | ( 43 | kind: Blunt, 44 | percent: 5, 45 | ), 46 | ( 47 | kind: Pierce, 48 | percent: 5, 49 | ), 50 | ], 51 | ), 52 | evasion: ( 53 | cost: ( 54 | cost: 64, 55 | multiplier_inverted: ( 56 | scale: 100, 57 | multipliers: [(multiplier:70,attribute:Dexterity)], 58 | ), 59 | ), 60 | chance: ( 61 | amount: 18, 62 | multiplier: ( 63 | scale: 100, 64 | multipliers: [(multiplier:90,attribute:Dexterity)], 65 | ), 66 | ), 67 | ), 68 | damage: ( 69 | list: [ 70 | ( 71 | kind: Blunt, 72 | amount: (start:12,end:14), 73 | amount_multiplier: (scale:100,multipliers:[(multiplier:80,attribute:Strength)]), 74 | hit_cost: (cost:128,multiplier_inverted:(scale:100,multipliers:[(multiplier:80,attribute:Dexterity)])), 75 | hit_chance: (amount:128,multiplier:(scale:100,multipliers:[(multiplier:128,attribute:Dexterity)])), 76 | ), 77 | ], 78 | ), 79 | equipment_display: ( 80 | items: { 81 | (OffHand, 0): (152, 48), 82 | (Finger, 1): (152, 88), 83 | (Finger, 0): (72, 88), 84 | (Neck, 0): (152, 8), 85 | (MainHand, 0): (72, 48), 86 | }, 87 | ), 88 | inventory_capacity: 8, 89 | ) -------------------------------------------------------------------------------- /assets/actors/ettin.actor.ron: -------------------------------------------------------------------------------- 1 | ( 2 | render: ( 3 | name: "Ettin", 4 | texture_path: "sprites/actors/ettin.png", 5 | texture_path_cosmetics: [], 6 | ), 7 | attributes: ( 8 | list: { 9 | Inteligence: 12, 10 | Willpower: 10, 11 | Perception: 16, 12 | Toughness: 12, 13 | Dexterity: 7, 14 | Strength: 12, 15 | }, 16 | ), 17 | protection: ( 18 | amounts: [ 19 | ( 20 | kind: Blunt, 21 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 22 | amount: 2, 23 | ), 24 | ( 25 | kind: Pierce, 26 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 27 | amount: 2, 28 | ), 29 | ( 30 | kind: Slash, 31 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 32 | amount: 2, 33 | ), 34 | ], 35 | ), 36 | resistance: ( 37 | amounts: [ 38 | ( 39 | kind: Slash, 40 | percent: 5, 41 | ), 42 | ( 43 | kind: Blunt, 44 | percent: 5, 45 | ), 46 | ( 47 | kind: Pierce, 48 | percent: 5, 49 | ), 50 | ], 51 | ), 52 | evasion: ( 53 | cost: ( 54 | cost: 44, 55 | multiplier_inverted: ( 56 | scale: 100, 57 | multipliers: [(multiplier:80,attribute:Dexterity)], 58 | ), 59 | ), 60 | chance: ( 61 | amount: 18, 62 | multiplier: ( 63 | scale: 100, 64 | multipliers: [(multiplier:100,attribute:Dexterity)], 65 | ), 66 | ), 67 | ), 68 | damage: ( 69 | list: [ 70 | ( 71 | kind: Blunt, 72 | amount: (start:10,end:12), 73 | amount_multiplier: (scale:100,multipliers:[(multiplier:80,attribute:Strength)]), 74 | hit_cost: (cost:128,multiplier_inverted:(scale:100,multipliers:[(multiplier:80,attribute:Dexterity)])), 75 | hit_chance: (amount:128,multiplier:(scale:100,multipliers:[(multiplier:128,attribute:Dexterity)])), 76 | ), 77 | ], 78 | ), 79 | equipment_display: ( 80 | items: { 81 | (OffHand, 0): (152, 48), 82 | (MainHand, 0): (72, 48), 83 | (Finger, 0): (72, 88), 84 | (Finger, 1): (152, 88), 85 | (Neck, 0): (152, 8), 86 | (Neck, 1): (192, 8), 87 | }, 88 | ), 89 | inventory_capacity: 8, 90 | ) -------------------------------------------------------------------------------- /assets/actors/frost_giant.actor.ron: -------------------------------------------------------------------------------- 1 | ( 2 | render: ( 3 | name: "Frost giant", 4 | texture_path: "sprites/actors/frost_giant.png", 5 | texture_path_cosmetics: [], 6 | ), 7 | attributes: ( 8 | list: { 9 | Inteligence: 12, 10 | Willpower: 7, 11 | Perception: 12, 12 | Toughness: 13, 13 | Dexterity: 7, 14 | Strength: 10, 15 | }, 16 | ), 17 | protection: ( 18 | amounts: [ 19 | ( 20 | kind: Blunt, 21 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 22 | amount: 2, 23 | ), 24 | ( 25 | kind: Pierce, 26 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 27 | amount: 2, 28 | ), 29 | ( 30 | kind: Slash, 31 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 32 | amount: 2, 33 | ), 34 | ], 35 | ), 36 | resistance: ( 37 | amounts: [ 38 | ( 39 | kind: Slash, 40 | percent: 5, 41 | ), 42 | ( 43 | kind: Blunt, 44 | percent: 5, 45 | ), 46 | ( 47 | kind: Pierce, 48 | percent: 5, 49 | ), 50 | ( 51 | kind: Cold, 52 | percent: 50, 53 | ), 54 | ( 55 | kind: Lightning, 56 | percent: 10, 57 | ), 58 | ], 59 | ), 60 | evasion: ( 61 | cost: ( 62 | cost: 64, 63 | multiplier_inverted: ( 64 | scale: 100, 65 | multipliers: [(multiplier:80,attribute:Dexterity)], 66 | ), 67 | ), 68 | chance: ( 69 | amount: 18, 70 | multiplier: ( 71 | scale: 100, 72 | multipliers: [(multiplier:100,attribute:Dexterity)], 73 | ), 74 | ), 75 | ), 76 | damage: ( 77 | list: [ 78 | ( 79 | kind: Cold, 80 | amount: (start:16,end:24), 81 | amount_multiplier: (scale:100,multipliers:[(multiplier:60,attribute:Inteligence),(multiplier:20,attribute:Strength)]), 82 | hit_cost: (cost:128,multiplier_inverted:(scale:100,multipliers:[(multiplier:80,attribute:Dexterity)])), 83 | hit_chance: (amount:128,multiplier:(scale:100,multipliers:[(multiplier:128,attribute:Dexterity)])), 84 | ), 85 | ], 86 | ), 87 | equipment_display: ( 88 | items: { 89 | (OffHand, 0): (152, 48), 90 | (Finger, 1): (152, 88), 91 | (Finger, 0): (72, 88), 92 | (Neck, 0): (152, 8), 93 | (MainHand, 0): (72, 48), 94 | }, 95 | ), 96 | inventory_capacity: 8, 97 | ) -------------------------------------------------------------------------------- /assets/actors/gnoll.actor.ron: -------------------------------------------------------------------------------- 1 | ( 2 | render: ( 3 | name: "Gnoll", 4 | texture_path: "sprites/actors/gnoll.png", 5 | texture_path_cosmetics: [], 6 | ), 7 | attributes: ( 8 | list: { 9 | Inteligence: 4, 10 | Willpower: 10, 11 | Perception: 8, 12 | Toughness: 9, 13 | Dexterity: 11, 14 | Strength: 9, 15 | }, 16 | ), 17 | protection: ( 18 | amounts: [ 19 | ( 20 | kind: Blunt, 21 | amount_multiplier: (scale:100,multipliers:[(multiplier:80,attribute:Toughness)]), 22 | amount: 1, 23 | ), 24 | ( 25 | kind: Pierce, 26 | amount_multiplier: (scale:100,multipliers:[(multiplier:80,attribute:Toughness)]), 27 | amount: 1, 28 | ), 29 | ( 30 | kind: Slash, 31 | amount_multiplier: (scale:100,multipliers:[(multiplier:80,attribute:Toughness)]), 32 | amount: 1, 33 | ), 34 | ], 35 | ), 36 | resistance: ( 37 | amounts: [ 38 | ( 39 | kind: Slash, 40 | percent: 5, 41 | ), 42 | ( 43 | kind: Blunt, 44 | percent: 5, 45 | ), 46 | ( 47 | kind: Pierce, 48 | percent: 5, 49 | ), 50 | ], 51 | ), 52 | evasion: ( 53 | cost: ( 54 | cost: 32, 55 | multiplier_inverted: ( 56 | scale: 100, 57 | multipliers: [(multiplier:80,attribute:Dexterity)], 58 | ), 59 | ), 60 | chance: ( 61 | amount: 20, 62 | multiplier: ( 63 | scale: 100, 64 | multipliers: [(multiplier:100,attribute:Dexterity)], 65 | ), 66 | ), 67 | ), 68 | damage: ( 69 | list: [ 70 | ( 71 | kind: Pierce, 72 | amount: (start:4,end:14), 73 | amount_multiplier: (scale:100,multipliers:[(multiplier:60,attribute:Strength),(multiplier:20,attribute:Perception)]), 74 | hit_cost: (cost:128,multiplier_inverted:(scale:100,multipliers:[(multiplier:80,attribute:Dexterity)])), 75 | hit_chance: (amount:128,multiplier:(scale:100,multipliers:[(multiplier:128,attribute:Dexterity)])), 76 | ), 77 | ( 78 | kind: Slash, 79 | amount: (start:7,end:8), 80 | amount_multiplier: (scale:100,multipliers:[(multiplier:80,attribute:Strength)]), 81 | hit_cost: (cost:128,multiplier_inverted:(scale:100,multipliers:[(multiplier:80,attribute:Dexterity)])), 82 | hit_chance: (amount:128,multiplier:(scale:100,multipliers:[(multiplier:128,attribute:Dexterity)])), 83 | ), 84 | ], 85 | ), 86 | equipment_display: ( 87 | items: { 88 | (Finger, 1): (152, 88), 89 | (Finger, 0): (72, 88), 90 | (Neck, 0): (152, 8), 91 | (MainHand, 0): (72, 48), 92 | }, 93 | ), 94 | inventory_capacity: 16, 95 | ) -------------------------------------------------------------------------------- /assets/actors/goblin.actor.ron: -------------------------------------------------------------------------------- 1 | ( 2 | render: ( 3 | name: "Goblin", 4 | texture_path: "sprites/actors/goblin.png", 5 | texture_path_cosmetics: [], 6 | ), 7 | attributes: ( 8 | list: { 9 | Inteligence: 4, 10 | Willpower: 6, 11 | Perception: 8, 12 | Toughness: 7, 13 | Dexterity: 14, 14 | Strength: 8, 15 | }, 16 | ), 17 | protection: ( 18 | amounts: [ 19 | ( 20 | kind: Blunt, 21 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 22 | amount: 1, 23 | ), 24 | ( 25 | kind: Pierce, 26 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 27 | amount: 1, 28 | ), 29 | ( 30 | kind: Slash, 31 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 32 | amount: 1, 33 | ), 34 | ], 35 | ), 36 | resistance: ( 37 | amounts: [ 38 | ( 39 | kind: Slash, 40 | percent: 5, 41 | ), 42 | ( 43 | kind: Blunt, 44 | percent: 5, 45 | ), 46 | ( 47 | kind: Pierce, 48 | percent: 5, 49 | ), 50 | ], 51 | ), 52 | evasion: ( 53 | cost: ( 54 | cost: 32, 55 | multiplier_inverted: ( 56 | scale: 100, 57 | multipliers: [(multiplier:80,attribute:Dexterity)], 58 | ), 59 | ), 60 | chance: ( 61 | amount: 20, 62 | multiplier: ( 63 | scale: 100, 64 | multipliers: [(multiplier:100,attribute:Dexterity)], 65 | ), 66 | ), 67 | ), 68 | damage: ( 69 | list: [ 70 | ( 71 | kind: Blunt, 72 | amount: (start:8,end:10), 73 | amount_multiplier: (scale:100,multipliers:[(multiplier:80,attribute:Strength)]), 74 | hit_cost: (cost:128,multiplier_inverted:(scale:100,multipliers:[(multiplier:80,attribute:Dexterity)])), 75 | hit_chance: (amount:128,multiplier:(scale:100,multipliers:[(multiplier:128,attribute:Dexterity)])), 76 | ), 77 | ], 78 | ), 79 | equipment_display: ( 80 | items: { 81 | (OffHand, 0): (152, 48), 82 | (Finger, 1): (152, 88), 83 | (Finger, 0): (72, 88), 84 | (Neck, 0): (152, 8), 85 | (MainHand, 0): (72, 48), 86 | }, 87 | ), 88 | inventory_capacity: 16, 89 | ) -------------------------------------------------------------------------------- /assets/actors/hobgoblin.actor.ron: -------------------------------------------------------------------------------- 1 | ( 2 | render: ( 3 | name: "Hobgoblin", 4 | texture_path: "sprites/actors/hobgoblin.png", 5 | texture_path_cosmetics: [], 6 | ), 7 | attributes: ( 8 | list: { 9 | Inteligence: 5, 10 | Willpower: 6, 11 | Perception: 8, 12 | Toughness: 11, 13 | Dexterity: 14, 14 | Strength: 9, 15 | }, 16 | ), 17 | protection: ( 18 | amounts: [ 19 | ( 20 | kind: Blunt, 21 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 22 | amount: 1, 23 | ), 24 | ( 25 | kind: Pierce, 26 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 27 | amount: 1, 28 | ), 29 | ( 30 | kind: Slash, 31 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 32 | amount: 1, 33 | ), 34 | ], 35 | ), 36 | resistance: ( 37 | amounts: [ 38 | ( 39 | kind: Slash, 40 | percent: 5, 41 | ), 42 | ( 43 | kind: Blunt, 44 | percent: 5, 45 | ), 46 | ( 47 | kind: Pierce, 48 | percent: 5, 49 | ), 50 | ], 51 | ), 52 | evasion: ( 53 | cost: ( 54 | cost: 32, 55 | multiplier_inverted: ( 56 | scale: 100, 57 | multipliers: [(multiplier:80,attribute:Dexterity)], 58 | ), 59 | ), 60 | chance: ( 61 | amount: 20, 62 | multiplier: ( 63 | scale: 100, 64 | multipliers: [(multiplier:100,attribute:Dexterity)], 65 | ), 66 | ), 67 | ), 68 | damage: ( 69 | list: [ 70 | ( 71 | kind: Blunt, 72 | amount: (start:9,end:11), 73 | amount_multiplier: (scale:100,multipliers:[(multiplier:80,attribute:Strength)]), 74 | hit_cost: (cost:128,multiplier_inverted:(scale:100,multipliers:[(multiplier:80,attribute:Dexterity)])), 75 | hit_chance: (amount:128,multiplier:(scale:100,multipliers:[(multiplier:128,attribute:Dexterity)])), 76 | ), 77 | ], 78 | ), 79 | equipment_display: ( 80 | items: { 81 | (OffHand, 0): (152, 48), 82 | (Finger, 1): (152, 88), 83 | (Finger, 0): (72, 88), 84 | (Neck, 0): (152, 8), 85 | (MainHand, 0): (72, 48), 86 | }, 87 | ), 88 | inventory_capacity: 16, 89 | ) -------------------------------------------------------------------------------- /assets/actors/human.actor.ron: -------------------------------------------------------------------------------- 1 | ( 2 | render: ( 3 | name: "Human", 4 | texture_path: "sprites/actors/human_male.png", 5 | texture_path_cosmetics: [ 6 | "sprites/actors/jacket_2.png", 7 | "sprites/actors/pants_black.png" 8 | ], 9 | ), 10 | attributes: ( 11 | list: { 12 | Inteligence: 10, 13 | Willpower: 10, 14 | Perception: 10, 15 | Toughness: 10, 16 | Dexterity: 10, 17 | Strength: 10, 18 | }, 19 | ), 20 | protection: ( 21 | amounts: [ 22 | ( 23 | kind: Blunt, 24 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 25 | amount: 1, 26 | ), 27 | ( 28 | kind: Pierce, 29 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 30 | amount: 1, 31 | ), 32 | ( 33 | kind: Slash, 34 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 35 | amount: 1, 36 | ), 37 | ], 38 | ), 39 | resistance: ( 40 | amounts: [ 41 | ( 42 | kind: Slash, 43 | percent: 5, 44 | ), 45 | ( 46 | kind: Blunt, 47 | percent: 5, 48 | ), 49 | ( 50 | kind: Pierce, 51 | percent: 5, 52 | ), 53 | ], 54 | ), 55 | evasion: ( 56 | cost: ( 57 | cost: 32, 58 | multiplier_inverted: ( 59 | scale: 100, 60 | multipliers: [(multiplier:80,attribute:Dexterity)], 61 | ), 62 | ), 63 | chance: ( 64 | amount: 20, 65 | multiplier: ( 66 | scale: 100, 67 | multipliers: [(multiplier:100,attribute:Dexterity)], 68 | ), 69 | ), 70 | ), 71 | damage: ( 72 | list: [ 73 | ( 74 | kind: Blunt, 75 | amount: (start:4,end:10), 76 | amount_multiplier: (scale:100,multipliers:[(multiplier:80,attribute:Strength)]), 77 | hit_cost: (cost:128,multiplier_inverted:(scale:100,multipliers:[(multiplier:80,attribute:Dexterity)])), 78 | hit_chance: (amount:128,multiplier:(scale:100,multipliers:[(multiplier:128,attribute:Dexterity)])), 79 | ), 80 | ], 81 | ), 82 | equipment_display: ( 83 | items: { 84 | (OffHand, 0): (152, 48), 85 | (Finger, 1): (152, 88), 86 | (Feet, 0): (112, 88), 87 | (Head, 0): (112, 8), 88 | (Finger, 0): (72, 88), 89 | (Neck, 0): (152, 8), 90 | (Body, 0): (112, 48), 91 | (MainHand, 0): (72, 48), 92 | }, 93 | ), 94 | inventory_capacity: 32, 95 | ) -------------------------------------------------------------------------------- /assets/actors/kobold.actor.ron: -------------------------------------------------------------------------------- 1 | ( 2 | render: ( 3 | name: "Kobold", 4 | texture_path: "sprites/actors/kobold.png", 5 | texture_path_cosmetics: [], 6 | ), 7 | attributes: ( 8 | list: { 9 | Inteligence: 4, 10 | Willpower: 6, 11 | Perception: 10, 12 | Toughness: 6, 13 | Dexterity: 12, 14 | Strength: 9, 15 | }, 16 | ), 17 | protection: ( 18 | amounts: [ 19 | ( 20 | kind: Blunt, 21 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 22 | amount: 1, 23 | ), 24 | ( 25 | kind: Pierce, 26 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 27 | amount: 1, 28 | ), 29 | ( 30 | kind: Slash, 31 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 32 | amount: 1, 33 | ), 34 | ], 35 | ), 36 | resistance: ( 37 | amounts: [ 38 | ( 39 | kind: Slash, 40 | percent: 5, 41 | ), 42 | ( 43 | kind: Blunt, 44 | percent: 5, 45 | ), 46 | ( 47 | kind: Pierce, 48 | percent: 5, 49 | ), 50 | ], 51 | ), 52 | evasion: ( 53 | cost: ( 54 | cost: 32, 55 | multiplier_inverted: ( 56 | scale: 100, 57 | multipliers: [(multiplier:80,attribute:Dexterity)], 58 | ), 59 | ), 60 | chance: ( 61 | amount: 20, 62 | multiplier: ( 63 | scale: 100, 64 | multipliers: [(multiplier:100,attribute:Dexterity)], 65 | ), 66 | ), 67 | ), 68 | damage: ( 69 | list: [ 70 | ( 71 | kind: Pierce, 72 | amount: (start:8,end:10), 73 | amount_multiplier: (scale:100,multipliers:[(multiplier:80,attribute:Strength)]), 74 | hit_cost: (cost:128,multiplier_inverted:(scale:100,multipliers:[(multiplier:80,attribute:Dexterity)])), 75 | hit_chance: (amount:128,multiplier:(scale:100,multipliers:[(multiplier:128,attribute:Dexterity)])), 76 | ), 77 | ], 78 | ), 79 | equipment_display: ( 80 | items: { 81 | (OffHand, 0): (152, 48), 82 | (Finger, 1): (152, 88), 83 | (Finger, 0): (72, 88), 84 | (Neck, 0): (152, 8), 85 | (MainHand, 0): (72, 48), 86 | }, 87 | ), 88 | inventory_capacity: 16, 89 | ) -------------------------------------------------------------------------------- /assets/actors/orc.actor.ron: -------------------------------------------------------------------------------- 1 | ( 2 | render: ( 3 | name: "Orc", 4 | texture_path: "sprites/actors/orc.png", 5 | texture_path_cosmetics: [], 6 | ), 7 | attributes: ( 8 | list: { 9 | Inteligence: 8, 10 | Willpower: 7, 11 | Perception: 7, 12 | Toughness: 12, 13 | Dexterity: 11, 14 | Strength: 13, 15 | }, 16 | ), 17 | protection: ( 18 | amounts: [ 19 | ( 20 | kind: Blunt, 21 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 22 | amount: 2, 23 | ), 24 | ( 25 | kind: Pierce, 26 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 27 | amount: 2, 28 | ), 29 | ( 30 | kind: Slash, 31 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 32 | amount: 2, 33 | ), 34 | ], 35 | ), 36 | resistance: ( 37 | amounts: [ 38 | ( 39 | kind: Slash, 40 | percent: 5, 41 | ), 42 | ( 43 | kind: Blunt, 44 | percent: 5, 45 | ), 46 | ( 47 | kind: Pierce, 48 | percent: 5, 49 | ), 50 | ], 51 | ), 52 | evasion: ( 53 | cost: ( 54 | cost: 32, 55 | multiplier_inverted: ( 56 | scale: 100, 57 | multipliers: [(multiplier:80,attribute:Dexterity)], 58 | ), 59 | ), 60 | chance: ( 61 | amount: 20, 62 | multiplier: ( 63 | scale: 100, 64 | multipliers: [(multiplier:100,attribute:Dexterity)], 65 | ), 66 | ), 67 | ), 68 | damage: ( 69 | list: [ 70 | ( 71 | kind: Blunt, 72 | amount: (start:8,end:12), 73 | amount_multiplier: (scale:100,multipliers:[(multiplier:80,attribute:Strength)]), 74 | hit_cost: (cost:128,multiplier_inverted:(scale:100,multipliers:[(multiplier:80,attribute:Dexterity)])), 75 | hit_chance: (amount:128,multiplier:(scale:100,multipliers:[(multiplier:128,attribute:Dexterity)])), 76 | ), 77 | ], 78 | ), 79 | equipment_display: ( 80 | items: { 81 | (OffHand, 0): (152, 48), 82 | (Finger, 1): (152, 88), 83 | (Finger, 0): (72, 88), 84 | (Neck, 0): (152, 8), 85 | (MainHand, 0): (72, 48), 86 | }, 87 | ), 88 | inventory_capacity: 24, 89 | ) -------------------------------------------------------------------------------- /assets/actors/stone_giant.actor.ron: -------------------------------------------------------------------------------- 1 | ( 2 | render: ( 3 | name: "Stone giant", 4 | texture_path: "sprites/actors/stone_giant.png", 5 | texture_path_cosmetics: [], 6 | ), 7 | attributes: ( 8 | list: { 9 | Inteligence: 11, 10 | Willpower: 8, 11 | Perception: 12, 12 | Toughness: 16, 13 | Dexterity: 6, 14 | Strength: 14, 15 | }, 16 | ), 17 | protection: ( 18 | amounts: [ 19 | ( 20 | kind: Blunt, 21 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 22 | amount: 4, 23 | ), 24 | ( 25 | kind: Pierce, 26 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 27 | amount: 4, 28 | ), 29 | ( 30 | kind: Slash, 31 | amount_multiplier: (scale:100,multipliers:[(multiplier:100,attribute:Toughness)]), 32 | amount: 4, 33 | ), 34 | ], 35 | ), 36 | resistance: ( 37 | amounts: [ 38 | ( 39 | kind: Slash, 40 | percent: 10, 41 | ), 42 | ( 43 | kind: Blunt, 44 | percent: 20, 45 | ), 46 | ( 47 | kind: Pierce, 48 | percent: 30, 49 | ), 50 | ], 51 | ), 52 | evasion: ( 53 | cost: ( 54 | cost: 64, 55 | multiplier_inverted: ( 56 | scale: 100, 57 | multipliers: [(multiplier:80,attribute:Dexterity)], 58 | ), 59 | ), 60 | chance: ( 61 | amount: 14, 62 | multiplier: ( 63 | scale: 100, 64 | multipliers: [(multiplier:100,attribute:Dexterity)], 65 | ), 66 | ), 67 | ), 68 | damage: ( 69 | list: [ 70 | ( 71 | kind: Blunt, 72 | amount: (start:14,end:18), 73 | amount_multiplier: (scale:100,multipliers:[(multiplier:80,attribute:Strength)]), 74 | hit_cost: (cost:128,multiplier_inverted:(scale:100,multipliers:[(multiplier:80,attribute:Dexterity)])), 75 | hit_chance: (amount:128,multiplier:(scale:100,multipliers:[(multiplier:128,attribute:Dexterity)])), 76 | ), 77 | ], 78 | ), 79 | equipment_display: ( 80 | items: { 81 | (Finger, 1): (152, 88), 82 | (Finger, 0): (72, 88), 83 | (Neck, 0): (152, 8), 84 | (MainHand, 0): (72, 48), 85 | }, 86 | ), 87 | inventory_capacity: 8, 88 | ) -------------------------------------------------------------------------------- /assets/combat/rogue.combat.ron: -------------------------------------------------------------------------------- 1 | ( 2 | ap_increment_formula: ( 3 | scale:6144, 4 | multipliers:[ 5 | (multiplier:80,attribute:Dexterity), 6 | (multiplier:20,attribute:Willpower), 7 | ] 8 | ), 9 | hp_full_formula: ( 10 | scale:7168, 11 | multipliers:[ 12 | (multiplier:70,attribute:Toughness), 13 | (multiplier:20,attribute:Strength), 14 | (multiplier:10,attribute:Willpower), 15 | ] 16 | ), 17 | hp_regen_increment_formula: ( 18 | scale:1024, 19 | multipliers:[ 20 | (multiplier:80,attribute:Toughness), 21 | (multiplier:10,attribute:Strength), 22 | (multiplier:10,attribute:Willpower), 23 | ] 24 | ), 25 | ) -------------------------------------------------------------------------------- /assets/fonts/pixeled.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/fonts/pixeled.ttf -------------------------------------------------------------------------------- /assets/inventory_themes/basic.inventorytheme.ron: -------------------------------------------------------------------------------- 1 | ( 2 | slot: "sprites/gui/inventory/slot.png", 3 | head_wear: "sprites/gui/inventory/head_wear.png", 4 | body_wear: "sprites/gui/inventory/body_wear.png", 5 | main_hand_gear: "sprites/gui/inventory/main_hand_gear.png", 6 | off_hand_gear: "sprites/gui/inventory/off_hand_gear.png", 7 | finger_wear: "sprites/gui/inventory/finger_wear.png", 8 | neck_wear: "sprites/gui/inventory/neck_wear.png", 9 | feet_wear: "sprites/gui/inventory/feet_wear.png", 10 | ) -------------------------------------------------------------------------------- /assets/items/body/chainmail.item.ron: -------------------------------------------------------------------------------- 1 | Armor(( 2 | render: ( 3 | name: "Chainmail", 4 | texture_path: "sprites/item/chain_mail_1.png", 5 | texture_equiped_path: Some("sprites/item_equiped/chainmail.png"), 6 | ), 7 | defense: ( 8 | protection: Some(( 9 | amounts: [ 10 | (kind:Blunt,amount_multiplier:(scale:100,multipliers:[]),amount:6), 11 | (kind:Pierce,amount_multiplier:(scale:100,multipliers:[]),amount:5), 12 | (kind:Slash,amount_multiplier:(scale:100,multipliers:[]),amount:8), 13 | ], 14 | )), 15 | resistance: Some(( 16 | amounts: [ 17 | (kind:Blunt,percent:4), 18 | (kind:Pierce,percent:3), 19 | (kind:Slash,percent:6), 20 | ], 21 | )), 22 | ), 23 | enchantment: ( 24 | attributes: None, 25 | ), 26 | )) -------------------------------------------------------------------------------- /assets/items/body/ringmail.item.ron: -------------------------------------------------------------------------------- 1 | Armor(( 2 | render: ( 3 | name: "Ringmail", 4 | texture_path: "sprites/item/ring_mail_1.png", 5 | texture_equiped_path: Some("sprites/item_equiped/ringmail.png"), 6 | ), 7 | defense: ( 8 | protection: Some(( 9 | amounts: [ 10 | (kind:Blunt,amount_multiplier:(scale:100,multipliers:[]),amount:5), 11 | (kind:Pierce,amount_multiplier:(scale:100,multipliers:[]),amount:3), 12 | (kind:Slash,amount_multiplier:(scale:100,multipliers:[]),amount:7), 13 | ], 14 | )), 15 | resistance: Some(( 16 | amounts: [ 17 | (kind:Blunt,percent:3), 18 | (kind:Pierce,percent:2), 19 | (kind:Slash,percent:5), 20 | ], 21 | )), 22 | ), 23 | enchantment: ( 24 | attributes: None, 25 | ), 26 | )) -------------------------------------------------------------------------------- /assets/items/body/robe.item.ron: -------------------------------------------------------------------------------- 1 | Armor(( 2 | render: ( 3 | name: "Robe", 4 | texture_path: "sprites/item/robe_1_new.png", 5 | texture_equiped_path: Some("sprites/item_equiped/robe_brown_2.png"), 6 | ), 7 | defense: ( 8 | protection: Some(( 9 | amounts: [ 10 | (kind:Blunt,amount_multiplier:(scale:100,multipliers:[]),amount:1), 11 | (kind:Pierce,amount_multiplier:(scale:100,multipliers:[]),amount:1), 12 | (kind:Slash,amount_multiplier:(scale:100,multipliers:[]),amount:1), 13 | ], 14 | )), 15 | resistance: Some(( 16 | amounts: [ 17 | (kind:Blunt,percent:1), 18 | (kind:Pierce,percent:1), 19 | (kind:Slash,percent:1), 20 | ], 21 | )), 22 | ), 23 | enchantment: ( 24 | attributes: None, 25 | ), 26 | )) -------------------------------------------------------------------------------- /assets/items/body/scalemail.item.ron: -------------------------------------------------------------------------------- 1 | Armor(( 2 | render: ( 3 | name: "Scalemail", 4 | texture_path: "sprites/item/scale_mail_1_new.png", 5 | texture_equiped_path: Some("sprites/item_equiped/scalemail.png"), 6 | ), 7 | defense: ( 8 | protection: Some(( 9 | amounts: [ 10 | (kind:Blunt,amount_multiplier:(scale:100,multipliers:[]),amount:8), 11 | (kind:Pierce,amount_multiplier:(scale:100,multipliers:[]),amount:8), 12 | (kind:Slash,amount_multiplier:(scale:100,multipliers:[]),amount:8), 13 | ], 14 | )), 15 | resistance: Some(( 16 | amounts: [ 17 | (kind:Blunt,percent:6), 18 | (kind:Pierce,percent:6), 19 | (kind:Slash,percent:6), 20 | ], 21 | )), 22 | ), 23 | enchantment: ( 24 | attributes: None, 25 | ), 26 | )) -------------------------------------------------------------------------------- /assets/items/feet/boots_green.item.ron: -------------------------------------------------------------------------------- 1 | Boots(( 2 | render: ( 3 | name: "Nimble boots", 4 | texture_path: "sprites/item/boots_4_green.png", 5 | texture_equiped_path: Some("sprites/item_equiped/middle_green.png"), 6 | ), 7 | defense: ( 8 | protection: Some(( 9 | amounts: [ 10 | (kind:Blunt,amount_multiplier:(scale:100,multipliers:[]),amount:1), 11 | (kind:Pierce,amount_multiplier:(scale:100,multipliers:[]),amount:1), 12 | (kind:Slash,amount_multiplier:(scale:100,multipliers:[]),amount:1), 13 | ], 14 | )), 15 | resistance: Some(( 16 | amounts: [ 17 | (kind:Blunt,percent:1), 18 | (kind:Pierce,percent:1), 19 | (kind:Slash,percent:1), 20 | ], 21 | )), 22 | ), 23 | enchantment: ( 24 | attributes: Some(( 25 | list: { 26 | Dexterity: 5, 27 | }, 28 | )), 29 | ), 30 | )) -------------------------------------------------------------------------------- /assets/items/finger/ring_precious.item.ron: -------------------------------------------------------------------------------- 1 | Ring(( 2 | render: ( 3 | name: "2 precious", 4 | texture_path: "sprites/item/gold_green.png", 5 | texture_equiped_path: None, 6 | ), 7 | defense: ( 8 | protection: Some(( 9 | amounts: [ 10 | (kind:Fire,amount_multiplier:(scale:100,multipliers:[]),amount:3), 11 | (kind:Lightning,amount_multiplier:(scale:100,multipliers:[]),amount:3), 12 | (kind:Cold,amount_multiplier:(scale:100,multipliers:[]),amount:3), 13 | ], 14 | )), 15 | resistance: Some(( 16 | amounts: [ 17 | (kind:Fire,percent:5), 18 | (kind:Cold,percent:5), 19 | (kind:Lightning,percent:5), 20 | ], 21 | )), 22 | ), 23 | enchantment: ( 24 | attributes: Some(( 25 | list: { 26 | Toughness: 2, 27 | Willpower: 2, 28 | Strength: 2, 29 | Dexterity: 2, 30 | Inteligence: 2, 31 | Perception: 2, 32 | }, 33 | )), 34 | ), 35 | )) -------------------------------------------------------------------------------- /assets/items/head/hat_wizard.item.ron: -------------------------------------------------------------------------------- 1 | Helm(( 2 | render: ( 3 | name: "Wizard hat", 4 | texture_path: "sprites/item/wizard_hat_1.png", 5 | texture_equiped_path: Some("sprites/item_equiped/wizard_white.png"), 6 | ), 7 | defense: ( 8 | protection: None, 9 | resistance: None, 10 | ), 11 | enchantment: ( 12 | attributes: Some(( 13 | list: { 14 | Willpower: 3, 15 | Inteligence: 6, 16 | }, 17 | )), 18 | ), 19 | )) -------------------------------------------------------------------------------- /assets/items/head/helmet.item.ron: -------------------------------------------------------------------------------- 1 | Helm(( 2 | render: ( 3 | name: "Helmet of toughness", 4 | texture_path: "sprites/item/helmet_1.png", 5 | texture_equiped_path: Some("sprites/item_equiped/helm_gimli.png"), 6 | ), 7 | defense: ( 8 | protection: Some(( 9 | amounts: [ 10 | (kind:Blunt,amount_multiplier:(scale:100,multipliers:[]),amount:3), 11 | (kind:Pierce,amount_multiplier:(scale:100,multipliers:[]),amount:3), 12 | (kind:Slash,amount_multiplier:(scale:100,multipliers:[]),amount:3), 13 | ], 14 | )), 15 | resistance: None, 16 | ), 17 | enchantment: ( 18 | attributes: Some(( 19 | list: { 20 | Toughness: 6, 21 | }, 22 | )), 23 | ), 24 | )) -------------------------------------------------------------------------------- /assets/items/mainhand/club.item.ron: -------------------------------------------------------------------------------- 1 | Weapon(( 2 | render: ( 3 | name: "Club", 4 | texture_path: "sprites/item/club.png", 5 | texture_equiped_path: Some("sprites/item_equiped/club_slant.png"), 6 | ), 7 | damage: ( 8 | kind: Blunt, 9 | amount: ( 10 | start: 10, 11 | end: 14, 12 | ), 13 | amount_multiplier: ( 14 | scale: 100, 15 | multipliers: [ 16 | (multiplier:80,attribute:Strength), 17 | ], 18 | ), 19 | hit_cost: ( 20 | cost: 140, 21 | multiplier_inverted: ( 22 | scale: 100, 23 | multipliers: [(multiplier:80,attribute:Dexterity)], 24 | ), 25 | ), 26 | hit_chance: ( 27 | amount: 160, 28 | multiplier: ( 29 | scale: 100, 30 | multipliers: [(multiplier:128,attribute:Dexterity)], 31 | ), 32 | ), 33 | ), 34 | )) -------------------------------------------------------------------------------- /assets/items/mainhand/elven_broadsword.item.ron: -------------------------------------------------------------------------------- 1 | Weapon(( 2 | render: ( 3 | name: "Elven broadsword", 4 | texture_path: "sprites/item/elven_broadsword.png", 5 | texture_equiped_path: Some("sprites/item_equiped/heavy_sword.png"), 6 | ), 7 | damage: ( 8 | kind: Slash, 9 | amount: ( 10 | start: 12, 11 | end: 18, 12 | ), 13 | amount_multiplier: ( 14 | scale: 100, 15 | multipliers: [ 16 | (multiplier:60,attribute:Strength), 17 | (multiplier:20,attribute:Dexterity), 18 | ], 19 | ), 20 | hit_cost: ( 21 | cost: 180, 22 | multiplier_inverted: ( 23 | scale: 100, 24 | multipliers: [(multiplier:80,attribute:Dexterity)], 25 | ), 26 | ), 27 | hit_chance: ( 28 | amount: 140, 29 | multiplier: ( 30 | scale: 100, 31 | multipliers: [(multiplier:128,attribute:Dexterity)], 32 | ), 33 | ), 34 | ), 35 | )) -------------------------------------------------------------------------------- /assets/items/mainhand/orcish_dagger.item.ron: -------------------------------------------------------------------------------- 1 | Weapon(( 2 | render: ( 3 | name: "Orcish dagger", 4 | texture_path: "sprites/item/orcish_dagger.png", 5 | texture_equiped_path: Some("sprites/item_equiped/dagger_slant.png"), 6 | ), 7 | damage: ( 8 | kind: Slash, 9 | amount: ( 10 | start: 6, 11 | end: 12, 12 | ), 13 | amount_multiplier: ( 14 | scale: 100, 15 | multipliers: [ 16 | (multiplier:50,attribute:Strength), 17 | (multiplier:30,attribute:Dexterity), 18 | ], 19 | ), 20 | hit_cost: ( 21 | cost: 128, 22 | multiplier_inverted: ( 23 | scale: 100, 24 | multipliers: [(multiplier:80,attribute:Dexterity)], 25 | ), 26 | ), 27 | hit_chance: ( 28 | amount: 190, 29 | multiplier: ( 30 | scale: 100, 31 | multipliers: [(multiplier:128,attribute:Dexterity)], 32 | ), 33 | ), 34 | ), 35 | )) -------------------------------------------------------------------------------- /assets/items/mainhand/spear.item.ron: -------------------------------------------------------------------------------- 1 | Weapon(( 2 | render: ( 3 | name: "Balanced spear", 4 | texture_path: "sprites/item/spear.png", 5 | texture_equiped_path: Some("sprites/item_equiped/spear.png"), 6 | ), 7 | damage: ( 8 | kind: Pierce, 9 | amount: ( 10 | start: 11, 11 | end: 21, 12 | ), 13 | amount_multiplier: ( 14 | scale: 100, 15 | multipliers: [ 16 | (multiplier:60,attribute:Strength), 17 | (multiplier:20,attribute:Perception), 18 | ], 19 | ), 20 | hit_cost: ( 21 | cost: 160, 22 | multiplier_inverted: ( 23 | scale: 100, 24 | multipliers: [(multiplier:80,attribute:Dexterity)], 25 | ), 26 | ), 27 | hit_chance: ( 28 | amount: 80, 29 | multiplier: ( 30 | scale: 100, 31 | multipliers: [(multiplier:128,attribute:Dexterity)], 32 | ), 33 | ), 34 | ), 35 | )) -------------------------------------------------------------------------------- /assets/items/mainhand/staff.item.ron: -------------------------------------------------------------------------------- 1 | Weapon(( 2 | render: ( 3 | name: "Scorch staff", 4 | texture_path: "sprites/item/staff_8.png", 5 | texture_equiped_path: Some("sprites/item_equiped/staff_large.png"), 6 | ), 7 | damage: ( 8 | kind: Fire, 9 | amount: ( 10 | start: 12, 11 | end: 17, 12 | ), 13 | amount_multiplier: ( 14 | scale: 100, 15 | multipliers: [ 16 | (multiplier:50,attribute:Inteligence), 17 | (multiplier:40,attribute:Willpower), 18 | ], 19 | ), 20 | hit_cost: ( 21 | cost: 140, 22 | multiplier_inverted: ( 23 | scale: 100, 24 | multipliers: [ 25 | (multiplier:50,attribute:Inteligence), 26 | (multiplier:40,attribute:Willpower), 27 | ], 28 | ), 29 | ), 30 | hit_chance: ( 31 | amount: 96, 32 | multiplier: ( 33 | scale: 100, 34 | multipliers: [(multiplier:128,attribute:Willpower)], 35 | ), 36 | ), 37 | ), 38 | )) -------------------------------------------------------------------------------- /assets/items/neck/amulet_stone.item.ron: -------------------------------------------------------------------------------- 1 | Amulet(( 2 | render: ( 3 | name: "Stone will amulet", 4 | texture_path: "sprites/item/stone_3_blue.png", 5 | texture_equiped_path: None, 6 | ), 7 | defense: ( 8 | protection: Some(( 9 | amounts: [ 10 | (kind:Fire,amount_multiplier:(scale:100,multipliers:[]),amount:1), 11 | (kind:Lightning,amount_multiplier:(scale:100,multipliers:[]),amount:5), 12 | (kind:Cold,amount_multiplier:(scale:100,multipliers:[]),amount:6), 13 | ], 14 | )), 15 | resistance: Some(( 16 | amounts: [ 17 | (kind:Fire,percent:2), 18 | (kind:Cold,percent:10), 19 | (kind:Lightning,percent:12), 20 | ], 21 | )), 22 | ), 23 | enchantment: ( 24 | attributes: Some(( 25 | list: { 26 | Willpower: 5, 27 | }, 28 | )), 29 | ), 30 | )) -------------------------------------------------------------------------------- /assets/items/offhand/buckler.item.ron: -------------------------------------------------------------------------------- 1 | Shield(( 2 | render: ( 3 | name: "Buckler", 4 | texture_path: "sprites/item/buckler_1.png", 5 | texture_equiped_path: Some("sprites/item_equiped/buckler_round_3.png"), 6 | ), 7 | protection: ( 8 | amounts: [ 9 | ( 10 | kind: Blunt, 11 | amount_multiplier: (scale:100,multipliers:[]), 12 | amount: 2, 13 | ), 14 | ( 15 | kind: Pierce, 16 | amount_multiplier: (scale:100,multipliers:[]), 17 | amount: 2, 18 | ), 19 | ( 20 | kind: Slash, 21 | amount_multiplier: (scale:100,multipliers:[]), 22 | amount: 2, 23 | ), 24 | ], 25 | ), 26 | block: ( 27 | block_type: [ 28 | Blunt, 29 | Pierce, 30 | Slash, 31 | ], 32 | cost: ( 33 | cost: 45, 34 | multiplier_inverted: ( 35 | scale: 100, 36 | multipliers: [(multiplier:128,attribute:Dexterity)], 37 | ), 38 | ), 39 | chance: ( 40 | amount: 50, 41 | multiplier: ( 42 | scale: 100, 43 | multipliers: [(multiplier:100,attribute:Dexterity)], 44 | ), 45 | ), 46 | ), 47 | )) -------------------------------------------------------------------------------- /assets/items/offhand/shield_large.item.ron: -------------------------------------------------------------------------------- 1 | Shield(( 2 | render: ( 3 | name: "Large shield", 4 | texture_path: "sprites/item/large_shield_1_new.png", 5 | texture_equiped_path: Some("sprites/item_equiped/lshield_long_red.png"), 6 | ), 7 | protection: ( 8 | amounts: [ 9 | ( 10 | kind: Blunt, 11 | amount_multiplier: (scale:100,multipliers:[]), 12 | amount: 5, 13 | ), 14 | ( 15 | kind: Pierce, 16 | amount_multiplier: (scale:100,multipliers:[]), 17 | amount: 5, 18 | ), 19 | ( 20 | kind: Slash, 21 | amount_multiplier: (scale:100,multipliers:[]), 22 | amount: 5, 23 | ), 24 | ], 25 | ), 26 | block: ( 27 | block_type: [ 28 | Blunt, 29 | Pierce, 30 | Slash, 31 | ], 32 | cost: ( 33 | cost: 75, 34 | multiplier_inverted: ( 35 | scale: 100, 36 | multipliers: [(multiplier:128,attribute:Dexterity)], 37 | ), 38 | ), 39 | chance: ( 40 | amount: 75, 41 | multiplier: ( 42 | scale: 100, 43 | multipliers: [(multiplier:100,attribute:Dexterity)], 44 | ), 45 | ), 46 | ), 47 | )) -------------------------------------------------------------------------------- /assets/map_themes/cobalt_mosaic.maptheme.ron: -------------------------------------------------------------------------------- 1 | ( 2 | floor: [ 3 | "sprites/floor/mosaic_0.png", 4 | "sprites/floor/mosaic_1.png", 5 | "sprites/floor/mosaic_2.png", 6 | "sprites/floor/mosaic_3.png", 7 | "sprites/floor/mosaic_4.png", 8 | "sprites/floor/mosaic_5.png", 9 | "sprites/floor/mosaic_6.png", 10 | "sprites/floor/mosaic_7.png", 11 | "sprites/floor/mosaic_8.png", 12 | "sprites/floor/mosaic_9.png", 13 | "sprites/floor/mosaic_10.png", 14 | "sprites/floor/mosaic_11.png", 15 | "sprites/floor/mosaic_12.png", 16 | "sprites/floor/mosaic_13.png", 17 | "sprites/floor/mosaic_14.png", 18 | "sprites/floor/mosaic_15.png", 19 | ], 20 | wall: [ 21 | "sprites/walls/cobalt_rock_1.png", 22 | "sprites/walls/cobalt_rock_2.png", 23 | "sprites/walls/cobalt_rock_3.png", 24 | "sprites/walls/cobalt_rock_4.png", 25 | "sprites/walls/cobalt_stone_1.png", 26 | "sprites/walls/cobalt_stone_2.png", 27 | "sprites/walls/cobalt_stone_3.png", 28 | "sprites/walls/cobalt_stone_4.png", 29 | "sprites/walls/cobalt_stone_5.png", 30 | "sprites/walls/cobalt_stone_6.png", 31 | "sprites/walls/cobalt_stone_7.png", 32 | "sprites/walls/cobalt_stone_8.png", 33 | "sprites/walls/cobalt_stone_9.png", 34 | "sprites/walls/cobalt_stone_10.png", 35 | "sprites/walls/cobalt_stone_11.png", 36 | "sprites/walls/cobalt_stone_12.png", 37 | ], 38 | ) -------------------------------------------------------------------------------- /assets/map_themes/pebble_brick.maptheme.ron: -------------------------------------------------------------------------------- 1 | ( 2 | floor: [ 3 | "sprites/floor/pebble_brown_0.png", 4 | "sprites/floor/pebble_brown_1.png", 5 | "sprites/floor/pebble_brown_2.png", 6 | "sprites/floor/pebble_brown_3.png", 7 | "sprites/floor/pebble_brown_4.png", 8 | "sprites/floor/pebble_brown_5.png", 9 | "sprites/floor/pebble_brown_6.png", 10 | "sprites/floor/pebble_brown_7.png", 11 | "sprites/floor/pebble_brown_8.png", 12 | ], 13 | wall: [ 14 | "sprites/walls/brick_brown_0.png", 15 | "sprites/walls/brick_brown_1.png", 16 | "sprites/walls/brick_brown_2.png", 17 | "sprites/walls/brick_brown_3.png", 18 | "sprites/walls/brick_brown_4.png", 19 | "sprites/walls/brick_brown_5.png", 20 | "sprites/walls/brick_brown_6.png", 21 | "sprites/walls/brick_brown_7.png", 22 | ], 23 | ) -------------------------------------------------------------------------------- /assets/map_themes/snake.maptheme.ron: -------------------------------------------------------------------------------- 1 | ( 2 | floor: [ 3 | // "sprites/floor/snake_0.png", 4 | // "sprites/floor/snake_1.png", 5 | // "sprites/floor/snake_2.png", 6 | // "sprites/floor/snake_3.png", 7 | "sprites/floor/snake-a_0.png", 8 | "sprites/floor/snake-a_1.png", 9 | "sprites/floor/snake-a_2.png", 10 | "sprites/floor/snake-a_3.png", 11 | "sprites/floor/snake-c_0.png", 12 | "sprites/floor/snake-c_1.png", 13 | "sprites/floor/snake-c_2.png", 14 | "sprites/floor/snake-c_3.png", 15 | "sprites/floor/snake-d_0.png", 16 | "sprites/floor/snake-d_1.png", 17 | "sprites/floor/snake-d_2.png", 18 | "sprites/floor/snake-d_3.png", 19 | 20 | ], 21 | wall: [ 22 | "sprites/walls/snake_0.png", 23 | "sprites/walls/snake_1.png", 24 | "sprites/walls/snake_2.png", 25 | "sprites/walls/snake_3.png", 26 | "sprites/walls/snake_4.png", 27 | "sprites/walls/snake_5.png", 28 | "sprites/walls/snake_6.png", 29 | "sprites/walls/snake_7.png", 30 | "sprites/walls/snake_8.png", 31 | "sprites/walls/snake_9.png", 32 | ], 33 | ) -------------------------------------------------------------------------------- /assets/sprites/actors/cyclops.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/actors/cyclops.png -------------------------------------------------------------------------------- /assets/sprites/actors/ettin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/actors/ettin.png -------------------------------------------------------------------------------- /assets/sprites/actors/frost_giant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/actors/frost_giant.png -------------------------------------------------------------------------------- /assets/sprites/actors/giant_amoeba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/actors/giant_amoeba.png -------------------------------------------------------------------------------- /assets/sprites/actors/gnoll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/actors/gnoll.png -------------------------------------------------------------------------------- /assets/sprites/actors/goblin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/actors/goblin.png -------------------------------------------------------------------------------- /assets/sprites/actors/hobgoblin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/actors/hobgoblin.png -------------------------------------------------------------------------------- /assets/sprites/actors/human_male.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/actors/human_male.png -------------------------------------------------------------------------------- /assets/sprites/actors/jacket_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/actors/jacket_2.png -------------------------------------------------------------------------------- /assets/sprites/actors/kobold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/actors/kobold.png -------------------------------------------------------------------------------- /assets/sprites/actors/orc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/actors/orc.png -------------------------------------------------------------------------------- /assets/sprites/actors/pants_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/actors/pants_black.png -------------------------------------------------------------------------------- /assets/sprites/actors/stone_giant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/actors/stone_giant.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_0.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_1.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_10.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_11.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_12.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_13.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_14.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_15.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_2.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_3.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_4.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_5.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_6.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_7.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_8.png -------------------------------------------------------------------------------- /assets/sprites/floor/mosaic_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/mosaic_9.png -------------------------------------------------------------------------------- /assets/sprites/floor/pebble_brown_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/pebble_brown_0.png -------------------------------------------------------------------------------- /assets/sprites/floor/pebble_brown_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/pebble_brown_1.png -------------------------------------------------------------------------------- /assets/sprites/floor/pebble_brown_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/pebble_brown_2.png -------------------------------------------------------------------------------- /assets/sprites/floor/pebble_brown_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/pebble_brown_3.png -------------------------------------------------------------------------------- /assets/sprites/floor/pebble_brown_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/pebble_brown_4.png -------------------------------------------------------------------------------- /assets/sprites/floor/pebble_brown_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/pebble_brown_5.png -------------------------------------------------------------------------------- /assets/sprites/floor/pebble_brown_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/pebble_brown_6.png -------------------------------------------------------------------------------- /assets/sprites/floor/pebble_brown_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/pebble_brown_7.png -------------------------------------------------------------------------------- /assets/sprites/floor/pebble_brown_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/pebble_brown_8.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake-a_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake-a_0.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake-a_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake-a_1.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake-a_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake-a_2.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake-a_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake-a_3.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake-c_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake-c_0.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake-c_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake-c_1.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake-c_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake-c_2.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake-c_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake-c_3.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake-d_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake-d_0.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake-d_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake-d_1.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake-d_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake-d_2.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake-d_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake-d_3.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake_0.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake_1.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake_2.png -------------------------------------------------------------------------------- /assets/sprites/floor/snake_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/floor/snake_3.png -------------------------------------------------------------------------------- /assets/sprites/gui/inventory/body_wear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/gui/inventory/body_wear.png -------------------------------------------------------------------------------- /assets/sprites/gui/inventory/feet_wear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/gui/inventory/feet_wear.png -------------------------------------------------------------------------------- /assets/sprites/gui/inventory/finger_wear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/gui/inventory/finger_wear.png -------------------------------------------------------------------------------- /assets/sprites/gui/inventory/head_wear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/gui/inventory/head_wear.png -------------------------------------------------------------------------------- /assets/sprites/gui/inventory/main_hand_gear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/gui/inventory/main_hand_gear.png -------------------------------------------------------------------------------- /assets/sprites/gui/inventory/neck_wear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/gui/inventory/neck_wear.png -------------------------------------------------------------------------------- /assets/sprites/gui/inventory/off_hand_gear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/gui/inventory/off_hand_gear.png -------------------------------------------------------------------------------- /assets/sprites/gui/inventory/potion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/gui/inventory/potion.png -------------------------------------------------------------------------------- /assets/sprites/gui/inventory/scroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/gui/inventory/scroll.png -------------------------------------------------------------------------------- /assets/sprites/gui/inventory/slot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/gui/inventory/slot.png -------------------------------------------------------------------------------- /assets/sprites/gui/tooltip/cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/gui/tooltip/cursor.png -------------------------------------------------------------------------------- /assets/sprites/gui/tooltip/cursor_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/gui/tooltip/cursor_green.png -------------------------------------------------------------------------------- /assets/sprites/gui/tooltip/cursor_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/gui/tooltip/cursor_red.png -------------------------------------------------------------------------------- /assets/sprites/item/boots_4_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/boots_4_green.png -------------------------------------------------------------------------------- /assets/sprites/item/buckler_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/buckler_1.png -------------------------------------------------------------------------------- /assets/sprites/item/chain_mail_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/chain_mail_1.png -------------------------------------------------------------------------------- /assets/sprites/item/club.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/club.png -------------------------------------------------------------------------------- /assets/sprites/item/elven_broadsword.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/elven_broadsword.png -------------------------------------------------------------------------------- /assets/sprites/item/gold_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/gold_green.png -------------------------------------------------------------------------------- /assets/sprites/item/helmet_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/helmet_1.png -------------------------------------------------------------------------------- /assets/sprites/item/large_shield_1_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/large_shield_1_new.png -------------------------------------------------------------------------------- /assets/sprites/item/orcish_dagger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/orcish_dagger.png -------------------------------------------------------------------------------- /assets/sprites/item/ring_mail_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/ring_mail_1.png -------------------------------------------------------------------------------- /assets/sprites/item/robe_1_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/robe_1_new.png -------------------------------------------------------------------------------- /assets/sprites/item/scale_mail_1_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/scale_mail_1_new.png -------------------------------------------------------------------------------- /assets/sprites/item/spear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/spear.png -------------------------------------------------------------------------------- /assets/sprites/item/staff_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/staff_8.png -------------------------------------------------------------------------------- /assets/sprites/item/stone_3_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/stone_3_blue.png -------------------------------------------------------------------------------- /assets/sprites/item/two_handed_sword.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/two_handed_sword.png -------------------------------------------------------------------------------- /assets/sprites/item/wizard_hat_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item/wizard_hat_1.png -------------------------------------------------------------------------------- /assets/sprites/item_equiped/buckler_round_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item_equiped/buckler_round_3.png -------------------------------------------------------------------------------- /assets/sprites/item_equiped/chainmail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item_equiped/chainmail.png -------------------------------------------------------------------------------- /assets/sprites/item_equiped/club_slant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item_equiped/club_slant.png -------------------------------------------------------------------------------- /assets/sprites/item_equiped/dagger_slant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item_equiped/dagger_slant.png -------------------------------------------------------------------------------- /assets/sprites/item_equiped/heavy_sword.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item_equiped/heavy_sword.png -------------------------------------------------------------------------------- /assets/sprites/item_equiped/helm_gimli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item_equiped/helm_gimli.png -------------------------------------------------------------------------------- /assets/sprites/item_equiped/lshield_long_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item_equiped/lshield_long_red.png -------------------------------------------------------------------------------- /assets/sprites/item_equiped/middle_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item_equiped/middle_green.png -------------------------------------------------------------------------------- /assets/sprites/item_equiped/ringmail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item_equiped/ringmail.png -------------------------------------------------------------------------------- /assets/sprites/item_equiped/robe_brown_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item_equiped/robe_brown_2.png -------------------------------------------------------------------------------- /assets/sprites/item_equiped/scalemail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item_equiped/scalemail.png -------------------------------------------------------------------------------- /assets/sprites/item_equiped/spear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item_equiped/spear.png -------------------------------------------------------------------------------- /assets/sprites/item_equiped/staff_large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item_equiped/staff_large.png -------------------------------------------------------------------------------- /assets/sprites/item_equiped/wizard_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/item_equiped/wizard_white.png -------------------------------------------------------------------------------- /assets/sprites/walls/brick_brown_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/brick_brown_0.png -------------------------------------------------------------------------------- /assets/sprites/walls/brick_brown_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/brick_brown_1.png -------------------------------------------------------------------------------- /assets/sprites/walls/brick_brown_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/brick_brown_2.png -------------------------------------------------------------------------------- /assets/sprites/walls/brick_brown_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/brick_brown_3.png -------------------------------------------------------------------------------- /assets/sprites/walls/brick_brown_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/brick_brown_4.png -------------------------------------------------------------------------------- /assets/sprites/walls/brick_brown_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/brick_brown_5.png -------------------------------------------------------------------------------- /assets/sprites/walls/brick_brown_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/brick_brown_6.png -------------------------------------------------------------------------------- /assets/sprites/walls/brick_brown_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/brick_brown_7.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_rock_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_rock_1.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_rock_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_rock_2.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_rock_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_rock_3.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_rock_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_rock_4.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_stone_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_stone_1.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_stone_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_stone_10.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_stone_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_stone_11.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_stone_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_stone_12.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_stone_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_stone_2.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_stone_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_stone_3.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_stone_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_stone_4.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_stone_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_stone_5.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_stone_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_stone_6.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_stone_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_stone_7.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_stone_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_stone_8.png -------------------------------------------------------------------------------- /assets/sprites/walls/cobalt_stone_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/cobalt_stone_9.png -------------------------------------------------------------------------------- /assets/sprites/walls/snake_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/snake_0.png -------------------------------------------------------------------------------- /assets/sprites/walls/snake_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/snake_1.png -------------------------------------------------------------------------------- /assets/sprites/walls/snake_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/snake_2.png -------------------------------------------------------------------------------- /assets/sprites/walls/snake_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/snake_3.png -------------------------------------------------------------------------------- /assets/sprites/walls/snake_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/snake_4.png -------------------------------------------------------------------------------- /assets/sprites/walls/snake_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/snake_5.png -------------------------------------------------------------------------------- /assets/sprites/walls/snake_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/snake_6.png -------------------------------------------------------------------------------- /assets/sprites/walls/snake_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/snake_7.png -------------------------------------------------------------------------------- /assets/sprites/walls/snake_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/snake_8.png -------------------------------------------------------------------------------- /assets/sprites/walls/snake_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/assets/sprites/walls/snake_9.png -------------------------------------------------------------------------------- /bevy_inventory/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bevy_inventory" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | [dependencies.bevy] 11 | version = "~0.9" 12 | default-features = false 13 | features = ["render"] 14 | -------------------------------------------------------------------------------- /bevy_inventory/src/events.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | #[derive(Debug, Copy, Clone)] 4 | pub struct ItemPickUpEvent { 5 | pub picker: Entity, 6 | } 7 | 8 | #[derive(Debug, Copy, Clone)] 9 | pub struct ItemDropEvent { 10 | pub droper: Entity, 11 | pub item: Entity, 12 | } 13 | -------------------------------------------------------------------------------- /bevy_inventory/src/lib.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy::utils::HashMap; 3 | use std::{ 4 | fmt::Debug, 5 | hash::Hash, 6 | ops::{Index, IndexMut}, 7 | }; 8 | 9 | pub use events::*; 10 | 11 | mod events; 12 | 13 | pub trait ItemType: Component + Copy + Clone + Eq + Hash + Debug + Default {} 14 | 15 | #[derive(Debug, Default, Clone, Component)] 16 | pub struct Equipment { 17 | pub items: HashMap<(I, u8), Option>, 18 | } 19 | 20 | impl Equipment { 21 | pub fn list(&self, t_items: &Query<&T, (With, Without)>) -> Vec 22 | where 23 | T: Component + Clone, 24 | V: Component, 25 | { 26 | self.iter_some() 27 | .filter_map(|(_, e)| { 28 | if let Ok(t) = t_items.get(e) { 29 | Some(t.clone()) 30 | } else { 31 | None 32 | } 33 | }) 34 | .collect() 35 | } 36 | pub fn add(&mut self, item: Entity, item_type: &I) -> bool { 37 | if let Some((_, item_slot)) = self 38 | .items 39 | .iter_mut() 40 | .find(|((t, _), b)| t == item_type && b.is_none()) 41 | { 42 | *item_slot = Some(item); 43 | true 44 | } else { 45 | false 46 | } 47 | } 48 | // TODO: implement replace add item and return existing one 49 | 50 | pub fn take(&mut self, item: Entity) -> bool { 51 | if let Some((_, e)) = self 52 | .items 53 | .iter_mut() 54 | .find(|(_, b)| b.is_some() && b.unwrap() == item) 55 | { 56 | *e = None; 57 | true 58 | } else { 59 | false 60 | } 61 | } 62 | 63 | pub fn iter_some(&'_ self) -> impl Iterator + '_ { 64 | // TODO: use filter_map instead 65 | self.items 66 | .iter() 67 | .filter(|(_, i)| i.is_some()) 68 | .map(move |(a, i)| (*a, i.unwrap())) 69 | } 70 | } 71 | 72 | impl Index<(I, u8)> for Equipment { 73 | type Output = Option; 74 | 75 | fn index(&self, index: (I, u8)) -> &Self::Output { 76 | if let Some(item) = self.items.get(&index) { 77 | return item; 78 | } 79 | &None 80 | } 81 | } 82 | 83 | impl IndexMut<(I, u8)> for Equipment { 84 | fn index_mut(&mut self, index: (I, u8)) -> &mut Self::Output { 85 | if let Some(ee) = self.items.get_mut(&index) { 86 | return ee; 87 | } 88 | panic!("No item with index {:?}", index); 89 | } 90 | } 91 | 92 | #[derive(Debug, Clone, Component)] 93 | pub struct Inventory { 94 | items: Vec>, 95 | } 96 | 97 | impl Default for Inventory { 98 | fn default() -> Self { 99 | Self::with_capacity(Inventory::DEFAULT_CAPACITY) 100 | } 101 | } 102 | 103 | impl Inventory { 104 | pub const DEFAULT_CAPACITY: usize = 32; 105 | 106 | pub fn with_capacity(cap: usize) -> Self { 107 | Self { 108 | items: vec![None; cap], 109 | } 110 | } 111 | 112 | pub fn add(&mut self, item: Entity) -> bool { 113 | if let Some((_, e)) = self.items.iter_mut().enumerate().find(|(_, b)| b.is_none()) { 114 | *e = Some(item); 115 | true 116 | } else { 117 | false 118 | } 119 | } 120 | 121 | pub fn take(&mut self, item: Entity) -> bool { 122 | if let Some((_, e)) = self 123 | .items 124 | .iter_mut() 125 | .enumerate() 126 | .find(|(_, b)| b.is_some() && b.unwrap() == item) 127 | { 128 | *e = None; 129 | true 130 | } else { 131 | false 132 | } 133 | } 134 | pub fn iter_some(&self) -> impl Iterator + '_ { 135 | self.items.iter().filter_map(|i| *i) 136 | } 137 | 138 | pub fn is_full(&self) -> bool { 139 | self.items.iter().all(|i| i.is_some()) 140 | } 141 | 142 | pub fn len(&self) -> usize { 143 | self.items.len() 144 | } 145 | 146 | pub fn is_empty(&self) -> bool { 147 | self.len() == 0 148 | } 149 | } 150 | 151 | impl Index for Inventory { 152 | type Output = Option; 153 | 154 | fn index(&self, index: usize) -> &Self::Output { 155 | &self.items[index] 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /bevy_inventory_ui/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bevy_inventory_ui" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | serde = "~1.0" 10 | bevy_inventory = { path = "../bevy_inventory" } 11 | 12 | [dependencies.bevy] 13 | version = "~0.9" 14 | default-features = false 15 | features = ["render"] 16 | 17 | # Dependencies for WASM only 18 | [target.'cfg(target_arch = "wasm32")'.dependencies.getrandom] 19 | version = "0.2" 20 | features = ["js"] 21 | -------------------------------------------------------------------------------- /bevy_inventory_ui/src/assets.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | #[derive(Debug, Clone, Resource)] 4 | pub struct InventoryUiAssets { 5 | pub slot: Handle, 6 | pub hover_cursor_image: Handle, 7 | pub font: Handle, 8 | } 9 | -------------------------------------------------------------------------------- /bevy_inventory_ui/src/draggable_ui.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | #[derive(Default, Debug, Clone, Component)] 4 | pub struct DragableUI { 5 | is_started: bool, 6 | current_cursor_position: Vec2, 7 | last_ui_position: UiRect, 8 | last_cursor_position: Vec2, 9 | } 10 | 11 | pub(crate) fn ui_drag_interaction( 12 | mut cursor_moved_reader: EventReader, 13 | mut interactive_dragables: Query<(&Interaction, &Style, &mut DragableUI)>, 14 | ) { 15 | for mm in cursor_moved_reader.iter() { 16 | for (i, s, mut d) in interactive_dragables.iter_mut() { 17 | if d.is_started { 18 | if *i != Interaction::Clicked { 19 | d.is_started = false; 20 | d.last_ui_position = UiRect::default(); 21 | d.last_cursor_position = Vec2::ZERO; 22 | d.current_cursor_position = Vec2::ZERO; 23 | } 24 | } else if *i == Interaction::Clicked { 25 | d.is_started = true; 26 | d.last_ui_position = s.position; 27 | d.last_cursor_position = mm.position; 28 | } 29 | if d.is_started { 30 | d.current_cursor_position = mm.position; 31 | } 32 | } 33 | } 34 | } 35 | 36 | pub(crate) fn ui_apply_drag_pos(mut dragables: Query<(&mut Style, &DragableUI)>) { 37 | for (mut style, d) in dragables.iter_mut().filter(|(_, d)| d.is_started) { 38 | let delta = d.last_cursor_position - d.current_cursor_position; 39 | let top = if let Val::Px(i) = d.last_ui_position.top { 40 | i 41 | } else { 42 | 0. 43 | }; 44 | let right = if let Val::Px(i) = d.last_ui_position.right { 45 | i 46 | } else { 47 | 0. 48 | }; 49 | style.position.top = Val::Px(top + delta.y); 50 | style.position.right = Val::Px(right + delta.x); 51 | // update z? 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /bevy_inventory_ui/src/lib.rs: -------------------------------------------------------------------------------- 1 | use bevy::{ 2 | ecs::system::Resource, 3 | prelude::*, 4 | utils::{hashbrown::hash_map::Iter, HashMap}, 5 | }; 6 | use bevy_inventory::ItemType; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | pub use assets::InventoryUiAssets; 10 | pub use plugin::InventoryUiPlugin; 11 | 12 | mod assets; 13 | mod draggable_ui; 14 | mod plugin; 15 | mod systems; 16 | 17 | /// Inventory display options. Must be used as a resource 18 | #[derive(Resource, Debug, Clone, Serialize, Deserialize)] 19 | pub struct InventoryDisplayOptions { 20 | /// size of item 21 | pub tile_size: f32, 22 | } 23 | 24 | /// equipment display locations in 128 height x 256 width canvas 25 | #[derive(Debug, Clone, Component, Serialize, Deserialize)] 26 | pub struct EquipmentDisplay { 27 | pub items: HashMap<(I, u8), Vec2>, 28 | } 29 | impl EquipmentDisplay { 30 | pub fn new(list: Vec<(I, u8, Vec2)>) -> Self { 31 | let mut items = HashMap::default(); 32 | for (t, i, r) in list { 33 | items.entry((t, i)).insert(r); 34 | } 35 | Self { items } 36 | } 37 | pub fn iter(&self) -> Iter<(I, u8), Vec2> { 38 | self.items.iter() 39 | } 40 | } 41 | impl Default for EquipmentDisplay { 42 | fn default() -> Self { 43 | EquipmentDisplay::new(vec![(I::default(), 0, Vec2::new(72., 58.))]) 44 | } 45 | } 46 | 47 | /// specifies the owner of the inventory and equipment UI 48 | #[derive(Debug, Clone, Component)] 49 | pub struct InventoryDisplayOwner { 50 | pub actor: Entity, 51 | } 52 | 53 | /// Specifies the node containing children of InventoryDisplaySlot 54 | #[derive(Debug, Clone, Component)] 55 | pub struct InventoryDisplayNode { 56 | /// Entity id of the actor having this inventory 57 | pub id: Entity, 58 | } 59 | /// Specifies the node containing children of EquipmentDisplaySlot 60 | #[derive(Debug, Clone, Component)] 61 | pub struct EquipmentDisplayNode { 62 | /// Entity id of the actor having this Equipment 63 | pub actor: Entity, 64 | } 65 | 66 | #[derive(Default, Debug, Clone, PartialEq, Eq, Component)] 67 | pub struct InventoryDisplaySlot { 68 | pub index: usize, 69 | pub item: Option, 70 | } 71 | 72 | #[derive(Default, Debug, Clone, Component)] 73 | pub struct EquipmentDisplaySlot { 74 | pub index: (I, u8), 75 | pub item: Option, 76 | pub is_dummy_rendered: bool, 77 | } 78 | 79 | /// specifies how to render stuff if it is placed in the inventory disply or equipment disply 80 | #[derive(Default, Debug, Clone, Component)] 81 | pub struct UiRenderInfo { 82 | pub image: UiImage, 83 | } 84 | 85 | #[derive(Debug, Copy, Clone)] 86 | pub struct InventoryDisplayToggleEvent { 87 | /// Entity ID of the actor wanting to toggle inventory display 88 | pub actor: Entity, 89 | } 90 | 91 | #[derive(Debug, Clone, Component)] 92 | pub(crate) struct UiHoverTip { 93 | pub(crate) hovered: bool, 94 | pub(crate) tooltip_shown: bool, 95 | /// could be any entity having UiTextInfo 96 | /// This entity will be used to place hover tip as a child entity 97 | tip_owner: Entity, 98 | } 99 | impl UiHoverTip { 100 | pub fn new(tip_owner: Entity) -> Self { 101 | Self { 102 | hovered: false, 103 | tooltip_shown: false, 104 | tip_owner, 105 | } 106 | } 107 | } 108 | #[derive(Debug,Default, Clone, Component)] 109 | pub(crate) struct WorldHoverTip { 110 | pub(crate) hovered: bool, 111 | pub(crate) tooltip_shown: bool, 112 | /// ui entity thet is actually displaying tip 113 | tip_entity: Option, 114 | } 115 | 116 | #[derive(Debug, Clone, Component)] 117 | pub struct UiTextInfo { 118 | pub name: String, 119 | pub titles_descriptions: Vec<(String, String)>, 120 | } 121 | 122 | // TODO: move to bevy_inventory lib 123 | #[derive(Debug, Clone, Component)] 124 | pub struct Equipable { 125 | actor: Entity, 126 | item: Entity, 127 | } 128 | // TODO: move to bevy_inventory lib 129 | #[derive(Debug, Clone, Component)] 130 | pub struct Unequipable { 131 | actor: Entity, 132 | item: Entity, 133 | } 134 | 135 | pub trait ItemTypeUiImage: Resource { 136 | fn get_image(&self, item_type: I) -> UiImage; 137 | } 138 | -------------------------------------------------------------------------------- /bevy_inventory_ui/src/plugin.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use crate::{ 4 | draggable_ui::{ui_apply_drag_pos, ui_drag_interaction}, 5 | systems::{ 6 | append_world_hovertip, equipment_update, inventory_update, toggle_inventory_open, 7 | ui_click_item_equip, ui_click_item_unequip, ui_hovertip_interaction, 8 | world_hovertip_interaction, 9 | }, 10 | InventoryDisplayToggleEvent, ItemTypeUiImage, 11 | }; 12 | use bevy::{ecs::schedule::StateData, prelude::*}; 13 | use bevy_inventory::{ItemDropEvent, ItemPickUpEvent, ItemType}; 14 | 15 | pub struct InventoryUiPlugin> { 16 | pub state_running: S, 17 | pub phantom_1: PhantomData, 18 | pub phantom_2: PhantomData, 19 | } 20 | 21 | impl> Plugin for InventoryUiPlugin { 22 | fn build(&self, app: &mut App) { 23 | app.add_system_to_stage(CoreStage::First, ui_drag_interaction) 24 | .add_system_set( 25 | SystemSet::on_update(self.state_running.clone()) 26 | .label("inventory_ui") 27 | .with_system(ui_apply_drag_pos) 28 | .with_system(append_world_hovertip) 29 | .with_system(toggle_inventory_open::) 30 | .with_system( 31 | equipment_update:: 32 | .after(ui_click_item_equip::) 33 | .after(ui_click_item_unequip::), 34 | ) 35 | .with_system( 36 | inventory_update:: 37 | .after(ui_click_item_equip::) 38 | .after(ui_click_item_unequip::), 39 | ) 40 | .with_system(ui_hovertip_interaction::) 41 | .with_system(world_hovertip_interaction) 42 | .with_system(ui_click_item_equip::) 43 | .with_system(ui_click_item_unequip::), 44 | ) 45 | .add_event::() 46 | .add_event::() 47 | .add_event::(); 48 | 49 | bevy::log::info!("Loaded InventoryUiPlugin Plugin"); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bevy_roguelike_combat" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | serde = "~1.0" 10 | rand = "~0.8" 11 | strum = "~0.24" 12 | 13 | [dependencies.bevy] 14 | version = "~0.9" 15 | default-features = false 16 | features = ["render"] 17 | 18 | # Dependencies for WASM only 19 | [target.'cfg(target_arch = "wasm32")'.dependencies.getrandom] 20 | version = "0.2" 21 | features = ["js"] 22 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/components/action_cost.rs: -------------------------------------------------------------------------------- 1 | use super::{AttributeType, Attributes, LinearFormula}; 2 | use bevy::{prelude::*, reflect::FromReflect}; 3 | use serde::{Deserialize, Serialize}; 4 | use std::fmt::Debug; 5 | 6 | /// applies to action being performed 7 | #[derive( 8 | Debug, Default, Clone, PartialEq, Eq, Component, Reflect, FromReflect, Serialize, Deserialize, 9 | )] 10 | #[reflect(Component)] 11 | pub struct ActionCost { 12 | /// cost in action points, [`super::ActionPoints::TURN_READY_DEFAULT`] being one single turn 13 | pub cost: i16, 14 | /// formula to compute the multiplier. 15 | pub multiplier_inverted: LinearFormula, 16 | } 17 | impl ActionCost { 18 | pub fn compute(&self, attributes: &Attributes) -> i16 { 19 | (self.cost as f32 / self.multiplier_inverted.compute(attributes)) as i16 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/components/action_points.rs: -------------------------------------------------------------------------------- 1 | use super::{AttributeType, Attributes, LinearFormula}; 2 | use bevy::{prelude::*, reflect::FromReflect}; 3 | use std::{fmt::Debug, hash::Hash}; 4 | 5 | pub const AP_MOVE_COST_DEFAULT: i16 = 100; 6 | pub const AP_IDLE_COST_DEFAULT: i16 = 64; 7 | pub const AP_TURN_READY_DEFAULT: i16 = 128; 8 | pub const AP_INCREMENT_MIN: i16 = 64; 9 | 10 | #[derive(Default, Component, Reflect)] 11 | #[reflect(Component)] 12 | pub struct ActionPointsDirty; 13 | 14 | #[derive(Default, Debug, Clone, Eq, PartialEq, Hash, Component, Reflect, FromReflect)] 15 | #[reflect(Component)] 16 | pub struct ActionPoints { 17 | turn_ready: i16, 18 | current: i16, 19 | increment: i16, 20 | increment_formula: LinearFormula, 21 | } 22 | impl ActionPoints { 23 | pub fn new(increment_formula: LinearFormula, atr: &Attributes) -> Self { 24 | Self { 25 | turn_ready: AP_TURN_READY_DEFAULT, 26 | increment: AP_INCREMENT_MIN + increment_formula.compute(atr) as i16, 27 | increment_formula, 28 | current: 0, 29 | } 30 | } 31 | pub fn update(&mut self, atr: &Attributes) { 32 | self.turn_ready = AP_TURN_READY_DEFAULT; 33 | self.increment = AP_INCREMENT_MIN + self.increment_formula.compute(atr) as i16; 34 | } 35 | 36 | pub fn turn_ready_to_act(&self) -> i16 { 37 | self.turn_ready 38 | } 39 | pub fn current(&self) -> i16 { 40 | self.current 41 | } 42 | pub fn current_add(&mut self) -> i16 { 43 | self.current += self.increment; 44 | self.current 45 | } 46 | pub fn current_minus(&mut self, cost: i16) -> i16 { 47 | self.current -= cost; 48 | self.current 49 | } 50 | pub fn increment(&self) -> i16 { 51 | self.increment 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/components/attributes.rs: -------------------------------------------------------------------------------- 1 | use bevy::{ 2 | prelude::*, 3 | reflect::{FromReflect, GetTypeRegistration}, 4 | utils::HashMap, 5 | }; 6 | use serde::{Deserialize, Serialize}; 7 | use std::{fmt::Debug, fmt::Display, hash::Hash, iter::Sum, ops::Add}; 8 | use strum::IntoEnumIterator; 9 | 10 | pub trait AttributeType: 11 | Component 12 | + Clone 13 | + Eq 14 | + Hash 15 | + Display 16 | + Default 17 | + Reflect 18 | + FromReflect 19 | + GetTypeRegistration 20 | + IntoEnumIterator 21 | { 22 | } 23 | 24 | #[derive(Debug, Clone, Eq, PartialEq, Component, Reflect, FromReflect, Serialize, Deserialize)] 25 | #[reflect(Component)] 26 | pub struct Attributes { 27 | pub list: HashMap, 28 | } 29 | impl Attributes { 30 | pub fn get(&self, attribute_type: &A) -> u8 { 31 | *self.list.get(attribute_type).unwrap_or(&0) 32 | } 33 | pub fn with_all(value: u8) -> Self { 34 | Self { 35 | list: HashMap::from_iter(A::iter().map(|a| (a, value))), 36 | } 37 | } 38 | } 39 | impl Default for Attributes { 40 | fn default() -> Self { 41 | Self::with_all(0) 42 | } 43 | } 44 | 45 | impl Add for Attributes { 46 | type Output = Attributes; 47 | 48 | fn add(self, rhs: Self) -> Self::Output { 49 | Self { 50 | list: HashMap::from_iter(self.list.iter().map(|(t, v)| (t.clone(), *v + rhs.get(t)))), 51 | } 52 | } 53 | } 54 | impl Sum for Attributes { 55 | fn sum>(iter: I) -> Self { 56 | iter.fold(Attributes::default(), |acc, a| acc + a) 57 | } 58 | } 59 | impl Display for Attributes { 60 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 61 | write!( 62 | f, 63 | "{}", 64 | self.list 65 | .iter() 66 | .map(|(attribute_type, &amount)| format!("{} +{}", attribute_type, amount)) 67 | .fold("".to_string(), |acc, x| format!("{}, {}", x, acc)) 68 | ) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/components/block.rs: -------------------------------------------------------------------------------- 1 | use super::{ActionCost, AttributeType, Attributes, Damage, DamageKind, Rate}; 2 | use bevy::{prelude::*, reflect::FromReflect}; 3 | use rand::prelude::*; 4 | use serde::{Deserialize, Serialize}; 5 | use std::fmt::Debug; 6 | 7 | /// Block works on specified damage types. Works together with [Rate]. 8 | #[derive( 9 | Debug, Default, Clone, PartialEq, Eq, Component, Reflect, FromReflect, Serialize, Deserialize, 10 | )] 11 | #[reflect(Component)] 12 | pub struct Block { 13 | // blocks specific damage type? 14 | pub block_type: Vec, 15 | pub cost: ActionCost, 16 | /// Block chance. Compared against [`Damage::hit_rate`]. 17 | pub chance: Rate, 18 | } 19 | 20 | impl Block { 21 | /// will try to block damage when block type matches. returns true and cost if blocked. if not returns false and zero. 22 | pub fn try_block( 23 | &self, 24 | damage: &Damage, 25 | self_attributes: &Attributes, 26 | attacker_attributes: &Attributes, 27 | rng: &mut StdRng, 28 | ) -> (bool, i16) { 29 | if !self.block_type.iter().any(|k| *k == damage.kind) { 30 | return (false, 0); 31 | } 32 | let rate_block = self.chance.compute(self_attributes); 33 | let rate_hit = damage.hit_chance.compute(attacker_attributes); 34 | let blocked = rng.gen_ratio(rate_block.min(rate_hit) as u32, rate_hit as u32); 35 | bevy::log::trace!( 36 | "block rate {}, hit rate {}, blocked {}", 37 | rate_block, 38 | rate_hit, 39 | blocked 40 | ); 41 | let cost = if blocked { 42 | self.cost.compute(self_attributes) 43 | } else { 44 | 0 45 | }; 46 | (blocked, cost) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/components/bundles.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::forget_non_drop)] // https://github.com/bevyengine/bevy/issues/4601 2 | use super::*; 3 | use bevy::prelude::*; 4 | 5 | #[derive(Bundle)] 6 | pub struct Combat { 7 | attributes: Attributes, 8 | ap: ActionPoints, 9 | hp: HitPoints, 10 | damage: DamageList, 11 | protection: Protection, 12 | evasion: Evasion, 13 | resistance: Resistance, 14 | stats: StatsComputed, 15 | stats_dirty: StatsComputedDirty, 16 | } 17 | 18 | #[allow(clippy::too_many_arguments)] 19 | impl Combat { 20 | pub fn new( 21 | attributes: &Attributes, 22 | ap_increment_formula: LinearFormula, 23 | hp_full_formula: LinearFormula, 24 | hp_regen_increment_formula: LinearFormula, 25 | damage: DamageList, 26 | protection: Protection, 27 | evasion: Evasion, 28 | resistance: Resistance, 29 | ) -> Self { 30 | Self { 31 | attributes: attributes.clone(), 32 | ap: ActionPoints::new(ap_increment_formula, attributes), 33 | hp: HitPoints::new(hp_full_formula, hp_regen_increment_formula, attributes), 34 | damage, 35 | protection, 36 | evasion, 37 | resistance, 38 | stats: StatsComputed::default(), 39 | stats_dirty: StatsComputedDirty {}, 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/components/damage.rs: -------------------------------------------------------------------------------- 1 | use super::{ActionCost, AttributeType, Attributes, LinearFormula, Rate}; 2 | use bevy::{ 3 | prelude::*, 4 | reflect::{FromReflect, GetTypeRegistration}, 5 | }; 6 | use rand::prelude::*; 7 | use serde::{Deserialize, Serialize}; 8 | use std::{fmt::Debug, fmt::Display, hash::Hash, ops::Range}; 9 | 10 | /// Type of damage that can be inflicted by actors of the environment. 11 | pub trait DamageKind: 12 | Component + Clone + Eq + Hash + Display + Default + Reflect + FromReflect + GetTypeRegistration 13 | { 14 | } 15 | 16 | #[derive( 17 | Debug, Default, Clone, PartialEq, Eq, Component, Reflect, FromReflect, Serialize, Deserialize, 18 | )] 19 | #[reflect(Component)] 20 | pub struct DamageList { 21 | pub list: Vec>, 22 | } 23 | 24 | /// Information about damage that can be calculated based on actor attributes. 25 | #[derive( 26 | Debug, Default, Clone, PartialEq, Eq, Component, Reflect, FromReflect, Serialize, Deserialize, 27 | )] 28 | #[reflect(Component)] 29 | pub struct Damage { 30 | pub kind: K, 31 | pub amount: Range, 32 | pub amount_multiplier: LinearFormula, 33 | pub hit_cost: ActionCost, 34 | pub hit_chance: Rate, 35 | } 36 | 37 | impl Damage { 38 | pub fn compute(&self, attributes: &Attributes, rng: &mut StdRng) -> i32 { 39 | (self.amount_roll(rng) as f32 * self.amount_multiplier.compute(attributes)) as i32 40 | } 41 | fn amount_roll(&self, rng: &mut StdRng) -> i32 { 42 | if !self.amount.is_empty() { 43 | rng.gen_range(self.amount.clone()) 44 | } else { 45 | self.amount.start 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/components/evasion.rs: -------------------------------------------------------------------------------- 1 | use super::{ActionCost, AttributeType, Attributes, Damage, DamageKind, Rate}; 2 | use bevy::{prelude::*, reflect::FromReflect}; 3 | use rand::prelude::*; 4 | use serde::{Deserialize, Serialize}; 5 | use std::fmt::Debug; 6 | 7 | /// Evasion works on any damage type. 8 | #[derive( 9 | Debug, Default, Clone, PartialEq, Eq, Component, Reflect, FromReflect, Serialize, Deserialize, 10 | )] 11 | #[reflect(Component)] 12 | pub struct Evasion { 13 | /// Cost in action points, [`super::ActionPoints::TURN_READY_DEFAULT`] being one single turn. 14 | /// Usually it should be close to 0.3 of the turn (posibility to avoid 3 attacks per turn). 15 | /// Usually dexterity should influence it 16 | pub cost: ActionCost, 17 | /// Evasion chance. Compared against [`Damage::hit_rate`]. 18 | pub chance: Rate, 19 | } 20 | impl Evasion { 21 | /// will try to evade damage. returns true and cost if evaded. if not returns false and zero. 22 | pub fn try_evade( 23 | &self, 24 | damage: &Damage, 25 | self_attributes: &Attributes, 26 | attacker_attributes: &Attributes, 27 | rng: &mut StdRng, 28 | ) -> (bool, i16) { 29 | let rate_evade = self.chance.compute(self_attributes); 30 | let rate_hit = damage.hit_chance.compute(attacker_attributes); 31 | let evaded = rng.gen_ratio(rate_evade.min(rate_hit) as u32, rate_hit as u32); 32 | bevy::log::trace!( 33 | "evade rate {}, hit rate {}, evaded {}", 34 | rate_evade, 35 | rate_hit, 36 | evaded 37 | ); 38 | let cost = if evaded { 39 | self.cost.compute(self_attributes) 40 | } else { 41 | 0 42 | }; 43 | (evaded, cost) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/components/formula.rs: -------------------------------------------------------------------------------- 1 | use super::{AttributeType, Attributes}; 2 | use bevy::{prelude::*, reflect::FromReflect}; 3 | use serde::{Deserialize, Serialize}; 4 | use std::{fmt::Debug, hash::Hash}; 5 | 6 | #[derive( 7 | Debug, 8 | Default, 9 | Clone, 10 | PartialEq, 11 | Eq, 12 | Hash, 13 | Component, 14 | Reflect, 15 | FromReflect, 16 | Serialize, 17 | Deserialize, 18 | )] 19 | #[reflect(Component)] 20 | pub struct Multiplier { 21 | /// multiplier taking into account amount of governing attribute. 22 | /// multiplier = 100; attribute = 10; will result in multiplier equal to 1. 23 | pub multiplier: u8, 24 | /// attribute that is taken into account when calculating the multiplier 25 | pub attribute: A, 26 | } 27 | impl Multiplier { 28 | pub fn compute(&self, attributes: &Attributes) -> f32 { 29 | attributes.get(&self.attribute) as f32 * self.multiplier as f32 / 1000. 30 | } 31 | } 32 | 33 | #[derive( 34 | Debug, 35 | Default, 36 | Clone, 37 | PartialEq, 38 | Eq, 39 | Hash, 40 | Component, 41 | Reflect, 42 | FromReflect, 43 | Serialize, 44 | Deserialize, 45 | )] 46 | #[reflect(Component)] 47 | pub struct LinearFormula { 48 | /// multiplier for the multipliers. 100 means it will be multiplied by 1. 49 | pub scale: u16, 50 | /// multipliers per attribute that will be summed up when computing. 51 | pub multipliers: Vec>, 52 | } 53 | impl LinearFormula { 54 | pub fn new(scale: u16, multipls: impl IntoIterator>) -> Self { 55 | Self { 56 | scale, 57 | multipliers: Vec::from_iter(multipls), 58 | } 59 | } 60 | /// formula where compute result is 1. 61 | pub fn one() -> Self { 62 | Self { 63 | scale: 100, 64 | multipliers: Vec::new(), 65 | } 66 | } 67 | pub fn compute(&self, attributes: &Attributes) -> f32 { 68 | (self.scale as f32 / 100.) 69 | * if self.multipliers.is_empty() { 70 | 1. 71 | } else { 72 | self.multipliers 73 | .iter() 74 | .map(|m| m.compute(attributes)) 75 | .sum::() 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/components/hit_points.rs: -------------------------------------------------------------------------------- 1 | use super::{AttributeType, Attributes, LinearFormula}; 2 | use bevy::{prelude::*, reflect::FromReflect}; 3 | use std::{fmt::Debug, hash::Hash}; 4 | 5 | pub const HP_FULL_MIN: i16 = 20; 6 | pub const HP_REGEN_READY_DEFAULT: i16 = 128; 7 | pub const HP_REGEN_INCREMENT_MIN: i16 = 16; 8 | 9 | #[derive(Default, Component, Reflect)] 10 | #[reflect(Component)] 11 | pub struct HitPointsDirty; 12 | 13 | #[derive(Default, Debug, Clone, Eq, PartialEq, Hash, Component, Reflect, FromReflect)] 14 | #[reflect(Component)] 15 | pub struct HitPoints { 16 | is_alive: bool, 17 | full: i16, 18 | current: i16, 19 | 20 | regen_ready: i16, 21 | regen_current: i16, 22 | regen_increment: i16, 23 | 24 | full_formula: LinearFormula, 25 | regen_increment_formula: LinearFormula, 26 | } 27 | impl HitPoints { 28 | pub fn new( 29 | full_formula: LinearFormula, 30 | regen_increment_formula: LinearFormula, 31 | atr: &Attributes, 32 | ) -> Self { 33 | let full = HP_FULL_MIN + full_formula.compute(atr) as i16; 34 | Self { 35 | is_alive: true, 36 | full, 37 | current: full, 38 | regen_ready: HP_REGEN_READY_DEFAULT, 39 | regen_current: 0, 40 | regen_increment: HP_REGEN_INCREMENT_MIN + regen_increment_formula.compute(atr) as i16, 41 | full_formula, 42 | regen_increment_formula, 43 | } 44 | } 45 | pub fn update(&mut self, atr: &Attributes) { 46 | let current_ratio = self.current as f32 / self.full as f32; 47 | self.full = HP_FULL_MIN + self.full_formula.compute(atr) as i16; 48 | self.current = (current_ratio * self.full as f32) as i16; 49 | self.regen_ready = HP_REGEN_READY_DEFAULT; 50 | self.regen_increment = 51 | HP_REGEN_INCREMENT_MIN + self.regen_increment_formula.compute(atr) as i16; 52 | } 53 | 54 | pub fn apply(&mut self, amount: i16) -> i16 { 55 | self.current = i16::min(self.current + amount, self.full); 56 | if self.current <= 0 { 57 | self.is_alive = false; 58 | } 59 | self.current 60 | } 61 | pub fn current(&self) -> i16 { 62 | self.current 63 | } 64 | pub fn percent(&self) -> f32 { 65 | self.current as f32 / self.full as f32 66 | } 67 | pub fn regen(&mut self) { 68 | self.regen_ratio(1.); 69 | } 70 | pub fn regen_ratio(&mut self, ratio: f32) { 71 | self.regen_current += (self.regen_increment as f32 * ratio) as i16; 72 | if self.regen_current > self.regen_ready { 73 | let amount = self.regen_current / self.regen_ready; 74 | let rem = self.regen_current % self.regen_ready; 75 | self.apply(amount); 76 | self.regen_current = rem; 77 | } 78 | } 79 | pub fn full(&self) -> i16 { 80 | self.full 81 | } 82 | 83 | pub fn is_alive(&self) -> bool { 84 | self.is_alive 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/components/mod.rs: -------------------------------------------------------------------------------- 1 | pub use action_cost::*; 2 | pub use action_points::*; 3 | pub use attributes::*; 4 | pub use block::*; 5 | pub use bundles::*; 6 | pub use damage::*; 7 | pub use evasion::*; 8 | pub use formula::*; 9 | pub use hit_points::*; 10 | pub use protection::*; 11 | pub use rate::*; 12 | pub use resistance::*; 13 | pub use stats_computed::*; 14 | 15 | mod action_cost; 16 | mod action_points; 17 | mod attributes; 18 | mod block; 19 | mod bundles; 20 | mod damage; 21 | mod evasion; 22 | mod formula; 23 | mod hit_points; 24 | mod protection; 25 | mod rate; 26 | mod resistance; 27 | mod stats_computed; 28 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/components/protection.rs: -------------------------------------------------------------------------------- 1 | use super::{AttributeType, Attributes, DamageKind, LinearFormula}; 2 | use bevy::{prelude::*, reflect::FromReflect}; 3 | use serde::{Deserialize, Serialize}; 4 | use std::{fmt::Debug, fmt::Display, hash::Hash}; 5 | 6 | #[derive( 7 | Debug, 8 | Default, 9 | Clone, 10 | PartialEq, 11 | Eq, 12 | Hash, 13 | Component, 14 | Reflect, 15 | FromReflect, 16 | Serialize, 17 | Deserialize, 18 | )] 19 | pub struct Protect { 20 | pub kind: K, 21 | pub amount_multiplier: LinearFormula, 22 | pub amount: i32, 23 | } 24 | 25 | impl Protect { 26 | pub fn compute(&self, attributes: &Attributes) -> i32 { 27 | (self.amount as f32 * self.amount_multiplier.compute(attributes)) as i32 28 | } 29 | } 30 | impl Display for Protect { 31 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 32 | // TODO: also write formula if present 33 | write!(f, "{} +{}", self.kind, self.amount) 34 | } 35 | } 36 | 37 | /// Protective Value (PV) or the amount of direct damage negated 38 | #[derive( 39 | Debug, 40 | Default, 41 | Clone, 42 | PartialEq, 43 | Eq, 44 | Hash, 45 | Component, 46 | Reflect, 47 | FromReflect, 48 | Serialize, 49 | Deserialize, 50 | )] 51 | #[reflect(Component)] 52 | pub struct Protection { 53 | pub amounts: Vec>, 54 | } 55 | impl Protection { 56 | pub fn new(protections: impl IntoIterator>) -> Self { 57 | Self { 58 | amounts: Vec::from_iter(protections), 59 | } 60 | } 61 | pub fn extend(&mut self, other: &Protection) -> &mut Protection { 62 | self.amounts.extend(other.clone().amounts); 63 | self 64 | } 65 | } 66 | impl Display for Protection { 67 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 68 | write!( 69 | f, 70 | "{}", 71 | self.amounts 72 | .iter() 73 | .map(|p| format!("{}", p)) 74 | .fold("".to_string(), |acc, x| format!("{}, {}", x, acc)) 75 | ) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/components/rate.rs: -------------------------------------------------------------------------------- 1 | use super::{AttributeType, Attributes, LinearFormula}; 2 | use bevy::{prelude::*, reflect::FromReflect}; 3 | use serde::{Deserialize, Serialize}; 4 | use std::fmt::Debug; 5 | 6 | #[derive( 7 | Debug, Default, Clone, PartialEq, Eq, Component, Reflect, FromReflect, Serialize, Deserialize, 8 | )] 9 | #[reflect(Component)] 10 | pub struct Rate { 11 | /// A chance to perform action modifier where 100 means a normal chance. 12 | pub amount: u8, 13 | pub multiplier: LinearFormula, 14 | } 15 | impl Rate { 16 | pub fn compute(&self, attributes: &Attributes) -> i32 { 17 | (self.multiplier.compute(attributes) * self.amount as f32) as i32 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/components/resistance.rs: -------------------------------------------------------------------------------- 1 | use super::DamageKind; 2 | use bevy::{prelude::*, reflect::FromReflect}; 3 | use serde::{Deserialize, Serialize}; 4 | use std::{fmt::Debug, fmt::Display, hash::Hash}; 5 | 6 | #[derive( 7 | Debug, 8 | Default, 9 | Clone, 10 | PartialEq, 11 | Eq, 12 | Hash, 13 | Component, 14 | Reflect, 15 | FromReflect, 16 | Serialize, 17 | Deserialize, 18 | )] 19 | #[reflect(Component)] 20 | pub struct Resist { 21 | pub kind: K, 22 | /// Resistance amount in percents. 100 means fully resists specified [`DamageKind`]. 23 | pub percent: u8, 24 | } 25 | impl Display for Resist { 26 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 27 | write!(f, "{} +{}", self.kind, self.percent) 28 | } 29 | } 30 | 31 | #[derive( 32 | Debug, Default, Clone, PartialEq, Eq, Component, Reflect, FromReflect, Serialize, Deserialize, 33 | )] 34 | #[reflect(Component)] 35 | pub struct Resistance { 36 | /// defines resistance in percents per damage kind. 37 | pub amounts: Vec>, 38 | } 39 | impl Resistance { 40 | pub fn new(resistances: impl IntoIterator>) -> Self { 41 | Self { 42 | amounts: Vec::from_iter(resistances), 43 | } 44 | } 45 | pub fn ingest(&mut self, other: &Resistance) -> &mut Resistance { 46 | // todo: fix me . instead match on DamageKind 47 | self.amounts.extend(other.clone().amounts); 48 | self 49 | } 50 | } 51 | impl Display for Resistance { 52 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 53 | write!( 54 | f, 55 | "{}", 56 | self.amounts 57 | .iter() 58 | .map(|r| format!("{}", r)) 59 | .fold("".to_string(), |acc, x| format!("{}, {}", x, acc)) 60 | ) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/components/stats_computed.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use bevy::prelude::*; 3 | use std::fmt::Debug; 4 | 5 | #[derive(Default, Component, Reflect)] 6 | #[reflect(Component)] 7 | pub struct StatsComputedDirty; 8 | 9 | #[derive(Component, Debug, Default, Clone, Reflect)] 10 | #[reflect(Component)] 11 | pub struct StatsComputed { 12 | pub attributes: Attributes, 13 | pub protection: Protection, 14 | pub resistance: Resistance, 15 | pub evasion: Evasion, 16 | pub block: Vec>, 17 | pub damage: Vec>, 18 | } 19 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/events.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | #[derive(Debug, Copy, Clone)] 4 | pub struct AttackEvent { 5 | pub attacker: Entity, 6 | pub defender: Entity, 7 | } 8 | 9 | #[derive(Debug, Copy, Clone)] 10 | pub struct SpendAPEvent { 11 | pub id: Entity, 12 | pub amount: i16, 13 | } 14 | 15 | #[derive(Debug, Copy, Clone)] 16 | pub struct ActionCompletedEvent { 17 | pub id: Entity, 18 | } 19 | 20 | impl SpendAPEvent { 21 | pub fn new(id: Entity, amount: i16) -> Self { 22 | Self { id, amount } 23 | } 24 | } 25 | 26 | #[derive(Debug, Copy, Clone)] 27 | pub struct IdleEvent { 28 | pub id: Entity, 29 | } 30 | 31 | // NOTE: a clunky component to transfer damage 32 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Component)] 33 | pub struct DamageHitPointsEvent { 34 | pub defender: Entity, 35 | pub amount: u16, 36 | } 37 | 38 | #[derive(Debug, Copy, Clone)] 39 | pub struct DeathEvent { 40 | pub actor: Entity, 41 | } 42 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use components::*; 2 | pub use events::*; 3 | pub use plugin::RoguelikeCombatPlugin; 4 | pub use rng::RogueRng; 5 | 6 | mod components; 7 | mod events; 8 | mod plugin; 9 | mod rng; 10 | mod systems; 11 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/plugin.rs: -------------------------------------------------------------------------------- 1 | use crate::{components::*, events::*, systems::*}; 2 | use bevy::{ecs::schedule::StateData, prelude::*}; 3 | use std::marker::PhantomData; 4 | 5 | pub struct RoguelikeCombatPlugin { 6 | pub state_running: S, 7 | pub phantom_1: PhantomData, 8 | pub phantom_2: PhantomData, 9 | } 10 | 11 | impl Plugin for RoguelikeCombatPlugin { 12 | fn build(&self, app: &mut App) { 13 | app.add_system_set_to_stage( 14 | CoreStage::PreUpdate, 15 | SystemSet::on_update(self.state_running.clone()) 16 | .with_system(attributes_update_action_points::) 17 | .with_system(attributes_update_hit_points::), 18 | ) 19 | .add_system_set_to_stage( 20 | CoreStage::Update, 21 | SystemSet::on_update(self.state_running.clone()) 22 | .with_system(attack::) 23 | .with_system(spend_ap::), 24 | ) 25 | .add_system_set_to_stage( 26 | CoreStage::PostUpdate, 27 | SystemSet::on_update(self.state_running.clone()) 28 | .with_system(damage_hit_points::) 29 | .with_system(idle_rest::.after(damage_hit_points::)), 30 | ) 31 | .register_type::>() 32 | .register_type::() 33 | .register_type::>() 34 | .register_type::() 35 | .register_type::>() 36 | .register_type::() 37 | .register_type::>() 38 | .register_type::>() 39 | .register_type::>() 40 | .register_type::>() 41 | .register_type::>() 42 | .register_type::>() 43 | .register_type::>() 44 | .register_type::>() 45 | .register_type::>() 46 | .register_type::>() 47 | .register_type::>() 48 | .register_type::>() 49 | .register_type::() 50 | .register_type::() 51 | .add_event::() 52 | .add_event::() 53 | .add_event::() 54 | .add_event::() 55 | .add_event::() 56 | .add_event::(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /bevy_roguelike_combat/src/rng.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::Resource; 2 | use rand::rngs::StdRng; 3 | use std::ops::{Deref, DerefMut}; 4 | 5 | #[derive(Resource)] 6 | pub struct RogueRng(pub StdRng); 7 | 8 | impl Deref for RogueRng { 9 | type Target = StdRng; 10 | 11 | fn deref(&self) -> &Self::Target { 12 | &self.0 13 | } 14 | } 15 | 16 | impl DerefMut for RogueRng { 17 | fn deref_mut(&mut self) -> &mut Self::Target { 18 | &mut self.0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bevy_roguelike_plugin" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [features] 9 | default = [] 10 | debug = ["colored"] 11 | 12 | [dependencies] 13 | vec_walk_dir = { path = "../vec_walk_dir" } 14 | bevy_roguelike_combat = { path = "../bevy_roguelike_combat" } 15 | map_generator = { path = "../map_generator" } 16 | bevy_inventory = { path = "../bevy_inventory" } 17 | bevy_inventory_ui = { path = "../bevy_inventory_ui" } 18 | bevy_tweening = "~0.6" 19 | bevy_common_assets = { version = "~0.4", features = ["ron"] } 20 | line_drawing = "~1.0" 21 | rand = "~0.8" 22 | serde = "~1.0" 23 | ron = "~0.8" 24 | strum = "~0.24" 25 | strum_macros = "~0.24" 26 | colored = { version = "~2.0", optional = true } 27 | 28 | [dependencies.bevy] 29 | version = "~0.9" 30 | default-features = false 31 | features = ["render"] 32 | 33 | # Dependencies for WASM only 34 | [target.'cfg(target_arch = "wasm32")'.dependencies.getrandom] 35 | version = "0.2" 36 | features = ["js"] 37 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/components/actor/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::forget_non_drop)] // https://github.com/bevyengine/bevy/issues/4601 2 | use super::*; 3 | use crate::resources::{ActorTemplate, CombatSettings}; 4 | use bevy::{prelude::*, reflect::FromReflect, utils::HashMap}; 5 | use bevy_inventory::{Equipment, Inventory, ItemType}; 6 | use bevy_inventory_ui::EquipmentDisplay; 7 | use bevy_roguelike_combat::*; 8 | use serde::{Deserialize, Serialize}; 9 | use std::fmt::Display; 10 | use strum_macros::EnumIter; 11 | 12 | #[derive( 13 | Debug, 14 | Default, 15 | Clone, 16 | Copy, 17 | PartialEq, 18 | Eq, 19 | Hash, 20 | Component, 21 | Reflect, 22 | FromReflect, 23 | Serialize, 24 | Deserialize, 25 | EnumIter, 26 | )] 27 | #[reflect(Component)] 28 | // #[reflect_value(PartialEq, Serialize, Deserialize)] 29 | pub enum RogueAttributeType { 30 | #[default] 31 | Strength, 32 | Dexterity, 33 | Inteligence, 34 | Toughness, 35 | Perception, 36 | Willpower, 37 | } 38 | impl Display for RogueAttributeType { 39 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 40 | write!( 41 | f, 42 | "{}", 43 | match self { 44 | RogueAttributeType::Strength => "str", 45 | RogueAttributeType::Dexterity => "dex", 46 | RogueAttributeType::Inteligence => "int", 47 | RogueAttributeType::Toughness => "tou", 48 | RogueAttributeType::Perception => "per", 49 | RogueAttributeType::Willpower => "wil", 50 | } 51 | ) 52 | } 53 | } 54 | impl AttributeType for RogueAttributeType {} 55 | 56 | fn from_display(display: &EquipmentDisplay) -> Equipment { 57 | let mut items = HashMap::default(); 58 | for (t, _) in display.items.iter() { 59 | items.entry(*t).insert(None); 60 | } 61 | Equipment { items } 62 | } 63 | 64 | #[derive(Bundle)] 65 | pub struct Actor { 66 | name: Name, 67 | team: Team, 68 | state: TurnState, 69 | combat: Combat, 70 | fov: FieldOfView, 71 | position: Vector2D, 72 | render_info: RenderInfo, 73 | equipment_display: EquipmentDisplay, 74 | equipment: Equipment, 75 | inventory: Inventory, 76 | } 77 | impl Actor { 78 | /// Creates a new [`Actor`] using specified [`ActorTemplate`]. 79 | pub fn new( 80 | asset_server: AssetServer, 81 | template: &ActorTemplate, 82 | combat_settings: &CombatSettings, 83 | team: u32, 84 | position: IVec2, 85 | ) -> Self { 86 | Self { 87 | name: Name::new(template.render.name.clone()), 88 | team: Team::new(team), 89 | state: TurnState::default(), 90 | combat: Combat::new( 91 | &template.attributes, 92 | combat_settings.ap_increment_formula.clone(), 93 | combat_settings.hp_full_formula.clone(), 94 | combat_settings.hp_regen_increment_formula.clone(), 95 | template.damage.clone(), 96 | template.protection.clone(), 97 | template.evasion.clone(), 98 | template.resistance.clone(), 99 | ), 100 | fov: FieldOfView::new(&template.attributes), 101 | inventory: Inventory::with_capacity(template.inventory_capacity), 102 | equipment_display: template.equipment_display.clone(), 103 | equipment: from_display(&template.equipment_display), 104 | position: Vector2D::from(position), 105 | render_info: RenderInfo { 106 | texture: asset_server.load(template.render.texture_path.as_str()), 107 | cosmetic_textures: template 108 | .render 109 | .texture_path_cosmetics 110 | .iter() 111 | .map(|p| asset_server.load(p.as_str())) 112 | .collect(), 113 | z: 2., 114 | }, 115 | } 116 | } 117 | } 118 | 119 | #[derive(Debug, Default, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Component, Reflect)] 120 | #[reflect(Component)] 121 | pub enum TurnState { 122 | #[default] 123 | Collect, 124 | Act, 125 | End, 126 | } 127 | 128 | #[derive(Default, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Component, Reflect)] 129 | #[reflect(Component)] 130 | pub struct MovingPlayer; 131 | 132 | #[derive(Debug, Default, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Component, Reflect)] 133 | #[reflect(Component)] 134 | pub struct MovingRandom; 135 | 136 | #[derive(Debug, Default, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Component, Reflect)] 137 | #[reflect(Component)] 138 | pub struct MovingFovRandom; 139 | 140 | #[derive(Default, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Component, Reflect)] 141 | #[reflect(Component)] 142 | pub struct Team { 143 | id: u32, 144 | } 145 | 146 | impl Team { 147 | pub fn new(id: u32) -> Self { 148 | Self { id } 149 | } 150 | pub fn id(&self) -> u32 { 151 | self.id 152 | } 153 | } 154 | 155 | // TODO: fix lousy name 156 | #[derive(Default, Debug, Clone, Eq, PartialEq, Component, Reflect)] 157 | #[reflect(Component)] 158 | pub struct HudHealthBar; 159 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/components/damage.rs: -------------------------------------------------------------------------------- 1 | use bevy::{prelude::*, reflect::FromReflect}; 2 | use bevy_roguelike_combat::DamageKind; 3 | use serde::{Deserialize, Serialize}; 4 | use std::fmt::Display; 5 | 6 | /// Type of damage that can be inflicted by actors or environment. 7 | #[derive( 8 | Debug, 9 | Default, 10 | Clone, 11 | Copy, 12 | PartialEq, 13 | Eq, 14 | Hash, 15 | Component, 16 | Reflect, 17 | FromReflect, 18 | Serialize, 19 | Deserialize, 20 | )] 21 | #[reflect(Component)] 22 | // #[reflect_value(PartialEq, Serialize, Deserialize)] 23 | pub enum RogueDamageKind { 24 | /// phisical crushing damage 25 | #[default] 26 | Blunt, 27 | /// phisical puncturing damage 28 | Pierce, 29 | /// phisical cut damage 30 | Slash, 31 | /// elemental heat damage 32 | Fire, 33 | /// elemental cold damage 34 | Cold, 35 | /// elemental electrical damage 36 | Lightning, 37 | } 38 | impl DamageKind for RogueDamageKind {} 39 | 40 | impl Display for RogueDamageKind { 41 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 42 | write!(f, "{:?}", self) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/components/environment.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | #[derive(Default, Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Component, Reflect)] 4 | #[reflect(Component)] 5 | pub struct MapTile { 6 | pub is_passable: bool, 7 | // TODO: include cost of moving into a tile 8 | 9 | // TODO: should also describe the kind of tile (what sides are open) 10 | } 11 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/components/fov.rs: -------------------------------------------------------------------------------- 1 | use super::RogueAttributeType; 2 | use bevy::{prelude::*, utils::HashSet}; 3 | use bevy_roguelike_combat::Attributes; 4 | 5 | // #[derive(Default, Component, Reflect)] 6 | // #[reflect(Component)] 7 | // pub struct VisibilityToggle; 8 | 9 | #[derive(Default, Component, Reflect)] 10 | #[reflect(Component)] 11 | pub struct FieldOfViewDirty; 12 | 13 | #[derive(Default, Debug, Clone, Eq, PartialEq, Component, Reflect)] 14 | #[reflect(Component)] 15 | pub struct FieldOfView { 16 | pub radius: i32, 17 | pub tiles_visible: HashSet, 18 | pub tiles_revealed: HashSet, 19 | // TODO: refactor as a separate dirty component (do not clash with FieldOfViewDirty use.. better name?) 20 | pub is_dirty: bool, 21 | } 22 | 23 | impl FieldOfView { 24 | pub const MIN_RADIUS: i32 = 2; 25 | 26 | // TODO: use formula instead like the rest of the attribute dependant derived attributes 27 | pub fn new(atr: &Attributes) -> Self { 28 | Self { 29 | radius: FieldOfView::MIN_RADIUS 30 | + (atr.get(&RogueAttributeType::Perception) as f32 / 3. 31 | + atr.get(&RogueAttributeType::Inteligence) as f32 / 10.) 32 | as i32, 33 | tiles_visible: HashSet::default(), 34 | tiles_revealed: HashSet::default(), 35 | is_dirty: true, 36 | } 37 | } 38 | pub fn update(&mut self, atr: &Attributes) { 39 | self.radius = FieldOfView::MIN_RADIUS 40 | + (atr.get(&RogueAttributeType::Perception) as f32 / 3. 41 | + atr.get(&RogueAttributeType::Inteligence) as f32 / 10.) as i32; 42 | self.is_dirty = true; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/components/item/from_template.rs: -------------------------------------------------------------------------------- 1 | use super::MutableQuality; 2 | use super::Quality; 3 | use crate::components::*; 4 | use crate::resources::*; 5 | use bevy::ecs::system::EntityCommands; 6 | use bevy::prelude::*; 7 | use bevy_inventory_ui::UiRenderInfo; 8 | use rand::prelude::*; 9 | 10 | pub fn spawn_item( 11 | ecmd: &mut EntityCommands, 12 | asset_server: AssetServer, 13 | template: &ItemTemplate, 14 | quality: &Quality, 15 | rng: &mut StdRng, 16 | ) { 17 | // TODO: refactor all insert calls into ergonomic inserts: https://github.com/bevyengine/bevy/pull/6039 18 | ecmd.insert(quality.clone()); 19 | match template { 20 | ItemTemplate::Weapon(Weapon { render, damage }) => { 21 | ecmd.insert(RogueItemType::MainHand); 22 | insert_render(ecmd, asset_server, render); 23 | ecmd.insert(damage.mutate(quality, rng)); 24 | } 25 | ItemTemplate::Shield(Shield { 26 | render, 27 | protection, 28 | block, 29 | }) => { 30 | ecmd.insert(RogueItemType::OffHand); 31 | insert_render(ecmd, asset_server, render); 32 | ecmd.insert(protection.mutate(quality, rng)) 33 | .insert(block.mutate(quality, rng)); 34 | } 35 | ItemTemplate::Helm(Helm { 36 | render, 37 | defense, 38 | enchantment, 39 | }) => { 40 | ecmd.insert(RogueItemType::Head); 41 | insert_render(ecmd, asset_server, render); 42 | insert_defense(ecmd, defense, quality, rng); 43 | insert_enchantment(ecmd, enchantment, quality, rng); 44 | } 45 | ItemTemplate::Armor(Armor { 46 | render, 47 | defense, 48 | enchantment, 49 | }) => { 50 | ecmd.insert(RogueItemType::Body); 51 | insert_render(ecmd, asset_server, render); 52 | insert_defense(ecmd, defense, quality, rng); 53 | insert_enchantment(ecmd, enchantment, quality, rng); 54 | } 55 | ItemTemplate::Boots(Boots { 56 | render, 57 | defense, 58 | enchantment, 59 | }) => { 60 | ecmd.insert(RogueItemType::Feet); 61 | insert_render(ecmd, asset_server, render); 62 | insert_defense(ecmd, defense, quality, rng); 63 | insert_enchantment(ecmd, enchantment, quality, rng); 64 | } 65 | ItemTemplate::Amulet(Amulet { 66 | render, 67 | defense, 68 | enchantment, 69 | }) => { 70 | ecmd.insert(RogueItemType::Neck); 71 | insert_render(ecmd, asset_server, render); 72 | insert_defense(ecmd, defense, quality, rng); 73 | insert_enchantment(ecmd, enchantment, quality, rng); 74 | } 75 | ItemTemplate::Ring(Ring { 76 | render, 77 | defense, 78 | enchantment, 79 | }) => { 80 | ecmd.insert(RogueItemType::Finger); 81 | insert_render(ecmd, asset_server, render); 82 | insert_defense(ecmd, defense, quality, rng); 83 | insert_enchantment(ecmd, enchantment, quality, rng); 84 | } 85 | } 86 | } 87 | 88 | fn insert_defense( 89 | ecmd: &mut EntityCommands, 90 | defense: &ItemDefense, 91 | quality: &Quality, 92 | rng: &mut StdRng, 93 | ) { 94 | if let Some(prot) = defense.protection.clone() { 95 | ecmd.insert(prot.mutate(quality, rng)); 96 | } 97 | if let Some(res) = defense.resistance.clone() { 98 | ecmd.insert(res.mutate(quality, rng)); 99 | } 100 | } 101 | fn insert_enchantment( 102 | ecmd: &mut EntityCommands, 103 | enchantment: &ItemEnchantment, 104 | quality: &Quality, 105 | rng: &mut StdRng, 106 | ) { 107 | if let Some(attributes) = enchantment.attributes.clone() { 108 | ecmd.insert(attributes.mutate(quality, rng)); 109 | } 110 | } 111 | 112 | fn insert_render(ecmd: &mut EntityCommands, asset_server: AssetServer, render: &ItemRenderInfo) { 113 | let texture = asset_server.load(render.texture_path.as_str()); 114 | ecmd.insert(( 115 | Name::new(render.name.clone()), 116 | UiRenderInfo { 117 | image: texture.clone().into(), 118 | }, 119 | RenderInfo { 120 | texture, 121 | cosmetic_textures: vec![], 122 | z: 1., 123 | }, 124 | )); 125 | if let Some(path_equiped) = render.texture_equiped_path.clone() { 126 | ecmd.insert(RenderInfoEquiped { 127 | texture: asset_server.load(path_equiped.as_str()), 128 | z: 4., 129 | }); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/components/item/mod.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy_inventory::ItemType; 3 | pub use from_template::spawn_item; 4 | pub use quality::MutableQuality; 5 | pub use quality::Quality; 6 | use serde::Deserialize; 7 | use serde::Serialize; 8 | 9 | mod from_template; 10 | mod quality; 11 | 12 | #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Component)] 13 | pub enum RogueItemType { 14 | #[default] 15 | MainHand, 16 | OffHand, 17 | Head, 18 | Neck, 19 | Body, 20 | Feet, 21 | Finger, 22 | } 23 | impl ItemType for RogueItemType {} 24 | 25 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Component)] 26 | pub struct EquipedRendition { 27 | // Entity id of the item rendition 28 | pub item_render_entity: Entity, 29 | } 30 | 31 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Component)] 32 | pub struct EquipedRenderedItem { 33 | /// Entity id of the item 34 | pub item: Entity, 35 | } 36 | 37 | #[derive(Debug, Component)] 38 | pub struct ItemEquipedOwned { 39 | /// Entity id of the actor that has that item equiped 40 | pub actor: Entity, 41 | } 42 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/components/mod.rs: -------------------------------------------------------------------------------- 1 | pub use actor::Actor; 2 | pub use actor::HudHealthBar; 3 | pub use actor::MovingFovRandom; 4 | pub use actor::MovingPlayer; 5 | pub use actor::MovingRandom; 6 | pub use actor::RogueAttributeType; 7 | pub use actor::Team; 8 | pub use actor::TurnState; 9 | pub use damage::RogueDamageKind; 10 | pub use environment::MapTile; 11 | pub use fov::FieldOfView; 12 | pub use fov::FieldOfViewDirty; 13 | pub use item::spawn_item; 14 | pub use item::EquipedRenderedItem; 15 | pub use item::EquipedRendition; 16 | pub use item::ItemEquipedOwned; 17 | pub use item::Quality; 18 | pub use item::RogueItemType; 19 | pub use render_info::RenderInfo; 20 | pub use render_info::RenderInfoEquiped; 21 | pub use vector2d::Vector2D; 22 | 23 | mod actor; 24 | mod damage; 25 | mod environment; 26 | mod fov; 27 | mod item; 28 | mod render_info; 29 | mod vector2d; 30 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/components/render_info.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | 3 | /// specifies how to render stuff if it is placed on a map 4 | #[derive(Default, Debug, Clone, Component, Reflect)] 5 | #[reflect(Component)] 6 | pub struct RenderInfo { 7 | pub texture: Handle, 8 | pub cosmetic_textures: Vec>, 9 | pub z: f32, 10 | } 11 | /// specifies how to render stuff if it is equiped by an actor 12 | #[derive(Default, Debug, Clone, Component, Reflect)] 13 | #[reflect(Component)] 14 | pub struct RenderInfoEquiped { 15 | pub texture: Handle, 16 | pub z: f32, 17 | } 18 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/components/vector2d.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::IVec2; 2 | use bevy::prelude::*; 3 | use serde::{Deserialize, Serialize}; 4 | use std::fmt::{self, Display, Formatter}; 5 | use std::ops::{Deref, DerefMut}; 6 | 7 | /// Vector of 2 Dimensions. 8 | #[derive( 9 | Default, Debug, Copy, Clone, Eq, PartialEq, Hash, Component, Deserialize, Serialize, Reflect, 10 | )] 11 | #[reflect(Component)] 12 | pub struct Vector2D { 13 | internal: IVec2, 14 | } 15 | impl Deref for Vector2D { 16 | type Target = IVec2; 17 | fn deref(&self) -> &Self::Target { 18 | &self.internal 19 | } 20 | } 21 | impl DerefMut for Vector2D { 22 | fn deref_mut(&mut self) -> &mut Self::Target { 23 | &mut self.internal 24 | } 25 | } 26 | impl From for Vector2D { 27 | fn from(internal: IVec2) -> Self { 28 | Self { internal } 29 | } 30 | } 31 | impl Display for Vector2D { 32 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 33 | write!(f, "({}, {})", self.x, self.y) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/events/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::components::Team; 2 | use bevy::prelude::*; 3 | 4 | // TODO: turn into Act component with is_dirty or is_used 5 | #[derive(Debug, Copy, Clone)] 6 | pub struct ActEvent { 7 | pub id: Entity, 8 | pub delta: IVec2, 9 | } 10 | 11 | #[derive(Debug, Copy, Clone)] 12 | pub struct MoveEvent { 13 | pub actor: Entity, 14 | pub team: Team, 15 | pub from: IVec2, 16 | pub to: IVec2, 17 | } 18 | 19 | #[derive(Debug, Copy, Clone)] 20 | pub struct CameraFocusEvent { 21 | pub position: IVec2, 22 | } 23 | impl CameraFocusEvent { 24 | pub fn new(position: IVec2) -> Self { 25 | Self { position } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/resources/actor_template.rs: -------------------------------------------------------------------------------- 1 | use crate::components::*; 2 | use bevy::reflect::TypeUuid; 3 | use bevy_inventory_ui::EquipmentDisplay; 4 | use bevy_roguelike_combat::*; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | #[derive(Serialize, Deserialize, TypeUuid)] 8 | #[uuid = "0c475ce2-2031-4259-9e22-6a8f3b94a1d1"] 9 | pub struct ActorTemplate { 10 | pub render: ActorRenderInfo, 11 | pub attributes: Attributes, 12 | pub protection: Protection, 13 | pub resistance: Resistance, 14 | pub evasion: Evasion, 15 | pub damage: DamageList, 16 | pub equipment_display: EquipmentDisplay, 17 | pub inventory_capacity: usize, 18 | // TODO: initial equipment 19 | // TODO: initial inventory 20 | } 21 | 22 | #[derive(Serialize, Deserialize)] 23 | pub struct ActorRenderInfo { 24 | pub name: String, 25 | pub texture_path: String, 26 | pub texture_path_cosmetics: Vec, 27 | } 28 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/resources/combat_settings.rs: -------------------------------------------------------------------------------- 1 | use crate::components::RogueAttributeType; 2 | use bevy::reflect::TypeUuid; 3 | use bevy_roguelike_combat::LinearFormula; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Serialize, Deserialize, TypeUuid)] 7 | #[uuid = "f08a6321-17b4-4087-843b-75251ea7bde4"] 8 | pub struct CombatSettings { 9 | pub ap_increment_formula: LinearFormula, 10 | pub hp_full_formula: LinearFormula, 11 | pub hp_regen_increment_formula: LinearFormula, 12 | } 13 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/resources/inventory_assets.rs: -------------------------------------------------------------------------------- 1 | use crate::components::RogueItemType; 2 | use bevy::prelude::*; 3 | use bevy::reflect::TypeUuid; 4 | use bevy_inventory_ui::ItemTypeUiImage; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | #[derive(Resource, Serialize, Deserialize, TypeUuid)] 8 | #[uuid = "ea186dbc-8eb4-48c5-b1c3-40b980b5da8a"] 9 | pub struct InventoryTheme { 10 | pub slot: String, 11 | pub head_wear: String, 12 | pub body_wear: String, 13 | pub main_hand_gear: String, 14 | pub off_hand_gear: String, 15 | pub finger_wear: String, 16 | pub neck_wear: String, 17 | pub feet_wear: String, 18 | } 19 | 20 | #[derive(Resource, Debug, Clone)] 21 | pub struct InventoryAssets { 22 | pub slot: Handle, 23 | pub head_wear: Handle, 24 | pub body_wear: Handle, 25 | pub main_hand_gear: Handle, 26 | pub off_hand_gear: Handle, 27 | pub finger_wear: Handle, 28 | pub neck_wear: Handle, 29 | pub feet_wear: Handle, 30 | } 31 | 32 | impl ItemTypeUiImage for InventoryAssets { 33 | fn get_image(&self, item_type: RogueItemType) -> UiImage { 34 | match item_type { 35 | RogueItemType::MainHand => self.main_hand_gear.clone(), 36 | RogueItemType::OffHand => self.off_hand_gear.clone(), 37 | RogueItemType::Head => self.head_wear.clone(), 38 | RogueItemType::Neck => self.neck_wear.clone(), 39 | RogueItemType::Body => self.body_wear.clone(), 40 | RogueItemType::Feet => self.feet_wear.clone(), 41 | RogueItemType::Finger => self.finger_wear.clone(), 42 | } 43 | .into() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/resources/item_template.rs: -------------------------------------------------------------------------------- 1 | use crate::components::{RogueAttributeType, RogueDamageKind}; 2 | use bevy::reflect::TypeUuid; 3 | use bevy_roguelike_combat::*; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | // TODO: each item should define possible artifact bonuses 7 | #[derive(Serialize, Deserialize, TypeUuid)] 8 | #[uuid = "5621d397-fbc8-4216-b1d8-3d90743338e8"] 9 | pub enum ItemTemplate { 10 | Weapon(Weapon), 11 | Shield(Shield), 12 | Helm(Helm), 13 | Armor(Armor), 14 | Boots(Boots), 15 | Amulet(Amulet), 16 | Ring(Ring), 17 | } 18 | #[derive(Serialize, Deserialize)] 19 | pub struct ItemRenderInfo { 20 | pub name: String, 21 | pub texture_path: String, 22 | pub texture_equiped_path: Option, 23 | } 24 | #[derive(Serialize, Deserialize)] 25 | pub struct ItemDefense { 26 | pub protection: Option>, 27 | pub resistance: Option>, 28 | } 29 | #[derive(Serialize, Deserialize)] 30 | pub struct ItemEnchantment { 31 | pub attributes: Option>, 32 | // TODO: add various enchantment options. 33 | // evasion boost 34 | // speed bost 35 | // ... 36 | } 37 | #[derive(Serialize, Deserialize)] 38 | pub struct Weapon { 39 | pub render: ItemRenderInfo, 40 | pub damage: Damage, 41 | } 42 | #[derive(Serialize, Deserialize)] 43 | pub struct Shield { 44 | pub render: ItemRenderInfo, 45 | pub protection: Protection, 46 | pub block: Block, 47 | } 48 | #[derive(Serialize, Deserialize)] 49 | pub struct Helm { 50 | pub render: ItemRenderInfo, 51 | pub defense: ItemDefense, 52 | pub enchantment: ItemEnchantment, 53 | } 54 | #[derive(Serialize, Deserialize)] 55 | pub struct Armor { 56 | pub render: ItemRenderInfo, 57 | pub defense: ItemDefense, 58 | pub enchantment: ItemEnchantment, 59 | } 60 | #[derive(Serialize, Deserialize)] 61 | pub struct Boots { 62 | pub render: ItemRenderInfo, 63 | pub defense: ItemDefense, 64 | pub enchantment: ItemEnchantment, 65 | } 66 | #[derive(Serialize, Deserialize)] 67 | pub struct Amulet { 68 | pub render: ItemRenderInfo, 69 | pub defense: ItemDefense, 70 | pub enchantment: ItemEnchantment, 71 | } 72 | #[derive(Serialize, Deserialize)] 73 | pub struct Ring { 74 | pub render: ItemRenderInfo, 75 | pub defense: ItemDefense, 76 | pub enchantment: ItemEnchantment, 77 | } 78 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/resources/map_info.rs: -------------------------------------------------------------------------------- 1 | use bevy::{math::IVec2, prelude::Resource}; 2 | use map_generator::*; 3 | use rand::prelude::*; 4 | 5 | #[cfg(feature = "debug")] 6 | use colored::Colorize; 7 | 8 | #[derive(Resource, Debug, Clone)] 9 | pub struct MapInfo { 10 | pub player_start: IVec2, 11 | pub camera_focus: IVec2, 12 | pub monster_spawns: Vec, 13 | pub item_spawns: Vec, 14 | } 15 | 16 | impl MapInfo { 17 | pub fn from_map(map: &Map, rng: &mut StdRng) -> MapInfo { 18 | let floor: Vec = map 19 | .enumerate() 20 | .filter(|(_, t)| **t == Tile::Floor) 21 | .map(|(p, _)| p) 22 | .collect(); 23 | 24 | let pidx = rng.gen_range(0..floor.len()); 25 | let monster_count = floor.len() / 32; 26 | let item_count = floor.len() / 64; 27 | let player_start = floor[pidx]; 28 | 29 | let mut monster_spawns = Vec::new(); 30 | while monster_spawns.len() < monster_count { 31 | let midx = rng.gen_range(0..floor.len()); 32 | let pt = floor[midx]; 33 | if midx != pidx && !monster_spawns.contains(&pt) { 34 | monster_spawns.push(pt); 35 | } 36 | } 37 | 38 | let mut item_spawns = Vec::new(); 39 | while item_spawns.len() < item_count { 40 | let iidx = rng.gen_range(0..floor.len()); 41 | let pt = floor[iidx]; 42 | if !item_spawns.contains(&pt) { 43 | item_spawns.push(pt); 44 | } 45 | } 46 | 47 | MapInfo { 48 | player_start, 49 | camera_focus: player_start, 50 | monster_spawns, 51 | item_spawns, 52 | } 53 | } 54 | 55 | pub fn to_colorized_string(&self) -> String { 56 | format!("player star: {}", self.player_start) 57 | } 58 | } 59 | 60 | pub trait Colorized { 61 | fn to_colorized_string(&self) -> String; 62 | } 63 | 64 | #[cfg(feature = "debug")] 65 | impl Colorized for Tile { 66 | fn to_colorized_string(&self) -> String { 67 | format!( 68 | "{}", 69 | match self { 70 | Tile::Wall => "#".bright_red(), 71 | Tile::Floor => ".".bright_green(), 72 | } 73 | ) 74 | } 75 | } 76 | 77 | #[cfg(feature = "debug")] 78 | impl Colorized for Map { 79 | fn to_colorized_string(&self) -> String { 80 | let mut buffer = format!("Map (w: {}, h: {})\n", self.size().x, self.size().y); 81 | let line: String = (0..(self.size().x + 2)).into_iter().map(|_| '-').collect(); 82 | buffer = format!("{}{}\n", buffer, line); 83 | for y in (0..self.size().y).rev() { 84 | buffer = format!("{}|", buffer); 85 | for tile in self.get_slice(y) { 86 | buffer = format!("{}{}", buffer, tile.to_colorized_string()); 87 | } 88 | buffer = format!("{}|\n", buffer); 89 | } 90 | format!("{}{}", buffer, line) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/resources/map_options.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | /// Map generation options. Must be used as a resource 5 | // We use serde to allow saving option presets and loading them at runtime 6 | #[derive(Resource, Debug, Clone, Serialize, Deserialize)] 7 | pub struct MapOptions { 8 | /// Tile map size in dimentions or tile 9 | pub map_size: IVec2, 10 | /// Tile world size 11 | pub tile_size: f32, 12 | } 13 | 14 | impl Default for MapOptions { 15 | fn default() -> Self { 16 | Self { 17 | map_size: IVec2::new(80, 50), 18 | tile_size: 32.0, 19 | } 20 | } 21 | } 22 | 23 | impl MapOptions { 24 | pub fn to_world_position(&self, pt: IVec2) -> Vec2 { 25 | // NOTE: When camera focus is in play we no longer need offset. 26 | // let x_offset = self.map_size.x as f32 * self.tile_size / -2.; 27 | // let y_offset = self.map_size.y as f32 * self.tile_size / -2.; 28 | Vec2::new( 29 | (pt.x as f32 * self.tile_size) + (self.tile_size / 2.), 30 | (pt.y as f32 * self.tile_size) + (self.tile_size / 2.), 31 | ) 32 | } 33 | pub fn to_closest_map_position(&self, translation: Vec3) -> IVec2 { 34 | IVec2::new( 35 | ((translation.x - self.tile_size / 2.) / self.tile_size) as i32, 36 | ((translation.y - self.tile_size / 2.) / self.tile_size) as i32, 37 | ) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/resources/map_theme.rs: -------------------------------------------------------------------------------- 1 | use bevy::reflect::TypeUuid; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, TypeUuid)] 5 | #[uuid = "272889fd-4633-4b9c-8d33-ff19d3f2ecef"] 6 | pub struct MapTheme { 7 | pub floor: Vec, 8 | pub wall: Vec, 9 | } 10 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/resources/mod.rs: -------------------------------------------------------------------------------- 1 | pub use actor_template::ActorRenderInfo; 2 | pub use actor_template::ActorTemplate; 3 | pub use combat_settings::CombatSettings; 4 | pub use inventory_assets::InventoryAssets; 5 | pub use inventory_assets::InventoryTheme; 6 | pub use item_template::*; 7 | pub use map_info::*; 8 | pub use map_options::*; 9 | pub use map_theme::MapTheme; 10 | pub use rogue_map::RogueMap; 11 | 12 | mod actor_template; 13 | mod combat_settings; 14 | mod inventory_assets; 15 | mod item_template; 16 | mod map_info; 17 | mod map_options; 18 | mod map_theme; 19 | mod rogue_map; 20 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/resources/rogue_map.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::Resource; 2 | use map_generator::Map; 3 | use std::ops::{Deref, DerefMut}; 4 | 5 | #[derive(Resource)] 6 | pub struct RogueMap(pub Map); 7 | 8 | impl Deref for RogueMap { 9 | type Target = Map; 10 | 11 | fn deref(&self) -> &Self::Target { 12 | &self.0 13 | } 14 | } 15 | 16 | impl DerefMut for RogueMap { 17 | fn deref_mut(&mut self) -> &mut Self::Target { 18 | &mut self.0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/systems/action.rs: -------------------------------------------------------------------------------- 1 | use crate::{components::*, events::*, resources::RogueMap}; 2 | use bevy::{log, prelude::*, utils::HashMap}; 3 | use bevy_inventory::{Equipment, Inventory, ItemType}; 4 | use bevy_inventory_ui::InventoryDisplayOwner; 5 | use bevy_roguelike_combat::*; 6 | use map_generator::*; 7 | 8 | pub fn act( 9 | actors: Query<(&Team, &Vector2D)>, 10 | enemies: Query<(Entity, &Team, &Vector2D)>, 11 | // TODO: ActComponent instead of ActEvent 12 | mut act_reader: EventReader, 13 | mut attack_writer: EventWriter, 14 | mut move_writer: EventWriter, 15 | mut idle_writer: EventWriter, 16 | map: Res, 17 | ) { 18 | let team_pt: HashMap<_, _> = actors.iter().map(|(t, p)| (**p, *t)).collect(); 19 | for e in act_reader.iter() { 20 | if e.delta == IVec2::new(0, 0) { 21 | idle_writer.send(IdleEvent { id: e.id }); 22 | continue; 23 | } 24 | if let Ok((team, pt)) = actors.get(e.id) { 25 | let dest = **pt + e.delta; 26 | if !map.is_in_bounds(dest) || map[dest] != Tile::Floor { 27 | idle_writer.send(IdleEvent { id: e.id }); 28 | continue; 29 | } 30 | if let Some(other_team) = team_pt.get(&dest) { 31 | if *other_team == *team { 32 | // NOTE: can not move into a tile ocupied by a team mate 33 | idle_writer.send(IdleEvent { id: e.id }); 34 | continue; 35 | } else if let Some((enemy_entity, _, _)) = 36 | enemies.iter().find(|(_, t, p)| *t != team && ***p == dest) 37 | { 38 | attack_writer.send({ 39 | AttackEvent { 40 | attacker: e.id, 41 | defender: enemy_entity, 42 | } 43 | }) 44 | } else { 45 | log::error!("nothing to attack at {:?} (... has bugs).", dest); 46 | } 47 | } else { 48 | move_writer.send(MoveEvent { 49 | actor: e.id, 50 | team: *team, 51 | from: **pt, 52 | to: dest, 53 | }); 54 | } 55 | } 56 | } 57 | } 58 | 59 | #[allow(clippy::type_complexity)] 60 | pub fn death_read( 61 | mut cmd: Commands, 62 | mut death_reader: EventReader, 63 | actors: Query<( 64 | &Vector2D, 65 | &Name, 66 | &HitPoints, 67 | &Inventory, 68 | &Equipment, 69 | )>, 70 | inventory_displays: Query<(Entity, &InventoryDisplayOwner)>, 71 | ) { 72 | for death in death_reader.iter() { 73 | if let Ok((pt, name, _hp, inventory, equipment)) = actors.get(death.actor) { 74 | for item_entity in inventory 75 | .iter_some() 76 | .chain(equipment.iter_some().map(|(_, item)| item)) 77 | { 78 | // NOTE: manually droping without itemDropEvent. 79 | // dirty way but... 80 | cmd.entity(item_entity).insert(*pt); 81 | } 82 | for (ui_node_entity, owner) in inventory_displays.iter() { 83 | if owner.actor == death.actor { 84 | cmd.entity(ui_node_entity).despawn_recursive(); 85 | } 86 | } 87 | // TODO: animated death 88 | // different animation based on negative percent of current hp 89 | bevy::log::info!("death to {} (id: {:?}) at {}", name, death.actor, pt); 90 | cmd.entity(death.actor).despawn_recursive(); 91 | } else { 92 | bevy::log::error!("Death to {:?}. But actor bedead not found.", death.actor); 93 | } 94 | } 95 | } 96 | 97 | pub fn try_move( 98 | mut actors: Query<(&mut Vector2D, &Team, &mut FieldOfView)>, 99 | mut move_reader: EventReader, 100 | mut ap_spend_writer: EventWriter, 101 | ) { 102 | let mut team_pt: HashMap<_, _> = actors.iter().map(|(p, t, _)| (**p, *t)).collect(); 103 | for e in move_reader.iter() { 104 | if let Ok((mut pt, _tt, mut fov)) = actors.get_mut(e.actor) { 105 | if let Some(_team) = team_pt.get(&e.to) { 106 | bevy::log::trace!( 107 | "trying to move from {} to {} by {:?}. location already ocupied", 108 | e.from, 109 | e.to, 110 | e.actor 111 | ); 112 | continue; 113 | } 114 | ap_spend_writer.send(SpendAPEvent::new(e.actor, AP_MOVE_COST_DEFAULT)); 115 | team_pt.entry(e.to).insert(e.team); 116 | *pt = Vector2D::from(e.to); 117 | fov.is_dirty = true; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/systems/actor_stats.rs: -------------------------------------------------------------------------------- 1 | use crate::components::*; 2 | use bevy::prelude::*; 3 | use bevy_inventory::{Equipment, ItemType}; 4 | use bevy_inventory_ui::UiTextInfo; 5 | use bevy_roguelike_combat::*; 6 | 7 | #[allow(clippy::type_complexity)] 8 | pub fn actors_fill_text_info( 9 | mut cmd: Commands, 10 | players: Query<&FieldOfView, With>, 11 | actors: Query<( 12 | Entity, 13 | &Name, 14 | &Team, 15 | &ActionPoints, 16 | &HitPoints, 17 | &StatsComputed, 18 | &Vector2D, 19 | Option<&UiTextInfo>, 20 | )>, 21 | ) { 22 | for player_fov in players.iter() { 23 | for (actor_entity, name, team, ap, hp, stats, pt, info) in actors.iter() { 24 | if player_fov.tiles_visible.iter().any(|t| *t == **pt) { 25 | let mut titles_descriptions = vec![]; 26 | titles_descriptions.push(("Team".to_string(), format!("{}", team.id()))); 27 | titles_descriptions.push(("Speed".to_string(), format!("{}", ap.increment()))); 28 | titles_descriptions.push(("Hit points".to_string(), hp.full().to_string())); 29 | titles_descriptions 30 | .push(("Attributes".to_string(), format!("{}", stats.attributes))); 31 | cmd.entity(actor_entity).insert(UiTextInfo { 32 | name: name.as_str().to_string(), 33 | titles_descriptions, 34 | }); 35 | } else if info.is_some() { 36 | cmd.entity(actor_entity).remove::(); 37 | } 38 | } 39 | } 40 | } 41 | 42 | #[allow(clippy::type_complexity)] 43 | pub fn attributes_update_field_of_view( 44 | mut cmd: Commands, 45 | mut actors: Query< 46 | ( 47 | Entity, 48 | &StatsComputed, 49 | &mut FieldOfView, 50 | ), 51 | With, 52 | >, 53 | ) { 54 | for (id, stats, mut fov) in actors.iter_mut() { 55 | fov.update(&stats.attributes); 56 | cmd.entity(id).remove::(); 57 | } 58 | } 59 | 60 | #[allow(clippy::type_complexity)] 61 | pub fn stats_recompute( 62 | mut cmd: Commands, 63 | mut actors: Query< 64 | ( 65 | Entity, 66 | &mut StatsComputed, 67 | &Attributes, 68 | &Protection, 69 | &Resistance, 70 | &Evasion, 71 | &DamageList, 72 | &Equipment, 73 | ), 74 | With, 75 | >, 76 | items_atr: Query<&Attributes, (With, Without)>, 77 | items_prt: Query< 78 | &Protection, 79 | (With, Without), 80 | >, 81 | items_res: Query<&Resistance, (With, Without)>, 82 | items_blk: Query<&Block, (With, Without)>, 83 | items_dmg: Query<&Damage, (With, Without)>, 84 | ) { 85 | for ( 86 | id, 87 | mut stats, 88 | innate_attributes, 89 | innate_protection, 90 | innate_resistance, 91 | evasion, 92 | unarmed_damage, 93 | equipment, 94 | ) in actors.iter_mut() 95 | { 96 | // NOTE: a lot of cloning, but hopefully not a common action to equip / unequip stuff 97 | stats.attributes = innate_attributes.clone() 98 | + equipment 99 | .list(&items_atr) 100 | .into_iter() 101 | .sum::>(); 102 | 103 | stats.protection = equipment 104 | .list(&items_prt) 105 | .iter() 106 | .fold(&mut Protection::default(), |acc, p| acc.extend(p)) 107 | .extend(innate_protection) 108 | .clone(); 109 | 110 | stats.resistance = equipment 111 | .list(&items_res) 112 | .iter() 113 | .fold(&mut Resistance::default(), |acc, p| acc.ingest(p)) 114 | .ingest(innate_resistance) 115 | .clone(); 116 | 117 | stats.evasion = evasion.clone(); 118 | stats.block = equipment.list(&items_blk); 119 | 120 | let mut damage = equipment.list(&items_dmg); 121 | if damage.is_empty() { 122 | damage.extend(unarmed_damage.list.clone()); 123 | } 124 | stats.damage = damage; 125 | 126 | cmd.entity(id).remove::().insert(( 127 | ActionPointsDirty {}, 128 | HitPointsDirty {}, 129 | FieldOfViewDirty {}, 130 | )); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/systems/camera.rs: -------------------------------------------------------------------------------- 1 | use crate::components::*; 2 | use crate::events::CameraFocusEvent; 3 | use crate::resources::*; 4 | use bevy::prelude::*; 5 | use bevy_tweening::lens::*; 6 | use bevy_tweening::*; 7 | use std::time::Duration; 8 | 9 | pub fn camera_set_focus_player( 10 | players: Query<&Vector2D, With>, 11 | mut map_info: ResMut, 12 | mut cmr_wrt: EventWriter, 13 | ) { 14 | for pt in players.iter() { 15 | if map_info.camera_focus != **pt { 16 | map_info.camera_focus = **pt; 17 | cmr_wrt.send(CameraFocusEvent::new(**pt)) 18 | } 19 | } 20 | } 21 | pub fn camera_focus_smooth( 22 | mut cmd: Commands, 23 | mut cmr_rdr: EventReader, 24 | cameras: Query<(Entity, &Transform), With>, 25 | map_options: Res, 26 | ) { 27 | for cfe in cmr_rdr.iter() { 28 | for (camera_entity, camera_transform) in cameras.iter() { 29 | cmd.entity(camera_entity).insert(Animator::new(Tween::new( 30 | EaseFunction::QuinticOut, 31 | Duration::from_millis(350), 32 | TransformPositionLens { 33 | start: camera_transform.translation, 34 | end: map_options 35 | .to_world_position(cfe.position) 36 | .extend(camera_transform.translation.z), 37 | }, 38 | ))); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/systems/fov.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | components::*, 3 | resources::{MapOptions, RogueMap}, 4 | }; 5 | use bevy::{prelude::*, utils::HashSet}; 6 | use bevy_roguelike_combat::{ActionPoints, HitPoints}; 7 | use line_drawing::{BresenhamCircle, Supercover}; 8 | use map_generator::*; 9 | 10 | #[allow(clippy::type_complexity)] 11 | pub fn field_of_view_set_visibility( 12 | players: Query<&FieldOfView, With>, 13 | mut visibles: Query<( 14 | &Vector2D, 15 | &Children, 16 | Option<&ActionPoints>, 17 | Option<&HitPoints>, 18 | )>, 19 | mut visible_children: Query<( 20 | &mut Sprite, 21 | &mut Transform, 22 | &mut Visibility, 23 | Option<&HudHealthBar>, 24 | )>, 25 | map_options: Res, 26 | ) { 27 | for fov in players.iter() { 28 | visibles.for_each_mut(|(pt, children, cp, hp)| { 29 | let is_revealed = fov.tiles_revealed.contains(pt); 30 | let is_visible = fov.tiles_visible.contains(pt); 31 | let is_ambient = cp.is_none(); 32 | let hp_percent = hp.map(|h| h.percent()).unwrap_or(1.); 33 | for c in children.iter() { 34 | if let Ok((mut s, mut t, mut v, h)) = visible_children.get_mut(*c) { 35 | let is_hud_hp = h.is_some(); 36 | v.is_visible = 37 | (hp_percent != 1. || !is_hud_hp) && is_visible || is_ambient && is_revealed; 38 | if !is_hud_hp { 39 | s.color = if is_visible && is_revealed { 40 | Color::WHITE 41 | } else { 42 | Color::rgb(0.65, 0.65, 0.65) 43 | }; 44 | } else { 45 | s.color.set_g(hp_percent); 46 | s.color.set_r(1. - hp_percent); 47 | 48 | if let Some(size) = s.custom_size { 49 | let x = map_options.tile_size * hp_percent; 50 | let slide = map_options.tile_size - x; 51 | s.custom_size = Some(Vec2::new(x, size.y)); 52 | t.translation.x = -slide / 2.; 53 | } 54 | } 55 | } 56 | } 57 | }); 58 | } 59 | } 60 | 61 | pub fn field_of_view_recompute( 62 | mut actors: Query<(&Vector2D, &mut FieldOfView)>, 63 | map: Res, 64 | ) { 65 | actors.par_for_each_mut(16, |(pt, mut fov)| { 66 | if !fov.is_dirty { 67 | return; 68 | } 69 | let visible_last = fov.tiles_visible.clone(); 70 | fov.tiles_visible = compute_fov(**pt, fov.radius, &map); 71 | let visible_current = fov.tiles_visible.clone(); 72 | fov.tiles_revealed.extend(visible_last); 73 | fov.tiles_revealed.extend(visible_current); 74 | fov.is_dirty = false; 75 | }); 76 | } 77 | 78 | fn compute_fov(pt: IVec2, radius: i32, map: &Map) -> HashSet { 79 | let mut fov = HashSet::default(); 80 | for (xo, yo) in BresenhamCircle::new(pt.x, pt.y, radius) { 81 | for vpt in Supercover::new((pt.x, pt.y), (xo, yo)) 82 | .map(|(x, y)| IVec2::new(x, y)) 83 | .filter(|p| map.is_in_bounds(*p)) 84 | { 85 | if map[vpt] == Tile::Wall { 86 | fov.insert(vpt); 87 | break; 88 | } 89 | fov.insert(vpt); 90 | } 91 | } 92 | fov 93 | } 94 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/systems/input.rs: -------------------------------------------------------------------------------- 1 | use crate::{components::*, events::*, resources::RogueMap}; 2 | use bevy::{ 3 | prelude::*, 4 | utils::{HashMap, HashSet}, 5 | }; 6 | use bevy_inventory::{Equipment, Inventory, ItemDropEvent, ItemPickUpEvent, ItemType}; 7 | use bevy_roguelike_combat::RogueRng; 8 | use line_drawing::WalkGrid; 9 | use map_generator::*; 10 | use rand::prelude::*; 11 | 12 | pub fn input_player( 13 | keys: Res>, 14 | players: Query<(Entity, &TurnState, &Inventory, &Equipment), With>, 15 | mut act_writer: EventWriter, 16 | mut pick_up_writer: EventWriter, 17 | mut drop_writer: EventWriter, 18 | ) { 19 | for (id, _, inv, eqv) in players 20 | .iter() 21 | .filter(|(_, ts, _, _)| **ts == TurnState::Act) 22 | { 23 | let delta = if keys.just_pressed(KeyCode::Up) { 24 | IVec2::new(0, 1) 25 | } else if keys.just_pressed(KeyCode::Down) { 26 | IVec2::new(0, -1) 27 | } else if keys.just_pressed(KeyCode::Left) { 28 | IVec2::new(-1, 0) 29 | } else if keys.just_pressed(KeyCode::Right) { 30 | IVec2::new(1, 0) 31 | } else if keys.just_pressed(KeyCode::Space) || keys.pressed(KeyCode::Z) { 32 | IVec2::new(0, 0) // stay put - skip turn 33 | } else if keys.just_pressed(KeyCode::Comma) { 34 | pick_up_writer.send(ItemPickUpEvent { picker: id }); 35 | IVec2::new(0, 0) // still stay put - skip turn 36 | } else if keys.just_pressed(KeyCode::D) { 37 | if let Some(ee) = inv.iter_some().last() { 38 | drop_writer.send(ItemDropEvent { 39 | droper: id, 40 | item: ee, 41 | }); 42 | } else if let Some((_, ee)) = eqv.iter_some().last() { 43 | drop_writer.send(ItemDropEvent { 44 | droper: id, 45 | item: ee, 46 | }); 47 | } 48 | return; 49 | } else { 50 | return; 51 | }; 52 | act_writer.send(ActEvent { id, delta }); 53 | } 54 | } 55 | 56 | #[allow(clippy::type_complexity)] 57 | pub fn input_fov_rand( 58 | mut rng: ResMut, 59 | actors: Query< 60 | ( 61 | Entity, 62 | &Vector2D, 63 | &Team, 64 | &TurnState, 65 | &FieldOfView, 66 | &Inventory, 67 | ), 68 | With, 69 | >, 70 | items: Query<&Vector2D, With>, 71 | actors_all: Query<(&Vector2D, &Team)>, 72 | mut act_writer: EventWriter, 73 | mut pick_up_writer: EventWriter, 74 | map: Res, 75 | ) { 76 | let team_pt: HashMap<_, _> = actors_all.iter().map(|(p, t)| (**p, *t)).collect(); 77 | let item_pt: HashSet<_> = items.iter().map(|p| **p).collect(); 78 | for (id, pt, team, _, fov, inv) in actors 79 | .iter() 80 | .filter(|(_, _, _, ts, _, _)| **ts == TurnState::Act) 81 | { 82 | let deltas = [ 83 | IVec2::new(0, 1), 84 | IVec2::new(0, -1), 85 | IVec2::new(-1, 0), 86 | IVec2::new(1, 0), 87 | IVec2::new(0, 0), // stay put - skip turn 88 | IVec2::new(0, 0), // stay put - skip turn 89 | IVec2::new(0, 0), // stay put - skip turn 90 | IVec2::new(0, 0), // stay put - skip turn 91 | IVec2::new(0, 0), 92 | ]; 93 | 94 | // NOTE: closest oposing team member search 95 | let mut distance_last = ((fov.radius + 1) * (fov.radius + 1)) as f32; 96 | let mut pt_move_target = None; 97 | for pt_visible in fov.tiles_visible.iter() { 98 | if let Some(other_team) = team_pt.get(pt_visible) { 99 | if *other_team != *team { 100 | let distance = pt_visible.as_vec2().distance_squared(pt.as_vec2()); 101 | if distance < distance_last { 102 | pt_move_target = Some(*pt_visible); 103 | distance_last = distance; 104 | } 105 | } 106 | } 107 | } 108 | // NOTE: moving towards an item 109 | let mut item_dest = false; 110 | if pt_move_target.is_none() && !inv.is_full() { 111 | distance_last = ((fov.radius + 1) * (fov.radius + 1)) as f32; 112 | for pt_visible in fov.tiles_visible.iter() { 113 | if let Some(pt) = item_pt.get(pt_visible) { 114 | let distance = pt_visible.as_vec2().distance_squared(pt.as_vec2()); 115 | if distance < distance_last { 116 | item_dest = true; 117 | pt_move_target = Some(*pt_visible); 118 | distance_last = distance; 119 | } 120 | } 121 | } 122 | } 123 | 124 | let mut delta = IVec2::new(0, 0); 125 | if let Some(tgt) = pt_move_target { 126 | if item_dest && distance_last < 1.1 { 127 | pick_up_writer.send(ItemPickUpEvent { picker: id }); 128 | } 129 | if let Some((x, y)) = WalkGrid::new((pt.x, pt.y), (tgt.x, tgt.y)).take(2).last() { 130 | let dest = IVec2::new(x, y); 131 | if map[dest] == Tile::Floor 132 | && !(team_pt.get(&dest).is_some() && *team_pt.get(&dest).unwrap() == *team) 133 | { 134 | delta = IVec2::new(x - pt.x, y - pt.y); 135 | } 136 | } 137 | } else { 138 | delta = deltas[rng.gen_range(0..deltas.len())]; 139 | } 140 | act_writer.send(ActEvent { id, delta }); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/systems/map.rs: -------------------------------------------------------------------------------- 1 | use crate::components::*; 2 | use crate::resources::MapOptions; 3 | use bevy::prelude::*; 4 | 5 | pub fn apply_position_to_transform( 6 | mut actors: Query<(&Vector2D, &mut Transform, Changed)>, 7 | map_options: Res, 8 | ) { 9 | for (pt, mut tr, _) in actors.iter_mut() { 10 | let z = tr.translation.z; 11 | tr.translation = map_options.to_world_position(**pt).extend(z); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/systems/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod action; 2 | pub mod actor_stats; 3 | pub mod camera; 4 | pub mod fov; 5 | pub mod input; 6 | pub mod inventory; 7 | pub mod map; 8 | pub mod render; 9 | pub mod turns; 10 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/systems/render.rs: -------------------------------------------------------------------------------- 1 | use crate::{components::*, resources::MapOptions}; 2 | use bevy::prelude::*; 3 | use bevy_inventory::{Equipment, ItemType}; 4 | use bevy_roguelike_combat::HitPoints; 5 | 6 | pub fn render_body( 7 | mut cmd: Commands, 8 | renderables: Query<(Entity, &Vector2D, &RenderInfo), Without>, 9 | map_options: Res, 10 | ) { 11 | for (rendity, pt, info) in renderables.iter() { 12 | cmd.entity(rendity) 13 | .insert(SpatialBundle { 14 | visibility: Visibility { is_visible: true }, 15 | transform: Transform::from_translation( 16 | map_options.to_world_position(**pt).extend(info.z), 17 | ), 18 | ..default() 19 | }) 20 | .with_children(|renderable| { 21 | renderable.spawn(( 22 | Name::new("render"), 23 | SpriteBundle { 24 | sprite: Sprite { 25 | color: Color::WHITE, 26 | custom_size: Some(Vec2::splat(map_options.tile_size)), 27 | ..Default::default() 28 | }, 29 | texture: info.texture.clone(), 30 | transform: Transform::from_xyz(0., 0., info.z + 0.1), 31 | ..Default::default() 32 | }, 33 | )); 34 | 35 | for cosmetic_texture in info.cosmetic_textures.iter() { 36 | renderable.spawn(( 37 | Name::new("render cosmetic"), 38 | SpriteBundle { 39 | sprite: Sprite { 40 | color: Color::WHITE, 41 | custom_size: Some(Vec2::splat(map_options.tile_size)), 42 | ..Default::default() 43 | }, 44 | texture: cosmetic_texture.clone(), 45 | transform: Transform::from_xyz(0., 0., info.z + 0.2), 46 | ..Default::default() 47 | }, 48 | )); 49 | } 50 | }); 51 | } 52 | } 53 | 54 | #[allow(clippy::type_complexity)] 55 | pub fn render_equiped_item( 56 | mut cmd: Commands, 57 | actors: Query<(Entity, &Vector2D), With>>, 58 | items: Query< 59 | (Entity, &RenderInfoEquiped, &ItemEquipedOwned), 60 | (With, Without), 61 | >, 62 | map_options: Res, 63 | ) { 64 | for (item_entity, info, owner) in items.iter() { 65 | if let Ok((_, _pt)) = actors.get(owner.actor) { 66 | let mut some_item_render_entity = None; 67 | cmd.entity(owner.actor).with_children(|renderable| { 68 | let item_render_entity = renderable 69 | .spawn(( 70 | Name::new("item render"), 71 | EquipedRenderedItem { item: item_entity }, 72 | SpriteBundle { 73 | sprite: Sprite { 74 | color: Color::WHITE, 75 | custom_size: Some(Vec2::splat(map_options.tile_size)), 76 | ..Default::default() 77 | }, 78 | texture: info.texture.clone(), 79 | transform: Transform::from_xyz(0., 0., info.z + 0.1), 80 | ..Default::default() 81 | }, 82 | )) 83 | .id(); 84 | some_item_render_entity = Some(item_render_entity); 85 | }); 86 | if let Some(item_render_entity) = some_item_render_entity { 87 | cmd.entity(item_entity) 88 | .insert(EquipedRendition { item_render_entity }); 89 | } 90 | } 91 | } 92 | } 93 | pub fn unrender_unequiped_items( 94 | mut cmd: Commands, 95 | items: Query<(Entity, &EquipedRendition), Without>, 96 | renditions: Query<&EquipedRenderedItem>, 97 | ) { 98 | for (item_entity, rendition) in items.iter() { 99 | cmd.entity(item_entity).remove::(); 100 | if renditions.get(rendition.item_render_entity).is_ok() { 101 | cmd.entity(rendition.item_render_entity).despawn_recursive(); 102 | } else { 103 | // TODO: close inventory when actor is dead; 104 | bevy::log::info!("item render entity not found. could not despawn."); 105 | } 106 | } 107 | } 108 | 109 | pub fn render_hud_health_bar( 110 | mut cmd: Commands, 111 | renderables: Query>, Without)>, 112 | map_options: Res, 113 | ) { 114 | for rendity in renderables.iter() { 115 | cmd.entity(rendity) 116 | .insert(HudHealthBar {}) 117 | .with_children(|renderable| { 118 | let height = map_options.tile_size / 16.; 119 | renderable.spawn(( 120 | Name::new("hud"), 121 | HudHealthBar {}, 122 | SpriteBundle { 123 | sprite: Sprite { 124 | color: Color::GREEN, 125 | custom_size: Some(Vec2::new(map_options.tile_size, height)), 126 | ..Default::default() 127 | }, 128 | transform: Transform::from_xyz( 129 | 0., 130 | -map_options.tile_size / 2. + height / 2., 131 | 100., 132 | ), 133 | ..Default::default() 134 | }, 135 | )); 136 | }); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /bevy_roguelike_plugin/src/systems/turns.rs: -------------------------------------------------------------------------------- 1 | use crate::components::*; 2 | use bevy::prelude::*; 3 | use bevy_roguelike_combat::*; 4 | 5 | // TODO: move to bevy_roguelike_turns? 6 | pub fn action_completed( 7 | mut actors: Query<(&mut TurnState, &mut HitPoints)>, 8 | mut action_completed_reader: EventReader, 9 | ) { 10 | for e in action_completed_reader.iter() { 11 | if let Ok((mut ts, mut hp)) = actors.get_mut(e.id) { 12 | *ts = TurnState::End; 13 | hp.regen(); 14 | } 15 | } 16 | } 17 | 18 | // TODO: move to bevy_roguelike_turns 19 | pub fn gather_action_points( 20 | mut actors: Query<(&mut ActionPoints, &mut TurnState)>, 21 | ) { 22 | actors.par_for_each_mut(16, |(mut ap, mut ts)| { 23 | if *ts == TurnState::Collect { 24 | *ts = if ap.current_add() > ap.turn_ready_to_act() { 25 | TurnState::Act 26 | } else { 27 | // NOTE: not yet ready to perform turn. 28 | // skip this turn. 29 | TurnState::End 30 | }; 31 | } 32 | }); 33 | } 34 | // TODO: move to bevy_roguelike_turns 35 | pub fn turn_end_now_gather(mut actors: Query<&mut TurnState>) { 36 | if actors.iter().all(|ts| *ts == TurnState::End) { 37 | actors.par_for_each_mut(16, |mut ts| { 38 | *ts = TurnState::Collect; 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /credits/CREDITS.md: -------------------------------------------------------------------------------- 1 | Credits 2 | ======= 3 | 4 | - [Hands-on Rust](https://pragprog.com/titles/hwrust/hands-on-rust/) book 5 | - [Bevy Minesweeper](https://dev.to/qongzi/bevy-minesweeper-introduction-4l7f) tutorial series 6 | - [Dungeon Crawl Stone Soup](https://opengameart.org/content/dungeon-crawl-32x32-tiles-supplemental) tiles 7 | - [Fantasy tileset](https://opengameart.org/content/32x32-fantasy-tileset) modified to create inventory theme 8 | - web build and github deploy from [bevy game template](https://github.com/NiklasEi/bevy_game_template) 9 | - [The Little Book of Rust Macros](https://veykril.github.io/tlborm/) 10 | - [Procedural Macros tutorial series](https://blog.turbo.fish/proc-macro-basics/) 11 | - Brother rabbit [Tomas Dambrauskas](https://github.com/tomuxmon) 12 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/example.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bevy Roguelike 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /inventory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomuxmon/bevy_roguelike/1c8ca74dbc53f3b44f8160e3e5deddd78737c25a/inventory.png -------------------------------------------------------------------------------- /map_generator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "map_generator" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | rand = "~0.8" 8 | glam = "~0.22" 9 | line_drawing = "~1.0" 10 | -------------------------------------------------------------------------------- /map_generator/src/drunkard.rs: -------------------------------------------------------------------------------- 1 | use super::prelude::*; 2 | 3 | const DEFAULT_WALK_RATIO: f32 = 0.015; 4 | const DEFAULT_FLOOR_RATIO: f32 = 0.6; 5 | 6 | pub struct DrunkardGenerator { 7 | pub walk_ratio: f32, 8 | pub floor_ratio: f32, 9 | } 10 | 11 | impl Default for DrunkardGenerator { 12 | fn default() -> Self { 13 | Self { 14 | walk_ratio: DEFAULT_WALK_RATIO, 15 | floor_ratio: DEFAULT_FLOOR_RATIO, 16 | } 17 | } 18 | } 19 | 20 | impl MapGenerator for DrunkardGenerator { 21 | fn gen(&self, rng: &mut StdRng, size: IVec2) -> Map { 22 | let mut map = Map::filled_with(size, Tile::Wall); 23 | let mut room_centers = Vec::new(); 24 | let walk_steps = ((size.x * size.y) as f32 * self.walk_ratio) as usize; 25 | let desired_floor = ((size.x * size.y) as f32 * self.floor_ratio) as usize; 26 | 27 | while map.iter().filter(|t| **t == Tile::Floor).count() < desired_floor { 28 | let mut from = IVec2::new( 29 | rng.gen_range(1..map.size().x - 1), 30 | rng.gen_range(1..map.size().y - 1), 31 | ); 32 | room_centers.push(from); 33 | let stagger_count = usize::max(walk_steps / 10, 5); 34 | for _ in 0..stagger_count { 35 | from = walk(from, walk_steps, rng, &mut map); 36 | } 37 | } 38 | 39 | map 40 | } 41 | } 42 | 43 | fn walk(from: IVec2, max_step: usize, rng: &mut StdRng, map: &mut Map) -> IVec2 { 44 | let mut pt = from; 45 | let mut last_valid_pt = from; 46 | let mut step = 0; 47 | loop { 48 | // Carve it! 49 | // TODO: carved floor counter to eliminate .. Tile::Floor).count() 50 | let tile = &mut map[pt]; 51 | *tile = Tile::Floor; 52 | 53 | if rng.gen_range(0..max_step) < max_step / 3 { 54 | last_valid_pt = pt; 55 | } 56 | 57 | let deltas = Map::get_wasd_neighbor_deltas(); 58 | let delta = deltas[rng.gen_range(0..deltas.len())]; 59 | pt += delta; 60 | 61 | step += 1; 62 | if map.is_edge(pt) || !map.is_in_bounds(pt) || step > max_step { 63 | return last_valid_pt; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /map_generator/src/empty.rs: -------------------------------------------------------------------------------- 1 | use super::prelude::*; 2 | 3 | pub struct EmptyGenerator {} 4 | 5 | impl MapGenerator for EmptyGenerator { 6 | fn gen(&self, _rng: &mut StdRng, size: IVec2) -> Map { 7 | let mut map = Map::filled_with(size, Tile::Floor); 8 | for p in map.get_edge() { 9 | let t = &mut map[p]; 10 | *t = Tile::Wall; 11 | } 12 | map 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /map_generator/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod drunkard; 2 | mod empty; 3 | mod life; 4 | mod map; 5 | mod rect; 6 | mod rooms; 7 | 8 | mod prelude { 9 | pub use super::map::Map; 10 | pub use super::map::Tile; 11 | pub use super::rect::Rect; 12 | pub use super::MapGenerator; 13 | pub use glam::IVec2; 14 | pub use line_drawing::WalkGrid; 15 | pub use rand::prelude::*; 16 | } 17 | 18 | pub use drunkard::DrunkardGenerator; 19 | pub use empty::EmptyGenerator; 20 | pub use life::ConwayLifeGenerator; 21 | pub use map::Map; 22 | pub use map::Tile; 23 | pub use rooms::RoomsGenerator; 24 | 25 | use prelude::*; 26 | 27 | pub trait MapGenerator { 28 | fn gen(&self, rng: &mut StdRng, size: IVec2) -> Map; 29 | } 30 | 31 | // TODO: implement possibility to do map generation composition (mix multiple generators) 32 | // TODO: implement space straversal and room detector 33 | // TODO: dijkstra plotting to determine distinct not connected rooms 34 | // TODO: dijkstra plotting to place enemies, players at positions relative to wall sides 35 | // TODO: dijkstra plotting to identify 1 tile wide tunnels 36 | 37 | pub struct RandomMapGenerator {} 38 | 39 | impl MapGenerator for RandomMapGenerator { 40 | fn gen(&self, rng: &mut StdRng, size: IVec2) -> Map { 41 | let generator: Box = match rng.gen_range(1..4) { 42 | 0 => Box::::default(), 43 | 1 => Box::::default(), 44 | 2 => Box::::default(), 45 | _ => Box::new(EmptyGenerator {}), 46 | }; 47 | generator.gen(rng, size) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /map_generator/src/life.rs: -------------------------------------------------------------------------------- 1 | use super::prelude::*; 2 | 3 | const DEFAULT_ITER_COUNT: usize = 9; 4 | 5 | pub struct ConwayLifeGenerator { 6 | iter_count: usize, 7 | } 8 | 9 | impl Default for ConwayLifeGenerator { 10 | fn default() -> Self { 11 | Self { 12 | iter_count: DEFAULT_ITER_COUNT, 13 | } 14 | } 15 | } 16 | 17 | impl MapGenerator for ConwayLifeGenerator { 18 | fn gen(&self, rng: &mut StdRng, size: IVec2) -> Map { 19 | let mut map = Map::random_noise(size, rng); 20 | for _ in 0..=self.iter_count { 21 | iteration(&mut map); 22 | } 23 | for p in map.get_edge() { 24 | let t = &mut map[p]; 25 | *t = Tile::Wall; 26 | } 27 | map 28 | } 29 | } 30 | 31 | fn iteration(map: &mut Map) { 32 | let map_clone = map.clone(); 33 | for y in 1..map_clone.size().y - 1 { 34 | for x in 1..map_clone.size().x - 1 { 35 | let pt = IVec2::new(x, y); 36 | let neighbors = count_neighbors(&map_clone, pt); 37 | let t = &mut map[pt]; 38 | *t = if neighbors > 4 || neighbors == 0 { 39 | Tile::Floor 40 | } else { 41 | Tile::Wall 42 | }; 43 | } 44 | } 45 | } 46 | 47 | fn count_neighbors(map: &Map, pt: IVec2) -> usize { 48 | Map::get_neighbor_deltas() 49 | .map(|nb| pt + nb) 50 | .iter() 51 | .filter(|nb| map.is_in_bounds(**nb) && map[**nb] == Tile::Wall) 52 | .count() 53 | } 54 | -------------------------------------------------------------------------------- /map_generator/src/map.rs: -------------------------------------------------------------------------------- 1 | use glam::IVec2; 2 | use rand::prelude::*; 3 | use std::ops::{Index, IndexMut}; 4 | use std::slice::Iter; 5 | 6 | /// Enum describing a map tile 7 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 8 | pub enum Tile { 9 | Wall, 10 | Floor, 11 | } 12 | 13 | // TODO: inject Resource? have a trait for map instead 14 | /// Flat tile map of tiles 15 | #[derive(Debug, Clone)] 16 | pub struct Map { 17 | size: IVec2, 18 | tiles: Vec, 19 | } 20 | 21 | impl Map { 22 | pub(crate) fn filled_with(size: IVec2, tile: Tile) -> Self { 23 | Self { 24 | size, 25 | tiles: vec![tile; (size.x * size.y) as usize], 26 | } 27 | } 28 | 29 | pub(crate) fn random_noise(size: IVec2, rng: &mut StdRng) -> Self { 30 | let mut tiles = Vec::new(); 31 | for _ in 0..size.x * size.y { 32 | let roll = rng.gen_range(0..=100); 33 | tiles.push(if roll > 55 { Tile::Floor } else { Tile::Wall }) 34 | } 35 | Self { size, tiles } 36 | } 37 | 38 | pub fn is_in_bounds(&self, pt: IVec2) -> bool { 39 | self.size.x > pt.x && self.size.y > pt.y && pt.x >= 0 && pt.y >= 0 40 | } 41 | 42 | pub fn is_edge(&self, pt: IVec2) -> bool { 43 | pt.x == 0 || pt.y == 0 || pt.x == self.size.x - 1 || pt.y == self.size.y - 1 44 | } 45 | 46 | /// itterates over underlying tiles vector 47 | pub fn iter(&self) -> Iter { 48 | self.tiles.iter() 49 | } 50 | /// enumerates tiles and positions of each tile 51 | pub fn enumerate(&self) -> impl Iterator { 52 | self.tiles 53 | .iter() 54 | .enumerate() 55 | .map(move |(idx, t)| (self.get_point(idx), t)) 56 | } 57 | 58 | pub fn get_edge(&self) -> Vec { 59 | let mut v = Vec::new(); 60 | for x in 0..self.size.x { 61 | v.push(IVec2::new(x, 0)); 62 | v.push(IVec2::new(x, self.size.y - 1)); 63 | } 64 | for y in 1..self.size.y - 1 { 65 | v.push(IVec2::new(0, y)); 66 | v.push(IVec2::new(self.size.x - 1, y)); 67 | } 68 | v 69 | } 70 | 71 | /// returns slice at y level 72 | pub fn get_slice(&self, y: i32) -> &[Tile] { 73 | let start = self.get_index(IVec2::new(0, y)); 74 | let end = start + self.size.x as usize; 75 | &self.tiles[start..end] 76 | } 77 | 78 | /// zero based indexing 79 | fn get_index(&self, pt: IVec2) -> usize { 80 | (pt.y * (self.size.x) + pt.x) as usize 81 | } 82 | fn get_point(&self, idx: usize) -> IVec2 { 83 | IVec2::new( 84 | (idx % self.size.x as usize) as i32, 85 | (idx / self.size.x as usize) as i32, 86 | ) 87 | } 88 | 89 | /// returns neighboring points (deltas): 90 | /// 91 | /// | | | | 92 | /// | --- | --- | --- | 93 | /// |(-1, 1)|(0, 1)|(1, 1)| 94 | /// |(-1, 0)|tile|(1, 0)| 95 | /// |(-1, -1)|(0, -1)|(1, -1)| 96 | pub fn get_neighbor_deltas() -> [IVec2; 8] { 97 | // TODO: should be static or const (not fn) but still be vectors :\ 98 | [ 99 | IVec2::new(-1, 1), 100 | IVec2::new(0, 1), 101 | IVec2::new(1, 1), 102 | IVec2::new(-1, 0), 103 | IVec2::new(1, 0), 104 | IVec2::new(-1, -1), 105 | IVec2::new(0, -1), 106 | IVec2::new(1, -1), 107 | ] 108 | } 109 | 110 | pub fn get_wasd_neighbor_deltas() -> [IVec2; 4] { 111 | // TODO: should be static or const (not fn) but still be vectors :\ 112 | [ 113 | IVec2::new(0, 1), 114 | IVec2::new(-1, 0), 115 | IVec2::new(1, 0), 116 | IVec2::new(0, -1), 117 | ] 118 | } 119 | pub fn size(&self) -> IVec2 { 120 | self.size 121 | } 122 | } 123 | impl Index for Map { 124 | type Output = Tile; 125 | 126 | fn index(&self, pt: IVec2) -> &Self::Output { 127 | let idx = self.get_index(pt); 128 | &self.tiles[idx] 129 | } 130 | } 131 | impl IndexMut for Map { 132 | fn index_mut(&mut self, pt: IVec2) -> &mut Self::Output { 133 | let idx = self.get_index(pt); 134 | &mut self.tiles[idx] 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /map_generator/src/rect.rs: -------------------------------------------------------------------------------- 1 | use glam::IVec2; 2 | 3 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 4 | pub struct Rect { 5 | pub start: IVec2, 6 | pub size: IVec2, 7 | } 8 | 9 | impl Rect { 10 | pub fn new(start: IVec2, size: IVec2) -> Self { 11 | Self { start, size } 12 | } 13 | 14 | pub fn get_center(&self) -> IVec2 { 15 | IVec2::new( 16 | self.start.x + self.size.x / 2, 17 | self.start.y + self.size.y / 2, 18 | ) 19 | } 20 | 21 | pub fn intersect_or_touch(&self, rhs: Rect) -> bool { 22 | self.start.x <= rhs.start.x + rhs.size.x 23 | && self.start.x + self.size.x >= rhs.start.x 24 | && self.start.y <= rhs.start.y + rhs.size.y 25 | && self.start.y + self.size.y >= rhs.start.y 26 | } 27 | 28 | pub fn for_each(&self, mut f: F) 29 | where 30 | F: FnMut(IVec2), 31 | { 32 | for y in self.start.y..self.start.y + self.size.y { 33 | for x in self.start.x..self.start.x + self.size.x { 34 | f(IVec2::new(x, y)); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /map_generator/src/rooms.rs: -------------------------------------------------------------------------------- 1 | use super::prelude::*; 2 | 3 | const DEFAULT_R0OM_SIZE_RATIO: f32 = 0.19; 4 | const DEFAULT_ROOM_COUNT_RATIO: f32 = 0.017; 5 | 6 | pub struct RoomsGenerator { 7 | pub room_size_ratio: f32, 8 | pub room_count_ratio: f32, 9 | } 10 | impl Default for RoomsGenerator { 11 | fn default() -> Self { 12 | Self { 13 | room_size_ratio: DEFAULT_R0OM_SIZE_RATIO, 14 | room_count_ratio: DEFAULT_ROOM_COUNT_RATIO, 15 | } 16 | } 17 | } 18 | 19 | impl MapGenerator for RoomsGenerator { 20 | fn gen(&self, rng: &mut StdRng, size: IVec2) -> Map { 21 | let mut map = Map::filled_with(size, Tile::Wall); 22 | let room_size_max = (size.as_vec2() * self.room_size_ratio).as_ivec2(); 23 | let room_count = ((size.x * size.y) as f32 * self.room_count_ratio) as usize; 24 | 25 | let rooms = build_rooms(rng, size, room_size_max, room_count); 26 | for room in rooms.iter() { 27 | room.for_each(|pt| { 28 | if map.is_in_bounds(pt) { 29 | let tile = &mut map[pt]; 30 | *tile = Tile::Floor; 31 | } 32 | }); 33 | } 34 | carve_coriddors(&mut map, rooms); 35 | 36 | map 37 | } 38 | } 39 | 40 | fn carve_coriddors(map: &mut Map, rooms: Vec) { 41 | // NOTE: huh.. we can do that in rust? 42 | let mut rooms = rooms; 43 | rooms.sort_by(|a, b| a.get_center().y.cmp(&b.get_center().y)); 44 | for (i, room) in rooms.iter().enumerate().skip(1) { 45 | let prev = rooms[i - 1].get_center(); 46 | let new = room.get_center(); 47 | for pt in WalkGrid::new((prev.x, prev.y), (new.x, new.y)).map(|(x, y)| IVec2::new(x, y)) { 48 | let tile = &mut map[pt]; 49 | *tile = Tile::Floor; 50 | } 51 | } 52 | } 53 | 54 | fn build_rooms( 55 | rng: &mut StdRng, 56 | map_size: IVec2, 57 | room_size_max: IVec2, 58 | room_count: usize, 59 | ) -> Vec { 60 | let mut rooms: Vec = Vec::new(); 61 | let mut i = 10000; 62 | while rooms.len() < room_count && i > 1 { 63 | i -= 1; 64 | let room_size = IVec2::new( 65 | rng.gen_range(i32::max(room_size_max.x / 4, 3)..i32::max(room_size_max.x, 5)), 66 | rng.gen_range(i32::max(room_size_max.y / 4, 3)..i32::max(room_size_max.y, 5)), 67 | ); 68 | let room = Rect::new( 69 | IVec2::new( 70 | rng.gen_range(1..map_size.x - room_size.x), 71 | rng.gen_range(1..map_size.y - room_size.y), 72 | ), 73 | room_size, 74 | ); 75 | let mut overlap = false; 76 | for r in rooms.iter() { 77 | if r.intersect_or_touch(room) { 78 | overlap = true; 79 | } 80 | } 81 | if !overlap { 82 | rooms.push(room); 83 | } 84 | } 85 | rooms 86 | } 87 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy_inventory_ui::InventoryDisplayOptions; 3 | use bevy_roguelike_plugin::{resources::*, RoguelikePlugin, StateNext}; 4 | 5 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 6 | pub enum AppState { 7 | Setup, 8 | AssetLoad, 9 | Construct, 10 | InGame, 11 | Pause, 12 | Reseting, 13 | } 14 | 15 | impl StateNext for AppState { 16 | fn next(&self) -> Option { 17 | match self { 18 | AppState::Setup => Some(AppState::AssetLoad), 19 | AppState::AssetLoad => Some(AppState::Construct), 20 | AppState::Construct => Some(AppState::InGame), 21 | AppState::InGame => Some(AppState::Pause), 22 | AppState::Pause => Some(AppState::InGame), 23 | AppState::Reseting => Some(AppState::Construct), 24 | } 25 | } 26 | } 27 | 28 | fn main() { 29 | let mut app = App::new(); 30 | app.add_state_to_stage(CoreStage::First, AppState::Setup) 31 | .add_state_to_stage(CoreStage::PreUpdate, AppState::Setup) 32 | .add_state_to_stage(CoreStage::Update, AppState::Setup) 33 | .add_state_to_stage(CoreStage::PostUpdate, AppState::Setup) 34 | .add_state_to_stage(CoreStage::Last, AppState::Setup) 35 | .insert_resource(ClearColor(Color::BLACK)) 36 | .add_plugins( 37 | DefaultPlugins 38 | .set(WindowPlugin { 39 | window: WindowDescriptor { 40 | title: "rogue bevy".to_string(), 41 | width: 1000., 42 | height: 600., 43 | ..Default::default() 44 | }, 45 | ..default() 46 | }) 47 | .set(ImagePlugin::default_nearest()), 48 | ) 49 | .add_plugin(RoguelikePlugin { 50 | state_asset_load: AppState::AssetLoad, 51 | state_construct: AppState::Construct, 52 | state_running: AppState::InGame, 53 | }) 54 | .add_startup_system(set_options); 55 | 56 | #[cfg(feature = "debug")] 57 | app.add_plugin(bevy_inspector_egui::WorldInspectorPlugin::new()); 58 | 59 | app.run(); 60 | } 61 | 62 | fn set_options(mut cmd: Commands) { 63 | cmd.insert_resource(MapOptions { 64 | map_size: IVec2::new(80, 50), 65 | tile_size: 32.0, 66 | }); 67 | cmd.insert_resource(InventoryDisplayOptions { tile_size: 32.0 }) 68 | } 69 | -------------------------------------------------------------------------------- /vec_walk_dir/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vec_walk_dir" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | proc-macro = true 10 | 11 | [dependencies] 12 | walkdir = "2" 13 | proc-macro2 = "1" 14 | syn = "1" 15 | quote = "1" 16 | -------------------------------------------------------------------------------- /vec_walk_dir/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use vec_walk_dir::expand_vec_walk_dir; 3 | 4 | mod vec_walk_dir; 5 | 6 | #[proc_macro] 7 | pub fn vec_walk_dir(input: TokenStream) -> TokenStream { 8 | let input = syn::parse_macro_input!(input as syn::LitStr); 9 | expand_vec_walk_dir(input) 10 | .unwrap_or_else(syn::Error::into_compile_error) 11 | .into() 12 | } 13 | -------------------------------------------------------------------------------- /vec_walk_dir/src/vec_walk_dir.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | use walkdir::{DirEntry, WalkDir}; 4 | 5 | pub fn expand_vec_walk_dir(input: syn::LitStr) -> syn::Result { 6 | let root = input.value(); 7 | 8 | let mut intermediate = Vec::new(); 9 | for path in WalkDir::new(root.clone()) 10 | .into_iter() 11 | .filter_entry(|e| !is_hidden(e)) 12 | .filter_map(|e| e.ok()) 13 | .filter_map(|e| { 14 | if e.metadata().map(|m| m.is_file()).unwrap_or(false) { 15 | Some(e.into_path()) 16 | } else { 17 | None 18 | } 19 | }) 20 | { 21 | if let Ok(path) = path.strip_prefix(root.clone()) { 22 | if let Some(str) = path.to_str() { 23 | let lit = proc_macro2::Literal::string(str); 24 | intermediate.push(quote! { v.push(#lit); }); 25 | } 26 | } 27 | } 28 | let intermediate = intermediate.into_iter().collect::(); 29 | 30 | Ok(quote! { 31 | { 32 | let mut v = Vec::new(); 33 | #intermediate 34 | v 35 | } 36 | }) 37 | } 38 | 39 | fn is_hidden(entry: &DirEntry) -> bool { 40 | entry 41 | .file_name() 42 | .to_str() 43 | .map(|s| s.starts_with('.')) 44 | .unwrap_or(false) 45 | } 46 | -------------------------------------------------------------------------------- /web/sounds.js: -------------------------------------------------------------------------------- 1 | // Insert hack to make sound autoplay on Chrome as soon as the user interacts with the tab: 2 | // https://developers.google.com/web/updates/2018/11/web-audio-autoplay#moving-forward 3 | 4 | // the following function keeps track of all AudioContexts and resumes them on the first user 5 | // interaction with the page. If the function is called and all contexts are already running, 6 | // it will remove itself from all event listeners. 7 | (function () { 8 | // An array of all contexts to resume on the page 9 | const audioContextList = []; 10 | 11 | // An array of various user interaction events we should listen for 12 | const userInputEventNames = [ 13 | "click", 14 | "contextmenu", 15 | "auxclick", 16 | "dblclick", 17 | "mousedown", 18 | "mouseup", 19 | "pointerup", 20 | "touchend", 21 | "keydown", 22 | "keyup", 23 | ]; 24 | 25 | // A proxy object to intercept AudioContexts and 26 | // add them to the array for tracking and resuming later 27 | self.AudioContext = new Proxy(self.AudioContext, { 28 | construct(target, args) { 29 | const result = new target(...args); 30 | audioContextList.push(result); 31 | return result; 32 | }, 33 | }); 34 | 35 | // To resume all AudioContexts being tracked 36 | function resumeAllContexts(_event) { 37 | let count = 0; 38 | 39 | audioContextList.forEach((context) => { 40 | if (context.state !== "running") { 41 | context.resume(); 42 | } else { 43 | count++; 44 | } 45 | }); 46 | 47 | // If all the AudioContexts have now resumed then we unbind all 48 | // the event listeners from the page to prevent unnecessary resume attempts 49 | // Checking count > 0 ensures that the user interaction happens AFTER the game started up 50 | if (count > 0 && count === audioContextList.length) { 51 | userInputEventNames.forEach((eventName) => { 52 | document.removeEventListener(eventName, resumeAllContexts); 53 | }); 54 | } 55 | } 56 | 57 | // We bind the resume function for each user interaction 58 | // event on the page 59 | userInputEventNames.forEach((eventName) => { 60 | document.addEventListener(eventName, resumeAllContexts); 61 | }); 62 | })(); 63 | -------------------------------------------------------------------------------- /web/styles.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | } 4 | 5 | body { 6 | min-height: 100%; 7 | background-color: black; 8 | margin: 0; 9 | display: flex; 10 | justify-content: center; 11 | align-items: center; 12 | } 13 | 14 | canvas { 15 | background-color: white; 16 | } --------------------------------------------------------------------------------