Compare commits
109 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d4ab70c7b | ||
|
|
e3a8c545fb | ||
|
|
17bebf03a4 | ||
|
|
d1fe86f645 | ||
|
|
6ba7778df0 | ||
|
|
be7ff6b895 | ||
|
|
7ac2e14471 | ||
|
|
b105a415d5 | ||
|
|
0c74531c5b | ||
|
|
e8d376fecf | ||
|
|
465cd51a60 | ||
|
|
1de0c20b76 | ||
|
|
6c359a0780 | ||
|
|
13322eb2a1 | ||
|
|
f862f00f0b | ||
|
|
deb9988935 | ||
|
|
098617fbf1 | ||
|
|
72a6ff6d14 | ||
|
|
3c9eb2de27 | ||
|
|
074e2465d7 | ||
|
|
59f7e9a8f7 | ||
|
|
0ee2a80715 | ||
|
|
396d548bf2 | ||
|
|
40b048fd65 | ||
|
|
af1040b970 | ||
|
|
44b0674b5a | ||
|
|
8bb6a54a39 | ||
|
|
95c642d4ef | ||
|
|
a92f60bb15 | ||
|
|
bc4f960d88 | ||
|
|
a9dd729a2b | ||
|
|
bb775761de | ||
|
|
139e718fd1 | ||
|
|
9c1298df6b | ||
|
|
f3af75bf44 | ||
|
|
d11971126c | ||
|
|
a7b4f621fb | ||
|
|
71576f36c3 | ||
|
|
a99f16b8db | ||
|
|
0d834f5e23 | ||
|
|
b524cc3b08 | ||
|
|
56f6cb6ae8 | ||
|
|
c80cb01816 | ||
|
|
f4d4b414d5 | ||
|
|
ec8793180d | ||
|
|
855304abc0 | ||
|
|
ac0da55d14 | ||
|
|
d66bc0d746 | ||
|
|
719cedf526 | ||
|
|
c757466df1 | ||
|
|
4d614daad5 | ||
|
|
1e5f565824 | ||
|
|
046837d9a1 | ||
|
|
cfbe4098b7 | ||
|
|
054468bbae | ||
|
|
e723f27375 | ||
|
|
0d230c7721 | ||
|
|
ae9f6b6ac6 | ||
|
|
06d5674199 | ||
|
|
bd450e806b | ||
|
|
849c400497 | ||
|
|
4094d3535c | ||
|
|
d6ba6c628c | ||
|
|
2c4b4ca143 | ||
|
|
65ec5c1b15 | ||
|
|
db29b60551 | ||
|
|
e258767405 | ||
|
|
8de3648bae | ||
|
|
093f9df86e | ||
|
|
030bda215a | ||
|
|
c499528616 | ||
|
|
b459966f95 | ||
|
|
aa63431d57 | ||
|
|
cdf0257598 | ||
|
|
1cd9f75ecc | ||
|
|
6cc659fd23 | ||
|
|
0586c2cdad | ||
|
|
1038573a3a | ||
|
|
4e24a9b50a | ||
|
|
df211c5c10 | ||
|
|
f3e58ad761 | ||
|
|
627d33b2d9 | ||
|
|
7f9ba34afa | ||
|
|
0d4c0c9558 | ||
|
|
a2fb893f49 | ||
|
|
7f02a5a30f | ||
|
|
e482b29fc6 | ||
|
|
e8aa7494a4 | ||
|
|
643ecfcf3e | ||
|
|
d9489e7ddc | ||
|
|
1299524c91 | ||
|
|
683ab95531 | ||
|
|
be1c7aa1c7 | ||
|
|
2967cddf7b | ||
|
|
a573971d49 | ||
|
|
2d55f83bb3 | ||
|
|
670b365def | ||
|
|
d58614b106 | ||
|
|
cee4d02ce2 | ||
|
|
8337f202cf | ||
|
|
141d4b63d2 | ||
|
|
ddcfd72318 | ||
|
|
441b22439f | ||
|
|
a29a7f5be4 | ||
|
|
ae3e061ce8 | ||
|
|
ebcce3183b | ||
|
|
1bea2c0026 | ||
|
|
4e0ed95a22 | ||
|
|
2a3c59ad33 |
108 changed files with 16213 additions and 2014 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -3,6 +3,7 @@ target
|
||||||
wasm/index.css
|
wasm/index.css
|
||||||
wasm/index.html
|
wasm/index.html
|
||||||
docs/gifs/*
|
docs/gifs/*
|
||||||
|
resources/archived resources
|
||||||
|
|
||||||
# VSCode/IDE config files
|
# VSCode/IDE config files
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
|
@ -12,7 +13,7 @@ Cargo.lock
|
||||||
.prettierrc.json
|
.prettierrc.json
|
||||||
|
|
||||||
# Save files, morgue files
|
# Save files, morgue files
|
||||||
savegame.json
|
savegame.bin
|
||||||
morgue
|
morgue
|
||||||
|
|
||||||
# A default user config will be written on first run, if not present
|
# A default user config will be written on first run, if not present
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,16 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
notan = { version = "0.10.0", features = ["text", "audio"] }
|
||||||
bracket-lib = { git = "https://github.com/amethyst/bracket-lib.git", rev = "851f6f08675444fb6fa088b9e67bee9fd75554c6", features = ["serde"] }
|
bracket-lib = { git = "https://github.com/amethyst/bracket-lib.git", rev = "851f6f08675444fb6fa088b9e67bee9fd75554c6", features = ["serde"] }
|
||||||
regex = "1.3.6"
|
regex = "1.3.6"
|
||||||
specs = { version = "0.16.1", features = ["serde"] }
|
specs = { version = "0.20.0", features = ["serde"] }
|
||||||
specs-derive = "0.4.1"
|
specs-derive = "0.4.1"
|
||||||
serde = { version = "1.0.93", features = ["derive"]}
|
serde = { version = "1.0.93", features = ["derive"]}
|
||||||
serde_json = "1.0.39"
|
serde_json = "1.0.39"
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
bincode = "1.3.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "^0.5" }
|
criterion = { version = "^0.5" }
|
||||||
|
|
|
||||||
150
raws/items.json
150
raws/items.json
|
|
@ -2,27 +2,30 @@
|
||||||
{
|
{
|
||||||
"id": "potion_health",
|
"id": "potion_health",
|
||||||
"name": { "name": "potion of health", "plural": "potions of health" },
|
"name": { "name": "potion of health", "plural": "potions of health" },
|
||||||
"renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "!", "sprite": "potion", "fg": "#FF00FF", "order": 4 },
|
||||||
|
"class": "potion",
|
||||||
"weight": 1,
|
"weight": 1,
|
||||||
"value": 50,
|
"value": 50,
|
||||||
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
"flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"],
|
||||||
"effects": { "heal": "4d4+2" },
|
"effects": { "heal": "4d4+2" },
|
||||||
"magic": { "class": "uncommon", "naming": "potion" }
|
"magic": { "class": "uncommon", "naming": "potion" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "potion_health_weak",
|
"id": "potion_health_weak",
|
||||||
"name": { "name": "potion of lesser health", "plural": "potions of lesser health" },
|
"name": { "name": "potion of lesser health", "plural": "potions of lesser health" },
|
||||||
"renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "!", "sprite": "potion", "fg": "#FF00FF", "order": 4 },
|
||||||
|
"class": "potion",
|
||||||
"weight": 1,
|
"weight": 1,
|
||||||
"value": 25,
|
"value": 25,
|
||||||
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
"flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"],
|
||||||
"effects": { "heal": "2d4+2" },
|
"effects": { "heal": "2d4+2" },
|
||||||
"magic": { "class": "uncommon", "naming": "potion" }
|
"magic": { "class": "uncommon", "naming": "potion" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "scroll_identify",
|
"id": "scroll_identify",
|
||||||
"name": { "name": "scroll of identify", "plural": "scrolls of identify" },
|
"name": { "name": "scroll of identify", "plural": "scrolls of identify" },
|
||||||
"renderable": { "glyph": "?", "fg": "#0FFFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "?", "sprite": "scroll_writing", "fg": "#0FFFFF", "order": 4 },
|
||||||
|
"class": "scroll",
|
||||||
"weight": 0.5,
|
"weight": 0.5,
|
||||||
"value": 100,
|
"value": 100,
|
||||||
"flags": ["CONSUMABLE", "DESTRUCTIBLE", "IDENTIFY"],
|
"flags": ["CONSUMABLE", "DESTRUCTIBLE", "IDENTIFY"],
|
||||||
|
|
@ -31,7 +34,8 @@
|
||||||
{
|
{
|
||||||
"id": "scroll_removecurse",
|
"id": "scroll_removecurse",
|
||||||
"name": { "name": "scroll of remove curse", "plural": "scrolls of remove curse" },
|
"name": { "name": "scroll of remove curse", "plural": "scrolls of remove curse" },
|
||||||
"renderable": { "glyph": "?", "fg": "#0FFFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "?", "sprite": "scroll_writing", "fg": "#0FFFFF", "order": 4 },
|
||||||
|
"class": "scroll",
|
||||||
"weight": 0.5,
|
"weight": 0.5,
|
||||||
"value": 200,
|
"value": 200,
|
||||||
"flags": ["CONSUMABLE", "DESTRUCTIBLE", "REMOVE_CURSE"],
|
"flags": ["CONSUMABLE", "DESTRUCTIBLE", "REMOVE_CURSE"],
|
||||||
|
|
@ -40,7 +44,8 @@
|
||||||
{
|
{
|
||||||
"id": "scroll_health",
|
"id": "scroll_health",
|
||||||
"name": { "name": "scroll of healing word", "plural": "scrolls of healing word" },
|
"name": { "name": "scroll of healing word", "plural": "scrolls of healing word" },
|
||||||
"renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "?", "sprite": "scroll_writing", "fg": "#00FFFF", "order": 4 },
|
||||||
|
"class": "scroll",
|
||||||
"weight": 0.5,
|
"weight": 0.5,
|
||||||
"value": 50,
|
"value": 50,
|
||||||
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
||||||
|
|
@ -50,7 +55,8 @@
|
||||||
{
|
{
|
||||||
"id": "scroll_mass_health",
|
"id": "scroll_mass_health",
|
||||||
"name": { "name": "scroll of mass healing word", "plural": "scrolls of mass healing word" },
|
"name": { "name": "scroll of mass healing word", "plural": "scrolls of mass healing word" },
|
||||||
"renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "?", "sprite": "scroll_writing", "fg": "#00FFFF", "order": 4 },
|
||||||
|
"class": "scroll",
|
||||||
"weight": 0.5,
|
"weight": 0.5,
|
||||||
"value": 200,
|
"value": 200,
|
||||||
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
||||||
|
|
@ -60,7 +66,8 @@
|
||||||
{
|
{
|
||||||
"id": "scroll_magicmissile",
|
"id": "scroll_magicmissile",
|
||||||
"name": { "name": "scroll of magic missile", "plural": "scrolls of magic missile" },
|
"name": { "name": "scroll of magic missile", "plural": "scrolls of magic missile" },
|
||||||
"renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "?", "sprite": "scroll_writing", "fg": "#00FFFF", "order": 4 },
|
||||||
|
"class": "scroll",
|
||||||
"weight": 0.5,
|
"weight": 0.5,
|
||||||
"value": 50,
|
"value": 50,
|
||||||
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
||||||
|
|
@ -70,7 +77,8 @@
|
||||||
{
|
{
|
||||||
"id": "scroll_embers",
|
"id": "scroll_embers",
|
||||||
"name": { "name": "scroll of embers", "plural": "scrolls of embers" },
|
"name": { "name": "scroll of embers", "plural": "scrolls of embers" },
|
||||||
"renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "?", "sprite": "scroll_writing", "fg": "#00FFFF", "order": 4 },
|
||||||
|
"class": "scroll",
|
||||||
"weight": 0.5,
|
"weight": 0.5,
|
||||||
"value": 100,
|
"value": 100,
|
||||||
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
||||||
|
|
@ -80,7 +88,8 @@
|
||||||
{
|
{
|
||||||
"id": "scroll_fireball",
|
"id": "scroll_fireball",
|
||||||
"name": { "name": "scroll of fireball", "plural": "scrolls of fireball" },
|
"name": { "name": "scroll of fireball", "plural": "scrolls of fireball" },
|
||||||
"renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "?", "sprite": "scroll_writing", "fg": "#00FFFF", "order": 4 },
|
||||||
|
"class": "scroll",
|
||||||
"weight": 0.5,
|
"weight": 0.5,
|
||||||
"value": 200,
|
"value": 200,
|
||||||
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
||||||
|
|
@ -95,7 +104,8 @@
|
||||||
{
|
{
|
||||||
"id": "scroll_confusion",
|
"id": "scroll_confusion",
|
||||||
"name": { "name": "scroll of confusion", "plural": "scrolls of confusion" },
|
"name": { "name": "scroll of confusion", "plural": "scrolls of confusion" },
|
||||||
"renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "?", "sprite": "scroll_writing", "fg": "#00FFFF", "order": 4 },
|
||||||
|
"class": "scroll",
|
||||||
"weight": 0.5,
|
"weight": 0.5,
|
||||||
"value": 100,
|
"value": 100,
|
||||||
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
||||||
|
|
@ -105,7 +115,8 @@
|
||||||
{
|
{
|
||||||
"id": "scroll_mass_confusion",
|
"id": "scroll_mass_confusion",
|
||||||
"name": { "name": "scroll of mass confusion", "plural": "scrolls of mass confusion" },
|
"name": { "name": "scroll of mass confusion", "plural": "scrolls of mass confusion" },
|
||||||
"renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "?", "sprite": "scroll_writing", "fg": "#00FFFF", "order": 4 },
|
||||||
|
"class": "scroll",
|
||||||
"weight": 0.5,
|
"weight": 0.5,
|
||||||
"value": 200,
|
"value": 200,
|
||||||
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
"flags": ["CONSUMABLE", "DESTRUCTIBLE"],
|
||||||
|
|
@ -115,7 +126,8 @@
|
||||||
{
|
{
|
||||||
"id": "scroll_magicmap",
|
"id": "scroll_magicmap",
|
||||||
"name": { "name": "scroll of magic mapping", "plural": "scrolls of magic mapping" },
|
"name": { "name": "scroll of magic mapping", "plural": "scrolls of magic mapping" },
|
||||||
"renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "?", "sprite": "scroll_writing", "fg": "#00FFFF", "order": 4 },
|
||||||
|
"class": "scroll",
|
||||||
"weight": 0.5,
|
"weight": 0.5,
|
||||||
"value": 50,
|
"value": 50,
|
||||||
"flags": ["CONSUMABLE", "DESTRUCTIBLE", "MAGICMAP"],
|
"flags": ["CONSUMABLE", "DESTRUCTIBLE", "MAGICMAP"],
|
||||||
|
|
@ -125,7 +137,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_dagger",
|
"id": "equip_dagger",
|
||||||
"name": { "name": "dagger", "plural": "daggers" },
|
"name": { "name": "dagger", "plural": "daggers" },
|
||||||
"renderable": { "glyph": ")", "fg": "#808080", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": ")", "sprite": "dagger", "fg": "#808080", "order": 4 },
|
||||||
|
"class": "weapon",
|
||||||
"weight": 1,
|
"weight": 1,
|
||||||
"value": 2,
|
"value": 2,
|
||||||
"flags": ["EQUIP_MELEE"],
|
"flags": ["EQUIP_MELEE"],
|
||||||
|
|
@ -134,7 +147,9 @@
|
||||||
{
|
{
|
||||||
"id": "equip_shortsword",
|
"id": "equip_shortsword",
|
||||||
"name": { "name": "shortsword", "plural": "shortswords" },
|
"name": { "name": "shortsword", "plural": "shortswords" },
|
||||||
"renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": ")", "sprite": "shortsword", "fg": "#C0C0C0", "order": 4 },
|
||||||
|
"avatar": "a_shortsword",
|
||||||
|
"class": "weapon",
|
||||||
"weight": 2,
|
"weight": 2,
|
||||||
"value": 10,
|
"value": 10,
|
||||||
"flags": ["EQUIP_MELEE"],
|
"flags": ["EQUIP_MELEE"],
|
||||||
|
|
@ -143,7 +158,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_rapier",
|
"id": "equip_rapier",
|
||||||
"name": { "name": "rapier", "plural": "rapiers" },
|
"name": { "name": "rapier", "plural": "rapiers" },
|
||||||
"renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": ")", "sprite": "gnome", "fg": "#C0C0C0", "order": 4 },
|
||||||
|
"class": "weapon",
|
||||||
"weight": 2,
|
"weight": 2,
|
||||||
"value": 10,
|
"value": 10,
|
||||||
"flags": ["EQUIP_MELEE"],
|
"flags": ["EQUIP_MELEE"],
|
||||||
|
|
@ -152,7 +168,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_pitchfork",
|
"id": "equip_pitchfork",
|
||||||
"name": { "name": "pitchfork", "plural": "pitchforks" },
|
"name": { "name": "pitchfork", "plural": "pitchforks" },
|
||||||
"renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": ")", "sprite": "trident", "fg": "#C0C0C0", "order": 4 },
|
||||||
|
"class": "weapon",
|
||||||
"weight": 2,
|
"weight": 2,
|
||||||
"value": 5,
|
"value": 5,
|
||||||
"flags": ["EQUIP_MELEE"],
|
"flags": ["EQUIP_MELEE"],
|
||||||
|
|
@ -161,7 +178,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_sickle",
|
"id": "equip_sickle",
|
||||||
"name": { "name": "sickle", "plural": "sickles" },
|
"name": { "name": "sickle", "plural": "sickles" },
|
||||||
"renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": ")", "sprite": "gnome", "fg": "#C0C0C0", "order": 4 },
|
||||||
|
"class": "weapon",
|
||||||
"weight": 2,
|
"weight": 2,
|
||||||
"value": 5,
|
"value": 5,
|
||||||
"flags": ["EQUIP_MELEE"],
|
"flags": ["EQUIP_MELEE"],
|
||||||
|
|
@ -170,7 +188,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_handaxe",
|
"id": "equip_handaxe",
|
||||||
"name": { "name": "handaxe", "plural": "handaxes" },
|
"name": { "name": "handaxe", "plural": "handaxes" },
|
||||||
"renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": ")", "sprite": "handaxe", "fg": "#C0C0C0", "order": 4 },
|
||||||
|
"class": "weapon",
|
||||||
"weight": 2,
|
"weight": 2,
|
||||||
"value": 5,
|
"value": 5,
|
||||||
"flags": ["EQUIP_MELEE"],
|
"flags": ["EQUIP_MELEE"],
|
||||||
|
|
@ -179,16 +198,29 @@
|
||||||
{
|
{
|
||||||
"id": "equip_longsword",
|
"id": "equip_longsword",
|
||||||
"name": { "name": "longsword", "plural": "longswords" },
|
"name": { "name": "longsword", "plural": "longswords" },
|
||||||
"renderable": { "glyph": ")", "fg": "#FFF8DC", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": ")", "sprite": "longsword", "fg": "#FFF8DC", "order": 4 },
|
||||||
|
"avatar": "a_longsword",
|
||||||
|
"class": "weapon",
|
||||||
"weight": 3,
|
"weight": 3,
|
||||||
"value": 15,
|
"value": 15,
|
||||||
"flags": ["EQUIP_MELEE"],
|
"flags": ["EQUIP_MELEE"],
|
||||||
"equip": { "flag": "STRENGTH", "damage": "1d8", "to_hit": 0 }
|
"equip": { "flag": "STRENGTH", "damage": "1d8", "to_hit": 0 }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "artifact_icingdeath",
|
||||||
|
"name": { "name": "Icingdeath", "plural": "Icingdeath" },
|
||||||
|
"renderable": { "glyph": ")", "sprite": "scimitar", "fg": "#37aecc", "order": 4 },
|
||||||
|
"class": "weapon",
|
||||||
|
"weight": 3,
|
||||||
|
"value": 300,
|
||||||
|
"flags": ["EQUIP_MELEE"],
|
||||||
|
"equip": { "flag": "FINESSE", "damage": "1d8+3;cold", "to_hit": 3 }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "equip_smallshield",
|
"id": "equip_smallshield",
|
||||||
"name": { "name": "buckler", "plural": "bucklers" },
|
"name": { "name": "buckler", "plural": "bucklers" },
|
||||||
"renderable": { "glyph": "[", "fg": "#808080", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "shield_small", "fg": "#808080", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 2,
|
"weight": 2,
|
||||||
"value": 5,
|
"value": 5,
|
||||||
"flags": ["EQUIP_SHIELD"],
|
"flags": ["EQUIP_SHIELD"],
|
||||||
|
|
@ -197,7 +229,9 @@
|
||||||
{
|
{
|
||||||
"id": "equip_mediumshield",
|
"id": "equip_mediumshield",
|
||||||
"name": { "name": "medium shield", "plural": "medium shields" },
|
"name": { "name": "medium shield", "plural": "medium shields" },
|
||||||
"renderable": { "glyph": "[", "fg": "#C0C0C0", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "shield_round", "fg": "#C0C0C0", "order": 4 },
|
||||||
|
"avatar": "a_medshield",
|
||||||
|
"class": "armour",
|
||||||
"weight": 6,
|
"weight": 6,
|
||||||
"value": 10,
|
"value": 10,
|
||||||
"flags": ["EQUIP_SHIELD"],
|
"flags": ["EQUIP_SHIELD"],
|
||||||
|
|
@ -206,7 +240,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_largeshield",
|
"id": "equip_largeshield",
|
||||||
"name": { "name": "large shield", "plural": "large shields" },
|
"name": { "name": "large shield", "plural": "large shields" },
|
||||||
"renderable": { "glyph": "[", "fg": "#FFF8DC", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "shield_large", "fg": "#FFF8DC", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 12,
|
"weight": 12,
|
||||||
"value": 35,
|
"value": 35,
|
||||||
"flags": ["EQUIP_SHIELD"],
|
"flags": ["EQUIP_SHIELD"],
|
||||||
|
|
@ -215,7 +250,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_body_weakleather",
|
"id": "equip_body_weakleather",
|
||||||
"name": { "name": "leather jacket", "plural": "leather jackets" },
|
"name": { "name": "leather jacket", "plural": "leather jackets" },
|
||||||
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 8,
|
"weight": 8,
|
||||||
"value": 5,
|
"value": 5,
|
||||||
"flags": ["EQUIP_BODY"],
|
"flags": ["EQUIP_BODY"],
|
||||||
|
|
@ -224,7 +260,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_body_leather",
|
"id": "equip_body_leather",
|
||||||
"name": { "name": "leather chestpiece", "plural": "leather chestpiece" },
|
"name": { "name": "leather chestpiece", "plural": "leather chestpiece" },
|
||||||
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 10,
|
"weight": 10,
|
||||||
"value": 10,
|
"value": 10,
|
||||||
"flags": ["EQUIP_BODY"],
|
"flags": ["EQUIP_BODY"],
|
||||||
|
|
@ -233,7 +270,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_body_studdedleather",
|
"id": "equip_body_studdedleather",
|
||||||
"name": { "name": "studded leather chestpiece", "plural": "studded leather chestpieces" },
|
"name": { "name": "studded leather chestpiece", "plural": "studded leather chestpieces" },
|
||||||
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 13,
|
"weight": 13,
|
||||||
"value": 45,
|
"value": 45,
|
||||||
"flags": ["EQUIP_BODY"],
|
"flags": ["EQUIP_BODY"],
|
||||||
|
|
@ -242,7 +280,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_body_ringmail_o",
|
"id": "equip_body_ringmail_o",
|
||||||
"name": { "name": "orcish ring mail", "plural": "orcish ring mail" },
|
"name": { "name": "orcish ring mail", "plural": "orcish ring mail" },
|
||||||
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 45,
|
"weight": 45,
|
||||||
"value": 50,
|
"value": 50,
|
||||||
"flags": ["EQUIP_BODY"],
|
"flags": ["EQUIP_BODY"],
|
||||||
|
|
@ -251,7 +290,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_body_ringmail",
|
"id": "equip_body_ringmail",
|
||||||
"name": { "name": "ring mail", "plural": "ring mail" },
|
"name": { "name": "ring mail", "plural": "ring mail" },
|
||||||
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 45,
|
"weight": 45,
|
||||||
"value": 70,
|
"value": 70,
|
||||||
"flags": ["EQUIP_BODY"],
|
"flags": ["EQUIP_BODY"],
|
||||||
|
|
@ -260,7 +300,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_head_leather",
|
"id": "equip_head_leather",
|
||||||
"name": { "name": "leather cap", "plural": "leather caps" },
|
"name": { "name": "leather cap", "plural": "leather caps" },
|
||||||
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 2,
|
"weight": 2,
|
||||||
"value": 10,
|
"value": 10,
|
||||||
"flags": ["EQUIP_HEAD"],
|
"flags": ["EQUIP_HEAD"],
|
||||||
|
|
@ -269,7 +310,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_head_elvish",
|
"id": "equip_head_elvish",
|
||||||
"name": { "name": "elvish leather helm", "plural": "elvish leather helms" },
|
"name": { "name": "elvish leather helm", "plural": "elvish leather helms" },
|
||||||
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 2,
|
"weight": 2,
|
||||||
"value": 25,
|
"value": 25,
|
||||||
"flags": ["EQUIP_HEAD"],
|
"flags": ["EQUIP_HEAD"],
|
||||||
|
|
@ -278,7 +320,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_head_o",
|
"id": "equip_head_o",
|
||||||
"name": { "name": "orcish helm", "plural": "orcish helm" },
|
"name": { "name": "orcish helm", "plural": "orcish helm" },
|
||||||
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 6,
|
"weight": 6,
|
||||||
"value": 25,
|
"value": 25,
|
||||||
"flags": ["EQUIP_HEAD"],
|
"flags": ["EQUIP_HEAD"],
|
||||||
|
|
@ -287,7 +330,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_head_iron",
|
"id": "equip_head_iron",
|
||||||
"name": { "name": "iron helm", "plural": "iron helm" },
|
"name": { "name": "iron helm", "plural": "iron helm" },
|
||||||
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 10,
|
"weight": 10,
|
||||||
"value": 45,
|
"value": 45,
|
||||||
"flags": ["EQUIP_HEAD"],
|
"flags": ["EQUIP_HEAD"],
|
||||||
|
|
@ -296,7 +340,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_feet_leather",
|
"id": "equip_feet_leather",
|
||||||
"name": { "name": "leather shoes", "plural": "leather shoes" },
|
"name": { "name": "leather shoes", "plural": "leather shoes" },
|
||||||
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 2,
|
"weight": 2,
|
||||||
"value": 10,
|
"value": 10,
|
||||||
"flags": ["EQUIP_FEET"]
|
"flags": ["EQUIP_FEET"]
|
||||||
|
|
@ -304,7 +349,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_feet_elvish",
|
"id": "equip_feet_elvish",
|
||||||
"name": { "name": "elvish leather shoes", "plural": "elvish leather shoes" },
|
"name": { "name": "elvish leather shoes", "plural": "elvish leather shoes" },
|
||||||
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 2,
|
"weight": 2,
|
||||||
"value": 25,
|
"value": 25,
|
||||||
"flags": ["EQUIP_FEET"],
|
"flags": ["EQUIP_FEET"],
|
||||||
|
|
@ -313,7 +359,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_feet_o",
|
"id": "equip_feet_o",
|
||||||
"name": { "name": "orcish boots", "plural": "orcish boots" },
|
"name": { "name": "orcish boots", "plural": "orcish boots" },
|
||||||
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 6,
|
"weight": 6,
|
||||||
"value": 25,
|
"value": 25,
|
||||||
"flags": ["EQUIP_FEET"],
|
"flags": ["EQUIP_FEET"],
|
||||||
|
|
@ -322,7 +369,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_feet_iron",
|
"id": "equip_feet_iron",
|
||||||
"name": { "name": "iron boots", "plural": "iron boots" },
|
"name": { "name": "iron boots", "plural": "iron boots" },
|
||||||
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 10,
|
"weight": 10,
|
||||||
"value": 45,
|
"value": 45,
|
||||||
"flags": ["EQUIP_FEET"],
|
"flags": ["EQUIP_FEET"],
|
||||||
|
|
@ -331,7 +379,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_neck_protection",
|
"id": "equip_neck_protection",
|
||||||
"name": { "name": "amulet of protection", "plural": "amulets of protection" },
|
"name": { "name": "amulet of protection", "plural": "amulets of protection" },
|
||||||
"renderable": { "glyph": "\"", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "\"", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "amulet",
|
||||||
"weight": 1,
|
"weight": 1,
|
||||||
"value": 200,
|
"value": 200,
|
||||||
"flags": ["EQUIP_NECK"],
|
"flags": ["EQUIP_NECK"],
|
||||||
|
|
@ -340,7 +389,8 @@
|
||||||
{
|
{
|
||||||
"id": "equip_back_protection",
|
"id": "equip_back_protection",
|
||||||
"name": { "name": "cloak of protection", "plural": "cloaks of protection" },
|
"name": { "name": "cloak of protection", "plural": "cloaks of protection" },
|
||||||
"renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "[", "sprite": "body_leather", "fg": "#aa6000", "order": 4 },
|
||||||
|
"class": "armour",
|
||||||
"weight": 1,
|
"weight": 1,
|
||||||
"value": 200,
|
"value": 200,
|
||||||
"flags": ["EQUIP_BACK"],
|
"flags": ["EQUIP_BACK"],
|
||||||
|
|
@ -349,7 +399,8 @@
|
||||||
{
|
{
|
||||||
"id": "wand_magicmissile",
|
"id": "wand_magicmissile",
|
||||||
"name": { "name": "wand of magic missile", "plural": "wands of magic missile" },
|
"name": { "name": "wand of magic missile", "plural": "wands of magic missile" },
|
||||||
"renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "/", "sprite": "body_leather", "fg": "#00FFFF", "order": 4 },
|
||||||
|
"class": "wand",
|
||||||
"weight": 2,
|
"weight": 2,
|
||||||
"value": 100,
|
"value": 100,
|
||||||
"flags": ["CHARGES"],
|
"flags": ["CHARGES"],
|
||||||
|
|
@ -359,7 +410,8 @@
|
||||||
{
|
{
|
||||||
"id": "wand_fireball",
|
"id": "wand_fireball",
|
||||||
"name": { "name": "wand of fireball", "plural": "wands of fireball" },
|
"name": { "name": "wand of fireball", "plural": "wands of fireball" },
|
||||||
"renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "/", "sprite": "body_leather", "fg": "#00FFFF", "order": 4 },
|
||||||
|
"class": "wand",
|
||||||
"weight": 2,
|
"weight": 2,
|
||||||
"value": 300,
|
"value": 300,
|
||||||
"flags": ["CHARGES"],
|
"flags": ["CHARGES"],
|
||||||
|
|
@ -369,7 +421,8 @@
|
||||||
{
|
{
|
||||||
"id": "wand_confusion",
|
"id": "wand_confusion",
|
||||||
"name": { "name": "wand of confusion", "plural": "wands of confusion" },
|
"name": { "name": "wand of confusion", "plural": "wands of confusion" },
|
||||||
"renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "/", "sprite": "body_leather", "fg": "#00FFFF", "order": 4 },
|
||||||
|
"class": "wand",
|
||||||
"weight": 2,
|
"weight": 2,
|
||||||
"value": 200,
|
"value": 200,
|
||||||
"flags": ["CHARGES"],
|
"flags": ["CHARGES"],
|
||||||
|
|
@ -379,7 +432,8 @@
|
||||||
{
|
{
|
||||||
"id": "wand_digging",
|
"id": "wand_digging",
|
||||||
"name": { "name": "wand of digging", "plural": "wands of digging" },
|
"name": { "name": "wand of digging", "plural": "wands of digging" },
|
||||||
"renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "/", "sprite": "body_leather", "fg": "#00FFFF", "order": 4 },
|
||||||
|
"class": "wand",
|
||||||
"weight": 2,
|
"weight": 2,
|
||||||
"value": 300,
|
"value": 300,
|
||||||
"flags": ["CHARGES", "DIGGER"],
|
"flags": ["CHARGES", "DIGGER"],
|
||||||
|
|
@ -389,17 +443,19 @@
|
||||||
{
|
{
|
||||||
"id": "food_rations",
|
"id": "food_rations",
|
||||||
"name": { "name": "rations", "plural": "rations" },
|
"name": { "name": "rations", "plural": "rations" },
|
||||||
"renderable": { "glyph": "%", "fg": "#FFA07A", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "%", "sprite": "meat", "fg": "#FFA07A", "order": 4 },
|
||||||
|
"class": "comestible",
|
||||||
"weight": 1,
|
"weight": 1,
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"flags": ["FOOD", "CONSUMABLE"]
|
"flags": ["FOOD", "CONSUMABLE", "STACKABLE"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "food_apple",
|
"id": "food_apple",
|
||||||
"name": { "name": "apple", "plural": "apples" },
|
"name": { "name": "apple", "plural": "apples" },
|
||||||
"renderable": { "glyph": "%", "fg": "#00FF00", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "%", "sprite": "body_leather", "fg": "#00FF00", "order": 4 },
|
||||||
|
"class": "comestible",
|
||||||
"weight": 0.5,
|
"weight": 0.5,
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"flags": ["FOOD", "CONSUMABLE"]
|
"flags": ["FOOD", "CONSUMABLE", "STACKABLE"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
104
raws/mobs.json
104
raws/mobs.json
|
|
@ -2,7 +2,7 @@
|
||||||
{
|
{
|
||||||
"id": "npc_barkeep",
|
"id": "npc_barkeep",
|
||||||
"name": "barkeep",
|
"name": "barkeep",
|
||||||
"renderable": { "glyph": "@", "fg": "#EE82EE", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "@", "sprite": "gnome", "fg": "#EE82EE", "order": 3 },
|
||||||
"flags": ["NEUTRAL", "IS_HUMAN"],
|
"flags": ["NEUTRAL", "IS_HUMAN"],
|
||||||
"vision_range": 4,
|
"vision_range": 4,
|
||||||
"quips": ["Drink?", "Something to eat?", "Don't go out on an empty stomach."]
|
"quips": ["Drink?", "Something to eat?", "Don't go out on an empty stomach."]
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
{
|
{
|
||||||
"id": "npc_townsperson",
|
"id": "npc_townsperson",
|
||||||
"name": "townsperson",
|
"name": "townsperson",
|
||||||
"renderable": { "glyph": "@", "fg": "#9fa86c", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "@", "sprite": "gnome", "fg": "#9fa86c", "order": 3 },
|
||||||
"flags": ["NEUTRAL", "RANDOM_PATH", "IS_HUMAN"],
|
"flags": ["NEUTRAL", "RANDOM_PATH", "IS_HUMAN"],
|
||||||
"vision_range": 4,
|
"vision_range": 4,
|
||||||
"quips": ["Hello!", "Good morning.", "<a quiet complaint about chores>"]
|
"quips": ["Hello!", "Good morning.", "<a quiet complaint about chores>"]
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
{
|
{
|
||||||
"id": "npc_drunk",
|
"id": "npc_drunk",
|
||||||
"name": "drunk",
|
"name": "drunk",
|
||||||
"renderable": { "glyph": "@", "fg": "#a0a83c", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "@", "sprite": "gnome", "fg": "#a0a83c", "order": 3 },
|
||||||
"flags": ["NEUTRAL", "IS_HUMAN"],
|
"flags": ["NEUTRAL", "IS_HUMAN"],
|
||||||
"vision_range": 4,
|
"vision_range": 4,
|
||||||
"quips": ["Hic!", "H-Hic'.", "Get me 'nother, would you?"]
|
"quips": ["Hic!", "H-Hic'.", "Get me 'nother, would you?"]
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
{
|
{
|
||||||
"id": "npc_fisher",
|
"id": "npc_fisher",
|
||||||
"name": "fisher",
|
"name": "fisher",
|
||||||
"renderable": { "glyph": "@", "fg": "#3ca3a8", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "@", "sprite": "gnome", "fg": "#3ca3a8", "order": 3 },
|
||||||
"flags": ["NEUTRAL", "IS_HUMAN"],
|
"flags": ["NEUTRAL", "IS_HUMAN"],
|
||||||
"vision_range": 4,
|
"vision_range": 4,
|
||||||
"quips": ["Hey."]
|
"quips": ["Hey."]
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
{
|
{
|
||||||
"id": "npc_dockworker",
|
"id": "npc_dockworker",
|
||||||
"name": "dock worker",
|
"name": "dock worker",
|
||||||
"renderable": { "glyph": "@", "fg": "#68d8de", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "@", "sprite": "gnome", "fg": "#68d8de", "order": 3 },
|
||||||
"flags": ["NEUTRAL", "IS_HUMAN"],
|
"flags": ["NEUTRAL", "IS_HUMAN"],
|
||||||
"vision_range": 4,
|
"vision_range": 4,
|
||||||
"quips": ["No boat for a few days.", "Not much for us to do."]
|
"quips": ["No boat for a few days.", "Not much for us to do."]
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
{
|
{
|
||||||
"id": "npc_priest",
|
"id": "npc_priest",
|
||||||
"name": "priest",
|
"name": "priest",
|
||||||
"renderable": { "glyph": "@", "fg": "#FFFFFF", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "@", "sprite": "gnome", "fg": "#FFFFFF", "order": 3 },
|
||||||
"flags": ["NEUTRAL", "IS_HUMAN"],
|
"flags": ["NEUTRAL", "IS_HUMAN"],
|
||||||
"vision_range": 4,
|
"vision_range": 4,
|
||||||
"quips": ["Light's givings.", "<a quiet prayer>", "Bless you."]
|
"quips": ["Light's givings.", "<a quiet prayer>", "Bless you."]
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
{
|
{
|
||||||
"id": "npc_miner",
|
"id": "npc_miner",
|
||||||
"name": "miner",
|
"name": "miner",
|
||||||
"renderable": { "glyph": "@", "fg": "#946123", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "@", "sprite": "gnome", "fg": "#946123", "order": 3 },
|
||||||
"flags": ["NEUTRAL", "IS_HUMAN"],
|
"flags": ["NEUTRAL", "IS_HUMAN"],
|
||||||
"vision_range": 4,
|
"vision_range": 4,
|
||||||
"attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "1d8" }],
|
"attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "1d8" }],
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
{
|
{
|
||||||
"id": "npc_guard",
|
"id": "npc_guard",
|
||||||
"name": "smalltown guard",
|
"name": "smalltown guard",
|
||||||
"renderable": { "glyph": "@", "fg": "#034efc", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "@", "sprite": "gnome", "fg": "#034efc", "order": 3 },
|
||||||
"flags": ["NEUTRAL", "RANDOM_PATH", "IS_HUMAN"],
|
"flags": ["NEUTRAL", "RANDOM_PATH", "IS_HUMAN"],
|
||||||
"level": 2,
|
"level": 2,
|
||||||
"attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "1d8" }],
|
"attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "1d8" }],
|
||||||
|
|
@ -69,7 +69,7 @@
|
||||||
{
|
{
|
||||||
"id": "rat",
|
"id": "rat",
|
||||||
"name": "rat",
|
"name": "rat",
|
||||||
"renderable": { "glyph": "r", "fg": "#aa6000", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "r", "sprite": "rat", "fg": "#aa6000", "order": 3 },
|
||||||
"flags": [],
|
"flags": [],
|
||||||
"bac": 6,
|
"bac": 6,
|
||||||
"attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d2" }],
|
"attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d2" }],
|
||||||
|
|
@ -78,7 +78,7 @@
|
||||||
{
|
{
|
||||||
"id": "chicken",
|
"id": "chicken",
|
||||||
"name": "chicken",
|
"name": "chicken",
|
||||||
"renderable": { "glyph": "c", "fg": "#BB6000", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "c", "sprite": "gnome", "fg": "#BB6000", "order": 3 },
|
||||||
"flags": ["HERBIVORE"],
|
"flags": ["HERBIVORE"],
|
||||||
"bac": 8,
|
"bac": 8,
|
||||||
"attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d3" }]
|
"attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d3" }]
|
||||||
|
|
@ -86,7 +86,7 @@
|
||||||
{
|
{
|
||||||
"id": "deer_little",
|
"id": "deer_little",
|
||||||
"name": "fawn",
|
"name": "fawn",
|
||||||
"renderable": { "glyph": "q", "fg": "#a57037", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "q", "sprite": "gnome", "fg": "#a57037", "order": 3 },
|
||||||
"flags": ["HERBIVORE"],
|
"flags": ["HERBIVORE"],
|
||||||
"bac": 8,
|
"bac": 8,
|
||||||
"attacks": [{ "name": "kicks", "hit_bonus": 0, "damage": "1d2" }]
|
"attacks": [{ "name": "kicks", "hit_bonus": 0, "damage": "1d2" }]
|
||||||
|
|
@ -94,7 +94,7 @@
|
||||||
{
|
{
|
||||||
"id": "sheep_little",
|
"id": "sheep_little",
|
||||||
"name": "lamb",
|
"name": "lamb",
|
||||||
"renderable": { "glyph": "q", "fg": "#e7e7e7", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "q", "sprite": "gnome", "fg": "#e7e7e7", "order": 3 },
|
||||||
"flags": ["HERBIVORE", "SMALL_GROUP"],
|
"flags": ["HERBIVORE", "SMALL_GROUP"],
|
||||||
"bac": 10,
|
"bac": 10,
|
||||||
"attacks": [{ "name": "kicks", "hit_bonus": 0, "damage": "1d2" }]
|
"attacks": [{ "name": "kicks", "hit_bonus": 0, "damage": "1d2" }]
|
||||||
|
|
@ -102,7 +102,7 @@
|
||||||
{
|
{
|
||||||
"id": "chicken_little",
|
"id": "chicken_little",
|
||||||
"name": "chick",
|
"name": "chick",
|
||||||
"renderable": { "glyph": "c", "fg": "#fae478", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "c", "sprite": "gnome", "fg": "#fae478", "order": 3 },
|
||||||
"flags": ["HERBIVORE"],
|
"flags": ["HERBIVORE"],
|
||||||
"bac": 10,
|
"bac": 10,
|
||||||
"attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d2" }]
|
"attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d2" }]
|
||||||
|
|
@ -110,7 +110,7 @@
|
||||||
{
|
{
|
||||||
"id": "horse_little",
|
"id": "horse_little",
|
||||||
"name": "pony",
|
"name": "pony",
|
||||||
"renderable": { "glyph": "u", "fg": "#b36c29", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "u", "sprite": "horse", "fg": "#b36c29", "order": 3 },
|
||||||
"flags": ["HERBIVORE", "MULTIATTACK"],
|
"flags": ["HERBIVORE", "MULTIATTACK"],
|
||||||
"level": 3,
|
"level": 3,
|
||||||
"bac": 6,
|
"bac": 6,
|
||||||
|
|
@ -124,7 +124,7 @@
|
||||||
{
|
{
|
||||||
"id": "horse",
|
"id": "horse",
|
||||||
"name": "horse",
|
"name": "horse",
|
||||||
"renderable": { "glyph": "u", "fg": "#744d29", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "u", "sprite": "horse", "fg": "#744d29", "order": 3 },
|
||||||
"flags": ["MULTIATTACK"],
|
"flags": ["MULTIATTACK"],
|
||||||
"level": 5,
|
"level": 5,
|
||||||
"bac": 5,
|
"bac": 5,
|
||||||
|
|
@ -137,7 +137,7 @@
|
||||||
{
|
{
|
||||||
"id": "horse_large",
|
"id": "horse_large",
|
||||||
"name": "warhorse",
|
"name": "warhorse",
|
||||||
"renderable": { "glyph": "u", "fg": "#8a3520", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "u", "sprite": "horse", "fg": "#8a3520", "order": 3 },
|
||||||
"flags": ["MULTIATTACK"],
|
"flags": ["MULTIATTACK"],
|
||||||
"level": 7,
|
"level": 7,
|
||||||
"bac": 4,
|
"bac": 4,
|
||||||
|
|
@ -150,7 +150,7 @@
|
||||||
{
|
{
|
||||||
"id": "rat_giant",
|
"id": "rat_giant",
|
||||||
"name": "giant rat",
|
"name": "giant rat",
|
||||||
"renderable": { "glyph": "r", "fg": "#bb8000", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "r", "sprite": "rat_large", "fg": "#bb8000", "order": 3 },
|
||||||
"flags": ["SMALL_GROUP"],
|
"flags": ["SMALL_GROUP"],
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"bac": 7,
|
"bac": 7,
|
||||||
|
|
@ -160,7 +160,7 @@
|
||||||
{
|
{
|
||||||
"id": "dog_little",
|
"id": "dog_little",
|
||||||
"name": "little dog",
|
"name": "little dog",
|
||||||
"renderable": { "glyph": "d", "fg": "#FFFFFF", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "d", "sprite": "dog", "fg": "#FFFFFF", "order": 3 },
|
||||||
"flags": ["NEUTRAL"],
|
"flags": ["NEUTRAL"],
|
||||||
"level": 2,
|
"level": 2,
|
||||||
"bac": 6,
|
"bac": 6,
|
||||||
|
|
@ -171,7 +171,7 @@
|
||||||
{
|
{
|
||||||
"id": "dog",
|
"id": "dog",
|
||||||
"name": "dog",
|
"name": "dog",
|
||||||
"renderable": { "glyph": "d", "fg": "#EEEEEE", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "d", "sprite": "dog", "fg": "#EEEEEE", "order": 3 },
|
||||||
"flags": [],
|
"flags": [],
|
||||||
"level": 4,
|
"level": 4,
|
||||||
"bac": 5,
|
"bac": 5,
|
||||||
|
|
@ -181,7 +181,7 @@
|
||||||
{
|
{
|
||||||
"id": "dog_large",
|
"id": "dog_large",
|
||||||
"name": "large dog",
|
"name": "large dog",
|
||||||
"renderable": { "glyph": "d", "fg": "#DDDDDD", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "d", "sprite": "dog", "fg": "#DDDDDD", "order": 3 },
|
||||||
"flags": [],
|
"flags": [],
|
||||||
"level": 6,
|
"level": 6,
|
||||||
"bac": 4,
|
"bac": 4,
|
||||||
|
|
@ -191,7 +191,7 @@
|
||||||
{
|
{
|
||||||
"id": "gnome",
|
"id": "gnome",
|
||||||
"name": "gnome",
|
"name": "gnome",
|
||||||
"renderable": { "glyph": "G", "fg": "#AA5500", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "G", "sprite": "gnome", "fg": "#AA5500", "order": 3 },
|
||||||
"flags": ["SMALL_GROUP", "IS_GNOME"],
|
"flags": ["SMALL_GROUP", "IS_GNOME"],
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"speed": 6,
|
"speed": 6,
|
||||||
|
|
@ -201,18 +201,17 @@
|
||||||
{
|
{
|
||||||
"id": "zombie_gnome",
|
"id": "zombie_gnome",
|
||||||
"name": "gnome zombie",
|
"name": "gnome zombie",
|
||||||
"renderable": { "glyph": "z", "fg": "#AA5500", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "z", "sprite": "gnome", "fg": "#AA5500", "order": 3 },
|
||||||
"flags": ["MINDLESS"],
|
"flags": ["MINDLESS"],
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"speed": 6,
|
"speed": 6,
|
||||||
"vision_range": 12,
|
|
||||||
"attacks": [{ "name": "claws", "hit_bonus": 0, "damage": "1d4" }],
|
"attacks": [{ "name": "claws", "hit_bonus": 0, "damage": "1d4" }],
|
||||||
"loot": { "table": "wands", "chance": 0.05 }
|
"loot": { "table": "wands", "chance": 0.05 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "goblin",
|
"id": "goblin",
|
||||||
"name": "goblin",
|
"name": "goblin",
|
||||||
"renderable": { "glyph": "g", "fg": "#00FF00", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "g", "sprite": "goblin", "fg": "#00FF00", "order": 3 },
|
||||||
"flags": [],
|
"flags": [],
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"speed": 9,
|
"speed": 9,
|
||||||
|
|
@ -221,7 +220,7 @@
|
||||||
{
|
{
|
||||||
"id": "kobold",
|
"id": "kobold",
|
||||||
"name": "kobold",
|
"name": "kobold",
|
||||||
"renderable": { "glyph": "k", "fg": "#AA5500", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "k", "sprite": "kobold", "fg": "#AA5500", "order": 3 },
|
||||||
"flags": [],
|
"flags": [],
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"speed": 6,
|
"speed": 6,
|
||||||
|
|
@ -231,41 +230,38 @@
|
||||||
{
|
{
|
||||||
"id": "zombie_kobold",
|
"id": "zombie_kobold",
|
||||||
"name": "kobold zombie",
|
"name": "kobold zombie",
|
||||||
"renderable": { "glyph": "z", "fg": "#AA5500", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "z", "sprite": "kobold", "fg": "#AA5500", "order": 3 },
|
||||||
"flags": ["MINDLESS"],
|
"flags": ["MINDLESS"],
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"speed": 6,
|
"speed": 6,
|
||||||
"vision_range": 12,
|
|
||||||
"attacks": [{ "name": "claws", "hit_bonus": 0, "damage": "1d4" }],
|
"attacks": [{ "name": "claws", "hit_bonus": 0, "damage": "1d4" }],
|
||||||
"loot": { "table": "scrolls", "chance": 0.05 }
|
"loot": { "table": "scrolls", "chance": 0.05 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "kobold_large",
|
"id": "kobold_large",
|
||||||
"name": "large kobold",
|
"name": "large kobold",
|
||||||
"renderable": { "glyph": "k", "fg": "#70461b", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "k", "sprite": "kobold_large", "fg": "#70461b", "order": 3 },
|
||||||
"flags": [],
|
"flags": [],
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"speed": 6,
|
"speed": 6,
|
||||||
"vision_range": 12,
|
|
||||||
"attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "1d6" }],
|
"attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "1d6" }],
|
||||||
"loot": { "table": "food", "chance": 0.05 }
|
"loot": { "table": "food", "chance": 0.05 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "zombie_orc",
|
"id": "zombie_orc",
|
||||||
"name": "orc zombie",
|
"name": "orc zombie",
|
||||||
"renderable": { "glyph": "z", "fg": "#dbd830", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "z", "sprite": "orc", "fg": "#dbd830", "order": 3 },
|
||||||
"flags": ["MINDLESS"],
|
"flags": ["MINDLESS"],
|
||||||
"level": 2,
|
"level": 2,
|
||||||
"bac": 9,
|
"bac": 9,
|
||||||
"speed": 6,
|
"speed": 6,
|
||||||
"vision_range": 12,
|
|
||||||
"attacks": [{ "name": "claws", "hit_bonus": 0, "damage": "1d6" }],
|
"attacks": [{ "name": "claws", "hit_bonus": 0, "damage": "1d6" }],
|
||||||
"loot": { "table": "potions", "chance": 0.05 }
|
"loot": { "table": "potions", "chance": 0.05 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "dwarf",
|
"id": "dwarf",
|
||||||
"name": "dwarf",
|
"name": "dwarf",
|
||||||
"renderable": { "glyph": "h", "fg": "#d61b1b", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "h", "sprite": "dwarf", "fg": "#d61b1b", "order": 3 },
|
||||||
"flags": ["IS_DWARF"],
|
"flags": ["IS_DWARF"],
|
||||||
"level": 2,
|
"level": 2,
|
||||||
"bac": 10,
|
"bac": 10,
|
||||||
|
|
@ -277,30 +273,28 @@
|
||||||
{
|
{
|
||||||
"id": "zombie_dwarf",
|
"id": "zombie_dwarf",
|
||||||
"name": "dwarf zombie",
|
"name": "dwarf zombie",
|
||||||
"renderable": { "glyph": "z", "fg": "#d61b1b", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "z", "sprite": "dwarf", "fg": "#d61b1b", "order": 3 },
|
||||||
"flags": ["MINDLESS"],
|
"flags": ["MINDLESS"],
|
||||||
"level": 2,
|
"level": 2,
|
||||||
"bac": 9,
|
"bac": 9,
|
||||||
"speed": 6,
|
"speed": 6,
|
||||||
"vision_range": 12,
|
|
||||||
"attacks": [{ "name": "claws", "hit_bonus": 0, "damage": "1d6" }],
|
"attacks": [{ "name": "claws", "hit_bonus": 0, "damage": "1d6" }],
|
||||||
"loot": { "table": "equipment", "chance": 0.05 }
|
"loot": { "table": "equipment", "chance": 0.05 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "kobold_captain",
|
"id": "kobold_captain",
|
||||||
"name": "kobold captain",
|
"name": "kobold captain",
|
||||||
"renderable": { "glyph": "k", "fg": "#9331ac", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "k", "sprite": "kobold_c", "fg": "#9331ac", "order": 3 },
|
||||||
"flags": [],
|
"flags": [],
|
||||||
"level": 2,
|
"level": 2,
|
||||||
"speed": 6,
|
"speed": 6,
|
||||||
"vision_range": 12,
|
|
||||||
"attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "2d4" }],
|
"attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "2d4" }],
|
||||||
"loot": { "table": "food", "chance": 0.05 }
|
"loot": { "table": "food", "chance": 0.05 }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "spider_cave",
|
"id": "spider_cave",
|
||||||
"name": "cave spider",
|
"name": "cave spider",
|
||||||
"renderable": { "glyph": "s", "fg": "#6b6b6b", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "s", "sprite": "spider", "fg": "#6b6b6b", "order": 3 },
|
||||||
"flags": ["SMALL_GROUP"],
|
"flags": ["SMALL_GROUP"],
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"bac": 3,
|
"bac": 3,
|
||||||
|
|
@ -311,7 +305,7 @@
|
||||||
{
|
{
|
||||||
"id": "ant_worker",
|
"id": "ant_worker",
|
||||||
"name": "worker ant",
|
"name": "worker ant",
|
||||||
"renderable": { "glyph": "a", "fg": "#ca7631", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "a", "sprite": "ant", "fg": "#ca7631", "order": 3 },
|
||||||
"flags": ["SMALL_GROUP"],
|
"flags": ["SMALL_GROUP"],
|
||||||
"level": 2,
|
"level": 2,
|
||||||
"bac": 3,
|
"bac": 3,
|
||||||
|
|
@ -322,7 +316,7 @@
|
||||||
{
|
{
|
||||||
"id": "ant_soldier",
|
"id": "ant_soldier",
|
||||||
"name": "soldier ant",
|
"name": "soldier ant",
|
||||||
"renderable": { "glyph": "a", "fg": "#ca3f26", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "a", "sprite": "ant", "fg": "#ca3f26", "order": 3 },
|
||||||
"flags": ["SMALL_GROUP", "POISON_RES"],
|
"flags": ["SMALL_GROUP", "POISON_RES"],
|
||||||
"level": 3,
|
"level": 3,
|
||||||
"bac": 3,
|
"bac": 3,
|
||||||
|
|
@ -336,7 +330,7 @@
|
||||||
{
|
{
|
||||||
"id": "caterpillar_cave",
|
"id": "caterpillar_cave",
|
||||||
"name": "caterpillar",
|
"name": "caterpillar",
|
||||||
"renderable": { "glyph": "a", "fg": "#6b6b6b", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "a", "sprite": "caterpillar", "fg": "#6b6b6b", "order": 3 },
|
||||||
"flags": ["SMALL_GROUP"],
|
"flags": ["SMALL_GROUP"],
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"bac": 3,
|
"bac": 3,
|
||||||
|
|
@ -347,7 +341,7 @@
|
||||||
{
|
{
|
||||||
"id": "caterpillar_giant",
|
"id": "caterpillar_giant",
|
||||||
"name": "giant caterpillar",
|
"name": "giant caterpillar",
|
||||||
"renderable": { "glyph": "a", "fg": "#b9aeae", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "a", "sprite": "caterpillar", "fg": "#b9aeae", "order": 3 },
|
||||||
"flags": ["SMALL_GROUP"],
|
"flags": ["SMALL_GROUP"],
|
||||||
"level": 2,
|
"level": 2,
|
||||||
"bac": 7,
|
"bac": 7,
|
||||||
|
|
@ -358,7 +352,7 @@
|
||||||
{
|
{
|
||||||
"id": "jackal",
|
"id": "jackal",
|
||||||
"name": "jackal",
|
"name": "jackal",
|
||||||
"renderable": { "glyph": "d", "fg": "#AA5500", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "d", "sprite": "dog", "fg": "#AA5500", "order": 3 },
|
||||||
"flags": ["CARNIVORE", "SMALL_GROUP"],
|
"flags": ["CARNIVORE", "SMALL_GROUP"],
|
||||||
"bac": 7,
|
"bac": 7,
|
||||||
"attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d2" }]
|
"attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d2" }]
|
||||||
|
|
@ -366,7 +360,7 @@
|
||||||
{
|
{
|
||||||
"id": "fox",
|
"id": "fox",
|
||||||
"name": "fox",
|
"name": "fox",
|
||||||
"renderable": { "glyph": "d", "fg": "#FF0000", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "d", "sprite": "dog", "fg": "#FF0000", "order": 3 },
|
||||||
"flags": ["CARNIVORE"],
|
"flags": ["CARNIVORE"],
|
||||||
"bac": 7,
|
"bac": 7,
|
||||||
"attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d3" }]
|
"attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d3" }]
|
||||||
|
|
@ -374,7 +368,7 @@
|
||||||
{
|
{
|
||||||
"id": "coyote",
|
"id": "coyote",
|
||||||
"name": "coyote",
|
"name": "coyote",
|
||||||
"renderable": { "glyph": "d", "fg": "#6E3215", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "d", "sprite": "dog", "fg": "#6E3215", "order": 3 },
|
||||||
"flags": ["CARNIVORE", "SMALL_GROUP"],
|
"flags": ["CARNIVORE", "SMALL_GROUP"],
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"bac": 7,
|
"bac": 7,
|
||||||
|
|
@ -383,7 +377,7 @@
|
||||||
{
|
{
|
||||||
"id": "wolf",
|
"id": "wolf",
|
||||||
"name": "wolf",
|
"name": "wolf",
|
||||||
"renderable": { "glyph": "d", "fg": "#5E4225", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "d", "sprite": "dog", "fg": "#5E4225", "order": 3 },
|
||||||
"flags": ["CARNIVORE"],
|
"flags": ["CARNIVORE"],
|
||||||
"level": 5,
|
"level": 5,
|
||||||
"bac": 4,
|
"bac": 4,
|
||||||
|
|
@ -392,7 +386,7 @@
|
||||||
{
|
{
|
||||||
"id": "goblin_chieftain",
|
"id": "goblin_chieftain",
|
||||||
"name": "goblin chieftain",
|
"name": "goblin chieftain",
|
||||||
"renderable": { "glyph": "g", "fg": "#9331ac", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "g", "sprite": "goblin_c", "fg": "#9331ac", "order": 3 },
|
||||||
"flags": [],
|
"flags": [],
|
||||||
"level": 2,
|
"level": 2,
|
||||||
"speed": 9,
|
"speed": 9,
|
||||||
|
|
@ -402,7 +396,7 @@
|
||||||
{
|
{
|
||||||
"id": "orc",
|
"id": "orc",
|
||||||
"name": "orc",
|
"name": "orc",
|
||||||
"renderable": { "glyph": "o", "fg": "#00FF00", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "o", "sprite": "orc", "fg": "#00FF00", "order": 3 },
|
||||||
"flags": ["SMALL_GROUP"],
|
"flags": ["SMALL_GROUP"],
|
||||||
"level": 1,
|
"level": 1,
|
||||||
"speed": 9,
|
"speed": 9,
|
||||||
|
|
@ -412,7 +406,7 @@
|
||||||
{
|
{
|
||||||
"id": "orc_hill",
|
"id": "orc_hill",
|
||||||
"name": "hill orc",
|
"name": "hill orc",
|
||||||
"renderable": { "glyph": "o", "fg": "#dbd830", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "o", "sprite": "orc", "fg": "#dbd830", "order": 3 },
|
||||||
"flags": ["LARGE_GROUP"],
|
"flags": ["LARGE_GROUP"],
|
||||||
"level": 2,
|
"level": 2,
|
||||||
"speed": 9,
|
"speed": 9,
|
||||||
|
|
@ -422,7 +416,7 @@
|
||||||
{
|
{
|
||||||
"id": "orc_captain",
|
"id": "orc_captain",
|
||||||
"name": "orc captain",
|
"name": "orc captain",
|
||||||
"renderable": { "glyph": "o", "fg": "#9331ac", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "o", "sprite": "orc", "fg": "#9331ac", "order": 3 },
|
||||||
"flags": ["MULTIATTACK"],
|
"flags": ["MULTIATTACK"],
|
||||||
"level": 5,
|
"level": 5,
|
||||||
"speed": 5,
|
"speed": 5,
|
||||||
|
|
@ -435,7 +429,7 @@
|
||||||
{
|
{
|
||||||
"id": "warg",
|
"id": "warg",
|
||||||
"name": "warg",
|
"name": "warg",
|
||||||
"renderable": { "glyph": "d", "fg": "#8b7164", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "d", "sprite": "dog", "fg": "#8b7164", "order": 3 },
|
||||||
"flags": ["SMALL_GROUP"],
|
"flags": ["SMALL_GROUP"],
|
||||||
"level": 7,
|
"level": 7,
|
||||||
"bac": 4,
|
"bac": 4,
|
||||||
|
|
@ -446,7 +440,7 @@
|
||||||
{
|
{
|
||||||
"id": "jaguar",
|
"id": "jaguar",
|
||||||
"name": "jaguar",
|
"name": "jaguar",
|
||||||
"renderable": { "glyph": "f", "fg": "#d3b947", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "f", "sprite": "cat_large", "fg": "#d3b947", "order": 3 },
|
||||||
"flags": ["MULTIATTACK"],
|
"flags": ["MULTIATTACK"],
|
||||||
"level": 4,
|
"level": 4,
|
||||||
"bac": 6,
|
"bac": 6,
|
||||||
|
|
@ -461,7 +455,7 @@
|
||||||
{
|
{
|
||||||
"id": "lynx",
|
"id": "lynx",
|
||||||
"name": "lynx",
|
"name": "lynx",
|
||||||
"renderable": { "glyph": "f", "fg": "#b5d347", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "f", "sprite": "cat_large", "fg": "#b5d347", "order": 3 },
|
||||||
"flags": ["MULTIATTACK"],
|
"flags": ["MULTIATTACK"],
|
||||||
"level": 5,
|
"level": 5,
|
||||||
"bac": 6,
|
"bac": 6,
|
||||||
|
|
@ -476,7 +470,7 @@
|
||||||
{
|
{
|
||||||
"id": "panther",
|
"id": "panther",
|
||||||
"name": "panther",
|
"name": "panther",
|
||||||
"renderable": { "glyph": "f", "fg": "#58554e", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "f", "sprite": "cat_large", "fg": "#58554e", "order": 3 },
|
||||||
"flags": ["MULTIATTACK"],
|
"flags": ["MULTIATTACK"],
|
||||||
"level": 5,
|
"level": 5,
|
||||||
"bac": 6,
|
"bac": 6,
|
||||||
|
|
@ -491,7 +485,7 @@
|
||||||
{
|
{
|
||||||
"id": "ogre",
|
"id": "ogre",
|
||||||
"name": "ogre",
|
"name": "ogre",
|
||||||
"renderable": { "glyph": "O", "fg": "#10A70d", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "O", "sprite": "ogre", "fg": "#10A70d", "order": 3 },
|
||||||
"flags": ["SMALL_GROUP"],
|
"flags": ["SMALL_GROUP"],
|
||||||
"level": 5,
|
"level": 5,
|
||||||
"bac": 5,
|
"bac": 5,
|
||||||
|
|
@ -502,7 +496,7 @@
|
||||||
{
|
{
|
||||||
"id": "treant_small",
|
"id": "treant_small",
|
||||||
"name": "treant sapling",
|
"name": "treant sapling",
|
||||||
"renderable": { "glyph": "♠️", "fg": "#10570d", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "♠️", "sprite": "gnome", "fg": "#10570d", "order": 3 },
|
||||||
"flags": ["LARGE_GROUP", "GREEN_BLOOD", "FIRE_WEAK"],
|
"flags": ["LARGE_GROUP", "GREEN_BLOOD", "FIRE_WEAK"],
|
||||||
"level": 2,
|
"level": 2,
|
||||||
"bac": 12,
|
"bac": 12,
|
||||||
|
|
|
||||||
|
|
@ -2,83 +2,89 @@
|
||||||
{
|
{
|
||||||
"id": "door",
|
"id": "door",
|
||||||
"name": "door",
|
"name": "door",
|
||||||
"renderable": { "glyph": "+", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "+", "sprite": "door", "alt": "door_open", "fg": "#00FFFF", "order": 5 },
|
||||||
"flags": ["DOOR"]
|
"door": { "open": false, "locked": false, "blocks_vis": true, "blocks_move": true }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "trapdoor",
|
||||||
|
"name": "trapdoor",
|
||||||
|
"renderable": { "glyph": "+", "sprite": "trapdoor", "alt": "trapdoor_open", "fg": "#00FFFF", "order": 5, "alt_order": 1 },
|
||||||
|
"door": { "open": false, "locked": false, "blocks_vis": false, "blocks_move": false }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "prop_altar",
|
"id": "prop_altar",
|
||||||
"name": "altar",
|
"name": "altar",
|
||||||
"renderable": { "glyph": "_", "fg": "#FFFFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "_", "sprite": "altar", "fg": "#FFFFFF", "order": 5 },
|
||||||
"flags": ["ENTRY_TRIGGER"],
|
"flags": ["ENTRY_TRIGGER"],
|
||||||
"effects": { "heal": "8d8" }
|
"effects": { "heal": "8d8" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "prop_keg",
|
"id": "prop_keg",
|
||||||
"name": "keg",
|
"name": "keg",
|
||||||
"renderable": { "glyph": "φ", "fg": "#AAAAAA", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "φ", "sprite": "gnome", "fg": "#AAAAAA", "order": 5 },
|
||||||
"flags": []
|
"flags": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "prop_table",
|
"id": "prop_table",
|
||||||
"name": "table",
|
"name": "table",
|
||||||
"renderable": { "glyph": "-", "fg": "#AAAAAA", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "-", "sprite": "table", "fg": "#a76d3d", "order": 5 },
|
||||||
"flags": []
|
"flags": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "prop_hay",
|
"id": "prop_hay",
|
||||||
"name": "hay",
|
"name": "hay",
|
||||||
"renderable": { "glyph": "%", "fg": "#c7ad39", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "%", "sprite": "plants", "fg": "#e2b82f", "order": 5 },
|
||||||
"flags": []
|
"flags": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "prop_statue",
|
"id": "prop_statue",
|
||||||
"name": "statue",
|
"name": "statue",
|
||||||
"renderable": { "glyph": "@", "fg": "#ffffff", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "@", "sprite": "altar", "fg": "#ffffff", "order": 5 },
|
||||||
"flags": []
|
"flags": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "prop_bed",
|
"id": "prop_bed",
|
||||||
"name": "bed",
|
"name": "bed",
|
||||||
"renderable": { "glyph": "=", "fg": "#AAAAAA", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "=", "sprite": "bed", "fg": "#a55d33", "order": 5 },
|
||||||
"flags": []
|
"flags": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "prop_chair",
|
"id": "prop_chair",
|
||||||
"name": "chair",
|
"name": "chair",
|
||||||
"renderable": { "glyph": "└", "fg": "#AAAAAA", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "└", "sprite": "chair_r", "fg": "#a76d3d", "order": 5 },
|
||||||
"flags": []
|
"flags": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "prop_candle",
|
"id": "prop_candle",
|
||||||
"name": "candle",
|
"name": "candle",
|
||||||
"renderable": { "glyph": "Ä", "fg": "#FFA500", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "Ä", "sprite": "candelabra", "fg": "#FFA500", "order": 4 },
|
||||||
"flags": []
|
"flags": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "trap_bear",
|
"id": "trap_bear",
|
||||||
"name": "bear trap",
|
"name": "bear trap",
|
||||||
"renderable": { "glyph": "^", "fg": "#e6e6e6", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "^", "sprite": "beartrap", "fg": "#e6e6e6", "order": 5 },
|
||||||
"flags": ["HIDDEN", "ENTRY_TRIGGER", "SINGLE_ACTIVATION"],
|
"flags": ["HIDDEN", "ENTRY_TRIGGER", "SINGLE_ACTIVATION"],
|
||||||
"effects": { "damage": "2d4" }
|
"effects": { "damage": "2d4" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "trap_mini_mine",
|
"id": "trap_mini_mine",
|
||||||
"name": "mini-mine",
|
"name": "mini-mine",
|
||||||
"renderable": { "glyph": "^", "fg": "#ff1e00", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "^", "sprite": "minimine", "fg": "#ff1e00", "order": 5 },
|
||||||
"flags": ["ENTRY_TRIGGER", "SINGLE_ACTIVATION"],
|
"flags": ["ENTRY_TRIGGER", "SINGLE_ACTIVATION"],
|
||||||
"effects": { "damage": "2d4", "aoe": "3" }
|
"effects": { "damage": "2d4", "aoe": "3" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "trap_stonefall",
|
"id": "trap_stonefall",
|
||||||
"name": "stonefall trap",
|
"name": "stonefall trap",
|
||||||
"renderable": { "glyph": "^", "fg": "#beb5a7", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "^", "sprite": "stones", "fg": "#beb5a7", "order": 5 },
|
||||||
"flags": ["HIDDEN", "ENTRY_TRIGGER", "SINGLE_ACTIVATION"],
|
"flags": ["HIDDEN", "ENTRY_TRIGGER", "SINGLE_ACTIVATION"],
|
||||||
"effects": { "damage": "2d10" }
|
"effects": { "damage": "2d10" }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "trap_confusion",
|
"id": "trap_confusion",
|
||||||
"name": "magic trap",
|
"name": "magic trap",
|
||||||
"renderable": { "glyph": "^", "fg": "#df07df", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "^", "sprite": "magic_trap", "fg": "#df07df", "order": 5 },
|
||||||
"flags": ["HIDDEN", "ENTRY_TRIGGER", "SINGLE_ACTIVATION"],
|
"flags": ["HIDDEN", "ENTRY_TRIGGER", "SINGLE_ACTIVATION"],
|
||||||
"effects": { "confusion": "3" }
|
"effects": { "confusion": "3" }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2556
resources/atlas.json
Normal file
2556
resources/atlas.json
Normal file
File diff suppressed because it is too large
Load diff
BIN
resources/atlas.png
Normal file
BIN
resources/atlas.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
BIN
resources/fonts/Greybeard-16px-Bold.ttf
Normal file
BIN
resources/fonts/Greybeard-16px-Bold.ttf
Normal file
Binary file not shown.
BIN
resources/fonts/Greybeard-16px-Italic.ttf
Normal file
BIN
resources/fonts/Greybeard-16px-Italic.ttf
Normal file
Binary file not shown.
BIN
resources/fonts/Greybeard-16px.ttf
Normal file
BIN
resources/fonts/Greybeard-16px.ttf
Normal file
Binary file not shown.
BIN
resources/fonts/Swanston-Bold.ttf
Normal file
BIN
resources/fonts/Swanston-Bold.ttf
Normal file
Binary file not shown.
BIN
resources/fonts/Swanston.ttf
Normal file
BIN
resources/fonts/Swanston.ttf
Normal file
Binary file not shown.
BIN
resources/icon.png
Normal file
BIN
resources/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 350 B |
BIN
resources/sounds/amb/relaxed.ogg
Normal file
BIN
resources/sounds/amb/relaxed.ogg
Normal file
Binary file not shown.
BIN
resources/sounds/door/blocked1.wav
Normal file
BIN
resources/sounds/door/blocked1.wav
Normal file
Binary file not shown.
BIN
resources/sounds/door/blocked2.wav
Normal file
BIN
resources/sounds/door/blocked2.wav
Normal file
Binary file not shown.
BIN
resources/sounds/door/blocked3.wav
Normal file
BIN
resources/sounds/door/blocked3.wav
Normal file
Binary file not shown.
BIN
resources/sounds/door/close1.wav
Normal file
BIN
resources/sounds/door/close1.wav
Normal file
Binary file not shown.
BIN
resources/sounds/door/close2.wav
Normal file
BIN
resources/sounds/door/close2.wav
Normal file
Binary file not shown.
BIN
resources/sounds/door/close3.wav
Normal file
BIN
resources/sounds/door/close3.wav
Normal file
Binary file not shown.
BIN
resources/sounds/door/open1.wav
Normal file
BIN
resources/sounds/door/open1.wav
Normal file
Binary file not shown.
BIN
resources/sounds/door/open2.wav
Normal file
BIN
resources/sounds/door/open2.wav
Normal file
Binary file not shown.
BIN
resources/sounds/door/open3.wav
Normal file
BIN
resources/sounds/door/open3.wav
Normal file
Binary file not shown.
BIN
resources/sounds/hit.wav
Normal file
BIN
resources/sounds/hit.wav
Normal file
Binary file not shown.
BIN
resources/sounds/hit/whoosh1.ogg
Normal file
BIN
resources/sounds/hit/whoosh1.ogg
Normal file
Binary file not shown.
BIN
resources/sounds/hit/whoosh2.ogg
Normal file
BIN
resources/sounds/hit/whoosh2.ogg
Normal file
Binary file not shown.
BIN
resources/sounds/hit/whoosh3.ogg
Normal file
BIN
resources/sounds/hit/whoosh3.ogg
Normal file
Binary file not shown.
BIN
resources/sounds/hit/whoosh4.ogg
Normal file
BIN
resources/sounds/hit/whoosh4.ogg
Normal file
Binary file not shown.
7482
resources/td.json
Normal file
7482
resources/td.json
Normal file
File diff suppressed because it is too large
Load diff
BIN
resources/td.png
Normal file
BIN
resources/td.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
|
|
@ -45,6 +45,7 @@ impl<'a> System<'a> for ApproachAI {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let mut path: Option<NavigationPath> = None;
|
let mut path: Option<NavigationPath> = None;
|
||||||
|
let mut curr_abs_diff = 100;
|
||||||
let idx = map.xy_idx(pos.x, pos.y);
|
let idx = map.xy_idx(pos.x, pos.y);
|
||||||
for tar_idx in target_idxs {
|
for tar_idx in target_idxs {
|
||||||
let potential_path = a_star_search(idx, tar_idx, &mut *map);
|
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()
|
potential_path.steps.len() < path.as_ref().unwrap().steps.len()
|
||||||
{
|
{
|
||||||
path = Some(potential_path);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{ gamelog, Attributes, Burden, EquipmentChanged, Equipped, InBackpack, Item, Pools };
|
use crate::{ gamelog, Attributes, Burden, EquipmentChanged, Equipped, InBackpack, Item, Pools };
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::data::entity::CARRY_CAPACITY_PER_STRENGTH;
|
use crate::consts::entity::CARRY_CAPACITY_PER_STRENGTH;
|
||||||
|
|
||||||
pub struct EncumbranceSystem {}
|
pub struct EncumbranceSystem {}
|
||||||
|
|
||||||
|
|
@ -20,7 +20,17 @@ impl<'a> System<'a> for EncumbranceSystem {
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
let (mut equip_dirty, entities, items, backpacks, wielded, mut pools, attributes, player, mut burdened) = data;
|
let (
|
||||||
|
mut equip_dirty,
|
||||||
|
entities,
|
||||||
|
items,
|
||||||
|
backpacks,
|
||||||
|
wielded,
|
||||||
|
mut pools,
|
||||||
|
attributes,
|
||||||
|
player,
|
||||||
|
mut burdened,
|
||||||
|
) = data;
|
||||||
if equip_dirty.is_empty() {
|
if equip_dirty.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -50,7 +60,7 @@ impl<'a> System<'a> for EncumbranceSystem {
|
||||||
pool.weight = *weight;
|
pool.weight = *weight;
|
||||||
if let Some(attr) = attributes.get(*entity) {
|
if let Some(attr) = attributes.get(*entity) {
|
||||||
let carry_capacity_lbs =
|
let carry_capacity_lbs =
|
||||||
(attr.strength.base + attr.strength.modifiers) * CARRY_CAPACITY_PER_STRENGTH;
|
(attr.strength.base + attr.strength.bonuses) * CARRY_CAPACITY_PER_STRENGTH;
|
||||||
if (pool.weight as i32) > 3 * carry_capacity_lbs {
|
if (pool.weight as i32) > 3 * carry_capacity_lbs {
|
||||||
// Overloaded
|
// Overloaded
|
||||||
burdened
|
burdened
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::data::entity::*;
|
use crate::consts::entity::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
Burden,
|
Burden,
|
||||||
BurdenLevel,
|
BurdenLevel,
|
||||||
|
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use crate::config::CONFIG;
|
use crate::config::CONFIG;
|
||||||
use crate::data::events::*;
|
use crate::consts::events::*;
|
||||||
|
|
||||||
pub struct EnergySystem {}
|
pub struct EnergySystem {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use crate::{
|
||||||
Intrinsics,
|
Intrinsics,
|
||||||
};
|
};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use crate::data::events::*;
|
use crate::consts::events::*;
|
||||||
|
|
||||||
pub struct RegenSystem {}
|
pub struct RegenSystem {}
|
||||||
|
|
||||||
|
|
@ -121,7 +121,8 @@ fn try_hp_regen_tick(pool: &mut Pools, amount: i32) {
|
||||||
|
|
||||||
fn get_mana_regen_per_tick(e: Entity, attributes: &ReadStorage<Attributes>) -> i32 {
|
fn get_mana_regen_per_tick(e: Entity, attributes: &ReadStorage<Attributes>) -> i32 {
|
||||||
let regen = if let Some(attributes) = attributes.get(e) {
|
let regen = if let Some(attributes) = attributes.get(e) {
|
||||||
(attributes.intelligence.bonus + attributes.wisdom.bonus) / 2 + MIN_MP_REGEN_PER_TURN
|
(attributes.intelligence.modifier() + attributes.wisdom.modifier()) / 2 +
|
||||||
|
MIN_MP_REGEN_PER_TURN
|
||||||
} else {
|
} else {
|
||||||
MIN_MP_REGEN_PER_TURN
|
MIN_MP_REGEN_PER_TURN
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use crate::data::events::*;
|
use crate::consts::events::*;
|
||||||
|
|
||||||
pub struct TurnStatusSystem {}
|
pub struct TurnStatusSystem {}
|
||||||
|
|
||||||
|
|
@ -83,8 +83,8 @@ impl<'a> System<'a> for TurnStatusSystem {
|
||||||
None,
|
None,
|
||||||
EffectType::Particle {
|
EffectType::Particle {
|
||||||
glyph: to_cp437('!'),
|
glyph: to_cp437('!'),
|
||||||
|
sprite: "gnome".to_string(), // FIXME: REMOVE THE GNOMES
|
||||||
fg: RGB::named(LIGHT_BLUE),
|
fg: RGB::named(LIGHT_BLUE),
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
lifespan: 200.0,
|
lifespan: 200.0,
|
||||||
delay: 0.0,
|
delay: 0.0,
|
||||||
},
|
},
|
||||||
|
|
@ -113,8 +113,8 @@ impl<'a> System<'a> for TurnStatusSystem {
|
||||||
None,
|
None,
|
||||||
EffectType::Particle {
|
EffectType::Particle {
|
||||||
glyph: to_cp437('?'),
|
glyph: to_cp437('?'),
|
||||||
|
sprite: "gnome".to_string(), // FIXME: REMOVE THE GNOMES
|
||||||
fg: RGB::named(MAGENTA),
|
fg: RGB::named(MAGENTA),
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
lifespan: 200.0,
|
lifespan: 200.0,
|
||||||
delay: 0.0,
|
delay: 0.0,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
166
src/camera.rs
166
src/camera.rs
|
|
@ -1,14 +1,66 @@
|
||||||
use super::{ Hidden, Map, Mind, Position, Prop, Renderable };
|
use super::{ Hidden, Map, Mind, Position, Prop, Renderable, Pools };
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use std::ops::Mul;
|
use std::ops::Mul;
|
||||||
|
use super::consts::visuals::{ TILES_IN_VIEWPORT_H, TILES_IN_VIEWPORT_W };
|
||||||
|
use super::consts::prelude::*;
|
||||||
|
|
||||||
const SHOW_BOUNDARIES: bool = false;
|
const SHOW_BOUNDARIES: bool = false;
|
||||||
|
|
||||||
pub fn get_screen_bounds(ecs: &World, _ctx: &mut BTerm) -> (i32, i32, i32, i32, i32, i32) {
|
pub struct Offsets {
|
||||||
let player_pos = ecs.fetch::<Point>();
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_offset() -> Offsets {
|
||||||
|
return Offsets { x: 1, y: 8 };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ScreenBounds {
|
||||||
|
pub min_x: i32,
|
||||||
|
pub max_x: i32,
|
||||||
|
pub min_y: i32,
|
||||||
|
pub max_y: i32,
|
||||||
|
pub x_offset: i32,
|
||||||
|
pub y_offset: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ScreenBoundsf32 {
|
||||||
|
pub min_x: f32,
|
||||||
|
pub max_x: f32,
|
||||||
|
pub min_y: f32,
|
||||||
|
pub max_y: f32,
|
||||||
|
pub x_offset: f32,
|
||||||
|
pub y_offset: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScreenBounds {
|
||||||
|
pub fn to_px(&self) -> ScreenBoundsf32 {
|
||||||
|
ScreenBoundsf32 {
|
||||||
|
min_x: (self.min_x as f32) * TILESIZE.sprite_x,
|
||||||
|
max_x: (self.max_x as f32) * TILESIZE.sprite_x,
|
||||||
|
min_y: (self.min_y as f32) * TILESIZE.sprite_y,
|
||||||
|
max_y: (self.max_y as f32) * TILESIZE.sprite_y,
|
||||||
|
x_offset: (self.x_offset as f32) * TILESIZE.x,
|
||||||
|
y_offset: (self.y_offset as f32) * TILESIZE.x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_screen_bounds(ecs: &World, debug: bool) -> ScreenBounds {
|
||||||
let map = ecs.fetch::<Map>();
|
let map = ecs.fetch::<Map>();
|
||||||
let (x_chars, y_chars, mut x_offset, mut y_offset) = (69, 41, 1, 10);
|
let player_pos = if !debug {
|
||||||
|
*ecs.fetch::<Point>()
|
||||||
|
} else {
|
||||||
|
Point::new(map.width / 2, map.height / 2)
|
||||||
|
};
|
||||||
|
|
||||||
|
let (x_chars, y_chars, mut x_offset, mut y_offset) = (
|
||||||
|
TILES_IN_VIEWPORT_W,
|
||||||
|
TILES_IN_VIEWPORT_H,
|
||||||
|
1,
|
||||||
|
8,
|
||||||
|
);
|
||||||
|
|
||||||
let centre_x = (x_chars / 2) as i32;
|
let centre_x = (x_chars / 2) as i32;
|
||||||
let centre_y = (y_chars / 2) as i32;
|
let centre_y = (y_chars / 2) as i32;
|
||||||
|
|
@ -28,33 +80,60 @@ pub fn get_screen_bounds(ecs: &World, _ctx: &mut BTerm) -> (i32, i32, i32, i32,
|
||||||
let max_x = min_x + (x_chars as i32);
|
let max_x = min_x + (x_chars as i32);
|
||||||
let max_y = min_y + (y_chars as i32);
|
let max_y = min_y + (y_chars as i32);
|
||||||
|
|
||||||
(min_x, max_x, min_y, max_y, x_offset, y_offset)
|
ScreenBounds { min_x, max_x, min_y, max_y, x_offset, y_offset }
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::consts::TILESIZE;
|
||||||
|
|
||||||
|
pub fn in_bounds(x: i32, y: i32, min_x: i32, min_y: i32, upper_x: i32, upper_y: i32) -> bool {
|
||||||
|
x >= min_x && x < upper_x && y >= min_y && y < upper_y
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_camera(ecs: &World, ctx: &mut BTerm) {
|
pub fn render_camera(ecs: &World, ctx: &mut BTerm) {
|
||||||
let map = ecs.fetch::<Map>();
|
let map = ecs.fetch::<Map>();
|
||||||
let (min_x, max_x, min_y, max_y, x_offset, y_offset) = get_screen_bounds(ecs, ctx);
|
let bounds = get_screen_bounds(ecs, false);
|
||||||
|
|
||||||
// Render map
|
// Render map
|
||||||
let mut y = 0;
|
let mut y = 0;
|
||||||
for t_y in min_y..max_y {
|
for t_y in bounds.min_y..bounds.max_y {
|
||||||
let mut x = 0;
|
let mut x = 0;
|
||||||
for t_x in min_x..max_x {
|
for t_x in bounds.min_x..bounds.max_x {
|
||||||
if t_x >= 0 && t_x < map.width && t_y >= 0 && t_y < map.height {
|
if t_x >= 0 && t_x < map.width && t_y >= 0 && t_y < map.height {
|
||||||
let idx = map.xy_idx(t_x, t_y);
|
let idx = map.xy_idx(t_x, t_y);
|
||||||
if map.revealed_tiles[idx] {
|
if map.revealed_tiles[idx] {
|
||||||
|
if 1 == 2 {
|
||||||
let (glyph, fg, bg) = crate::map::themes::get_tile_renderables_for_id(
|
let (glyph, fg, bg) = crate::map::themes::get_tile_renderables_for_id(
|
||||||
idx,
|
idx,
|
||||||
&*map,
|
&*map,
|
||||||
Some(*ecs.fetch::<Point>()),
|
Some(*ecs.fetch::<Point>()),
|
||||||
None
|
None
|
||||||
);
|
);
|
||||||
ctx.set(x + x_offset, y + y_offset, fg, bg, glyph);
|
ctx.set(x + bounds.x_offset, y + bounds.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::<Point>())
|
||||||
|
);
|
||||||
|
ctx.add_sprite(
|
||||||
|
Rect::with_size(
|
||||||
|
x * 16 + bounds.x_offset * 16,
|
||||||
|
y * 16 + bounds.y_offset * 16,
|
||||||
|
16,
|
||||||
|
16
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
RGBA::named(WHITE),
|
||||||
|
0 // Ya
|
||||||
|
);
|
||||||
|
ctx.set_active_console(TILE_LAYER);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if SHOW_BOUNDARIES {
|
} else if SHOW_BOUNDARIES {
|
||||||
ctx.set(
|
ctx.set(
|
||||||
x + x_offset,
|
x + bounds.x_offset,
|
||||||
y + y_offset,
|
y + bounds.y_offset,
|
||||||
RGB::named(DARKSLATEGRAY),
|
RGB::named(DARKSLATEGRAY),
|
||||||
RGB::named(BLACK),
|
RGB::named(BLACK),
|
||||||
to_cp437('#')
|
to_cp437('#')
|
||||||
|
|
@ -67,8 +146,11 @@ pub fn render_camera(ecs: &World, ctx: &mut BTerm) {
|
||||||
|
|
||||||
// Render entities
|
// Render entities
|
||||||
{
|
{
|
||||||
|
ctx.set_active_console(ENTITY_LAYER);
|
||||||
|
|
||||||
let positions = ecs.read_storage::<Position>();
|
let positions = ecs.read_storage::<Position>();
|
||||||
let renderables = ecs.read_storage::<Renderable>();
|
let renderables = ecs.read_storage::<Renderable>();
|
||||||
|
let pools = ecs.read_storage::<Pools>();
|
||||||
let minds = ecs.read_storage::<Mind>();
|
let minds = ecs.read_storage::<Mind>();
|
||||||
let hidden = ecs.read_storage::<Hidden>();
|
let hidden = ecs.read_storage::<Hidden>();
|
||||||
let props = ecs.write_storage::<Prop>();
|
let props = ecs.write_storage::<Prop>();
|
||||||
|
|
@ -79,22 +161,22 @@ pub fn render_camera(ecs: &World, ctx: &mut BTerm) {
|
||||||
data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order));
|
data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order));
|
||||||
for (pos, render, ent, _hidden) in data.iter() {
|
for (pos, render, ent, _hidden) in data.iter() {
|
||||||
let idx = map.xy_idx(pos.x, pos.y);
|
let idx = map.xy_idx(pos.x, pos.y);
|
||||||
let entity_offset_x = pos.x - min_x;
|
let entity_offset_x = pos.x - bounds.min_x;
|
||||||
let entity_offset_y = pos.y - min_y;
|
let entity_offset_y = pos.y - bounds.min_y;
|
||||||
if pos.x < max_x && pos.y < max_y && pos.x >= min_x && pos.y >= min_y {
|
if
|
||||||
|
pos.x < bounds.max_x &&
|
||||||
|
pos.y < bounds.max_y &&
|
||||||
|
pos.x >= bounds.min_x &&
|
||||||
|
pos.y >= bounds.min_y
|
||||||
|
{
|
||||||
let mut draw = false;
|
let mut draw = false;
|
||||||
let mut fg = render.fg;
|
let mut fg = render.fg;
|
||||||
let mut bg = crate::map::themes::get_tile_renderables_for_id(
|
let bg = BLACK;
|
||||||
idx,
|
|
||||||
&*map,
|
|
||||||
Some(*ecs.fetch::<Point>()),
|
|
||||||
None
|
|
||||||
).2;
|
|
||||||
// Draw entities on visible tiles
|
// Draw entities on visible tiles
|
||||||
if map.visible_tiles[idx] {
|
if map.visible_tiles[idx] {
|
||||||
draw = true;
|
draw = true;
|
||||||
} else {
|
} else {
|
||||||
fg = fg.mul(crate::data::visuals::NON_VISIBLE_MULTIPLIER);
|
fg = fg.mul(crate::consts::visuals::NON_VISIBLE_MULTIPLIER);
|
||||||
// We don't darken BG, because get_tile_renderables_for_id handles this.
|
// We don't darken BG, because get_tile_renderables_for_id handles this.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,9 +186,6 @@ pub fn render_camera(ecs: &World, ctx: &mut BTerm) {
|
||||||
let has_mind = minds.get(*ent);
|
let has_mind = minds.get(*ent);
|
||||||
if let Some(_) = has_mind {
|
if let Some(_) = has_mind {
|
||||||
draw = true;
|
draw = true;
|
||||||
if !map.revealed_tiles[idx] {
|
|
||||||
bg = RGB::named(BLACK);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -118,17 +197,52 @@ pub fn render_camera(ecs: &World, ctx: &mut BTerm) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if draw {
|
if draw {
|
||||||
|
/* if let Some(sprite) = render.sprite {
|
||||||
|
ctx.set_active_console(0);
|
||||||
|
ctx.add_sprite(
|
||||||
|
Rect::with_size(
|
||||||
|
entity_offset_x * 16 + bounds.x_offset * 16,
|
||||||
|
entity_offset_y * 16 + bounds.y_offset * 16,
|
||||||
|
16,
|
||||||
|
16
|
||||||
|
),
|
||||||
|
render.render_order,
|
||||||
|
RGBA::named(WHITE),
|
||||||
|
sprite
|
||||||
|
);
|
||||||
|
ctx.set_active_console(ENTITY_LAYER);
|
||||||
|
} else */ {
|
||||||
ctx.set(
|
ctx.set(
|
||||||
entity_offset_x + x_offset,
|
entity_offset_x + bounds.x_offset,
|
||||||
entity_offset_y + y_offset,
|
entity_offset_y + bounds.y_offset,
|
||||||
fg,
|
fg,
|
||||||
bg,
|
bg,
|
||||||
render.glyph
|
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 + bounds.x_offset) * 16 + 2,
|
||||||
|
(entity_offset_y + bounds.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_debug_map(map: &Map, ctx: &mut BTerm) {
|
pub fn render_debug_map(map: &Map, ctx: &mut BTerm) {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ pub struct SerializationHelper {
|
||||||
#[derive(Component, Serialize, Deserialize, Clone)]
|
#[derive(Component, Serialize, Deserialize, Clone)]
|
||||||
pub struct DMSerializationHelper {
|
pub struct DMSerializationHelper {
|
||||||
pub map: super::map::MasterDungeonMap,
|
pub map: super::map::MasterDungeonMap,
|
||||||
pub log: Vec<Vec<crate::gamelog::LogFragment>>,
|
pub log: std::collections::BTreeMap<i32, Vec<crate::gamelog::LogFragment>>,
|
||||||
pub event_counts: HashMap<String, i32>,
|
pub event_counts: HashMap<String, i32>,
|
||||||
pub events: HashMap<u32, Vec<String>>,
|
pub events: HashMap<u32, Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
@ -38,12 +38,91 @@ pub struct OtherLevelPosition {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, ConvertSaveload, Clone)]
|
#[derive(Debug, Component, ConvertSaveload, Clone)]
|
||||||
pub struct Renderable {
|
pub struct Renderable {
|
||||||
pub glyph: FontCharType,
|
pub glyph: FontCharType, // Legacy, and for drawing the morgue map.
|
||||||
|
pub sprite: String,
|
||||||
|
pub sprite_alt: Option<String>,
|
||||||
pub fg: RGB,
|
pub fg: RGB,
|
||||||
pub bg: RGB,
|
pub fg_alt: Option<RGB>,
|
||||||
pub render_order: i32,
|
pub render_order: i32,
|
||||||
|
pub render_order_alt: Option<i32>,
|
||||||
|
pub offset: (f32, f32),
|
||||||
|
pub offset_alt: Option<(f32, f32)>,
|
||||||
|
// 0 = always on top: particle effects
|
||||||
|
// 1 = things that should appear infront of the player: railings, etc.
|
||||||
|
// 2 = the player
|
||||||
|
// 3 = other mobs
|
||||||
|
// 4 = interactable items
|
||||||
|
// 5 = other props: table, etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Renderable {
|
||||||
|
pub fn new(glyph: FontCharType, sprite: String, fg: RGB, render_order: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
glyph,
|
||||||
|
sprite,
|
||||||
|
sprite_alt: None,
|
||||||
|
fg,
|
||||||
|
fg_alt: None,
|
||||||
|
render_order,
|
||||||
|
render_order_alt: None,
|
||||||
|
offset: (0.0, 0.0),
|
||||||
|
offset_alt: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn swap(&mut self) {
|
||||||
|
let sprite = self.swap_sprite();
|
||||||
|
let fg = self.swap_fg();
|
||||||
|
let render_order = self.swap_render_order();
|
||||||
|
let offset = self.swap_offset();
|
||||||
|
let did_something = sprite || fg || render_order || offset;
|
||||||
|
if !did_something {
|
||||||
|
unreachable!(
|
||||||
|
".swap() was called on a Renderable component, but nothing happened. {:?}",
|
||||||
|
self
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn swap_sprite(&mut self) -> bool {
|
||||||
|
if let Some(sprite_alt) = &mut self.sprite_alt {
|
||||||
|
std::mem::swap(&mut self.sprite, sprite_alt);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
pub fn swap_fg(&mut self) -> bool {
|
||||||
|
if let Some(fg_alt) = &mut self.fg_alt {
|
||||||
|
std::mem::swap(&mut self.fg, fg_alt);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
pub fn swap_render_order(&mut self) -> bool {
|
||||||
|
if let Some(render_order_alt) = &mut self.render_order_alt {
|
||||||
|
std::mem::swap(&mut self.render_order, render_order_alt);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
pub fn swap_offset(&mut self) -> bool {
|
||||||
|
if let Some(offset_alt) = &mut self.offset_alt {
|
||||||
|
std::mem::swap(&mut self.offset, offset_alt);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Avatar {
|
||||||
|
pub sprite: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Avatar {
|
||||||
|
pub fn new(sprite: String) -> Self {
|
||||||
|
Self { sprite }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
|
|
@ -131,6 +210,9 @@ pub struct BlocksVisibility {}
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Door {
|
pub struct Door {
|
||||||
pub open: bool,
|
pub open: bool,
|
||||||
|
pub locked: bool,
|
||||||
|
pub blocks_vis: bool,
|
||||||
|
pub blocks_move: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)]
|
||||||
|
|
@ -182,8 +264,36 @@ pub struct Pools {
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Attribute {
|
pub struct Attribute {
|
||||||
pub base: i32,
|
pub base: i32,
|
||||||
pub modifiers: i32,
|
pub bonuses: i32,
|
||||||
pub bonus: i32,
|
pub exercise: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attribute {
|
||||||
|
pub fn new(base: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
base,
|
||||||
|
bonuses: 0,
|
||||||
|
exercise: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Raw attribute score. e.g. 10 base, + 4 from armour: 14 strength.
|
||||||
|
pub fn current(&self) -> i32 {
|
||||||
|
self.base + self.bonuses
|
||||||
|
}
|
||||||
|
// Attribute bonus. e.g. 14 strength = +2, 8 strength = -1
|
||||||
|
pub fn modifier(&self) -> i32 {
|
||||||
|
crate::gamesystem::attr_bonus(self.current())
|
||||||
|
}
|
||||||
|
pub fn improve(&mut self) {
|
||||||
|
if self.exercise < 50 {
|
||||||
|
self.exercise += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn abuse(&mut self) {
|
||||||
|
if self.exercise > -50 {
|
||||||
|
self.exercise -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
|
||||||
|
|
@ -215,7 +325,6 @@ pub struct GrantsSpell {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: GrantsIntrinsic, Intrinsics, etc. ? Done the same way as spells?
|
// TODO: GrantsIntrinsic, Intrinsics, etc. ? Done the same way as spells?
|
||||||
|
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Attributes {
|
pub struct Attributes {
|
||||||
pub strength: Attribute,
|
pub strength: Attribute,
|
||||||
|
|
@ -226,6 +335,93 @@ pub struct Attributes {
|
||||||
pub charisma: Attribute,
|
pub charisma: Attribute,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Attributes {
|
||||||
|
pub const STR: i32 = 0;
|
||||||
|
pub const DEX: i32 = 1;
|
||||||
|
pub const CON: i32 = 2;
|
||||||
|
pub const INT: i32 = 3;
|
||||||
|
pub const WIS: i32 = 4;
|
||||||
|
pub const CHA: i32 = 5;
|
||||||
|
pub fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
strength: Attribute::new(10),
|
||||||
|
dexterity: Attribute::new(10),
|
||||||
|
constitution: Attribute::new(10),
|
||||||
|
intelligence: Attribute::new(10),
|
||||||
|
wisdom: Attribute::new(10),
|
||||||
|
charisma: Attribute::new(10),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn with_stats(str: i32, dex: i32, con: i32, int: i32, wis: i32, cha: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
strength: Attribute::new(str),
|
||||||
|
dexterity: Attribute::new(dex),
|
||||||
|
constitution: Attribute::new(con),
|
||||||
|
intelligence: Attribute::new(int),
|
||||||
|
wisdom: Attribute::new(wis),
|
||||||
|
charisma: Attribute::new(cha),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn attr_from_index(&self, attr: i32) -> &Attribute {
|
||||||
|
match attr {
|
||||||
|
Self::STR => &self.strength,
|
||||||
|
Self::DEX => &self.dexterity,
|
||||||
|
Self::CON => &self.constitution,
|
||||||
|
Self::INT => &self.intelligence,
|
||||||
|
Self::WIS => &self.wisdom,
|
||||||
|
Self::CHA => &self.charisma,
|
||||||
|
_ => unreachable!("Tried to get an attribute that doesn't exist."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn exercise(&mut self, attr: i32, improve: bool) {
|
||||||
|
match attr {
|
||||||
|
Self::STR => {
|
||||||
|
if improve {
|
||||||
|
self.strength.improve();
|
||||||
|
} else {
|
||||||
|
self.strength.abuse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::DEX => {
|
||||||
|
if improve {
|
||||||
|
self.dexterity.improve();
|
||||||
|
} else {
|
||||||
|
self.dexterity.abuse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::CON => {
|
||||||
|
if improve {
|
||||||
|
self.constitution.improve();
|
||||||
|
} else {
|
||||||
|
self.constitution.abuse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::INT => {
|
||||||
|
if improve {
|
||||||
|
self.intelligence.improve();
|
||||||
|
} else {
|
||||||
|
self.intelligence.abuse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::WIS => {
|
||||||
|
if improve {
|
||||||
|
self.wisdom.improve();
|
||||||
|
} else {
|
||||||
|
self.wisdom.abuse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::CHA => {
|
||||||
|
if improve {
|
||||||
|
self.charisma.improve();
|
||||||
|
} else {
|
||||||
|
self.charisma.abuse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!("Tried to exercise an attribute that doesn't exist."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||||
pub struct WantsToMelee {
|
pub struct WantsToMelee {
|
||||||
pub target: Entity,
|
pub target: Entity,
|
||||||
|
|
@ -258,10 +454,40 @@ pub struct Beatitude {
|
||||||
pub known: bool,
|
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)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
pub weight: f32, // in lbs
|
pub weight: f32, // in lbs
|
||||||
pub value: f32, // base
|
pub value: f32, // base
|
||||||
|
pub category: ItemType,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
|
||||||
|
|
@ -289,9 +515,23 @@ pub struct IdentifiedItem {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Stackable {}
|
||||||
|
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct EquipmentChanged {}
|
pub struct EquipmentChanged {}
|
||||||
|
|
||||||
|
#[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(PartialEq, Copy, Clone, Serialize, Deserialize)]
|
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||||
pub enum BurdenLevel {
|
pub enum BurdenLevel {
|
||||||
Burdened,
|
Burdened,
|
||||||
|
|
@ -509,6 +749,9 @@ pub struct InBackpack {
|
||||||
pub owner: Entity,
|
pub owner: Entity,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct WantsToAssignKey {}
|
||||||
|
|
||||||
#[derive(Component, Debug, ConvertSaveload)]
|
#[derive(Component, Debug, ConvertSaveload)]
|
||||||
pub struct WantsToPickupItem {
|
pub struct WantsToPickupItem {
|
||||||
pub collected_by: Entity,
|
pub collected_by: Entity,
|
||||||
|
|
@ -558,7 +801,9 @@ pub struct Charges {
|
||||||
#[derive(Component, Serialize, Deserialize, Clone)]
|
#[derive(Component, Serialize, Deserialize, Clone)]
|
||||||
pub struct SpawnParticleLine {
|
pub struct SpawnParticleLine {
|
||||||
pub glyph: FontCharType,
|
pub glyph: FontCharType,
|
||||||
|
pub sprite: String,
|
||||||
pub tail_glyph: FontCharType,
|
pub tail_glyph: FontCharType,
|
||||||
|
pub tail_sprite: String,
|
||||||
pub colour: RGB,
|
pub colour: RGB,
|
||||||
pub lifetime_ms: f32,
|
pub lifetime_ms: f32,
|
||||||
pub trail_colour: RGB,
|
pub trail_colour: RGB,
|
||||||
|
|
@ -568,6 +813,7 @@ pub struct SpawnParticleLine {
|
||||||
#[derive(Component, Serialize, Deserialize, Clone)]
|
#[derive(Component, Serialize, Deserialize, Clone)]
|
||||||
pub struct SpawnParticleSimple {
|
pub struct SpawnParticleSimple {
|
||||||
pub glyph: FontCharType,
|
pub glyph: FontCharType,
|
||||||
|
pub sprite: String,
|
||||||
pub colour: RGB,
|
pub colour: RGB,
|
||||||
pub lifetime_ms: f32,
|
pub lifetime_ms: f32,
|
||||||
}
|
}
|
||||||
|
|
@ -575,8 +821,11 @@ pub struct SpawnParticleSimple {
|
||||||
#[derive(Component, Serialize, Deserialize, Clone)]
|
#[derive(Component, Serialize, Deserialize, Clone)]
|
||||||
pub struct SpawnParticleBurst {
|
pub struct SpawnParticleBurst {
|
||||||
pub glyph: FontCharType,
|
pub glyph: FontCharType,
|
||||||
|
pub sprite: String,
|
||||||
pub head_glyph: FontCharType,
|
pub head_glyph: FontCharType,
|
||||||
|
pub head_sprite: String,
|
||||||
pub tail_glyph: FontCharType,
|
pub tail_glyph: FontCharType,
|
||||||
|
pub tail_sprite: String,
|
||||||
pub colour: RGB,
|
pub colour: RGB,
|
||||||
pub lerp: RGB,
|
pub lerp: RGB,
|
||||||
pub lifetime_ms: f32,
|
pub lifetime_ms: f32,
|
||||||
|
|
|
||||||
|
|
@ -61,11 +61,11 @@ impl Config {
|
||||||
requires_write |= config.logging.apply_values(&parsed_config);
|
requires_write |= config.logging.apply_values(&parsed_config);
|
||||||
requires_write |= config.visuals.apply_values(&parsed_config);
|
requires_write |= config.visuals.apply_values(&parsed_config);
|
||||||
if requires_write {
|
if requires_write {
|
||||||
|
console::log("Parsed config, but some values were changed. Saving new ver.");
|
||||||
if let Err(write_err) = config.save_to_file(filename) {
|
if let Err(write_err) = config.save_to_file(filename) {
|
||||||
console::log(format!("Error writing config: {:?}", write_err));
|
console::log(format!("Error writing config: {:?}", write_err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -101,6 +101,8 @@ impl Section for LogConfig {
|
||||||
fn apply_values(&mut self, parsed_config: &Value) -> bool {
|
fn apply_values(&mut self, parsed_config: &Value) -> bool {
|
||||||
if let Some(section) = parsed_config.get("logging") {
|
if let Some(section) = parsed_config.get("logging") {
|
||||||
let mut missing = false;
|
let mut missing = false;
|
||||||
|
apply_bool_value!(self, section, missing, show_mapgen);
|
||||||
|
apply_bool_value!(self, section, missing, log_combat);
|
||||||
apply_bool_value!(self, section, missing, log_spawning);
|
apply_bool_value!(self, section, missing, log_spawning);
|
||||||
apply_bool_value!(self, section, missing, log_ticks);
|
apply_bool_value!(self, section, missing, log_ticks);
|
||||||
missing
|
missing
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
pub const DEFAULT_VIEWSHED_STANDARD: i32 = 16; // Standard viewshed radius for almost all entities.
|
pub const DEFAULT_VIEWSHED_STANDARD: i32 = 7; // Standard viewshed radius for almost all entities.
|
||||||
pub const CARRY_CAPACITY_PER_STRENGTH: i32 = 5; // How much weight can be carried per point of strength.
|
pub const CARRY_CAPACITY_PER_STRENGTH: i32 = 5; // How much weight can be carried per point of strength.
|
||||||
pub const NORMAL_SPEED: i32 = 12; // Normal speed for almost all entities.
|
pub const NORMAL_SPEED: i32 = 12; // Normal speed for almost all entities.
|
||||||
pub const SPEED_MOD_BURDENED: f32 = 0.75;
|
pub const SPEED_MOD_BURDENED: f32 = 0.75;
|
||||||
|
|
@ -25,6 +25,7 @@ pub const NUTRITION_BLESSED: &str = "Delicious";
|
||||||
|
|
||||||
pub const LEVELUP_PLAYER: &str = "Welcome to experience level";
|
pub const LEVELUP_PLAYER: &str = "Welcome to experience level";
|
||||||
pub const YOU_PICKUP_ITEM: &str = "You pick up the";
|
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_DROP_ITEM: &str = "You drop the";
|
||||||
pub const YOU_EQUIP_ITEM: &str = "You equip the";
|
pub const YOU_EQUIP_ITEM: &str = "You equip the";
|
||||||
pub const YOU_REMOVE_ITEM: &str = "You unequip your";
|
pub const YOU_REMOVE_ITEM: &str = "You unequip your";
|
||||||
32
src/consts/mod.rs
Normal file
32
src/consts/mod.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
pub mod entity;
|
||||||
|
pub mod visuals;
|
||||||
|
pub mod messages;
|
||||||
|
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 };
|
||||||
|
pub use super::visuals::{ VIEWPORT_H, VIEWPORT_W };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Spritesize {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub sprite_x: f32,
|
||||||
|
pub sprite_y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const TILESIZE: Spritesize = Spritesize {
|
||||||
|
x: 16.0,
|
||||||
|
y: 16.0,
|
||||||
|
sprite_x: 16.0 * ZOOM_FACTOR,
|
||||||
|
sprite_y: 24.0 * ZOOM_FACTOR,
|
||||||
|
};
|
||||||
|
pub const ZOOM_FACTOR: f32 = 2.0;
|
||||||
|
pub const FONTSIZE: f32 = 16.0;
|
||||||
|
|
||||||
|
pub const DISPLAYWIDTH: u32 = 120;
|
||||||
|
pub const DISPLAYHEIGHT: u32 = 67;
|
||||||
119
src/consts/sprites.rs
Normal file
119
src/consts/sprites.rs
Normal file
|
|
@ -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;
|
||||||
|
|
@ -1,17 +1,34 @@
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
|
use super::ZOOM_FACTOR;
|
||||||
|
|
||||||
// POST-PROCESSING
|
// 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 WITH_DARKEN_BY_DISTANCE: bool = true; // If further away tiles should get darkened, instead of a harsh transition to non-visible.
|
||||||
|
|
||||||
|
// Counted in 16x16 tiles, because that's how most of the screen is drawn. However,
|
||||||
|
// the viewport itself uses 16x24 sprites - so this translates to 70x28 tiles drawn.
|
||||||
|
// It also works nicely for zooming in, displaying 35x14 tiles cleanly onscreen.
|
||||||
|
pub const VIEWPORT_W: i32 = 70;
|
||||||
|
pub const VIEWPORT_H: i32 = 54;
|
||||||
|
|
||||||
|
pub const TILES_IN_VIEWPORT_W: i32 = 70 / (ZOOM_FACTOR as i32);
|
||||||
|
pub const TILES_IN_VIEWPORT_H: i32 = 36 / (ZOOM_FACTOR as i32);
|
||||||
|
|
||||||
|
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 BRIGHTEN_FG_COLOUR_BY: i32 = 16;
|
||||||
pub const GLOBAL_OFFSET_MIN_CLAMP: f32 = -0.5;
|
pub const GLOBAL_OFFSET_MIN_CLAMP: f32 = -0.5;
|
||||||
pub const GLOBAL_OFFSET_MAX_CLAMP: f32 = 1.0;
|
pub const GLOBAL_OFFSET_MAX_CLAMP: f32 = 0.5;
|
||||||
|
pub const SPRITE_OFFSET_MIN_CLAMP: f32 = 0.85;
|
||||||
|
pub const SPRITE_OFFSET_MAX_CLAMP: f32 = 1.0;
|
||||||
pub const WITH_SCANLINES_BRIGHTEN_AMOUNT: f32 = 0.1; // 0.0 = no brightening, 1.0 = full brightening.
|
pub const WITH_SCANLINES_BRIGHTEN_AMOUNT: f32 = 0.1; // 0.0 = no brightening, 1.0 = full brightening.
|
||||||
pub const NON_VISIBLE_MULTIPLIER: f32 = 0.3; // 0.0 = black, 1.0 = full colour.
|
pub const NON_VISIBLE_MULTIPLIER: f32 = 0.1; // 0.0 = black, 1.0 = full colour.
|
||||||
pub const NON_VISIBLE_MULTIPLIER_IF_SCANLINES: f32 = 0.8; // as above, but when using scanlines. should be higher.
|
pub const NON_VISIBLE_MULTIPLIER_IF_SCANLINES: f32 = 0.8; // as above, but when using scanlines. should be higher.
|
||||||
pub const MAX_DARKENING: f32 = 0.45; // 0.0 = black, 1.0 = full colour - only used if WITH_DARKEN_BY_DISTANCE is true.
|
pub const MAX_DARKENING: f32 = 0.2; // 0.0 = black, 1.0 = full colour - only used if WITH_DARKEN_BY_DISTANCE is true.
|
||||||
pub const MAX_DARKENING_IF_SCANLINES: f32 = 0.9; // as above, but when using scanlines. should be higher.
|
pub const MAX_DARKENING_IF_SCANLINES: f32 = 0.9; // as above, but when using scanlines. should be higher.
|
||||||
pub const START_DARKEN_AT_N_TILES: f32 = 8.0; // start darkening at this distance (should always be less than entity::DEFAULT_VIEWSHED_STANDARD).
|
pub const START_DARKEN_AT_N_TILES: f32 = 1.0; // start darkening at this distance (should always be less than entity::DEFAULT_VIEWSHED_STANDARD).
|
||||||
|
|
||||||
pub const SHORT_PARTICLE_LIFETIME: f32 = 100.0; // in ms
|
pub const SHORT_PARTICLE_LIFETIME: f32 = 100.0; // in ms
|
||||||
pub const DEFAULT_PARTICLE_LIFETIME: f32 = 200.0;
|
pub const DEFAULT_PARTICLE_LIFETIME: f32 = 200.0;
|
||||||
|
|
@ -57,6 +74,7 @@ pub const SHALLOW_WATER_OFFSETS: (i32, i32, i32) = (3, 10, 45);
|
||||||
pub const DEEP_WATER_COLOUR: (u8, u8, u8) = (18, 33, 63);
|
pub const DEEP_WATER_COLOUR: (u8, u8, u8) = (18, 33, 63);
|
||||||
pub const DEEP_WATER_OFFSETS: (i32, i32, i32) = (5, 10, 32);
|
pub const DEEP_WATER_OFFSETS: (i32, i32, i32) = (5, 10, 32);
|
||||||
pub const BARS_COLOUR: (u8, u8, u8) = (100, 100, 100);
|
pub const BARS_COLOUR: (u8, u8, u8) = (100, 100, 100);
|
||||||
|
pub const BARS_OFFSETS: (i32, i32, i32) = (10, 10, 10);
|
||||||
pub const IMPASSABLE_MOUNTAIN_COLOUR: (u8, u8, u8) = (20, 23, 20);
|
pub const IMPASSABLE_MOUNTAIN_COLOUR: (u8, u8, u8) = (20, 23, 20);
|
||||||
pub const IMPASSABLE_MOUNTAIN_OFFSETS: (i32, i32, i32) = (4, 4, 4);
|
pub const IMPASSABLE_MOUNTAIN_OFFSETS: (i32, i32, i32) = (4, 4, 4);
|
||||||
// FOREST THEME
|
// FOREST THEME
|
||||||
|
|
@ -11,10 +11,12 @@ use super::{
|
||||||
Position,
|
Position,
|
||||||
Renderable,
|
Renderable,
|
||||||
RunState,
|
RunState,
|
||||||
|
WantsToRemoveKey,
|
||||||
|
WantsToDelete,
|
||||||
};
|
};
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use crate::data::events;
|
use crate::consts::events;
|
||||||
|
|
||||||
pub fn delete_the_dead(ecs: &mut World) {
|
pub fn delete_the_dead(ecs: &mut World) {
|
||||||
let mut dead: Vec<Entity> = Vec::new();
|
let mut dead: Vec<Entity> = Vec::new();
|
||||||
|
|
@ -65,7 +67,17 @@ pub fn delete_the_dead(ecs: &mut World) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (items_to_delete, loot_to_spawn) = handle_dead_entity_items(ecs, &dead);
|
let (mut items_to_delete, loot_to_spawn) = handle_dead_entity_items(ecs, &dead);
|
||||||
|
{
|
||||||
|
let entities = ecs.entities();
|
||||||
|
let removekeys = ecs.read_storage::<WantsToRemoveKey>();
|
||||||
|
let delete = ecs.read_storage::<WantsToDelete>();
|
||||||
|
// Add items marked for deletion to the list, but only if they've already had their key
|
||||||
|
// assignments handled, to ensure we don't leave any dangling references behind.
|
||||||
|
for (e, _d, _r) in (&entities, &delete, !&removekeys).join() {
|
||||||
|
items_to_delete.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
for loot in loot_to_spawn {
|
for loot in loot_to_spawn {
|
||||||
crate::raws::spawn_named_entity(
|
crate::raws::spawn_named_entity(
|
||||||
&crate::raws::RAWS.lock().unwrap(),
|
&crate::raws::RAWS.lock().unwrap(),
|
||||||
|
|
@ -82,6 +94,7 @@ pub fn delete_the_dead(ecs: &mut World) {
|
||||||
// For everything that died, increment the event log, and delete.
|
// For everything that died, increment the event log, and delete.
|
||||||
for victim in dead {
|
for victim in dead {
|
||||||
gamelog::record_event(events::EVENT::Turn(1));
|
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.");
|
ecs.delete_entity(victim).expect("Unable to delete.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
pub mod entity;
|
|
||||||
pub mod visuals;
|
|
||||||
pub mod messages;
|
|
||||||
pub mod char_create;
|
|
||||||
pub mod events;
|
|
||||||
pub mod ids;
|
|
||||||
pub mod names;
|
|
||||||
32
src/effects/attr.rs
Normal file
32
src/effects/attr.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
use specs::prelude::*;
|
||||||
|
use bracket_lib::prelude::*;
|
||||||
|
use super::{ EffectSpawner, EffectType };
|
||||||
|
use crate::components::Attributes;
|
||||||
|
|
||||||
|
const ATTRIBUTE_SOFTCAP: i32 = 20;
|
||||||
|
const ABUSE_CHANCE: i32 = 2; // 1 in this chance of abuse. 2 = 50%, 3 = 33%, etc.
|
||||||
|
|
||||||
|
pub(crate) fn exercise(ecs: &mut World, effect: &EffectSpawner, target: Entity) {
|
||||||
|
// Unwrap vars from the effect
|
||||||
|
let (attr, inc) = if let EffectType::Exercise { attribute, increment } = effect.effect_type {
|
||||||
|
(attribute, increment)
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get target attributes
|
||||||
|
let mut attributes = ecs.write_storage::<Attributes>();
|
||||||
|
if let Some(has_attr) = attributes.get_mut(target) {
|
||||||
|
// Roll a d20. If we're trying to exercise a stat, we need to roll higher
|
||||||
|
// than the stat's current value. If we're abusing a stat, flip a coin.
|
||||||
|
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
|
||||||
|
let success = if inc {
|
||||||
|
rng.roll_dice(1, ATTRIBUTE_SOFTCAP) > has_attr.attr_from_index(attr).current()
|
||||||
|
} else {
|
||||||
|
rng.roll_dice(1, ABUSE_CHANCE) == 1
|
||||||
|
};
|
||||||
|
if success {
|
||||||
|
has_attr.exercise(attr, inc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,12 +15,13 @@ use crate::{
|
||||||
HungerState,
|
HungerState,
|
||||||
Bleeds,
|
Bleeds,
|
||||||
HasDamageModifiers,
|
HasDamageModifiers,
|
||||||
|
DamageType,
|
||||||
};
|
};
|
||||||
use crate::gui::with_article;
|
use crate::gui::with_article;
|
||||||
use crate::data::visuals::{ DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME };
|
use crate::consts::visuals::{ DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME };
|
||||||
use crate::data::messages::LEVELUP_PLAYER;
|
use crate::consts::messages::LEVELUP_PLAYER;
|
||||||
use crate::data::events::*;
|
use crate::consts::events::*;
|
||||||
use crate::data::messages::*;
|
use crate::consts::messages::*;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
||||||
|
|
@ -38,33 +39,88 @@ pub fn inflict_damage(ecs: &mut World, damage: &EffectSpawner, target: Entity) {
|
||||||
};
|
};
|
||||||
target_pool.hit_points.current -= ((amount as f32) * mult) as i32;
|
target_pool.hit_points.current -= ((amount as f32) * mult) as i32;
|
||||||
let bleeders = ecs.read_storage::<Bleeds>();
|
let bleeders = ecs.read_storage::<Bleeds>();
|
||||||
|
// If the target bleeds, handle bloodstains and use the bleed colour for sfx.
|
||||||
if let Some(bleeds) = bleeders.get(target) {
|
if let Some(bleeds) = bleeders.get(target) {
|
||||||
|
if target_pool.hit_points.current < 1 {
|
||||||
|
super::DEAD_ENTITIES.lock().unwrap().push_back(target);
|
||||||
|
add_effect(damage.source, EffectType::EntityDeath, Targets::Entity {
|
||||||
|
target,
|
||||||
|
});
|
||||||
|
for i in 0..3 {
|
||||||
|
let sprite = (
|
||||||
|
match i {
|
||||||
|
0 => "explode1",
|
||||||
|
1 => "explode2",
|
||||||
|
_ => "explode3",
|
||||||
|
}
|
||||||
|
).to_string();
|
||||||
|
add_effect(
|
||||||
|
None,
|
||||||
|
EffectType::Particle {
|
||||||
|
glyph: to_cp437('‼'),
|
||||||
|
sprite,
|
||||||
|
fg: bleeds.colour,
|
||||||
|
lifespan: 75.0,
|
||||||
|
delay: 75.0 * (i as f32),
|
||||||
|
},
|
||||||
|
Targets::Entity { target }
|
||||||
|
);
|
||||||
add_effect(
|
add_effect(
|
||||||
None,
|
None,
|
||||||
EffectType::Bloodstain { colour: bleeds.colour },
|
EffectType::Bloodstain { colour: bleeds.colour },
|
||||||
Targets::Entity { target }
|
Targets::Entity { target }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Regular damage taken effect - use damagetype to determine which one to play.
|
||||||
add_effect(
|
add_effect(
|
||||||
None,
|
None,
|
||||||
EffectType::Particle {
|
EffectType::Particle {
|
||||||
glyph: to_cp437('‼'),
|
glyph: to_cp437('‼'),
|
||||||
|
sprite: "gnome".to_string(), // FIXME: REMOVE THE GNOMES
|
||||||
fg: RGB::named(ORANGE),
|
fg: RGB::named(ORANGE),
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
lifespan: DEFAULT_PARTICLE_LIFETIME,
|
lifespan: DEFAULT_PARTICLE_LIFETIME,
|
||||||
delay: 0.0,
|
delay: 0.0,
|
||||||
},
|
},
|
||||||
Targets::Entity { target }
|
Targets::Entity { target }
|
||||||
);
|
);
|
||||||
if target_pool.hit_points.current < 1 {
|
add_effect(
|
||||||
super::DEAD_ENTITIES.lock().unwrap().push_back(target);
|
None,
|
||||||
add_effect(damage.source, EffectType::EntityDeath, Targets::Entity { target });
|
EffectType::Bloodstain { colour: bleeds.colour },
|
||||||
|
Targets::Entity { target }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: Damage taken particle effects when the target does not bleed.
|
||||||
|
// Also damage types, etc.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(_destructible) = ecs.read_storage::<Destructible>().get(target) {
|
} else if let Some(_destructible) = ecs.read_storage::<Destructible>().get(target) {
|
||||||
add_effect(damage.source, EffectType::EntityDeath, Targets::Entity { target });
|
add_effect(damage.source, EffectType::EntityDeath, Targets::Entity { target });
|
||||||
}
|
}
|
||||||
|
if let EffectType::Damage { amount: _, damage_type } = &damage.effect_type {
|
||||||
|
get_random_damage_sound(ecs, damage_type, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_random_damage_sound(ecs: &World, damage_type: &DamageType, target: Entity) {
|
||||||
|
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
|
||||||
|
match damage_type {
|
||||||
|
DamageType::Physical => {
|
||||||
|
let sound = (
|
||||||
|
match rng.roll_dice(1, 4) {
|
||||||
|
1 => "whoosh1",
|
||||||
|
2 => "whoosh2",
|
||||||
|
3 => "whoosh3",
|
||||||
|
_ => "whoosh4",
|
||||||
|
}
|
||||||
|
).to_string();
|
||||||
|
add_effect(None, EffectType::Sound { sound }, Targets::Entity { target });
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn heal_damage(ecs: &mut World, heal: &EffectSpawner, target: Entity) {
|
pub fn heal_damage(ecs: &mut World, heal: &EffectSpawner, target: Entity) {
|
||||||
|
|
@ -83,10 +139,10 @@ pub fn heal_damage(ecs: &mut World, heal: &EffectSpawner, target: Entity) {
|
||||||
}
|
}
|
||||||
add_effect(
|
add_effect(
|
||||||
None,
|
None,
|
||||||
EffectType::Particle {
|
EffectType::Particle { // GNOMES
|
||||||
glyph: to_cp437('♥'),
|
glyph: to_cp437('♥'),
|
||||||
|
sprite: "gnome".to_string(),
|
||||||
fg: RGB::named(BLUE),
|
fg: RGB::named(BLUE),
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
lifespan: DEFAULT_PARTICLE_LIFETIME,
|
lifespan: DEFAULT_PARTICLE_LIFETIME,
|
||||||
delay: 0.0,
|
delay: 0.0,
|
||||||
},
|
},
|
||||||
|
|
@ -184,7 +240,7 @@ fn get_death_message(ecs: &World, source: Entity) -> String {
|
||||||
result.push_str(format!("{}", PLAYER_DIED_SUICIDE).as_str());
|
result.push_str(format!("{}", PLAYER_DIED_SUICIDE).as_str());
|
||||||
} else if let Some(name) = ecs.read_storage::<Name>().get(source) {
|
} else if let Some(name) = ecs.read_storage::<Name>().get(source) {
|
||||||
result.push_str(
|
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 {
|
} else {
|
||||||
result.push_str(format!("{}", PLAYER_DIED_UNKNOWN).as_str());
|
result.push_str(format!("{}", PLAYER_DIED_UNKNOWN).as_str());
|
||||||
|
|
@ -266,11 +322,11 @@ pub fn entity_death(ecs: &mut World, effect: &EffectSpawner, target: Entity) {
|
||||||
for i in 0..5 {
|
for i in 0..5 {
|
||||||
if player_pos.y - i > 1 {
|
if player_pos.y - i > 1 {
|
||||||
add_effect(
|
add_effect(
|
||||||
None,
|
None, // FIXME: REMOVE THE GNOMES
|
||||||
EffectType::Particle {
|
EffectType::Particle {
|
||||||
glyph: to_cp437('░'),
|
glyph: to_cp437('░'),
|
||||||
|
sprite: "gnome".to_string(),
|
||||||
fg: RGB::named(GOLD),
|
fg: RGB::named(GOLD),
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
lifespan: LONG_PARTICLE_LIFETIME,
|
lifespan: LONG_PARTICLE_LIFETIME,
|
||||||
delay: (i as f32) * 100.0,
|
delay: (i as f32) * 100.0,
|
||||||
},
|
},
|
||||||
|
|
@ -281,8 +337,8 @@ pub fn entity_death(ecs: &mut World, effect: &EffectSpawner, target: Entity) {
|
||||||
None,
|
None,
|
||||||
EffectType::Particle {
|
EffectType::Particle {
|
||||||
glyph: to_cp437('░'),
|
glyph: to_cp437('░'),
|
||||||
|
sprite: "gnome".to_string(),
|
||||||
fg: RGB::named(GOLD),
|
fg: RGB::named(GOLD),
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
lifespan: LONG_PARTICLE_LIFETIME,
|
lifespan: LONG_PARTICLE_LIFETIME,
|
||||||
delay: (i as f32) * 100.0,
|
delay: (i as f32) * 100.0,
|
||||||
},
|
},
|
||||||
|
|
@ -297,8 +353,8 @@ pub fn entity_death(ecs: &mut World, effect: &EffectSpawner, target: Entity) {
|
||||||
None,
|
None,
|
||||||
EffectType::Particle {
|
EffectType::Particle {
|
||||||
glyph: to_cp437('░'),
|
glyph: to_cp437('░'),
|
||||||
|
sprite: "gnome".to_string(),
|
||||||
fg: RGB::named(GOLD),
|
fg: RGB::named(GOLD),
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
lifespan: LONG_PARTICLE_LIFETIME,
|
lifespan: LONG_PARTICLE_LIFETIME,
|
||||||
delay: (i as f32) * 100.0,
|
delay: (i as f32) * 100.0,
|
||||||
},
|
},
|
||||||
|
|
@ -319,11 +375,11 @@ pub fn entity_death(ecs: &mut World, effect: &EffectSpawner, target: Entity) {
|
||||||
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
|
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
|
||||||
let hp_gained = hp_per_level(
|
let hp_gained = hp_per_level(
|
||||||
&mut rng,
|
&mut rng,
|
||||||
source_attributes.constitution.base + source_attributes.constitution.modifiers
|
source_attributes.constitution.base + source_attributes.constitution.bonuses
|
||||||
);
|
);
|
||||||
let mana_gained = mana_per_level(
|
let mana_gained = mana_per_level(
|
||||||
&mut rng,
|
&mut rng,
|
||||||
source_attributes.intelligence.base + source_attributes.intelligence.modifiers
|
source_attributes.intelligence.base + source_attributes.intelligence.bonuses
|
||||||
);
|
);
|
||||||
source_pools.hit_points.max += hp_gained;
|
source_pools.hit_points.max += hp_gained;
|
||||||
source_pools.hit_points.current += hp_gained;
|
source_pools.hit_points.current += hp_gained;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use super::BUC;
|
||||||
use crate::spatial;
|
use crate::spatial;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
use notan::prelude::*;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use crate::components::*;
|
use crate::components::*;
|
||||||
|
|
@ -11,7 +12,9 @@ mod hunger;
|
||||||
mod particles;
|
mod particles;
|
||||||
mod targeting;
|
mod targeting;
|
||||||
mod triggers;
|
mod triggers;
|
||||||
|
mod attr;
|
||||||
mod intrinsics;
|
mod intrinsics;
|
||||||
|
pub mod sound;
|
||||||
|
|
||||||
pub use targeting::aoe_tiles;
|
pub use targeting::aoe_tiles;
|
||||||
|
|
||||||
|
|
@ -32,6 +35,10 @@ pub enum EffectType {
|
||||||
amount: i32,
|
amount: i32,
|
||||||
increment_max: bool,
|
increment_max: bool,
|
||||||
},
|
},
|
||||||
|
Exercise {
|
||||||
|
attribute: i32,
|
||||||
|
increment: bool,
|
||||||
|
},
|
||||||
Confusion {
|
Confusion {
|
||||||
turns: i32,
|
turns: i32,
|
||||||
},
|
},
|
||||||
|
|
@ -40,8 +47,8 @@ pub enum EffectType {
|
||||||
},
|
},
|
||||||
Particle {
|
Particle {
|
||||||
glyph: FontCharType,
|
glyph: FontCharType,
|
||||||
|
sprite: String,
|
||||||
fg: RGB,
|
fg: RGB,
|
||||||
bg: RGB,
|
|
||||||
lifespan: f32,
|
lifespan: f32,
|
||||||
delay: f32,
|
delay: f32,
|
||||||
},
|
},
|
||||||
|
|
@ -58,6 +65,9 @@ pub enum EffectType {
|
||||||
TriggerFire {
|
TriggerFire {
|
||||||
trigger: Entity,
|
trigger: Entity,
|
||||||
},
|
},
|
||||||
|
Sound {
|
||||||
|
sound: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -90,7 +100,7 @@ pub fn add_effect(source: Option<Entity>, effect_type: EffectType, target: Targe
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterates through the effects queue, applying each effect to their target.
|
/// Iterates through the effects queue, applying each effect to their target.
|
||||||
pub fn run_effects_queue(ecs: &mut World) {
|
pub fn run_effects_queue(app: &mut App, ecs: &mut World) {
|
||||||
// First removes any effect in the EFFECT_QUEUE with a dead entity as its source.
|
// First removes any effect in the EFFECT_QUEUE with a dead entity as its source.
|
||||||
loop {
|
loop {
|
||||||
let dead_entity: Option<Entity> = DEAD_ENTITIES.lock().unwrap().pop_front();
|
let dead_entity: Option<Entity> = DEAD_ENTITIES.lock().unwrap().pop_front();
|
||||||
|
|
@ -106,7 +116,7 @@ pub fn run_effects_queue(ecs: &mut World) {
|
||||||
loop {
|
loop {
|
||||||
let effect: Option<EffectSpawner> = EFFECT_QUEUE.lock().unwrap().pop_front();
|
let effect: Option<EffectSpawner> = EFFECT_QUEUE.lock().unwrap().pop_front();
|
||||||
if let Some(effect) = effect {
|
if let Some(effect) = effect {
|
||||||
target_applicator(ecs, &effect);
|
target_applicator(app, ecs, &effect);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -114,7 +124,7 @@ pub fn run_effects_queue(ecs: &mut World) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies an effect to the correct target(s).
|
/// Applies an effect to the correct target(s).
|
||||||
fn target_applicator(ecs: &mut World, effect: &EffectSpawner) {
|
fn target_applicator(app: &mut App, ecs: &mut World, effect: &EffectSpawner) {
|
||||||
// Item use is handled differently - it creates other effects with itself
|
// Item use is handled differently - it creates other effects with itself
|
||||||
// as the source, passing all effects attached to the item into the queue.
|
// as the source, passing all effects attached to the item into the queue.
|
||||||
if let EffectType::ItemUse { item } = effect.effect_type {
|
if let EffectType::ItemUse { item } = effect.effect_type {
|
||||||
|
|
@ -126,25 +136,26 @@ fn target_applicator(ecs: &mut World, effect: &EffectSpawner) {
|
||||||
}
|
}
|
||||||
// Otherwise, just match the effect and enact it directly.
|
// Otherwise, just match the effect and enact it directly.
|
||||||
match &effect.target {
|
match &effect.target {
|
||||||
Targets::Tile { target } => affect_tile(ecs, effect, *target),
|
Targets::Tile { target } => affect_tile(app, ecs, effect, *target),
|
||||||
Targets::TileList { targets } =>
|
Targets::TileList { targets } =>
|
||||||
targets.iter().for_each(|target| affect_tile(ecs, effect, *target)),
|
targets.iter().for_each(|target| affect_tile(app, ecs, effect, *target)),
|
||||||
Targets::Entity { target } => affect_entity(ecs, effect, *target),
|
Targets::Entity { target } => affect_entity(app, ecs, effect, *target),
|
||||||
Targets::EntityList { targets } =>
|
Targets::EntityList { targets } =>
|
||||||
targets.iter().for_each(|target| affect_entity(ecs, effect, *target)),
|
targets.iter().for_each(|target| affect_entity(app, ecs, effect, *target)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs an effect on a given tile index
|
/// Runs an effect on a given tile index
|
||||||
fn affect_tile(ecs: &mut World, effect: &EffectSpawner, target: usize) {
|
fn affect_tile(app: &mut App, ecs: &mut World, effect: &EffectSpawner, target: usize) {
|
||||||
if tile_effect_hits_entities(&effect.effect_type) {
|
if tile_effect_hits_entities(&effect.effect_type) {
|
||||||
spatial::for_each_tile_content(target, |entity| {
|
spatial::for_each_tile_content(target, |entity| {
|
||||||
affect_entity(ecs, effect, entity);
|
affect_entity(app, ecs, effect, entity);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
match &effect.effect_type {
|
match &effect.effect_type {
|
||||||
EffectType::Particle { .. } => particles::particle_to_tile(ecs, target as i32, &effect),
|
EffectType::Particle { .. } => particles::particle_to_tile(ecs, target as i32, &effect),
|
||||||
|
EffectType::Sound { .. } => sound::play_sound(app, ecs, &effect, target),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
// Run the effect
|
// Run the effect
|
||||||
|
|
@ -163,10 +174,11 @@ fn tile_effect_hits_entities(effect: &EffectType) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs an effect on a given entity
|
/// Runs an effect on a given entity
|
||||||
fn affect_entity(ecs: &mut World, effect: &EffectSpawner, target: Entity) {
|
fn affect_entity(app: &mut App, ecs: &mut World, effect: &EffectSpawner, target: Entity) {
|
||||||
match &effect.effect_type {
|
match &effect.effect_type {
|
||||||
EffectType::Damage { .. } => damage::inflict_damage(ecs, effect, target),
|
EffectType::Damage { .. } => damage::inflict_damage(ecs, effect, target),
|
||||||
EffectType::Healing { .. } => damage::heal_damage(ecs, effect, target),
|
EffectType::Healing { .. } => damage::heal_damage(ecs, effect, target),
|
||||||
|
EffectType::Exercise { .. } => attr::exercise(ecs, effect, target),
|
||||||
EffectType::Confusion { .. } => damage::add_confusion(ecs, effect, target),
|
EffectType::Confusion { .. } => damage::add_confusion(ecs, effect, target),
|
||||||
EffectType::Bloodstain { colour } => {
|
EffectType::Bloodstain { colour } => {
|
||||||
if let Some(pos) = targeting::entity_position(ecs, target) {
|
if let Some(pos) = targeting::entity_position(ecs, target) {
|
||||||
|
|
@ -181,6 +193,11 @@ fn affect_entity(ecs: &mut World, effect: &EffectSpawner, target: Entity) {
|
||||||
EffectType::EntityDeath => damage::entity_death(ecs, effect, target),
|
EffectType::EntityDeath => damage::entity_death(ecs, effect, target),
|
||||||
EffectType::ModifyNutrition { .. } => hunger::modify_nutrition(ecs, effect, target),
|
EffectType::ModifyNutrition { .. } => hunger::modify_nutrition(ecs, effect, target),
|
||||||
EffectType::AddIntrinsic { .. } => intrinsics::add_intrinsic(ecs, effect, target),
|
EffectType::AddIntrinsic { .. } => intrinsics::add_intrinsic(ecs, effect, target),
|
||||||
|
EffectType::Sound { .. } => {
|
||||||
|
if let Some(pos) = targeting::entity_position(ecs, target) {
|
||||||
|
sound::play_sound(app, ecs, &effect, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,27 +4,27 @@ use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
||||||
pub fn particle_to_tile(ecs: &mut World, target: i32, effect: &EffectSpawner) {
|
pub fn particle_to_tile(ecs: &mut World, target: i32, effect: &EffectSpawner) {
|
||||||
if let EffectType::Particle { glyph, fg, bg, lifespan, delay } = effect.effect_type {
|
if let EffectType::Particle { glyph, sprite, fg, lifespan, delay } = &effect.effect_type {
|
||||||
let map = ecs.fetch::<Map>();
|
let map = ecs.fetch::<Map>();
|
||||||
let mut particle_builder = ecs.fetch_mut::<ParticleBuilder>();
|
let mut particle_builder = ecs.fetch_mut::<ParticleBuilder>();
|
||||||
if delay <= 0.0 {
|
if delay <= &0.0 {
|
||||||
particle_builder.request(
|
particle_builder.request(
|
||||||
target % map.width,
|
target % map.width,
|
||||||
target / map.width,
|
target / map.width,
|
||||||
fg,
|
*fg,
|
||||||
bg,
|
*glyph,
|
||||||
glyph,
|
sprite.clone(),
|
||||||
lifespan
|
*lifespan
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
particle_builder.delay(
|
particle_builder.delay(
|
||||||
target % map.width,
|
target % map.width,
|
||||||
target / map.width,
|
target / map.width,
|
||||||
fg,
|
*fg,
|
||||||
bg,
|
*glyph,
|
||||||
glyph,
|
sprite.clone(),
|
||||||
lifespan,
|
*lifespan,
|
||||||
delay
|
*delay
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36,8 +36,8 @@ pub fn handle_simple_particles(ecs: &World, entity: Entity, target: &Targets) {
|
||||||
None,
|
None,
|
||||||
EffectType::Particle {
|
EffectType::Particle {
|
||||||
glyph: part.glyph,
|
glyph: part.glyph,
|
||||||
|
sprite: part.sprite.clone(),
|
||||||
fg: part.colour,
|
fg: part.colour,
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
lifespan: part.lifetime_ms,
|
lifespan: part.lifetime_ms,
|
||||||
delay: 0.0,
|
delay: 0.0,
|
||||||
},
|
},
|
||||||
|
|
@ -56,7 +56,9 @@ pub fn handle_burst_particles(ecs: &World, entity: Entity, target: &Targets) {
|
||||||
end_pos,
|
end_pos,
|
||||||
&(SpawnParticleLine {
|
&(SpawnParticleLine {
|
||||||
glyph: part.head_glyph,
|
glyph: part.head_glyph,
|
||||||
|
sprite: part.head_sprite.clone(),
|
||||||
tail_glyph: part.tail_glyph,
|
tail_glyph: part.tail_glyph,
|
||||||
|
tail_sprite: part.tail_sprite.clone(),
|
||||||
colour: part.colour,
|
colour: part.colour,
|
||||||
trail_colour: part.trail_colour,
|
trail_colour: part.trail_colour,
|
||||||
lifetime_ms: part.trail_lifetime_ms, // 75.0 is good here.
|
lifetime_ms: part.trail_lifetime_ms, // 75.0 is good here.
|
||||||
|
|
@ -75,8 +77,8 @@ pub fn handle_burst_particles(ecs: &World, entity: Entity, target: &Targets) {
|
||||||
None,
|
None,
|
||||||
EffectType::Particle {
|
EffectType::Particle {
|
||||||
glyph: part.glyph,
|
glyph: part.glyph,
|
||||||
|
sprite: part.sprite.clone(),
|
||||||
fg: part.colour.lerp(part.lerp, (i as f32) * 0.1),
|
fg: part.colour.lerp(part.lerp, (i as f32) * 0.1),
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
lifespan: part.lifetime_ms / 10.0, // ~50-80 is good here.
|
lifespan: part.lifetime_ms / 10.0, // ~50-80 is good here.
|
||||||
delay: burst_delay + ((i as f32) * part.lifetime_ms) / 10.0, // above + burst_delay
|
delay: burst_delay + ((i as f32) * part.lifetime_ms) / 10.0, // above + burst_delay
|
||||||
},
|
},
|
||||||
|
|
@ -163,8 +165,8 @@ fn spawn_line_particles(ecs: &World, start: i32, end: i32, part: &SpawnParticleL
|
||||||
None,
|
None,
|
||||||
EffectType::Particle {
|
EffectType::Particle {
|
||||||
glyph: part.glyph,
|
glyph: part.glyph,
|
||||||
|
sprite: part.sprite.clone(),
|
||||||
fg: part.colour,
|
fg: part.colour,
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
lifespan: part.lifetime_ms,
|
lifespan: part.lifetime_ms,
|
||||||
delay: (i as f32) * part.lifetime_ms,
|
delay: (i as f32) * part.lifetime_ms,
|
||||||
},
|
},
|
||||||
|
|
@ -175,8 +177,8 @@ fn spawn_line_particles(ecs: &World, start: i32, end: i32, part: &SpawnParticleL
|
||||||
None,
|
None,
|
||||||
EffectType::Particle {
|
EffectType::Particle {
|
||||||
glyph: part.tail_glyph,
|
glyph: part.tail_glyph,
|
||||||
|
sprite: part.tail_sprite.clone(),
|
||||||
fg: part.trail_colour,
|
fg: part.trail_colour,
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
lifespan: part.trail_lifetime_ms,
|
lifespan: part.trail_lifetime_ms,
|
||||||
delay: (i as f32) * part.lifetime_ms,
|
delay: (i as f32) * part.lifetime_ms,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
145
src/effects/sound.rs
Normal file
145
src/effects/sound.rs
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
use bracket_lib::prelude::*;
|
||||||
|
use notan::prelude::*;
|
||||||
|
use specs::prelude::*;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use super::{ EffectSpawner, EffectType, Targets, add_effect };
|
||||||
|
use crate::Map;
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub static ref SOUNDS: Mutex<HashMap<String, (AudioSource, AudioType)>> = Mutex::new(HashMap::new());
|
||||||
|
pub static ref VOLUME: Mutex<f32> = Mutex::new(1.0);
|
||||||
|
pub static ref AMBIENCE: Mutex<Option<Sound>> = Mutex::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
|
pub enum AudioType {
|
||||||
|
Ambient,
|
||||||
|
SFX,
|
||||||
|
}
|
||||||
|
|
||||||
|
const AMBIENCE_VOL_MUL: f32 = 0.8;
|
||||||
|
const SFX_VOL_MUL: f32 = 1.0;
|
||||||
|
|
||||||
|
pub fn play_sound(app: &mut App, ecs: &mut World, effect: &EffectSpawner, target: usize) {
|
||||||
|
// Extract sound from the EffectType, or panic if we somehow called this with the wrong effect.
|
||||||
|
let sound = if let EffectType::Sound { sound } = &effect.effect_type {
|
||||||
|
sound
|
||||||
|
} else {
|
||||||
|
unreachable!("add_intrinsic() called with the wrong EffectType")
|
||||||
|
};
|
||||||
|
// Fetch all the relevant precursors.
|
||||||
|
let sounds = SOUNDS.lock().unwrap();
|
||||||
|
let volume = VOLUME.lock().unwrap();
|
||||||
|
let source = sounds.get(sound).unwrap();
|
||||||
|
let (vol, repeat) = match source.1 {
|
||||||
|
AudioType::Ambient => (*volume * AMBIENCE_VOL_MUL, true),
|
||||||
|
AudioType::SFX => {
|
||||||
|
let map = ecs.fetch::<Map>();
|
||||||
|
let ppos = ecs.fetch::<Point>();
|
||||||
|
// Get a slight variation on sound volume, just so things don't sound too uniform.
|
||||||
|
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
|
||||||
|
let vol_random = rng.range(0.9, 1.1);
|
||||||
|
// Calc distance from player to target.
|
||||||
|
let dist = DistanceAlg::PythagorasSquared.distance2d(
|
||||||
|
*ppos,
|
||||||
|
Point::new((target as i32) % map.width, (target as i32) / map.width)
|
||||||
|
);
|
||||||
|
// Play sound at volume proportional to distance.
|
||||||
|
(*volume * SFX_VOL_MUL * vol_random * (1.0 - (dist as f32) / 14.0), false)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Play the sound.
|
||||||
|
let sound: Sound = app.audio.play_sound(&source.0, vol, repeat);
|
||||||
|
if repeat {
|
||||||
|
replace_ambience(app, &sound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(app: &mut App) {
|
||||||
|
let mut ambience = AMBIENCE.lock().unwrap();
|
||||||
|
if let Some(old) = ambience.take() {
|
||||||
|
app.audio.stop(&old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ambience(sound: &str) {
|
||||||
|
add_effect(None, EffectType::Sound { sound: sound.to_string() }, Targets::Tile { target: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn replace_ambience(app: &mut App, sound: &Sound) {
|
||||||
|
let mut ambience = AMBIENCE.lock().unwrap();
|
||||||
|
if let Some(old) = ambience.take() {
|
||||||
|
app.audio.stop(&old);
|
||||||
|
}
|
||||||
|
*ambience = Some(sound.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_sounds(app: &mut App) {
|
||||||
|
let sound_data: &[(&str, &[u8], AudioType)] = &[
|
||||||
|
// (key, file_path, audiotype)
|
||||||
|
("a_relax", include_bytes!("../../resources/sounds/amb/relaxed.ogg"), AudioType::Ambient),
|
||||||
|
("whoosh1", include_bytes!("../../resources/sounds/hit/whoosh1.ogg"), AudioType::SFX),
|
||||||
|
("whoosh2", include_bytes!("../../resources/sounds/hit/whoosh2.ogg"), AudioType::SFX),
|
||||||
|
("whoosh3", include_bytes!("../../resources/sounds/hit/whoosh3.ogg"), AudioType::SFX),
|
||||||
|
("whoosh4", include_bytes!("../../resources/sounds/hit/whoosh4.ogg"), AudioType::SFX),
|
||||||
|
("d_blocked1", include_bytes!("../../resources/sounds/door/blocked1.wav"), AudioType::SFX),
|
||||||
|
("d_blocked2", include_bytes!("../../resources/sounds/door/blocked2.wav"), AudioType::SFX),
|
||||||
|
("d_blocked3", include_bytes!("../../resources/sounds/door/blocked3.wav"), AudioType::SFX),
|
||||||
|
("d_open1", include_bytes!("../../resources/sounds/door/open1.wav"), AudioType::SFX),
|
||||||
|
("d_open2", include_bytes!("../../resources/sounds/door/open2.wav"), AudioType::SFX),
|
||||||
|
("d_open3", include_bytes!("../../resources/sounds/door/open3.wav"), AudioType::SFX),
|
||||||
|
("d_close1", include_bytes!("../../resources/sounds/door/close1.wav"), AudioType::SFX),
|
||||||
|
("d_close2", include_bytes!("../../resources/sounds/door/close2.wav"), AudioType::SFX),
|
||||||
|
("d_close3", include_bytes!("../../resources/sounds/door/close3.wav"), AudioType::SFX),
|
||||||
|
];
|
||||||
|
let mut sounds = SOUNDS.lock().unwrap();
|
||||||
|
for (k, bytes, audiotype) in sound_data {
|
||||||
|
sounds.insert(k.to_string(), (app.audio.create_source(bytes).unwrap(), *audiotype));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_volume(vol: f32) {
|
||||||
|
let mut volume = VOLUME.lock().unwrap();
|
||||||
|
*volume = vol;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clean(app: &mut App) {
|
||||||
|
app.audio.clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shorthand functions for adding generic, frequent SFX to the effect queue.
|
||||||
|
pub fn door_open(idx: usize) {
|
||||||
|
let mut rng = RandomNumberGenerator::new();
|
||||||
|
let sound = (
|
||||||
|
match rng.range(0, 3) {
|
||||||
|
0 => "d_open1",
|
||||||
|
1 => "d_open2",
|
||||||
|
_ => "d_open3",
|
||||||
|
}
|
||||||
|
).to_string();
|
||||||
|
super::add_effect(None, EffectType::Sound { sound }, Targets::Tile { target: idx });
|
||||||
|
}
|
||||||
|
pub fn door_resist(idx: usize) {
|
||||||
|
let mut rng = RandomNumberGenerator::new();
|
||||||
|
let sound = (
|
||||||
|
match rng.range(0, 3) {
|
||||||
|
0 => "d_blocked1",
|
||||||
|
1 => "d_blocked2",
|
||||||
|
_ => "d_blocked3",
|
||||||
|
}
|
||||||
|
).to_string();
|
||||||
|
add_effect(None, EffectType::Sound { sound }, Targets::Tile { target: idx });
|
||||||
|
}
|
||||||
|
pub fn door_close(idx: usize) {
|
||||||
|
let mut rng = RandomNumberGenerator::new();
|
||||||
|
let sound = (
|
||||||
|
match rng.range(0, 3) {
|
||||||
|
0 => "d_close1",
|
||||||
|
1 => "d_close2",
|
||||||
|
_ => "d_close3",
|
||||||
|
}
|
||||||
|
).to_string();
|
||||||
|
add_effect(None, EffectType::Sound { sound }, Targets::Tile { target: idx });
|
||||||
|
}
|
||||||
|
|
@ -33,8 +33,10 @@ use crate::{
|
||||||
KnownSpells,
|
KnownSpells,
|
||||||
Position,
|
Position,
|
||||||
Viewshed,
|
Viewshed,
|
||||||
|
WantsToRemoveKey,
|
||||||
|
WantsToDelete,
|
||||||
};
|
};
|
||||||
use crate::data::messages::*;
|
use crate::consts::messages::*;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
pub fn item_trigger(source: Option<Entity>, item: Entity, target: &Targets, ecs: &mut World) {
|
pub fn item_trigger(source: Option<Entity>, item: Entity, target: &Targets, ecs: &mut World) {
|
||||||
|
|
@ -57,7 +59,10 @@ pub fn item_trigger(source: Option<Entity>, item: Entity, target: &Targets, ecs:
|
||||||
let did_something = event_trigger(source, item, target, ecs);
|
let did_something = event_trigger(source, item, target, ecs);
|
||||||
// If it's a consumable, delete it
|
// If it's a consumable, delete it
|
||||||
if did_something && ecs.read_storage::<Consumable>().get(item).is_some() {
|
if did_something && ecs.read_storage::<Consumable>().get(item).is_some() {
|
||||||
ecs.entities().delete(item).expect("Failed to delete item");
|
let mut removekey = ecs.write_storage::<WantsToRemoveKey>();
|
||||||
|
removekey.insert(item, WantsToRemoveKey {}).expect("Unable to insert WantsToRemoveKey");
|
||||||
|
let mut delete = ecs.write_storage::<WantsToDelete>();
|
||||||
|
delete.insert(item, WantsToDelete {}).expect("Unable to insert WantsToDelete");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ impl Logger {
|
||||||
|
|
||||||
/// Pushes the finished log entry.
|
/// Pushes the finished log entry.
|
||||||
pub fn log(self) {
|
pub fn log(self) {
|
||||||
return append_entry(self.fragments);
|
let key = crate::gamelog::get_event_count(crate::consts::events::EVENT::COUNT_TURN);
|
||||||
|
return append_entry(key, self.fragments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::collections::{ HashSet, HashMap };
|
use std::collections::{ HashSet, HashMap };
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use crate::data::events::EVENT;
|
use crate::consts::events::EVENT;
|
||||||
use crate::data::names::*;
|
use crate::consts::names::*;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// A count of each event that has happened over the run. i.e. "turns", "descended", "ascended"
|
/// A count of each event that has happened over the run. i.e. "turns", "descended", "ascended"
|
||||||
|
|
@ -126,7 +126,7 @@ pub fn record_event(event: EVENT) {
|
||||||
new_event = format!("Discovered {}", name);
|
new_event = format!("Discovered {}", name);
|
||||||
}
|
}
|
||||||
EVENT::Identified(name) => {
|
EVENT::Identified(name) => {
|
||||||
new_event = format!("Identified {}", crate::gui::with_article(name));
|
new_event = format!("Identified {}", crate::gui::with_article(&name));
|
||||||
}
|
}
|
||||||
EVENT::PlayerDied(str) => {
|
EVENT::PlayerDied(str) => {
|
||||||
// Generating the String is handled in the death effect, to avoid passing the ecs here.
|
// Generating the String is handled in the death effect, to avoid passing the ecs here.
|
||||||
|
|
|
||||||
|
|
@ -1,91 +1,70 @@
|
||||||
use super::{ events, LogFragment, Logger };
|
use super::{ events, LogFragment, Logger };
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use notan::prelude::*;
|
||||||
|
use notan::text::CreateText;
|
||||||
|
use crate::consts::{ TILESIZE, FONTSIZE };
|
||||||
|
use crate::consts::visuals::VIEWPORT_W;
|
||||||
|
use crate::Fonts;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref LOG: Mutex<Vec<Vec<LogFragment>>> = Mutex::new(Vec::new());
|
pub static ref LOG: Mutex<BTreeMap<i32, Vec<LogFragment>>> = Mutex::new(BTreeMap::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
/// Render with specificied params.
|
||||||
pub fn append_fragment(fragment: LogFragment) {
|
pub fn render_log(
|
||||||
LOG.lock().unwrap().push(vec![fragment]);
|
draw: &RenderTexture,
|
||||||
|
gfx: &mut Graphics,
|
||||||
|
font: &Fonts,
|
||||||
|
pos: &(f32, f32),
|
||||||
|
width: f32,
|
||||||
|
entries: usize
|
||||||
|
) {
|
||||||
|
let mut text = gfx.create_text();
|
||||||
|
let log = LOG.lock().unwrap();
|
||||||
|
let latest: Vec<_> = log.iter().rev().take(entries).collect();
|
||||||
|
let mut initialised = false;
|
||||||
|
let mut y = pos.1;
|
||||||
|
for (_, entries) in latest {
|
||||||
|
let mut written_on_line = false;
|
||||||
|
for frag in entries.iter() {
|
||||||
|
if !written_on_line {
|
||||||
|
text.add(&frag.text)
|
||||||
|
.font(font.n())
|
||||||
|
.position(pos.0, y)
|
||||||
|
.size(FONTSIZE)
|
||||||
|
.max_width(width)
|
||||||
|
.color(Color::from_rgb(frag.colour.r, frag.colour.g, frag.colour.b))
|
||||||
|
.v_align_bottom();
|
||||||
|
written_on_line = true;
|
||||||
|
initialised = true;
|
||||||
|
} else {
|
||||||
|
text.chain(&frag.text)
|
||||||
|
.color(Color::from_rgb(frag.colour.r, frag.colour.g, frag.colour.b))
|
||||||
|
.size(FONTSIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if initialised {
|
||||||
|
y = text.last_bounds().min_y();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gfx.render_to(draw, &text);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn append_entry(fragments: Vec<LogFragment>) {
|
pub fn append_entry(turn: i32, fragments: Vec<LogFragment>) {
|
||||||
LOG.lock().unwrap().push(fragments);
|
let mut log = LOG.lock().unwrap();
|
||||||
|
if let Some(existing) = log.get_mut(&turn) {
|
||||||
|
existing.extend(fragments);
|
||||||
|
} else {
|
||||||
|
log.insert(turn, fragments);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_log() {
|
pub fn clear_log() {
|
||||||
LOG.lock().unwrap().clear();
|
LOG.lock().unwrap().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_log(
|
|
||||||
console: &mut Box<dyn Console>,
|
|
||||||
pos: Point,
|
|
||||||
_descending: bool,
|
|
||||||
len: usize,
|
|
||||||
maximum_len: i32
|
|
||||||
) {
|
|
||||||
let mut y = pos.y;
|
|
||||||
let mut x = pos.x;
|
|
||||||
// Reverse the log, take the number we want to show, and iterate through them
|
|
||||||
LOG.lock()
|
|
||||||
.unwrap()
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.take(len)
|
|
||||||
.for_each(|log| {
|
|
||||||
let mut entry_len = -2;
|
|
||||||
// Iterate through each message fragment, and get the total length
|
|
||||||
// in lines, by adding the length of every fragment and dividing it
|
|
||||||
// by the maximum length we desire. Then shuffle our start-y by that much.
|
|
||||||
log.iter().for_each(|frag| {
|
|
||||||
entry_len += frag.text.len() as i32;
|
|
||||||
});
|
|
||||||
let lines = entry_len / maximum_len;
|
|
||||||
y -= lines;
|
|
||||||
let mut i = 0;
|
|
||||||
log.iter().for_each(|frag| {
|
|
||||||
// Split every fragment up into single characters.
|
|
||||||
let parts = frag.text.split("");
|
|
||||||
for part in parts {
|
|
||||||
// This is an extremely hacky solution to a problem I don't understand yet.
|
|
||||||
// -- without this, the lines *here* and the line count *above* wont match.
|
|
||||||
if part == "" || part == "\\" {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if i > entry_len {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
if x + (part.len() as i32) > pos.x + maximum_len {
|
|
||||||
if y > pos.y - (len as i32) {
|
|
||||||
console.print(x, y, "-");
|
|
||||||
}
|
|
||||||
y += 1;
|
|
||||||
x = pos.x;
|
|
||||||
}
|
|
||||||
// Stay within bounds
|
|
||||||
if y > pos.y - (len as i32) {
|
|
||||||
console.print_color(
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
frag.colour.into(),
|
|
||||||
RGB::named(BLACK).into(),
|
|
||||||
part
|
|
||||||
);
|
|
||||||
}
|
|
||||||
x += part.len() as i32;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Take away one from the y-axis, because we want to start each entry
|
|
||||||
// on a new line, and go up an additional amount depending on how many
|
|
||||||
// lines our *previous* entry took.
|
|
||||||
y -= 1 + lines;
|
|
||||||
x = pos.x;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn setup_log() {
|
pub fn setup_log() {
|
||||||
clear_log();
|
clear_log();
|
||||||
events::clear_events();
|
events::clear_events();
|
||||||
|
|
@ -98,11 +77,11 @@ pub fn setup_log() {
|
||||||
.log();
|
.log();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clone_log() -> Vec<Vec<crate::gamelog::LogFragment>> {
|
pub fn clone_log() -> BTreeMap<i32, Vec<LogFragment>> {
|
||||||
return LOG.lock().unwrap().clone();
|
return LOG.lock().unwrap().clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore_log(log: &mut Vec<Vec<crate::gamelog::LogFragment>>) {
|
pub fn restore_log(log: &mut BTreeMap<i32, Vec<LogFragment>>) {
|
||||||
LOG.lock().unwrap().clear();
|
LOG.lock().unwrap().clear();
|
||||||
LOG.lock().unwrap().append(log);
|
LOG.lock().unwrap().append(log);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ mod builder;
|
||||||
pub use builder::*;
|
pub use builder::*;
|
||||||
mod logstore;
|
mod logstore;
|
||||||
use logstore::*;
|
use logstore::*;
|
||||||
pub use logstore::{ clear_log, clone_log, print_log, restore_log, setup_log };
|
pub use logstore::{ LOG, clear_log, clone_log, render_log, restore_log, setup_log };
|
||||||
mod events;
|
mod events;
|
||||||
pub use events::*;
|
pub use events::*;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use super::{ Skill, Skills };
|
use super::{ Skill, Skills };
|
||||||
use crate::gui::{ Ancestry, Class };
|
use crate::gui::{ Ancestry, Class };
|
||||||
use crate::data::entity;
|
use crate::consts::entity;
|
||||||
use crate::data::char_create::*;
|
use crate::consts::char_create::*;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
|
|
||||||
|
|
@ -85,6 +85,7 @@ pub fn get_attribute_rolls(
|
||||||
ancestry: Ancestry
|
ancestry: Ancestry
|
||||||
) -> (i32, i32, i32, i32, i32, i32) {
|
) -> (i32, i32, i32, i32, i32, i32) {
|
||||||
let (mut str, mut dex, mut con, mut int, mut wis, mut cha) = match class {
|
let (mut str, mut dex, mut con, mut int, mut wis, mut cha) = match class {
|
||||||
|
Class::Unset => VILLAGER_MIN_ATTR,
|
||||||
Class::Fighter => FIGHTER_MIN_ATTR,
|
Class::Fighter => FIGHTER_MIN_ATTR,
|
||||||
Class::Rogue => ROGUE_MIN_ATTR,
|
Class::Rogue => ROGUE_MIN_ATTR,
|
||||||
Class::Wizard => WIZARD_MIN_ATTR,
|
Class::Wizard => WIZARD_MIN_ATTR,
|
||||||
|
|
@ -92,6 +93,7 @@ pub fn get_attribute_rolls(
|
||||||
};
|
};
|
||||||
let mut remaining_points = TOTAL_ATTRIBUTE_POINTS_MAXIMUM - (str + dex + con + int + wis + cha);
|
let mut remaining_points = TOTAL_ATTRIBUTE_POINTS_MAXIMUM - (str + dex + con + int + wis + cha);
|
||||||
let improve_chance: [i32; 6] = match class {
|
let improve_chance: [i32; 6] = match class {
|
||||||
|
Class::Unset => VILLAGER_IMPR_CHANCE,
|
||||||
Class::Fighter => FIGHTER_IMPR_CHANCE,
|
Class::Fighter => FIGHTER_IMPR_CHANCE,
|
||||||
Class::Rogue => ROGUE_IMPR_CHANCE,
|
Class::Rogue => ROGUE_IMPR_CHANCE,
|
||||||
Class::Wizard => WIZARD_IMPR_CHANCE,
|
Class::Wizard => WIZARD_IMPR_CHANCE,
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ use super::{
|
||||||
RunState,
|
RunState,
|
||||||
State,
|
State,
|
||||||
};
|
};
|
||||||
use crate::data::entity;
|
use crate::consts::entity;
|
||||||
use crate::data::char_create::*;
|
use crate::consts::char_create::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
raws,
|
raws,
|
||||||
Attribute,
|
Attribute,
|
||||||
|
|
@ -28,10 +28,11 @@ use bracket_lib::prelude::*;
|
||||||
use serde::{ Deserialize, Serialize };
|
use serde::{ Deserialize, Serialize };
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use crate::consts::prelude::*;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum Ancestry {
|
pub enum Ancestry {
|
||||||
NULL,
|
Unset,
|
||||||
Human,
|
Human,
|
||||||
Dwarf,
|
Dwarf,
|
||||||
Gnome,
|
Gnome,
|
||||||
|
|
@ -39,8 +40,9 @@ pub enum Ancestry {
|
||||||
Catfolk,
|
Catfolk,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum Class {
|
pub enum Class {
|
||||||
|
Unset,
|
||||||
Fighter,
|
Fighter,
|
||||||
Rogue,
|
Rogue,
|
||||||
Wizard,
|
Wizard,
|
||||||
|
|
@ -48,48 +50,53 @@ pub enum Class {
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref ANCESTRY_CLASS_DATA: HashMap<String, Vec<String>> = {
|
static ref ANCESTRYDATA: HashMap<Ancestry, Vec<String>> = {
|
||||||
let mut m = HashMap::new();
|
let mut m = HashMap::new();
|
||||||
// Ancestry
|
|
||||||
m.insert(
|
m.insert(
|
||||||
"human".to_string(),
|
Ancestry::Human,
|
||||||
vec![
|
vec![
|
||||||
"nothing".to_string()]);
|
"nothing".to_string()]);
|
||||||
m.insert(
|
m.insert(
|
||||||
"dwarf".to_string(),
|
Ancestry::Dwarf,
|
||||||
vec![
|
vec![
|
||||||
"a natural bonus to defence".to_string()]);
|
"a natural bonus to defence".to_string()]);
|
||||||
m.insert(
|
m.insert(
|
||||||
"elf".to_string(),
|
Ancestry::Elf,
|
||||||
vec![
|
vec![
|
||||||
"minor telepathy".to_string(),
|
"minor telepathy".to_string(),
|
||||||
"a slightly increased speed".to_string()]);
|
"a slightly increased speed".to_string()]);
|
||||||
m.insert(
|
m.insert(
|
||||||
"catfolk".to_string(),
|
Ancestry::Catfolk,
|
||||||
vec![
|
vec![
|
||||||
"increased speed".to_string(),
|
"increased speed".to_string(),
|
||||||
"increased unarmed damage".to_string()]);
|
"increased unarmed damage".to_string()]);
|
||||||
// Class
|
return m;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref CLASSDATA: HashMap<Class, Vec<String>> = {
|
||||||
|
let mut m = HashMap::new();
|
||||||
m.insert(
|
m.insert(
|
||||||
"fighter".to_string(),
|
Class::Fighter,
|
||||||
vec![
|
vec![
|
||||||
format!("a longsword, ring mail, and {} food", FIGHTER_STARTING_FOOD),
|
format!("a longsword, ring mail, and {} food", FIGHTER_STARTING_FOOD),
|
||||||
"10 str, 8 dex, 10 con, 6 int, 6 wis, 8 cha".to_string(),
|
"10 str, 8 dex, 10 con, 6 int, 6 wis, 8 cha".to_string(),
|
||||||
"and 27 random stat points".to_string()]);
|
"and 27 random stat points".to_string()]);
|
||||||
m.insert(
|
m.insert(
|
||||||
"rogue".to_string(),
|
Class::Rogue,
|
||||||
vec![
|
vec![
|
||||||
format!("a rapier, leather armour, and {} food", ROGUE_STARTING_FOOD),
|
format!("a rapier, leather armour, and {} food", ROGUE_STARTING_FOOD),
|
||||||
"8 str, 10 dex, 8 con, 6 int, 8 wis, 10 cha".to_string(),
|
"8 str, 10 dex, 8 con, 6 int, 8 wis, 10 cha".to_string(),
|
||||||
"and 35 random stat points".to_string()]);
|
"and 35 random stat points".to_string()]);
|
||||||
m.insert(
|
m.insert(
|
||||||
"wizard".to_string(),
|
Class::Wizard,
|
||||||
vec![
|
vec![
|
||||||
format!("a dagger, random scrolls/potions, and {} food", WIZARD_STARTING_FOOD),
|
format!("a dagger, random scrolls/potions, and {} food", WIZARD_STARTING_FOOD),
|
||||||
"6 str, 8 dex, 6 con, 10 int, 10 wis, 8 cha".to_string(),
|
"6 str, 8 dex, 6 con, 10 int, 10 wis, 8 cha".to_string(),
|
||||||
"and 17 random stat points".to_string()]);
|
"and 17 random stat points".to_string()]);
|
||||||
m.insert(
|
m.insert(
|
||||||
"villager".to_string(),
|
Class::Villager,
|
||||||
vec![
|
vec![
|
||||||
format!("the first weapon you could find, and {} food", VILLAGER_STARTING_FOOD),
|
format!("the first weapon you could find, and {} food", VILLAGER_STARTING_FOOD),
|
||||||
"6 str, 6 dex, 6 con, 6 int, 6 wis, 6 cha".to_string(),
|
"6 str, 6 dex, 6 con, 6 int, 6 wis, 6 cha".to_string(),
|
||||||
|
|
@ -110,142 +117,123 @@ pub enum CharCreateResult {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use notan::prelude::*;
|
||||||
|
use notan::draw::{ Draw, DrawTextSection };
|
||||||
|
use super::{ FONTSIZE, TILESIZE };
|
||||||
|
use crate::consts::DISPLAYHEIGHT;
|
||||||
|
use crate::Fonts;
|
||||||
|
|
||||||
|
pub fn draw_charcreation(
|
||||||
|
ecs: &World,
|
||||||
|
draw: &mut Draw,
|
||||||
|
atlas: &HashMap<String, Texture>,
|
||||||
|
font: &Fonts
|
||||||
|
) {
|
||||||
|
let runstate = ecs.read_resource::<RunState>();
|
||||||
|
let (class, ancestry) = match *runstate {
|
||||||
|
RunState::CharacterCreation { class, ancestry } => (class, ancestry),
|
||||||
|
_ => unreachable!("draw_charcreation() called outside of CharacterCreation runstate."),
|
||||||
|
};
|
||||||
|
let (mut x, mut y) = (2.0 * TILESIZE.x, ((DISPLAYHEIGHT as f32) * TILESIZE.x) / 4.0);
|
||||||
|
const COLUMN_WIDTH: f32 = 20.0 * TILESIZE.x;
|
||||||
|
draw.text(&font.ib(), "Who are you?")
|
||||||
|
.size(FONTSIZE * 2.0)
|
||||||
|
.position(x, y)
|
||||||
|
.h_align_left();
|
||||||
|
y = draw.last_text_bounds().max_y();
|
||||||
|
let initial_y = y;
|
||||||
|
let ancestries = [
|
||||||
|
("h. Human", Ancestry::Human),
|
||||||
|
("e. Elf", Ancestry::Elf),
|
||||||
|
("d. Dwarf", Ancestry::Dwarf),
|
||||||
|
("c. Catfolk", Ancestry::Catfolk),
|
||||||
|
];
|
||||||
|
for (k, v) in &ancestries {
|
||||||
|
draw.text(font.n(), k)
|
||||||
|
.size(FONTSIZE)
|
||||||
|
.position(x, y)
|
||||||
|
.h_align_left()
|
||||||
|
.color(get_colour(ancestry, *v));
|
||||||
|
y = draw.last_text_bounds().max_y();
|
||||||
|
}
|
||||||
|
y = initial_y;
|
||||||
|
x += COLUMN_WIDTH;
|
||||||
|
let classes = [
|
||||||
|
("f. Fighter", Class::Fighter),
|
||||||
|
("r. Rogue", Class::Rogue),
|
||||||
|
("w. Wizard", Class::Wizard),
|
||||||
|
("v. Villager", Class::Villager),
|
||||||
|
];
|
||||||
|
for (k, v) in &classes {
|
||||||
|
draw.text(font.n(), k)
|
||||||
|
.size(FONTSIZE)
|
||||||
|
.position(x, y)
|
||||||
|
.h_align_left()
|
||||||
|
.color(get_colour(class, *v));
|
||||||
|
y = draw.last_text_bounds().max_y();
|
||||||
|
}
|
||||||
|
y = initial_y;
|
||||||
|
x += COLUMN_WIDTH;
|
||||||
|
for line in ANCESTRYDATA.get(&ancestry).unwrap().iter() {
|
||||||
|
draw.text(font.n(), line).size(FONTSIZE).position(x, y).h_align_left();
|
||||||
|
y = draw.last_text_bounds().max_y();
|
||||||
|
}
|
||||||
|
y += TILESIZE.x;
|
||||||
|
for line in CLASSDATA.get(&class).unwrap().iter() {
|
||||||
|
draw.text(font.n(), line).size(FONTSIZE).position(x, y).h_align_left();
|
||||||
|
y = draw.last_text_bounds().max_y();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_colour<T>(selected: T, desired: T) -> Color where T: PartialEq {
|
||||||
|
if selected == desired { Color::from_rgb(0.0, 1.0, 0.0) } else { Color::WHITE }
|
||||||
|
}
|
||||||
|
|
||||||
/// Handles the player character creation screen.
|
/// Handles the player character creation screen.
|
||||||
pub fn character_creation(gs: &mut State, ctx: &mut BTerm) -> CharCreateResult {
|
pub fn character_creation(gs: &mut State, ctx: &mut App) -> CharCreateResult {
|
||||||
let runstate = gs.ecs.fetch::<RunState>();
|
let runstate = gs.ecs.fetch::<RunState>();
|
||||||
|
let (ancestry, class) = match *runstate {
|
||||||
|
RunState::CharacterCreation { ancestry, class } => (ancestry, class),
|
||||||
|
_ => unreachable!("character_creation() called outside of CharacterCreation runstate."),
|
||||||
|
};
|
||||||
|
|
||||||
let mut x = 2;
|
let key = &ctx.keyboard;
|
||||||
let mut y = 11;
|
for keycode in key.pressed.iter() {
|
||||||
let column_width = 20;
|
match *keycode {
|
||||||
|
KeyCode::Escape => {
|
||||||
ctx.print_color(x, y, RGB::named(WHITE), RGB::named(BLACK), CHAR_CREATE_HEADER);
|
return CharCreateResult::Selected { ancestry: Ancestry::Unset, class };
|
||||||
y += 2;
|
|
||||||
|
|
||||||
if let RunState::CharacterCreation { ancestry, class } = *runstate {
|
|
||||||
let selected_fg = RGB::named(GREEN);
|
|
||||||
let unselected_fg = RGB::named(WHITE);
|
|
||||||
let mut fg;
|
|
||||||
let bg = RGB::named(BLACK);
|
|
||||||
|
|
||||||
// Ancestry
|
|
||||||
ctx.print_color(x, y, bg, unselected_fg, "Ancestry");
|
|
||||||
ctx.print_color(x + column_width, y, bg, unselected_fg, "Class");
|
|
||||||
y += 1;
|
|
||||||
let mut race_str = "human";
|
|
||||||
if ancestry == Ancestry::Human {
|
|
||||||
fg = selected_fg;
|
|
||||||
} else {
|
|
||||||
fg = unselected_fg;
|
|
||||||
}
|
}
|
||||||
ctx.print_color(x, y, fg, bg, "h. Human");
|
KeyCode::Return => {
|
||||||
if ancestry == Ancestry::Elf {
|
|
||||||
fg = selected_fg;
|
|
||||||
race_str = "elf";
|
|
||||||
} else {
|
|
||||||
fg = unselected_fg;
|
|
||||||
}
|
|
||||||
ctx.print_color(x, y + 1, fg, bg, "e. Elf");
|
|
||||||
if ancestry == Ancestry::Dwarf {
|
|
||||||
fg = selected_fg;
|
|
||||||
race_str = "dwarf";
|
|
||||||
} else {
|
|
||||||
fg = unselected_fg;
|
|
||||||
}
|
|
||||||
ctx.print_color(x, y + 2, fg, bg, "d. Dwarf");
|
|
||||||
if ancestry == Ancestry::Catfolk {
|
|
||||||
fg = selected_fg;
|
|
||||||
race_str = "catfolk";
|
|
||||||
} else {
|
|
||||||
fg = unselected_fg;
|
|
||||||
}
|
|
||||||
ctx.print_color(x, y + 3, fg, bg, "c. Catfolk");
|
|
||||||
// Class
|
|
||||||
let mut class_str = "fighter";
|
|
||||||
x += column_width;
|
|
||||||
if class == Class::Fighter {
|
|
||||||
fg = selected_fg;
|
|
||||||
} else {
|
|
||||||
fg = unselected_fg;
|
|
||||||
}
|
|
||||||
ctx.print_color(x, y, fg, bg, "f. Fighter");
|
|
||||||
if class == Class::Rogue {
|
|
||||||
fg = selected_fg;
|
|
||||||
class_str = "rogue";
|
|
||||||
} else {
|
|
||||||
fg = unselected_fg;
|
|
||||||
}
|
|
||||||
ctx.print_color(x, y + 1, fg, bg, "r. Rogue");
|
|
||||||
if class == Class::Wizard {
|
|
||||||
fg = selected_fg;
|
|
||||||
class_str = "wizard";
|
|
||||||
} else {
|
|
||||||
fg = unselected_fg;
|
|
||||||
}
|
|
||||||
ctx.print_color(x, y + 2, fg, bg, "w. Wizard");
|
|
||||||
if class == Class::Villager {
|
|
||||||
fg = selected_fg;
|
|
||||||
class_str = "villager";
|
|
||||||
} else {
|
|
||||||
fg = unselected_fg;
|
|
||||||
}
|
|
||||||
ctx.print_color(x, y + 3, fg, bg, "v. Villager");
|
|
||||||
// Selected ancestry/class benefits
|
|
||||||
x += column_width;
|
|
||||||
ctx.print_color(x, y, selected_fg, bg, ANCESTRY_INFO_HEADER);
|
|
||||||
for line in ANCESTRY_CLASS_DATA.get(race_str).unwrap().iter() {
|
|
||||||
y += 1;
|
|
||||||
ctx.print_color(x + 1, y, unselected_fg, bg, line);
|
|
||||||
}
|
|
||||||
y += 2;
|
|
||||||
ctx.print_color(x, y, selected_fg, bg, CLASS_INFO_HEADER);
|
|
||||||
for line in ANCESTRY_CLASS_DATA.get(class_str).unwrap().iter() {
|
|
||||||
y += 1;
|
|
||||||
ctx.print_color(x + 1, y, unselected_fg, bg, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
match ctx.key {
|
|
||||||
None => {
|
|
||||||
return CharCreateResult::NoSelection { ancestry, class };
|
|
||||||
}
|
|
||||||
Some(key) =>
|
|
||||||
match key {
|
|
||||||
VirtualKeyCode::Escape => {
|
|
||||||
return CharCreateResult::Selected { ancestry: Ancestry::NULL, class };
|
|
||||||
}
|
|
||||||
VirtualKeyCode::Return => {
|
|
||||||
return CharCreateResult::Selected { ancestry, class };
|
return CharCreateResult::Selected { ancestry, class };
|
||||||
}
|
}
|
||||||
VirtualKeyCode::H => {
|
KeyCode::H => {
|
||||||
return CharCreateResult::NoSelection { ancestry: Ancestry::Human, class };
|
return CharCreateResult::NoSelection { ancestry: Ancestry::Human, class };
|
||||||
}
|
}
|
||||||
VirtualKeyCode::E => {
|
KeyCode::E => {
|
||||||
return CharCreateResult::NoSelection { ancestry: Ancestry::Elf, class };
|
return CharCreateResult::NoSelection { ancestry: Ancestry::Elf, class };
|
||||||
}
|
}
|
||||||
VirtualKeyCode::D => {
|
KeyCode::D => {
|
||||||
return CharCreateResult::NoSelection { ancestry: Ancestry::Dwarf, class };
|
return CharCreateResult::NoSelection { ancestry: Ancestry::Dwarf, class };
|
||||||
}
|
}
|
||||||
VirtualKeyCode::C => {
|
KeyCode::C => {
|
||||||
return CharCreateResult::NoSelection { ancestry: Ancestry::Catfolk, class };
|
return CharCreateResult::NoSelection { ancestry: Ancestry::Catfolk, class };
|
||||||
}
|
}
|
||||||
VirtualKeyCode::F => {
|
KeyCode::F => {
|
||||||
return CharCreateResult::NoSelection { ancestry, class: Class::Fighter };
|
return CharCreateResult::NoSelection { ancestry, class: Class::Fighter };
|
||||||
}
|
}
|
||||||
VirtualKeyCode::R => {
|
KeyCode::R => {
|
||||||
return CharCreateResult::NoSelection { ancestry, class: Class::Rogue };
|
return CharCreateResult::NoSelection { ancestry, class: Class::Rogue };
|
||||||
}
|
}
|
||||||
VirtualKeyCode::W => {
|
KeyCode::W => {
|
||||||
return CharCreateResult::NoSelection { ancestry, class: Class::Wizard };
|
return CharCreateResult::NoSelection { ancestry, class: Class::Wizard };
|
||||||
}
|
}
|
||||||
VirtualKeyCode::V => {
|
KeyCode::V => {
|
||||||
return CharCreateResult::NoSelection { ancestry, class: Class::Villager };
|
return CharCreateResult::NoSelection { ancestry, class: Class::Villager };
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
return CharCreateResult::NoSelection { ancestry, class };
|
return CharCreateResult::NoSelection { ancestry, class };
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return CharCreateResult::NoSelection { ancestry: Ancestry::Human, class: Class::Fighter };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles player ancestry setup.
|
/// Handles player ancestry setup.
|
||||||
|
|
@ -267,25 +255,9 @@ pub fn setup_player_ancestry(ecs: &mut World, ancestry: Ancestry) {
|
||||||
match ancestry {
|
match ancestry {
|
||||||
Ancestry::Human => {}
|
Ancestry::Human => {}
|
||||||
Ancestry::Dwarf => {
|
Ancestry::Dwarf => {
|
||||||
renderables
|
|
||||||
.insert(*player, Renderable {
|
|
||||||
glyph: to_cp437(DWARF_GLYPH),
|
|
||||||
fg: RGB::named(DWARF_COLOUR),
|
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
render_order: 0,
|
|
||||||
})
|
|
||||||
.expect("Unable to insert renderable component");
|
|
||||||
*player_skills.skills.entry(Skill::Defence).or_insert(0) += DWARF_DEFENCE_MOD;
|
*player_skills.skills.entry(Skill::Defence).or_insert(0) += DWARF_DEFENCE_MOD;
|
||||||
}
|
}
|
||||||
Ancestry::Elf => {
|
Ancestry::Elf => {
|
||||||
renderables
|
|
||||||
.insert(*player, Renderable {
|
|
||||||
glyph: to_cp437(ELF_GLYPH),
|
|
||||||
fg: RGB::named(ELF_COLOUR),
|
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
render_order: 0,
|
|
||||||
})
|
|
||||||
.expect("Unable to insert renderable component");
|
|
||||||
let mut telepaths = ecs.write_storage::<Telepath>();
|
let mut telepaths = ecs.write_storage::<Telepath>();
|
||||||
telepaths
|
telepaths
|
||||||
.insert(*player, Telepath {
|
.insert(*player, Telepath {
|
||||||
|
|
@ -303,14 +275,6 @@ pub fn setup_player_ancestry(ecs: &mut World, ancestry: Ancestry) {
|
||||||
.expect("Unable to insert energy component");
|
.expect("Unable to insert energy component");
|
||||||
}
|
}
|
||||||
Ancestry::Catfolk => {
|
Ancestry::Catfolk => {
|
||||||
renderables
|
|
||||||
.insert(*player, Renderable {
|
|
||||||
glyph: to_cp437(CATFOLK_GLYPH),
|
|
||||||
fg: RGB::named(CATFOLK_COLOUR),
|
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
render_order: 0,
|
|
||||||
})
|
|
||||||
.expect("Unable to insert renderable component");
|
|
||||||
let mut speeds = ecs.write_storage::<Energy>();
|
let mut speeds = ecs.write_storage::<Energy>();
|
||||||
speeds
|
speeds
|
||||||
.insert(*player, Energy {
|
.insert(*player, Energy {
|
||||||
|
|
@ -342,14 +306,7 @@ pub fn setup_player_class(ecs: &mut World, class: Class, ancestry: Ancestry) {
|
||||||
let mut attributes = ecs.write_storage::<Attributes>();
|
let mut attributes = ecs.write_storage::<Attributes>();
|
||||||
let (str, dex, con, int, wis, cha) = get_attribute_rolls(&mut rng, class, ancestry);
|
let (str, dex, con, int, wis, cha) = get_attribute_rolls(&mut rng, class, ancestry);
|
||||||
attributes
|
attributes
|
||||||
.insert(player, Attributes {
|
.insert(player, Attributes::with_stats(str, dex, con, int, wis, cha))
|
||||||
strength: Attribute { base: str, modifiers: 0, bonus: attr_bonus(str) },
|
|
||||||
dexterity: Attribute { base: dex, modifiers: 0, bonus: attr_bonus(dex) },
|
|
||||||
constitution: Attribute { base: con, modifiers: 0, bonus: attr_bonus(con) },
|
|
||||||
intelligence: Attribute { base: int, modifiers: 0, bonus: attr_bonus(int) },
|
|
||||||
wisdom: Attribute { base: wis, modifiers: 0, bonus: attr_bonus(wis) },
|
|
||||||
charisma: Attribute { base: cha, modifiers: 0, bonus: attr_bonus(cha) },
|
|
||||||
})
|
|
||||||
.expect("Unable to insert attributes component");
|
.expect("Unable to insert attributes component");
|
||||||
|
|
||||||
let mut pools = ecs.write_storage::<Pools>();
|
let mut pools = ecs.write_storage::<Pools>();
|
||||||
|
|
@ -407,6 +364,9 @@ fn get_starting_inventory(
|
||||||
let mut carried: Vec<String> = Vec::new();
|
let mut carried: Vec<String> = Vec::new();
|
||||||
let starting_food: &str;
|
let starting_food: &str;
|
||||||
match class {
|
match class {
|
||||||
|
Class::Unset => {
|
||||||
|
starting_food = VILLAGER_STARTING_FOOD;
|
||||||
|
}
|
||||||
Class::Fighter => {
|
Class::Fighter => {
|
||||||
starting_food = FIGHTER_STARTING_FOOD;
|
starting_food = FIGHTER_STARTING_FOOD;
|
||||||
equipped = vec![
|
equipped = vec![
|
||||||
|
|
@ -440,7 +400,7 @@ fn get_starting_inventory(
|
||||||
}
|
}
|
||||||
Class::Villager => {
|
Class::Villager => {
|
||||||
starting_food = VILLAGER_STARTING_FOOD;
|
starting_food = VILLAGER_STARTING_FOOD;
|
||||||
pick_random_table_item(rng, &mut equipped, "villager_equipment", "1", None);
|
pick_random_table_item(rng, &mut equipped, "villager_equipment", "1d1", None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pick_random_table_item(rng, &mut carried, "food", starting_food, None);
|
pick_random_table_item(rng, &mut carried, "food", starting_food, None);
|
||||||
|
|
@ -454,7 +414,9 @@ fn pick_random_table_item(
|
||||||
dice_str: &'static str,
|
dice_str: &'static str,
|
||||||
difficulty: Option<i32>
|
difficulty: Option<i32>
|
||||||
) {
|
) {
|
||||||
let dice = parse_dice_string(dice_str).expect("Error parsing dice");
|
let dice = parse_dice_string(dice_str).expect(
|
||||||
|
format!("Error parsing dice: {}", dice_str).as_str()
|
||||||
|
);
|
||||||
for _i in 0..rng.roll_dice(dice.n_dice, dice.die_type) + dice.bonus {
|
for _i in 0..rng.roll_dice(dice.n_dice, dice.die_type) + dice.bonus {
|
||||||
push_to.push(raws::table_by_name(&raws::RAWS.lock().unwrap(), table, difficulty).roll(rng));
|
push_to.push(raws::table_by_name(&raws::RAWS.lock().unwrap(), table, difficulty).roll(rng));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
use super::State;
|
use super::{ State };
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
|
use notan::prelude::*;
|
||||||
|
use notan::draw::DrawTextSection;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::consts::{ TILESIZE, FONTSIZE };
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
pub enum CheatMenuResult {
|
pub enum CheatMenuResult {
|
||||||
|
|
@ -12,53 +16,50 @@ pub enum CheatMenuResult {
|
||||||
GodMode,
|
GodMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_cheat_menu(_gs: &mut State, ctx: &mut BTerm) -> CheatMenuResult {
|
pub fn show_cheat_menu(_gs: &mut State, ctx: &mut App) -> CheatMenuResult {
|
||||||
let (x_offset, y_offset) = (1, 10);
|
let key = &ctx.keyboard;
|
||||||
ctx.print_color(
|
for keycode in key.pressed.iter() {
|
||||||
1 + x_offset,
|
match *keycode {
|
||||||
1 + y_offset,
|
KeyCode::A => {
|
||||||
RGB::named(RED),
|
return CheatMenuResult::Ascend;
|
||||||
RGB::named(BLACK),
|
|
||||||
"DEBUG MENU! [aA-zZ][Esc.]"
|
|
||||||
);
|
|
||||||
let x = 1 + x_offset;
|
|
||||||
let mut y = 3 + y_offset;
|
|
||||||
let count = 5;
|
|
||||||
let width = 19;
|
|
||||||
|
|
||||||
ctx.draw_box(x, y, width, (count + 1) as i32, RGB::named(RED), RGB::named(BLACK));
|
|
||||||
y += 1;
|
|
||||||
// Asc
|
|
||||||
ctx.set(x_offset + 2, y, RGB::named(YELLOW), RGB::named(BLACK), to_cp437('a'));
|
|
||||||
ctx.print(x_offset + 4, y, "ASCEND A FLOOR");
|
|
||||||
y += 1;
|
|
||||||
// Desc
|
|
||||||
ctx.set(x_offset + 2, y, RGB::named(YELLOW), RGB::named(BLACK), to_cp437('d'));
|
|
||||||
ctx.print(x_offset + 4, y, "DESCEND A FLOOR");
|
|
||||||
y += 1;
|
|
||||||
// Heal
|
|
||||||
ctx.set(x_offset + 2, y, RGB::named(YELLOW), RGB::named(BLACK), to_cp437('h'));
|
|
||||||
ctx.print(x_offset + 4, y, "HEAL TO FULL");
|
|
||||||
y += 1;
|
|
||||||
// Reveal map
|
|
||||||
ctx.set(x_offset + 2, y, RGB::named(YELLOW), RGB::named(BLACK), to_cp437('m'));
|
|
||||||
ctx.print(x_offset + 4, y, "MAGIC MAP REVEAL");
|
|
||||||
y += 1;
|
|
||||||
// Godmode
|
|
||||||
ctx.set(x_offset + 2, y, RGB::named(YELLOW), RGB::named(BLACK), to_cp437('g'));
|
|
||||||
ctx.print(x_offset + 4, y, "GOD MODE");
|
|
||||||
// Match keys
|
|
||||||
match ctx.key {
|
|
||||||
None => CheatMenuResult::NoResponse,
|
|
||||||
Some(key) =>
|
|
||||||
match key {
|
|
||||||
VirtualKeyCode::A => CheatMenuResult::Ascend,
|
|
||||||
VirtualKeyCode::D => CheatMenuResult::Descend,
|
|
||||||
VirtualKeyCode::H => CheatMenuResult::Heal,
|
|
||||||
VirtualKeyCode::M => CheatMenuResult::MagicMap,
|
|
||||||
VirtualKeyCode::G => CheatMenuResult::GodMode,
|
|
||||||
VirtualKeyCode::Escape => CheatMenuResult::Cancel,
|
|
||||||
_ => CheatMenuResult::NoResponse,
|
|
||||||
}
|
}
|
||||||
|
KeyCode::D => {
|
||||||
|
return CheatMenuResult::Descend;
|
||||||
}
|
}
|
||||||
|
KeyCode::H => {
|
||||||
|
return CheatMenuResult::Heal;
|
||||||
|
}
|
||||||
|
KeyCode::M => {
|
||||||
|
return CheatMenuResult::MagicMap;
|
||||||
|
}
|
||||||
|
KeyCode::G => {
|
||||||
|
return CheatMenuResult::GodMode;
|
||||||
|
}
|
||||||
|
KeyCode::Escape => {
|
||||||
|
return CheatMenuResult::Cancel;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return CheatMenuResult::NoResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_cheat_menu(
|
||||||
|
draw: &mut notan::draw::Draw,
|
||||||
|
_atlas: &HashMap<String, Texture>,
|
||||||
|
font: &crate::Fonts
|
||||||
|
) {
|
||||||
|
let offsets = crate::camera::get_offset();
|
||||||
|
const DEBUG_MENU: &str =
|
||||||
|
r#"DEBUG MENU! [aA-zZ][Esc.]
|
||||||
|
|
||||||
|
a - ASCEND A FLOOR
|
||||||
|
d - DESCEND A FLOOR
|
||||||
|
h - HEAL TO FULL
|
||||||
|
m - MAGIC MAP REVEAL
|
||||||
|
g - GOD MODE"#;
|
||||||
|
draw.text(&font.n(), DEBUG_MENU)
|
||||||
|
.position((1.0 + (offsets.x as f32)) * TILESIZE.x, (1.0 + (offsets.y as f32)) * TILESIZE.x)
|
||||||
|
.color(Color::RED)
|
||||||
|
.size(FONTSIZE);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
use super::{ State, RunState, tooltip::draw_tooltips, camera::get_screen_bounds };
|
use super::{ State, RunState, World, tooltip::draw_tooltips, camera::get_offset };
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
|
use notan::prelude::*;
|
||||||
|
use notan::draw::{ Draw, DrawImages };
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::consts::visuals::{ TILES_IN_VIEWPORT_H, TILES_IN_VIEWPORT_W };
|
||||||
|
use crate::consts::TILESIZE;
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
pub enum FarlookResult {
|
pub enum FarlookResult {
|
||||||
|
|
@ -10,45 +15,62 @@ pub enum FarlookResult {
|
||||||
Cancel,
|
Cancel,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_farlook(gs: &mut State, ctx: &mut BTerm) -> FarlookResult {
|
pub fn show_farlook(gs: &mut State, ctx: &mut App) -> FarlookResult {
|
||||||
let runstate = gs.ecs.fetch::<RunState>();
|
let runstate = gs.ecs.fetch::<RunState>();
|
||||||
let (_min_x, _max_x, _min_y, _max_y, x_offset, y_offset) = get_screen_bounds(&gs.ecs, ctx);
|
|
||||||
|
|
||||||
ctx.print_color(
|
|
||||||
1 + x_offset,
|
|
||||||
1 + y_offset,
|
|
||||||
RGB::named(WHITE),
|
|
||||||
RGB::named(BLACK),
|
|
||||||
"Look at what? [move keys][Esc.]"
|
|
||||||
);
|
|
||||||
|
|
||||||
if let RunState::Farlook { x, y } = *runstate {
|
if let RunState::Farlook { x, y } = *runstate {
|
||||||
let (screen_x, screen_y) = (69, 41);
|
let x = x.clamp(0, TILES_IN_VIEWPORT_W - 1);
|
||||||
let x = x.clamp(x_offset, x_offset - 1 + (screen_x as i32));
|
let y = y.clamp(0, TILES_IN_VIEWPORT_H - 1);
|
||||||
let y = y.clamp(y_offset, y_offset - 1 + (screen_y as i32));
|
let key = &ctx.keyboard;
|
||||||
|
// Movement
|
||||||
ctx.set(x, y, RGB::named(WHITE), RGB::named(BLACK), to_cp437('X'));
|
for keycode in key.pressed.iter() {
|
||||||
draw_tooltips(&gs.ecs, ctx, Some((x, y)));
|
match *keycode {
|
||||||
|
KeyCode::Escape | KeyCode::X => {
|
||||||
return match ctx.key {
|
return FarlookResult::Cancel;
|
||||||
None => FarlookResult::NoResponse { x, y },
|
|
||||||
Some(key) =>
|
|
||||||
match key {
|
|
||||||
VirtualKeyCode::Escape | VirtualKeyCode::X => FarlookResult::Cancel,
|
|
||||||
VirtualKeyCode::Numpad9 => FarlookResult::NoResponse { x: x + 1, y: y - 1 },
|
|
||||||
VirtualKeyCode::Numpad8 => FarlookResult::NoResponse { x, y: y - 1 },
|
|
||||||
VirtualKeyCode::Numpad7 => FarlookResult::NoResponse { x: x - 1, y: y - 1 },
|
|
||||||
VirtualKeyCode::Numpad6 => FarlookResult::NoResponse { x: x + 1, y },
|
|
||||||
VirtualKeyCode::Numpad4 => FarlookResult::NoResponse { x: x - 1, y },
|
|
||||||
VirtualKeyCode::Numpad3 => FarlookResult::NoResponse { x: x + 1, y: y + 1 },
|
|
||||||
VirtualKeyCode::Numpad2 => FarlookResult::NoResponse { x, y: y + 1 },
|
|
||||||
VirtualKeyCode::Numpad1 => FarlookResult::NoResponse { x: x - 1, y: y + 1 },
|
|
||||||
_ => FarlookResult::NoResponse { x, y },
|
|
||||||
}
|
}
|
||||||
};
|
KeyCode::Numpad1 => {
|
||||||
|
return FarlookResult::NoResponse { x: x - 1, y: y + 1 };
|
||||||
|
}
|
||||||
|
KeyCode::Numpad2 => {
|
||||||
|
return FarlookResult::NoResponse { x, y: y + 1 };
|
||||||
|
}
|
||||||
|
KeyCode::Numpad3 => {
|
||||||
|
return FarlookResult::NoResponse { x: x + 1, y: y + 1 };
|
||||||
|
}
|
||||||
|
KeyCode::Numpad4 => {
|
||||||
|
return FarlookResult::NoResponse { x: x - 1, y };
|
||||||
|
}
|
||||||
|
KeyCode::Numpad6 => {
|
||||||
|
return FarlookResult::NoResponse { x: x + 1, y };
|
||||||
|
}
|
||||||
|
KeyCode::Numpad7 => {
|
||||||
|
return FarlookResult::NoResponse { x: x - 1, y: y - 1 };
|
||||||
|
}
|
||||||
|
KeyCode::Numpad8 => {
|
||||||
|
return FarlookResult::NoResponse { x, y: y - 1 };
|
||||||
|
}
|
||||||
|
KeyCode::Numpad9 => {
|
||||||
|
return FarlookResult::NoResponse { x: x + 1, y: y - 1 };
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FarlookResult::NoResponse { x, y };
|
||||||
} else {
|
} else {
|
||||||
let ppos = gs.ecs.fetch::<Point>();
|
return FarlookResult::NoResponse { x: TILES_IN_VIEWPORT_W / 2, y: TILES_IN_VIEWPORT_H / 2 };
|
||||||
// TODO: PPOS + offsets (should get these from screen_bounds())
|
|
||||||
return FarlookResult::NoResponse { x: ppos.x + x_offset, y: ppos.x + y_offset };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn draw_farlook(
|
||||||
|
ecs: &World,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
draw: &mut Draw,
|
||||||
|
atlas: &HashMap<String, Texture>
|
||||||
|
) {
|
||||||
|
let placement = super::viewport_to_px(x, y);
|
||||||
|
draw.image(atlas.get("select1").unwrap())
|
||||||
|
.position(placement.x, placement.y)
|
||||||
|
.size(TILESIZE.sprite_x, TILESIZE.sprite_y);
|
||||||
|
let _idx = super::viewport_to_idx(ecs, x, y);
|
||||||
|
// Get tooltip for idx, etc.
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,12 @@ use super::{
|
||||||
item_colour_ecs,
|
item_colour_ecs,
|
||||||
obfuscate_name_ecs,
|
obfuscate_name_ecs,
|
||||||
print_options,
|
print_options,
|
||||||
|
unique_ecs,
|
||||||
renderable_colour,
|
renderable_colour,
|
||||||
ItemMenuResult,
|
ItemMenuResult,
|
||||||
UniqueInventoryItem,
|
UniqueInventoryItem,
|
||||||
BUC,
|
BUC,
|
||||||
|
Key,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
gamelog,
|
gamelog,
|
||||||
|
|
@ -23,7 +25,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// Handles the Identify menu.
|
/// Handles the Identify menu.
|
||||||
pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Entity>) {
|
pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Entity>) {
|
||||||
|
|
@ -37,9 +39,12 @@ pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Enti
|
||||||
let names = gs.ecs.read_storage::<Name>();
|
let names = gs.ecs.read_storage::<Name>();
|
||||||
let renderables = gs.ecs.read_storage::<Renderable>();
|
let renderables = gs.ecs.read_storage::<Renderable>();
|
||||||
let beatitudes = gs.ecs.read_storage::<Beatitude>();
|
let beatitudes = gs.ecs.read_storage::<Beatitude>();
|
||||||
|
let keys = gs.ecs.read_storage::<Key>();
|
||||||
|
|
||||||
let build_identify_iterator = || {
|
let build_identify_iterator = || {
|
||||||
(&entities, &items, &renderables, &names).join().filter(|(item_entity, _i, _r, n)| {
|
(&entities, &items, &renderables, &names, &keys)
|
||||||
|
.join()
|
||||||
|
.filter(|(item_entity, _i, _r, n, _k)| {
|
||||||
// If not owned by the player, return false.
|
// If not owned by the player, return false.
|
||||||
let mut keep = false;
|
let mut keep = false;
|
||||||
if let Some(bp) = backpack.get(*item_entity) {
|
if let Some(bp) = backpack.get(*item_entity) {
|
||||||
|
|
@ -91,51 +96,32 @@ pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Enti
|
||||||
.log();
|
.log();
|
||||||
return (ItemMenuResult::Selected, Some(build_identify_iterator().nth(0).unwrap().0));
|
return (ItemMenuResult::Selected, Some(build_identify_iterator().nth(0).unwrap().0));
|
||||||
}
|
}
|
||||||
let mut player_inventory: super::PlayerInventory = BTreeMap::new();
|
let mut player_inventory: super::PlayerInventory = HashMap::new();
|
||||||
for (entity, _i, renderable, name) in build_identify_iterator() {
|
for (entity, _i, renderable, name, key) in build_identify_iterator() {
|
||||||
let (singular, plural) = obfuscate_name_ecs(&gs.ecs, entity);
|
let unique_item = unique_ecs(&gs.ecs, entity);
|
||||||
let beatitude_status = if
|
|
||||||
let Some(beatitude) = gs.ecs.read_storage::<Beatitude>().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
|
player_inventory
|
||||||
.entry(unique_item)
|
.entry(unique_item)
|
||||||
.and_modify(|(_e, count)| {
|
.and_modify(|slot| {
|
||||||
*count += 1;
|
slot.count += 1;
|
||||||
})
|
})
|
||||||
.or_insert((entity, 1));
|
.or_insert(super::InventorySlot { item: entity, count: 1, idx: key.idx });
|
||||||
}
|
}
|
||||||
// Get display args
|
// Get display args
|
||||||
let width = get_max_inventory_width(&player_inventory);
|
let width = get_max_inventory_width(&player_inventory);
|
||||||
let (_, _, _, _, x_offset, y_offset) = crate::camera::get_screen_bounds(&gs.ecs, ctx);
|
let offsets = crate::camera::get_offset();
|
||||||
let (x, y) = (x_offset + 1, y_offset + 3);
|
let (x, y) = (offsets.x + 1, offsets.y + 3);
|
||||||
// Draw menu
|
// Draw menu
|
||||||
ctx.print_color(
|
ctx.print_color(
|
||||||
1 + x_offset,
|
1 + offsets.x,
|
||||||
1 + y_offset,
|
1 + offsets.y,
|
||||||
RGB::named(WHITE),
|
RGB::named(WHITE),
|
||||||
RGB::named(BLACK),
|
RGB::named(BLACK),
|
||||||
"Identify which item? [aA-zZ][Esc.]"
|
"Identify which item? [aA-zZ][Esc.]"
|
||||||
);
|
);
|
||||||
ctx.draw_box(x, y, width + 2, count + 1, RGB::named(WHITE), RGB::named(BLACK));
|
ctx.draw_box(x, y, width + 2, count + 1, RGB::named(WHITE), RGB::named(BLACK));
|
||||||
print_options(&player_inventory, x + 1, y + 1, ctx);
|
|
||||||
// Input
|
// Input
|
||||||
match ctx.key {
|
/*match ctx.key {
|
||||||
None => (ItemMenuResult::NoResponse, None),
|
None => (ItemMenuResult::NoResponse, None),
|
||||||
Some(key) =>
|
Some(key) =>
|
||||||
match key {
|
match key {
|
||||||
|
|
@ -161,4 +147,6 @@ pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Enti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
(ItemMenuResult::NoResponse, None)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
76
src/gui/inventory.rs
Normal file
76
src/gui/inventory.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
use notan::prelude::*;
|
||||||
|
use notan::draw::{ Draw, Font, DrawTextSection };
|
||||||
|
use specs::prelude::*;
|
||||||
|
use super::TILESIZE;
|
||||||
|
use crate::{ Fonts, camera::get_offset };
|
||||||
|
use super::{ items, Filter, print_options, ItemType, FONTSIZE };
|
||||||
|
|
||||||
|
pub enum Location {
|
||||||
|
All,
|
||||||
|
Backpack,
|
||||||
|
Equipped,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_itemtypes() -> Vec<ItemType> {
|
||||||
|
vec![
|
||||||
|
ItemType::Weapon,
|
||||||
|
ItemType::Armour,
|
||||||
|
ItemType::Comestible,
|
||||||
|
ItemType::Potion,
|
||||||
|
ItemType::Scroll,
|
||||||
|
ItemType::Spellbook,
|
||||||
|
ItemType::Wand,
|
||||||
|
ItemType::Amulet,
|
||||||
|
ItemType::Ring
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_items(
|
||||||
|
ecs: &World,
|
||||||
|
draw: &mut Draw,
|
||||||
|
font: &Fonts,
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
loc: Location,
|
||||||
|
itemtypes: Option<Vec<ItemType>>
|
||||||
|
) {
|
||||||
|
let mut y = y;
|
||||||
|
if let Some(itemtypes) = itemtypes {
|
||||||
|
for itemtype in itemtypes {
|
||||||
|
let filter = match loc {
|
||||||
|
Location::All => Filter::All(Some(itemtype)),
|
||||||
|
Location::Backpack => Filter::Backpack(Some(itemtype)),
|
||||||
|
Location::Equipped => Filter::Equipped,
|
||||||
|
};
|
||||||
|
let inv = items(ecs, filter);
|
||||||
|
if inv.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
draw.text(&font.b(), itemtype.string())
|
||||||
|
.position(x, y)
|
||||||
|
.color(Color::WHITE)
|
||||||
|
.size(FONTSIZE);
|
||||||
|
y += TILESIZE.x;
|
||||||
|
y = print_options(ecs, draw, font, &inv, x, y) + TILESIZE.x;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let filter = match loc {
|
||||||
|
Location::All => Filter::All(None),
|
||||||
|
Location::Backpack => Filter::Backpack(None),
|
||||||
|
Location::Equipped => Filter::Equipped,
|
||||||
|
};
|
||||||
|
let inv = items(ecs, filter);
|
||||||
|
if inv.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
y = print_options(ecs, draw, font, &inv, x, y) + TILESIZE.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_all_items(ecs: &World, draw: &mut Draw, font: &Fonts, x: f32, y: f32) {
|
||||||
|
draw_items(ecs, draw, font, x, y, Location::All, Some(all_itemtypes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_backpack_items(ecs: &World, draw: &mut Draw, font: &Fonts, x: f32, y: f32) {
|
||||||
|
draw_items(ecs, draw, font, x, y, Location::Backpack, Some(all_itemtypes()));
|
||||||
|
}
|
||||||
|
|
@ -1,65 +1,65 @@
|
||||||
use bracket_lib::prelude::*;
|
use notan::prelude::*;
|
||||||
|
|
||||||
pub fn letter_to_option(key: VirtualKeyCode, shift: bool) -> i32 {
|
pub fn letter_to_option(key: KeyCode, shift: bool) -> Option<usize> {
|
||||||
if shift {
|
if shift {
|
||||||
match key {
|
match key {
|
||||||
VirtualKeyCode::A => 26,
|
KeyCode::A => Some(26),
|
||||||
VirtualKeyCode::B => 27,
|
KeyCode::B => Some(27),
|
||||||
VirtualKeyCode::C => 28,
|
KeyCode::C => Some(28),
|
||||||
VirtualKeyCode::D => 29,
|
KeyCode::D => Some(29),
|
||||||
VirtualKeyCode::E => 30,
|
KeyCode::E => Some(30),
|
||||||
VirtualKeyCode::F => 31,
|
KeyCode::F => Some(31),
|
||||||
VirtualKeyCode::G => 32,
|
KeyCode::G => Some(32),
|
||||||
VirtualKeyCode::H => 33,
|
KeyCode::H => Some(33),
|
||||||
VirtualKeyCode::I => 34,
|
KeyCode::I => Some(34),
|
||||||
VirtualKeyCode::J => 35,
|
KeyCode::J => Some(35),
|
||||||
VirtualKeyCode::K => 36,
|
KeyCode::K => Some(36),
|
||||||
VirtualKeyCode::L => 37,
|
KeyCode::L => Some(37),
|
||||||
VirtualKeyCode::M => 38,
|
KeyCode::M => Some(38),
|
||||||
VirtualKeyCode::N => 39,
|
KeyCode::N => Some(39),
|
||||||
VirtualKeyCode::O => 40,
|
KeyCode::O => Some(40),
|
||||||
VirtualKeyCode::P => 41,
|
KeyCode::P => Some(41),
|
||||||
VirtualKeyCode::Q => 42,
|
KeyCode::Q => Some(42),
|
||||||
VirtualKeyCode::R => 43,
|
KeyCode::R => Some(43),
|
||||||
VirtualKeyCode::S => 44,
|
KeyCode::S => Some(44),
|
||||||
VirtualKeyCode::T => 45,
|
KeyCode::T => Some(45),
|
||||||
VirtualKeyCode::U => 46,
|
KeyCode::U => Some(46),
|
||||||
VirtualKeyCode::V => 47,
|
KeyCode::V => Some(47),
|
||||||
VirtualKeyCode::W => 48,
|
KeyCode::W => Some(48),
|
||||||
VirtualKeyCode::X => 49,
|
KeyCode::X => Some(49),
|
||||||
VirtualKeyCode::Y => 50,
|
KeyCode::Y => Some(50),
|
||||||
VirtualKeyCode::Z => 51,
|
KeyCode::Z => Some(51),
|
||||||
_ => -1,
|
_ => None,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match key {
|
match key {
|
||||||
VirtualKeyCode::A => 0,
|
KeyCode::A => Some(0),
|
||||||
VirtualKeyCode::B => 1,
|
KeyCode::B => Some(1),
|
||||||
VirtualKeyCode::C => 2,
|
KeyCode::C => Some(2),
|
||||||
VirtualKeyCode::D => 3,
|
KeyCode::D => Some(3),
|
||||||
VirtualKeyCode::E => 4,
|
KeyCode::E => Some(4),
|
||||||
VirtualKeyCode::F => 5,
|
KeyCode::F => Some(5),
|
||||||
VirtualKeyCode::G => 6,
|
KeyCode::G => Some(6),
|
||||||
VirtualKeyCode::H => 7,
|
KeyCode::H => Some(7),
|
||||||
VirtualKeyCode::I => 8,
|
KeyCode::I => Some(8),
|
||||||
VirtualKeyCode::J => 9,
|
KeyCode::J => Some(9),
|
||||||
VirtualKeyCode::K => 10,
|
KeyCode::K => Some(10),
|
||||||
VirtualKeyCode::L => 11,
|
KeyCode::L => Some(11),
|
||||||
VirtualKeyCode::M => 12,
|
KeyCode::M => Some(12),
|
||||||
VirtualKeyCode::N => 13,
|
KeyCode::N => Some(13),
|
||||||
VirtualKeyCode::O => 14,
|
KeyCode::O => Some(14),
|
||||||
VirtualKeyCode::P => 15,
|
KeyCode::P => Some(15),
|
||||||
VirtualKeyCode::Q => 16,
|
KeyCode::Q => Some(16),
|
||||||
VirtualKeyCode::R => 17,
|
KeyCode::R => Some(17),
|
||||||
VirtualKeyCode::S => 18,
|
KeyCode::S => Some(18),
|
||||||
VirtualKeyCode::T => 19,
|
KeyCode::T => Some(19),
|
||||||
VirtualKeyCode::U => 20,
|
KeyCode::U => Some(20),
|
||||||
VirtualKeyCode::V => 21,
|
KeyCode::V => Some(21),
|
||||||
VirtualKeyCode::W => 22,
|
KeyCode::W => Some(22),
|
||||||
VirtualKeyCode::X => 23,
|
KeyCode::X => Some(23),
|
||||||
VirtualKeyCode::Y => 24,
|
KeyCode::Y => Some(24),
|
||||||
VirtualKeyCode::Z => 25,
|
KeyCode::Z => Some(25),
|
||||||
_ => -1,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
47
src/gui/main_menu.rs
Normal file
47
src/gui/main_menu.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
use notan::prelude::*;
|
||||||
|
use notan::draw::{ Draw, CreateDraw, DrawTextSection, Font };
|
||||||
|
use specs::prelude::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use super::{ FONTSIZE, RunState, DISPLAYWIDTH, TILESIZE, MainMenuSelection };
|
||||||
|
use crate::consts::DISPLAYHEIGHT;
|
||||||
|
use crate::Fonts;
|
||||||
|
|
||||||
|
pub fn draw_mainmenu(ecs: &World, draw: &mut Draw, atlas: &HashMap<String, Texture>, font: &Fonts) {
|
||||||
|
let runstate = ecs.read_resource::<RunState>();
|
||||||
|
let selected = match *runstate {
|
||||||
|
RunState::MainMenu { menu_selection } => menu_selection,
|
||||||
|
_ => unreachable!("draw_mainmenu() called outside of MainMenu runstate."),
|
||||||
|
};
|
||||||
|
let save_exists = crate::saveload_system::does_save_exist();
|
||||||
|
const MID_X: f32 = ((DISPLAYWIDTH as f32) * TILESIZE.x) / 2.0;
|
||||||
|
|
||||||
|
let (x, mut y) = (MID_X, ((DISPLAYHEIGHT as f32) * TILESIZE.x) / 4.0);
|
||||||
|
draw.text(&font.ib(), "RUST-RL")
|
||||||
|
.size(FONTSIZE * 2.0)
|
||||||
|
.position(x, y)
|
||||||
|
.h_align_center();
|
||||||
|
y = draw.last_text_bounds().max_y();
|
||||||
|
draw.text(&font.n(), "New Game")
|
||||||
|
.size(FONTSIZE)
|
||||||
|
.position(x, y)
|
||||||
|
.h_align_center()
|
||||||
|
.color(get_colour(selected, MainMenuSelection::NewGame));
|
||||||
|
if save_exists {
|
||||||
|
y = draw.last_text_bounds().max_y();
|
||||||
|
draw.text(font.n(), "Load Game")
|
||||||
|
.size(FONTSIZE)
|
||||||
|
.position(x, y)
|
||||||
|
.h_align_center()
|
||||||
|
.color(get_colour(selected, MainMenuSelection::LoadGame));
|
||||||
|
}
|
||||||
|
y = draw.last_text_bounds().max_y();
|
||||||
|
draw.text(&font.n(), "Quit")
|
||||||
|
.size(FONTSIZE)
|
||||||
|
.position(x, y)
|
||||||
|
.h_align_center()
|
||||||
|
.color(get_colour(selected, MainMenuSelection::Quit));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_colour(selected: MainMenuSelection, desired: MainMenuSelection) -> Color {
|
||||||
|
if selected == desired { Color::from_rgb(0.0, 1.0, 0.0) } else { Color::WHITE }
|
||||||
|
}
|
||||||
1641
src/gui/mod.rs
1641
src/gui/mod.rs
File diff suppressed because it is too large
Load diff
|
|
@ -3,9 +3,11 @@ use super::{
|
||||||
item_colour_ecs,
|
item_colour_ecs,
|
||||||
obfuscate_name_ecs,
|
obfuscate_name_ecs,
|
||||||
print_options,
|
print_options,
|
||||||
|
unique_ecs,
|
||||||
renderable_colour,
|
renderable_colour,
|
||||||
ItemMenuResult,
|
ItemMenuResult,
|
||||||
UniqueInventoryItem,
|
UniqueInventoryItem,
|
||||||
|
InventorySlot,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
gamelog,
|
gamelog,
|
||||||
|
|
@ -18,10 +20,11 @@ use crate::{
|
||||||
Renderable,
|
Renderable,
|
||||||
states::state::*,
|
states::state::*,
|
||||||
BUC,
|
BUC,
|
||||||
|
Key,
|
||||||
};
|
};
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// Handles the Remove Curse menu.
|
/// Handles the Remove Curse menu.
|
||||||
pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Entity>) {
|
pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<Entity>) {
|
||||||
|
|
@ -33,11 +36,12 @@ pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<
|
||||||
let beatitudes = gs.ecs.read_storage::<Beatitude>();
|
let beatitudes = gs.ecs.read_storage::<Beatitude>();
|
||||||
let names = gs.ecs.read_storage::<Name>();
|
let names = gs.ecs.read_storage::<Name>();
|
||||||
let renderables = gs.ecs.read_storage::<Renderable>();
|
let renderables = gs.ecs.read_storage::<Renderable>();
|
||||||
|
let keys = gs.ecs.read_storage::<Key>();
|
||||||
|
|
||||||
let build_cursed_iterator = || {
|
let build_cursed_iterator = || {
|
||||||
(&entities, &items, &beatitudes, &renderables, &names)
|
(&entities, &items, &beatitudes, &renderables, &names, &keys)
|
||||||
.join()
|
.join()
|
||||||
.filter(|(item_entity, _i, b, _r, _n)| {
|
.filter(|(item_entity, _i, b, _r, _n, _k)| {
|
||||||
// Set all items to FALSE initially.
|
// Set all items to FALSE initially.
|
||||||
let mut keep = false;
|
let mut keep = false;
|
||||||
// If found in the player's backpack, set to TRUE
|
// If found in the player's backpack, set to TRUE
|
||||||
|
|
@ -86,8 +90,8 @@ pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<
|
||||||
.log();
|
.log();
|
||||||
return (ItemMenuResult::Selected, Some(item));
|
return (ItemMenuResult::Selected, Some(item));
|
||||||
}
|
}
|
||||||
let mut player_inventory: super::PlayerInventory = BTreeMap::new();
|
let mut player_inventory: super::PlayerInventory = HashMap::new();
|
||||||
for (entity, _i, _b, renderable, name) in build_cursed_iterator() {
|
for (entity, _i, _b, renderable, name, key) in build_cursed_iterator() {
|
||||||
let (singular, plural) = obfuscate_name_ecs(&gs.ecs, entity);
|
let (singular, plural) = obfuscate_name_ecs(&gs.ecs, entity);
|
||||||
let beatitude_status = if
|
let beatitude_status = if
|
||||||
let Some(beatitude) = gs.ecs.read_storage::<Beatitude>().get(entity)
|
let Some(beatitude) = gs.ecs.read_storage::<Beatitude>().get(entity)
|
||||||
|
|
@ -100,37 +104,34 @@ pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
let unique_item = UniqueInventoryItem {
|
let unique_item = unique_ecs(&gs.ecs, entity);
|
||||||
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
|
player_inventory
|
||||||
.entry(unique_item)
|
.entry(unique_item)
|
||||||
.and_modify(|(_e, count)| {
|
.and_modify(|slot| {
|
||||||
*count += 1;
|
slot.count += 1;
|
||||||
})
|
})
|
||||||
.or_insert((entity, 1));
|
.or_insert(InventorySlot {
|
||||||
|
item: entity,
|
||||||
|
count: 1,
|
||||||
|
idx: key.idx,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// Get display args
|
// Get display args
|
||||||
let width = get_max_inventory_width(&player_inventory);
|
let width = get_max_inventory_width(&player_inventory);
|
||||||
let (_, _, _, _, x_offset, y_offset) = crate::camera::get_screen_bounds(&gs.ecs, ctx);
|
let offsets = crate::camera::get_offset();
|
||||||
let (x, y) = (x_offset + 1, y_offset + 3);
|
let (x, y) = (offsets.x + 1, offsets.y + 3);
|
||||||
// Draw menu
|
// Draw menu
|
||||||
ctx.print_color(
|
ctx.print_color(
|
||||||
1 + x_offset,
|
1 + offsets.x,
|
||||||
1 + y_offset,
|
1 + offsets.y,
|
||||||
RGB::named(WHITE),
|
RGB::named(WHITE),
|
||||||
RGB::named(BLACK),
|
RGB::named(BLACK),
|
||||||
"Decurse which item? [aA-zZ][Esc.]"
|
"Decurse which item? [aA-zZ][Esc.]"
|
||||||
);
|
);
|
||||||
ctx.draw_box(x, y, width + 2, count + 1, RGB::named(WHITE), RGB::named(BLACK));
|
ctx.draw_box(x, y, width + 2, count + 1, RGB::named(WHITE), RGB::named(BLACK));
|
||||||
print_options(&player_inventory, x + 1, y + 1, ctx);
|
|
||||||
// Input
|
// Input
|
||||||
match ctx.key {
|
/*match ctx.key {
|
||||||
None => (ItemMenuResult::NoResponse, None),
|
None => (ItemMenuResult::NoResponse, None),
|
||||||
Some(key) =>
|
Some(key) =>
|
||||||
match key {
|
match key {
|
||||||
|
|
@ -155,5 +156,6 @@ pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option<
|
||||||
(ItemMenuResult::NoResponse, None)
|
(ItemMenuResult::NoResponse, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
(ItemMenuResult::NoResponse, None)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ use super::{
|
||||||
RGB,
|
RGB,
|
||||||
};
|
};
|
||||||
use crate::TileType;
|
use crate::TileType;
|
||||||
use crate::data::ids::*;
|
use crate::consts::ids::*;
|
||||||
|
use crate::consts::prelude::*;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
||||||
|
|
@ -45,6 +46,7 @@ impl Tooltip {
|
||||||
return (self.lines.len() as i32) + 2i32;
|
return (self.lines.len() as i32) + 2i32;
|
||||||
}
|
}
|
||||||
fn render(&self, ctx: &mut BTerm, x: i32, y: i32) {
|
fn render(&self, ctx: &mut BTerm, x: i32, y: i32) {
|
||||||
|
ctx.set_active_console(TEXT_LAYER);
|
||||||
ctx.draw_box(
|
ctx.draw_box(
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
|
|
@ -56,12 +58,13 @@ impl Tooltip {
|
||||||
for (i, s) in self.lines.iter().enumerate() {
|
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.print_color(x + 1, y + (i as i32) + 1, s.1, RGB::named(BLACK), &s.0);
|
||||||
}
|
}
|
||||||
|
ctx.set_active_console(TILE_LAYER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) {
|
pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) {
|
||||||
let (min_x, _max_x, min_y, _max_y, x_offset, y_offset) = get_screen_bounds(ecs, ctx);
|
let bounds = get_screen_bounds(ecs, false);
|
||||||
let map = ecs.fetch::<Map>();
|
let map = ecs.fetch::<Map>();
|
||||||
let names = ecs.read_storage::<Name>();
|
let names = ecs.read_storage::<Name>();
|
||||||
let positions = ecs.read_storage::<Position>();
|
let positions = ecs.read_storage::<Position>();
|
||||||
|
|
@ -74,8 +77,8 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) {
|
||||||
|
|
||||||
let mouse_pos = if xy.is_none() { ctx.mouse_pos() } else { xy.unwrap() };
|
let mouse_pos = if xy.is_none() { ctx.mouse_pos() } else { xy.unwrap() };
|
||||||
let mut mouse_pos_adjusted = mouse_pos;
|
let mut mouse_pos_adjusted = mouse_pos;
|
||||||
mouse_pos_adjusted.0 += min_x - x_offset;
|
mouse_pos_adjusted.0 += bounds.min_x - bounds.x_offset;
|
||||||
mouse_pos_adjusted.1 += min_y - y_offset;
|
mouse_pos_adjusted.1 += bounds.min_y - bounds.y_offset;
|
||||||
if mouse_pos_adjusted.0 >= map.width
|
if mouse_pos_adjusted.0 >= map.width
|
||||||
|| mouse_pos_adjusted.1 >= map.height
|
|| mouse_pos_adjusted.1 >= map.height
|
||||||
|| mouse_pos_adjusted.1 < 0 // Might need to be 1, and -1 from map height/width.
|
|| mouse_pos_adjusted.1 < 0 // Might need to be 1, and -1 from map height/width.
|
||||||
|
|
@ -121,18 +124,18 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) {
|
||||||
let attr = attributes.get(entity);
|
let attr = attributes.get(entity);
|
||||||
if let Some(a) = attr {
|
if let Some(a) = attr {
|
||||||
let mut s = "".to_string();
|
let mut s = "".to_string();
|
||||||
if a.strength.bonus < -2 { s += "weak "};
|
if a.strength.modifier() < -2 { s += "weak "};
|
||||||
if a.strength.bonus > 2 { s += "strong "};
|
if a.strength.modifier() > 2 { s += "strong "};
|
||||||
if a.dexterity.bonus < -2 { s += "clumsy "};
|
if a.dexterity.modifier() < -2 { s += "clumsy "};
|
||||||
if a.dexterity.bonus > 2 { s += "agile "};
|
if a.dexterity.modifier() > 2 { s += "agile "};
|
||||||
if a.constitution.bonus < -2 { s += "frail "};
|
if a.constitution.modifier() < -2 { s += "frail "};
|
||||||
if a.constitution.bonus > 2 { s += "hardy "};
|
if a.constitution.modifier() > 2 { s += "hardy "};
|
||||||
if a.intelligence.bonus < -2 { s += "dim "};
|
if a.intelligence.modifier() < -2 { s += "dim "};
|
||||||
if a.intelligence.bonus > 2 { s += "smart "};
|
if a.intelligence.modifier() > 2 { s += "smart "};
|
||||||
if a.wisdom.bonus < -2 { s += "unwise "};
|
if a.wisdom.modifier() < -2 { s += "unwise "};
|
||||||
if a.wisdom.bonus > 2 { s += "wisened "};
|
if a.wisdom.modifier() > 2 { s += "wisened "};
|
||||||
if a.charisma.bonus < -2 { s += "ugly"};
|
if a.charisma.modifier() < -2 { s += "ugly"};
|
||||||
if a.charisma.bonus > 2 { s += "attractive"};
|
if a.charisma.modifier() > 2 { s += "attractive"};
|
||||||
if !s.is_empty() {
|
if !s.is_empty() {
|
||||||
if s.ends_with(" ") {
|
if s.ends_with(" ") {
|
||||||
s.pop();
|
s.pop();
|
||||||
|
|
@ -175,13 +178,15 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) {
|
||||||
if mouse_pos.0 > 35 {
|
if mouse_pos.0 > 35 {
|
||||||
// Render to the left
|
// Render to the left
|
||||||
arrow = to_cp437('→');
|
arrow = to_cp437('→');
|
||||||
arrow_x = mouse_pos.0 - 1;
|
arrow_x = mouse_pos.0 * 2 - 1;
|
||||||
} else {
|
} else {
|
||||||
// Render to the right
|
// Render to the right
|
||||||
arrow = to_cp437('←');
|
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(arrow_x, arrow_y, white, RGB::named(BLACK), arrow);
|
||||||
|
ctx.set_active_console(TILE_LAYER);
|
||||||
|
|
||||||
let mut total_height = 0;
|
let mut total_height = 0;
|
||||||
for t in tooltips.iter() {
|
for t in tooltips.iter() {
|
||||||
|
|
@ -195,9 +200,9 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) {
|
||||||
|
|
||||||
for t in tooltips.iter() {
|
for t in tooltips.iter() {
|
||||||
let x = if mouse_pos.0 > 35 {
|
let x = if mouse_pos.0 > 35 {
|
||||||
mouse_pos.0 - (1 + t.width())
|
(mouse_pos.0 * 2) - (1 + t.width())
|
||||||
} else {
|
} else {
|
||||||
mouse_pos.0 + (1 + 1)
|
(mouse_pos.0 * 2) + 2 + 1
|
||||||
};
|
};
|
||||||
t.render(ctx, x, y);
|
t.render(ctx, x, y);
|
||||||
y += t.height();
|
y += t.height();
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,12 @@ use crate::{
|
||||||
ObfuscatedName,
|
ObfuscatedName,
|
||||||
Position,
|
Position,
|
||||||
WantsToPickupItem,
|
WantsToPickupItem,
|
||||||
|
WantsToAssignKey,
|
||||||
|
Renderable,
|
||||||
|
Stackable,
|
||||||
};
|
};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use crate::data::messages;
|
use crate::consts::messages;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
|
|
||||||
pub struct ItemCollectionSystem {}
|
pub struct ItemCollectionSystem {}
|
||||||
|
|
@ -30,9 +33,12 @@ impl<'a> System<'a> for ItemCollectionSystem {
|
||||||
WriteStorage<'a, EquipmentChanged>,
|
WriteStorage<'a, EquipmentChanged>,
|
||||||
ReadStorage<'a, MagicItem>,
|
ReadStorage<'a, MagicItem>,
|
||||||
ReadStorage<'a, ObfuscatedName>,
|
ReadStorage<'a, ObfuscatedName>,
|
||||||
|
ReadStorage<'a, Renderable>,
|
||||||
ReadStorage<'a, Beatitude>,
|
ReadStorage<'a, Beatitude>,
|
||||||
ReadExpect<'a, MasterDungeonMap>,
|
ReadExpect<'a, MasterDungeonMap>,
|
||||||
ReadStorage<'a, Charges>,
|
ReadStorage<'a, Charges>,
|
||||||
|
ReadStorage<'a, WantsToAssignKey>,
|
||||||
|
ReadStorage<'a, Stackable>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
|
|
@ -45,20 +51,17 @@ impl<'a> System<'a> for ItemCollectionSystem {
|
||||||
mut equipment_changed,
|
mut equipment_changed,
|
||||||
magic_items,
|
magic_items,
|
||||||
obfuscated_names,
|
obfuscated_names,
|
||||||
|
renderables,
|
||||||
beatitudes,
|
beatitudes,
|
||||||
dm,
|
dm,
|
||||||
wands,
|
wands,
|
||||||
|
wants_key,
|
||||||
|
stackable,
|
||||||
) = data;
|
) = data;
|
||||||
|
|
||||||
for pickup in wants_pickup.join() {
|
let mut to_remove: Vec<Entity> = Vec::new();
|
||||||
positions.remove(pickup.item);
|
// For every item that wants to be picked up, that *isn't* still waiting on a key assignment.
|
||||||
backpack
|
for (pickup, _key) in (&wants_pickup, !&wants_key).join() {
|
||||||
.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 {
|
if pickup.collected_by == *player_entity {
|
||||||
gamelog::Logger
|
gamelog::Logger
|
||||||
::new()
|
::new()
|
||||||
|
|
@ -82,8 +85,17 @@ impl<'a> System<'a> for ItemCollectionSystem {
|
||||||
.period()
|
.period()
|
||||||
.log();
|
.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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,10 @@ use crate::{
|
||||||
ObfuscatedName,
|
ObfuscatedName,
|
||||||
Position,
|
Position,
|
||||||
WantsToDropItem,
|
WantsToDropItem,
|
||||||
|
WantsToRemoveKey,
|
||||||
};
|
};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use crate::data::messages;
|
use crate::consts::messages;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
|
|
||||||
pub struct ItemDropSystem {}
|
pub struct ItemDropSystem {}
|
||||||
|
|
@ -34,6 +35,7 @@ impl<'a> System<'a> for ItemDropSystem {
|
||||||
ReadStorage<'a, ObfuscatedName>,
|
ReadStorage<'a, ObfuscatedName>,
|
||||||
ReadExpect<'a, MasterDungeonMap>,
|
ReadExpect<'a, MasterDungeonMap>,
|
||||||
ReadStorage<'a, Charges>,
|
ReadStorage<'a, Charges>,
|
||||||
|
WriteStorage<'a, WantsToRemoveKey>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
|
|
@ -50,6 +52,7 @@ impl<'a> System<'a> for ItemDropSystem {
|
||||||
obfuscated_names,
|
obfuscated_names,
|
||||||
dm,
|
dm,
|
||||||
wands,
|
wands,
|
||||||
|
mut keys,
|
||||||
) = data;
|
) = data;
|
||||||
|
|
||||||
for (entity, to_drop) in (&entities, &wants_drop).join() {
|
for (entity, to_drop) in (&entities, &wants_drop).join() {
|
||||||
|
|
@ -68,6 +71,9 @@ impl<'a> System<'a> for ItemDropSystem {
|
||||||
backpack.remove(to_drop.item);
|
backpack.remove(to_drop.item);
|
||||||
|
|
||||||
if entity == *player_entity {
|
if entity == *player_entity {
|
||||||
|
keys.insert(to_drop.item, WantsToRemoveKey {}).expect(
|
||||||
|
"Unable to insert WantsToRemoveKey"
|
||||||
|
);
|
||||||
gamelog::Logger
|
gamelog::Logger
|
||||||
::new()
|
::new()
|
||||||
.append(messages::YOU_DROP_ITEM)
|
.append(messages::YOU_DROP_ITEM)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ use crate::{
|
||||||
BUC,
|
BUC,
|
||||||
};
|
};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use crate::data::messages;
|
use crate::consts::messages;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
|
|
||||||
pub struct ItemEquipSystem {}
|
pub struct ItemEquipSystem {}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
Player,
|
Player,
|
||||||
};
|
};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use crate::data::events::*;
|
use crate::consts::events::*;
|
||||||
use crate::gamelog;
|
use crate::gamelog;
|
||||||
|
|
||||||
pub struct ItemIdentificationSystem {}
|
pub struct ItemIdentificationSystem {}
|
||||||
|
|
|
||||||
175
src/inventory/keyhandling.rs
Normal file
175
src/inventory/keyhandling.rs
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
use crate::{
|
||||||
|
gamelog,
|
||||||
|
gui::unique,
|
||||||
|
Beatitude,
|
||||||
|
Charges,
|
||||||
|
MagicItem,
|
||||||
|
MasterDungeonMap,
|
||||||
|
Name,
|
||||||
|
ObfuscatedName,
|
||||||
|
Stackable,
|
||||||
|
Renderable,
|
||||||
|
WantsToAssignKey,
|
||||||
|
WantsToRemoveKey,
|
||||||
|
Key,
|
||||||
|
};
|
||||||
|
use specs::prelude::*;
|
||||||
|
use crate::consts::messages;
|
||||||
|
use bracket_lib::prelude::*;
|
||||||
|
use crate::invkeys::*;
|
||||||
|
|
||||||
|
pub struct KeyHandling {}
|
||||||
|
|
||||||
|
const DEBUG_KEYHANDLING: bool = false;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
if DEBUG_KEYHANDLING {
|
||||||
|
console::log(&format!("KEYHANDLING: Item is stackable."));
|
||||||
|
}
|
||||||
|
let maybe_key = item_exists(&unique);
|
||||||
|
if maybe_key.is_some() {
|
||||||
|
if DEBUG_KEYHANDLING {
|
||||||
|
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.");
|
||||||
|
if DEBUG_KEYHANDLING {
|
||||||
|
console::log(&format!("KEYHANDLING: Assigned key idx {} to item.", key));
|
||||||
|
}
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !handled {
|
||||||
|
if DEBUG_KEYHANDLING {
|
||||||
|
console::log(
|
||||||
|
&format!("KEYHANDLING: Item is not stackable, or no existing stack found.")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(idx) = assign_next_available() {
|
||||||
|
if DEBUG_KEYHANDLING {
|
||||||
|
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 {
|
||||||
|
if DEBUG_KEYHANDLING {
|
||||||
|
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) {
|
||||||
|
if DEBUG_KEYHANDLING {
|
||||||
|
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.
|
||||||
|
if DEBUG_KEYHANDLING {
|
||||||
|
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 {
|
||||||
|
if DEBUG_KEYHANDLING {
|
||||||
|
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 {
|
||||||
|
if DEBUG_KEYHANDLING {
|
||||||
|
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.
|
||||||
|
if DEBUG_KEYHANDLING {
|
||||||
|
console::log(&format!("KEYHANDLING: Removing key component from item."));
|
||||||
|
}
|
||||||
|
keys.remove(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
wants_removekey.clear();
|
||||||
|
wants_keys.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ mod equip_system;
|
||||||
mod identification_system;
|
mod identification_system;
|
||||||
mod remove_system;
|
mod remove_system;
|
||||||
mod use_system;
|
mod use_system;
|
||||||
|
mod keyhandling;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
collection_system::ItemCollectionSystem,
|
collection_system::ItemCollectionSystem,
|
||||||
|
|
@ -12,4 +13,5 @@ pub use self::{
|
||||||
identification_system::ItemIdentificationSystem,
|
identification_system::ItemIdentificationSystem,
|
||||||
remove_system::ItemRemoveSystem,
|
remove_system::ItemRemoveSystem,
|
||||||
use_system::ItemUseSystem,
|
use_system::ItemUseSystem,
|
||||||
|
keyhandling::KeyHandling,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use crate::data::messages;
|
use crate::consts::messages;
|
||||||
|
|
||||||
pub struct ItemRemoveSystem {}
|
pub struct ItemRemoveSystem {}
|
||||||
|
|
||||||
|
|
|
||||||
59
src/invkeys.rs
Normal file
59
src/invkeys.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::collections::{ HashMap };
|
||||||
|
use specs::prelude::*;
|
||||||
|
use crate::gui::UniqueInventoryItem;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref INVKEYS: Mutex<HashMap<UniqueInventoryItem, usize>> = Mutex::new(HashMap::new());
|
||||||
|
pub static ref ASSIGNEDKEYS: Mutex<Vec<bool>> = Mutex::new(vec![false; 52]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For (de)serialization.
|
||||||
|
pub fn clone_invkeys() -> HashMap<UniqueInventoryItem, usize> {
|
||||||
|
let invkeys = INVKEYS.lock().unwrap();
|
||||||
|
invkeys.clone()
|
||||||
|
}
|
||||||
|
pub fn restore_invkeys(invkeys: HashMap<UniqueInventoryItem, usize>) {
|
||||||
|
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<usize> {
|
||||||
|
let invkeys = INVKEYS.lock().unwrap();
|
||||||
|
use bracket_lib::prelude::*;
|
||||||
|
if invkeys.contains_key(item) {
|
||||||
|
Some(*invkeys.get(item).unwrap())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assign_next_available() -> Option<usize> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
@ -29,7 +29,7 @@ pub mod trigger_system;
|
||||||
pub mod inventory;
|
pub mod inventory;
|
||||||
pub mod particle_system;
|
pub mod particle_system;
|
||||||
pub mod ai;
|
pub mod ai;
|
||||||
pub mod data;
|
pub mod consts;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod effects;
|
pub mod effects;
|
||||||
pub mod gamesystem;
|
pub mod gamesystem;
|
||||||
|
|
@ -38,9 +38,11 @@ pub mod rex_assets;
|
||||||
pub mod spatial;
|
pub mod spatial;
|
||||||
pub mod morgue;
|
pub mod morgue;
|
||||||
pub mod states;
|
pub mod states;
|
||||||
|
pub mod invkeys;
|
||||||
|
|
||||||
pub use components::*;
|
pub use components::*;
|
||||||
use particle_system::ParticleBuilder;
|
use particle_system::ParticleBuilder;
|
||||||
pub use map::*;
|
pub use map::*;
|
||||||
pub use states::runstate::RunState;
|
pub use states::runstate::RunState;
|
||||||
pub use states::state::State;
|
pub use states::state::State;
|
||||||
|
pub use states::state::Fonts;
|
||||||
|
|
|
||||||
602
src/main.rs
602
src/main.rs
|
|
@ -1,31 +1,70 @@
|
||||||
use rust_rl::*;
|
use rust_rl::*;
|
||||||
|
use notan::prelude::*;
|
||||||
|
use notan::math::{ vec2, vec3, Mat4, Vec2 };
|
||||||
|
use notan::draw::create_textures_from_atlas;
|
||||||
|
use notan::draw::{ CreateFont, CreateDraw, DrawImages, Draw, DrawTextSection, DrawShapes };
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use specs::saveload::{ SimpleMarker, SimpleMarkerAllocator };
|
use specs::saveload::{ SimpleMarker, SimpleMarkerAllocator };
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::consts::{ DISPLAYHEIGHT, DISPLAYWIDTH, TILESIZE, FONTSIZE };
|
||||||
|
use crate::states::state::Fonts;
|
||||||
|
|
||||||
const DISPLAYWIDTH: i32 = 105;
|
const WORK_SIZE: Vec2 = vec2(
|
||||||
const DISPLAYHEIGHT: i32 = 56;
|
(DISPLAYWIDTH as f32) * TILESIZE.x,
|
||||||
|
(DISPLAYHEIGHT as f32) * TILESIZE.x
|
||||||
|
);
|
||||||
|
|
||||||
fn main() -> BError {
|
#[notan_main]
|
||||||
// Embedded resources for use in wasm build
|
fn main() -> Result<(), String> {
|
||||||
const CURSES_14_16_BYTES: &[u8] = include_bytes!("../resources/curses14x16.png");
|
let win_config = WindowConfig::new()
|
||||||
EMBED.lock().add_resource("resources/curses14x16.png".to_string(), CURSES_14_16_BYTES);
|
.set_size(DISPLAYWIDTH * (TILESIZE.x as u32), DISPLAYHEIGHT * (TILESIZE.x as u32))
|
||||||
|
.set_title("RUST-RL")
|
||||||
//link_resource!(CURSES14X16, "../resources/curses_14x16.png");
|
.set_resizable(true)
|
||||||
|
.set_fullscreen(true)
|
||||||
let mut context = BTermBuilder::new()
|
.set_taskbar_icon_data(Some(include_bytes!("../resources/icon.png")))
|
||||||
.with_title("rust-rl")
|
.set_vsync(true);
|
||||||
.with_dimensions(DISPLAYWIDTH, DISPLAYHEIGHT)
|
notan
|
||||||
.with_font("curses14x16.png", 14, 16)
|
::init_with(setup)
|
||||||
.with_tile_dimensions(14, 16)
|
.add_config(win_config)
|
||||||
.with_simple_console(DISPLAYWIDTH, DISPLAYHEIGHT, "curses14x16.png")
|
.add_config(notan::draw::DrawConfig)
|
||||||
.build()?;
|
.draw(draw)
|
||||||
if config::CONFIG.visuals.with_scanlines {
|
.update(update)
|
||||||
context.with_post_scanlines(config::CONFIG.visuals.with_screen_burn);
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn setup(app: &mut App, gfx: &mut Graphics) -> State {
|
||||||
|
effects::sound::init_sounds(app);
|
||||||
|
effects::sound::ambience("a_relax");
|
||||||
|
let texture = gfx
|
||||||
|
.create_texture()
|
||||||
|
.from_image(include_bytes!("../resources/atlas.png"))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let data = include_bytes!("../resources/atlas.json");
|
||||||
|
let atlas = create_textures_from_atlas(data, &texture).unwrap();
|
||||||
|
let texture = gfx
|
||||||
|
.create_texture()
|
||||||
|
.from_image(include_bytes!("../resources/td.png"))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let data = include_bytes!("../resources/td.json");
|
||||||
|
let interface = create_textures_from_atlas(data, &texture).unwrap();
|
||||||
|
let font = Fonts::new(
|
||||||
|
gfx.create_font(include_bytes!("../resources/fonts/Greybeard-16px.ttf")).unwrap(),
|
||||||
|
Some(
|
||||||
|
gfx.create_font(include_bytes!("../resources/fonts/Greybeard-16px-Bold.ttf")).unwrap()
|
||||||
|
),
|
||||||
|
Some(
|
||||||
|
gfx.create_font(include_bytes!("../resources/fonts/Greybeard-16px-Italic.ttf")).unwrap()
|
||||||
|
)
|
||||||
|
);
|
||||||
let mut gs = State {
|
let mut gs = State {
|
||||||
ecs: World::new(),
|
ecs: World::new(),
|
||||||
|
//audio: sounds,
|
||||||
|
atlas,
|
||||||
|
interface,
|
||||||
|
font,
|
||||||
mapgen_next_state: Some(RunState::MainMenu {
|
mapgen_next_state: Some(RunState::MainMenu {
|
||||||
menu_selection: gui::MainMenuSelection::NewGame,
|
menu_selection: gui::MainMenuSelection::NewGame,
|
||||||
}),
|
}),
|
||||||
|
|
@ -37,6 +76,7 @@ fn main() -> BError {
|
||||||
gs.ecs.register::<Position>();
|
gs.ecs.register::<Position>();
|
||||||
gs.ecs.register::<OtherLevelPosition>();
|
gs.ecs.register::<OtherLevelPosition>();
|
||||||
gs.ecs.register::<Renderable>();
|
gs.ecs.register::<Renderable>();
|
||||||
|
gs.ecs.register::<Avatar>();
|
||||||
gs.ecs.register::<Burden>();
|
gs.ecs.register::<Burden>();
|
||||||
gs.ecs.register::<Prop>();
|
gs.ecs.register::<Prop>();
|
||||||
gs.ecs.register::<Player>();
|
gs.ecs.register::<Player>();
|
||||||
|
|
@ -111,6 +151,11 @@ fn main() -> BError {
|
||||||
gs.ecs.register::<SpawnParticleLine>();
|
gs.ecs.register::<SpawnParticleLine>();
|
||||||
gs.ecs.register::<HasDamageModifiers>();
|
gs.ecs.register::<HasDamageModifiers>();
|
||||||
gs.ecs.register::<Intrinsics>();
|
gs.ecs.register::<Intrinsics>();
|
||||||
|
gs.ecs.register::<Stackable>();
|
||||||
|
gs.ecs.register::<WantsToAssignKey>();
|
||||||
|
gs.ecs.register::<Key>();
|
||||||
|
gs.ecs.register::<WantsToRemoveKey>();
|
||||||
|
gs.ecs.register::<WantsToDelete>();
|
||||||
gs.ecs.register::<IntrinsicChanged>();
|
gs.ecs.register::<IntrinsicChanged>();
|
||||||
gs.ecs.register::<SimpleMarker<SerializeMe>>();
|
gs.ecs.register::<SimpleMarker<SerializeMe>>();
|
||||||
gs.ecs.register::<SerializationHelper>();
|
gs.ecs.register::<SerializationHelper>();
|
||||||
|
|
@ -127,13 +172,526 @@ fn main() -> BError {
|
||||||
gs.ecs.insert(gui::Ancestry::Human); // ancestry
|
gs.ecs.insert(gui::Ancestry::Human); // ancestry
|
||||||
let player_entity = spawner::player(&mut gs.ecs, 0, 0);
|
let player_entity = spawner::player(&mut gs.ecs, 0, 0);
|
||||||
gs.ecs.insert(player_entity); // Player entity
|
gs.ecs.insert(player_entity); // Player entity
|
||||||
gs.ecs.insert(RunState::MapGeneration {}); // RunState
|
gs.ecs.insert(RunState::MapGeneration {}); // TODO: Set this back to RunState::MapGen
|
||||||
gs.ecs.insert(particle_system::ParticleBuilder::new());
|
gs.ecs.insert(particle_system::ParticleBuilder::new());
|
||||||
gs.ecs.insert(rex_assets::RexAssets::new());
|
gs.ecs.insert(rex_assets::RexAssets::new());
|
||||||
|
|
||||||
gamelog::setup_log();
|
gamelog::setup_log();
|
||||||
gamelog::record_event(data::events::EVENT::Level(1));
|
gamelog::record_event(consts::events::EVENT::Level(1));
|
||||||
gs.generate_world_map(1, TileType::Floor);
|
gs.generate_world_map(1, TileType::Floor);
|
||||||
|
|
||||||
main_loop(context, gs)
|
gs
|
||||||
|
}
|
||||||
|
const ASCII_MODE: bool = false; // Change this to config setting
|
||||||
|
const SHOW_BOUNDARIES: bool = false; // Config setting
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum DrawType {
|
||||||
|
None,
|
||||||
|
Player,
|
||||||
|
Visible,
|
||||||
|
Telepathy,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Hash)]
|
||||||
|
struct DrawKey {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
render_order: i32,
|
||||||
|
}
|
||||||
|
struct DrawInfo {
|
||||||
|
e: Entity,
|
||||||
|
draw_type: DrawType,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_entities(
|
||||||
|
map: &Map,
|
||||||
|
ecs: &World,
|
||||||
|
draw: &mut Draw,
|
||||||
|
atlas: &HashMap<String, Texture>,
|
||||||
|
_font: &Fonts
|
||||||
|
) {
|
||||||
|
{
|
||||||
|
let bounds = crate::camera::get_screen_bounds(ecs, false);
|
||||||
|
let bounds_to_px = bounds.to_px();
|
||||||
|
let offset_x = bounds_to_px.x_offset - bounds_to_px.min_x;
|
||||||
|
let offset_y = bounds_to_px.y_offset - bounds_to_px.min_y;
|
||||||
|
let positions = ecs.read_storage::<Position>();
|
||||||
|
let renderables = ecs.read_storage::<Renderable>();
|
||||||
|
let hidden = ecs.read_storage::<Hidden>();
|
||||||
|
let minds = ecs.read_storage::<Mind>();
|
||||||
|
let pools = ecs.read_storage::<Pools>();
|
||||||
|
let entities = ecs.entities();
|
||||||
|
let player = ecs.read_storage::<Player>();
|
||||||
|
let data = (&positions, &renderables, &entities, !&hidden).join().collect::<Vec<_>>();
|
||||||
|
let mut to_draw: HashMap<DrawKey, DrawInfo> = HashMap::new();
|
||||||
|
for (pos, render, e, _h) in data.iter() {
|
||||||
|
let idx = map.xy_idx(pos.x, pos.y);
|
||||||
|
if
|
||||||
|
crate::camera::in_bounds(
|
||||||
|
pos.x,
|
||||||
|
pos.y,
|
||||||
|
bounds.min_x,
|
||||||
|
bounds.min_y,
|
||||||
|
bounds.max_x,
|
||||||
|
bounds.max_y
|
||||||
|
)
|
||||||
|
{
|
||||||
|
let draw_type = if map.visible_tiles[idx] {
|
||||||
|
// If it's anything else, just draw it.
|
||||||
|
if player.get(*e).is_some() {
|
||||||
|
DrawType::Player
|
||||||
|
} else {
|
||||||
|
DrawType::Visible
|
||||||
|
}
|
||||||
|
} else if map.telepath_tiles[idx] {
|
||||||
|
let has_mind = minds.get(*e);
|
||||||
|
if has_mind.is_some() {
|
||||||
|
// Mobs we see through telepathy - generally we just
|
||||||
|
// draw these, but it uses a unique enum variant so
|
||||||
|
// it can be treated differently if needed in future.
|
||||||
|
DrawType::Telepathy
|
||||||
|
} else {
|
||||||
|
DrawType::None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If we don't see it, and we don't sense it with
|
||||||
|
// telepathy, don't draw it at all.
|
||||||
|
DrawType::None
|
||||||
|
};
|
||||||
|
match draw_type {
|
||||||
|
DrawType::None => {}
|
||||||
|
_ => {
|
||||||
|
to_draw.insert(
|
||||||
|
DrawKey { x: pos.x, y: pos.y, render_order: render.render_order },
|
||||||
|
DrawInfo { e: *e, draw_type }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut entries: Vec<(&DrawKey, &DrawInfo)> = to_draw.iter().collect();
|
||||||
|
entries.sort_by_key(|&(k, _v)| std::cmp::Reverse(k.render_order));
|
||||||
|
for entry in entries.iter() {
|
||||||
|
// TODO: ABSTRACT THESE INTO FUNCTIONS ONCE FUNCTIONALITY IS SETTLED ON.
|
||||||
|
match entry.1.draw_type {
|
||||||
|
DrawType::Visible | DrawType::Telepathy => {
|
||||||
|
let renderable = renderables.get(entry.1.e).unwrap();
|
||||||
|
let id = if let Some(sprite) = atlas.get(&renderable.sprite) {
|
||||||
|
sprite
|
||||||
|
} else {
|
||||||
|
panic!("No entity sprite found for ID: {}", &renderable.sprite);
|
||||||
|
};
|
||||||
|
let x_pos = (entry.0.x as f32) * TILESIZE.sprite_x + offset_x;
|
||||||
|
let y_pos = (entry.0.y as f32) * TILESIZE.sprite_y + offset_y;
|
||||||
|
let mul = themes::darken_by_distance(
|
||||||
|
Point::new(entry.0.x, entry.0.y),
|
||||||
|
*ecs.fetch::<Point>()
|
||||||
|
);
|
||||||
|
let col = Color::from_rgb(
|
||||||
|
renderable.fg.r * mul,
|
||||||
|
renderable.fg.g * mul,
|
||||||
|
renderable.fg.b * mul
|
||||||
|
);
|
||||||
|
draw.image(id)
|
||||||
|
.position(
|
||||||
|
x_pos + renderable.offset.0 * TILESIZE.sprite_x,
|
||||||
|
y_pos + renderable.offset.1 * TILESIZE.sprite_y
|
||||||
|
)
|
||||||
|
.color(col)
|
||||||
|
.size(TILESIZE.sprite_x, TILESIZE.sprite_y);
|
||||||
|
if let Some(pool) = pools.get(entry.1.e) {
|
||||||
|
if pool.hit_points.current < pool.hit_points.max {
|
||||||
|
draw_entity_hp(x_pos, y_pos, pool, draw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DrawType::Player => {
|
||||||
|
let (x_pos, y_pos) = (
|
||||||
|
(entry.0.x as f32) * TILESIZE.sprite_x + offset_x,
|
||||||
|
(entry.0.y as f32) * TILESIZE.sprite_y + offset_y,
|
||||||
|
);
|
||||||
|
let textures = get_avatar_textures(ecs, atlas);
|
||||||
|
for (tex, col) in textures.iter() {
|
||||||
|
draw.image(tex)
|
||||||
|
.position(x_pos, y_pos)
|
||||||
|
.color(*col)
|
||||||
|
.size(TILESIZE.sprite_x, TILESIZE.sprite_y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_avatar_textures(ecs: &World, atlas: &HashMap<String, Texture>) -> Vec<(Texture, Color)> {
|
||||||
|
let player = ecs.fetch::<Entity>();
|
||||||
|
let renderables = ecs.read_storage::<Renderable>();
|
||||||
|
let equipped = ecs.read_storage::<Equipped>();
|
||||||
|
let has_avatar = ecs.read_storage::<Avatar>();
|
||||||
|
let mut avis = Vec::new();
|
||||||
|
if let Some(renderables) = renderables.get(*player) {
|
||||||
|
if let Some(sprite) = atlas.get(&renderables.sprite) {
|
||||||
|
avis.push((
|
||||||
|
sprite.clone(),
|
||||||
|
Color::from_rgb(renderables.fg.r, renderables.fg.g, renderables.fg.b),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
panic!("No player sprite found for ID: {}", &renderables.sprite);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("No player renderable found!");
|
||||||
|
}
|
||||||
|
for (_e, a, r) in (&equipped, &has_avatar, &renderables)
|
||||||
|
.join()
|
||||||
|
.filter(|item| item.0.owner == *player) {
|
||||||
|
if let Some(sprite) = atlas.get(&a.sprite) {
|
||||||
|
avis.push((sprite.clone(), Color::from_rgb(r.fg.r, r.fg.g, r.fg.b)));
|
||||||
|
} else {
|
||||||
|
panic!("No avatar sprite found for ID: {}", &a.sprite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
avis
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draws a HP bar LINE_WIDTH pixels thick centered above the entity.
|
||||||
|
fn draw_entity_hp(x: f32, y: f32, hp: &Pools, draw: &mut Draw) {
|
||||||
|
const LINE_WIDTH: f32 = 3.0;
|
||||||
|
let y = y + LINE_WIDTH + 1.0;
|
||||||
|
let x = x;
|
||||||
|
let fill_pct = (hp.hit_points.current as f32) / (hp.hit_points.max as f32);
|
||||||
|
draw.line((x + 1.0, y), (x + (TILESIZE.sprite_x - 1.0) * fill_pct, y))
|
||||||
|
.width(LINE_WIDTH)
|
||||||
|
.color(Color::GREEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_map_in_view(
|
||||||
|
map: &Map,
|
||||||
|
ecs: &World,
|
||||||
|
draw: &mut Draw,
|
||||||
|
atlas: &HashMap<String, Texture>,
|
||||||
|
mapgen: bool
|
||||||
|
) {
|
||||||
|
let bounds = crate::camera::get_screen_bounds(ecs, mapgen);
|
||||||
|
let mut y = 0;
|
||||||
|
for tile_y in bounds.min_y..bounds.max_y {
|
||||||
|
let mut x = 0;
|
||||||
|
for tile_x in bounds.min_x..bounds.max_x {
|
||||||
|
if crate::camera::in_bounds(tile_x, tile_y, 0, 0, map.width, map.height) {
|
||||||
|
let idx = map.xy_idx(tile_x, tile_y);
|
||||||
|
if map.revealed_tiles[idx] || mapgen {
|
||||||
|
let draw_x =
|
||||||
|
(x as f32) * TILESIZE.sprite_x + (bounds.x_offset as f32) * TILESIZE.x;
|
||||||
|
let draw_y =
|
||||||
|
(y as f32) * TILESIZE.sprite_y + (bounds.y_offset as f32) * TILESIZE.x;
|
||||||
|
if ASCII_MODE {
|
||||||
|
let (glyph, fg, bg) = crate::map::themes::get_tile_renderables_for_id(
|
||||||
|
idx,
|
||||||
|
&*map,
|
||||||
|
Some(*ecs.fetch::<Point>()),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
// TODO: Draw ASCII
|
||||||
|
} else {
|
||||||
|
let (id, tint) = crate::map::themes::get_sprite_for_id(
|
||||||
|
idx,
|
||||||
|
&*map,
|
||||||
|
Some(*ecs.fetch::<Point>())
|
||||||
|
);
|
||||||
|
let sprite = if let Some(sprite) = atlas.get(id) {
|
||||||
|
sprite
|
||||||
|
} else {
|
||||||
|
panic!("No sprite found for ID: {}", id);
|
||||||
|
};
|
||||||
|
draw.image(sprite)
|
||||||
|
.position(draw_x, draw_y)
|
||||||
|
.color(tint)
|
||||||
|
.size(TILESIZE.sprite_x, TILESIZE.sprite_y);
|
||||||
|
}
|
||||||
|
if !map.visible_tiles[idx] {
|
||||||
|
// Recall map memory. TODO: Improve this? Optimize? Do we need to remember more fields?
|
||||||
|
if let Some(memories) = map.memory.get(&idx) {
|
||||||
|
let mut sorted: Vec<_> = memories.iter().collect();
|
||||||
|
sorted.sort_by(|a, b| a.render_order.cmp(&b.render_order));
|
||||||
|
for memory in sorted.iter() {
|
||||||
|
let mult = consts::visuals::NON_VISIBLE_MULTIPLIER;
|
||||||
|
let col = Color::from_rgb(
|
||||||
|
memory.fg.r * mult,
|
||||||
|
memory.fg.g * mult,
|
||||||
|
memory.fg.b * mult
|
||||||
|
);
|
||||||
|
let sprite = if let Some(sprite) = atlas.get(&memory.sprite) {
|
||||||
|
sprite
|
||||||
|
} else {
|
||||||
|
panic!("No sprite found for ID: {}", memory.sprite);
|
||||||
|
};
|
||||||
|
draw.image(sprite)
|
||||||
|
.position(
|
||||||
|
draw_x + memory.offset.0 * TILESIZE.sprite_x,
|
||||||
|
draw_y + memory.offset.1 * TILESIZE.sprite_y
|
||||||
|
)
|
||||||
|
.color(col)
|
||||||
|
.size(TILESIZE.sprite_x, TILESIZE.sprite_y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if SHOW_BOUNDARIES {
|
||||||
|
// TODO: Draw boundaries
|
||||||
|
}
|
||||||
|
x += 1;
|
||||||
|
}
|
||||||
|
y += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BoxDraw {
|
||||||
|
frame: String,
|
||||||
|
fill: bool,
|
||||||
|
top_left: (i32, i32),
|
||||||
|
top_right: (i32, i32),
|
||||||
|
bottom_left: (i32, i32),
|
||||||
|
bottom_right: (i32, i32),
|
||||||
|
}
|
||||||
|
fn draw_spritebox(panel: BoxDraw, draw: &mut Draw, atlas: &HashMap<String, Texture>) {
|
||||||
|
draw.image(atlas.get(&format!("{}_1", panel.frame)).unwrap()).position(
|
||||||
|
(panel.top_left.0 as f32) * TILESIZE.x,
|
||||||
|
(panel.top_left.1 as f32) * TILESIZE.x
|
||||||
|
);
|
||||||
|
for i in panel.top_left.0 + 1..panel.top_right.0 {
|
||||||
|
draw.image(atlas.get(&format!("{}_2", panel.frame)).unwrap()).position(
|
||||||
|
(i as f32) * TILESIZE.x,
|
||||||
|
(panel.top_left.1 as f32) * TILESIZE.x
|
||||||
|
);
|
||||||
|
}
|
||||||
|
draw.image(atlas.get(&format!("{}_3", panel.frame)).unwrap()).position(
|
||||||
|
(panel.top_right.0 as f32) * TILESIZE.x,
|
||||||
|
(panel.top_right.1 as f32) * TILESIZE.x
|
||||||
|
);
|
||||||
|
for i in panel.top_left.1 + 1..panel.bottom_left.1 {
|
||||||
|
draw.image(atlas.get(&format!("{}_4", panel.frame)).unwrap()).position(
|
||||||
|
(panel.top_left.0 as f32) * TILESIZE.x,
|
||||||
|
(i as f32) * TILESIZE.x
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if panel.fill {
|
||||||
|
for i in panel.top_left.0 + 1..panel.top_right.0 {
|
||||||
|
for j in panel.top_left.1 + 1..panel.bottom_left.1 {
|
||||||
|
draw.image(atlas.get(&format!("{}_5", panel.frame)).unwrap()).position(
|
||||||
|
(i as f32) * TILESIZE.x,
|
||||||
|
(j as f32) * TILESIZE.x
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in panel.top_right.1 + 1..panel.bottom_right.1 {
|
||||||
|
draw.image(atlas.get(&format!("{}_6", panel.frame)).unwrap()).position(
|
||||||
|
(panel.top_right.0 as f32) * TILESIZE.x,
|
||||||
|
(i as f32) * TILESIZE.x
|
||||||
|
);
|
||||||
|
}
|
||||||
|
draw.image(atlas.get(&format!("{}_7", panel.frame)).unwrap()).position(
|
||||||
|
(panel.bottom_left.0 as f32) * TILESIZE.x,
|
||||||
|
(panel.bottom_left.1 as f32) * TILESIZE.x
|
||||||
|
);
|
||||||
|
for i in panel.bottom_left.0 + 1..panel.bottom_right.0 {
|
||||||
|
draw.image(atlas.get(&format!("{}_8", panel.frame)).unwrap()).position(
|
||||||
|
(i as f32) * TILESIZE.x,
|
||||||
|
(panel.bottom_left.1 as f32) * TILESIZE.x
|
||||||
|
);
|
||||||
|
}
|
||||||
|
draw.image(atlas.get(&format!("{}_9", panel.frame)).unwrap()).position(
|
||||||
|
(panel.bottom_right.0 as f32) * TILESIZE.x,
|
||||||
|
(panel.bottom_right.1 as f32) * TILESIZE.x
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::consts::visuals::{ VIEWPORT_H, VIEWPORT_W };
|
||||||
|
fn draw_bg(_ecs: &World, draw: &mut Draw, atlas: &HashMap<String, Texture>) {
|
||||||
|
let offset = crate::camera::get_offset();
|
||||||
|
let log = BoxDraw {
|
||||||
|
frame: "line".to_string(),
|
||||||
|
fill: false,
|
||||||
|
top_left: (0, 0),
|
||||||
|
top_right: (offset.x + VIEWPORT_W, 0),
|
||||||
|
bottom_left: (0, offset.y - 2),
|
||||||
|
bottom_right: (offset.x + VIEWPORT_W, offset.y - 2),
|
||||||
|
};
|
||||||
|
let game = BoxDraw {
|
||||||
|
frame: "line".to_string(),
|
||||||
|
fill: false,
|
||||||
|
top_left: (offset.x - 1, offset.y - 1),
|
||||||
|
top_right: (offset.x + VIEWPORT_W, offset.y - 1),
|
||||||
|
bottom_left: (offset.x - 1, offset.y + VIEWPORT_H),
|
||||||
|
bottom_right: (offset.x + VIEWPORT_W, offset.y + VIEWPORT_H),
|
||||||
|
};
|
||||||
|
let attr = BoxDraw {
|
||||||
|
frame: "line".to_string(),
|
||||||
|
fill: false,
|
||||||
|
top_left: (offset.x - 1, offset.y + VIEWPORT_H + 1),
|
||||||
|
top_right: (offset.x + VIEWPORT_W, offset.y + VIEWPORT_H + 1),
|
||||||
|
bottom_left: (offset.x - 1, (DISPLAYHEIGHT as i32) - 1),
|
||||||
|
bottom_right: (offset.x + VIEWPORT_W, (DISPLAYHEIGHT as i32) - 1),
|
||||||
|
};
|
||||||
|
let sidebox = BoxDraw {
|
||||||
|
frame: "line".to_string(),
|
||||||
|
fill: false,
|
||||||
|
top_left: (offset.x + VIEWPORT_W + 1, 0),
|
||||||
|
top_right: ((DISPLAYWIDTH as i32) - 1, 0),
|
||||||
|
bottom_left: (offset.x + VIEWPORT_W + 1, (DISPLAYHEIGHT as i32) - 1),
|
||||||
|
bottom_right: ((DISPLAYWIDTH as i32) - 1, (DISPLAYHEIGHT as i32) - 1),
|
||||||
|
};
|
||||||
|
draw_spritebox(log, draw, atlas);
|
||||||
|
draw_spritebox(game, draw, atlas);
|
||||||
|
draw_spritebox(attr, draw, atlas);
|
||||||
|
draw_spritebox(sidebox, draw, atlas);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(_app: &mut App, gfx: &mut Graphics, gs: &mut State) {
|
||||||
|
let mut draw = gfx.create_draw();
|
||||||
|
draw.clear(Color::BLACK);
|
||||||
|
let mut log = false;
|
||||||
|
let runstate = *gs.ecs.fetch::<RunState>();
|
||||||
|
match runstate {
|
||||||
|
RunState::MainMenu { .. } => {
|
||||||
|
gui::draw_mainmenu(&gs.ecs, &mut draw, &gs.atlas, &gs.font);
|
||||||
|
}
|
||||||
|
RunState::CharacterCreation { .. } => {
|
||||||
|
gui::draw_charcreation(&gs.ecs, &mut draw, &gs.atlas, &gs.font);
|
||||||
|
}
|
||||||
|
RunState::PreRun { .. } => {}
|
||||||
|
RunState::MapGeneration => {
|
||||||
|
draw_bg(&gs.ecs, &mut draw, &gs.interface);
|
||||||
|
if config::CONFIG.logging.show_mapgen && gs.mapgen_history.len() > 0 {
|
||||||
|
render_map_in_view(
|
||||||
|
&gs.mapgen_history[gs.mapgen_index],
|
||||||
|
&gs.ecs,
|
||||||
|
&mut draw,
|
||||||
|
&gs.atlas,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
gui::draw_ui2(&gs.ecs, &mut draw, &gs.atlas, &gs.font);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let map = gs.ecs.fetch::<Map>();
|
||||||
|
draw_bg(&gs.ecs, &mut draw, &gs.interface);
|
||||||
|
render_map_in_view(&*map, &gs.ecs, &mut draw, &gs.atlas, false);
|
||||||
|
// Special case: targeting needs to be drawn *below* entities, but above tiles.
|
||||||
|
if let RunState::ShowTargeting { range, item: _, x, y, aoe } = runstate {
|
||||||
|
gui::draw_targeting(&gs.ecs, &mut draw, &gs.atlas, x, y, range, aoe);
|
||||||
|
}
|
||||||
|
draw_entities(&*map, &gs.ecs, &mut draw, &gs.atlas, &gs.font);
|
||||||
|
gui::draw_ui2(&gs.ecs, &mut draw, &gs.atlas, &gs.font);
|
||||||
|
log = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match runstate {
|
||||||
|
RunState::Farlook { x, y } => {
|
||||||
|
gui::draw_farlook(&gs.ecs, x, y, &mut draw, &gs.atlas);
|
||||||
|
//draw_tooltips(&gs.ecs, ctx, Some((x, y))); TODO: Put this in draw loop
|
||||||
|
}
|
||||||
|
RunState::ShowCheatMenu => {
|
||||||
|
gui::draw_cheat_menu(&mut draw, &gs.atlas, &gs.font);
|
||||||
|
}
|
||||||
|
RunState::ActionWithDirection { .. } => {
|
||||||
|
corner_text("In what direction? [0-9]/[YUHJKLBN]", &mut draw, &gs.font);
|
||||||
|
}
|
||||||
|
RunState::GameOver => {
|
||||||
|
corner_text("Create morgue file? [Y/N]", &mut draw, &gs.font);
|
||||||
|
}
|
||||||
|
RunState::ShowInventory => {
|
||||||
|
corner_text("Use what? [aA-zZ]/[Esc.]", &mut draw, &gs.font);
|
||||||
|
let offset = crate::camera::get_offset();
|
||||||
|
let (x, y) = (
|
||||||
|
((1 + offset.x) as f32) * TILESIZE.x,
|
||||||
|
((3 + offset.y) as f32) * TILESIZE.x,
|
||||||
|
);
|
||||||
|
gui::draw_backpack_items(&gs.ecs, &mut draw, &gs.font, x, y);
|
||||||
|
}
|
||||||
|
RunState::ShowDropItem => {
|
||||||
|
corner_text("Drop what? [aA-zZ]/[Esc.]", &mut draw, &gs.font);
|
||||||
|
let offset = crate::camera::get_offset();
|
||||||
|
let (x, y) = (
|
||||||
|
((1 + offset.x) as f32) * TILESIZE.x,
|
||||||
|
((3 + offset.y) as f32) * TILESIZE.x,
|
||||||
|
);
|
||||||
|
gui::draw_backpack_items(&gs.ecs, &mut draw, &gs.font, x, y);
|
||||||
|
}
|
||||||
|
RunState::ShowRemoveItem => {
|
||||||
|
corner_text("Unequip which item? [aA-zZ]/[Esc.]", &mut draw, &gs.font);
|
||||||
|
let offset = crate::camera::get_offset();
|
||||||
|
let (x, y) = (
|
||||||
|
((1 + offset.x) as f32) * TILESIZE.x,
|
||||||
|
((3 + offset.y) as f32) * TILESIZE.x,
|
||||||
|
);
|
||||||
|
gui::draw_items(&gs.ecs, &mut draw, &gs.font, x, y, gui::Location::Equipped, None);
|
||||||
|
}
|
||||||
|
RunState::ShowTargeting { .. } => {
|
||||||
|
corner_text("Targeting which tile? [0-9]/[YUHJKLBN]", &mut draw, &gs.font);
|
||||||
|
}
|
||||||
|
RunState::HelpScreen => {
|
||||||
|
corner_text("The help screen is a placeholder! [?]", &mut draw, &gs.font);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
// TODO: Once the rest of drawing is finalised, this should be abstracted
|
||||||
|
// into some functions that make it easier to tell what is going on. But
|
||||||
|
// for the short-term:
|
||||||
|
// 1. notan::Text is required for rich text drawing, rather than just the
|
||||||
|
// basics that are accessible with notan::Draw's .text() method.
|
||||||
|
// 2. notan::Text cannot be projected, and rendering both Draw and Text
|
||||||
|
// requires two GPU calls instead of just one.
|
||||||
|
// 3. To fix this, our log is drawn to notan::Text, then rendered to a
|
||||||
|
// render texture, and applied as any other image to notan::Draw.
|
||||||
|
// 4. notan::Draw is projected, and then rendered, and everything works.
|
||||||
|
// Further stuff: Make the render texture only as large as is required,
|
||||||
|
// so text cannot escape the bounds of the logbox.
|
||||||
|
let (width, height) = gfx.size();
|
||||||
|
let win_size = vec2(width as f32, height as f32);
|
||||||
|
let (projection, _) = calc_projection(win_size, WORK_SIZE);
|
||||||
|
if log {
|
||||||
|
let buffer = gfx
|
||||||
|
.create_render_texture(width, height)
|
||||||
|
.build()
|
||||||
|
.expect("Failed to create render texture");
|
||||||
|
gamelog::render_log(
|
||||||
|
&buffer,
|
||||||
|
gfx,
|
||||||
|
&gs.font,
|
||||||
|
&(TILESIZE.x, TILESIZE.x * 6.0 + 4.0),
|
||||||
|
(VIEWPORT_W as f32) * TILESIZE.x,
|
||||||
|
5
|
||||||
|
);
|
||||||
|
draw.image(&buffer)
|
||||||
|
.position(0.0, 0.0)
|
||||||
|
.size(width as f32, height as f32);
|
||||||
|
}
|
||||||
|
draw.set_projection(Some(projection));
|
||||||
|
gfx.render(&draw);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(ctx: &mut App, state: &mut State) {
|
||||||
|
state.update(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn corner_text(text: &str, draw: &mut Draw, font: &Fonts) {
|
||||||
|
let offset = crate::camera::get_offset();
|
||||||
|
draw.text(&font.b(), &text)
|
||||||
|
.position(((offset.x + 1) as f32) * TILESIZE.x, ((offset.y + 1) as f32) * TILESIZE.x)
|
||||||
|
.size(FONTSIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_projection(win_size: Vec2, work_size: Vec2) -> (Mat4, f32) {
|
||||||
|
let ratio = (win_size.x / work_size.x).min(win_size.y / work_size.y);
|
||||||
|
let proj = Mat4::orthographic_rh_gl(0.0, win_size.x, win_size.y, 0.0, -1.0, 1.0);
|
||||||
|
let scale = Mat4::from_scale(vec3(ratio, ratio, 1.0));
|
||||||
|
let position = vec3(
|
||||||
|
(win_size.x - work_size.x * ratio) * 0.5,
|
||||||
|
(win_size.y - work_size.y * ratio) * 0.5,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
let trans = Mat4::from_translation(position);
|
||||||
|
(proj * trans * scale, ratio)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use bracket_lib::prelude::*;
|
||||||
use serde::{ Deserialize, Serialize };
|
use serde::{ Deserialize, Serialize };
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use std::collections::{ HashMap, HashSet };
|
use std::collections::{ HashMap, HashSet };
|
||||||
use crate::data::events::*;
|
use crate::consts::events::*;
|
||||||
|
|
||||||
#[derive(Default, Serialize, Deserialize, Clone)]
|
#[derive(Default, Serialize, Deserialize, Clone)]
|
||||||
pub struct MasterDungeonMap {
|
pub struct MasterDungeonMap {
|
||||||
|
|
@ -112,26 +112,7 @@ fn make_scroll_name(rng: &mut RandomNumberGenerator) -> String {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
const POTION_COLOURS: &[&str] = &[
|
const POTION_COLOURS: &[&str] = &["blue", "red", "green", "yellow", "black"];
|
||||||
"red",
|
|
||||||
"orange",
|
|
||||||
"yellow",
|
|
||||||
"green",
|
|
||||||
"blue",
|
|
||||||
"indigo",
|
|
||||||
"violet",
|
|
||||||
"black",
|
|
||||||
"white",
|
|
||||||
"silver",
|
|
||||||
"gold",
|
|
||||||
"rainbow",
|
|
||||||
"blood",
|
|
||||||
"purple",
|
|
||||||
"cyan",
|
|
||||||
"brown",
|
|
||||||
"grey",
|
|
||||||
"octarine",
|
|
||||||
];
|
|
||||||
const POTION_ADJECTIVES: &[&str] = &[
|
const POTION_ADJECTIVES: &[&str] = &[
|
||||||
"swirling",
|
"swirling",
|
||||||
"viscous",
|
"viscous",
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use crate::data::events::*;
|
use crate::consts::events::*;
|
||||||
|
|
||||||
const TRY_SPAWN_CHANCE: i32 = 70;
|
const TRY_SPAWN_CHANCE: i32 = 70;
|
||||||
const FEATURE_MESSAGE_CHANCE: i32 = 110;
|
const FEATURE_MESSAGE_CHANCE: i32 = 110;
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,42 @@
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
|
use specs::prelude::*;
|
||||||
use serde::{ Deserialize, Serialize };
|
use serde::{ Deserialize, Serialize };
|
||||||
use std::collections::{ HashSet, HashMap };
|
use std::collections::{ HashSet, HashMap };
|
||||||
mod tiletype;
|
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;
|
mod interval_spawning_system;
|
||||||
pub use interval_spawning_system::{ maybe_map_message, try_spawn_interval };
|
pub use interval_spawning_system::{ maybe_map_message, try_spawn_interval };
|
||||||
pub mod dungeon;
|
pub mod dungeon;
|
||||||
pub use dungeon::{ level_transition, MasterDungeonMap };
|
pub use dungeon::{ level_transition, MasterDungeonMap };
|
||||||
pub mod themes;
|
pub mod themes;
|
||||||
use super::data::visuals::{
|
use super::consts::visuals::{
|
||||||
BRIGHTEN_FG_COLOUR_BY,
|
BRIGHTEN_FG_COLOUR_BY,
|
||||||
GLOBAL_OFFSET_MIN_CLAMP,
|
GLOBAL_OFFSET_MIN_CLAMP,
|
||||||
GLOBAL_OFFSET_MAX_CLAMP,
|
GLOBAL_OFFSET_MAX_CLAMP,
|
||||||
|
SPRITE_OFFSET_MIN_CLAMP,
|
||||||
|
SPRITE_OFFSET_MAX_CLAMP,
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: If the map size gets too small, entities stop being rendered starting from the right.
|
// FIXME: If the map size gets too small, entities stop being rendered starting from the right.
|
||||||
// i.e. on a map size of 40*40, only entities to the left of the player are rendered.
|
// i.e. on a map size of 40*40, only entities to the left of the player are rendered.
|
||||||
// on a map size of 42*42, the player can see entities up to 2 tiles to their right.
|
// on a map size of 42*42, the player can see entities up to 2 tiles to their right.
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct MapMemory {
|
||||||
|
pub sprite: String,
|
||||||
|
pub fg: RGB,
|
||||||
|
pub offset: (f32, f32),
|
||||||
|
pub render_order: i32,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Serialize, Deserialize, Clone)]
|
#[derive(Default, Serialize, Deserialize, Clone)]
|
||||||
pub struct Map {
|
pub struct Map {
|
||||||
pub overmap: bool,
|
pub overmap: bool,
|
||||||
|
|
@ -25,6 +44,7 @@ pub struct Map {
|
||||||
pub width: i32,
|
pub width: i32,
|
||||||
pub height: i32,
|
pub height: i32,
|
||||||
pub revealed_tiles: Vec<bool>,
|
pub revealed_tiles: Vec<bool>,
|
||||||
|
pub memory: HashMap<usize, Vec<MapMemory>>,
|
||||||
pub visible_tiles: Vec<bool>,
|
pub visible_tiles: Vec<bool>,
|
||||||
pub lit_tiles: Vec<bool>,
|
pub lit_tiles: Vec<bool>,
|
||||||
pub telepath_tiles: Vec<bool>,
|
pub telepath_tiles: Vec<bool>,
|
||||||
|
|
@ -63,6 +83,7 @@ impl Map {
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
revealed_tiles: vec![false; map_tile_count],
|
revealed_tiles: vec![false; map_tile_count],
|
||||||
|
memory: HashMap::new(),
|
||||||
visible_tiles: vec![false; map_tile_count],
|
visible_tiles: vec![false; map_tile_count],
|
||||||
lit_tiles: vec![true; map_tile_count], // NYI: Light sources. Once those exist, we can set this to false.
|
lit_tiles: vec![true; map_tile_count], // NYI: Light sources. Once those exist, we can set this to false.
|
||||||
telepath_tiles: vec![false; map_tile_count],
|
telepath_tiles: vec![false; map_tile_count],
|
||||||
|
|
@ -91,9 +112,9 @@ impl Map {
|
||||||
rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP),
|
rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP),
|
||||||
);
|
);
|
||||||
map.colour_offset[idx].1 = (
|
map.colour_offset[idx].1 = (
|
||||||
rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP),
|
rng.range(SPRITE_OFFSET_MIN_CLAMP, SPRITE_OFFSET_MAX_CLAMP),
|
||||||
rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP),
|
rng.range(SPRITE_OFFSET_MIN_CLAMP, SPRITE_OFFSET_MAX_CLAMP),
|
||||||
rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP),
|
rng.range(SPRITE_OFFSET_MIN_CLAMP, SPRITE_OFFSET_MAX_CLAMP),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,53 @@
|
||||||
use super::{ Map, Point, TileType };
|
use super::{ Map, Point, TileType };
|
||||||
use crate::data::visuals::*;
|
use crate::consts::visuals::*;
|
||||||
use crate::config::CONFIG;
|
use crate::config::CONFIG;
|
||||||
use crate::data::ids::*;
|
use crate::consts::ids::*;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use std::ops::{ Add, Mul };
|
use std::ops::{ Add, Mul };
|
||||||
|
use notan::prelude::*;
|
||||||
|
|
||||||
|
pub fn get_sprite_for_id(idx: usize, map: &Map, other_pos: Option<Point>) -> (&str, Color) {
|
||||||
|
let bloody = if map.bloodstains.contains_key(&idx) {
|
||||||
|
Some(map.bloodstains[&idx])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let f = map.colour_offset[idx].0.0; // Using offset as a source of random.
|
||||||
|
let (sprite, offset, mut colour) = match map.tiles[idx] {
|
||||||
|
TileType::Wall =>
|
||||||
|
(
|
||||||
|
map.tiles[idx].sprite(check_if_base(TileType::Wall, idx, map), f, bloody),
|
||||||
|
map.tiles[idx].offset(),
|
||||||
|
map.tiles[idx].col(bloody),
|
||||||
|
),
|
||||||
|
_ =>
|
||||||
|
(
|
||||||
|
map.tiles[idx].sprite(false, f, bloody),
|
||||||
|
map.tiles[idx].offset(),
|
||||||
|
map.tiles[idx].col(bloody),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
// Get the right modifier for visibility - darkened by distance from POV, or full dark for out-of-view.
|
||||||
|
let visibility = if !map.visible_tiles[idx] {
|
||||||
|
NON_VISIBLE_MULTIPLIER
|
||||||
|
} else {
|
||||||
|
if other_pos.is_some() {
|
||||||
|
darken_by_distance(
|
||||||
|
Point::new((idx as i32) % map.width, (idx as i32) / map.width),
|
||||||
|
other_pos.unwrap()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Apply our offsets to our base colour.
|
||||||
|
colour = apply_colour_offset(colour, map, idx, offset, true);
|
||||||
|
// Apply our visibility modifier
|
||||||
|
colour = colour.mul(visibility);
|
||||||
|
// Convert to a notan colour.
|
||||||
|
let tint = Color::from_rgb(colour.r, colour.g, colour.b);
|
||||||
|
return (sprite, tint);
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the renderables for a tile, with darkening/offset/post-processing/etc. Passing a val for "debug" will ignore viewshed.
|
/// 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(
|
pub fn get_tile_renderables_for_id(
|
||||||
|
|
@ -136,10 +180,24 @@ fn get_forest_theme_renderables(idx:usize, map: &Map, debug: Option<bool>) -> (F
|
||||||
return (glyph, fg, bg, offsets, bg_offsets);
|
return (glyph, fg, bg, offsets, bg_offsets);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_revealed_and_wall(map: &Map, x: i32, y: i32, debug: Option<bool>) -> bool {
|
fn is_revealed_and(tt: TileType, map: &Map, x: i32, y: i32, debug: Option<bool>) -> bool {
|
||||||
let idx = map.xy_idx(x, y);
|
let idx = map.xy_idx(x, y);
|
||||||
map.tiles[idx] == TileType::Wall &&
|
map.tiles[idx] == tt && (if debug.is_none() { map.revealed_tiles[idx] } else { true })
|
||||||
(if debug.is_none() { map.revealed_tiles[idx] } else { true })
|
}
|
||||||
|
|
||||||
|
fn check_if_base(tt: TileType, idx: usize, map: &Map) -> bool {
|
||||||
|
let x = (idx as i32) % map.width;
|
||||||
|
let y = (idx as i32) / map.width;
|
||||||
|
// If we're on the edge, it can only be a base sprite.
|
||||||
|
if y > map.height - 2 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If the tile below is a revealed wall, we're not the base.
|
||||||
|
if is_revealed_and(tt, map, x, y + 1, None) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// If the tile below isn't a revealed wall, we're the base.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wall_glyph(map: &Map, x: i32, y: i32, debug: Option<bool>) -> FontCharType {
|
fn wall_glyph(map: &Map, x: i32, y: i32, debug: Option<bool>) -> FontCharType {
|
||||||
|
|
@ -156,37 +214,37 @@ fn wall_glyph(map: &Map, x: i32, y: i32, debug: Option<bool>) -> FontCharType {
|
||||||
let mut mask: u8 = 0;
|
let mut mask: u8 = 0;
|
||||||
let diagonals_matter: Vec<u8> = vec![7, 11, 13, 14, 15];
|
let diagonals_matter: Vec<u8> = vec![7, 11, 13, 14, 15];
|
||||||
|
|
||||||
if is_revealed_and_wall(map, x, y - 1, debug) {
|
if is_revealed_and(TileType::Wall, map, x, y - 1, debug) {
|
||||||
// N
|
// N
|
||||||
mask += 1;
|
mask += 1;
|
||||||
}
|
}
|
||||||
if is_revealed_and_wall(map, x, y + 1, debug) {
|
if is_revealed_and(TileType::Wall, map, x, y + 1, debug) {
|
||||||
// S
|
// S
|
||||||
mask += 2;
|
mask += 2;
|
||||||
}
|
}
|
||||||
if is_revealed_and_wall(map, x - 1, y, debug) {
|
if is_revealed_and(TileType::Wall, map, x - 1, y, debug) {
|
||||||
// W
|
// W
|
||||||
mask += 4;
|
mask += 4;
|
||||||
}
|
}
|
||||||
if is_revealed_and_wall(map, x + 1, y, debug) {
|
if is_revealed_and(TileType::Wall, map, x + 1, y, debug) {
|
||||||
// E
|
// E
|
||||||
mask += 8;
|
mask += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
if diagonals_matter.contains(&mask) {
|
if diagonals_matter.contains(&mask) {
|
||||||
if is_revealed_and_wall(map, x + 1, y - 1, debug) {
|
if is_revealed_and(TileType::Wall, map, x + 1, y - 1, debug) {
|
||||||
// Top right
|
// Top right
|
||||||
mask += 16;
|
mask += 16;
|
||||||
}
|
}
|
||||||
if is_revealed_and_wall(map, x - 1, y - 1, debug) {
|
if is_revealed_and(TileType::Wall, map, x - 1, y - 1, debug) {
|
||||||
// Top left
|
// Top left
|
||||||
mask += 32;
|
mask += 32;
|
||||||
}
|
}
|
||||||
if is_revealed_and_wall(map, x + 1, y + 1, debug) {
|
if is_revealed_and(TileType::Wall, map, x + 1, y + 1, debug) {
|
||||||
// Bottom right
|
// Bottom right
|
||||||
mask += 64;
|
mask += 64;
|
||||||
}
|
}
|
||||||
if is_revealed_and_wall(map, x - 1, y + 1, debug) {
|
if is_revealed_and(TileType::Wall, map, x - 1, y + 1, debug) {
|
||||||
// Bottom left
|
// Bottom left
|
||||||
mask += 128;
|
mask += 128;
|
||||||
}
|
}
|
||||||
|
|
@ -326,11 +384,11 @@ pub fn multiply_by_float(rgb: RGB, offsets: (f32, f32, f32)) -> RGB {
|
||||||
return RGB::from_f32(r, g, b);
|
return RGB::from_f32(r, g, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn darken_by_distance(pos: Point, other_pos: Point) -> f32 {
|
pub fn darken_by_distance(pos: Point, other_pos: Point) -> f32 {
|
||||||
let distance = DistanceAlg::Pythagoras.distance2d(pos, other_pos) as f32; // Get distance in tiles.
|
let distance = DistanceAlg::Pythagoras.distance2d(pos, other_pos) as f32; // Get distance in tiles.
|
||||||
let interp_factor =
|
let interp_factor =
|
||||||
(distance - START_DARKEN_AT_N_TILES) /
|
(distance - START_DARKEN_AT_N_TILES) /
|
||||||
((crate::data::entity::DEFAULT_VIEWSHED_STANDARD as f32) - START_DARKEN_AT_N_TILES);
|
((crate::consts::entity::DEFAULT_VIEWSHED_STANDARD as f32) - START_DARKEN_AT_N_TILES);
|
||||||
let interp_factor = interp_factor.max(0.0).min(1.0); // Clamp [0-1]
|
let interp_factor = interp_factor.max(0.0).min(1.0); // Clamp [0-1]
|
||||||
let result =
|
let result =
|
||||||
1.0 -
|
1.0 -
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
use serde::{ Deserialize, Serialize };
|
use serde::{ Deserialize, Serialize };
|
||||||
|
use bracket_lib::prelude::*;
|
||||||
|
use crate::consts::visuals::*;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize, Debug)]
|
#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize, Debug)]
|
||||||
pub enum TileType {
|
pub enum TileType {
|
||||||
|
|
@ -27,9 +29,117 @@ pub enum TileType {
|
||||||
ToOvermap(i32),
|
ToOvermap(i32),
|
||||||
ToLocal(i32),
|
ToLocal(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TileType {
|
||||||
|
pub fn sprite(&self, base: bool, float: f32, bloody: Option<RGB>) -> &str {
|
||||||
|
if base {
|
||||||
|
return self.h(float, bloody);
|
||||||
|
}
|
||||||
|
return self.v(float, bloody);
|
||||||
|
}
|
||||||
|
fn h(&self, float: f32, _bloody: Option<RGB>) -> &str {
|
||||||
|
let options = match self {
|
||||||
|
TileType::Wall => vec!["wall_b", "wall_b_cracked"],
|
||||||
|
_ => unreachable!("Tried to get a h (base) sprite for a non-wall tile."),
|
||||||
|
};
|
||||||
|
return options[(float * (options.len() as f32)) as usize];
|
||||||
|
}
|
||||||
|
fn v(&self, float: f32, bloody: Option<RGB>) -> &str {
|
||||||
|
let mut options = match self {
|
||||||
|
TileType::ImpassableMountain => vec!["wall_b"],
|
||||||
|
TileType::Wall => vec!["wall"],
|
||||||
|
TileType::DeepWater => vec!["water", "water2"],
|
||||||
|
TileType::Fence => vec!["tiles4"],
|
||||||
|
TileType::Bars => vec!["wall_b"],
|
||||||
|
TileType::Floor => vec!["dot"],
|
||||||
|
TileType::WoodFloor => vec!["planks", "planks_missing", "planks_missing2"],
|
||||||
|
TileType::Gravel => vec!["fluff", "fluff2"],
|
||||||
|
TileType::Road =>
|
||||||
|
vec![
|
||||||
|
"tiles",
|
||||||
|
"tiles_missing",
|
||||||
|
"tiles_missing2",
|
||||||
|
"tiles_missing3",
|
||||||
|
"tiles_missing4",
|
||||||
|
"tiles_missing5",
|
||||||
|
"tiles_missing6"
|
||||||
|
],
|
||||||
|
TileType::Grass => vec!["fluff", "fluff2"],
|
||||||
|
TileType::Foliage => vec!["grass_small", "grass"],
|
||||||
|
TileType::HeavyFoliage => vec!["grass_flower"],
|
||||||
|
TileType::Sand => vec!["fluff", "fluff2"],
|
||||||
|
TileType::ShallowWater => vec!["water", "water2"],
|
||||||
|
TileType::Bridge => vec!["planks"],
|
||||||
|
TileType::DownStair => vec!["stair_down"],
|
||||||
|
TileType::UpStair => vec!["stair_up"],
|
||||||
|
TileType::ToLocal(_) => vec!["stair_down"],
|
||||||
|
TileType::ToOvermap(_) => vec!["stair_up"],
|
||||||
|
};
|
||||||
|
if bloody.is_some() && tile_walkable(*self) {
|
||||||
|
options.extend(
|
||||||
|
vec!["blood1", "blood2", "blood3", "blood4", "blood5", "blood6", "blood7"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return options[(float * (options.len() as f32)) as usize];
|
||||||
|
}
|
||||||
|
pub fn offset(&self) -> (i32, i32, i32) {
|
||||||
|
match self {
|
||||||
|
TileType::ImpassableMountain => IMPASSABLE_MOUNTAIN_OFFSETS,
|
||||||
|
TileType::Wall => WALL_OFFSETS,
|
||||||
|
TileType::DeepWater => DEEP_WATER_OFFSETS,
|
||||||
|
TileType::Fence => FENCE_OFFSETS,
|
||||||
|
TileType::Bars => BARS_OFFSETS,
|
||||||
|
TileType::Floor => FLOOR_OFFSETS,
|
||||||
|
TileType::WoodFloor => WOOD_FLOOR_OFFSETS,
|
||||||
|
TileType::Gravel => GRAVEL_OFFSETS,
|
||||||
|
TileType::Road => ROAD_OFFSETS,
|
||||||
|
TileType::Grass => GRASS_OFFSETS,
|
||||||
|
TileType::Foliage => FOLIAGE_OFFSETS,
|
||||||
|
TileType::HeavyFoliage => HEAVY_FOLIAGE_OFFSETS,
|
||||||
|
TileType::Sand => SAND_OFFSETS,
|
||||||
|
TileType::ShallowWater => SHALLOW_WATER_OFFSETS,
|
||||||
|
TileType::Bridge => BRIDGE_OFFSETS,
|
||||||
|
TileType::DownStair => STAIR_OFFSETS,
|
||||||
|
TileType::UpStair => STAIR_OFFSETS,
|
||||||
|
TileType::ToLocal(_) => WALL_OFFSETS,
|
||||||
|
TileType::ToOvermap(_) => WALL_OFFSETS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn col(&self, bloody: Option<RGB>) -> RGB {
|
||||||
|
if let Some(bloody) = bloody {
|
||||||
|
return bloody;
|
||||||
|
}
|
||||||
|
RGB::named(match self {
|
||||||
|
TileType::ImpassableMountain => IMPASSABLE_MOUNTAIN_COLOUR,
|
||||||
|
TileType::Wall => WALL_COLOUR,
|
||||||
|
TileType::DeepWater => DEEP_WATER_COLOUR,
|
||||||
|
TileType::Fence => FENCE_COLOUR,
|
||||||
|
TileType::Bars => BARS_COLOUR,
|
||||||
|
TileType::Floor => FLOOR_COLOUR,
|
||||||
|
TileType::WoodFloor => WOOD_FLOOR_COLOUR,
|
||||||
|
TileType::Gravel => GRAVEL_COLOUR,
|
||||||
|
TileType::Road => ROAD_COLOUR,
|
||||||
|
TileType::Grass => GRASS_COLOUR,
|
||||||
|
TileType::Foliage => FOLIAGE_COLOUR,
|
||||||
|
TileType::HeavyFoliage => HEAVY_FOLIAGE_COLOUR,
|
||||||
|
TileType::Sand => SAND_COLOUR,
|
||||||
|
TileType::ShallowWater => SHALLOW_WATER_COLOUR,
|
||||||
|
TileType::Bridge => BRIDGE_COLOUR,
|
||||||
|
TileType::DownStair => STAIR_COLOUR,
|
||||||
|
TileType::UpStair => STAIR_COLOUR,
|
||||||
|
TileType::ToLocal(_) => WALL_COLOUR,
|
||||||
|
TileType::ToOvermap(_) => WALL_COLOUR,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tile_walkable(tt: TileType) -> bool {
|
pub fn tile_walkable(tt: TileType) -> bool {
|
||||||
match tt {
|
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,
|
_ => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -40,6 +150,11 @@ pub fn tile_opaque(tt: TileType) -> bool {
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn tile_blocks_telepathy(tt: TileType) -> bool {
|
||||||
|
match tt {
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn tile_cost(tt: TileType) -> f32 {
|
pub fn tile_cost(tt: TileType) -> f32 {
|
||||||
match tt {
|
match tt {
|
||||||
TileType::Road => 0.75,
|
TileType::Road => 0.75,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use super::{
|
||||||
Foliage,
|
Foliage,
|
||||||
};
|
};
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use crate::data::names::*;
|
use crate::consts::names::*;
|
||||||
|
|
||||||
pub fn forest_builder(
|
pub fn forest_builder(
|
||||||
new_id: i32,
|
new_id: i32,
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,8 @@ use common::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use voronoi_spawning::VoronoiSpawning;
|
use voronoi_spawning::VoronoiSpawning;
|
||||||
use super::config::CONFIG;
|
use super::config::CONFIG;
|
||||||
use super::data::ids::*;
|
use super::consts::ids::*;
|
||||||
use super::data::names::*;
|
use super::consts::names::*;
|
||||||
//use wfc::WaveFunctionCollapseBuilder;
|
//use wfc::WaveFunctionCollapseBuilder;
|
||||||
mod room_exploder;
|
mod room_exploder;
|
||||||
use room_exploder::RoomExploder;
|
use room_exploder::RoomExploder;
|
||||||
|
|
@ -215,12 +215,12 @@ fn random_start_position(rng: &mut RandomNumberGenerator) -> (XStart, YStart) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_room_builder(rng: &mut RandomNumberGenerator, builder: &mut BuilderChain, end: bool) {
|
fn random_room_builder(rng: &mut RandomNumberGenerator, builder: &mut BuilderChain, end: bool) {
|
||||||
let build_roll = rng.roll_dice(1, 3);
|
let build_roll = rng.roll_dice(1, 2); // TODO: BSP Interiors, change this to 1d3 and uncomment.
|
||||||
// Start with a room builder.
|
// Start with a room builder.
|
||||||
match build_roll {
|
match build_roll {
|
||||||
1 => builder.start_with(SimpleMapBuilder::new(None)),
|
1 => builder.start_with(SimpleMapBuilder::new(None)),
|
||||||
2 => builder.start_with(BspDungeonBuilder::new()),
|
_ => builder.start_with(BspDungeonBuilder::new()),
|
||||||
_ => builder.start_with(BspInteriorBuilder::new()),
|
//_ => builder.start_with(BspInteriorBuilder::new()),
|
||||||
}
|
}
|
||||||
|
|
||||||
// BspInterior makes its own doorways. If we're not using that one,
|
// BspInterior makes its own doorways. If we're not using that one,
|
||||||
|
|
@ -301,7 +301,7 @@ fn random_shape_builder(
|
||||||
end: bool
|
end: bool
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Pick an initial builder
|
// Pick an initial builder
|
||||||
let builder_roll = rng.roll_dice(1, 16);
|
let builder_roll = rng.roll_dice(1, 13);
|
||||||
let mut want_doors = true;
|
let mut want_doors = true;
|
||||||
match builder_roll {
|
match builder_roll {
|
||||||
1 => builder.start_with(CellularAutomataBuilder::new()),
|
1 => builder.start_with(CellularAutomataBuilder::new()),
|
||||||
|
|
@ -319,11 +319,7 @@ fn random_shape_builder(
|
||||||
10 => builder.start_with(DLABuilder::central_attractor()),
|
10 => builder.start_with(DLABuilder::central_attractor()),
|
||||||
11 => builder.start_with(DLABuilder::insectoid()),
|
11 => builder.start_with(DLABuilder::insectoid()),
|
||||||
12 => builder.start_with(VoronoiBuilder::pythagoras()),
|
12 => builder.start_with(VoronoiBuilder::pythagoras()),
|
||||||
13 => builder.start_with(VoronoiBuilder::manhattan()),
|
_ => builder.start_with(VoronoiBuilder::manhattan()),
|
||||||
_ =>
|
|
||||||
builder.start_with(
|
|
||||||
PrefabBuilder::constant(prefab_builder::prefab_levels::WFC_POPULATED)
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 'Select' the centre by placing a starting position, and cull everywhere unreachable.
|
// 'Select' the centre by placing a starting position, and cull everywhere unreachable.
|
||||||
|
|
@ -439,6 +435,8 @@ pub fn random_builder(
|
||||||
builder
|
builder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::consts::prelude::*;
|
||||||
|
|
||||||
pub fn level_builder(
|
pub fn level_builder(
|
||||||
id: i32,
|
id: i32,
|
||||||
rng: &mut RandomNumberGenerator,
|
rng: &mut RandomNumberGenerator,
|
||||||
|
|
@ -447,15 +445,15 @@ pub fn level_builder(
|
||||||
initial_player_level: i32
|
initial_player_level: i32
|
||||||
) -> BuilderChain {
|
) -> BuilderChain {
|
||||||
match id {
|
match id {
|
||||||
ID_OVERMAP => room_accretion(),
|
ID_OVERMAP => overmap_builder(),
|
||||||
ID_TOWN => town_builder(id, rng, width, height, 0, initial_player_level),
|
ID_TOWN => town_builder(id, rng, width, height, 0, initial_player_level),
|
||||||
ID_TOWN2 => forest_builder(id, rng, width, height, 1, initial_player_level),
|
ID_TOWN2 => forest_builder(id, rng, width, height, 1, initial_player_level),
|
||||||
ID_TOWN3 =>
|
ID_TOWN3 =>
|
||||||
random_builder(
|
random_builder(
|
||||||
id,
|
id,
|
||||||
rng,
|
rng,
|
||||||
width,
|
VIEWPORT_W,
|
||||||
height,
|
VIEWPORT_H,
|
||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
initial_player_level,
|
initial_player_level,
|
||||||
|
|
@ -466,8 +464,8 @@ pub fn level_builder(
|
||||||
random_builder(
|
random_builder(
|
||||||
id,
|
id,
|
||||||
rng,
|
rng,
|
||||||
width,
|
VIEWPORT_W,
|
||||||
height,
|
VIEWPORT_H,
|
||||||
4 + diff(ID_INFINITE, id),
|
4 + diff(ID_INFINITE, id),
|
||||||
1 + diff(ID_INFINITE, id),
|
1 + diff(ID_INFINITE, id),
|
||||||
initial_player_level,
|
initial_player_level,
|
||||||
|
|
@ -478,8 +476,8 @@ pub fn level_builder(
|
||||||
random_builder(
|
random_builder(
|
||||||
id,
|
id,
|
||||||
rng,
|
rng,
|
||||||
width,
|
VIEWPORT_W,
|
||||||
height,
|
VIEWPORT_H,
|
||||||
1,
|
1,
|
||||||
404,
|
404,
|
||||||
initial_player_level,
|
initial_player_level,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ pub mod prefab_levels;
|
||||||
pub mod prefab_sections;
|
pub mod prefab_sections;
|
||||||
pub mod prefab_vaults;
|
pub mod prefab_vaults;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use crate::data::ids::*;
|
use crate::consts::ids::*;
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|
|
||||||
|
|
@ -5,61 +5,12 @@ pub struct PrefabLevel {
|
||||||
pub height: usize,
|
pub height: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub const WFC_POPULATED: PrefabLevel = PrefabLevel { template: LEVEL_MAP, width: 80, height: 43 };
|
|
||||||
pub const OVERMAP: PrefabLevel = PrefabLevel { template: OVERMAP_TEMPLATE, width: 69, height: 41 };
|
pub const OVERMAP: PrefabLevel = PrefabLevel { template: OVERMAP_TEMPLATE, width: 69, height: 41 };
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
const LEVEL_MAP: &str =
|
|
||||||
"
|
|
||||||
################################################################################
|
|
||||||
# ######################################################## #########
|
|
||||||
# @ ###### ######### #### ################### #######
|
|
||||||
# #### g # ############### #####
|
|
||||||
# #### # # ####### #### ############# ###
|
|
||||||
##### ######### # # ####### ######### #### ##### ###
|
|
||||||
##### ######### ###### ####### o ######### #### ## ##### ###
|
|
||||||
## #### ######### ### ## o ###
|
|
||||||
##### ######### ### #### ####### ## ##### ###
|
|
||||||
##### ######### ### #### ####### # ### ## ##### ###
|
|
||||||
##### ######### ### #### ####### ####### ##### o ###
|
|
||||||
### ## ### #### ####### ################ ###
|
|
||||||
### ## ### o ###### ########### # ############ ###
|
|
||||||
### ## ### ###### ########### ### ###
|
|
||||||
### % ###### ########### # ### ! ## ###
|
|
||||||
### ## ### ###### ## ####### ## ###
|
|
||||||
### ## ### ## ### ##### # ######################## #####
|
|
||||||
### ## ### ## ### ##### # # ###################### #####
|
|
||||||
#### ## ####### ###### ##### ### #### o ########### ###### #####
|
|
||||||
#### ## ####### ###### #### ## #### # ######### ###### ######
|
|
||||||
# ## ####### ###### #### ## #### ############ ##### ######
|
|
||||||
# g ## ####### ###### #### ## % ########### o o #### # #
|
|
||||||
# ## ### #### ## #### # ####### ## ## #### g #
|
|
||||||
####### ####### #### ###### ! ! ### # #
|
|
||||||
###### ##### #### # ###### ### ######
|
|
||||||
##### ##### # ########## ### ######
|
|
||||||
##### ! ### ###### # ########## o##o ### # ##
|
|
||||||
##### ### ####### ## # ###### ### g ##
|
|
||||||
# ## #### ######## ### o ####### ^########^ #### # ##
|
|
||||||
# g # ###### ######## ##### ####### ^ ^ #### ######
|
|
||||||
# ##g#### ###### ######## ################ ##### ######
|
|
||||||
# ## ########## ########## ######## ################# ###### #
|
|
||||||
##### ######### ########## % ######## ################### ######## ## #
|
|
||||||
#### ### ######## ########## ######## #################### ########## # #
|
|
||||||
### ##### ###### ######### ######## ########### ####### # g# #
|
|
||||||
### ##### ############### ### ########### ####### #### #
|
|
||||||
### ##### #### ############## ######## g g ########### #### # ^ #
|
|
||||||
#### ###^#### ############# ######## ##### #### # g# #
|
|
||||||
##### ###### ### ######## ##### g #### ! ####^^ #
|
|
||||||
#!%^## ### ## ########## ######## gg g # > #
|
|
||||||
#!%^ ### ### ############### ######## ##### g #### # g# #
|
|
||||||
# %^## ^ ### ############### ######## ##### ##################
|
|
||||||
################################################################################";
|
|
||||||
|
|
||||||
const OVERMAP_TEMPLATE: &str =
|
const OVERMAP_TEMPLATE: &str =
|
||||||
"
|
"
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈........≈≈≈≈≈≈≈≈
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈...........≈≈≈≈≈≈≈
|
^^^^^^^^^^^^^^^^^^^^^^^^^^≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈..≈......≈≈≈≈≈≈≈
|
||||||
^^^^^^^^^^^^^^^....^^^^^^≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈.............≈≈≈≈≈
|
^^^^^^^^^^^^^^^....^^^^^^≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈.............≈≈≈≈≈
|
||||||
^^^^^^^^^^^^^^...........≈≈≈≈≈≈≈≈≈........≈≈≈≈≈≈≈≈..............≈≈≈≈≈
|
^^^^^^^^^^^^^^...........≈≈≈≈≈≈≈≈≈........≈≈≈≈≈≈≈≈..............≈≈≈≈≈
|
||||||
^^^^^^^^^^^^^............≈≈≈≈≈≈≈≈...........≈≈≈≈≈..............≈≈≈≈≈≈
|
^^^^^^^^^^^^^............≈≈≈≈≈≈≈≈...........≈≈≈≈≈..............≈≈≈≈≈≈
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use super::{ BuilderMap, MetaMapBuilder, Rect, TileType };
|
use super::{ BuilderMap, MetaMapBuilder, Rect, TileType };
|
||||||
use crate::tile_walkable;
|
use crate::tile_walkable;
|
||||||
use crate::data::messages::{
|
use crate::consts::messages::{
|
||||||
FEATURE_TREANTS,
|
FEATURE_TREANTS,
|
||||||
FEATURE_BARRACKS_GOBLIN,
|
FEATURE_BARRACKS_GOBLIN,
|
||||||
FEATURE_BARRACKS_KOBOLD,
|
FEATURE_BARRACKS_KOBOLD,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use super::{ BuilderChain, BuilderMap, InitialMapBuilder, Position, TileType, FillEdges };
|
use super::{ BuilderChain, BuilderMap, InitialMapBuilder, Position, TileType, FillEdges };
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use crate::data::names::*;
|
use crate::consts::names::*;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
|
|
||||||
pub fn town_builder(
|
pub fn town_builder(
|
||||||
|
|
@ -271,6 +271,7 @@ impl TownBuilder {
|
||||||
building.1 + building.3 / 2
|
building.1 + building.3 / 2
|
||||||
);
|
);
|
||||||
build_data.map.tiles[exit_idx] = TileType::DownStair;
|
build_data.map.tiles[exit_idx] = TileType::DownStair;
|
||||||
|
build_data.spawn_list.push((exit_idx, "trapdoor".to_string()));
|
||||||
let mut to_place: Vec<&str> = vec!["npc_miner", "npc_miner", "npc_guard", "prop_chair"];
|
let mut to_place: Vec<&str> = vec!["npc_miner", "npc_miner", "npc_guard", "prop_chair"];
|
||||||
self.random_building_spawn(building, build_data, rng, &mut to_place, exit_idx)
|
self.random_building_spawn(building, build_data, rng, &mut to_place, exit_idx)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
||||||
let attack_verb = attack.1;
|
let attack_verb = attack.1;
|
||||||
// Get all offensive bonuses
|
// Get all offensive bonuses
|
||||||
let d20 = rng.roll_dice(1, 20);
|
let d20 = rng.roll_dice(1, 20);
|
||||||
let attribute_hit_bonus = attacker_attributes.dexterity.bonus;
|
let attribute_hit_bonus = attacker_attributes.dexterity.modifier();
|
||||||
let skill_hit_bonus = gamesystem::skill_bonus(Skill::Melee, &*attacker_skills);
|
let skill_hit_bonus = gamesystem::skill_bonus(Skill::Melee, &*attacker_skills);
|
||||||
let mut equipment_hit_bonus = weapon_info.hit_bonus;
|
let mut equipment_hit_bonus = weapon_info.hit_bonus;
|
||||||
for (wielded, to_hit) in (&equipped, &to_hit).join() {
|
for (wielded, to_hit) in (&equipped, &to_hit).join() {
|
||||||
|
|
@ -187,7 +187,7 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
||||||
|
|
||||||
// Get armour class
|
// Get armour class
|
||||||
let bac = target_pools.bac;
|
let bac = target_pools.bac;
|
||||||
let attribute_ac_bonus = target_attributes.dexterity.bonus / 2;
|
let attribute_ac_bonus = target_attributes.dexterity.modifier() / 2;
|
||||||
let skill_ac_bonus = gamesystem::skill_bonus(Skill::Defence, &*target_skills);
|
let skill_ac_bonus = gamesystem::skill_bonus(Skill::Defence, &*target_skills);
|
||||||
let mut armour_ac_bonus = 0;
|
let mut armour_ac_bonus = 0;
|
||||||
for (wielded, ac) in (&equipped, &ac).join() {
|
for (wielded, ac) in (&equipped, &ac).join() {
|
||||||
|
|
@ -245,19 +245,19 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
||||||
let mut attribute_damage_bonus = weapon_info.damage_bonus;
|
let mut attribute_damage_bonus = weapon_info.damage_bonus;
|
||||||
match weapon_info.attribute {
|
match weapon_info.attribute {
|
||||||
WeaponAttribute::Dexterity => {
|
WeaponAttribute::Dexterity => {
|
||||||
attribute_damage_bonus += attacker_attributes.dexterity.bonus;
|
attribute_damage_bonus += attacker_attributes.dexterity.modifier();
|
||||||
}
|
}
|
||||||
WeaponAttribute::Strength => {
|
WeaponAttribute::Strength => {
|
||||||
attribute_damage_bonus += attacker_attributes.strength.bonus;
|
attribute_damage_bonus += attacker_attributes.strength.modifier();
|
||||||
}
|
}
|
||||||
WeaponAttribute::Finesse => {
|
WeaponAttribute::Finesse => {
|
||||||
if
|
if
|
||||||
attacker_attributes.dexterity.bonus >
|
attacker_attributes.dexterity.modifier() >
|
||||||
attacker_attributes.strength.bonus
|
attacker_attributes.strength.modifier()
|
||||||
{
|
{
|
||||||
attribute_damage_bonus += attacker_attributes.dexterity.bonus;
|
attribute_damage_bonus += attacker_attributes.dexterity.modifier();
|
||||||
} else {
|
} else {
|
||||||
attribute_damage_bonus += attacker_attributes.strength.bonus;
|
attribute_damage_bonus += attacker_attributes.strength.modifier();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -295,11 +295,6 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pos = positions.get(wants_melee.target);
|
|
||||||
if let Some(pos) = pos {
|
|
||||||
particle_builder.damage_taken(pos.x, pos.y);
|
|
||||||
}
|
|
||||||
add_effect(
|
add_effect(
|
||||||
Some(entity),
|
Some(entity),
|
||||||
EffectType::Damage { amount: damage, damage_type: weapon_info.damage_type },
|
EffectType::Damage { amount: damage, damage_type: weapon_info.damage_type },
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use specs::prelude::*;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use to_char;
|
use to_char;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::data::events::*;
|
use crate::consts::events::*;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub fn create_morgue_file(ecs: &World) {
|
pub fn create_morgue_file(ecs: &World) {
|
||||||
|
|
@ -36,6 +36,7 @@ fn create_file_name(ecs: &World, morgue_dir: &str) -> String {
|
||||||
let pools = ecs.read_storage::<Pools>();
|
let pools = ecs.read_storage::<Pools>();
|
||||||
let pool = pools.get(*e).unwrap();
|
let pool = pools.get(*e).unwrap();
|
||||||
let class = match ecs.read_storage::<HasClass>().get(*e).unwrap().name {
|
let class = match ecs.read_storage::<HasClass>().get(*e).unwrap().name {
|
||||||
|
Class::Unset => "classless",
|
||||||
Class::Fighter => "fighter",
|
Class::Fighter => "fighter",
|
||||||
Class::Wizard => "wizard",
|
Class::Wizard => "wizard",
|
||||||
Class::Rogue => "rogue",
|
Class::Rogue => "rogue",
|
||||||
|
|
@ -47,7 +48,7 @@ fn create_file_name(ecs: &World, morgue_dir: &str) -> String {
|
||||||
Ancestry::Dwarf => "dwarf",
|
Ancestry::Dwarf => "dwarf",
|
||||||
Ancestry::Gnome => "gnome",
|
Ancestry::Gnome => "gnome",
|
||||||
Ancestry::Catfolk => "catfolk",
|
Ancestry::Catfolk => "catfolk",
|
||||||
Ancestry::NULL => "NULL",
|
Ancestry::Unset => "NULL",
|
||||||
};
|
};
|
||||||
return format!(
|
return format!(
|
||||||
"{}/lv{}-{}-{}-{}.txt",
|
"{}/lv{}-{}-{}-{}.txt",
|
||||||
|
|
@ -64,6 +65,7 @@ fn create_morgue_string(ecs: &World) -> String {
|
||||||
let mut morgue_info: String = Default::default();
|
let mut morgue_info: String = Default::default();
|
||||||
let e = ecs.fetch::<Entity>();
|
let e = ecs.fetch::<Entity>();
|
||||||
let class = match ecs.read_storage::<HasClass>().get(*e).unwrap().name {
|
let class = match ecs.read_storage::<HasClass>().get(*e).unwrap().name {
|
||||||
|
Class::Unset => "classless",
|
||||||
Class::Fighter => "fighter",
|
Class::Fighter => "fighter",
|
||||||
Class::Wizard => "wizard",
|
Class::Wizard => "wizard",
|
||||||
Class::Rogue => "rogue",
|
Class::Rogue => "rogue",
|
||||||
|
|
@ -75,7 +77,7 @@ fn create_morgue_string(ecs: &World) -> String {
|
||||||
Ancestry::Dwarf => "dwarf",
|
Ancestry::Dwarf => "dwarf",
|
||||||
Ancestry::Gnome => "gnome",
|
Ancestry::Gnome => "gnome",
|
||||||
Ancestry::Catfolk => "catfolk",
|
Ancestry::Catfolk => "catfolk",
|
||||||
Ancestry::NULL => "NULL",
|
Ancestry::Unset => "NULL",
|
||||||
};
|
};
|
||||||
let pools = ecs.read_storage::<Pools>();
|
let pools = ecs.read_storage::<Pools>();
|
||||||
let pool = pools.get(*e).unwrap();
|
let pool = pools.get(*e).unwrap();
|
||||||
|
|
@ -127,19 +129,19 @@ fn draw_tombstone(ecs: &World, len: usize) -> String {
|
||||||
pool.mana.max,
|
pool.mana.max,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
attr.strength.base + attr.strength.modifiers,
|
attr.strength.base + attr.strength.bonuses,
|
||||||
attr.strength.bonus,
|
attr.strength.modifier(),
|
||||||
attr.constitution.base + attr.constitution.modifiers,
|
attr.constitution.base + attr.constitution.bonuses,
|
||||||
attr.constitution.bonus,
|
attr.constitution.modifier(),
|
||||||
attr.wisdom.base + attr.wisdom.modifiers,
|
attr.wisdom.base + attr.wisdom.bonuses,
|
||||||
attr.wisdom.bonus,
|
attr.wisdom.modifier(),
|
||||||
"",
|
"",
|
||||||
attr.dexterity.base + attr.dexterity.modifiers,
|
attr.dexterity.base + attr.dexterity.bonuses,
|
||||||
attr.dexterity.bonus,
|
attr.dexterity.modifier(),
|
||||||
attr.intelligence.base + attr.intelligence.modifiers,
|
attr.intelligence.base + attr.intelligence.bonuses,
|
||||||
attr.intelligence.bonus,
|
attr.intelligence.modifier(),
|
||||||
attr.charisma.base + attr.charisma.modifiers,
|
attr.charisma.base + attr.charisma.bonuses,
|
||||||
attr.charisma.bonus,
|
attr.charisma.modifier(),
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
map.name,
|
map.name,
|
||||||
|
|
@ -162,10 +164,10 @@ fn draw_map(ecs: &World) -> String {
|
||||||
if idx == map.xy_idx(point.x, point.y) {
|
if idx == map.xy_idx(point.x, point.y) {
|
||||||
glyph_u16 = to_cp437('@');
|
glyph_u16 = to_cp437('@');
|
||||||
} else if crate::spatial::has_tile_content(idx) {
|
} else if crate::spatial::has_tile_content(idx) {
|
||||||
let mut render_order = 0;
|
let mut render_order = 4;
|
||||||
crate::spatial::for_each_tile_content(idx, |e| {
|
crate::spatial::for_each_tile_content(idx, |e| {
|
||||||
if let Some(renderable) = ecs.read_storage::<Renderable>().get(e) {
|
if let Some(renderable) = ecs.read_storage::<Renderable>().get(e) {
|
||||||
if renderable.render_order >= render_order {
|
if renderable.render_order <= render_order {
|
||||||
render_order = renderable.render_order;
|
render_order = renderable.render_order;
|
||||||
glyph_u16 = renderable.glyph;
|
glyph_u16 = renderable.glyph;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,27 @@
|
||||||
use super::{ ParticleLifetime, Position, Renderable, BTerm };
|
use super::{ ParticleLifetime, Position, Renderable, BTerm };
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
|
use notan::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use crate::data::visuals::{ DEFAULT_PARTICLE_LIFETIME, SHORT_PARTICLE_LIFETIME };
|
use crate::consts::visuals::{ DEFAULT_PARTICLE_LIFETIME, SHORT_PARTICLE_LIFETIME };
|
||||||
|
|
||||||
/// Runs each tick, deleting particles who are past their expiry.
|
/// Runs each tick, deleting particles who are past their expiry.
|
||||||
// Should make an addition to this to also spawn delayed particles,
|
// Should make an addition to this to also spawn delayed particles,
|
||||||
// running through a list and removing the frame_time_ms from the
|
// running through a list and removing the frame_time_ms from the
|
||||||
// delay. When delay is <= 0, make a particle_builder.request for
|
// delay. When delay is <= 0, make a particle_builder.request for
|
||||||
// the particle.
|
// the particle.
|
||||||
pub fn particle_ticker(ecs: &mut World, ctx: &BTerm) {
|
pub fn particle_ticker(ecs: &mut World, ctx: &App) {
|
||||||
cull_dead_particles(ecs, ctx);
|
cull_dead_particles(ecs, ctx);
|
||||||
create_delayed_particles(ecs, ctx);
|
create_delayed_particles(ecs, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cull_dead_particles(ecs: &mut World, ctx: &BTerm) {
|
fn cull_dead_particles(ecs: &mut World, ctx: &App) {
|
||||||
let mut dead_particles: Vec<Entity> = Vec::new();
|
let mut dead_particles: Vec<Entity> = Vec::new();
|
||||||
{
|
{
|
||||||
// Age out particles
|
// Age out particles
|
||||||
let mut particles = ecs.write_storage::<ParticleLifetime>();
|
let mut particles = ecs.write_storage::<ParticleLifetime>();
|
||||||
let entities = ecs.entities();
|
let entities = ecs.entities();
|
||||||
for (entity, mut particle) in (&entities, &mut particles).join() {
|
for (entity, mut particle) in (&entities, &mut particles).join() {
|
||||||
particle.lifetime_ms -= ctx.frame_time_ms;
|
particle.lifetime_ms -= ctx.timer.delta_f32() * 1000.0;
|
||||||
if particle.lifetime_ms < 0.0 {
|
if particle.lifetime_ms < 0.0 {
|
||||||
dead_particles.push(entity);
|
dead_particles.push(entity);
|
||||||
}
|
}
|
||||||
|
|
@ -39,18 +40,18 @@ pub fn check_queue(ecs: &World) -> bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_delayed_particles(ecs: &mut World, ctx: &BTerm) {
|
fn create_delayed_particles(ecs: &mut World, ctx: &App) {
|
||||||
let mut particle_builder = ecs.write_resource::<ParticleBuilder>();
|
let mut particle_builder = ecs.write_resource::<ParticleBuilder>();
|
||||||
let mut handled_particles: Vec<ParticleRequest> = Vec::new();
|
let mut handled_particles: Vec<ParticleRequest> = Vec::new();
|
||||||
for delayed_particle in particle_builder.delayed_requests.iter_mut() {
|
for delayed_particle in particle_builder.delayed_requests.iter_mut() {
|
||||||
delayed_particle.delay -= ctx.frame_time_ms;
|
delayed_particle.delay -= ctx.timer.delta_f32() * 1000.0;
|
||||||
if delayed_particle.delay < 0.0 {
|
if delayed_particle.delay < 0.0 {
|
||||||
handled_particles.push(ParticleRequest {
|
handled_particles.push(ParticleRequest {
|
||||||
x: delayed_particle.particle.x,
|
x: delayed_particle.particle.x,
|
||||||
y: delayed_particle.particle.y,
|
y: delayed_particle.particle.y,
|
||||||
fg: delayed_particle.particle.fg,
|
fg: delayed_particle.particle.fg,
|
||||||
bg: delayed_particle.particle.bg,
|
|
||||||
glyph: delayed_particle.particle.glyph,
|
glyph: delayed_particle.particle.glyph,
|
||||||
|
sprite: delayed_particle.particle.sprite.clone(),
|
||||||
lifetime: delayed_particle.particle.lifetime,
|
lifetime: delayed_particle.particle.lifetime,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -80,12 +81,7 @@ fn create_delayed_particles(ecs: &mut World, ctx: &BTerm) {
|
||||||
.insert(p, Position { x: handled.x, y: handled.y })
|
.insert(p, Position { x: handled.x, y: handled.y })
|
||||||
.expect("Could not insert position");
|
.expect("Could not insert position");
|
||||||
renderables
|
renderables
|
||||||
.insert(p, Renderable {
|
.insert(p, Renderable::new(handled.glyph, handled.sprite, handled.fg, 0))
|
||||||
fg: handled.fg,
|
|
||||||
bg: handled.bg,
|
|
||||||
glyph: handled.glyph,
|
|
||||||
render_order: 0,
|
|
||||||
})
|
|
||||||
.expect("Could not insert renderables");
|
.expect("Could not insert renderables");
|
||||||
particles
|
particles
|
||||||
.insert(p, ParticleLifetime { lifetime_ms: handled.lifetime })
|
.insert(p, ParticleLifetime { lifetime_ms: handled.lifetime })
|
||||||
|
|
@ -98,8 +94,8 @@ pub struct ParticleRequest {
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
fg: RGB,
|
fg: RGB,
|
||||||
bg: RGB,
|
|
||||||
glyph: FontCharType,
|
glyph: FontCharType,
|
||||||
|
sprite: String,
|
||||||
lifetime: f32,
|
lifetime: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,11 +122,11 @@ impl ParticleBuilder {
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
fg: RGB,
|
fg: RGB,
|
||||||
bg: RGB,
|
|
||||||
glyph: FontCharType,
|
glyph: FontCharType,
|
||||||
|
sprite: String,
|
||||||
lifetime: f32
|
lifetime: f32
|
||||||
) {
|
) {
|
||||||
self.requests.push(ParticleRequest { x, y, fg, bg, glyph, lifetime });
|
self.requests.push(ParticleRequest { x, y, fg, glyph, sprite, lifetime });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delay(
|
pub fn delay(
|
||||||
|
|
@ -138,150 +134,43 @@ impl ParticleBuilder {
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
fg: RGB,
|
fg: RGB,
|
||||||
bg: RGB,
|
|
||||||
glyph: FontCharType,
|
glyph: FontCharType,
|
||||||
|
sprite: String,
|
||||||
lifetime: f32,
|
lifetime: f32,
|
||||||
delay: f32
|
delay: f32
|
||||||
) {
|
) {
|
||||||
self.delayed_requests.push(DelayedParticleRequest {
|
self.delayed_requests.push(DelayedParticleRequest {
|
||||||
delay: delay,
|
delay: delay,
|
||||||
particle: ParticleRequest { x, y, fg, bg, glyph, lifetime },
|
particle: ParticleRequest { x, y, fg, glyph, sprite, lifetime },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MASSIVE TODO: Animate these, or make them random. PLACEHOLDER.
|
||||||
pub fn damage_taken(&mut self, x: i32, y: i32) {
|
pub fn damage_taken(&mut self, x: i32, y: i32) {
|
||||||
self.request(
|
self.request(x, y, RGB::named(RED), to_cp437('‼'), "slash1".to_string(), 75.0);
|
||||||
x,
|
self.delay(x, y, RGB::named(RED), to_cp437('‼'), "slash2".to_string(), 75.0, 75.0);
|
||||||
y,
|
self.delay(x, y, RGB::named(RED), to_cp437('‼'), "slash3".to_string(), 75.0, 150.0);
|
||||||
RGB::named(ORANGE),
|
|
||||||
RGB::named(BLACK),
|
|
||||||
to_cp437('‼'),
|
|
||||||
DEFAULT_PARTICLE_LIFETIME
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attack_miss(&mut self, x: i32, y: i32) {
|
pub fn attack_miss(&mut self, x: i32, y: i32) {
|
||||||
self.request(
|
self.request(
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
RGB::named(CYAN),
|
RGB::named(CYAN),
|
||||||
RGB::named(BLACK),
|
|
||||||
to_cp437('‼'),
|
to_cp437('‼'),
|
||||||
|
"slash1".to_string(),
|
||||||
DEFAULT_PARTICLE_LIFETIME
|
DEFAULT_PARTICLE_LIFETIME
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kick(&mut self, x: i32, y: i32) {
|
pub fn kick(&mut self, x: i32, y: i32) {
|
||||||
self.request(
|
self.request(
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
RGB::named(CHOCOLATE),
|
RGB::named(CHOCOLATE),
|
||||||
RGB::named(BLACK),
|
|
||||||
to_cp437('‼'),
|
to_cp437('‼'),
|
||||||
|
"kick".to_string(),
|
||||||
SHORT_PARTICLE_LIFETIME
|
SHORT_PARTICLE_LIFETIME
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Makes a particle request in the shape of an 'x'. Sort of.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn request_star(
|
|
||||||
&mut self,
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
fg: RGB,
|
|
||||||
bg: RGB,
|
|
||||||
glyph: FontCharType,
|
|
||||||
lifetime: f32,
|
|
||||||
secondary_fg: RGB
|
|
||||||
) {
|
|
||||||
let eighth_l = lifetime / 8.0;
|
|
||||||
let quarter_l = eighth_l * 2.0;
|
|
||||||
self.request(x, y, fg, bg, glyph, lifetime);
|
|
||||||
self.delay(
|
|
||||||
x + 1,
|
|
||||||
y + 1,
|
|
||||||
secondary_fg.lerp(bg, 0.8),
|
|
||||||
bg,
|
|
||||||
to_cp437('/'),
|
|
||||||
quarter_l,
|
|
||||||
eighth_l
|
|
||||||
);
|
|
||||||
self.delay(
|
|
||||||
x + 1,
|
|
||||||
y - 1,
|
|
||||||
secondary_fg.lerp(bg, 0.6),
|
|
||||||
bg,
|
|
||||||
to_cp437('\\'),
|
|
||||||
quarter_l,
|
|
||||||
quarter_l
|
|
||||||
);
|
|
||||||
self.delay(
|
|
||||||
x - 1,
|
|
||||||
y - 1,
|
|
||||||
secondary_fg.lerp(bg, 0.2),
|
|
||||||
bg,
|
|
||||||
to_cp437('/'),
|
|
||||||
quarter_l,
|
|
||||||
eighth_l * 3.0
|
|
||||||
);
|
|
||||||
self.delay(
|
|
||||||
x - 1,
|
|
||||||
y + 1,
|
|
||||||
secondary_fg.lerp(bg, 0.4),
|
|
||||||
bg,
|
|
||||||
to_cp437('\\'),
|
|
||||||
quarter_l,
|
|
||||||
lifetime
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes a rainbow particle request in the shape of an 'x'. Sort of.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn request_rainbow_star(&mut self, x: i32, y: i32, glyph: FontCharType, lifetime: f32) {
|
|
||||||
let bg = RGB::named(BLACK);
|
|
||||||
let eighth_l = lifetime / 8.0;
|
|
||||||
let quarter_l = eighth_l * 2.0;
|
|
||||||
let half_l = quarter_l * 2.0;
|
|
||||||
|
|
||||||
self.request(x, y, RGB::named(CYAN), bg, glyph, lifetime);
|
|
||||||
self.delay(x + 1, y + 1, RGB::named(RED), bg, to_cp437('\\'), half_l, eighth_l);
|
|
||||||
self.delay(x + 1, y - 1, RGB::named(ORANGE), bg, to_cp437('/'), half_l, quarter_l);
|
|
||||||
self.delay(x - 1, y - 1, RGB::named(GREEN), bg, to_cp437('\\'), half_l, eighth_l * 3.0);
|
|
||||||
self.delay(x - 1, y + 1, RGB::named(YELLOW), bg, to_cp437('/'), half_l, half_l);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes a rainbow particle request. Sort of.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn request_rainbow(&mut self, x: i32, y: i32, glyph: FontCharType, lifetime: f32) {
|
|
||||||
let bg = RGB::named(BLACK);
|
|
||||||
let eighth_l = lifetime / 8.0;
|
|
||||||
|
|
||||||
self.request(x, y, RGB::named(RED), bg, glyph, eighth_l);
|
|
||||||
self.delay(x, y, RGB::named(ORANGE), bg, glyph, eighth_l, eighth_l);
|
|
||||||
self.delay(x, y, RGB::named(YELLOW), bg, glyph, eighth_l, eighth_l * 2.0);
|
|
||||||
self.delay(x, y, RGB::named(GREEN), bg, glyph, eighth_l, eighth_l * 3.0);
|
|
||||||
self.delay(x, y, RGB::named(BLUE), bg, glyph, eighth_l, eighth_l * 4.0);
|
|
||||||
self.delay(x, y, RGB::named(INDIGO), bg, glyph, eighth_l, eighth_l * 5.0);
|
|
||||||
self.delay(x, y, RGB::named(VIOLET), bg, glyph, eighth_l, eighth_l * 6.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Makes a particle request in the shape of a +.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn request_plus(
|
|
||||||
&mut self,
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
fg: RGB,
|
|
||||||
bg: RGB,
|
|
||||||
glyph: FontCharType,
|
|
||||||
lifetime: f32
|
|
||||||
) {
|
|
||||||
self.request(x, y, fg, bg, glyph, lifetime * 2.0);
|
|
||||||
self.request(x + 1, y, fg, bg, to_cp437('─'), lifetime);
|
|
||||||
self.request(x - 1, y, fg, bg, to_cp437('─'), lifetime);
|
|
||||||
self.request(x, y + 1, fg, bg, to_cp437('│'), lifetime);
|
|
||||||
self.request(x, y - 1, fg, bg, to_cp437('│'), lifetime);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ParticleSpawnSystem {}
|
pub struct ParticleSpawnSystem {}
|
||||||
|
|
@ -305,12 +194,15 @@ impl<'a> System<'a> for ParticleSpawnSystem {
|
||||||
.insert(p, Position { x: new_particle.x, y: new_particle.y })
|
.insert(p, Position { x: new_particle.x, y: new_particle.y })
|
||||||
.expect("Could not insert position");
|
.expect("Could not insert position");
|
||||||
renderables
|
renderables
|
||||||
.insert(p, Renderable {
|
.insert(
|
||||||
fg: new_particle.fg,
|
p,
|
||||||
bg: new_particle.bg,
|
Renderable::new(
|
||||||
glyph: new_particle.glyph,
|
new_particle.glyph,
|
||||||
render_order: 0,
|
new_particle.sprite.clone(),
|
||||||
})
|
new_particle.fg,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
.expect("Could not insert renderables");
|
.expect("Could not insert renderables");
|
||||||
particles
|
particles
|
||||||
.insert(p, ParticleLifetime { lifetime_ms: new_particle.lifetime })
|
.insert(p, ParticleLifetime { lifetime_ms: new_particle.lifetime })
|
||||||
|
|
|
||||||
224
src/player.rs
224
src/player.rs
|
|
@ -30,15 +30,20 @@ use super::{
|
||||||
Viewshed,
|
Viewshed,
|
||||||
WantsToMelee,
|
WantsToMelee,
|
||||||
WantsToPickupItem,
|
WantsToPickupItem,
|
||||||
|
WantsToAssignKey,
|
||||||
get_dest,
|
get_dest,
|
||||||
Destination,
|
Destination,
|
||||||
DamageType,
|
DamageType,
|
||||||
|
effects::sound,
|
||||||
};
|
};
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use std::cmp::{ max, min };
|
use std::cmp::{ max, min };
|
||||||
use crate::data::events::*;
|
use crate::consts::events::*;
|
||||||
use crate::data::ids::*;
|
use crate::consts::ids::*;
|
||||||
|
use crate::gui::with_article;
|
||||||
|
use notan::prelude::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState {
|
pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
let mut positions = ecs.write_storage::<Position>();
|
let mut positions = ecs.write_storage::<Position>();
|
||||||
|
|
@ -89,6 +94,7 @@ pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
if door.open == true {
|
if door.open == true {
|
||||||
let renderables = ecs.read_storage::<Renderable>();
|
let renderables = ecs.read_storage::<Renderable>();
|
||||||
if multiple_tile_content {
|
if multiple_tile_content {
|
||||||
|
sound::door_resist(destination_idx);
|
||||||
if let Some(name) = names.get(potential_target) {
|
if let Some(name) = names.get(potential_target) {
|
||||||
gamelog::Logger
|
gamelog::Logger
|
||||||
::new()
|
::new()
|
||||||
|
|
@ -99,7 +105,8 @@ pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
.append("is blocked.")
|
.append("is blocked.")
|
||||||
.log();
|
.log();
|
||||||
}
|
}
|
||||||
} else if rng.roll_dice(1, 6) + attributes.strength.bonus < 2 {
|
} else if rng.roll_dice(1, 6) + attributes.strength.modifier() < 2 {
|
||||||
|
sound::door_resist(destination_idx);
|
||||||
if let Some(name) = names.get(potential_target) {
|
if let Some(name) = names.get(potential_target) {
|
||||||
gamelog::Logger
|
gamelog::Logger
|
||||||
::new()
|
::new()
|
||||||
|
|
@ -111,13 +118,18 @@ pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
.log();
|
.log();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
sound::door_close(destination_idx);
|
||||||
door.open = false;
|
door.open = false;
|
||||||
|
if door.blocks_vis {
|
||||||
blocks_visibility
|
blocks_visibility
|
||||||
.insert(potential_target, BlocksVisibility {})
|
.insert(potential_target, BlocksVisibility {})
|
||||||
.expect("Unable to insert BlocksVisibility.");
|
.expect("Unable to insert BlocksVisibility.");
|
||||||
|
}
|
||||||
|
if door.blocks_move {
|
||||||
blocks_movement
|
blocks_movement
|
||||||
.insert(potential_target, BlocksTile {})
|
.insert(potential_target, BlocksTile {})
|
||||||
.expect("Unable to insert BlocksTile.");
|
.expect("Unable to insert BlocksTile.");
|
||||||
|
}
|
||||||
if let Some(name) = names.get(potential_target) {
|
if let Some(name) = names.get(potential_target) {
|
||||||
gamelog::Logger
|
gamelog::Logger
|
||||||
::new()
|
::new()
|
||||||
|
|
@ -132,7 +144,7 @@ pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
std::mem::drop(renderables);
|
std::mem::drop(renderables);
|
||||||
let mut renderables = ecs.write_storage::<Renderable>();
|
let mut renderables = ecs.write_storage::<Renderable>();
|
||||||
let render_data = renderables.get_mut(potential_target).unwrap();
|
let render_data = renderables.get_mut(potential_target).unwrap();
|
||||||
render_data.glyph = to_cp437('+'); // Nethack open door, maybe just use '/' instead.
|
render_data.swap();
|
||||||
door_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y));
|
door_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y));
|
||||||
}
|
}
|
||||||
result = RunState::Ticking;
|
result = RunState::Ticking;
|
||||||
|
|
@ -201,7 +213,8 @@ pub fn open(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
if let Some(door) = door {
|
if let Some(door) = door {
|
||||||
if door.open == false {
|
if door.open == false {
|
||||||
let renderables = ecs.read_storage::<Renderable>();
|
let renderables = ecs.read_storage::<Renderable>();
|
||||||
if rng.roll_dice(1, 6) + attributes.strength.bonus < 2 {
|
if rng.roll_dice(1, 6) + attributes.strength.modifier() < 2 {
|
||||||
|
sound::door_resist(destination_idx);
|
||||||
if let Some(name) = names.get(potential_target) {
|
if let Some(name) = names.get(potential_target) {
|
||||||
gamelog::Logger
|
gamelog::Logger
|
||||||
::new()
|
::new()
|
||||||
|
|
@ -213,6 +226,7 @@ pub fn open(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
.log();
|
.log();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
sound::door_open(destination_idx);
|
||||||
door.open = true;
|
door.open = true;
|
||||||
blocks_visibility.remove(potential_target);
|
blocks_visibility.remove(potential_target);
|
||||||
blocks_movement.remove(potential_target);
|
blocks_movement.remove(potential_target);
|
||||||
|
|
@ -229,7 +243,7 @@ pub fn open(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
std::mem::drop(renderables);
|
std::mem::drop(renderables);
|
||||||
let mut renderables = ecs.write_storage::<Renderable>();
|
let mut renderables = ecs.write_storage::<Renderable>();
|
||||||
let render_data = renderables.get_mut(potential_target).unwrap();
|
let render_data = renderables.get_mut(potential_target).unwrap();
|
||||||
render_data.glyph = to_cp437('▓'); // Nethack open door, maybe just use '/' instead.
|
render_data.swap();
|
||||||
door_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y));
|
door_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y));
|
||||||
}
|
}
|
||||||
result = RunState::Ticking;
|
result = RunState::Ticking;
|
||||||
|
|
@ -330,15 +344,15 @@ pub fn kick(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
None,
|
None,
|
||||||
EffectType::Particle {
|
EffectType::Particle {
|
||||||
glyph: to_cp437('‼'),
|
glyph: to_cp437('‼'),
|
||||||
|
sprite: "gnome".to_string(), // FIXME: REMOVE THE GNOMES
|
||||||
fg: RGB::named(CHOCOLATE),
|
fg: RGB::named(CHOCOLATE),
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
lifespan: 150.0,
|
lifespan: 150.0,
|
||||||
delay: 0.0,
|
delay: 0.0,
|
||||||
},
|
},
|
||||||
Targets::Entity { target: potential_target }
|
Targets::Entity { target: potential_target }
|
||||||
);
|
);
|
||||||
// ~33% chance of breaking it down + str
|
// ~33% chance of breaking it down + str
|
||||||
if rng.roll_dice(1, 10) + attributes.strength.bonus > 6 {
|
if rng.roll_dice(1, 10) + attributes.strength.modifier() > 6 {
|
||||||
gamelog::Logger
|
gamelog::Logger
|
||||||
::new()
|
::new()
|
||||||
.append("As you kick the")
|
.append("As you kick the")
|
||||||
|
|
@ -390,8 +404,8 @@ pub fn kick(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
None,
|
None,
|
||||||
EffectType::Particle {
|
EffectType::Particle {
|
||||||
glyph: to_cp437('‼'),
|
glyph: to_cp437('‼'),
|
||||||
|
sprite: "gnome".to_string(), // FIXME: REMOVE THE GNOMES
|
||||||
fg: RGB::named(CHOCOLATE),
|
fg: RGB::named(CHOCOLATE),
|
||||||
bg: RGB::named(BLACK),
|
|
||||||
lifespan: 150.0,
|
lifespan: 150.0,
|
||||||
delay: 0.0,
|
delay: 0.0,
|
||||||
},
|
},
|
||||||
|
|
@ -491,7 +505,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState
|
||||||
}
|
}
|
||||||
let door = doors.get_mut(potential_target);
|
let door = doors.get_mut(potential_target);
|
||||||
if let Some(door) = door {
|
if let Some(door) = door {
|
||||||
if door.open == false {
|
if door.open == false && door.blocks_move {
|
||||||
if let Some(name) = names.get(potential_target) {
|
if let Some(name) = names.get(potential_target) {
|
||||||
let colour = if
|
let colour = if
|
||||||
let Some(_) = ecs.read_storage::<Item>().get(potential_target)
|
let Some(_) = ecs.read_storage::<Item>().get(potential_target)
|
||||||
|
|
@ -560,11 +574,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");
|
let mut logger = gamelog::Logger::new().append("You see");
|
||||||
for i in 0..seen_items.len() {
|
for i in 0..seen_items.len() {
|
||||||
if i > 0 && i < seen_items.len() {
|
if i > 0 && i < seen_items.len() {
|
||||||
logger = logger.append(", a");
|
logger = logger.append(", ");
|
||||||
}
|
}
|
||||||
logger = logger
|
logger = logger
|
||||||
.colour(seen_items[i].1)
|
.colour(seen_items[i].1)
|
||||||
.append_n(&seen_items[i].0)
|
.append_n(with_article(&seen_items[i].0))
|
||||||
.colour(WHITE);
|
.colour(WHITE);
|
||||||
}
|
}
|
||||||
logger.period().log();
|
logger.period().log();
|
||||||
|
|
@ -633,7 +647,9 @@ fn get_item(ecs: &mut World) -> RunState {
|
||||||
return RunState::AwaitingInput;
|
return RunState::AwaitingInput;
|
||||||
}
|
}
|
||||||
Some(item) => {
|
Some(item) => {
|
||||||
|
let mut assignkey = ecs.write_storage::<WantsToAssignKey>();
|
||||||
let mut pickup = ecs.write_storage::<WantsToPickupItem>();
|
let mut pickup = ecs.write_storage::<WantsToPickupItem>();
|
||||||
|
assignkey.insert(item, WantsToAssignKey {}).expect("Unable to insert WantsToAssignKey");
|
||||||
pickup
|
pickup
|
||||||
.insert(*player_entity, WantsToPickupItem { collected_by: *player_entity, item })
|
.insert(*player_entity, WantsToPickupItem { collected_by: *player_entity, item })
|
||||||
.expect("Unable to insert want to pickup item.");
|
.expect("Unable to insert want to pickup item.");
|
||||||
|
|
@ -642,135 +658,117 @@ fn get_item(ecs: &mut World) -> RunState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn player_input(gs: &mut State, ctx: &mut BTerm, on_overmap: bool) -> RunState {
|
fn try_descend(ecs: &mut World) -> RunState {
|
||||||
match ctx.key {
|
let dest = try_change_level(ecs, false);
|
||||||
None => {
|
let curr_map_id = ecs.fetch::<Map>().id;
|
||||||
return RunState::AwaitingInput;
|
return match dest {
|
||||||
}
|
Destination::None => RunState::AwaitingInput,
|
||||||
Some(key) =>
|
Destination::NextLevel => RunState::GoToLevel(curr_map_id + 1, TileType::UpStair),
|
||||||
match key {
|
Destination::PreviousLevel => RunState::GoToLevel(curr_map_id - 1, TileType::DownStair),
|
||||||
// Cardinals
|
Destination::ToLocal(id) => RunState::GoToLevel(ID_OVERMAP, TileType::ToLocal(id)),
|
||||||
VirtualKeyCode::Left | VirtualKeyCode::Numpad4 | VirtualKeyCode::H => {
|
Destination::ToOvermap(id) => RunState::GoToLevel(id, TileType::ToOvermap(id)),
|
||||||
return try_move_player(-1, 0, &mut gs.ecs);
|
};
|
||||||
}
|
}
|
||||||
VirtualKeyCode::Right | VirtualKeyCode::Numpad6 | VirtualKeyCode::L => {
|
fn try_ascend(ecs: &mut World) -> RunState {
|
||||||
return try_move_player(1, 0, &mut gs.ecs);
|
let dest = try_change_level(ecs, true);
|
||||||
}
|
let curr_map_id = ecs.fetch::<Map>().id;
|
||||||
VirtualKeyCode::Up | VirtualKeyCode::Numpad8 | VirtualKeyCode::K => {
|
return match dest {
|
||||||
return try_move_player(0, -1, &mut gs.ecs);
|
Destination::None => RunState::AwaitingInput,
|
||||||
}
|
Destination::NextLevel => RunState::GoToLevel(curr_map_id + 1, TileType::UpStair),
|
||||||
VirtualKeyCode::Down | VirtualKeyCode::Numpad2 | VirtualKeyCode::J => {
|
Destination::PreviousLevel => RunState::GoToLevel(curr_map_id - 1, TileType::DownStair),
|
||||||
return try_move_player(0, 1, &mut gs.ecs);
|
Destination::ToLocal(id) => RunState::GoToLevel(ID_OVERMAP, TileType::ToLocal(id)),
|
||||||
}
|
Destination::ToOvermap(id) => RunState::GoToLevel(id, TileType::ToOvermap(id)),
|
||||||
// Diagonals
|
};
|
||||||
VirtualKeyCode::Numpad9 | VirtualKeyCode::U => {
|
}
|
||||||
return try_move_player(1, -1, &mut gs.ecs);
|
|
||||||
}
|
pub fn player_input(gs: &mut State, ctx: &mut App, on_overmap: bool) -> RunState {
|
||||||
VirtualKeyCode::Numpad7 | VirtualKeyCode::Y => {
|
let key = &ctx.keyboard;
|
||||||
return try_move_player(-1, -1, &mut gs.ecs);
|
// Movement
|
||||||
}
|
for keycode in key.pressed.iter() {
|
||||||
VirtualKeyCode::Numpad3 | VirtualKeyCode::N => {
|
match *keycode {
|
||||||
return try_move_player(1, 1, &mut gs.ecs);
|
KeyCode::Numpad1 | KeyCode::B => {
|
||||||
}
|
|
||||||
VirtualKeyCode::Numpad1 | VirtualKeyCode::B => {
|
|
||||||
return try_move_player(-1, 1, &mut gs.ecs);
|
return try_move_player(-1, 1, &mut gs.ecs);
|
||||||
}
|
}
|
||||||
// id
|
KeyCode::Numpad2 | KeyCode::Down | KeyCode::J => {
|
||||||
VirtualKeyCode::Period => {
|
return try_move_player(0, 1, &mut gs.ecs);
|
||||||
if ctx.shift {
|
}
|
||||||
let dest = try_change_level(&mut gs.ecs, false);
|
KeyCode::Numpad3 | KeyCode::N => {
|
||||||
let curr_map_id = gs.ecs.fetch::<Map>().id;
|
return try_move_player(1, 1, &mut gs.ecs);
|
||||||
return match dest {
|
}
|
||||||
// If we have no destination, do nothing.
|
KeyCode::Numpad4 | KeyCode::Left | KeyCode::H => {
|
||||||
Destination::None => RunState::AwaitingInput,
|
return try_move_player(-1, 0, &mut gs.ecs);
|
||||||
// If we want to go to the next level, go to the up-stair tile of id + 1.
|
}
|
||||||
Destination::NextLevel =>
|
KeyCode::Numpad6 | KeyCode::Right | KeyCode::L => {
|
||||||
RunState::GoToLevel(curr_map_id + 1, TileType::UpStair),
|
return try_move_player(1, 0, &mut gs.ecs);
|
||||||
// If we want to go to the previous level, go to the down-stair tile of id - 1.
|
}
|
||||||
Destination::PreviousLevel =>
|
KeyCode::Numpad7 | KeyCode::Y => {
|
||||||
RunState::GoToLevel(curr_map_id - 1, TileType::DownStair),
|
return try_move_player(-1, -1, &mut gs.ecs);
|
||||||
Destination::ToLocal(id) =>
|
}
|
||||||
RunState::GoToLevel(ID_OVERMAP, TileType::ToLocal(id)),
|
KeyCode::Numpad8 | KeyCode::Up | KeyCode::K => {
|
||||||
Destination::ToOvermap(id) =>
|
return try_move_player(0, -1, &mut gs.ecs);
|
||||||
RunState::GoToLevel(id, TileType::ToOvermap(id)),
|
}
|
||||||
};
|
KeyCode::Numpad9 | KeyCode::U => {
|
||||||
} else {
|
return try_move_player(1, -1, &mut gs.ecs);
|
||||||
return skip_turn(&mut gs.ecs); // (Wait a turn)
|
}
|
||||||
|
KeyCode::Period => {
|
||||||
|
if key.shift() {
|
||||||
|
return try_descend(&mut gs.ecs);
|
||||||
|
}
|
||||||
|
return skip_turn(&mut gs.ecs);
|
||||||
|
}
|
||||||
|
KeyCode::Comma => {
|
||||||
|
if key.shift() {
|
||||||
|
return try_ascend(&mut gs.ecs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VirtualKeyCode::Comma => {
|
KeyCode::Slash => {
|
||||||
if ctx.shift {
|
if key.shift() {
|
||||||
let dest = try_change_level(&mut gs.ecs, true);
|
|
||||||
let curr_map_id = gs.ecs.fetch::<Map>().id;
|
|
||||||
return match dest {
|
|
||||||
Destination::None => RunState::AwaitingInput,
|
|
||||||
Destination::NextLevel =>
|
|
||||||
RunState::GoToLevel(curr_map_id + 1, TileType::UpStair),
|
|
||||||
Destination::PreviousLevel =>
|
|
||||||
RunState::GoToLevel(curr_map_id - 1, TileType::DownStair),
|
|
||||||
Destination::ToLocal(id) =>
|
|
||||||
RunState::GoToLevel(ID_OVERMAP, TileType::ToLocal(id)),
|
|
||||||
Destination::ToOvermap(id) =>
|
|
||||||
RunState::GoToLevel(id, TileType::ToOvermap(id)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VirtualKeyCode::Slash => {
|
|
||||||
if ctx.shift {
|
|
||||||
return RunState::HelpScreen;
|
return RunState::HelpScreen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VirtualKeyCode::NumpadDecimal => {
|
KeyCode::C => {
|
||||||
return skip_turn(&mut gs.ecs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Items
|
|
||||||
VirtualKeyCode::C => {
|
|
||||||
if !on_overmap {
|
if !on_overmap {
|
||||||
return RunState::ActionWithDirection { function: try_door };
|
return RunState::ActionWithDirection { function: try_door };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VirtualKeyCode::O => {
|
KeyCode::O => {
|
||||||
if !on_overmap {
|
if !on_overmap {
|
||||||
return RunState::ActionWithDirection { function: open };
|
return RunState::ActionWithDirection { function: open };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VirtualKeyCode::F => {
|
KeyCode::F => {
|
||||||
if !on_overmap {
|
if !on_overmap {
|
||||||
return RunState::ActionWithDirection { function: kick };
|
return RunState::ActionWithDirection { function: kick };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VirtualKeyCode::G => {
|
KeyCode::G => {
|
||||||
return get_item(&mut gs.ecs);
|
return get_item(&mut gs.ecs);
|
||||||
}
|
}
|
||||||
VirtualKeyCode::I => {
|
KeyCode::I => {
|
||||||
return RunState::ShowInventory;
|
return RunState::ShowInventory;
|
||||||
}
|
}
|
||||||
VirtualKeyCode::D => {
|
KeyCode::D => {
|
||||||
return RunState::ShowDropItem;
|
return RunState::ShowDropItem;
|
||||||
}
|
}
|
||||||
VirtualKeyCode::R => {
|
KeyCode::R => {
|
||||||
return RunState::ShowRemoveItem;
|
return RunState::ShowRemoveItem;
|
||||||
}
|
}
|
||||||
// Other
|
KeyCode::Minus => {
|
||||||
VirtualKeyCode::Minus => {
|
|
||||||
return RunState::ShowCheatMenu;
|
return RunState::ShowCheatMenu;
|
||||||
}
|
}
|
||||||
VirtualKeyCode::Escape => {
|
KeyCode::Escape => {
|
||||||
return RunState::SaveGame;
|
return RunState::SaveGame;
|
||||||
}
|
}
|
||||||
VirtualKeyCode::X => {
|
KeyCode::X => {
|
||||||
let (min_x, _max_x, min_y, _max_y, x_offset, y_offset) = get_screen_bounds(
|
let bounds = get_screen_bounds(&gs.ecs, false);
|
||||||
&gs.ecs,
|
|
||||||
ctx
|
|
||||||
);
|
|
||||||
let ppos = gs.ecs.fetch::<Point>();
|
let ppos = gs.ecs.fetch::<Point>();
|
||||||
let (x, y) = (ppos.x + x_offset - min_x, ppos.y + y_offset - min_y);
|
let (x, y) = (
|
||||||
|
ppos.x + bounds.x_offset - bounds.min_x,
|
||||||
|
ppos.y + bounds.y_offset - bounds.min_y,
|
||||||
|
);
|
||||||
return RunState::Farlook { x, y };
|
return RunState::Farlook { x, y };
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {}
|
||||||
return RunState::AwaitingInput;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return RunState::AwaitingInput;
|
return RunState::AwaitingInput;
|
||||||
|
|
@ -781,6 +779,18 @@ fn try_change_level(ecs: &mut World, backtracking: bool) -> Destination {
|
||||||
let map = ecs.fetch::<Map>();
|
let map = ecs.fetch::<Map>();
|
||||||
let player_idx = map.xy_idx(player_pos.x, player_pos.y);
|
let player_idx = map.xy_idx(player_pos.x, player_pos.y);
|
||||||
let this_tile = map.tiles[player_idx];
|
let this_tile = map.tiles[player_idx];
|
||||||
|
let mut blocked = false;
|
||||||
|
crate::spatial::for_each_tile_content(player_idx, |potential| {
|
||||||
|
if let Some(is_door) = ecs.read_storage::<Door>().get(potential) {
|
||||||
|
if is_door.open == false {
|
||||||
|
blocked = true;
|
||||||
|
gamelog::Logger::new().append("The way is blocked.").log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if blocked {
|
||||||
|
return Destination::None;
|
||||||
|
}
|
||||||
return get_dest(this_tile, backtracking);
|
return get_dest(this_tile, backtracking);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ pub struct Item {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: Name,
|
pub name: Name,
|
||||||
pub renderable: Option<Renderable>,
|
pub renderable: Option<Renderable>,
|
||||||
|
pub avatar: Option<String>,
|
||||||
|
pub class: String,
|
||||||
pub weight: Option<f32>,
|
pub weight: Option<f32>,
|
||||||
pub value: Option<f32>,
|
pub value: Option<f32>,
|
||||||
pub equip: Option<Equippable>,
|
pub equip: Option<Equippable>,
|
||||||
|
|
@ -30,9 +32,16 @@ pub struct Equippable {
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct Renderable {
|
pub struct Renderable {
|
||||||
pub glyph: String,
|
pub glyph: String,
|
||||||
|
pub sprite: String,
|
||||||
|
pub alt: Option<String>,
|
||||||
pub fg: String,
|
pub fg: String,
|
||||||
pub bg: String,
|
pub fg_alt: Option<String>,
|
||||||
pub order: i32,
|
pub order: i32,
|
||||||
|
pub order_alt: Option<i32>,
|
||||||
|
pub x: Option<f32>,
|
||||||
|
pub x_alt: Option<f32>,
|
||||||
|
pub y: Option<f32>,
|
||||||
|
pub y_alt: Option<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,13 @@ pub struct Prop {
|
||||||
pub renderable: Option<Renderable>,
|
pub renderable: Option<Renderable>,
|
||||||
pub flags: Option<Vec<String>>,
|
pub flags: Option<Vec<String>>,
|
||||||
pub effects: Option<HashMap<String, String>>,
|
pub effects: Option<HashMap<String, String>>,
|
||||||
|
pub door: Option<Door>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Door {
|
||||||
|
pub open: bool,
|
||||||
|
pub locked: bool,
|
||||||
|
pub blocks_vis: bool,
|
||||||
|
pub blocks_move: bool,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ use crate::gamesystem::*;
|
||||||
use crate::gui::Ancestry;
|
use crate::gui::Ancestry;
|
||||||
use crate::random_table::RandomTable;
|
use crate::random_table::RandomTable;
|
||||||
use crate::config::CONFIG;
|
use crate::config::CONFIG;
|
||||||
use crate::data::visuals::BLOODSTAIN_COLOUR;
|
use crate::consts::visuals::BLOODSTAIN_COLOUR;
|
||||||
use crate::data::entity::DEFAULT_VIEWSHED_STANDARD;
|
use crate::consts::entity::DEFAULT_VIEWSHED_STANDARD;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use specs::saveload::{ MarkedBuilder, SimpleMarker };
|
use specs::saveload::{ MarkedBuilder, SimpleMarker };
|
||||||
|
|
@ -53,11 +53,6 @@ macro_rules! apply_flags {
|
||||||
"BLOCKS_VISIBILITY" => $eb = $eb.with(BlocksVisibility {}),
|
"BLOCKS_VISIBILITY" => $eb = $eb.with(BlocksVisibility {}),
|
||||||
"ENTRY_TRIGGER" => $eb = $eb.with(EntryTrigger {}),
|
"ENTRY_TRIGGER" => $eb = $eb.with(EntryTrigger {}),
|
||||||
"SINGLE_ACTIVATION" => $eb = $eb.with(SingleActivation {}),
|
"SINGLE_ACTIVATION" => $eb = $eb.with(SingleActivation {}),
|
||||||
"DOOR" => {
|
|
||||||
$eb = $eb.with(Door { open: false });
|
|
||||||
$eb = $eb.with(BlocksVisibility {});
|
|
||||||
$eb = $eb.with(BlocksTile {});
|
|
||||||
}
|
|
||||||
// --- EFFECT FLAGS ---
|
// --- EFFECT FLAGS ---
|
||||||
"FOOD" => $eb = $eb.with(ProvidesNutrition {}),
|
"FOOD" => $eb = $eb.with(ProvidesNutrition {}),
|
||||||
"CONSUMABLE" => $eb = $eb.with(Consumable {}),
|
"CONSUMABLE" => $eb = $eb.with(Consumable {}),
|
||||||
|
|
@ -66,6 +61,7 @@ macro_rules! apply_flags {
|
||||||
"IDENTIFY" => $eb = $eb.with(ProvidesIdentify {}),
|
"IDENTIFY" => $eb = $eb.with(ProvidesIdentify {}),
|
||||||
"DIGGER" => $eb = $eb.with(Digger {}),
|
"DIGGER" => $eb = $eb.with(Digger {}),
|
||||||
"MAGICMAP" => $eb = $eb.with(MagicMapper {}),
|
"MAGICMAP" => $eb = $eb.with(MagicMapper {}),
|
||||||
|
"STACKABLE" => $eb = $eb.with(Stackable {}),
|
||||||
// CAN BE DESTROYED BY DAMAGE
|
// CAN BE DESTROYED BY DAMAGE
|
||||||
"DESTRUCTIBLE" => $eb = $eb.with(Destructible {}),
|
"DESTRUCTIBLE" => $eb = $eb.with(Destructible {}),
|
||||||
// --- EQUIP SLOTS ---
|
// --- EQUIP SLOTS ---
|
||||||
|
|
@ -281,6 +277,7 @@ pub fn spawn_named_item(
|
||||||
if known_beatitude && !identified_items.contains(&item_template.name.name) {
|
if known_beatitude && !identified_items.contains(&item_template.name.name) {
|
||||||
dm.identified_items.insert(item_template.name.name.clone());
|
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(player_entity);
|
||||||
std::mem::drop(dm);
|
std::mem::drop(dm);
|
||||||
// -- DROP EVERYTHING THAT INVOLVES THE ECS BEFORE THIS POINT ---
|
// -- DROP EVERYTHING THAT INVOLVES THE ECS BEFORE THIS POINT ---
|
||||||
|
|
@ -293,12 +290,30 @@ pub fn spawn_named_item(
|
||||||
eb = eb.with(Item {
|
eb = eb.with(Item {
|
||||||
weight: item_template.weight.unwrap_or(0.0),
|
weight: item_template.weight.unwrap_or(0.0),
|
||||||
value: item_template.value.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);
|
eb = spawn_position(pos, eb, key, raws);
|
||||||
|
if needs_key {
|
||||||
|
eb = eb.with(WantsToAssignKey {});
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(renderable) = &item_template.renderable {
|
if let Some(renderable) = &item_template.renderable {
|
||||||
eb = eb.with(get_renderable_component(renderable));
|
eb = eb.with(get_renderable_component(renderable));
|
||||||
}
|
}
|
||||||
|
if let Some(avatar) = &item_template.avatar {
|
||||||
|
eb = eb.with(Avatar::new(avatar.clone()));
|
||||||
|
}
|
||||||
// BEATITUDE
|
// BEATITUDE
|
||||||
let buc = if let Some(buc_status) = buc {
|
let buc = if let Some(buc_status) = buc {
|
||||||
buc_status
|
buc_status
|
||||||
|
|
@ -392,6 +407,7 @@ pub fn spawn_named_mob(
|
||||||
if raws.mob_index.contains_key(key) {
|
if raws.mob_index.contains_key(key) {
|
||||||
let mob_template = &raws.raws.mobs[raws.mob_index[key]];
|
let mob_template = &raws.raws.mobs[raws.mob_index[key]];
|
||||||
let mut player_level = 1;
|
let mut player_level = 1;
|
||||||
|
let needs_key;
|
||||||
{
|
{
|
||||||
let pools = ecs.read_storage::<Pools>();
|
let pools = ecs.read_storage::<Pools>();
|
||||||
let player_entity = ecs.fetch::<Entity>();
|
let player_entity = ecs.fetch::<Entity>();
|
||||||
|
|
@ -399,12 +415,15 @@ pub fn spawn_named_mob(
|
||||||
if let Some(pool) = player_pool {
|
if let Some(pool) = player_pool {
|
||||||
player_level = pool.level;
|
player_level = pool.level;
|
||||||
}
|
}
|
||||||
|
needs_key = is_player_owned(&player_entity, &pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut eb;
|
let mut eb;
|
||||||
// New entity with a position, name, combatstats, and viewshed
|
// New entity with a position, name, combatstats, and viewshed
|
||||||
eb = ecs.create_entity().marked::<SimpleMarker<SerializeMe>>();
|
eb = ecs.create_entity().marked::<SimpleMarker<SerializeMe>>();
|
||||||
eb = spawn_position(pos, eb, key, raws);
|
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(Name { name: mob_template.name.clone(), plural: mob_template.name.clone() });
|
||||||
eb = eb.with(Viewshed {
|
eb = eb.with(Viewshed {
|
||||||
visible_tiles: Vec::new(),
|
visible_tiles: Vec::new(),
|
||||||
|
|
@ -445,36 +464,29 @@ pub fn spawn_named_mob(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup combat stats
|
// Setup combat stats
|
||||||
let mut attr = Attributes {
|
let mut attr = Attributes::default();
|
||||||
strength: Attribute { base: 10, modifiers: 0, bonus: 0 },
|
|
||||||
dexterity: Attribute { base: 10, modifiers: 0, bonus: 0 },
|
|
||||||
constitution: Attribute { base: 10, modifiers: 0, bonus: 0 },
|
|
||||||
intelligence: Attribute { base: 10, modifiers: 0, bonus: 0 },
|
|
||||||
wisdom: Attribute { base: 10, modifiers: 0, bonus: 0 },
|
|
||||||
charisma: Attribute { base: 10, modifiers: 0, bonus: 0 },
|
|
||||||
};
|
|
||||||
let mut mob_con = 10;
|
let mut mob_con = 10;
|
||||||
let mut mob_int = 10;
|
let mut mob_int = 10;
|
||||||
if let Some(attributes) = &mob_template.attributes {
|
if let Some(attributes) = &mob_template.attributes {
|
||||||
if let Some(str) = attributes.str {
|
if let Some(str) = attributes.str {
|
||||||
attr.strength = Attribute { base: str, modifiers: 0, bonus: attr_bonus(str) };
|
attr.strength = Attribute::new(str);
|
||||||
}
|
}
|
||||||
if let Some(dex) = attributes.dex {
|
if let Some(dex) = attributes.dex {
|
||||||
attr.strength = Attribute { base: dex, modifiers: 0, bonus: attr_bonus(dex) };
|
attr.strength = Attribute::new(dex);
|
||||||
}
|
}
|
||||||
if let Some(con) = attributes.con {
|
if let Some(con) = attributes.con {
|
||||||
attr.constitution = Attribute { base: con, modifiers: 0, bonus: attr_bonus(con) };
|
attr.constitution = Attribute::new(con);
|
||||||
mob_con = con;
|
mob_con = con;
|
||||||
}
|
}
|
||||||
if let Some(int) = attributes.int {
|
if let Some(int) = attributes.int {
|
||||||
attr.intelligence = Attribute { base: int, modifiers: 0, bonus: attr_bonus(int) };
|
attr.intelligence = Attribute::new(int);
|
||||||
mob_int = int;
|
mob_int = int;
|
||||||
}
|
}
|
||||||
if let Some(wis) = attributes.wis {
|
if let Some(wis) = attributes.wis {
|
||||||
attr.wisdom = Attribute { base: wis, modifiers: 0, bonus: attr_bonus(wis) };
|
attr.wisdom = Attribute::new(wis);
|
||||||
}
|
}
|
||||||
if let Some(cha) = attributes.cha {
|
if let Some(cha) = attributes.cha {
|
||||||
attr.charisma = Attribute { base: cha, modifiers: 0, bonus: attr_bonus(cha) };
|
attr.charisma = Attribute::new(cha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
eb = eb.with(attr);
|
eb = eb.with(attr);
|
||||||
|
|
@ -632,10 +644,18 @@ pub fn spawn_named_prop(
|
||||||
pos: SpawnType
|
pos: SpawnType
|
||||||
) -> Option<Entity> {
|
) -> Option<Entity> {
|
||||||
if raws.prop_index.contains_key(key) {
|
if raws.prop_index.contains_key(key) {
|
||||||
|
let needs_key;
|
||||||
|
{
|
||||||
|
let player_entity = ecs.fetch::<Entity>();
|
||||||
|
needs_key = is_player_owned(&player_entity, &pos);
|
||||||
|
}
|
||||||
// ENTITY BUILDER PREP
|
// ENTITY BUILDER PREP
|
||||||
let prop_template = &raws.raws.props[raws.prop_index[key]];
|
let prop_template = &raws.raws.props[raws.prop_index[key]];
|
||||||
let mut eb = ecs.create_entity().marked::<SimpleMarker<SerializeMe>>();
|
let mut eb = ecs.create_entity().marked::<SimpleMarker<SerializeMe>>();
|
||||||
eb = spawn_position(pos, eb, key, raws);
|
eb = spawn_position(pos, eb, key, raws);
|
||||||
|
if needs_key {
|
||||||
|
eb = eb.with(WantsToAssignKey {});
|
||||||
|
}
|
||||||
// APPLY MANDATORY COMPONENTS FOR A PROP:
|
// APPLY MANDATORY COMPONENTS FOR A PROP:
|
||||||
// - Name
|
// - Name
|
||||||
// - Prop {}
|
// - Prop {}
|
||||||
|
|
@ -656,6 +676,23 @@ pub fn spawn_named_prop(
|
||||||
if let Some(effects_list) = &prop_template.effects {
|
if let Some(effects_list) = &prop_template.effects {
|
||||||
apply_effects!(effects_list, eb);
|
apply_effects!(effects_list, eb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(door) = &prop_template.door {
|
||||||
|
eb = eb.with(Door {
|
||||||
|
open: door.open,
|
||||||
|
locked: door.locked,
|
||||||
|
blocks_vis: door.blocks_vis,
|
||||||
|
blocks_move: door.blocks_move,
|
||||||
|
});
|
||||||
|
if !door.open {
|
||||||
|
if door.blocks_vis {
|
||||||
|
eb = eb.with(BlocksVisibility {});
|
||||||
|
}
|
||||||
|
if door.blocks_move {
|
||||||
|
eb = eb.with(BlocksTile {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// BUILD THE ENTITY
|
// BUILD THE ENTITY
|
||||||
return Some(eb.build());
|
return Some(eb.build());
|
||||||
}
|
}
|
||||||
|
|
@ -686,14 +723,70 @@ fn spawn_position<'a>(
|
||||||
eb
|
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(
|
fn get_renderable_component(
|
||||||
renderable: &super::item_structs::Renderable
|
renderable: &super::item_structs::Renderable
|
||||||
) -> crate::components::Renderable {
|
) -> crate::components::Renderable {
|
||||||
|
let glyph = to_cp437(renderable.glyph.chars().next().unwrap());
|
||||||
|
let sprite = renderable.sprite.clone();
|
||||||
|
let sprite_alt = if let Some(sprite_alt) = &renderable.alt {
|
||||||
|
Some(sprite_alt.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let fg = RGB::from_hex(&renderable.fg).expect("Invalid RGB");
|
||||||
|
let fg_alt = if let Some(fg_alt) = &renderable.fg_alt {
|
||||||
|
Some(RGB::from_hex(&fg_alt).expect("Invalid RGB"))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let render_order = renderable.order;
|
||||||
|
let render_order_alt = if let Some(order_alt) = renderable.order_alt {
|
||||||
|
Some(order_alt)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let offset_x = if let Some(x) = renderable.x { x } else { 0.0 };
|
||||||
|
let offset_y = if let Some(y) = renderable.y { -y } else { 0.0 };
|
||||||
|
|
||||||
|
let offset_alt: Option<(f32, f32)> = if
|
||||||
|
renderable.x_alt.is_some() ||
|
||||||
|
renderable.y_alt.is_some()
|
||||||
|
{
|
||||||
|
Some((
|
||||||
|
if let Some(x) = renderable.x_alt { x } else { 0.0 },
|
||||||
|
if let Some(y) = renderable.y_alt { -y } else { 0.0 },
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
crate::components::Renderable {
|
crate::components::Renderable {
|
||||||
glyph: to_cp437(renderable.glyph.chars().next().unwrap()),
|
glyph,
|
||||||
fg: RGB::from_hex(&renderable.fg).expect("Invalid RGB"),
|
sprite,
|
||||||
bg: RGB::from_hex(&renderable.bg).expect("Invalid RGB"),
|
sprite_alt,
|
||||||
render_order: renderable.order,
|
fg,
|
||||||
|
fg_alt,
|
||||||
|
render_order,
|
||||||
|
render_order_alt,
|
||||||
|
offset: (offset_x, offset_y),
|
||||||
|
offset_alt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1017,7 +1110,7 @@ fn get_ancestry_string(ancestry: Ancestry) -> &'static str {
|
||||||
Ancestry::Gnome => {
|
Ancestry::Gnome => {
|
||||||
return "gnome";
|
return "gnome";
|
||||||
}
|
}
|
||||||
Ancestry::NULL => {
|
Ancestry::Unset => {
|
||||||
return "NULL";
|
return "NULL";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1026,35 +1119,41 @@ fn get_ancestry_string(ancestry: Ancestry) -> &'static str {
|
||||||
fn parse_particle_line(n: &str) -> SpawnParticleLine {
|
fn parse_particle_line(n: &str) -> SpawnParticleLine {
|
||||||
let tokens: Vec<_> = n.split(';').collect();
|
let tokens: Vec<_> = n.split(';').collect();
|
||||||
SpawnParticleLine {
|
SpawnParticleLine {
|
||||||
glyph: to_cp437(tokens[0].chars().next().unwrap()),
|
sprite: tokens[0].to_string(),
|
||||||
tail_glyph: to_cp437(tokens[1].chars().next().unwrap()),
|
tail_sprite: tokens[1].to_string(),
|
||||||
colour: RGB::from_hex(tokens[2]).expect("Invalid RGB"),
|
glyph: to_cp437(tokens[2].chars().next().unwrap()),
|
||||||
lifetime_ms: tokens[3].parse::<f32>().unwrap(),
|
tail_glyph: to_cp437(tokens[3].chars().next().unwrap()),
|
||||||
trail_colour: RGB::from_hex(tokens[4]).expect("Invalid trail RGB"),
|
colour: RGB::from_hex(tokens[4]).expect("Invalid RGB"),
|
||||||
trail_lifetime_ms: tokens[5].parse::<f32>().unwrap(),
|
lifetime_ms: tokens[5].parse::<f32>().unwrap(),
|
||||||
|
trail_colour: RGB::from_hex(tokens[6]).expect("Invalid trail RGB"),
|
||||||
|
trail_lifetime_ms: tokens[7].parse::<f32>().unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_particle(n: &str) -> SpawnParticleSimple {
|
fn parse_particle(n: &str) -> SpawnParticleSimple {
|
||||||
let tokens: Vec<_> = n.split(';').collect();
|
let tokens: Vec<_> = n.split(';').collect();
|
||||||
SpawnParticleSimple {
|
SpawnParticleSimple {
|
||||||
glyph: to_cp437(tokens[0].chars().next().unwrap()),
|
sprite: tokens[0].to_string(),
|
||||||
colour: RGB::from_hex(tokens[1]).expect("Invalid RGB"),
|
glyph: to_cp437(tokens[1].chars().next().unwrap()),
|
||||||
lifetime_ms: tokens[2].parse::<f32>().unwrap(),
|
colour: RGB::from_hex(tokens[2]).expect(&format!("Invalid RGB: {}", n)),
|
||||||
|
lifetime_ms: tokens[3].parse::<f32>().unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_particle_burst(n: &str) -> SpawnParticleBurst {
|
fn parse_particle_burst(n: &str) -> SpawnParticleBurst {
|
||||||
let tokens: Vec<_> = n.split(';').collect();
|
let tokens: Vec<_> = n.split(';').collect();
|
||||||
SpawnParticleBurst {
|
SpawnParticleBurst {
|
||||||
glyph: to_cp437(tokens[0].chars().next().unwrap()),
|
sprite: tokens[0].to_string(),
|
||||||
head_glyph: to_cp437(tokens[1].chars().next().unwrap()),
|
head_sprite: tokens[1].to_string(),
|
||||||
tail_glyph: to_cp437(tokens[2].chars().next().unwrap()),
|
tail_sprite: tokens[2].to_string(),
|
||||||
colour: RGB::from_hex(tokens[3]).expect("Invalid RGB"),
|
glyph: to_cp437(tokens[3].chars().next().unwrap()),
|
||||||
lerp: RGB::from_hex(tokens[4]).expect("Invalid LERP RGB"),
|
head_glyph: to_cp437(tokens[4].chars().next().unwrap()),
|
||||||
lifetime_ms: tokens[5].parse::<f32>().unwrap(),
|
tail_glyph: to_cp437(tokens[5].chars().next().unwrap()),
|
||||||
trail_colour: RGB::from_hex(tokens[6]).expect("Invalid trail RGB"),
|
colour: RGB::from_hex(tokens[6]).expect("Invalid RGB"),
|
||||||
trail_lifetime_ms: tokens[7].parse::<f32>().unwrap(),
|
lerp: RGB::from_hex(tokens[7]).expect("Invalid LERP RGB"),
|
||||||
|
lifetime_ms: tokens[8].parse::<f32>().unwrap(),
|
||||||
|
trail_colour: RGB::from_hex(tokens[9]).expect("Invalid trail RGB"),
|
||||||
|
trail_lifetime_ms: tokens[10].parse::<f32>().unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use super::components::*;
|
use super::components::*;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::error::NoError;
|
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
use specs::saveload::{
|
use specs::saveload::{
|
||||||
DeserializeComponents,
|
DeserializeComponents,
|
||||||
|
|
@ -12,11 +11,12 @@ use specs::saveload::{
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::convert::Infallible;
|
||||||
|
|
||||||
macro_rules! serialize_individually {
|
macro_rules! serialize_individually {
|
||||||
($ecs:expr, $ser:expr, $data:expr, $($type:ty),*) => {
|
($ecs:expr, $ser:expr, $data:expr, $($type:ty),*) => {
|
||||||
$(
|
$(
|
||||||
SerializeComponents::<NoError, SimpleMarker<SerializeMe>>::serialize(
|
SerializeComponents::<Infallible, SimpleMarker<SerializeMe>>::serialize(
|
||||||
&( $ecs.read_storage::<$type>(), ),
|
&( $ecs.read_storage::<$type>(), ),
|
||||||
&$data.0,
|
&$data.0,
|
||||||
&$data.1,
|
&$data.1,
|
||||||
|
|
@ -28,7 +28,13 @@ macro_rules! serialize_individually {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub fn save_game(_ecs: &mut World) {}
|
pub fn save_game(_ecs: &mut World) {
|
||||||
|
console::log(
|
||||||
|
"Unfortunately, saving isn't supported in any easy way on the web. Sorry!
|
||||||
|
You can, at least, save your morgue file after dying - it'll be written
|
||||||
|
to the log, and just needs saving into a text file."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub fn save_game(ecs: &mut World) {
|
pub fn save_game(ecs: &mut World) {
|
||||||
|
|
@ -55,8 +61,8 @@ pub fn save_game(ecs: &mut World) {
|
||||||
{
|
{
|
||||||
let data = (ecs.entities(), ecs.read_storage::<SimpleMarker<SerializeMe>>());
|
let data = (ecs.entities(), ecs.read_storage::<SimpleMarker<SerializeMe>>());
|
||||||
|
|
||||||
let writer = File::create("./savegame.json").unwrap();
|
let writer = File::create("./savegame.bin").unwrap();
|
||||||
let mut serializer = serde_json::Serializer::new(writer);
|
let mut serializer = bincode::Serializer::new(writer, bincode::options());
|
||||||
serialize_individually!(
|
serialize_individually!(
|
||||||
ecs,
|
ecs,
|
||||||
serializer,
|
serializer,
|
||||||
|
|
@ -64,6 +70,7 @@ pub fn save_game(ecs: &mut World) {
|
||||||
AOE,
|
AOE,
|
||||||
ArmourClassBonus,
|
ArmourClassBonus,
|
||||||
Attributes,
|
Attributes,
|
||||||
|
Avatar,
|
||||||
Beatitude,
|
Beatitude,
|
||||||
Bleeds,
|
Bleeds,
|
||||||
Blind,
|
Blind,
|
||||||
|
|
@ -98,6 +105,7 @@ pub fn save_game(ecs: &mut World) {
|
||||||
IntrinsicChanged,
|
IntrinsicChanged,
|
||||||
Intrinsics,
|
Intrinsics,
|
||||||
Item,
|
Item,
|
||||||
|
Key,
|
||||||
KnownSpells,
|
KnownSpells,
|
||||||
LootTable,
|
LootTable,
|
||||||
MagicItem,
|
MagicItem,
|
||||||
|
|
@ -127,17 +135,21 @@ pub fn save_game(ecs: &mut World) {
|
||||||
SpawnParticleBurst,
|
SpawnParticleBurst,
|
||||||
SpawnParticleLine,
|
SpawnParticleLine,
|
||||||
SpawnParticleSimple,
|
SpawnParticleSimple,
|
||||||
|
Stackable,
|
||||||
TakingTurn,
|
TakingTurn,
|
||||||
Telepath,
|
Telepath,
|
||||||
ToHitBonus,
|
ToHitBonus,
|
||||||
Viewshed,
|
Viewshed,
|
||||||
Charges,
|
Charges,
|
||||||
WantsToApproach,
|
WantsToApproach,
|
||||||
|
WantsToAssignKey,
|
||||||
|
WantsToDelete,
|
||||||
WantsToDropItem,
|
WantsToDropItem,
|
||||||
WantsToFlee,
|
WantsToFlee,
|
||||||
WantsToMelee,
|
WantsToMelee,
|
||||||
WantsToPickupItem,
|
WantsToPickupItem,
|
||||||
WantsToRemoveItem,
|
WantsToRemoveItem,
|
||||||
|
WantsToRemoveKey,
|
||||||
WantsToUseItem,
|
WantsToUseItem,
|
||||||
SerializationHelper,
|
SerializationHelper,
|
||||||
DMSerializationHelper
|
DMSerializationHelper
|
||||||
|
|
@ -150,13 +162,13 @@ pub fn save_game(ecs: &mut World) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn does_save_exist() -> bool {
|
pub fn does_save_exist() -> bool {
|
||||||
Path::new("./savegame.json").exists()
|
Path::new("./savegame.bin").exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! deserialize_individually {
|
macro_rules! deserialize_individually {
|
||||||
($ecs:expr, $de:expr, $data:expr, $($type:ty),*) => {
|
($ecs:expr, $de:expr, $data:expr, $($type:ty),*) => {
|
||||||
$(
|
$(
|
||||||
DeserializeComponents::<NoError, _>::deserialize(
|
DeserializeComponents::<Infallible, _>::deserialize(
|
||||||
&mut ( &mut $ecs.write_storage::<$type>(), ),
|
&mut ( &mut $ecs.write_storage::<$type>(), ),
|
||||||
&$data.0, // entities
|
&$data.0, // entities
|
||||||
&mut $data.1, // marker
|
&mut $data.1, // marker
|
||||||
|
|
@ -180,8 +192,8 @@ pub fn load_game(ecs: &mut World) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = fs::read_to_string("./savegame.json").unwrap();
|
let data = fs::read("./savegame.bin").unwrap();
|
||||||
let mut de = serde_json::Deserializer::from_str(&data);
|
let mut de = bincode::Deserializer::with_reader(&*data, bincode::options());
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut d = (
|
let mut d = (
|
||||||
|
|
@ -197,6 +209,7 @@ pub fn load_game(ecs: &mut World) {
|
||||||
AOE,
|
AOE,
|
||||||
ArmourClassBonus,
|
ArmourClassBonus,
|
||||||
Attributes,
|
Attributes,
|
||||||
|
Avatar,
|
||||||
Beatitude,
|
Beatitude,
|
||||||
Bleeds,
|
Bleeds,
|
||||||
Blind,
|
Blind,
|
||||||
|
|
@ -231,6 +244,7 @@ pub fn load_game(ecs: &mut World) {
|
||||||
IntrinsicChanged,
|
IntrinsicChanged,
|
||||||
Intrinsics,
|
Intrinsics,
|
||||||
Item,
|
Item,
|
||||||
|
Key,
|
||||||
KnownSpells,
|
KnownSpells,
|
||||||
LootTable,
|
LootTable,
|
||||||
MagicItem,
|
MagicItem,
|
||||||
|
|
@ -260,17 +274,21 @@ pub fn load_game(ecs: &mut World) {
|
||||||
SpawnParticleBurst,
|
SpawnParticleBurst,
|
||||||
SpawnParticleLine,
|
SpawnParticleLine,
|
||||||
SpawnParticleSimple,
|
SpawnParticleSimple,
|
||||||
|
Stackable,
|
||||||
TakingTurn,
|
TakingTurn,
|
||||||
Telepath,
|
Telepath,
|
||||||
ToHitBonus,
|
ToHitBonus,
|
||||||
Viewshed,
|
Viewshed,
|
||||||
Charges,
|
Charges,
|
||||||
WantsToApproach,
|
WantsToApproach,
|
||||||
|
WantsToAssignKey,
|
||||||
|
WantsToDelete,
|
||||||
WantsToDropItem,
|
WantsToDropItem,
|
||||||
WantsToFlee,
|
WantsToFlee,
|
||||||
WantsToMelee,
|
WantsToMelee,
|
||||||
WantsToPickupItem,
|
WantsToPickupItem,
|
||||||
WantsToRemoveItem,
|
WantsToRemoveItem,
|
||||||
|
WantsToRemoveKey,
|
||||||
WantsToUseItem,
|
WantsToUseItem,
|
||||||
SerializationHelper,
|
SerializationHelper,
|
||||||
DMSerializationHelper
|
DMSerializationHelper
|
||||||
|
|
@ -311,7 +329,7 @@ pub fn load_game(ecs: &mut World) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_save() {
|
pub fn delete_save() {
|
||||||
if Path::new("./savegame.json").exists() {
|
if Path::new("./savegame.bin").exists() {
|
||||||
std::fs::remove_file("./savegame.json").expect("Unable to delete file");
|
std::fs::remove_file("./savegame.bin").expect("Unable to delete file");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue