diff --git a/.github/workflows/cargo-build-test.yml b/.github/workflows/cargo-build-test.yml index cafaa58..d54fa2f 100644 --- a/.github/workflows/cargo-build-test.yml +++ b/.github/workflows/cargo-build-test.yml @@ -12,7 +12,7 @@ env: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/Cargo.toml b/Cargo.toml index 55d7645..24b6918 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rust-rl" -version = "0.1.4" +version = "0.1.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/README.md b/README.md index c1809cf..9127091 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,18 @@ #### using _rltk/bracket-lib_, and _specs_ -[![Rust](https://github.com/Llywelwyn/rust-rl/actions/workflows/cargo-build-test.yml/badge.svg)](https://github.com/Llywelwyn/rust-rl/actions/workflows/cargo-build-test.yml) - -check out the page in the header for the wasm version, pick [a release](https://github.com/Llywelwyn/rust-rl/releases), or build manually with: +check out the page in the header for the wasm version, pick [a release of your choice](https://github.com/Llywelwyn/rust-rl/releases), or build manually with: `git clone https://github.com/Llywelwyn/rust-rl/ && cd rust-rl && cargo build --release`, ![image](https://github.com/Llywelwyn/rust-rl/assets/82828093/b05e4f0b-2062-4abe-9fee-c679f9ef420d) +this year for roguelikedev does the complete tutorial, i followed along with thebracket's [_roguelike tutorial - in rust_](https://bfnightly.bracketproductions.com). the notes i made during the sprint are being kept below for posterity - further changes since then are noted in [changelog.txt](https://github.com/Llywelwyn/rust-rl/blob/9150ed39e45bee536060cdc769d274e639021012/changelog.txt), and in the release notes. + +i'm also working on translating over my progress into blog entries on my site @ [llyw.co.uk](https://llyw.co.uk/), with a larger focus on some of the more interesting implementation details. + --- -
- boring details about the sprint where this project started
week 1 @@ -157,4 +157,3 @@ check out the page in the header for the wasm version, pick [a release](https:// ![squares](https://github.com/Llywelwyn/rust-rl/assets/82828093/b752e1cb-340d-475d-84ae-68fdb4977a80)
-
diff --git a/docs/combat_system.txt b/docs/combat_system.txt index 44e2f65..13a780b 100644 --- a/docs/combat_system.txt +++ b/docs/combat_system.txt @@ -49,7 +49,3 @@ Complex example, with negative AC: bloodstains: if starts on bloodied tile, remove blood + heal, gain xp, grow (little dog -> dog), etc. - You have negative AC, so you roll 1d14 for damage reduction, and get an 8. - The total damage is 6 - 8 = -2, but damage can't be negative, so you take 1 point of damage. - -tl;dr -1. Lower AC is better -2. Aim for 0 AC - it's an important breakpoint. Every point of AC before 0 counts for a lot. diff --git a/raws/items.json b/raws/items.json index 7599afe..2c0c678 100644 --- a/raws/items.json +++ b/raws/items.json @@ -3,10 +3,9 @@ "id": "potion_health", "name": { "name": "potion of health", "plural": "potions of health" }, "renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 2 }, - "class": "potion", "weight": 1, "value": 50, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "heal": "4d4+2" }, "magic": { "class": "uncommon", "naming": "potion" } }, @@ -14,10 +13,9 @@ "id": "potion_health_weak", "name": { "name": "potion of lesser health", "plural": "potions of lesser health" }, "renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 2 }, - "class": "potion", "weight": 1, "value": 25, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "heal": "2d4+2" }, "magic": { "class": "uncommon", "naming": "potion" } }, @@ -25,30 +23,27 @@ "id": "scroll_identify", "name": { "name": "scroll of identify", "plural": "scrolls of identify" }, "renderable": { "glyph": "?", "fg": "#0FFFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", "weight": 0.5, "value": 100, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE", "IDENTIFY"], + "flags": ["CONSUMABLE", "DESTRUCTIBLE", "IDENTIFY"], "magic": { "class": "uncommon", "naming": "scroll" } }, { "id": "scroll_removecurse", "name": { "name": "scroll of remove curse", "plural": "scrolls of remove curse" }, "renderable": { "glyph": "?", "fg": "#0FFFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", "weight": 0.5, "value": 200, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE", "REMOVE_CURSE"], + "flags": ["CONSUMABLE", "DESTRUCTIBLE", "REMOVE_CURSE"], "magic": { "class": "rare", "naming": "scroll" } }, { "id": "scroll_health", "name": { "name": "scroll of healing word", "plural": "scrolls of healing word" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", "weight": 0.5, "value": 50, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "particle_line": "*;-;#53f06d;75.0;#f9ff9f;100.0", "ranged": "12", "heal": "1d4+2" }, "magic": { "class": "uncommon", "naming": "scroll" } }, @@ -56,10 +51,9 @@ "id": "scroll_mass_health", "name": { "name": "scroll of mass healing word", "plural": "scrolls of mass healing word" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", "weight": 0.5, "value": 200, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "particle": "*;#53f06d;200.0", "ranged": "12", "aoe": "3", "heal": "1d4+2" }, "magic": { "class": "rare", "naming": "scroll" } }, @@ -67,10 +61,9 @@ "id": "scroll_magicmissile", "name": { "name": "scroll of magic missile", "plural": "scrolls of magic missile" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", "weight": 0.5, "value": 50, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "particle_line": "*;-;#00b7ff;75.0;#f4fc83;100.0", "ranged": "12", "damage": "3d4+3;magic" }, "magic": { "class": "uncommon", "naming": "scroll" } }, @@ -78,10 +71,9 @@ "id": "scroll_embers", "name": { "name": "scroll of embers", "plural": "scrolls of embers" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", "weight": 0.5, "value": 100, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "particle": "*;#FFA500;200.0", "ranged": "10", "damage": "4d6;fire", "aoe": "2" }, "magic": { "class": "uncommon", "naming": "scroll" } }, @@ -89,10 +81,9 @@ "id": "scroll_fireball", "name": { "name": "scroll of fireball", "plural": "scrolls of fireball" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", "weight": 0.5, "value": 200, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "particle_burst": "▓;*;~;#FFA500;#000000;500.0;#ffd381;60.0", "ranged": "10", @@ -105,10 +96,9 @@ "id": "scroll_confusion", "name": { "name": "scroll of confusion", "plural": "scrolls of confusion" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", "weight": 0.5, "value": 100, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "particle_line": "*;-;#ad56a6;75.0;#cacaca;100.0", "ranged": "10", "confusion": "4" }, "magic": { "class": "uncommon", "naming": "scroll" } }, @@ -116,10 +106,9 @@ "id": "scroll_mass_confusion", "name": { "name": "scroll of mass confusion", "plural": "scrolls of mass confusion" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", "weight": 0.5, "value": 200, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "particle": "*;#ad56a6;200.0", "ranged": "10", "aoe": "3", "confusion": "3" }, "magic": { "class": "veryrare", "naming": "scroll" } }, @@ -127,10 +116,9 @@ "id": "scroll_magicmap", "name": { "name": "scroll of magic mapping", "plural": "scrolls of magic mapping" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", "weight": 0.5, "value": 50, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE", "MAGICMAP"], + "flags": ["CONSUMABLE", "DESTRUCTIBLE", "MAGICMAP"], "effects": {}, "magic": { "class": "common", "naming": "scroll" } }, @@ -138,7 +126,6 @@ "id": "equip_dagger", "name": { "name": "dagger", "plural": "daggers" }, "renderable": { "glyph": ")", "fg": "#808080", "bg": "#000000", "order": 2 }, - "class": "weapon", "weight": 1, "value": 2, "flags": ["EQUIP_MELEE"], @@ -148,7 +135,6 @@ "id": "equip_shortsword", "name": { "name": "shortsword", "plural": "shortswords" }, "renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, - "class": "weapon", "weight": 2, "value": 10, "flags": ["EQUIP_MELEE"], @@ -158,7 +144,6 @@ "id": "equip_rapier", "name": { "name": "rapier", "plural": "rapiers" }, "renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, - "class": "weapon", "weight": 2, "value": 10, "flags": ["EQUIP_MELEE"], @@ -168,7 +153,6 @@ "id": "equip_pitchfork", "name": { "name": "pitchfork", "plural": "pitchforks" }, "renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, - "class": "weapon", "weight": 2, "value": 5, "flags": ["EQUIP_MELEE"], @@ -178,7 +162,6 @@ "id": "equip_sickle", "name": { "name": "sickle", "plural": "sickles" }, "renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, - "class": "weapon", "weight": 2, "value": 5, "flags": ["EQUIP_MELEE"], @@ -188,7 +171,6 @@ "id": "equip_handaxe", "name": { "name": "handaxe", "plural": "handaxes" }, "renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, - "class": "weapon", "weight": 2, "value": 5, "flags": ["EQUIP_MELEE"], @@ -198,7 +180,6 @@ "id": "equip_longsword", "name": { "name": "longsword", "plural": "longswords" }, "renderable": { "glyph": ")", "fg": "#FFF8DC", "bg": "#000000", "order": 2 }, - "class": "weapon", "weight": 3, "value": 15, "flags": ["EQUIP_MELEE"], @@ -208,7 +189,6 @@ "id": "equip_smallshield", "name": { "name": "buckler", "plural": "bucklers" }, "renderable": { "glyph": "[", "fg": "#808080", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 2, "value": 5, "flags": ["EQUIP_SHIELD"], @@ -218,7 +198,6 @@ "id": "equip_mediumshield", "name": { "name": "medium shield", "plural": "medium shields" }, "renderable": { "glyph": "[", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 6, "value": 10, "flags": ["EQUIP_SHIELD"], @@ -228,7 +207,6 @@ "id": "equip_largeshield", "name": { "name": "large shield", "plural": "large shields" }, "renderable": { "glyph": "[", "fg": "#FFF8DC", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 12, "value": 35, "flags": ["EQUIP_SHIELD"], @@ -238,7 +216,6 @@ "id": "equip_body_weakleather", "name": { "name": "leather jacket", "plural": "leather jackets" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 8, "value": 5, "flags": ["EQUIP_BODY"], @@ -248,7 +225,6 @@ "id": "equip_body_leather", "name": { "name": "leather chestpiece", "plural": "leather chestpiece" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 10, "value": 10, "flags": ["EQUIP_BODY"], @@ -258,7 +234,6 @@ "id": "equip_body_studdedleather", "name": { "name": "studded leather chestpiece", "plural": "studded leather chestpieces" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 13, "value": 45, "flags": ["EQUIP_BODY"], @@ -268,7 +243,6 @@ "id": "equip_body_ringmail_o", "name": { "name": "orcish ring mail", "plural": "orcish ring mail" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 45, "value": 50, "flags": ["EQUIP_BODY"], @@ -278,7 +252,6 @@ "id": "equip_body_ringmail", "name": { "name": "ring mail", "plural": "ring mail" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 45, "value": 70, "flags": ["EQUIP_BODY"], @@ -288,7 +261,6 @@ "id": "equip_head_leather", "name": { "name": "leather cap", "plural": "leather caps" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 2, "value": 10, "flags": ["EQUIP_HEAD"], @@ -298,7 +270,6 @@ "id": "equip_head_elvish", "name": { "name": "elvish leather helm", "plural": "elvish leather helms" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 2, "value": 25, "flags": ["EQUIP_HEAD"], @@ -308,7 +279,6 @@ "id": "equip_head_o", "name": { "name": "orcish helm", "plural": "orcish helm" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 6, "value": 25, "flags": ["EQUIP_HEAD"], @@ -318,7 +288,6 @@ "id": "equip_head_iron", "name": { "name": "iron helm", "plural": "iron helm" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 10, "value": 45, "flags": ["EQUIP_HEAD"], @@ -328,7 +297,6 @@ "id": "equip_feet_leather", "name": { "name": "leather shoes", "plural": "leather shoes" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 2, "value": 10, "flags": ["EQUIP_FEET"] @@ -337,7 +305,6 @@ "id": "equip_feet_elvish", "name": { "name": "elvish leather shoes", "plural": "elvish leather shoes" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 2, "value": 25, "flags": ["EQUIP_FEET"], @@ -347,7 +314,6 @@ "id": "equip_feet_o", "name": { "name": "orcish boots", "plural": "orcish boots" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 6, "value": 25, "flags": ["EQUIP_FEET"], @@ -357,7 +323,6 @@ "id": "equip_feet_iron", "name": { "name": "iron boots", "plural": "iron boots" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 10, "value": 45, "flags": ["EQUIP_FEET"], @@ -367,7 +332,6 @@ "id": "equip_neck_protection", "name": { "name": "amulet of protection", "plural": "amulets of protection" }, "renderable": { "glyph": "\"", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "amulet", "weight": 1, "value": 200, "flags": ["EQUIP_NECK"], @@ -377,7 +341,6 @@ "id": "equip_back_protection", "name": { "name": "cloak of protection", "plural": "cloaks of protection" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 1, "value": 200, "flags": ["EQUIP_BACK"], @@ -387,7 +350,6 @@ "id": "wand_magicmissile", "name": { "name": "wand of magic missile", "plural": "wands of magic missile" }, "renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "wand", "weight": 2, "value": 100, "flags": ["CHARGES"], @@ -398,7 +360,6 @@ "id": "wand_fireball", "name": { "name": "wand of fireball", "plural": "wands of fireball" }, "renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "wand", "weight": 2, "value": 300, "flags": ["CHARGES"], @@ -409,7 +370,6 @@ "id": "wand_confusion", "name": { "name": "wand of confusion", "plural": "wands of confusion" }, "renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "wand", "weight": 2, "value": 200, "flags": ["CHARGES"], @@ -420,7 +380,6 @@ "id": "wand_digging", "name": { "name": "wand of digging", "plural": "wands of digging" }, "renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "wand", "weight": 2, "value": 300, "flags": ["CHARGES", "DIGGER"], @@ -431,18 +390,16 @@ "id": "food_rations", "name": { "name": "rations", "plural": "rations" }, "renderable": { "glyph": "%", "fg": "#FFA07A", "bg": "#000000", "order": 2 }, - "class": "comestible", "weight": 1, "value": 1, - "flags": ["FOOD", "CONSUMABLE", "STACKABLE"] + "flags": ["FOOD", "CONSUMABLE"] }, { "id": "food_apple", "name": { "name": "apple", "plural": "apples" }, "renderable": { "glyph": "%", "fg": "#00FF00", "bg": "#000000", "order": 2 }, - "class": "comestible", "weight": 0.5, "value": 1, - "flags": ["FOOD", "CONSUMABLE", "STACKABLE"] + "flags": ["FOOD", "CONSUMABLE"] } ] diff --git a/raws/props.json b/raws/props.json index 0c48291..f60ff11 100644 --- a/raws/props.json +++ b/raws/props.json @@ -2,7 +2,7 @@ { "id": "door", "name": "door", - "renderable": { "glyph": "+", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, + "renderable": { "glyph": "+", "sprite": 17, "fg": "#00FFFF", "bg": "#000000", "order": 2 }, "flags": ["DOOR"] }, { @@ -21,7 +21,7 @@ { "id": "prop_table", "name": "table", - "renderable": { "glyph": "-", "fg": "#AAAAAA", "bg": "#000000", "order": 2 }, + "renderable": { "glyph": "-", "sprite": 52, "fg": "#AAAAAA", "bg": "#000000", "order": 2 }, "flags": [] }, { @@ -33,25 +33,25 @@ { "id": "prop_statue", "name": "statue", - "renderable": { "glyph": "@", "fg": "#ffffff", "bg": "#000000", "order": 2 }, + "renderable": { "glyph": "@", "sprite": 29, "fg": "#ffffff", "bg": "#000000", "order": 2 }, "flags": [] }, { "id": "prop_bed", "name": "bed", - "renderable": { "glyph": "=", "fg": "#AAAAAA", "bg": "#000000", "order": 2 }, + "renderable": { "glyph": "=", "sprite": 50, "fg": "#AAAAAA", "bg": "#000000", "order": 2 }, "flags": [] }, { "id": "prop_chair", "name": "chair", - "renderable": { "glyph": "└", "fg": "#AAAAAA", "bg": "#000000", "order": 2 }, + "renderable": { "glyph": "└", "sprite": 51, "fg": "#AAAAAA", "bg": "#000000", "order": 2 }, "flags": [] }, { "id": "prop_candle", "name": "candle", - "renderable": { "glyph": "Ä", "fg": "#FFA500", "bg": "#000000", "order": 2 }, + "renderable": { "glyph": "Ä", "sprite": 3, "fg": "#FFA500", "bg": "#000000", "order": 2 }, "flags": [] }, { diff --git a/resources/curses11x20.png b/resources/curses11x20.png new file mode 100644 index 0000000..5f5e1e5 Binary files /dev/null and b/resources/curses11x20.png differ diff --git a/resources/curses12x24.png b/resources/curses12x24.png new file mode 100644 index 0000000..4504d0a Binary files /dev/null and b/resources/curses12x24.png differ diff --git a/resources/curses16x16.pdn b/resources/curses16x16.pdn new file mode 100644 index 0000000..a13f60d Binary files /dev/null and b/resources/curses16x16.pdn differ diff --git a/resources/curses16x16.png b/resources/curses16x16.png new file mode 100644 index 0000000..042abea Binary files /dev/null and b/resources/curses16x16.png differ diff --git a/resources/curses8x16.pdn b/resources/curses8x16.pdn new file mode 100644 index 0000000..02b2112 Binary files /dev/null and b/resources/curses8x16.pdn differ diff --git a/resources/curses8x16.png b/resources/curses8x16.png new file mode 100644 index 0000000..4b83036 Binary files /dev/null and b/resources/curses8x16.png differ diff --git a/resources/healthbar11x2.png b/resources/healthbar11x2.png new file mode 100644 index 0000000..96fb67c Binary files /dev/null and b/resources/healthbar11x2.png differ diff --git a/resources/healthbar22x2.png b/resources/healthbar22x2.png new file mode 100644 index 0000000..daa15d1 Binary files /dev/null and b/resources/healthbar22x2.png differ diff --git a/resources/nagidal22x20_centred.png b/resources/nagidal22x20_centred.png new file mode 100644 index 0000000..1383fb9 Binary files /dev/null and b/resources/nagidal22x20_centred.png differ diff --git a/resources/nagidal22x22_centred.png b/resources/nagidal22x22_centred.png new file mode 100644 index 0000000..f941b27 Binary files /dev/null and b/resources/nagidal22x22_centred.png differ diff --git a/resources/nagidal24x24.png b/resources/nagidal24x24.png new file mode 100644 index 0000000..2d4aba9 Binary files /dev/null and b/resources/nagidal24x24.png differ diff --git a/resources/terminal10x10_gs_tc.png b/resources/terminal10x10_gs_tc.png new file mode 100644 index 0000000..5e0cdc9 Binary files /dev/null and b/resources/terminal10x10_gs_tc.png differ diff --git a/resources/vga8x16.png b/resources/vga8x16.png new file mode 100644 index 0000000..913e32c Binary files /dev/null and b/resources/vga8x16.png differ diff --git a/resources/world16x16.png b/resources/world16x16.png new file mode 100644 index 0000000..e05e1d5 Binary files /dev/null and b/resources/world16x16.png differ diff --git a/src/ai/approach_ai_system.rs b/src/ai/approach_ai_system.rs index c3cc2ca..6e6e7a8 100644 --- a/src/ai/approach_ai_system.rs +++ b/src/ai/approach_ai_system.rs @@ -45,6 +45,7 @@ impl<'a> System<'a> for ApproachAI { continue; }; let mut path: Option = None; + let mut curr_abs_diff = 100; let idx = map.xy_idx(pos.x, pos.y); for tar_idx in target_idxs { let potential_path = a_star_search(idx, tar_idx, &mut *map); @@ -54,6 +55,17 @@ impl<'a> System<'a> for ApproachAI { potential_path.steps.len() < path.as_ref().unwrap().steps.len() { path = Some(potential_path); + let (x1, y1) = (pos.x, pos.y); + let (x2, y2) = ((tar_idx as i32) % map.width, (tar_idx as i32) / map.width); + curr_abs_diff = i32::abs(x2 - x1) + i32::abs(y2 - y1); + } else if potential_path.steps.len() == path.as_ref().unwrap().steps.len() { + let (x1, y1) = (pos.x, pos.y); + let (x2, y2) = ((tar_idx as i32) % map.width, (tar_idx as i32) / map.width); + let abs_diff = i32::abs(x2 - x1) + i32::abs(y2 - y1); + if abs_diff < curr_abs_diff { + path = Some(potential_path); + curr_abs_diff = abs_diff; + } } } } diff --git a/src/ai/turn_status_system.rs b/src/ai/turn_status_system.rs index e072e45..db3acaa 100644 --- a/src/ai/turn_status_system.rs +++ b/src/ai/turn_status_system.rs @@ -65,7 +65,9 @@ impl<'a> System<'a> for TurnStatusSystem { not_confused.push(entity); if entity == *player_entity { logger = logger + .colour(renderable_colour(&renderables, entity)) .append("You") + .colour(WHITE) .append("snap out of it."); log = true; } else { @@ -92,7 +94,9 @@ impl<'a> System<'a> for TurnStatusSystem { not_my_turn.push(entity); if entity == *player_entity { logger = logger + .colour(renderable_colour(&renderables, entity)) .append("You") + .colour(WHITE) .append("are confused!"); log = true; gamelog::record_event(EVENT::PlayerConfused(1)); diff --git a/src/camera.rs b/src/camera.rs index 00b6869..090476b 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,14 +1,16 @@ -use super::{ Hidden, Map, Mind, Position, Prop, Renderable }; +use super::{ Hidden, Map, Mind, Position, Prop, Renderable, Pools }; use bracket_lib::prelude::*; use specs::prelude::*; use std::ops::Mul; +use super::data::visuals::{ VIEWPORT_W, VIEWPORT_H }; +use super::data::prelude::*; const SHOW_BOUNDARIES: bool = false; pub fn get_screen_bounds(ecs: &World, _ctx: &mut BTerm) -> (i32, i32, i32, i32, i32, i32) { let player_pos = ecs.fetch::(); let map = ecs.fetch::(); - let (x_chars, y_chars, mut x_offset, mut y_offset) = (69, 41, 1, 10); + let (x_chars, y_chars, mut x_offset, mut y_offset) = (VIEWPORT_W, VIEWPORT_H, 1, 10); let centre_x = (x_chars / 2) as i32; let centre_y = (y_chars / 2) as i32; @@ -43,13 +45,29 @@ pub fn render_camera(ecs: &World, ctx: &mut BTerm) { if t_x >= 0 && t_x < map.width && t_y >= 0 && t_y < map.height { let idx = map.xy_idx(t_x, t_y); if map.revealed_tiles[idx] { - let (glyph, fg, bg) = crate::map::themes::get_tile_renderables_for_id( - idx, - &*map, - Some(*ecs.fetch::()), - None - ); - ctx.set(x + x_offset, y + y_offset, fg, bg, glyph); + if 1 == 2 { + let (glyph, fg, bg) = crate::map::themes::get_tile_renderables_for_id( + idx, + &*map, + Some(*ecs.fetch::()), + None + ); + ctx.set(x + x_offset, y + y_offset, fg, bg, glyph); + } else { + ctx.set_active_console(0); + let (id, tint) = crate::map::themes::get_sprite_for_id( + idx, + &*map, + Some(*ecs.fetch::()) + ); + ctx.add_sprite( + Rect::with_size(x * 16 + x_offset * 16, y * 16 + y_offset * 16, 16, 16), + 0, + tint, + id + ); + ctx.set_active_console(TILE_LAYER); + } } } else if SHOW_BOUNDARIES { ctx.set( @@ -67,8 +85,11 @@ pub fn render_camera(ecs: &World, ctx: &mut BTerm) { // Render entities { + ctx.set_active_console(ENTITY_LAYER); + let positions = ecs.read_storage::(); let renderables = ecs.read_storage::(); + let pools = ecs.read_storage::(); let minds = ecs.read_storage::(); let hidden = ecs.read_storage::(); let props = ecs.write_storage::(); @@ -84,12 +105,7 @@ pub fn render_camera(ecs: &World, ctx: &mut BTerm) { if pos.x < max_x && pos.y < max_y && pos.x >= min_x && pos.y >= min_y { let mut draw = false; let mut fg = render.fg; - let mut bg = crate::map::themes::get_tile_renderables_for_id( - idx, - &*map, - Some(*ecs.fetch::()), - None - ).2; + let bg = BLACK; // Draw entities on visible tiles if map.visible_tiles[idx] { draw = true; @@ -104,9 +120,6 @@ pub fn render_camera(ecs: &World, ctx: &mut BTerm) { let has_mind = minds.get(*ent); if let Some(_) = has_mind { draw = true; - if !map.revealed_tiles[idx] { - bg = RGB::named(BLACK); - } } } } @@ -118,16 +131,51 @@ pub fn render_camera(ecs: &World, ctx: &mut BTerm) { } } if draw { - ctx.set( - entity_offset_x + x_offset, - entity_offset_y + y_offset, - fg, - bg, - render.glyph - ); + if let Some(sprite) = render.sprite { + ctx.set_active_console(0); + ctx.add_sprite( + Rect::with_size( + entity_offset_x * 16 + x_offset * 16, + entity_offset_y * 16 + y_offset * 16, + 16, + 16 + ), + render.render_order, + RGBA::named(WHITE), + sprite + ); + ctx.set_active_console(ENTITY_LAYER); + } else { + ctx.set( + entity_offset_x + x_offset, + entity_offset_y + y_offset, + fg, + bg, + render.glyph + ); + } + if let Some(pool) = pools.get(*ent) { + if pool.hit_points.current < pool.hit_points.max { + ctx.set_active_console(HP_BAR_LAYER); + crate::gui::draw_lerping_bar( + ctx, + (entity_offset_x + x_offset) * 16 + 2, + (entity_offset_y + y_offset) * 16 - 1, + 14, + pool.hit_points.current, + pool.hit_points.max, + RGB::named(GREEN), + RGB::named(RED), + false, + false + ); + ctx.set_active_console(ENTITY_LAYER); + } + } } } } + ctx.set_active_console(TILE_LAYER); } } diff --git a/src/components.rs b/src/components.rs index 45ca1cf..b489abe 100644 --- a/src/components.rs +++ b/src/components.rs @@ -41,6 +41,7 @@ pub struct OtherLevelPosition { #[derive(Component, ConvertSaveload, Clone)] pub struct Renderable { pub glyph: FontCharType, + pub sprite: Option, pub fg: RGB, pub bg: RGB, pub render_order: i32, @@ -243,55 +244,16 @@ pub enum BUC { Blessed, } -impl BUC { - pub fn noncursed(&self) -> bool { - match self { - BUC::Cursed => false, - _ => true, - } - } -} - #[derive(Component, Debug, Serialize, Deserialize, Eq, PartialEq, Hash, Clone)] pub struct Beatitude { pub buc: BUC, pub known: bool, } -#[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq, Eq)] -pub enum ItemType { - Amulet, - Weapon, - Armour, - Comestible, - Scroll, - Spellbook, - Potion, - Ring, - Wand, -} - -impl ItemType { - pub fn string(&self) -> &str { - match self { - ItemType::Amulet => "Amulets", - ItemType::Weapon => "Weapons", - ItemType::Armour => "Armour", - ItemType::Comestible => "Comestibles", - ItemType::Scroll => "Scrolls", - ItemType::Spellbook => "Spellbooks", - ItemType::Potion => "Potions", - ItemType::Ring => "Rings", - ItemType::Wand => "Wands", - } - } -} - #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Item { pub weight: f32, // in lbs pub value: f32, // base - pub category: ItemType, } #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] @@ -466,45 +428,11 @@ pub enum Intrinsic { Speed, // 4/3x speed multiplier } -impl Intrinsic { - pub fn describe(&self) -> &str { - match self { - Intrinsic::Regeneration => "regenerates health", - Intrinsic::Speed => "is hasted", - } - } -} - #[derive(Component, Serialize, Deserialize, Debug, Clone)] pub struct Intrinsics { pub list: HashSet, } -impl Intrinsics { - pub fn describe(&self) -> String { - let mut descriptions = Vec::new(); - for intrinsic in &self.list { - descriptions.push(intrinsic.describe()); - } - match descriptions.len() { - 0 => - unreachable!("describe() should never be called on an empty Intrinsics component."), - 1 => format!("It {}.", descriptions[0]), - _ => { - let last = descriptions.pop().unwrap(); - let joined = descriptions.join(", "); - format!("It {}, and {}.", joined, last) - } - } - } -} - -#[derive(Component, Serialize, Deserialize, Debug, Clone)] -pub struct IntrinsicChanged { - pub gained: HashSet, - pub lost: HashSet, -} - #[derive(Component, Debug, ConvertSaveload, Clone)] pub struct InflictsDamage { pub damage_type: DamageType, @@ -648,20 +576,3 @@ pub struct EntityMoved {} #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct MultiAttack {} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct Stackable {} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct WantsToRemoveKey {} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct WantsToDelete {} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct Key { - pub idx: usize, -} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct WantsToAssignKey {} diff --git a/src/damage_system.rs b/src/damage_system.rs index b0cc566..a4224a7 100644 --- a/src/damage_system.rs +++ b/src/damage_system.rs @@ -11,8 +11,6 @@ use super::{ Position, Renderable, RunState, - WantsToRemoveKey, - WantsToDelete, }; use bracket_lib::prelude::*; use specs::prelude::*; @@ -67,17 +65,7 @@ pub fn delete_the_dead(ecs: &mut World) { } } } - let (mut items_to_delete, loot_to_spawn) = handle_dead_entity_items(ecs, &dead); - { - let entities = ecs.entities(); - let removekeys = ecs.read_storage::(); - let delete = ecs.read_storage::(); - // Add items marked for deletion to the list, but only if they've already had their - // key assignments handled, to ensurew we don't leave any dangling references behind. - for (e, _d, _r) in (&entities, &delete, !&removekeys).join() { - items_to_delete.push(e); - } - } + let (items_to_delete, loot_to_spawn) = handle_dead_entity_items(ecs, &dead); for loot in loot_to_spawn { crate::raws::spawn_named_entity( &crate::raws::RAWS.lock().unwrap(), @@ -94,7 +82,6 @@ pub fn delete_the_dead(ecs: &mut World) { // For everything that died, increment the event log, and delete. for victim in dead { gamelog::record_event(events::EVENT::Turn(1)); - // TODO: Delete stuff from inventory? This should be handled elsewhere. ecs.delete_entity(victim).expect("Unable to delete."); } } diff --git a/src/data/messages.rs b/src/data/messages.rs index 7175b2a..89e39c8 100644 --- a/src/data/messages.rs +++ b/src/data/messages.rs @@ -25,7 +25,6 @@ pub const NUTRITION_BLESSED: &str = "Delicious"; pub const LEVELUP_PLAYER: &str = "Welcome to experience level"; pub const YOU_PICKUP_ITEM: &str = "You pick up the"; -pub const NO_MORE_KEYS: &str = "Your backpack cannot accomodate any more items"; pub const YOU_DROP_ITEM: &str = "You drop the"; pub const YOU_EQUIP_ITEM: &str = "You equip the"; pub const YOU_REMOVE_ITEM: &str = "You unequip your"; diff --git a/src/data/mod.rs b/src/data/mod.rs index 8abda34..cefe83f 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -5,3 +5,8 @@ pub mod char_create; pub mod events; pub mod ids; pub mod names; +pub mod sprites; + +pub mod prelude { + pub use super::visuals::{ TILE_LAYER, ENTITY_LAYER, TEXT_LAYER, HP_BAR_LAYER }; +} diff --git a/src/data/sprites.rs b/src/data/sprites.rs new file mode 100644 index 0000000..1b9d7bf --- /dev/null +++ b/src/data/sprites.rs @@ -0,0 +1,119 @@ +// Row 1 +pub const UNKN: usize = 0; +pub const UNKN2: usize = 1; +pub const UNKN3: usize = 2; +pub const CANDLE: usize = 3; +pub const CANDLE2: usize = 4; +pub const CANDLE3: usize = 5; +pub const CANDLE4: usize = 6; +pub const CANDLE5: usize = 7; +pub const CANDLE6: usize = 8; +pub const CAULDRON: usize = 9; +pub const CAULDRON2: usize = 10; +pub const POTS: usize = 11; +pub const POTS2: usize = 12; +pub const POT: usize = 13; +pub const SPIKES: usize = 14; +pub const SPIKES2: usize = 15; +// Row 2 +pub const WINDOW: usize = 16; +pub const DOOR: usize = 17; +pub const DOOR_OPEN: usize = 18; +pub const ROOF_BASE: usize = 19; +pub const ROOF_BASE2: usize = 20; +pub const ROOF: usize = 21; +pub const ROOF2: usize = 22; +pub const ROOF_CHIMNEY: usize = 23; +pub const SIGN: usize = 24; +pub const SIGN_BLACKSMITH: usize = 25; +pub const SIGN_POTION: usize = 26; +pub const SIGN_FURNITURE: usize = 27; +pub const WINDOW_LIT: usize = 28; +pub const STATUE_ANGEL: usize = 29; +pub const STATUE: usize = 30; +pub const STATUE_SPIDER: usize = 31; +// Row 3 +pub const UNKN4: usize = 32; +pub const UNKN5: usize = 33; +pub const UNKN6: usize = 34; +pub const UNKN7: usize = 35; +pub const UNKN8: usize = 36; +pub const TREE: usize = 37; +pub const TREE2: usize = 38; +pub const PATH_GRASS: usize = 39; +pub const PATH_GRASS_QUAD: usize = 40; +pub const PATH_GRASS_QUAD2: usize = 41; +pub const PATH_GRASS_QUAD3: usize = 42; +pub const CAMPFIRE: usize = 43; +pub const CAMPFIRE_LIT: usize = 44; +pub const CAMPFIRE_LIT2: usize = 45; // ANIMATE WITH % 2 AND SOMETHING TO DO WITH FRAME TIME +pub const THRONE: usize = 46; +pub const THRONE2: usize = 47; +// Row 4 +pub const BOOKSHELF: usize = 48; +pub const BOOKSHELF_EMPTY: usize = 49; +pub const BED: usize = 50; +pub const CHAIR: usize = 51; +pub const TABLE: usize = 52; +pub const TABLE_L: usize = 53; +pub const TABLE_M: usize = 54; +pub const TABLE_R: usize = 55; +pub const CHAIR_AT_TABLE_L: usize = 56; +pub const TABLE_M_PARCHMENT: usize = 57; +pub const CHAIR_AT_TABLE_R: usize = 58; +pub const TABLE_DARK: usize = 59; +pub const TABLE_DARK_SKULL: usize = 60; +pub const TABLE_DARK_L: usize = 61; +pub const TABLE_DARK_M: usize = 62; +pub const TABLE_DARK_R: usize = 63; +// Row 5 +pub const GRASS: usize = 64; +pub const GRASS2: usize = 65; +pub const GRASS3: usize = 66; +pub const GRASS4: usize = 67; +pub const GRASS5: usize = 68; +pub const MUSHROOM: usize = 69; +pub const MUSHROOM_PURPLE: usize = 70; +pub const MUSHROOM_ORANGE: usize = 71; +pub const LILYPAD: usize = 72; +pub const LILYPAD2: usize = 73; +pub const LILYPAD3: usize = 74; +pub const LILYPAD4: usize = 75; +pub const LILYPAD5: usize = 76; +pub const LILYPAD6: usize = 77; +pub const LILYPAD7: usize = 78; +pub const LILYPAD8: usize = 79; +// Row 6 (80-95) +// Row 7 (96-111) +// Row 8 (112-127) +pub const FLOOR_WOOD: usize = 124; +// Row 9 (128-143) +// Row 10 (144-159) +// Row 11 (160-175) +pub const WATER_DEEP: usize = 164; +// Row 12 (176-191) +// Row 13 (192-207) +// Row 14 (208-223) +pub const FLOOR_GRASS: usize = 216; +// Row 15 (224-239) +pub const FLOOR: usize = 224; +// Row 16 (240-255) +// Row 17 (256-271) +// Row 18 (272-287) +// Row 19 (288-303) +pub const WALL_BASE: usize = 288; +pub const WALL_BASE2: usize = 289; +pub const WALL_BASE3: usize = 290; +pub const WALL_BASE4: usize = 291; +pub const WALL_CLOTH_BASE: usize = 292; +pub const WALL_CRACKED_BASE: usize = 293; +pub const WALL: usize = 294; +pub const WALL2: usize = 295; +pub const WALL3: usize = 296; +pub const WALL4: usize = 297; +pub const WALL_CRACKED: usize = 298; +pub const WALL_CLOTH_H: usize = 299; +pub const STAIR_D: usize = 300; +pub const STAIR_A: usize = 301; +pub const BASIN: usize = 302; +pub const BASIN_EMPTY: usize = 303; diff --git a/src/data/visuals.rs b/src/data/visuals.rs index 8f59a24..26fa5ee 100644 --- a/src/data/visuals.rs +++ b/src/data/visuals.rs @@ -2,6 +2,13 @@ use bracket_lib::prelude::*; // POST-PROCESSING pub const WITH_DARKEN_BY_DISTANCE: bool = true; // If further away tiles should get darkened, instead of a harsh transition to non-visible. +pub const VIEWPORT_W: i32 = 69; +pub const VIEWPORT_H: i32 = 41; + +pub const TILE_LAYER: usize = 1; +pub const ENTITY_LAYER: usize = 2; +pub const TEXT_LAYER: usize = 3; +pub const HP_BAR_LAYER: usize = 4; pub const BRIGHTEN_FG_COLOUR_BY: i32 = 16; pub const GLOBAL_OFFSET_MIN_CLAMP: f32 = -0.5; diff --git a/src/effects/damage.rs b/src/effects/damage.rs index 80885e2..74d1178 100644 --- a/src/effects/damage.rs +++ b/src/effects/damage.rs @@ -184,7 +184,7 @@ fn get_death_message(ecs: &World, source: Entity) -> String { result.push_str(format!("{}", PLAYER_DIED_SUICIDE).as_str()); } else if let Some(name) = ecs.read_storage::().get(source) { result.push_str( - format!("{} {}", PLAYER_DIED_NAMED_ATTACKER, with_article(name.name.clone())).as_str() + format!("{} {}", PLAYER_DIED_NAMED_ATTACKER, with_article(&name.name)).as_str() ); } else { result.push_str(format!("{}", PLAYER_DIED_UNKNOWN).as_str()); diff --git a/src/effects/intrinsics.rs b/src/effects/intrinsics.rs deleted file mode 100644 index 01776e1..0000000 --- a/src/effects/intrinsics.rs +++ /dev/null @@ -1,11 +0,0 @@ -use super::{ EffectSpawner, EffectType }; -use specs::prelude::*; - -pub fn add_intrinsic(ecs: &mut World, effect: &EffectSpawner, target: Entity) { - let intrinsic = if let EffectType::AddIntrinsic { intrinsic } = &effect.effect_type { - intrinsic - } else { - unreachable!("add_intrinsic() called with the wrong EffectType") - }; - add_intr!(ecs, target, *intrinsic); -} diff --git a/src/effects/mod.rs b/src/effects/mod.rs index 5552f5a..c23b52b 100644 --- a/src/effects/mod.rs +++ b/src/effects/mod.rs @@ -4,14 +4,13 @@ use bracket_lib::prelude::*; use specs::prelude::*; use std::collections::VecDeque; use std::sync::Mutex; -use crate::components::*; +use crate::components::DamageType; mod damage; mod hunger; mod particles; mod targeting; mod triggers; -mod intrinsics; pub use targeting::aoe_tiles; @@ -52,9 +51,6 @@ pub enum EffectType { ModifyNutrition { amount: i32, }, - AddIntrinsic { - intrinsic: Intrinsic, - }, TriggerFire { trigger: Entity, }, @@ -157,7 +153,6 @@ fn tile_effect_hits_entities(effect: &EffectType) -> bool { EffectType::Healing { .. } => true, EffectType::ModifyNutrition { .. } => true, EffectType::Confusion { .. } => true, - EffectType::AddIntrinsic { .. } => true, _ => false, } } @@ -180,7 +175,6 @@ fn affect_entity(ecs: &mut World, effect: &EffectSpawner, target: Entity) { } EffectType::EntityDeath => damage::entity_death(ecs, effect, target), EffectType::ModifyNutrition { .. } => hunger::modify_nutrition(ecs, effect, target), - EffectType::AddIntrinsic { .. } => intrinsics::add_intrinsic(ecs, effect, target), _ => {} } } diff --git a/src/effects/triggers.rs b/src/effects/triggers.rs index cb4e5d3..ac38ccb 100644 --- a/src/effects/triggers.rs +++ b/src/effects/triggers.rs @@ -1,4 +1,4 @@ -use super::{ add_effect, particles, spatial, EffectType, Entity, Targets, World }; +use super::{ add_effect, get_noncursed, particles, spatial, EffectType, Entity, Targets, World }; use crate::{ gamelog, gui::item_colour_ecs, @@ -33,8 +33,6 @@ use crate::{ KnownSpells, Position, Viewshed, - WantsToRemoveKey, - WantsToDelete, }; use crate::data::messages::*; use bracket_lib::prelude::*; @@ -59,10 +57,7 @@ pub fn item_trigger(source: Option, item: Entity, target: &Targets, ecs: let did_something = event_trigger(source, item, target, ecs); // If it's a consumable, delete it if did_something && ecs.read_storage::().get(item).is_some() { - let mut removekey = ecs.write_storage::(); - removekey.insert(item, WantsToRemoveKey {}).expect("Unable to insert WantsToRemoveKey"); - let mut delete = ecs.write_storage::(); - delete.insert(item, WantsToDelete {}).expect("Unable to insert WantsToDelete"); + ecs.entities().delete(item).expect("Failed to delete item"); } } @@ -210,7 +205,7 @@ fn handle_healing( healing_item.modifier; add_effect( event.source, - EffectType::Healing { amount: roll, increment_max: event.buc.noncursed() }, + EffectType::Healing { amount: roll, increment_max: get_noncursed(&event.buc) }, event.target.clone() ); for target in get_entity_targets(&event.target) { @@ -223,7 +218,9 @@ fn handle_healing( let renderables = ecs.read_storage::(); if ecs.read_storage::().get(target).is_some() { logger = logger + .colour(renderable_colour(&renderables, target)) .append("You") + .colour(WHITE) .append(HEAL_PLAYER_HIT) .buc(event.buc.clone(), None, Some(HEAL_PLAYER_HIT_BLESSED)); } else { @@ -265,7 +262,9 @@ fn handle_damage( let player_viewshed = viewsheds.get(*ecs.fetch::()).unwrap(); if ecs.read_storage::().get(target).is_some() { logger = logger + .colour(renderable_colour(&renderables, target)) .append("You") + .colour(WHITE) .append(DAMAGE_PLAYER_HIT); event.log = true; } else if diff --git a/src/gamelog/events.rs b/src/gamelog/events.rs index 3e0006f..bef5ff6 100644 --- a/src/gamelog/events.rs +++ b/src/gamelog/events.rs @@ -126,7 +126,7 @@ pub fn record_event(event: EVENT) { new_event = format!("Discovered {}", name); } EVENT::Identified(name) => { - new_event = format!("Identified {}", crate::gui::with_article(name)); + new_event = format!("Identified {}", name); } EVENT::PlayerDied(str) => { // Generating the String is handled in the death effect, to avoid passing the ecs here. diff --git a/src/gui/character_creation.rs b/src/gui/character_creation.rs index da1ecdb..bc6e68e 100644 --- a/src/gui/character_creation.rs +++ b/src/gui/character_creation.rs @@ -28,6 +28,7 @@ use bracket_lib::prelude::*; use serde::{ Deserialize, Serialize }; use specs::prelude::*; use std::collections::HashMap; +use crate::data::prelude::*; #[derive(Serialize, Deserialize, Copy, Clone, PartialEq)] pub enum Ancestry { @@ -112,6 +113,7 @@ pub enum CharCreateResult { /// Handles the player character creation screen. pub fn character_creation(gs: &mut State, ctx: &mut BTerm) -> CharCreateResult { + ctx.set_active_console(TEXT_LAYER); let runstate = gs.ecs.fetch::(); let mut x = 2; @@ -245,6 +247,7 @@ pub fn character_creation(gs: &mut State, ctx: &mut BTerm) -> CharCreateResult { } } } + ctx.set_active_console(TILE_LAYER); return CharCreateResult::NoSelection { ancestry: Ancestry::Human, class: Class::Fighter }; } @@ -270,6 +273,7 @@ pub fn setup_player_ancestry(ecs: &mut World, ancestry: Ancestry) { renderables .insert(*player, Renderable { glyph: to_cp437(DWARF_GLYPH), + sprite: None, // TODO: Dwarf sprite fg: RGB::named(DWARF_COLOUR), bg: RGB::named(BLACK), render_order: 0, @@ -281,6 +285,7 @@ pub fn setup_player_ancestry(ecs: &mut World, ancestry: Ancestry) { renderables .insert(*player, Renderable { glyph: to_cp437(ELF_GLYPH), + sprite: None, // TODO: Elf sprite fg: RGB::named(ELF_COLOUR), bg: RGB::named(BLACK), render_order: 0, @@ -306,6 +311,7 @@ pub fn setup_player_ancestry(ecs: &mut World, ancestry: Ancestry) { renderables .insert(*player, Renderable { glyph: to_cp437(CATFOLK_GLYPH), + sprite: None, // TODO: Catfolk sprite fg: RGB::named(CATFOLK_COLOUR), bg: RGB::named(BLACK), render_order: 0, diff --git a/src/gui/farlook.rs b/src/gui/farlook.rs index c8f6312..045b172 100644 --- a/src/gui/farlook.rs +++ b/src/gui/farlook.rs @@ -1,4 +1,11 @@ -use super::{ State, RunState, tooltip::draw_tooltips, camera::get_screen_bounds }; +use super::{ + State, + RunState, + tooltip::draw_tooltips, + camera::get_screen_bounds, + VIEWPORT_H, + VIEWPORT_W, +}; use bracket_lib::prelude::*; #[derive(PartialEq, Copy, Clone)] @@ -23,9 +30,8 @@ pub fn show_farlook(gs: &mut State, ctx: &mut BTerm) -> FarlookResult { ); if let RunState::Farlook { x, y } = *runstate { - let (screen_x, screen_y) = (69, 41); - let x = x.clamp(x_offset, x_offset - 1 + (screen_x as i32)); - let y = y.clamp(y_offset, y_offset - 1 + (screen_y as i32)); + let x = x.clamp(x_offset, x_offset - 1 + VIEWPORT_W); + let y = y.clamp(y_offset, y_offset - 1 + VIEWPORT_H); ctx.set(x, y, RGB::named(WHITE), RGB::named(BLACK), to_cp437('X')); draw_tooltips(&gs.ecs, ctx, Some((x, y))); diff --git a/src/gui/identify_menu.rs b/src/gui/identify_menu.rs index 31ce8d7..14e0686 100644 --- a/src/gui/identify_menu.rs +++ b/src/gui/identify_menu.rs @@ -3,10 +3,10 @@ use super::{ item_colour_ecs, obfuscate_name_ecs, print_options, - unique_ecs, - check_key, - letter_to_option, + renderable_colour, ItemMenuResult, + UniqueInventoryItem, + BUC, }; use crate::{ gamelog, @@ -19,12 +19,11 @@ use crate::{ Name, ObfuscatedName, Renderable, - Key, states::state::*, }; use bracket_lib::prelude::*; use specs::prelude::*; -use std::collections::HashMap; +use std::collections::BTreeMap; /// Handles the Identify menu. pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option) { @@ -38,41 +37,38 @@ pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option(); let renderables = gs.ecs.read_storage::(); let beatitudes = gs.ecs.read_storage::(); - let keys = gs.ecs.read_storage::(); let build_identify_iterator = || { - (&entities, &items, &renderables, &names, &keys) - .join() - .filter(|(item_entity, _i, _r, n, _k)| { - // If not owned by the player, return false. - let mut keep = false; - if let Some(bp) = backpack.get(*item_entity) { - if bp.owner == *player_entity { - keep = true; - } + (&entities, &items, &renderables, &names).join().filter(|(item_entity, _i, _r, n)| { + // If not owned by the player, return false. + let mut keep = false; + if let Some(bp) = backpack.get(*item_entity) { + if bp.owner == *player_entity { + keep = true; } - // If not equipped by the player, return false. - if let Some(equip) = equipped.get(*item_entity) { - if equip.owner == *player_entity { - keep = true; - } + } + // If not equipped by the player, return false. + if let Some(equip) = equipped.get(*item_entity) { + if equip.owner == *player_entity { + keep = true; } - if !keep { - return false; - } - // If not obfuscated, or already identified, return false. - if - (!obfuscated.get(*item_entity).is_some() || - dm.identified_items.contains(&n.name)) && - beatitudes - .get(*item_entity) - .map(|beatitude| beatitude.known) - .unwrap_or(true) - { - return false; - } - return true; - }) + } + if !keep { + return false; + } + // If not obfuscated, or already identified, return false. + if + (!obfuscated.get(*item_entity).is_some() || + dm.identified_items.contains(&n.name)) && + beatitudes + .get(*item_entity) + .map(|beatitude| beatitude.known) + .unwrap_or(true) + { + return false; + } + return true; + }) }; // Build list of items to display @@ -95,15 +91,34 @@ pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option().get(entity) + { + match beatitude.buc { + BUC::Blessed => 1, + BUC::Uncursed => 2, + BUC::Cursed => 3, + } + } else { + 0 + }; + let unique_item = UniqueInventoryItem { + display_name: super::DisplayName { singular: singular.clone(), plural: plural.clone() }, + rgb: item_colour_ecs(&gs.ecs, entity), + renderables: renderable_colour(&renderables, entity), + glyph: renderable.glyph, + beatitude_status: beatitude_status, + name: name.name.clone(), + }; player_inventory .entry(unique_item) - .and_modify(|slot| { - slot.count += 1; + .and_modify(|(_e, count)| { + *count += 1; }) - .or_insert(super::InventorySlot { item: entity, count: 1, idx: key.idx }); + .or_insert((entity, 1)); } // Get display args let width = get_max_inventory_width(&player_inventory); @@ -118,7 +133,7 @@ pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option (ItemMenuResult::NoResponse, None), @@ -126,17 +141,21 @@ pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option (ItemMenuResult::Cancel, None), _ => { - let selection = letter_to_option::letter_to_option(key, ctx.shift); - if selection != -1 && check_key(selection as usize) { - // Get the first entity with a Key {} component that has an idx matching "selection". - let entities = gs.ecs.entities(); - let keyed_items = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - for (e, key, _b) in (&entities, &keyed_items, &backpack).join() { - if key.idx == (selection as usize) { - return (ItemMenuResult::Selected, Some(e)); - } - } + let selection = letter_to_option(key); + if selection > -1 && selection < (count as i32) { + let item = player_inventory + .iter() + .nth(selection as usize) + .unwrap().1.0; + gamelog::Logger + ::new() + .append("You identify the") + .colour(item_colour_ecs(&gs.ecs, item)) + .append_n(obfuscate_name_ecs(&gs.ecs, item).0) + .colour(WHITE) + .append("!") + .log(); + return (ItemMenuResult::Selected, Some(item)); } (ItemMenuResult::NoResponse, None) } diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 7604527..65482af 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -32,23 +32,21 @@ use super::{ Skills, Viewshed, BUC, - Key, - Item, - ItemType, data::ids::get_local_col, }; +use crate::data::prelude::*; use crate::data::entity::CARRY_CAPACITY_PER_STRENGTH; use crate::data::visuals::{ TARGETING_LINE_COL, TARGETING_CURSOR_COL, TARGETING_AOE_COL, TARGETING_VALID_COL, + VIEWPORT_W, + VIEWPORT_H, }; use bracket_lib::prelude::*; use specs::prelude::*; -use std::collections::HashMap; -use crate::invkeys::check_key; - +use std::collections::BTreeMap; mod character_creation; mod cheat_menu; mod letter_to_option; @@ -87,121 +85,33 @@ pub fn draw_lerping_bar( n: i32, max: i32, full_colour: RGB, - empty_colour: RGB + empty_colour: RGB, + with_text: bool, + with_bg: bool ) { let percent = (n as f32) / (max as f32); let fill_width = (percent * (width as f32)) as i32; let bg = empty_colour.lerp(full_colour, percent); - let fg = RGB::named(BLACK); + let black = RGB::named(BLACK); for x in 0..width { if x <= fill_width { - ctx.print_color(sx + x, sy, fg, bg, " "); - } else { - ctx.print_color(sx + x, sy, RGB::named(BLACK), RGB::named(BLACK), " "); + ctx.print_color(sx + x, sy, black, bg, ' '); + } else if with_bg { + ctx.print_color(sx + x, sy, black, black, ' '); } } - ctx.print(sx - 1, sy, "["); - let health = format!("{}/{}", n, max); - ctx.print_color(sx + 1, sy, fg, bg, health); - ctx.print(sx + width, sy, "]"); -} - -fn draw_xp(ctx: &mut BTerm, pt: Point, pool: &Pools) { - ctx.print_color( - pt.x, - pt.y, - RGB::named(WHITE), - RGB::named(BLACK), - format!("XP{}/{}", pool.level, pool.xp) - ); -} - -fn calc_ac(ecs: &World, skills: &Skills, stats: &Pools, attr: &Attributes) -> i32 { - let skill_ac_bonus = gamesystem::skill_bonus(Skill::Defence, skills); - let mut armour_ac_bonus = 0; - let equipped = ecs.read_storage::(); - let ac = ecs.read_storage::(); - let player_entity = ecs.fetch::(); - for (wielded, ac) in (&equipped, &ac).join() { - if wielded.owner == *player_entity { - armour_ac_bonus += ac.amount; - } - } - stats.bac - attr.dexterity.bonus / 2 - skill_ac_bonus - armour_ac_bonus -} - -fn draw_ac(ctx: &mut BTerm, pt: Point, ac: i32) { - ctx.print_color(pt.x, pt.y, RGB::named(PINK), RGB::named(BLACK), "AC"); - ctx.print_color(pt.x + 2, pt.y, RGB::named(WHITE), RGB::named(BLACK), ac); -} - -fn draw_attributes(ctx: &mut BTerm, pt: Point, a: &Attributes) { - ctx.print_color(pt.x, pt.y, RGB::named(RED), RGB::named(BLACK), "STR"); - ctx.print_color(pt.x + 3, pt.y, RGB::named(WHITE), RGB::named(BLACK), a.strength.base); - ctx.print_color(pt.x + 7, pt.y, RGB::named(GREEN), RGB::named(BLACK), "DEX"); - ctx.print_color(pt.x + 10, pt.y, RGB::named(WHITE), RGB::named(BLACK), a.dexterity.base); - ctx.print_color(pt.x + 14, pt.y, RGB::named(ORANGE), RGB::named(BLACK), "CON"); - ctx.print_color(pt.x + 17, pt.y, RGB::named(WHITE), RGB::named(BLACK), a.constitution.base); - ctx.print_color(pt.x, 54, RGB::named(CYAN), RGB::named(BLACK), "INT"); - ctx.print_color(pt.x + 3, pt.y + 1, RGB::named(WHITE), RGB::named(BLACK), a.intelligence.base); - ctx.print_color(pt.x + 7, pt.y + 1, RGB::named(YELLOW), RGB::named(BLACK), "WIS"); - ctx.print_color(pt.x + 10, pt.y + 1, RGB::named(WHITE), RGB::named(BLACK), a.wisdom.base); - ctx.print_color(pt.x + 14, pt.y + 1, RGB::named(PURPLE), RGB::named(BLACK), "CHA"); - ctx.print_color(pt.x + 17, pt.y + 1, RGB::named(WHITE), RGB::named(BLACK), a.charisma.base); -} - -fn draw_hunger(ctx: &mut BTerm, pt: Point, hunger: &HungerClock) { - match hunger.state { - HungerState::Satiated => { - ctx.print_color_right( - pt.x, - pt.y, - get_hunger_colour(hunger.state), - RGB::named(BLACK), - "Satiated" - ); - } - HungerState::Normal => {} - HungerState::Hungry => { - ctx.print_color_right( - pt.x, - pt.y, - get_hunger_colour(hunger.state), - RGB::named(BLACK), - "Hungry" - ); - } - HungerState::Weak => { - ctx.print_color_right( - pt.x, - pt.y, - get_hunger_colour(hunger.state), - RGB::named(BLACK), - "Weak" - ); - } - HungerState::Fainting => { - ctx.print_color_right( - pt.x, - pt.y, - get_hunger_colour(hunger.state), - RGB::named(BLACK), - "Fainting" - ); - } - HungerState::Starving => { - ctx.print_color_right( - pt.x, - pt.y, - get_hunger_colour(hunger.state), - RGB::named(BLACK), - "Starving" - ); - } + if with_text { + ctx.print(sx - 1, sy, "["); + let health = format!("{}/{}", n, max); + ctx.print_color(sx + 1, sy, black, bg, health); + ctx.print(sx + width, sy, "]"); } } +pub const TEXT_FONT_MOD: i32 = 2; + pub fn draw_ui(ecs: &World, ctx: &mut BTerm) { + ctx.set_active_console(TEXT_LAYER); // Render stats let pools = ecs.read_storage::(); let attributes = ecs.read_storage::(); @@ -219,35 +129,138 @@ pub fn draw_ui(ecs: &World, ctx: &mut BTerm) { // Draw hp/mana bars draw_lerping_bar( ctx, - 2, + 2 * TEXT_FONT_MOD, 53, - 22, + 22 * TEXT_FONT_MOD, stats.hit_points.current, stats.hit_points.max, RGB::from_u8(0, 255, 0), - RGB::from_u8(255, 0, 0) + RGB::from_u8(255, 0, 0), + true, + true ); draw_lerping_bar( ctx, - 2, + 2 * TEXT_FONT_MOD, 54, - 22, + 22 * TEXT_FONT_MOD, stats.mana.current, stats.mana.max, RGB::named(BLUE), - RGB::named(BLACK) + RGB::named(BLACK), + true, + true ); - draw_ac(ctx, Point::new(26, 53), calc_ac(ecs, skills, stats, attributes)); - draw_xp(ctx, Point::new(26, 54), stats); - draw_attributes(ctx, Point::new(38, 53), attributes); - draw_hunger(ctx, Point::new(70, 53), hunger); - // Burden + // Draw AC + let skill_ac_bonus = gamesystem::skill_bonus(Skill::Defence, &*skills); + let mut armour_ac_bonus = 0; + let equipped = ecs.read_storage::(); + let ac = ecs.read_storage::(); let player_entity = ecs.fetch::(); + for (wielded, ac) in (&equipped, &ac).join() { + if wielded.owner == *player_entity { + armour_ac_bonus += ac.amount; + } + } + let armour_class = + stats.bac - attributes.dexterity.bonus / 2 - skill_ac_bonus - armour_ac_bonus; + ctx.print_color(26 * TEXT_FONT_MOD, 53, RGB::named(PINK), RGB::named(BLACK), "AC"); + ctx.print_color(28 * TEXT_FONT_MOD, 53, RGB::named(WHITE), RGB::named(BLACK), armour_class); + // Draw level + ctx.print_color( + 26 * TEXT_FONT_MOD, + 54, + RGB::named(WHITE), + RGB::named(BLACK), + format!("XP{}/{}", stats.level, stats.xp) + ); + // Draw attributes + let x = 38 * TEXT_FONT_MOD; + ctx.print_color(x, 53, RGB::named(RED), RGB::named(BLACK), "STR"); + ctx.print_color(x + 3, 53, RGB::named(WHITE), RGB::named(BLACK), attributes.strength.base); + ctx.print_color(x + 7, 53, RGB::named(GREEN), RGB::named(BLACK), "DEX"); + ctx.print_color( + x + 10, + 53, + RGB::named(WHITE), + RGB::named(BLACK), + attributes.dexterity.base + ); + ctx.print_color(x + 14, 53, RGB::named(ORANGE), RGB::named(BLACK), "CON"); + ctx.print_color( + x + 17, + 53, + RGB::named(WHITE), + RGB::named(BLACK), + attributes.constitution.base + ); + ctx.print_color(x, 54, RGB::named(CYAN), RGB::named(BLACK), "INT"); + ctx.print_color( + x + 3, + 54, + RGB::named(WHITE), + RGB::named(BLACK), + attributes.intelligence.base + ); + ctx.print_color(x + 7, 54, RGB::named(YELLOW), RGB::named(BLACK), "WIS"); + ctx.print_color(x + 10, 54, RGB::named(WHITE), RGB::named(BLACK), attributes.wisdom.base); + ctx.print_color(x + 14, 54, RGB::named(PURPLE), RGB::named(BLACK), "CHA"); + ctx.print_color(x + 17, 54, RGB::named(WHITE), RGB::named(BLACK), attributes.charisma.base); + // Draw hunger + match hunger.state { + HungerState::Satiated => { + ctx.print_color_right( + (VIEWPORT_W + 1) * TEXT_FONT_MOD, + 53, + get_hunger_colour(hunger.state), + RGB::named(BLACK), + "Satiated" + ); + } + HungerState::Normal => {} + HungerState::Hungry => { + ctx.print_color_right( + (VIEWPORT_W + 1) * TEXT_FONT_MOD, + 53, + get_hunger_colour(hunger.state), + RGB::named(BLACK), + "Hungry" + ); + } + HungerState::Weak => { + ctx.print_color_right( + (VIEWPORT_W + 1) * TEXT_FONT_MOD, + 53, + get_hunger_colour(hunger.state), + RGB::named(BLACK), + "Weak" + ); + } + HungerState::Fainting => { + ctx.print_color_right( + (VIEWPORT_W + 1) * TEXT_FONT_MOD, + 53, + get_hunger_colour(hunger.state), + RGB::named(BLACK), + "Fainting" + ); + } + HungerState::Starving => { + ctx.print_color_right( + (VIEWPORT_W + 1) * TEXT_FONT_MOD, + 53, + get_hunger_colour(hunger.state), + RGB::named(BLACK), + "Starving" + ); + } + } + // Burden if let Some(burden) = burden.get(*player_entity) { match burden.level { crate::BurdenLevel::Burdened => { ctx.print_color_right( - 70, + (VIEWPORT_W + 1) * TEXT_FONT_MOD, 50, RGB::named(BROWN1), RGB::named(BLACK), @@ -256,7 +269,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut BTerm) { } crate::BurdenLevel::Strained => { ctx.print_color_right( - 70, + (VIEWPORT_W + 1) * TEXT_FONT_MOD, 50, RGB::named(ORANGE), RGB::named(BLACK), @@ -264,27 +277,88 @@ pub fn draw_ui(ecs: &World, ctx: &mut BTerm) { ); } crate::BurdenLevel::Overloaded => { - ctx.print_color_right(70, 50, RGB::named(RED), RGB::named(BLACK), "Overloaded"); + ctx.print_color_right( + (VIEWPORT_W + 1) * TEXT_FONT_MOD, + 50, + RGB::named(RED), + RGB::named(BLACK), + "Overloaded" + ); } } } if stats.god { - ctx.print_color(20, 20, RGB::named(YELLOW), RGB::named(BLACK), "--- GODMODE: ON ---"); + ctx.print_color( + 20 * TEXT_FONT_MOD, + 20, + RGB::named(YELLOW), + RGB::named(BLACK), + "--- GODMODE: ON ---" + ); } // Draw equipment + let renderables = ecs.read_storage::(); + let mut equipment: Vec<(String, RGB, RGB, FontCharType)> = Vec::new(); + let entities = ecs.entities(); + for (entity, _equipped, renderable) in (&entities, &equipped, &renderables) + .join() + .filter(|item| item.1.owner == *player_entity) { + equipment.push(( + obfuscate_name_ecs(ecs, entity).0, + RGB::named(item_colour_ecs(ecs, entity)), + renderable.fg, + renderable.glyph, + )); + } let mut y = 1; - let equipment = items(&ecs, Filter::Equipped); if !equipment.is_empty() { - ctx.print_color(72, y, RGB::named(BLACK), RGB::named(WHITE), "Equipment"); - y += 1; - y = print_options(&ecs, &equipment, 72, y, ctx); - y += 1; + ctx.print_color( + (VIEWPORT_W + 3) * TEXT_FONT_MOD, + y, + RGB::named(BLACK), + RGB::named(WHITE), + "Equipment" + ); + let mut j = 0; + for item in equipment { + y += 1; + ctx.set( + (VIEWPORT_W + 3) * TEXT_FONT_MOD, + y, + RGB::named(YELLOW), + RGB::named(BLACK), + 97 + (j as FontCharType) + ); + j += 1; + ctx.set((VIEWPORT_W + 3) * TEXT_FONT_MOD + 2, y, item.2, RGB::named(BLACK), item.3); + ctx.print_color( + (VIEWPORT_W + 3) * TEXT_FONT_MOD + 4, + y, + item.1, + RGB::named(BLACK), + &item.0 + ); + ctx.print_color( + (VIEWPORT_W + 3) * TEXT_FONT_MOD + 4 + (item.0.len() as i32) + 1, + y, + RGB::named(WHITE), + RGB::named(BLACK), + "(worn)" + ); + } + y += 2; } - // Draw backpack - ctx.print_color(72, y, RGB::named(BLACK), RGB::named(WHITE), "Backpack"); + // Draw consumables ctx.print_color( - 81, + (VIEWPORT_W + 3) * TEXT_FONT_MOD, + y, + RGB::named(BLACK), + RGB::named(WHITE), + "Backpack" + ); + ctx.print_color( + (VIEWPORT_W + 12) * TEXT_FONT_MOD, y, RGB::named(WHITE), RGB::named(BLACK), @@ -296,26 +370,32 @@ pub fn draw_ui(ecs: &World, ctx: &mut BTerm) { ) ); y += 1; - let backpack = items(&ecs, Filter::Backpack); - y = print_options(&ecs, &backpack, 72, y, ctx); + let player_inventory = get_player_inventory(&ecs); + y = print_options(&player_inventory, (VIEWPORT_W + 3) * TEXT_FONT_MOD, y, ctx).0; // Draw spells - if we have any -- NYI! if let Some(known_spells) = ecs.read_storage::().get(*player_entity) { y += 1; // Draw known spells - ctx.print_color(72, y, RGB::named(BLACK), RGB::named(WHITE), "Known Spells"); + ctx.print_color( + (VIEWPORT_W + 3) * TEXT_FONT_MOD, + y, + RGB::named(BLACK), + RGB::named(WHITE), + "Known Spells" + ); y += 1; let mut index = 1; for spell in known_spells.list.iter() { ctx.print_color( - 72, + (VIEWPORT_W + 3) * TEXT_FONT_MOD, y, RGB::named(YELLOW), RGB::named(BLACK), &format!("{}", index) ); ctx.print_color( - 74, + (VIEWPORT_W + 3) * TEXT_FONT_MOD + 2, y, RGB::named(CYAN), RGB::named(BLACK), @@ -372,22 +452,34 @@ pub fn draw_ui(ecs: &World, ctx: &mut BTerm) { if !seen_entities.is_empty() { y += 1; - ctx.print_color(72, y, RGB::named(BLACK), RGB::named(WHITE), "In View"); + ctx.print_color( + (VIEWPORT_W + 3) * TEXT_FONT_MOD, + y, + RGB::named(BLACK), + RGB::named(WHITE), + "In View" + ); for entity in seen_entities { y += 1; - ctx.set(72, y, entity.2, RGB::named(BLACK), entity.3); - ctx.print_color(74, y, entity.1, RGB::named(BLACK), entity.0); + ctx.set((VIEWPORT_W + 3) * TEXT_FONT_MOD, y, entity.2, RGB::named(BLACK), entity.3); + ctx.print_color( + (VIEWPORT_W + 3) * TEXT_FONT_MOD + 2, + y, + entity.1, + RGB::named(BLACK), + entity.0 + ); } } } // Render the message log at [1, 7], ascending, with 7 lines and a max width of 68. gamelog::print_log( - &mut BACKEND_INTERNAL.lock().consoles[0].console, - Point::new(1, 7), + &mut BACKEND_INTERNAL.lock().consoles[TEXT_LAYER].console, + Point::new(1 * TEXT_FONT_MOD, 7), false, 7, - 68 + (VIEWPORT_W - 1) * TEXT_FONT_MOD ); // Render id @@ -397,12 +489,18 @@ pub fn draw_ui(ecs: &World, ctx: &mut BTerm) { } else { format!("{}", map.short_name) }; - ctx.print_color_right(70, 54, get_local_col(map.id), RGB::named(BLACK), &id); + ctx.print_color_right( + (VIEWPORT_W + 1) * TEXT_FONT_MOD, + 54, + get_local_col(map.id), + RGB::named(BLACK), + &id + ); // Render turn let turns = crate::gamelog::get_event_count(EVENT::COUNT_TURN); ctx.print_color_right( - 69 - id.len(), + VIEWPORT_W * TEXT_FONT_MOD - (id.len() as i32), 54, RGB::named(YELLOW), RGB::named(BLACK), @@ -410,10 +508,39 @@ pub fn draw_ui(ecs: &World, ctx: &mut BTerm) { ); // Boxes and tooltips last, so they draw over everything else. - ctx.draw_hollow_box(0, 0, 70, 8, RGB::named(WHITE), RGB::named(BLACK)); // Log box - ctx.draw_hollow_box(0, 9, 70, 42, RGB::named(WHITE), RGB::named(BLACK)); // Camera box - ctx.draw_hollow_box(0, 52, 70, 3, RGB::named(WHITE), RGB::named(BLACK)); // Stats box - ctx.draw_hollow_box(71, 0, 33, 55, RGB::named(WHITE), RGB::named(BLACK)); // Side box + ctx.draw_hollow_box( + 0 * TEXT_FONT_MOD, + 0, + (VIEWPORT_W + 1) * TEXT_FONT_MOD, + 8, + RGB::named(WHITE), + RGB::named(BLACK) + ); // Log box + ctx.draw_hollow_box( + 0 * TEXT_FONT_MOD, + 9, + (VIEWPORT_W + 1) * TEXT_FONT_MOD, + 42, + RGB::named(WHITE), + RGB::named(BLACK) + ); // Camera box + ctx.draw_hollow_box( + 0 * TEXT_FONT_MOD, + 52, + (VIEWPORT_W + 1) * TEXT_FONT_MOD, + 3, + RGB::named(WHITE), + RGB::named(BLACK) + ); // Stats box + ctx.draw_hollow_box( + (VIEWPORT_W + 2) * TEXT_FONT_MOD, + 0, + 33 * TEXT_FONT_MOD, + 55, + RGB::named(WHITE), + RGB::named(BLACK) + ); // Side box + ctx.set_active_console(TILE_LAYER); tooltip::draw_tooltips(ecs, ctx, None); } @@ -481,46 +608,46 @@ pub enum ItemMenuResult { } pub fn print_options( - _ecs: &World, inventory: &PlayerInventory, mut x: i32, mut y: i32, ctx: &mut BTerm -) -> i32 { +) -> (i32, i32) { + let mut j = 0; let initial_x: i32 = x; - let mut sorted: Vec<_> = inventory.iter().collect(); - sorted.sort_by(|a, b| a.1.idx.cmp(&b.1.idx)); - - for (info, slot) in sorted { + let mut width: i32 = -1; + for (item, (_e, item_count)) in inventory { x = initial_x; // Print the character required to access this item. i.e. (a) - if slot.idx < 26 { - ctx.set(x, y, RGB::named(YELLOW), RGB::named(BLACK), 97 + slot.idx); + if j < 26 { + ctx.set(x, y, RGB::named(YELLOW), RGB::named(BLACK), 97 + (j as FontCharType)); } else { // If we somehow have more than 26, start using capitals - ctx.set(x, y, RGB::named(YELLOW), RGB::named(BLACK), 65 - 26 + slot.idx); + ctx.set(x, y, RGB::named(YELLOW), RGB::named(BLACK), 65 - 26 + (j as FontCharType)); } x += 2; - let fg = RGB::from_u8(info.renderables.0, info.renderables.1, info.renderables.2); - ctx.set(x, y, fg, RGB::named(BLACK), info.glyph); + let fg = RGB::from_u8(item.renderables.0, item.renderables.1, item.renderables.2); + ctx.set(x, y, fg, RGB::named(BLACK), item.glyph); x += 2; - let fg = RGB::from_u8(info.rgb.0, info.rgb.1, info.rgb.2); - if slot.count > 1 { + let fg = RGB::from_u8(item.rgb.0, item.rgb.1, item.rgb.2); + if item_count > &1 { // If more than one, print the number and pluralise // i.e. (a) 3 daggers - ctx.print_color(x, y, fg, RGB::named(BLACK), slot.count); + ctx.print_color(x, y, fg, RGB::named(BLACK), item_count); x += 2; - ctx.print_color(x, y, fg, RGB::named(BLACK), info.display_name.plural.to_string()); + ctx.print_color(x, y, fg, RGB::named(BLACK), item.display_name.plural.to_string()); + let this_width = x - initial_x + (item.display_name.plural.len() as i32); + width = if width > this_width { width } else { this_width }; } else { - if info.display_name.singular.to_lowercase().ends_with("s") { + if item.display_name.singular.to_lowercase().ends_with("s") { ctx.print_color(x, y, fg, RGB::named(BLACK), "some"); x += 5; } else if ['a', 'e', 'i', 'o', 'u'] .iter() - .any(|&v| info.display_name.singular.to_lowercase().starts_with(v)) + .any(|&v| item.display_name.singular.to_lowercase().starts_with(v)) { // If one and starts with a vowel, print 'an' // i.e. (a) an apple @@ -532,54 +659,40 @@ pub fn print_options( ctx.print_color(x, y, fg, RGB::named(BLACK), "a"); x += 2; } - /* - let text = if let Some(worn) = ecs.read_storage::().get(slot.item) { - use crate::EquipmentSlot; - let text = match worn.slot { - EquipmentSlot::Melee | EquipmentSlot::Shield => "being held", - _ => "being worn", - }; - format!("{} ({})", info.display_name.singular.to_string(), text) - } else { - info.display_name.singular.to_string() - }; - */ - let text = info.display_name.singular.to_string(); - ctx.print_color(x, y, fg, RGB::named(BLACK), text); + ctx.print_color(x, y, fg, RGB::named(BLACK), item.display_name.singular.to_string()); + let this_width = x - initial_x + (item.display_name.singular.len() as i32); + width = if width > this_width { width } else { this_width }; } y += 1; + j += 1; } - return y; + return (y, width); } -const PADDING: i32 = 4; -const SOME: i32 = 4; -const AN: i32 = 2; -const A: i32 = 1; - pub fn get_max_inventory_width(inventory: &PlayerInventory) -> i32 { let mut width: i32 = 0; - for (item, slot) in inventory { - let mut this_width = item.display_name.singular.len() as i32; - if slot.count <= 1 { + for (item, (_e, count)) in inventory { + let mut this_width = 4; // The spaces before and after the character to select this item, etc. + if count <= &1 { + this_width += item.display_name.singular.len() as i32; if item.display_name.singular == item.display_name.plural { - this_width += SOME; + this_width += 4; // "some".len } else if ['a', 'e', 'i', 'o', 'u'].iter().any(|&v| item.display_name.singular.starts_with(v)) { - this_width += AN; + this_width += 2; // "an".len } else { - this_width += A; + this_width += 1; // "a".len } } else { - this_width = - (item.display_name.plural.len() as i32) + (slot.count.to_string().len() as i32); // i.e. "12".len + this_width += item.display_name.plural.len() as i32; + this_width += count.to_string().len() as i32; // i.e. "12".len } width = if width > this_width { width } else { this_width }; } - return width + PADDING; + return width; } // Inside the ECS @@ -626,7 +739,7 @@ pub fn obfuscate_name( if has_beatitude.known { let prefix = match has_beatitude.buc { BUC::Cursed => Some("cursed "), - BUC::Uncursed => Some("uncursed "), + BUC::Uncursed => None, BUC::Blessed => Some("blessed "), }; if prefix.is_some() { @@ -821,13 +934,13 @@ pub fn show_help(ctx: &mut BTerm) -> YesNoResult { } } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(PartialEq, Eq, PartialOrd, Ord)] struct DisplayName { singular: String, plural: String, } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(PartialEq, Eq, PartialOrd, Ord)] pub struct UniqueInventoryItem { display_name: DisplayName, rgb: (u8, u8, u8), @@ -837,74 +950,62 @@ pub struct UniqueInventoryItem { name: String, } -pub struct InventorySlot { - pub item: Entity, - pub count: i32, - pub idx: usize, -} +pub type PlayerInventory = BTreeMap; -pub type PlayerInventory = HashMap; - -pub enum Filter { - All, - Backpack, - Equipped, - Category(ItemType), -} - -macro_rules! includeitem { - ($inv:expr, $ecs:expr, $e:expr, $k:expr) => { - $inv.entry(unique_ecs($ecs, $e)) - .and_modify(|slot| { - slot.count += 1; - }) - .or_insert(InventorySlot { - item: $e, - count: 1, - idx: $k.idx, - }); - }; -} - -pub fn items(ecs: &World, filter: Filter) -> PlayerInventory { +pub fn get_player_inventory(ecs: &World) -> PlayerInventory { + let player_entity = ecs.fetch::(); + let names = ecs.read_storage::(); + let backpack = ecs.read_storage::(); let entities = ecs.entities(); - let keys = ecs.read_storage::(); - let mut inv: PlayerInventory = HashMap::new(); - match filter { - Filter::All => { - for (e, k) in (&entities, &keys).join() { - includeitem!(inv, ecs, e, k); + let renderables = ecs.read_storage::(); + + let mut player_inventory: BTreeMap = BTreeMap::new(); + for (entity, _pack, name, renderable) in (&entities, &backpack, &names, &renderables) + .join() + .filter(|item| item.1.owner == *player_entity) { + // RGB can't be used as a key. This is converting the RGB (tuple of f32) into a tuple of u8s. + let item_colour = item_colour_ecs(ecs, entity); + let renderables = ( + (renderable.fg.r * 255.0) as u8, + (renderable.fg.g * 255.0) as u8, + (renderable.fg.b * 255.0) as u8, + ); + let (singular, plural) = obfuscate_name_ecs(ecs, entity); + let beatitude_status = if let Some(beatitude) = ecs.read_storage::().get(entity) { + match beatitude.buc { + BUC::Blessed => 1, + BUC::Uncursed => 2, + BUC::Cursed => 3, } - } - Filter::Backpack => { - let backpack = ecs.read_storage::(); - for (e, k, _b) in (&entities, &keys, &backpack).join() { - includeitem!(inv, ecs, e, k); - } - } - Filter::Equipped => { - let equipped = ecs.read_storage::(); - for (e, k, _e) in (&entities, &keys, &equipped).join() { - includeitem!(inv, ecs, e, k); - } - } - Filter::Category(itemtype) => { - let items = ecs.read_storage::(); - for (e, k, _i) in (&entities, &keys, &items) - .join() - .filter(|e| e.2.category == itemtype) { - includeitem!(inv, ecs, e, k); - } - } + } else { + 0 + }; + let unique_item = UniqueInventoryItem { + display_name: DisplayName { singular: singular.clone(), plural: plural }, + rgb: item_colour, + renderables: renderables, + glyph: renderable.glyph, + beatitude_status: beatitude_status, + name: name.name.clone(), + }; + player_inventory + .entry(unique_item) + .and_modify(|(_e, count)| { + *count += 1; + }) + .or_insert((entity, 1)); } - inv + + return player_inventory; } pub fn show_inventory(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option) { - let player_inventory = items(&gs.ecs, Filter::Backpack); + ctx.set_active_console(TEXT_LAYER); + + let player_inventory = get_player_inventory(&gs.ecs); let count = player_inventory.len(); - let (x_offset, y_offset) = (1, 10); + let (x_offset, y_offset) = (1 * TEXT_FONT_MOD, 10); let on_overmap = gs.ecs.fetch::().overmap; let message = if !on_overmap { @@ -919,7 +1020,9 @@ pub fn show_inventory(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Optio let y = 3 + y_offset; let width = get_max_inventory_width(&player_inventory); ctx.draw_box(x, y, width + 2, (count + 1) as i32, RGB::named(WHITE), RGB::named(BLACK)); - print_options(&gs.ecs, &player_inventory, x + 1, y + 1, ctx); + print_options(&player_inventory, x + 1, y + 1, ctx); + + ctx.set_active_console(TILE_LAYER); match ctx.key { None => (ItemMenuResult::NoResponse, None), @@ -928,23 +1031,22 @@ pub fn show_inventory(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Optio VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), _ => { let selection = letter_to_option::letter_to_option(key, ctx.shift); - if selection != -1 && check_key(selection as usize) { + if selection > -1 && selection < (count as i32) { if on_overmap { gamelog::Logger ::new() .append("You can't use items on the overmap.") .log(); } else { - // Get the first entity with a Key {} component that has idx matching selection - let entities = gs.ecs.entities(); - let keyed_items = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - for (e, key, _b) in (&entities, &keyed_items, &backpack).join() { - if key.idx == (selection as usize) { - return (ItemMenuResult::Selected, Some(e)); - } - } - // TODO: Gamelog about not having selected item? + return ( + ItemMenuResult::Selected, + Some( + player_inventory + .iter() + .nth(selection as usize) + .unwrap().1.0 + ), + ); } } (ItemMenuResult::NoResponse, None) @@ -954,7 +1056,7 @@ pub fn show_inventory(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Optio } pub fn drop_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option) { - let player_inventory = items(&gs.ecs, Filter::Backpack); + let player_inventory = get_player_inventory(&gs.ecs); let count = player_inventory.len(); let (x_offset, y_offset) = (1, 10); @@ -972,7 +1074,7 @@ pub fn drop_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Optio let y = 3 + y_offset; let width = get_max_inventory_width(&player_inventory); ctx.draw_box(x, y, width + 2, (count + 1) as i32, RGB::named(WHITE), RGB::named(BLACK)); - print_options(&gs.ecs, &player_inventory, x + 1, y + 1, ctx); + print_options(&player_inventory, x + 1, y + 1, ctx); match ctx.key { None => (ItemMenuResult::NoResponse, None), @@ -980,23 +1082,23 @@ pub fn drop_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Optio match key { VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), _ => { - let selection = letter_to_option::letter_to_option(key, ctx.shift); - if selection != -1 && check_key(selection as usize) { + let selection = letter_to_option(key); + if selection > -1 && selection < (count as i32) { if on_overmap { gamelog::Logger ::new() .append("You can't drop items on the overmap.") .log(); } else { - // Get the first entity with a Key {} component that has an idx matching "selection". - let entities = gs.ecs.entities(); - let keyed_items = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - for (e, key, _b) in (&entities, &keyed_items, &backpack).join() { - if key.idx == (selection as usize) { - return (ItemMenuResult::Selected, Some(e)); - } - } + return ( + ItemMenuResult::Selected, + Some( + player_inventory + .iter() + .nth(selection as usize) + .unwrap().1.0 + ), + ); } } (ItemMenuResult::NoResponse, None) @@ -1006,8 +1108,11 @@ pub fn drop_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Optio } pub fn remove_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option) { - let player_inventory = items(&gs.ecs, Filter::Equipped); - let count = player_inventory.len(); + let player_entity = gs.ecs.fetch::(); + let backpack = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + let inventory = (&backpack).join().filter(|item| item.owner == *player_entity); + let count = inventory.count(); let (x_offset, y_offset) = (1, 10); @@ -1019,11 +1124,38 @@ pub fn remove_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Opt "Unequip what? [aA-zZ][Esc.]" ); + let mut equippable: Vec<(Entity, String)> = Vec::new(); + let mut width = 2; + for (entity, _pack) in (&entities, &backpack) + .join() + .filter(|item| item.1.owner == *player_entity) { + let this_name = &obfuscate_name_ecs(&gs.ecs, entity).0; + let this_width = 5 + this_name.len(); + width = if width > this_width { width } else { this_width }; + equippable.push((entity, this_name.to_string())); + } + let x = 1 + x_offset; - let y = 3 + y_offset; - let width = get_max_inventory_width(&player_inventory); - ctx.draw_box(x, y, width + 2, (count + 1) as i32, RGB::named(WHITE), RGB::named(BLACK)); - print_options(&gs.ecs, &player_inventory, x + 1, y + 1, ctx); + let mut y = 3 + y_offset; + + ctx.draw_box(x, y, width, (count + 1) as i32, RGB::named(WHITE), RGB::named(BLACK)); + y += 1; + + let mut j = 0; + let renderables = gs.ecs.read_storage::(); + for (e, name) in &equippable { + let (mut fg, glyph) = if let Some(renderable) = renderables.get(*e) { + (renderable.fg, renderable.glyph) + } else { + (RGB::named(WHITE), to_cp437('-')) + }; + ctx.set(x + 1, y, RGB::named(YELLOW), RGB::named(BLACK), 97 + (j as FontCharType)); + ctx.set(x + 3, y, fg, RGB::named(BLACK), glyph); + fg = RGB::named(item_colour_ecs(&gs.ecs, *e)); + ctx.print_color(x + 5, y, fg, RGB::named(BLACK), name); + y += 1; + j += 1; + } match ctx.key { None => (ItemMenuResult::NoResponse, None), @@ -1031,17 +1163,9 @@ pub fn remove_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Opt match key { VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), _ => { - let selection = letter_to_option::letter_to_option(key, ctx.shift); - if selection != -1 && check_key(selection as usize) { - // Get the first entity with a Key {} component that has an idx matching "selection". - let entities = gs.ecs.entities(); - let keyed_items = gs.ecs.read_storage::(); - let equipped = gs.ecs.read_storage::(); - for (e, key, _e) in (&entities, &keyed_items, &equipped).join() { - if key.idx == (selection as usize) { - return (ItemMenuResult::Selected, Some(e)); - } - } + let selection = letter_to_option(key); + if selection > -1 && selection < (count as i32) { + return (ItemMenuResult::Selected, Some(equippable[selection as usize].0)); } (ItemMenuResult::NoResponse, None) } @@ -1111,9 +1235,8 @@ pub fn ranged_target( &gs.ecs, ctx ); - let (screen_x, screen_y) = (69, 41); - let x = x.clamp(x_offset, x_offset - 1 + (screen_x as i32)); - let y = y.clamp(y_offset, y_offset - 1 + (screen_y as i32)); + let x = x.clamp(x_offset, x_offset - 1 + VIEWPORT_W); + let y = y.clamp(y_offset, y_offset - 1 + VIEWPORT_H); let mut mouse_pos_adjusted = mouse_pos; mouse_pos_adjusted.0 += min_x - x_offset; @@ -1429,7 +1552,7 @@ pub fn game_over(ctx: &mut BTerm) -> YesNoResult { } } -pub fn with_article(name: String) -> String { +pub fn with_article(name: &String) -> String { // If first letter is a capital if name.chars().nth(0).unwrap().is_uppercase() { return format!("{}", name); @@ -1441,72 +1564,3 @@ pub fn with_article(name: String) -> String { } format!("a {}", name) } - -pub fn unique( - entity: Entity, - names: &ReadStorage, - obfuscated_names: &ReadStorage, - renderables: &ReadStorage, - beatitudes: &ReadStorage, - magic_items: &ReadStorage, - charges: Option<&ReadStorage>, - dm: &MasterDungeonMap -) -> UniqueInventoryItem { - let item_colour = item_colour(entity, beatitudes); - let (singular, plural) = obfuscate_name( - entity, - names, - magic_items, - obfuscated_names, - beatitudes, - dm, - charges - ); - let (renderables, glyph) = if let Some(renderable) = renderables.get(entity) { - ( - ( - (renderable.fg.r * 255.0) as u8, - (renderable.fg.g * 255.0) as u8, - (renderable.fg.b * 255.0) as u8, - ), - renderable.glyph, - ) - } else { - unreachable!("Item has no renderable component.") - }; - let name = if let Some(name) = names.get(entity) { - name - } else { - unreachable!("Item has no name component.") - }; - let beatitude_status = if let Some(beatitude) = beatitudes.get(entity) { - match beatitude.buc { - BUC::Blessed => 1, - BUC::Uncursed => 2, - BUC::Cursed => 3, - } - } else { - 0 - }; - UniqueInventoryItem { - display_name: DisplayName { singular: singular.clone(), plural }, - rgb: item_colour, - renderables, - glyph, - beatitude_status, - name: name.name.clone(), - } -} - -pub fn unique_ecs(ecs: &World, entity: Entity) -> UniqueInventoryItem { - return unique( - entity, - &ecs.read_storage::(), - &ecs.read_storage::(), - &ecs.read_storage::(), - &ecs.read_storage::(), - &ecs.read_storage::(), - Some(&ecs.read_storage::()), - &ecs.fetch::() - ); -} diff --git a/src/gui/remove_curse_menu.rs b/src/gui/remove_curse_menu.rs index 68cea65..f8d1f14 100644 --- a/src/gui/remove_curse_menu.rs +++ b/src/gui/remove_curse_menu.rs @@ -3,11 +3,9 @@ use super::{ item_colour_ecs, obfuscate_name_ecs, print_options, - unique_ecs, - check_key, - letter_to_option, + renderable_colour, ItemMenuResult, - InventorySlot, + UniqueInventoryItem, }; use crate::{ gamelog, @@ -20,11 +18,10 @@ use crate::{ Renderable, states::state::*, BUC, - Key, }; use bracket_lib::prelude::*; use specs::prelude::*; -use std::collections::HashMap; +use std::collections::BTreeMap; /// Handles the Remove Curse menu. pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option) { @@ -36,12 +33,11 @@ pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option< let beatitudes = gs.ecs.read_storage::(); let names = gs.ecs.read_storage::(); let renderables = gs.ecs.read_storage::(); - let keys = gs.ecs.read_storage::(); let build_cursed_iterator = || { - (&entities, &items, &beatitudes, &renderables, &names, &keys) + (&entities, &items, &beatitudes, &renderables, &names) .join() - .filter(|(item_entity, _i, b, _r, _n, _k)| { + .filter(|(item_entity, _i, b, _r, _n)| { // Set all items to FALSE initially. let mut keep = false; // If found in the player's backpack, set to TRUE @@ -90,19 +86,34 @@ pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option< .log(); return (ItemMenuResult::Selected, Some(item)); } - let mut player_inventory: super::PlayerInventory = HashMap::new(); - for (entity, _i, _b, _r, _n, key) in build_cursed_iterator() { - let unique_item = unique_ecs(&gs.ecs, entity); + let mut player_inventory: super::PlayerInventory = BTreeMap::new(); + for (entity, _i, _b, renderable, name) in build_cursed_iterator() { + let (singular, plural) = obfuscate_name_ecs(&gs.ecs, entity); + let beatitude_status = if + let Some(beatitude) = gs.ecs.read_storage::().get(entity) + { + match beatitude.buc { + BUC::Blessed => 1, + BUC::Uncursed => 2, + BUC::Cursed => 3, + } + } else { + 0 + }; + let unique_item = UniqueInventoryItem { + display_name: super::DisplayName { singular: singular.clone(), plural: plural.clone() }, + rgb: item_colour_ecs(&gs.ecs, entity), + renderables: renderable_colour(&renderables, entity), + glyph: renderable.glyph, + beatitude_status: beatitude_status, + name: name.name.clone(), + }; player_inventory .entry(unique_item) - .and_modify(|slot| { - slot.count += 1; + .and_modify(|(_e, count)| { + *count += 1; }) - .or_insert(InventorySlot { - item: entity, - count: 1, - idx: key.idx, - }); + .or_insert((entity, 1)); } // Get display args let width = get_max_inventory_width(&player_inventory); @@ -117,7 +128,7 @@ pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option< "Decurse which item? [aA-zZ][Esc.]" ); ctx.draw_box(x, y, width + 2, count + 1, RGB::named(WHITE), RGB::named(BLACK)); - print_options(&gs.ecs, &player_inventory, x + 1, y + 1, ctx); + print_options(&player_inventory, x + 1, y + 1, ctx); // Input match ctx.key { None => (ItemMenuResult::NoResponse, None), @@ -125,17 +136,21 @@ pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option< match key { VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), _ => { - let selection = letter_to_option::letter_to_option(key, ctx.shift); - if selection != -1 && check_key(selection as usize) { - // Get the first entity with a Key {} component that has an idx matching "selection". - let entities = gs.ecs.entities(); - let keyed_items = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - for (e, key, _b) in (&entities, &keyed_items, &backpack).join() { - if key.idx == (selection as usize) { - return (ItemMenuResult::Selected, Some(e)); - } - } + let selection = letter_to_option(key); + if selection > -1 && selection < (count as i32) { + let item = player_inventory + .iter() + .nth(selection as usize) + .unwrap().1.0; + gamelog::Logger + ::new() + .append("You decurse the") + .colour(item_colour_ecs(&gs.ecs, item)) + .append_n(obfuscate_name_ecs(&gs.ecs, item).0) + .colour(WHITE) + .append("!") + .log(); + return (ItemMenuResult::Selected, Some(item)); } (ItemMenuResult::NoResponse, None) } diff --git a/src/gui/tooltip.rs b/src/gui/tooltip.rs index 94c3f97..1aebed0 100644 --- a/src/gui/tooltip.rs +++ b/src/gui/tooltip.rs @@ -12,6 +12,7 @@ use super::{ }; use crate::TileType; use crate::data::ids::*; +use crate::data::prelude::*; use bracket_lib::prelude::*; use specs::prelude::*; @@ -45,6 +46,7 @@ impl Tooltip { return (self.lines.len() as i32) + 2i32; } fn render(&self, ctx: &mut BTerm, x: i32, y: i32) { + ctx.set_active_console(TEXT_LAYER); ctx.draw_box( x, y, @@ -56,6 +58,7 @@ impl Tooltip { for (i, s) in self.lines.iter().enumerate() { ctx.print_color(x + 1, y + (i as i32) + 1, s.1, RGB::named(BLACK), &s.0); } + ctx.set_active_console(TILE_LAYER); } } @@ -111,12 +114,6 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) { if position.x == mouse_pos_adjusted.0 && position.y == mouse_pos_adjusted.1 { let mut tip = Tooltip::new(); tip.add(crate::gui::obfuscate_name_ecs(ecs, entity).0, renderable.fg); - let intrinsics = ecs.read_storage::(); - if let Some(intrinsics) = intrinsics.get(entity) { - if !intrinsics.list.is_empty() { - tip.add(intrinsics.describe(), RGB::named(WHITE)); - } - } // Attributes let attr = attributes.get(entity); if let Some(a) = attr { @@ -175,13 +172,15 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) { if mouse_pos.0 > 35 { // Render to the left arrow = to_cp437('→'); - arrow_x = mouse_pos.0 - 1; + arrow_x = mouse_pos.0 * 2 - 1; } else { // Render to the right arrow = to_cp437('←'); - arrow_x = mouse_pos.0 + 1; + arrow_x = (mouse_pos.0 + 1) * 2; } + ctx.set_active_console(TEXT_LAYER); ctx.set(arrow_x, arrow_y, white, RGB::named(BLACK), arrow); + ctx.set_active_console(TILE_LAYER); let mut total_height = 0; for t in tooltips.iter() { @@ -195,9 +194,9 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) { for t in tooltips.iter() { let x = if mouse_pos.0 > 35 { - mouse_pos.0 - (1 + t.width()) + (mouse_pos.0 * 2) - (1 + t.width()) } else { - mouse_pos.0 + (1 + 1) + (mouse_pos.0 * 2) + 2 + 1 }; t.render(ctx, x, y); y += t.height(); diff --git a/src/inventory/collection_system.rs b/src/inventory/collection_system.rs index 70fb25c..2fb7276 100644 --- a/src/inventory/collection_system.rs +++ b/src/inventory/collection_system.rs @@ -12,7 +12,6 @@ use crate::{ ObfuscatedName, Position, WantsToPickupItem, - WantsToAssignKey, }; use specs::prelude::*; use crate::data::messages; @@ -34,7 +33,6 @@ impl<'a> System<'a> for ItemCollectionSystem { ReadStorage<'a, Beatitude>, ReadExpect<'a, MasterDungeonMap>, ReadStorage<'a, Charges>, - ReadStorage<'a, WantsToAssignKey>, ); fn run(&mut self, data: Self::SystemData) { @@ -50,11 +48,17 @@ impl<'a> System<'a> for ItemCollectionSystem { beatitudes, dm, wands, - wants_key, ) = data; - let mut to_remove: Vec = Vec::new(); - // For every item that wants to be picked up that *isn't* waiting on a key assignment. - for (pickup, _key) in (&wants_pickup, !&wants_key).join() { + + for pickup in wants_pickup.join() { + positions.remove(pickup.item); + backpack + .insert(pickup.item, InBackpack { owner: pickup.collected_by }) + .expect("Unable to pickup item."); + equipment_changed + .insert(pickup.collected_by, EquipmentChanged {}) + .expect("Unable to insert EquipmentChanged."); + if pickup.collected_by == *player_entity { gamelog::Logger ::new() @@ -78,17 +82,8 @@ impl<'a> System<'a> for ItemCollectionSystem { .period() .log(); } - positions.remove(pickup.item); - backpack - .insert(pickup.item, InBackpack { owner: pickup.collected_by }) - .expect("Unable to pickup item"); - equipment_changed - .insert(pickup.collected_by, EquipmentChanged {}) - .expect("Unable to insert EquipmentChanged"); - to_remove.push(pickup.collected_by); - } - for item in to_remove.iter() { - wants_pickup.remove(*item); } + + wants_pickup.clear(); } } diff --git a/src/inventory/drop_system.rs b/src/inventory/drop_system.rs index af2d8a2..34084b4 100644 --- a/src/inventory/drop_system.rs +++ b/src/inventory/drop_system.rs @@ -12,7 +12,6 @@ use crate::{ ObfuscatedName, Position, WantsToDropItem, - WantsToRemoveKey, }; use specs::prelude::*; use crate::data::messages; @@ -35,7 +34,6 @@ impl<'a> System<'a> for ItemDropSystem { ReadStorage<'a, ObfuscatedName>, ReadExpect<'a, MasterDungeonMap>, ReadStorage<'a, Charges>, - WriteStorage<'a, WantsToRemoveKey>, ); fn run(&mut self, data: Self::SystemData) { @@ -52,7 +50,6 @@ impl<'a> System<'a> for ItemDropSystem { obfuscated_names, dm, wands, - mut keys, ) = data; for (entity, to_drop) in (&entities, &wants_drop).join() { @@ -71,9 +68,6 @@ impl<'a> System<'a> for ItemDropSystem { backpack.remove(to_drop.item); if entity == *player_entity { - keys.insert(to_drop.item, WantsToRemoveKey {}).expect( - "Unable to insert WantsToRemoveKey" - ); gamelog::Logger ::new() .append(messages::YOU_DROP_ITEM) diff --git a/src/inventory/keyhandling.rs b/src/inventory/keyhandling.rs deleted file mode 100644 index 7194a18..0000000 --- a/src/inventory/keyhandling.rs +++ /dev/null @@ -1,153 +0,0 @@ -use crate::{ - gamelog, - gui::unique, - Beatitude, - Charges, - MagicItem, - MasterDungeonMap, - Name, - ObfuscatedName, - Stackable, - Renderable, - WantsToAssignKey, - WantsToRemoveKey, - Key, -}; -use specs::prelude::*; -use crate::data::messages; -use bracket_lib::prelude::*; -use crate::invkeys::*; - -pub struct KeyHandling {} - -const DEBUG_KEYHANDLING: bool = true; - -impl<'a> System<'a> for KeyHandling { - #[allow(clippy::type_complexity)] - type SystemData = ( - Entities<'a>, - WriteStorage<'a, WantsToAssignKey>, - WriteStorage<'a, WantsToRemoveKey>, - WriteStorage<'a, Key>, - ReadStorage<'a, Stackable>, - ReadStorage<'a, Name>, - ReadStorage<'a, ObfuscatedName>, - ReadStorage<'a, Renderable>, - ReadStorage<'a, Beatitude>, - ReadStorage<'a, MagicItem>, - ReadStorage<'a, Charges>, - ReadExpect<'a, MasterDungeonMap>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - entities, - mut wants_keys, - mut wants_removekey, - mut keys, - stackable, - names, - obfuscated_names, - renderables, - beatitudes, - magic_items, - wands, - dm, - ) = data; - - // For every entity that wants to be picked up, that still needs a key assigned. - for (e, _wants_key) in (&entities, &wants_keys).join() { - if DEBUG_KEYHANDLING { - console::log(&format!("KEYHANDLING: Assigning key to {:?}", e)); - } - let (stacks, mut handled, unique) = ( - if let Some(_) = stackable.get(e) { true } else { false }, - false, - unique( - e, - &names, - &obfuscated_names, - &renderables, - &beatitudes, - &magic_items, - Some(&wands), - &dm - ), - ); - if stacks { - console::log(&format!("KEYHANDLING: Item is stackable.")); - let maybe_key = item_exists(&unique); - if maybe_key.is_some() { - console::log(&format!("KEYHANDLING: Existing stack found for this item.")); - let key = maybe_key.unwrap(); - keys.insert(e, Key { idx: key }).expect("Unable to insert Key."); - console::log(&format!("KEYHANDLING: Assigned key idx {} to item.", key)); - handled = true; - } - } - if !handled { - console::log( - &format!("KEYHANDLING: Item is not stackable, or no existing stack found.") - ); - if let Some(idx) = assign_next_available() { - console::log( - &format!("KEYHANDLING: Assigned next available index {} to item.", idx) - ); - keys.insert(e, Key { idx }).expect("Unable to insert Key."); - register_stackable(stacks, unique, idx); - } else { - console::log(&format!("KEYHANDLING: No more keys available.")); - gamelog::Logger - ::new() - .append(messages::NO_MORE_KEYS) - .colour(WHITE) - .period() - .log(); - } - } - } - for (e, _wants_key) in (&entities, &wants_removekey).join() { - let idx = keys.get(e).unwrap().idx; - if DEBUG_KEYHANDLING { - console::log(&format!("KEYHANDLING: Removing key from {:?}", e)); - } - // If the item is *not* stackable, then we can just remove the key and clear the index. - if let None = stackable.get(e) { - console::log( - &format!("KEYHANDLING: Item is not stackable, clearing index {}.", idx) - ); - clear_idx(idx); - keys.remove(e); - continue; - } - // If the item *is* stackable, then we need to check if there are any other items that - // share this key assignment, before clearing the index. - console::log( - &format!( - "KEYHANDLING: Item is stackable, checking if any other items share this key." - ) - ); - let mut sole_item_with_key = true; - for (entity, key) in (&entities, &keys).join() { - if entity != e && key.idx == idx { - console::log(&format!("KEYHANDLING: Another item shares index {}", idx)); - sole_item_with_key = false; - break; - } - } - // If no other items shared this key, free up the index. - if sole_item_with_key { - console::log( - &format!("KEYHANDLING: No other items found, clearing index {}.", idx) - ); - clear_idx(idx); - } - // Either way, remove the key component from this item, because we're dropping it. - console::log(&format!("KEYHANDLING: Removing key component from item.")); - keys.remove(e); - } - - wants_removekey.clear(); - wants_keys.clear(); - } -} diff --git a/src/inventory/mod.rs b/src/inventory/mod.rs index 76748e0..eceaccb 100644 --- a/src/inventory/mod.rs +++ b/src/inventory/mod.rs @@ -4,7 +4,6 @@ mod equip_system; mod identification_system; mod remove_system; mod use_system; -mod keyhandling; pub use self::{ collection_system::ItemCollectionSystem, @@ -13,5 +12,4 @@ pub use self::{ identification_system::ItemIdentificationSystem, remove_system::ItemRemoveSystem, use_system::ItemUseSystem, - keyhandling::KeyHandling, }; diff --git a/src/invkeys.rs b/src/invkeys.rs deleted file mode 100644 index 2cee2f4..0000000 --- a/src/invkeys.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::sync::Mutex; -use std::collections::HashMap; -use crate::gui::UniqueInventoryItem; - -lazy_static! { - pub static ref INVKEYS: Mutex> = Mutex::new(HashMap::new()); - pub static ref ASSIGNEDKEYS: Mutex> = Mutex::new(vec![false; 52]); -} - -/// For (de)serialization. -pub fn clone_invkeys() -> HashMap { - let invkeys = INVKEYS.lock().unwrap(); - invkeys.clone() -} -pub fn restore_invkeys(invkeys: HashMap) { - INVKEYS.lock().unwrap().clear(); - INVKEYS.lock().unwrap().extend(invkeys); -} - -pub fn check_key(idx: usize) -> bool { - let lock = ASSIGNEDKEYS.lock().unwrap(); - lock[idx] -} - -pub fn item_exists(item: &UniqueInventoryItem) -> Option { - let invkeys = INVKEYS.lock().unwrap(); - use bracket_lib::prelude::*; - console::log(&format!("{:?}", item)); - if invkeys.contains_key(item) { - Some(*invkeys.get(item).unwrap()) - } else { - None - } -} - -pub fn assign_next_available() -> Option { - let mut lock = ASSIGNEDKEYS.lock().unwrap(); - for (i, key) in lock.iter_mut().enumerate() { - if !*key { - *key = true; - return Some(i); - } - } - None -} - -pub fn register_stackable(stacks: bool, item: UniqueInventoryItem, idx: usize) { - if stacks { - let mut invkeys = INVKEYS.lock().unwrap(); - invkeys.insert(item, idx); - } -} - -pub fn clear_idx(idx: usize) { - let mut lock = ASSIGNEDKEYS.lock().unwrap(); - lock[idx] = false; - let mut invkeys = INVKEYS.lock().unwrap(); - invkeys.retain(|_k, v| *v != idx); -} diff --git a/src/lib.rs b/src/lib.rs index e184a58..812c7be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,9 +8,6 @@ extern crate serde; #[macro_use] extern crate lazy_static; -#[macro_use] -pub mod macros; - pub mod camera; pub mod components; pub mod raws; @@ -38,7 +35,6 @@ pub mod rex_assets; pub mod spatial; pub mod morgue; pub mod states; -pub mod invkeys; pub use components::*; use particle_system::ParticleBuilder; diff --git a/src/macros/mod.rs b/src/macros/mod.rs deleted file mode 100644 index a064f44..0000000 --- a/src/macros/mod.rs +++ /dev/null @@ -1,93 +0,0 @@ -// macros/mod.rs - -#[macro_export] -/// Used to check if the player has a given component. -macro_rules! player_has_component { - ($ecs:expr, $component:ty) => { - { - let player = $ecs.fetch::(); - let component = $ecs.read_storage::<$component>(); - if let Some(player_component) = component.get(*player) { - true - } else { - false - } - } - }; -} - -#[macro_export] -/// Used to check if a given entity has a given Intrinsic. -macro_rules! has { - ($ecs:expr, $entity:expr, $intrinsic:expr) => { - { - let intrinsics = $ecs.read_storage::(); - if let Some(has_intrinsics) = intrinsics.get($entity) { - has_intrinsics.list.contains(&$intrinsic) - } else { - false - } - } - }; -} - -#[macro_export] -/// Used to check if the player has a given Intrinsic. -macro_rules! player_has { - ($ecs:expr, $intrinsic:expr) => { - { - let player = $ecs.fetch::(); - let intrinsics = $ecs.read_storage::(); - if let Some(player_intrinsics) = intrinsics.get(*player) { - player_intrinsics.list.contains(&$intrinsic) - } else { - false - } - } - }; -} - -#[macro_export] -/// Handles adding an Intrinsic to the player, and adding it to the IntrinsicChanged component. -macro_rules! add_intr { - ($ecs:expr, $entity:expr, $intrinsic:expr) => { - { - let mut intrinsics = $ecs.write_storage::(); - if let Some(player_intrinsics) = intrinsics.get_mut($entity) { - if !player_intrinsics.list.contains(&$intrinsic) { - player_intrinsics.list.insert($intrinsic); - let mut intrinsic_changed = $ecs.write_storage::(); - if let Some(this_intrinsic_changed) = intrinsic_changed.get_mut($entity) { - this_intrinsic_changed.gained.insert($intrinsic); - } else { - intrinsic_changed.insert($entity, crate::IntrinsicChanged { - gained: { - let mut m = std::collections::HashSet::new(); - m.insert($intrinsic); - m - }, - lost: std::collections::HashSet::new() - }).expect("Failed to insert IntrinsicChanged component."); - } - } - } else { - intrinsics.insert($entity, crate::Intrinsics { - list: { - let mut m = std::collections::HashSet::new(); - m.insert($intrinsic); - m - } - }).expect("Failed to insert Intrinsics component."); - let mut intrinsic_changed = $ecs.write_storage::(); - intrinsic_changed.insert($entity, crate::IntrinsicChanged { - gained: { - let mut m = std::collections::HashSet::new(); - m.insert($intrinsic); - m - }, - lost: std::collections::HashSet::new() - }).expect("Failed to insert IntrinsicChanged component."); - } - } - }; -} diff --git a/src/main.rs b/src/main.rs index fc2c72b..3cdaa0a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,22 +3,43 @@ use specs::prelude::*; use specs::saveload::{ SimpleMarker, SimpleMarkerAllocator }; use bracket_lib::prelude::*; -const DISPLAYWIDTH: i32 = 105; +const DISPLAYWIDTH: i32 = 100; const DISPLAYHEIGHT: i32 = 56; fn main() -> BError { // Embedded resources for use in wasm build - const CURSES_14_16_BYTES: &[u8] = include_bytes!("../resources/curses14x16.png"); - EMBED.lock().add_resource("resources/curses14x16.png".to_string(), CURSES_14_16_BYTES); + { + const WORLD_16_16_BYTES: &[u8] = include_bytes!("../resources/world16x16.png"); + const CURSES_16_16_BYTES: &[u8] = include_bytes!("../resources/curses16x16.png"); + const CURSES_8_16_BYTES: &[u8] = include_bytes!("../resources/curses8x16.png"); + const SINGLE_1_1_BYTES: &[u8] = include_bytes!("../resources/healthbar22x2.png"); + let mut lock = bracket_lib::terminal::EMBED.lock(); + lock.add_resource("resources/world16x16.png".to_string(), WORLD_16_16_BYTES); + lock.add_resource("resources/curses16x16.png".to_string(), CURSES_16_16_BYTES); + lock.add_resource("resources/curses8x16.png".to_string(), CURSES_8_16_BYTES); + lock.add_resource("resources/healthbar22x2.png".to_string(), SINGLE_1_1_BYTES); + } - //link_resource!(CURSES14X16, "../resources/curses_14x16.png"); + let world_sheet = SpriteSheet { + filename: "resources/world16x16.png".to_string(), + sprites: register_spritesheet(16, 16, 19, 16), + backing: None, + }; let mut context = BTermBuilder::new() .with_title("rust-rl") .with_dimensions(DISPLAYWIDTH, DISPLAYHEIGHT) - .with_font("curses14x16.png", 14, 16) - .with_tile_dimensions(14, 16) - .with_simple_console(DISPLAYWIDTH, DISPLAYHEIGHT, "curses14x16.png") + .with_font("curses16x16.png", 16, 16) + .with_font("curses8x16.png", 8, 16) + .with_font("healthbar22x2.png", 1, 1) + .with_tile_dimensions(16, 16) + .with_gutter(2) + .with_sprite_console(DISPLAYWIDTH * 16, DISPLAYHEIGHT * 16, 0) + .with_sprite_sheet(world_sheet) + .with_simple_console_no_bg(DISPLAYWIDTH, DISPLAYHEIGHT, "curses16x16.png") + .with_simple_console_no_bg(DISPLAYWIDTH, DISPLAYHEIGHT, "curses16x16.png") + .with_sparse_console(DISPLAYWIDTH * 2, DISPLAYHEIGHT, "curses8x16.png") + .with_sparse_console(DISPLAYWIDTH * 16, DISPLAYHEIGHT * 16, "healthbar22x2.png") .build()?; if config::CONFIG.visuals.with_scanlines { context.with_post_scanlines(config::CONFIG.visuals.with_screen_burn); @@ -111,12 +132,6 @@ fn main() -> BError { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); gs.ecs.register::>(); gs.ecs.register::(); gs.ecs.register::(); @@ -142,3 +157,15 @@ fn main() -> BError { main_loop(context, gs) } + +fn register_spritesheet(width: i32, height: i32, rows: i32, columns: i32) -> Vec { + let mut sprites: Vec = Vec::new(); + for y in 0..rows { + for x in 0..columns { + sprites.push( + Sprite::new(Rect::with_size(x * width + 1, y * height + 1, width, height)) + ); + } + } + sprites +} diff --git a/src/map/dungeon.rs b/src/map/dungeon.rs index 4407efa..ed0bf91 100644 --- a/src/map/dungeon.rs +++ b/src/map/dungeon.rs @@ -112,26 +112,7 @@ fn make_scroll_name(rng: &mut RandomNumberGenerator) -> String { return name; } -const POTION_COLOURS: &[&str] = &[ - "red", - "orange", - "yellow", - "green", - "blue", - "indigo", - "violet", - "black", - "white", - "silver", - "gold", - "rainbow", - "blood", - "purple", - "cyan", - "brown", - "grey", - "octarine", -]; +const POTION_COLOURS: &[&str] = &["blue", "red", "green", "yellow", "black"]; const POTION_ADJECTIVES: &[&str] = &[ "swirling", "viscous", diff --git a/src/map/mod.rs b/src/map/mod.rs index 99a0c6a..9bcacca 100644 --- a/src/map/mod.rs +++ b/src/map/mod.rs @@ -2,7 +2,15 @@ use bracket_lib::prelude::*; use serde::{ Deserialize, Serialize }; use std::collections::{ HashSet, HashMap }; mod tiletype; -pub use tiletype::{ tile_cost, tile_opaque, tile_walkable, TileType, get_dest, Destination }; +pub use tiletype::{ + tile_cost, + tile_opaque, + tile_walkable, + tile_blocks_telepathy, + TileType, + get_dest, + Destination, +}; mod interval_spawning_system; pub use interval_spawning_system::{ maybe_map_message, try_spawn_interval }; pub mod dungeon; diff --git a/src/map/themes.rs b/src/map/themes.rs index 8ddcc2a..bd6fcf3 100644 --- a/src/map/themes.rs +++ b/src/map/themes.rs @@ -5,6 +5,22 @@ use crate::data::ids::*; use bracket_lib::prelude::*; use std::ops::{ Add, Mul }; +pub fn get_sprite_for_id(idx: usize, map: &Map, other_pos: Option) -> (usize, RGBA) { + let x = (idx as i32) % map.width; + let y = (idx as i32) / map.width; + let tile = map.tiles[idx]; + let base = match tile { + TileType::Wall => wall_sprite(tile.sprite(), map, x, y), + _ => tile.sprite(), + }; + let sprite_id = pick_variant(base, tile.variants(), idx, map); + let tint = if !map.visible_tiles[idx] { + RGBA::from_f32(0.75, 0.75, 0.75, 1.0) + } else { + RGBA::named(WHITE) + }; + return (sprite_id, tint); +} /// Gets the renderables for a tile, with darkening/offset/post-processing/etc. Passing a val for "debug" will ignore viewshed. pub fn get_tile_renderables_for_id( idx: usize, @@ -142,6 +158,20 @@ fn is_revealed_and_wall(map: &Map, x: i32, y: i32, debug: Option) -> bool (if debug.is_none() { map.revealed_tiles[idx] } else { true }) } +fn wall_sprite(id: usize, map: &Map, x: i32, y: i32) -> usize { + if y > map.height - (2 as i32) { + return id; + } + if is_revealed_and_wall(map, x, y + 1, None) { + return id + 6; + } + return id; +} + +fn pick_variant(base: usize, variants: usize, idx: usize, map: &Map) -> usize { + return base + ((map.colour_offset[idx].0.0 * (variants as f32)) as usize); +} + fn wall_glyph(map: &Map, x: i32, y: i32, debug: Option) -> FontCharType { if x < 1 || diff --git a/src/map/tiletype.rs b/src/map/tiletype.rs index f451c9b..beb483c 100644 --- a/src/map/tiletype.rs +++ b/src/map/tiletype.rs @@ -1,4 +1,5 @@ use serde::{ Deserialize, Serialize }; +use crate::data::sprites::*; #[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize, Debug)] pub enum TileType { @@ -27,9 +28,64 @@ pub enum TileType { ToOvermap(i32), ToLocal(i32), } + +impl TileType { + pub fn sprite(&self) -> usize { + match self { + TileType::ImpassableMountain => STATUE, + TileType::Wall => WALL_BASE, + TileType::DeepWater => WATER_DEEP, + TileType::Fence => WALL_BASE, + TileType::Bars => WALL_BASE, + TileType::Floor => FLOOR, + TileType::WoodFloor => FLOOR_WOOD, + TileType::Gravel => FLOOR, + TileType::Road => PATH_GRASS, + TileType::Grass => FLOOR_GRASS, + TileType::Foliage => FLOOR_GRASS, + TileType::HeavyFoliage => FLOOR_GRASS, + TileType::Sand => FLOOR, + TileType::ShallowWater => WATER_DEEP, + TileType::Bridge => FLOOR, + TileType::DownStair => STAIR_D, + TileType::UpStair => STAIR_A, + TileType::ToLocal(_) => MUSHROOM, + TileType::ToOvermap(_) => MUSHROOM_ORANGE, + } + } + + pub fn variants(&self) -> usize { + match self { + TileType::ImpassableMountain => 1, + TileType::Wall => 4, + TileType::DeepWater => 2, + TileType::Fence => 1, + TileType::Bars => 1, + TileType::Floor => 6, + TileType::WoodFloor => 3, + TileType::Gravel => 1, + TileType::Road => 4, + TileType::Grass => 6, + TileType::Foliage => 1, + TileType::HeavyFoliage => 1, + TileType::Sand => 1, + TileType::ShallowWater => 2, + TileType::Bridge => 1, + TileType::DownStair => 1, + TileType::UpStair => 1, + TileType::ToLocal(_) => 1, + TileType::ToOvermap(_) => 1, + } + } +} + pub fn tile_walkable(tt: TileType) -> bool { match tt { - TileType::ImpassableMountain | TileType::Wall | TileType::DeepWater | TileType::Fence | TileType::Bars => false, + | TileType::ImpassableMountain + | TileType::Wall + | TileType::DeepWater + | TileType::Fence + | TileType::Bars => false, _ => true, } } @@ -40,6 +96,11 @@ pub fn tile_opaque(tt: TileType) -> bool { _ => false, } } +pub fn tile_blocks_telepathy(tt: TileType) -> bool { + match tt { + _ => false, + } +} pub fn tile_cost(tt: TileType) -> f32 { match tt { TileType::Road => 0.75, diff --git a/src/particle_system.rs b/src/particle_system.rs index 69e12b7..b576984 100644 --- a/src/particle_system.rs +++ b/src/particle_system.rs @@ -81,6 +81,7 @@ fn create_delayed_particles(ecs: &mut World, ctx: &BTerm) { .expect("Could not insert position"); renderables .insert(p, Renderable { + sprite: None, // TODO: Particle sprite fg: handled.fg, bg: handled.bg, glyph: handled.glyph, @@ -306,6 +307,7 @@ impl<'a> System<'a> for ParticleSpawnSystem { .expect("Could not insert position"); renderables .insert(p, Renderable { + sprite: None, // TODO: Particle sprite fg: new_particle.fg, bg: new_particle.bg, glyph: new_particle.glyph, diff --git a/src/player.rs b/src/player.rs index 36af74b..d91ba3b 100644 --- a/src/player.rs +++ b/src/player.rs @@ -30,7 +30,6 @@ use super::{ Viewshed, WantsToMelee, WantsToPickupItem, - WantsToAssignKey, get_dest, Destination, DamageType, @@ -40,6 +39,7 @@ use specs::prelude::*; use std::cmp::{ max, min }; use crate::data::events::*; use crate::data::ids::*; +use crate::gui::with_article; pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState { let mut positions = ecs.write_storage::(); @@ -134,6 +134,7 @@ pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState { let mut renderables = ecs.write_storage::(); let render_data = renderables.get_mut(potential_target).unwrap(); render_data.glyph = to_cp437('+'); // Nethack open door, maybe just use '/' instead. + render_data.sprite = Some(17); // TODO: Enum door_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y)); } result = RunState::Ticking; @@ -231,6 +232,7 @@ pub fn open(i: i32, j: i32, ecs: &mut World) -> RunState { let mut renderables = ecs.write_storage::(); let render_data = renderables.get_mut(potential_target).unwrap(); render_data.glyph = to_cp437('▓'); // Nethack open door, maybe just use '/' instead. + render_data.sprite = Some(18); // TODO: Enum door_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y)); } result = RunState::Ticking; @@ -561,11 +563,11 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState let mut logger = gamelog::Logger::new().append("You see"); for i in 0..seen_items.len() { if i > 0 && i < seen_items.len() { - logger = logger.append(", a"); + logger = logger.append(", "); } logger = logger .colour(seen_items[i].1) - .append_n(&seen_items[i].0) + .append_n(with_article(&seen_items[i].0)) .colour(WHITE); } logger.period().log(); @@ -634,9 +636,7 @@ fn get_item(ecs: &mut World) -> RunState { return RunState::AwaitingInput; } Some(item) => { - let mut assignkey = ecs.write_storage::(); let mut pickup = ecs.write_storage::(); - assignkey.insert(item, WantsToAssignKey {}).expect("Unable to insert WantsToAssignKey"); pickup .insert(*player_entity, WantsToPickupItem { collected_by: *player_entity, item }) .expect("Unable to insert want to pickup item."); diff --git a/src/raws/item_structs.rs b/src/raws/item_structs.rs index c670e54..1bd1a39 100644 --- a/src/raws/item_structs.rs +++ b/src/raws/item_structs.rs @@ -6,7 +6,6 @@ pub struct Item { pub id: String, pub name: Name, pub renderable: Option, - pub class: String, pub weight: Option, pub value: Option, pub equip: Option, @@ -31,6 +30,7 @@ pub struct Equippable { #[derive(Deserialize, Debug)] pub struct Renderable { pub glyph: String, + pub sprite: Option, pub fg: String, pub bg: String, pub order: i32, diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index 0e2b227..d15b7c7 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -66,7 +66,6 @@ macro_rules! apply_flags { "IDENTIFY" => $eb = $eb.with(ProvidesIdentify {}), "DIGGER" => $eb = $eb.with(Digger {}), "MAGICMAP" => $eb = $eb.with(MagicMapper {}), - "STACKABLE" => $eb = $eb.with(Stackable {}), // CAN BE DESTROYED BY DAMAGE "DESTRUCTIBLE" => $eb = $eb.with(Destructible {}), // --- EQUIP SLOTS --- @@ -282,7 +281,6 @@ pub fn spawn_named_item( if known_beatitude && !identified_items.contains(&item_template.name.name) { dm.identified_items.insert(item_template.name.name.clone()); } - let needs_key = is_player_owned(&player_entity, &pos); std::mem::drop(player_entity); std::mem::drop(dm); // -- DROP EVERYTHING THAT INVOLVES THE ECS BEFORE THIS POINT --- @@ -295,23 +293,9 @@ pub fn spawn_named_item( eb = eb.with(Item { weight: item_template.weight.unwrap_or(0.0), value: item_template.value.unwrap_or(0.0), - category: match item_template.class.as_str() { - "amulet" => ItemType::Amulet, - "weapon" => ItemType::Weapon, - "armour" => ItemType::Armour, - "comestible" => ItemType::Comestible, - "scroll" => ItemType::Scroll, - "spellbook" => ItemType::Spellbook, - "potion" => ItemType::Potion, - "ring" => ItemType::Ring, - "wand" => ItemType::Wand, - _ => unreachable!("Unknown item type."), - }, }); eb = spawn_position(pos, eb, key, raws); - if needs_key { - eb = eb.with(WantsToAssignKey {}); - } + if let Some(renderable) = &item_template.renderable { eb = eb.with(get_renderable_component(renderable)); } @@ -408,7 +392,6 @@ pub fn spawn_named_mob( if raws.mob_index.contains_key(key) { let mob_template = &raws.raws.mobs[raws.mob_index[key]]; let mut player_level = 1; - let needs_key; { let pools = ecs.read_storage::(); let player_entity = ecs.fetch::(); @@ -416,15 +399,12 @@ pub fn spawn_named_mob( if let Some(pool) = player_pool { player_level = pool.level; } - needs_key = is_player_owned(&player_entity, &pos); } + let mut eb; // New entity with a position, name, combatstats, and viewshed eb = ecs.create_entity().marked::>(); eb = spawn_position(pos, eb, key, raws); - if needs_key { - eb = eb.with(WantsToAssignKey {}); - } eb = eb.with(Name { name: mob_template.name.clone(), plural: mob_template.name.clone() }); eb = eb.with(Viewshed { visible_tiles: Vec::new(), @@ -652,18 +632,10 @@ pub fn spawn_named_prop( pos: SpawnType ) -> Option { if raws.prop_index.contains_key(key) { - let needs_key; - { - let player_entity = ecs.fetch::(); - needs_key = is_player_owned(&player_entity, &pos); - } // ENTITY BUILDER PREP let prop_template = &raws.raws.props[raws.prop_index[key]]; let mut eb = ecs.create_entity().marked::>(); eb = spawn_position(pos, eb, key, raws); - if needs_key { - eb = eb.with(WantsToAssignKey {}); - } // APPLY MANDATORY COMPONENTS FOR A PROP: // - Name // - Prop {} @@ -714,28 +686,16 @@ fn spawn_position<'a>( eb } -fn is_player_owned(player: &Entity, pos: &SpawnType) -> bool { - match pos { - SpawnType::Carried { by } => { - if by == player { - return true; - } - } - SpawnType::Equipped { by } => { - if by == player { - return true; - } - } - _ => {} - } - false -} - fn get_renderable_component( renderable: &super::item_structs::Renderable ) -> crate::components::Renderable { crate::components::Renderable { glyph: to_cp437(renderable.glyph.chars().next().unwrap()), + sprite: if let Some(sprite) = &renderable.sprite { + Some(sprite.clone()) + } else { + None + }, fg: RGB::from_hex(&renderable.fg).expect("Invalid RGB"), bg: RGB::from_hex(&renderable.bg).expect("Invalid RGB"), render_order: renderable.order, diff --git a/src/saveload_system.rs b/src/saveload_system.rs index f3b284d..894e4ff 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -9,7 +9,6 @@ use specs::saveload::{ SimpleMarker, SimpleMarkerAllocator, }; - use std::fs; use std::fs::File; use std::path::Path; @@ -96,10 +95,8 @@ pub fn save_game(ecs: &mut World) { IdentifiedItem, InBackpack, InflictsDamage, - IntrinsicChanged, Intrinsics, Item, - Key, KnownSpells, LootTable, MagicItem, @@ -129,21 +126,17 @@ pub fn save_game(ecs: &mut World) { SpawnParticleBurst, SpawnParticleLine, SpawnParticleSimple, - Stackable, TakingTurn, Telepath, ToHitBonus, Viewshed, Charges, WantsToApproach, - WantsToAssignKey, - WantsToDelete, WantsToDropItem, WantsToFlee, WantsToMelee, WantsToPickupItem, WantsToRemoveItem, - WantsToRemoveKey, WantsToUseItem, SerializationHelper, DMSerializationHelper @@ -234,10 +227,8 @@ pub fn load_game(ecs: &mut World) { IdentifiedItem, InBackpack, InflictsDamage, - IntrinsicChanged, Intrinsics, Item, - Key, KnownSpells, LootTable, MagicItem, @@ -267,21 +258,17 @@ pub fn load_game(ecs: &mut World) { SpawnParticleBurst, SpawnParticleLine, SpawnParticleSimple, - Stackable, TakingTurn, Telepath, ToHitBonus, Viewshed, Charges, WantsToApproach, - WantsToAssignKey, - WantsToDelete, WantsToDropItem, WantsToFlee, WantsToMelee, WantsToPickupItem, WantsToRemoveItem, - WantsToRemoveKey, WantsToUseItem, SerializationHelper, DMSerializationHelper diff --git a/src/spawner.rs b/src/spawner.rs index 3fa673c..21a68b1 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -54,6 +54,7 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { .with(BlocksTile {}) .with(Renderable { glyph: to_cp437('@'), + sprite: None, // TODO: Player sprite fg: RGB::named(YELLOW), bg: RGB::named(BLACK), render_order: 0, diff --git a/src/states/state.rs b/src/states/state.rs index 3dde32b..ed4cd58 100644 --- a/src/states/state.rs +++ b/src/states/state.rs @@ -23,6 +23,7 @@ use crate::camera; use crate::saveload_system; use crate::morgue; use crate::damage_system; +use crate::data::prelude::*; pub struct State { pub ecs: World, @@ -64,13 +65,12 @@ impl State { fn resolve_entity_decisions(&mut self) { let mut trigger_system = trigger_system::TriggerSystem {}; + let mut inventory_system = inventory::ItemCollectionSystem {}; let mut item_equip_system = inventory::ItemEquipSystem {}; let mut item_use_system = inventory::ItemUseSystem {}; let mut item_drop_system = inventory::ItemDropSystem {}; let mut item_remove_system = inventory::ItemRemoveSystem {}; - let mut inventory_system = inventory::ItemCollectionSystem {}; let mut item_id_system = inventory::ItemIdentificationSystem {}; - let mut key_system = inventory::KeyHandling {}; let mut melee_system = MeleeCombatSystem {}; trigger_system.run_now(&self.ecs); inventory_system.run_now(&self.ecs); @@ -79,7 +79,6 @@ impl State { item_drop_system.run_now(&self.ecs); item_remove_system.run_now(&self.ecs); item_id_system.run_now(&self.ecs); - key_system.run_now(&self.ecs); melee_system.run_now(&self.ecs); effects::run_effects_queue(&mut self.ecs); @@ -166,6 +165,15 @@ impl GameState for State { new_runstate = *runstate; } // Clear screen + ctx.set_active_console(0); + ctx.cls(); + ctx.set_active_console(HP_BAR_LAYER); + ctx.cls(); + ctx.set_active_console(TEXT_LAYER); + ctx.cls(); + ctx.set_active_console(ENTITY_LAYER); + ctx.cls(); + ctx.set_active_console(TILE_LAYER); ctx.cls(); particle_system::particle_ticker(&mut self.ecs, ctx); @@ -344,11 +352,7 @@ impl GameState for State { gui::ItemMenuResult::NoResponse => {} gui::ItemMenuResult::Selected => { let item_entity = result.1.unwrap(); - let mut removekey = self.ecs.write_storage::(); let mut intent = self.ecs.write_storage::(); - removekey - .insert(item_entity, WantsToRemoveKey {}) - .expect("Unable to insert WantsToRemoveKey"); intent .insert(*self.ecs.fetch::(), WantsToDropItem { item: item_entity, @@ -565,6 +569,15 @@ impl GameState for State { new_runstate = self.mapgen_next_state.unwrap(); } if self.mapgen_history.len() != 0 { + ctx.set_active_console(0); + ctx.cls(); + ctx.set_active_console(HP_BAR_LAYER); + ctx.cls(); + ctx.set_active_console(TEXT_LAYER); + ctx.cls(); + ctx.set_active_console(ENTITY_LAYER); + ctx.cls(); + ctx.set_active_console(TILE_LAYER); ctx.cls(); camera::render_debug_map(&self.mapgen_history[self.mapgen_index], ctx); diff --git a/src/visibility_system.rs b/src/visibility_system.rs index e53b0e1..521eecd 100644 --- a/src/visibility_system.rs +++ b/src/visibility_system.rs @@ -11,6 +11,7 @@ use super::{ Viewshed, Renderable, gui::renderable_colour, + tile_blocks_telepathy, }; use bracket_lib::prelude::*; use bracket_lib::pathfinding::FieldOfViewAlg::SymmetricShadowcasting; @@ -120,7 +121,7 @@ impl<'a> System<'a> for VisibilitySystem { if let Some(_is_blind) = blind_entities.get(ent) { range *= BLIND_TELEPATHY_RANGE_MULTIPLIER; } - telepath.telepath_tiles = fast_fov(pos.x, pos.y, range); + telepath.telepath_tiles = fast_fov(pos.x, pos.y, range, &map); telepath.telepath_tiles.retain( |p| p.x >= 0 && p.x < map.width && p.y >= 0 && p.y < map.height ); @@ -141,7 +142,7 @@ impl<'a> System<'a> for VisibilitySystem { } } -pub fn fast_fov(p_x: i32, p_y: i32, r: i32) -> Vec { +pub fn fast_fov(p_x: i32, p_y: i32, r: i32, map: &WriteExpect) -> Vec { let mut visible_tiles: Vec = Vec::new(); let mut i = 0; @@ -152,7 +153,17 @@ pub fn fast_fov(p_x: i32, p_y: i32, r: i32) -> Vec { let mut ox: f32 = (p_x as f32) + (0.5 as f32); let mut oy: f32 = (p_y as f32) + (0.5 as f32); for _i in 0..r { - visible_tiles.push(Point::new(ox as i32, oy as i32)); + let (ox_i32, oy_i32) = (ox as i32, oy as i32); + visible_tiles.push(Point::new(ox_i32, oy_i32)); + if + ox_i32 >= 0 && + ox_i32 < map.width && + oy_i32 >= 0 && + oy_i32 < map.height && + tile_blocks_telepathy(map.tiles[map.xy_idx(ox_i32, oy_i32)]) + { + break; + } ox += x; oy += y; } diff --git a/wasm/rust-rl.js b/wasm/rust-rl.js index ffbe20e..7036263 100644 --- a/wasm/rust-rl.js +++ b/wasm/rust-rl.js @@ -212,11 +212,11 @@ function makeMutClosure(arg0, arg1, dtor, f) { return real; } function __wbg_adapter_20(arg0, arg1) { - wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hb6c6b1cd103d974c(arg0, arg1); + wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h66b665bcfa5ccc10(arg0, arg1); } function __wbg_adapter_23(arg0, arg1, arg2) { - wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h02b9f16709be0849(arg0, arg1, addHeapObject(arg2)); + wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h7f980deb71f217f3(arg0, arg1, addHeapObject(arg2)); } function handleError(f, args) { @@ -817,16 +817,16 @@ function __wbg_get_imports() { const ret = wasm.memory; return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper258 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 15, __wbg_adapter_20); + imports.wbg.__wbindgen_closure_wrapper257 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 14, __wbg_adapter_20); return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper2954 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 696, __wbg_adapter_23); + imports.wbg.__wbindgen_closure_wrapper2960 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 698, __wbg_adapter_23); return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper2956 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 696, __wbg_adapter_23); + imports.wbg.__wbindgen_closure_wrapper2962 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 698, __wbg_adapter_23); return addHeapObject(ret); }; diff --git a/wasm/rust-rl_bg.wasm b/wasm/rust-rl_bg.wasm index f48ccb7..79ebb75 100644 Binary files a/wasm/rust-rl_bg.wasm and b/wasm/rust-rl_bg.wasm differ diff --git a/web/rust-rl.js b/web/rust-rl.js new file mode 100644 index 0000000..7036263 --- /dev/null +++ b/web/rust-rl.js @@ -0,0 +1,887 @@ +let wasm_bindgen; +(function() { + const __exports = {}; + let script_src; + if (typeof document !== 'undefined' && document.currentScript !== null) { + script_src = new URL(document.currentScript.src, location.href).toString(); + } + let wasm = undefined; + + const heap = new Array(128).fill(undefined); + + heap.push(undefined, null, true, false); + +function getObject(idx) { return heap[idx]; } + +let heap_next = heap.length; + +function dropObject(idx) { + if (idx < 132) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +let WASM_VECTOR_LEN = 0; + +let cachedUint8Memory0 = null; + +function getUint8Memory0() { + if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) { + cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8Memory0; +} + +const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); + +const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); +} + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; +}); + +function passStringToWasm0(arg, malloc, realloc) { + + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8Memory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8Memory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +function isLikeNone(x) { + return x === undefined || x === null; +} + +let cachedInt32Memory0 = null; + +function getInt32Memory0() { + if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { + cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); + } + return cachedInt32Memory0; +} + +function debugString(val) { + // primitive types + const type = typeof val; + if (type == 'number' || type == 'boolean' || val == null) { + return `${val}`; + } + if (type == 'string') { + return `"${val}"`; + } + if (type == 'symbol') { + const description = val.description; + if (description == null) { + return 'Symbol'; + } else { + return `Symbol(${description})`; + } + } + if (type == 'function') { + const name = val.name; + if (typeof name == 'string' && name.length > 0) { + return `Function(${name})`; + } else { + return 'Function'; + } + } + // objects + if (Array.isArray(val)) { + const length = val.length; + let debug = '['; + if (length > 0) { + debug += debugString(val[0]); + } + for(let i = 1; i < length; i++) { + debug += ', ' + debugString(val[i]); + } + debug += ']'; + return debug; + } + // Test for built-in + const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); + let className; + if (builtInMatches.length > 1) { + className = builtInMatches[1]; + } else { + // Failed to match the standard '[object ClassName]' + return toString.call(val); + } + if (className == 'Object') { + // we're a user defined class or Object + // JSON.stringify avoids problems with cycles, and is generally much + // easier than looping through ownProperties of `val`. + try { + return 'Object(' + JSON.stringify(val) + ')'; + } catch (_) { + return 'Object'; + } + } + // errors + if (val instanceof Error) { + return `${val.name}: ${val.message}\n${val.stack}`; + } + // TODO we could test for more things here, like `Set`s and `Map`s. + return className; +} + +const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); + +if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +} + +function makeMutClosure(arg0, arg1, dtor, f) { + const state = { a: arg0, b: arg1, cnt: 1, dtor }; + const real = (...args) => { + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + const a = state.a; + state.a = 0; + try { + return f(a, state.b, ...args); + } finally { + if (--state.cnt === 0) { + wasm.__wbindgen_export_2.get(state.dtor)(a, state.b); + + } else { + state.a = a; + } + } + }; + real.original = state; + + return real; +} +function __wbg_adapter_20(arg0, arg1) { + wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h66b665bcfa5ccc10(arg0, arg1); +} + +function __wbg_adapter_23(arg0, arg1, arg2) { + wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h7f980deb71f217f3(arg0, arg1, addHeapObject(arg2)); +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + wasm.__wbindgen_exn_store(addHeapObject(e)); + } +} + +function getArrayU8FromWasm0(ptr, len) { + ptr = ptr >>> 0; + return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); +} + +async function __wbg_load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + + } catch (e) { + if (module.headers.get('Content-Type') != 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { + throw e; + } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + + } else { + return instance; + } + } +} + +function __wbg_get_imports() { + const imports = {}; + imports.wbg = {}; + imports.wbg.__wbindgen_cb_drop = function(arg0) { + const obj = takeObject(arg0).original; + if (obj.cnt-- == 1) { + obj.a = 0; + return true; + } + const ret = false; + return ret; + }; + imports.wbg.__wbindgen_object_drop_ref = function(arg0) { + takeObject(arg0); + }; + imports.wbg.__wbg_log_0e24d345b14995ec = function(arg0, arg1) { + console.log(getStringFromWasm0(arg0, arg1)); + }; + imports.wbg.__wbindgen_object_clone_ref = function(arg0) { + const ret = getObject(arg0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_new_abda76e883ba8a5f = function() { + const ret = new Error(); + return addHeapObject(ret); + }; + imports.wbg.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) { + const ret = getObject(arg1).stack; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) { + let deferred0_0; + let deferred0_1; + try { + deferred0_0 = arg0; + deferred0_1 = arg1; + console.error(getStringFromWasm0(arg0, arg1)); + } finally { + wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); + } + }; + imports.wbg.__wbindgen_string_get = function(arg0, arg1) { + const obj = getObject(arg1); + const ret = typeof(obj) === 'string' ? obj : undefined; + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbindgen_boolean_get = function(arg0) { + const v = getObject(arg0); + const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2; + return ret; + }; + imports.wbg.__wbg_instanceof_WebGl2RenderingContext_f921526c513bf717 = function(arg0) { + let result; + try { + result = getObject(arg0) instanceof WebGL2RenderingContext; + } catch { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_bindVertexArray_8863a216d7b0a339 = function(arg0, arg1) { + getObject(arg0).bindVertexArray(getObject(arg1)); + }; + imports.wbg.__wbg_bufferData_21334671c4ba6004 = function(arg0, arg1, arg2, arg3) { + getObject(arg0).bufferData(arg1 >>> 0, getObject(arg2), arg3 >>> 0); + }; + imports.wbg.__wbg_createVertexArray_51d51e1e1e13e9f6 = function(arg0) { + const ret = getObject(arg0).createVertexArray(); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_texImage2D_07240affd06971e9 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { + getObject(arg0).texImage2D(arg1 >>> 0, arg2, arg3, arg4, arg5, arg6, arg7 >>> 0, arg8 >>> 0, getObject(arg9)); + }, arguments) }; + imports.wbg.__wbg_attachShader_47256b6b3d42a22e = function(arg0, arg1, arg2) { + getObject(arg0).attachShader(getObject(arg1), getObject(arg2)); + }; + imports.wbg.__wbg_bindBuffer_24f6010e273fa400 = function(arg0, arg1, arg2) { + getObject(arg0).bindBuffer(arg1 >>> 0, getObject(arg2)); + }; + imports.wbg.__wbg_bindFramebuffer_a9573e340dab20fe = function(arg0, arg1, arg2) { + getObject(arg0).bindFramebuffer(arg1 >>> 0, getObject(arg2)); + }; + imports.wbg.__wbg_bindTexture_92d6d7f8bff9531e = function(arg0, arg1, arg2) { + getObject(arg0).bindTexture(arg1 >>> 0, getObject(arg2)); + }; + imports.wbg.__wbg_blendFunc_533de6de45b80a09 = function(arg0, arg1, arg2) { + getObject(arg0).blendFunc(arg1 >>> 0, arg2 >>> 0); + }; + imports.wbg.__wbg_clear_2db2efe323bfdf68 = function(arg0, arg1) { + getObject(arg0).clear(arg1 >>> 0); + }; + imports.wbg.__wbg_clearColor_7a7d04702f7e38e5 = function(arg0, arg1, arg2, arg3, arg4) { + getObject(arg0).clearColor(arg1, arg2, arg3, arg4); + }; + imports.wbg.__wbg_compileShader_6bf78b425d5c98e1 = function(arg0, arg1) { + getObject(arg0).compileShader(getObject(arg1)); + }; + imports.wbg.__wbg_createBuffer_323425af422748ac = function(arg0) { + const ret = getObject(arg0).createBuffer(); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_createFramebuffer_1684a99697ac9563 = function(arg0) { + const ret = getObject(arg0).createFramebuffer(); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_createProgram_4eaf3b97b5747a62 = function(arg0) { + const ret = getObject(arg0).createProgram(); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_createShader_429776c9dd6fb87b = function(arg0, arg1) { + const ret = getObject(arg0).createShader(arg1 >>> 0); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_createTexture_1bf4d6fec570124b = function(arg0) { + const ret = getObject(arg0).createTexture(); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_disable_e02106ca6c7002d6 = function(arg0, arg1) { + getObject(arg0).disable(arg1 >>> 0); + }; + imports.wbg.__wbg_drawArrays_c91ce3f736bf1f2a = function(arg0, arg1, arg2, arg3) { + getObject(arg0).drawArrays(arg1 >>> 0, arg2, arg3); + }; + imports.wbg.__wbg_drawElements_a9529eefaf2008bd = function(arg0, arg1, arg2, arg3, arg4) { + getObject(arg0).drawElements(arg1 >>> 0, arg2, arg3 >>> 0, arg4); + }; + imports.wbg.__wbg_enable_195891416c520019 = function(arg0, arg1) { + getObject(arg0).enable(arg1 >>> 0); + }; + imports.wbg.__wbg_enableVertexAttribArray_8804480c2ea0bb72 = function(arg0, arg1) { + getObject(arg0).enableVertexAttribArray(arg1 >>> 0); + }; + imports.wbg.__wbg_framebufferTexture2D_e88fcbd7f8523bb8 = function(arg0, arg1, arg2, arg3, arg4, arg5) { + getObject(arg0).framebufferTexture2D(arg1 >>> 0, arg2 >>> 0, arg3 >>> 0, getObject(arg4), arg5); + }; + imports.wbg.__wbg_getError_7191ad6ea53607fe = function(arg0) { + const ret = getObject(arg0).getError(); + return ret; + }; + imports.wbg.__wbg_getExtension_77909f6d51d49d4d = function() { return handleError(function (arg0, arg1, arg2) { + const ret = getObject(arg0).getExtension(getStringFromWasm0(arg1, arg2)); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_getParameter_55b36a787dbbfb74 = function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg0).getParameter(arg1 >>> 0); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_getProgramInfoLog_b81bc53188e286fa = function(arg0, arg1, arg2) { + const ret = getObject(arg1).getProgramInfoLog(getObject(arg2)); + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbg_getProgramParameter_35522a0bfdfaad27 = function(arg0, arg1, arg2) { + const ret = getObject(arg0).getProgramParameter(getObject(arg1), arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_getShaderInfoLog_968b93e75477d725 = function(arg0, arg1, arg2) { + const ret = getObject(arg1).getShaderInfoLog(getObject(arg2)); + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbg_getShaderParameter_ac2727ae4fe7648e = function(arg0, arg1, arg2) { + const ret = getObject(arg0).getShaderParameter(getObject(arg1), arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_getSupportedExtensions_fafc31aab913037d = function(arg0) { + const ret = getObject(arg0).getSupportedExtensions(); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_getUniformLocation_9f6eb60c560a347b = function(arg0, arg1, arg2, arg3) { + const ret = getObject(arg0).getUniformLocation(getObject(arg1), getStringFromWasm0(arg2, arg3)); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_linkProgram_33998194075d71fb = function(arg0, arg1) { + getObject(arg0).linkProgram(getObject(arg1)); + }; + imports.wbg.__wbg_shaderSource_1cb7c64dc7d1a500 = function(arg0, arg1, arg2, arg3) { + getObject(arg0).shaderSource(getObject(arg1), getStringFromWasm0(arg2, arg3)); + }; + imports.wbg.__wbg_texParameteri_85dad939f62a15aa = function(arg0, arg1, arg2, arg3) { + getObject(arg0).texParameteri(arg1 >>> 0, arg2 >>> 0, arg3); + }; + imports.wbg.__wbg_uniform1i_d2e61a6a43889648 = function(arg0, arg1, arg2) { + getObject(arg0).uniform1i(getObject(arg1), arg2); + }; + imports.wbg.__wbg_uniform3f_8364a0959b6c1570 = function(arg0, arg1, arg2, arg3, arg4) { + getObject(arg0).uniform3f(getObject(arg1), arg2, arg3, arg4); + }; + imports.wbg.__wbg_useProgram_3683cf6f60939dcd = function(arg0, arg1) { + getObject(arg0).useProgram(getObject(arg1)); + }; + imports.wbg.__wbg_vertexAttribPointer_316ffe2f0458fde7 = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6) { + getObject(arg0).vertexAttribPointer(arg1 >>> 0, arg2, arg3 >>> 0, arg4 !== 0, arg5, arg6); + }; + imports.wbg.__wbg_getElementById_cc0e0d931b0d9a28 = function(arg0, arg1, arg2) { + const ret = getObject(arg0).getElementById(getStringFromWasm0(arg1, arg2)); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_instanceof_HtmlCanvasElement_da5f9efa0688cf6d = function(arg0) { + let result; + try { + result = getObject(arg0) instanceof HTMLCanvasElement; + } catch { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_setwidth_a667a942dba6656e = function(arg0, arg1) { + getObject(arg0).width = arg1 >>> 0; + }; + imports.wbg.__wbg_setheight_a747d440760fe5aa = function(arg0, arg1) { + getObject(arg0).height = arg1 >>> 0; + }; + imports.wbg.__wbg_getContext_7c5944ea807bf5d3 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = getObject(arg0).getContext(getStringFromWasm0(arg1, arg2)); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_offsetX_5a58f16f6c3a41b6 = function(arg0) { + const ret = getObject(arg0).offsetX; + return ret; + }; + imports.wbg.__wbg_offsetY_c45b4956f6429a95 = function(arg0) { + const ret = getObject(arg0).offsetY; + return ret; + }; + imports.wbg.__wbg_now_0cfdc90c97d0c24b = function(arg0) { + const ret = getObject(arg0).now(); + return ret; + }; + imports.wbg.__wbg_instanceof_Window_9029196b662bc42a = function(arg0) { + let result; + try { + result = getObject(arg0) instanceof Window; + } catch { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_charCode_75cea1a3a6d66388 = function(arg0) { + const ret = getObject(arg0).charCode; + return ret; + }; + imports.wbg.__wbg_keyCode_dfa86be31f5ef90c = function(arg0) { + const ret = getObject(arg0).keyCode; + return ret; + }; + imports.wbg.__wbg_code_96d6322b968b2d17 = function(arg0, arg1) { + const ret = getObject(arg1).code; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbg_getModifierState_5102ee8843516d2f = function(arg0, arg1, arg2) { + const ret = getObject(arg0).getModifierState(getStringFromWasm0(arg1, arg2)); + return ret; + }; + imports.wbg.__wbg_document_f7ace2b956f30a4f = function(arg0) { + const ret = getObject(arg0).document; + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_performance_2c295061c8b01e0b = function(arg0) { + const ret = getObject(arg0).performance; + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_setonkeydown_933cca3c9000a932 = function(arg0, arg1) { + getObject(arg0).onkeydown = getObject(arg1); + }; + imports.wbg.__wbg_setonkeyup_0dfb23e81d0afdde = function(arg0, arg1) { + getObject(arg0).onkeyup = getObject(arg1); + }; + imports.wbg.__wbg_requestAnimationFrame_d082200514b6674d = function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg0).requestAnimationFrame(getObject(arg1)); + return ret; + }, arguments) }; + imports.wbg.__wbg_setonmousedown_4f38d9c057bbfcbd = function(arg0, arg1) { + getObject(arg0).onmousedown = getObject(arg1); + }; + imports.wbg.__wbg_setonmousemove_c0b17753786f3544 = function(arg0, arg1) { + getObject(arg0).onmousemove = getObject(arg1); + }; + imports.wbg.__wbg_setonmouseup_4b447fa380e33802 = function(arg0, arg1) { + getObject(arg0).onmouseup = getObject(arg1); + }; + imports.wbg.__wbg_bufferData_a11a9f65f31e7256 = function(arg0, arg1, arg2, arg3) { + getObject(arg0).bufferData(arg1 >>> 0, getObject(arg2), arg3 >>> 0); + }; + imports.wbg.__wbg_texImage2D_6175916e58c59bc7 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) { + getObject(arg0).texImage2D(arg1 >>> 0, arg2, arg3, arg4, arg5, arg6, arg7 >>> 0, arg8 >>> 0, getObject(arg9)); + }, arguments) }; + imports.wbg.__wbg_attachShader_b65b695055670cb5 = function(arg0, arg1, arg2) { + getObject(arg0).attachShader(getObject(arg1), getObject(arg2)); + }; + imports.wbg.__wbg_bindBuffer_313561e5bc0e533f = function(arg0, arg1, arg2) { + getObject(arg0).bindBuffer(arg1 >>> 0, getObject(arg2)); + }; + imports.wbg.__wbg_bindFramebuffer_56bf6536a4ced0ec = function(arg0, arg1, arg2) { + getObject(arg0).bindFramebuffer(arg1 >>> 0, getObject(arg2)); + }; + imports.wbg.__wbg_bindTexture_9cb5c770d1ba2cca = function(arg0, arg1, arg2) { + getObject(arg0).bindTexture(arg1 >>> 0, getObject(arg2)); + }; + imports.wbg.__wbg_blendFunc_fbe9d3a688fe71c3 = function(arg0, arg1, arg2) { + getObject(arg0).blendFunc(arg1 >>> 0, arg2 >>> 0); + }; + imports.wbg.__wbg_clear_2ccea1f65b510c97 = function(arg0, arg1) { + getObject(arg0).clear(arg1 >>> 0); + }; + imports.wbg.__wbg_clearColor_de587608b28bc7ed = function(arg0, arg1, arg2, arg3, arg4) { + getObject(arg0).clearColor(arg1, arg2, arg3, arg4); + }; + imports.wbg.__wbg_compileShader_d88d0a8cd9b72b4d = function(arg0, arg1) { + getObject(arg0).compileShader(getObject(arg1)); + }; + imports.wbg.__wbg_createBuffer_59051f4461e7c5e2 = function(arg0) { + const ret = getObject(arg0).createBuffer(); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_createFramebuffer_223c1212ad76affc = function(arg0) { + const ret = getObject(arg0).createFramebuffer(); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_createProgram_88dbe21c0b682e1a = function(arg0) { + const ret = getObject(arg0).createProgram(); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_createShader_9d7d388633caad18 = function(arg0, arg1) { + const ret = getObject(arg0).createShader(arg1 >>> 0); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_createTexture_9d0bb4d741b8ad76 = function(arg0) { + const ret = getObject(arg0).createTexture(); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_disable_5cf2070641fa2ed7 = function(arg0, arg1) { + getObject(arg0).disable(arg1 >>> 0); + }; + imports.wbg.__wbg_drawArrays_d5c7dc2b2376c85a = function(arg0, arg1, arg2, arg3) { + getObject(arg0).drawArrays(arg1 >>> 0, arg2, arg3); + }; + imports.wbg.__wbg_drawElements_3316ee0cd1117c2a = function(arg0, arg1, arg2, arg3, arg4) { + getObject(arg0).drawElements(arg1 >>> 0, arg2, arg3 >>> 0, arg4); + }; + imports.wbg.__wbg_enable_8965e69c596f0a94 = function(arg0, arg1) { + getObject(arg0).enable(arg1 >>> 0); + }; + imports.wbg.__wbg_enableVertexAttribArray_2b0475db43533cf2 = function(arg0, arg1) { + getObject(arg0).enableVertexAttribArray(arg1 >>> 0); + }; + imports.wbg.__wbg_framebufferTexture2D_953e69a8bec22fa9 = function(arg0, arg1, arg2, arg3, arg4, arg5) { + getObject(arg0).framebufferTexture2D(arg1 >>> 0, arg2 >>> 0, arg3 >>> 0, getObject(arg4), arg5); + }; + imports.wbg.__wbg_getError_1e5ec1ec9e58b323 = function(arg0) { + const ret = getObject(arg0).getError(); + return ret; + }; + imports.wbg.__wbg_getProgramInfoLog_0b7af4ad85fa52a4 = function(arg0, arg1, arg2) { + const ret = getObject(arg1).getProgramInfoLog(getObject(arg2)); + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbg_getProgramParameter_2a3735278367f8bc = function(arg0, arg1, arg2) { + const ret = getObject(arg0).getProgramParameter(getObject(arg1), arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_getShaderInfoLog_979aafa403ffb252 = function(arg0, arg1, arg2) { + const ret = getObject(arg1).getShaderInfoLog(getObject(arg2)); + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbg_getShaderParameter_e8054f1d9026fb70 = function(arg0, arg1, arg2) { + const ret = getObject(arg0).getShaderParameter(getObject(arg1), arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_getUniformLocation_688976233799a45a = function(arg0, arg1, arg2, arg3) { + const ret = getObject(arg0).getUniformLocation(getObject(arg1), getStringFromWasm0(arg2, arg3)); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_linkProgram_9a2d12d120d99917 = function(arg0, arg1) { + getObject(arg0).linkProgram(getObject(arg1)); + }; + imports.wbg.__wbg_shaderSource_f435f9b74440bb54 = function(arg0, arg1, arg2, arg3) { + getObject(arg0).shaderSource(getObject(arg1), getStringFromWasm0(arg2, arg3)); + }; + imports.wbg.__wbg_texParameteri_1f17358e51eb8069 = function(arg0, arg1, arg2, arg3) { + getObject(arg0).texParameteri(arg1 >>> 0, arg2 >>> 0, arg3); + }; + imports.wbg.__wbg_uniform1i_9f94ef0ba6b3cc66 = function(arg0, arg1, arg2) { + getObject(arg0).uniform1i(getObject(arg1), arg2); + }; + imports.wbg.__wbg_uniform3f_c682f4b32f713d1a = function(arg0, arg1, arg2, arg3, arg4) { + getObject(arg0).uniform3f(getObject(arg1), arg2, arg3, arg4); + }; + imports.wbg.__wbg_useProgram_019eb6df066fabf5 = function(arg0, arg1) { + getObject(arg0).useProgram(getObject(arg1)); + }; + imports.wbg.__wbg_vertexAttribPointer_ca11984ee8843c0a = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6) { + getObject(arg0).vertexAttribPointer(arg1 >>> 0, arg2, arg3 >>> 0, arg4 !== 0, arg5, arg6); + }; + imports.wbg.__wbg_bindVertexArrayOES_b7d9da7e073aa6b5 = function(arg0, arg1) { + getObject(arg0).bindVertexArrayOES(getObject(arg1)); + }; + imports.wbg.__wbg_createVertexArrayOES_6a3c3a5a68201f8f = function(arg0) { + const ret = getObject(arg0).createVertexArrayOES(); + return isLikeNone(ret) ? 0 : addHeapObject(ret); + }; + imports.wbg.__wbg_self_1c2814d86e6e51e3 = function() { return handleError(function () { + const ret = self.self; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_crypto_70532d614bc7e028 = function(arg0) { + const ret = getObject(arg0).crypto; + return addHeapObject(ret); + }; + imports.wbg.__wbg_msCrypto_4e9b4dd0e1abade6 = function(arg0) { + const ret = getObject(arg0).msCrypto; + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_is_undefined = function(arg0) { + const ret = getObject(arg0) === undefined; + return ret; + }; + imports.wbg.__wbg_static_accessor_MODULE_7781e47b50010688 = function() { + const ret = module; + return addHeapObject(ret); + }; + imports.wbg.__wbg_require_9ace3ae680954e98 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = getObject(arg0).require(getStringFromWasm0(arg1, arg2)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_getRandomValues_f6c9b08ef5448767 = function() { return handleError(function (arg0, arg1) { + getObject(arg0).getRandomValues(getObject(arg1)); + }, arguments) }; + imports.wbg.__wbg_randomFillSync_bf67eeddb65b346b = function() { return handleError(function (arg0, arg1, arg2) { + getObject(arg0).randomFillSync(getArrayU8FromWasm0(arg1, arg2)); + }, arguments) }; + imports.wbg.__wbg_get_44be0491f933a435 = function(arg0, arg1) { + const ret = getObject(arg0)[arg1 >>> 0]; + return addHeapObject(ret); + }; + imports.wbg.__wbg_length_fff51ee6522a1a18 = function(arg0) { + const ret = getObject(arg0).length; + return ret; + }; + imports.wbg.__wbg_newnoargs_581967eacc0e2604 = function(arg0, arg1) { + const ret = new Function(getStringFromWasm0(arg0, arg1)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_call_cb65541d95d71282 = function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg0).call(getObject(arg1)); + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_self_1ff1d729e9aae938 = function() { return handleError(function () { + const ret = self.self; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_window_5f4faef6c12b79ec = function() { return handleError(function () { + const ret = window.window; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_globalThis_1d39714405582d3c = function() { return handleError(function () { + const ret = globalThis.globalThis; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_global_651f05c6a0944d1c = function() { return handleError(function () { + const ret = global.global; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_now_9c5990bda04c7e53 = function() { + const ret = Date.now(); + return ret; + }; + imports.wbg.__wbg_buffer_085ec1f694018c4f = function(arg0) { + const ret = getObject(arg0).buffer; + return addHeapObject(ret); + }; + imports.wbg.__wbg_newwithbyteoffsetandlength_828b952f0e692245 = function(arg0, arg1, arg2) { + const ret = new Int8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_newwithbyteoffsetandlength_735ed5ea2ae07fe9 = function(arg0, arg1, arg2) { + const ret = new Int16Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_newwithbyteoffsetandlength_9f43b22ab631d1d6 = function(arg0, arg1, arg2) { + const ret = new Int32Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_newwithbyteoffsetandlength_6da8e527659b86aa = function(arg0, arg1, arg2) { + const ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_new_8125e318e6245eed = function(arg0) { + const ret = new Uint8Array(getObject(arg0)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_set_5cf90238115182c3 = function(arg0, arg1, arg2) { + getObject(arg0).set(getObject(arg1), arg2 >>> 0); + }; + imports.wbg.__wbg_length_72e2208bbc0efc61 = function(arg0) { + const ret = getObject(arg0).length; + return ret; + }; + imports.wbg.__wbg_newwithbyteoffsetandlength_31ff1024ef0c63c7 = function(arg0, arg1, arg2) { + const ret = new Uint16Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_newwithbyteoffsetandlength_6df0e8c3efd2a5d3 = function(arg0, arg1, arg2) { + const ret = new Uint32Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_newwithbyteoffsetandlength_69193e31c844b792 = function(arg0, arg1, arg2) { + const ret = new Float32Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_newwithlength_e5d69174d6984cd7 = function(arg0) { + const ret = new Uint8Array(arg0 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbg_subarray_13db269f57aa838d = function(arg0, arg1, arg2) { + const ret = getObject(arg0).subarray(arg1 >>> 0, arg2 >>> 0); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { + const ret = debugString(getObject(arg1)); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbindgen_throw = function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }; + imports.wbg.__wbindgen_memory = function() { + const ret = wasm.memory; + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_closure_wrapper257 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 14, __wbg_adapter_20); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_closure_wrapper2960 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 698, __wbg_adapter_23); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_closure_wrapper2962 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 698, __wbg_adapter_23); + return addHeapObject(ret); + }; + + return imports; +} + +function __wbg_init_memory(imports, maybe_memory) { + +} + +function __wbg_finalize_init(instance, module) { + wasm = instance.exports; + __wbg_init.__wbindgen_wasm_module = module; + cachedInt32Memory0 = null; + cachedUint8Memory0 = null; + + wasm.__wbindgen_start(); + return wasm; +} + +function initSync(module) { + if (wasm !== undefined) return wasm; + + const imports = __wbg_get_imports(); + + __wbg_init_memory(imports); + + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + + const instance = new WebAssembly.Instance(module, imports); + + return __wbg_finalize_init(instance, module); +} + +async function __wbg_init(input) { + if (wasm !== undefined) return wasm; + + if (typeof input === 'undefined' && script_src !== 'undefined') { + input = script_src.replace(/\.js$/, '_bg.wasm'); + } + const imports = __wbg_get_imports(); + + if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { + input = fetch(input); + } + + __wbg_init_memory(imports); + + const { instance, module } = await __wbg_load(await input, imports); + + return __wbg_finalize_init(instance, module); +} + +wasm_bindgen = Object.assign(__wbg_init, { initSync }, __exports); + +})(); diff --git a/web/rust-rl_bg.wasm b/web/rust-rl_bg.wasm new file mode 100644 index 0000000..79ebb75 Binary files /dev/null and b/web/rust-rl_bg.wasm differ