the town
This commit is contained in:
parent
5f091bc2ab
commit
b940142f16
14 changed files with 529 additions and 45 deletions
131
raws/mobs.json
131
raws/mobs.json
|
|
@ -1,11 +1,107 @@
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"id": "npc_barkeep",
|
||||||
|
"name": "barkeep",
|
||||||
|
"renderable": { "glyph": "@", "fg": "#EE82EE", "bg": "#000000", "order": 1 },
|
||||||
|
"flags": ["BLOCKS_TILE"],
|
||||||
|
"stats": { "max_hp": 8, "hp": 8, "defence": 1, "power": 1 },
|
||||||
|
"vision_range": 4,
|
||||||
|
"ai": "bystander"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "npc_townsperson",
|
||||||
|
"name": "townsperson",
|
||||||
|
"renderable": { "glyph": "@", "fg": "#9fa86c", "bg": "#000000", "order": 1 },
|
||||||
|
"flags": ["BLOCKS_TILE"],
|
||||||
|
"stats": { "max_hp": 8, "hp": 8, "defence": 0, "power": 1 },
|
||||||
|
"vision_range": 4,
|
||||||
|
"ai": "bystander",
|
||||||
|
"quips": [ "I don't get paid nearly enough to"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "npc_drunk",
|
||||||
|
"name": "drunk",
|
||||||
|
"renderable": { "glyph": "@", "fg": "#a0a83c", "bg": "#000000", "order": 1 },
|
||||||
|
"flags": ["BLOCKS_TILE"],
|
||||||
|
"stats": { "max_hp": 8, "hp": 8, "defence": 0, "power": 1 },
|
||||||
|
"vision_range": 4,
|
||||||
|
"ai": "bystander",
|
||||||
|
"quips": [ "Hic!", "H-Hic'."]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "npc_fisher",
|
||||||
|
"name": "fisher",
|
||||||
|
"renderable": { "glyph": "@", "fg": "#3ca3a8", "bg": "#000000", "order": 1 },
|
||||||
|
"flags": ["BLOCKS_TILE"],
|
||||||
|
"stats": { "max_hp": 8, "hp": 8, "defence": 0, "power": 1 },
|
||||||
|
"vision_range": 4,
|
||||||
|
"ai": "bystander",
|
||||||
|
"quips": [ "Placeholder."]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "npc_dockworker",
|
||||||
|
"name": "dock worker",
|
||||||
|
"renderable": { "glyph": "@", "fg": "#68d8de", "bg": "#000000", "order": 1 },
|
||||||
|
"flags": ["BLOCKS_TILE"],
|
||||||
|
"stats": { "max_hp": 8, "hp": 8, "defence": 1, "power": 1 },
|
||||||
|
"vision_range": 4,
|
||||||
|
"ai": "bystander",
|
||||||
|
"quips": [ "Placeholder."]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "npc_priest",
|
||||||
|
"name": "priest",
|
||||||
|
"renderable": { "glyph": "@", "fg": "#FFFFFF", "bg": "#000000", "order": 1 },
|
||||||
|
"flags": ["BLOCKS_TILE"],
|
||||||
|
"stats": { "max_hp": 8, "hp": 8, "defence": 0, "power": 1 },
|
||||||
|
"vision_range": 4,
|
||||||
|
"ai": "bystander"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "npc_miner",
|
||||||
|
"name": "miner",
|
||||||
|
"renderable": { "glyph": "@", "fg": "#946123", "bg": "#000000", "order": 1 },
|
||||||
|
"flags": ["BLOCKS_TILE"],
|
||||||
|
"stats": { "max_hp": 8, "hp": 8, "defence": 0, "power": 1 },
|
||||||
|
"vision_range": 4,
|
||||||
|
"ai": "bystander"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "npc_guard",
|
||||||
|
"name": "smalltown guard",
|
||||||
|
"renderable": { "glyph": "@", "fg": "#034efc", "bg": "#000000", "order": 1 },
|
||||||
|
"flags": ["BLOCKS_TILE"],
|
||||||
|
"stats": { "max_hp": 12, "hp": 12, "defence": 3, "power": 3 },
|
||||||
|
"vision_range": 4,
|
||||||
|
"ai": "bystander",
|
||||||
|
"quips": ["I don't get paid nearly enough to go down the mine."]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "dog_little",
|
"id": "dog_little",
|
||||||
"name": "little dog",
|
"name": "little dog",
|
||||||
"renderable": { "glyph": "d", "fg": "#FFFFFF", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "d", "fg": "#FFFFFF", "bg": "#000000", "order": 1 },
|
||||||
"flags": ["BLOCKS_TILE"],
|
"flags": ["BLOCKS_TILE"],
|
||||||
"stats": { "max_hp": 6, "hp": 6, "defence": 0, "power": 1 },
|
"stats": { "max_hp": 6, "hp": 6, "defence": 0, "power": 1 },
|
||||||
"vision_range": 12
|
"vision_range": 12,
|
||||||
|
"ai": "melee"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rat",
|
||||||
|
"name": "rat",
|
||||||
|
"renderable": { "glyph": "r", "fg": "#aa6000", "bg": "#000000", "order": 1 },
|
||||||
|
"flags": ["BLOCKS_TILE"],
|
||||||
|
"stats": { "max_hp": 4, "hp": 4, "defence": 0, "power": 1 },
|
||||||
|
"vision_range": 8,
|
||||||
|
"ai": "melee"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rat_giant",
|
||||||
|
"name": "giant rat",
|
||||||
|
"renderable": { "glyph": "r", "fg": "#bb8000", "bg": "#000000", "order": 1 },
|
||||||
|
"flags": ["BLOCKS_TILE"],
|
||||||
|
"stats": { "max_hp": 4, "hp": 4, "defence": 0, "power": 1 },
|
||||||
|
"vision_range": 8,
|
||||||
|
"ai": "melee"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "dog",
|
"id": "dog",
|
||||||
|
|
@ -13,7 +109,8 @@
|
||||||
"renderable": { "glyph": "d", "fg": "#EEEEEE", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "d", "fg": "#EEEEEE", "bg": "#000000", "order": 1 },
|
||||||
"flags": ["BLOCKS_TILE"],
|
"flags": ["BLOCKS_TILE"],
|
||||||
"stats": { "max_hp": 8, "hp": 8, "defence": 0, "power": 2 },
|
"stats": { "max_hp": 8, "hp": 8, "defence": 0, "power": 2 },
|
||||||
"vision_range": 12
|
"vision_range": 12,
|
||||||
|
"ai": "melee"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "dog_large",
|
"id": "dog_large",
|
||||||
|
|
@ -21,7 +118,8 @@
|
||||||
"renderable": { "glyph": "d", "fg": "#DDDDDD", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "d", "fg": "#DDDDDD", "bg": "#000000", "order": 1 },
|
||||||
"flags": ["BLOCKS_TILE"],
|
"flags": ["BLOCKS_TILE"],
|
||||||
"stats": { "max_hp": 12, "hp": 12, "defence": 0, "power": 3 },
|
"stats": { "max_hp": 12, "hp": 12, "defence": 0, "power": 3 },
|
||||||
"vision_range": 12
|
"vision_range": 12,
|
||||||
|
"ai": "melee"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "goblin",
|
"id": "goblin",
|
||||||
|
|
@ -29,7 +127,8 @@
|
||||||
"renderable": { "glyph": "g", "fg": "#00FF00", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "g", "fg": "#00FF00", "bg": "#000000", "order": 1 },
|
||||||
"flags": ["BLOCKS_TILE"],
|
"flags": ["BLOCKS_TILE"],
|
||||||
"stats": { "max_hp": 6, "hp": 6, "defence": 0, "power": 2 },
|
"stats": { "max_hp": 6, "hp": 6, "defence": 0, "power": 2 },
|
||||||
"vision_range": 12
|
"vision_range": 12,
|
||||||
|
"ai": "melee"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "kobold",
|
"id": "kobold",
|
||||||
|
|
@ -37,7 +136,8 @@
|
||||||
"renderable": { "glyph": "k", "fg": "#AA5500", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "k", "fg": "#AA5500", "bg": "#000000", "order": 1 },
|
||||||
"flags": ["BLOCKS_TILE"],
|
"flags": ["BLOCKS_TILE"],
|
||||||
"stats": { "max_hp": 6, "hp": 6, "defence": 0, "power": 1 },
|
"stats": { "max_hp": 6, "hp": 6, "defence": 0, "power": 1 },
|
||||||
"vision_range": 7
|
"vision_range": 7,
|
||||||
|
"ai": "melee"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "jackal",
|
"id": "jackal",
|
||||||
|
|
@ -45,7 +145,8 @@
|
||||||
"renderable": { "glyph": "d", "fg": "#AA5500", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "d", "fg": "#AA5500", "bg": "#000000", "order": 1 },
|
||||||
"flags": ["BLOCKS_TILE"],
|
"flags": ["BLOCKS_TILE"],
|
||||||
"stats": { "max_hp": 6, "hp": 6, "defence": 0, "power": 1 },
|
"stats": { "max_hp": 6, "hp": 6, "defence": 0, "power": 1 },
|
||||||
"vision_range": 12
|
"vision_range": 12,
|
||||||
|
"ai": "melee"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "fox",
|
"id": "fox",
|
||||||
|
|
@ -53,7 +154,8 @@
|
||||||
"renderable": { "glyph": "d", "fg": "#FF0000", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "d", "fg": "#FF0000", "bg": "#000000", "order": 1 },
|
||||||
"flags": ["BLOCKS_TILE"],
|
"flags": ["BLOCKS_TILE"],
|
||||||
"stats": { "max_hp": 4, "hp": 4, "defence": 0, "power": 1 },
|
"stats": { "max_hp": 4, "hp": 4, "defence": 0, "power": 1 },
|
||||||
"vision_range": 12
|
"vision_range": 12,
|
||||||
|
"ai": "melee"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "coyote",
|
"id": "coyote",
|
||||||
|
|
@ -61,7 +163,8 @@
|
||||||
"renderable": { "glyph": "d", "fg": "#6E3215", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "d", "fg": "#6E3215", "bg": "#000000", "order": 1 },
|
||||||
"flags": ["BLOCKS_TILE"],
|
"flags": ["BLOCKS_TILE"],
|
||||||
"stats": { "max_hp": 8, "hp": 8, "defence": 0, "power": 2 },
|
"stats": { "max_hp": 8, "hp": 8, "defence": 0, "power": 2 },
|
||||||
"vision_range": 12
|
"vision_range": 12,
|
||||||
|
"ai": "melee"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "goblin_chieftain",
|
"id": "goblin_chieftain",
|
||||||
|
|
@ -69,7 +172,8 @@
|
||||||
"renderable": { "glyph": "G", "fg": "#00FF00", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "G", "fg": "#00FF00", "bg": "#000000", "order": 1 },
|
||||||
"flags": ["BLOCKS_TILE"],
|
"flags": ["BLOCKS_TILE"],
|
||||||
"stats": { "max_hp": 8, "hp": 8, "defence": 1, "power": 2 },
|
"stats": { "max_hp": 8, "hp": 8, "defence": 1, "power": 2 },
|
||||||
"vision_range": 12
|
"vision_range": 12,
|
||||||
|
"ai": "melee"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "orc",
|
"id": "orc",
|
||||||
|
|
@ -77,7 +181,8 @@
|
||||||
"renderable": { "glyph": "o", "fg": "#00FF00", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "o", "fg": "#00FF00", "bg": "#000000", "order": 1 },
|
||||||
"flags": ["BLOCKS_TILE"],
|
"flags": ["BLOCKS_TILE"],
|
||||||
"stats": { "max_hp": 8, "hp": 8, "defence": 0, "power": 3 },
|
"stats": { "max_hp": 8, "hp": 8, "defence": 0, "power": 3 },
|
||||||
"vision_range": 12
|
"vision_range": 12,
|
||||||
|
"ai": "melee"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "orc_large",
|
"id": "orc_large",
|
||||||
|
|
@ -85,7 +190,8 @@
|
||||||
"renderable": { "glyph": "o", "fg": "#008000", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "o", "fg": "#008000", "bg": "#000000", "order": 1 },
|
||||||
"flags": ["BLOCKS_TILE"],
|
"flags": ["BLOCKS_TILE"],
|
||||||
"stats": { "max_hp": 12, "hp": 12, "defence": 1, "power": 3 },
|
"stats": { "max_hp": 12, "hp": 12, "defence": 1, "power": 3 },
|
||||||
"vision_range": 12
|
"vision_range": 12,
|
||||||
|
"ai": "melee"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "ogre",
|
"id": "ogre",
|
||||||
|
|
@ -93,6 +199,7 @@
|
||||||
"renderable": { "glyph": "O", "fg": "#00FF00", "bg": "#000000", "order": 1 },
|
"renderable": { "glyph": "O", "fg": "#00FF00", "bg": "#000000", "order": 1 },
|
||||||
"flags": ["BLOCKS_TILE"],
|
"flags": ["BLOCKS_TILE"],
|
||||||
"stats": { "max_hp": 24, "hp": 24, "defence": 3, "power": 3 },
|
"stats": { "max_hp": 24, "hp": 24, "defence": 3, "power": 3 },
|
||||||
"vision_range": 8
|
"vision_range": 8,
|
||||||
|
"ai": "melee"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,37 @@
|
||||||
"id": "door",
|
"id": "door",
|
||||||
"name": "door",
|
"name": "door",
|
||||||
"renderable": { "glyph": "+", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
"renderable": { "glyph": "+", "fg": "#00FFFF", "bg": "#000000", "order": 2 },
|
||||||
"flags": ["BLOCKS_TILE", "BLOCKS_VISIBILITY", "DOOR"]
|
"flags": ["BLOCKS_TILE", "BLOCKS_VISIBILITY", "DOOR", "PROP"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "prop_keg",
|
||||||
|
"name": "keg",
|
||||||
|
"renderable": { "glyph": "φ", "fg": "#AAAAAA", "bg": "#000000", "order": 2 },
|
||||||
|
"flags": ["PROP"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "prop_table",
|
||||||
|
"name": "table",
|
||||||
|
"renderable": { "glyph": "-", "fg": "#AAAAAA", "bg": "#000000", "order": 2 },
|
||||||
|
"flags": ["PROP"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "prop_bed",
|
||||||
|
"name": "bed",
|
||||||
|
"renderable": { "glyph": "=", "fg": "#AAAAAA", "bg": "#000000", "order": 2 },
|
||||||
|
"flags": ["PROP"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "prop_chair",
|
||||||
|
"name": "chair",
|
||||||
|
"renderable": { "glyph": "└", "fg": "#AAAAAA", "bg": "#000000", "order": 2 },
|
||||||
|
"flags": ["PROP"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "prop_candle",
|
||||||
|
"name": "candle",
|
||||||
|
"renderable": { "glyph": "Ä", "fg": "#FFA500", "bg": "#000000", "order": 2 },
|
||||||
|
"flags": ["PROP"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "trap_bear",
|
"id": "trap_bear",
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,13 @@
|
||||||
{
|
{
|
||||||
"id": "mobs",
|
"id": "mobs",
|
||||||
"table": [
|
"table": [
|
||||||
|
{ "id": "rat", "weight": 1, "difficulty": 1},
|
||||||
{ "id": "goblin", "weight": 3, "difficulty": 1},
|
{ "id": "goblin", "weight": 3, "difficulty": 1},
|
||||||
{ "id": "kobold", "weight": 1, "difficulty": 1},
|
{ "id": "kobold", "weight": 1, "difficulty": 1},
|
||||||
{ "id": "fox", "weight": 1, "difficulty": 1},
|
{ "id": "fox", "weight": 1, "difficulty": 1},
|
||||||
{ "id": "jackal", "weight": 4, "difficulty": 1},
|
{ "id": "jackal", "weight": 4, "difficulty": 1},
|
||||||
|
|
||||||
|
{ "id": "rat_giant", "weight": 2, "difficulty": 2},
|
||||||
{ "id": "coyote", "weight": 4, "difficulty": 2},
|
{ "id": "coyote", "weight": 4, "difficulty": 2},
|
||||||
|
|
||||||
{ "id": "dog_little", "weight": 1, "difficulty": 3},
|
{ "id": "dog_little", "weight": 1, "difficulty": 3},
|
||||||
|
|
|
||||||
92
src/bystander_ai_system.rs
Normal file
92
src/bystander_ai_system.rs
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
use super::{gamelog, Bystander, EntityMoved, Map, Name, Point, Position, Quips, RunState, Viewshed};
|
||||||
|
use specs::prelude::*;
|
||||||
|
|
||||||
|
pub struct BystanderAI {}
|
||||||
|
|
||||||
|
impl<'a> System<'a> for BystanderAI {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
type SystemData = (
|
||||||
|
WriteExpect<'a, Map>,
|
||||||
|
ReadExpect<'a, RunState>,
|
||||||
|
Entities<'a>,
|
||||||
|
WriteStorage<'a, Viewshed>,
|
||||||
|
ReadStorage<'a, Bystander>,
|
||||||
|
WriteStorage<'a, Position>,
|
||||||
|
WriteStorage<'a, EntityMoved>,
|
||||||
|
WriteExpect<'a, rltk::RandomNumberGenerator>,
|
||||||
|
ReadExpect<'a, Point>,
|
||||||
|
WriteStorage<'a, Quips>,
|
||||||
|
ReadStorage<'a, Name>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
|
let (
|
||||||
|
mut map,
|
||||||
|
runstate,
|
||||||
|
entities,
|
||||||
|
mut viewshed,
|
||||||
|
bystander,
|
||||||
|
mut position,
|
||||||
|
mut entity_moved,
|
||||||
|
mut rng,
|
||||||
|
player_pos,
|
||||||
|
mut quips,
|
||||||
|
names,
|
||||||
|
) = data;
|
||||||
|
|
||||||
|
if *runstate != RunState::MonsterTurn {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (entity, mut viewshed, _bystander, mut pos) in (&entities, &mut viewshed, &bystander, &mut position).join()
|
||||||
|
{
|
||||||
|
// Possibly quip
|
||||||
|
let quip = quips.get_mut(entity);
|
||||||
|
if let Some(quip) = quip {
|
||||||
|
if !quip.available.is_empty()
|
||||||
|
&& viewshed.visible_tiles.contains(&player_pos)
|
||||||
|
&& rng.roll_dice(1, 20) == 1
|
||||||
|
{
|
||||||
|
let name = names.get(entity);
|
||||||
|
let quip_index = if quip.available.len() == 1 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
(rng.roll_dice(1, quip.available.len() as i32) - 1) as usize
|
||||||
|
};
|
||||||
|
gamelog::Logger::new()
|
||||||
|
.npc_name(&name.unwrap().name)
|
||||||
|
.append_n("says \"")
|
||||||
|
.append_n(&quip.available[quip_index])
|
||||||
|
.append("\"")
|
||||||
|
.log();
|
||||||
|
quip.available.remove(quip_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to move randomly
|
||||||
|
let mut x = pos.x;
|
||||||
|
let mut y = pos.y;
|
||||||
|
let move_roll = rng.roll_dice(1, 8);
|
||||||
|
match move_roll {
|
||||||
|
1 => x -= 1,
|
||||||
|
2 => x += 1,
|
||||||
|
3 => y -= 1,
|
||||||
|
4 => y += 1,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if x > 0 && x < map.width - 1 && y > 0 && y < map.height - 1 {
|
||||||
|
let dest_idx = map.xy_idx(x, y);
|
||||||
|
if !map.blocked[dest_idx] {
|
||||||
|
let idx = map.xy_idx(pos.x, pos.y);
|
||||||
|
map.blocked[idx] = false;
|
||||||
|
pos.x = x;
|
||||||
|
pos.y = y;
|
||||||
|
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker");
|
||||||
|
map.blocked[dest_idx] = true;
|
||||||
|
viewshed.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use super::{Door, Hidden, Map, Mind, Position, Renderable};
|
use super::{Hidden, Map, Mind, Position, Prop, Renderable};
|
||||||
use rltk::{Point, Rltk, RGB};
|
use rltk::{Point, Rltk, RGB};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
use std::ops::Mul;
|
||||||
|
|
||||||
const SHOW_BOUNDARIES: bool = false;
|
const SHOW_BOUNDARIES: bool = false;
|
||||||
|
|
||||||
|
|
@ -53,7 +54,7 @@ pub fn render_camera(ecs: &World, ctx: &mut Rltk) {
|
||||||
let renderables = ecs.read_storage::<Renderable>();
|
let renderables = ecs.read_storage::<Renderable>();
|
||||||
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 doors = ecs.write_storage::<Door>();
|
let props = ecs.write_storage::<Prop>();
|
||||||
let map = ecs.fetch::<Map>();
|
let map = ecs.fetch::<Map>();
|
||||||
let entities = ecs.entities();
|
let entities = ecs.entities();
|
||||||
|
|
||||||
|
|
@ -66,11 +67,13 @@ pub fn render_camera(ecs: &World, ctx: &mut Rltk) {
|
||||||
if entity_offset_x > 0 && entity_offset_x < map_width && entity_offset_y > 0 && entity_offset_y < map_height
|
if entity_offset_x > 0 && entity_offset_x < map_width && entity_offset_y > 0 && entity_offset_y < map_height
|
||||||
{
|
{
|
||||||
let mut draw = false;
|
let mut draw = false;
|
||||||
let fg = render.fg;
|
let mut fg = render.fg;
|
||||||
let (_glyph, _fg, bg) = crate::map::themes::get_tile_glyph(idx, &*map);
|
let (_glyph, _fg, bg) = crate::map::themes::get_tile_glyph(idx, &*map);
|
||||||
// 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 {
|
||||||
|
fg = fg.mul(0.75);
|
||||||
}
|
}
|
||||||
// Draw entities with minds within telepath range
|
// Draw entities with minds within telepath range
|
||||||
if map.telepath_tiles[idx] {
|
if map.telepath_tiles[idx] {
|
||||||
|
|
@ -79,9 +82,9 @@ pub fn render_camera(ecs: &World, ctx: &mut Rltk) {
|
||||||
draw = true;
|
draw = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Draw all doors
|
// Draw all props
|
||||||
let is_door = doors.get(*ent);
|
let is_prop = props.get(*ent);
|
||||||
if let Some(_) = is_door {
|
if let Some(_) = is_prop {
|
||||||
if map.revealed_tiles[idx] {
|
if map.revealed_tiles[idx] {
|
||||||
draw = true;
|
draw = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,20 @@ pub struct Renderable {
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Player {}
|
pub struct Player {}
|
||||||
|
|
||||||
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Prop {}
|
||||||
|
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Monster {}
|
pub struct Monster {}
|
||||||
|
|
||||||
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Bystander {}
|
||||||
|
|
||||||
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Quips {
|
||||||
|
pub available: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Mind {}
|
pub struct Mind {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ mod visibility_system;
|
||||||
use visibility_system::VisibilitySystem;
|
use visibility_system::VisibilitySystem;
|
||||||
mod monster_ai_system;
|
mod monster_ai_system;
|
||||||
use monster_ai_system::MonsterAI;
|
use monster_ai_system::MonsterAI;
|
||||||
|
pub mod bystander_ai_system;
|
||||||
mod map_indexing_system;
|
mod map_indexing_system;
|
||||||
use map_indexing_system::MapIndexingSystem;
|
use map_indexing_system::MapIndexingSystem;
|
||||||
mod damage_system;
|
mod damage_system;
|
||||||
|
|
@ -123,6 +124,7 @@ impl State {
|
||||||
fn run_systems(&mut self) {
|
fn run_systems(&mut self) {
|
||||||
let mut vis = VisibilitySystem {};
|
let mut vis = VisibilitySystem {};
|
||||||
let mut mob = MonsterAI {};
|
let mut mob = MonsterAI {};
|
||||||
|
let mut bystanders = bystander_ai_system::BystanderAI {};
|
||||||
let mut mapindex = MapIndexingSystem {};
|
let mut mapindex = MapIndexingSystem {};
|
||||||
let mut trigger_system = trigger_system::TriggerSystem {};
|
let mut trigger_system = trigger_system::TriggerSystem {};
|
||||||
let mut melee_system = MeleeCombatSystem {};
|
let mut melee_system = MeleeCombatSystem {};
|
||||||
|
|
@ -136,6 +138,7 @@ impl State {
|
||||||
|
|
||||||
vis.run_now(&self.ecs);
|
vis.run_now(&self.ecs);
|
||||||
mob.run_now(&self.ecs);
|
mob.run_now(&self.ecs);
|
||||||
|
bystanders.run_now(&self.ecs);
|
||||||
mapindex.run_now(&self.ecs);
|
mapindex.run_now(&self.ecs);
|
||||||
trigger_system.run_now(&self.ecs);
|
trigger_system.run_now(&self.ecs);
|
||||||
inventory_system.run_now(&self.ecs);
|
inventory_system.run_now(&self.ecs);
|
||||||
|
|
@ -499,8 +502,11 @@ fn main() -> rltk::BError {
|
||||||
|
|
||||||
gs.ecs.register::<Position>();
|
gs.ecs.register::<Position>();
|
||||||
gs.ecs.register::<Renderable>();
|
gs.ecs.register::<Renderable>();
|
||||||
|
gs.ecs.register::<Prop>();
|
||||||
gs.ecs.register::<Player>();
|
gs.ecs.register::<Player>();
|
||||||
gs.ecs.register::<Monster>();
|
gs.ecs.register::<Monster>();
|
||||||
|
gs.ecs.register::<Bystander>();
|
||||||
|
gs.ecs.register::<Quips>();
|
||||||
gs.ecs.register::<Mind>();
|
gs.ecs.register::<Mind>();
|
||||||
gs.ecs.register::<Viewshed>();
|
gs.ecs.register::<Viewshed>();
|
||||||
gs.ecs.register::<Telepath>();
|
gs.ecs.register::<Telepath>();
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,7 @@ pub fn get_tile_glyph(idx: usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) {
|
||||||
}
|
}
|
||||||
TileType::Bridge => {
|
TileType::Bridge => {
|
||||||
glyph = rltk::to_cp437('.');
|
glyph = rltk::to_cp437('.');
|
||||||
fg = default_bg;
|
bg = RGB::from_u8(59, 49, 43);
|
||||||
bg = default_bg;
|
|
||||||
}
|
}
|
||||||
TileType::Gravel => {
|
TileType::Gravel => {
|
||||||
glyph = rltk::to_cp437(';');
|
glyph = rltk::to_cp437(';');
|
||||||
|
|
|
||||||
|
|
@ -328,6 +328,7 @@ pub fn random_builder(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn level_builder(new_id: i32, rng: &mut rltk::RandomNumberGenerator, width: i32, height: i32) -> BuilderChain {
|
pub fn level_builder(new_id: i32, rng: &mut rltk::RandomNumberGenerator, width: i32, height: i32) -> BuilderChain {
|
||||||
|
// TODO: With difficulty and ID/depth decoupled, this can be used for branches later.
|
||||||
let difficulty = new_id;
|
let difficulty = new_id;
|
||||||
match new_id {
|
match new_id {
|
||||||
1 => town_builder(new_id, rng, width, height),
|
1 => town_builder(new_id, rng, width, height),
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,16 @@ impl InitialMapBuilder for TownBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum BuildingTag {
|
||||||
|
Tavern,
|
||||||
|
Temple,
|
||||||
|
PlayerHouse,
|
||||||
|
NPCHouse,
|
||||||
|
Mine,
|
||||||
|
Abandoned,
|
||||||
|
Unassigned,
|
||||||
|
}
|
||||||
|
|
||||||
impl TownBuilder {
|
impl TownBuilder {
|
||||||
pub fn new() -> Box<TownBuilder> {
|
pub fn new() -> Box<TownBuilder> {
|
||||||
return Box::new(TownBuilder {});
|
return Box::new(TownBuilder {});
|
||||||
|
|
@ -30,6 +40,7 @@ impl TownBuilder {
|
||||||
*t = true;
|
*t = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build map
|
||||||
self.grass_layer(build_data);
|
self.grass_layer(build_data);
|
||||||
let piers = self.water_and_piers(rng, build_data);
|
let piers = self.water_and_piers(rng, build_data);
|
||||||
let (mut available_building_tiles, wall_gap_y) = self.town_walls(rng, build_data);
|
let (mut available_building_tiles, wall_gap_y) = self.town_walls(rng, build_data);
|
||||||
|
|
@ -38,23 +49,198 @@ impl TownBuilder {
|
||||||
self.path_from_tiles_to_nearest_tiletype(build_data, &doors, TileType::Road, TileType::Road, true);
|
self.path_from_tiles_to_nearest_tiletype(build_data, &doors, TileType::Road, TileType::Road, true);
|
||||||
self.path_from_tiles_to_nearest_tiletype(build_data, &piers, TileType::Road, TileType::Road, false);
|
self.path_from_tiles_to_nearest_tiletype(build_data, &piers, TileType::Road, TileType::Road, false);
|
||||||
|
|
||||||
build_data.take_snapshot();
|
// Spawn entities
|
||||||
|
let building_size = self.sort_buildings(&buildings);
|
||||||
|
self.building_factory(rng, build_data, &buildings, &building_size);
|
||||||
|
self.spawn_dockers(build_data, rng);
|
||||||
|
self.spawn_townsfolk(build_data, rng, &mut available_building_tiles);
|
||||||
|
|
||||||
// Sort buildings by size
|
build_data.take_snapshot();
|
||||||
let mut building_size: Vec<(usize, i32)> = Vec::new();
|
}
|
||||||
|
|
||||||
|
fn sort_buildings(&mut self, buildings: &[(i32, i32, i32, i32)]) -> Vec<(usize, i32, BuildingTag)> {
|
||||||
|
// Sort buildings by size, defaulting them to Unassigned buildings
|
||||||
|
let mut building_size: Vec<(usize, i32, BuildingTag)> = Vec::new();
|
||||||
for (i, building) in buildings.iter().enumerate() {
|
for (i, building) in buildings.iter().enumerate() {
|
||||||
building_size.push((i, building.2 * building.3));
|
building_size.push((i, building.2 * building.3, BuildingTag::Unassigned));
|
||||||
}
|
}
|
||||||
building_size.sort_by(|a, b| b.1.cmp(&a.1));
|
building_size.sort_by(|a, b| b.1.cmp(&a.1));
|
||||||
|
|
||||||
// Largest building as start position
|
// Set individual buildings to their correct tags
|
||||||
let tavern = &buildings[building_size[0].0];
|
building_size[0].2 = BuildingTag::Tavern;
|
||||||
build_data.starting_position = Some(Position { x: tavern.0 + (tavern.2 / 2), y: tavern.1 + (tavern.3 / 2) });
|
building_size[1].2 = BuildingTag::Temple;
|
||||||
|
building_size[2].2 = BuildingTag::Mine;
|
||||||
|
building_size[3].2 = BuildingTag::PlayerHouse;
|
||||||
|
for b in building_size.iter_mut().skip(3) {
|
||||||
|
b.2 = BuildingTag::NPCHouse
|
||||||
|
}
|
||||||
|
let last_idx = building_size.len() - 1;
|
||||||
|
building_size[last_idx].2 = BuildingTag::Abandoned;
|
||||||
|
|
||||||
// Smallest building as mine entrance
|
return building_size;
|
||||||
let mine = &buildings[building_size[building_size.len() - 1].0];
|
}
|
||||||
let exit_idx = build_data.map.xy_idx(mine.0 + (mine.2 / 2), mine.1 + (mine.3 / 2));
|
|
||||||
|
fn building_factory(
|
||||||
|
&mut self,
|
||||||
|
rng: &mut rltk::RandomNumberGenerator,
|
||||||
|
build_data: &mut BuilderMap,
|
||||||
|
buildings: &[(i32, i32, i32, i32)],
|
||||||
|
building_index: &[(usize, i32, BuildingTag)],
|
||||||
|
) {
|
||||||
|
for (i, building) in buildings.iter().enumerate() {
|
||||||
|
let build_tag = &building_index[i].2;
|
||||||
|
match build_tag {
|
||||||
|
BuildingTag::Tavern => self.build_tavern(&building, build_data, rng),
|
||||||
|
BuildingTag::Temple => self.build_temple(&building, build_data, rng),
|
||||||
|
BuildingTag::Mine => self.build_mine(&building, build_data, rng),
|
||||||
|
BuildingTag::PlayerHouse => self.build_playerhouse(&building, build_data, rng),
|
||||||
|
BuildingTag::NPCHouse => self.build_npchouse(&building, build_data, rng),
|
||||||
|
BuildingTag::Abandoned => self.build_abandoned(&building, build_data, rng),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_dockers(&mut self, build_data: &mut BuilderMap, rng: &mut rltk::RandomNumberGenerator) {
|
||||||
|
for (idx, tt) in build_data.map.tiles.iter().enumerate() {
|
||||||
|
if *tt == TileType::Bridge && rng.roll_dice(1, 20) == 1 {
|
||||||
|
let roll = rng.roll_dice(1, 2);
|
||||||
|
match roll {
|
||||||
|
1 => build_data.spawn_list.push((idx, "npc_fisher".to_string())),
|
||||||
|
_ => build_data.spawn_list.push((idx, "npc_dockworker".to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_townsfolk(
|
||||||
|
&mut self,
|
||||||
|
build_data: &mut BuilderMap,
|
||||||
|
rng: &mut rltk::RandomNumberGenerator,
|
||||||
|
available_building_tiles: &mut HashSet<usize>,
|
||||||
|
) {
|
||||||
|
for idx in available_building_tiles.iter() {
|
||||||
|
if rng.roll_dice(1, 40) == 1 {
|
||||||
|
let roll = rng.roll_dice(1, 3);
|
||||||
|
match roll {
|
||||||
|
1 => build_data.spawn_list.push((*idx, "npc_fisher".to_string())),
|
||||||
|
2 => build_data.spawn_list.push((*idx, "npc_dockworker".to_string())),
|
||||||
|
3 => build_data.spawn_list.push((*idx, "npc_townsperson".to_string())),
|
||||||
|
_ => build_data.spawn_list.push((*idx, "npc_drunk".to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_building_spawn(
|
||||||
|
&mut self,
|
||||||
|
building: &(i32, i32, i32, i32),
|
||||||
|
build_data: &mut BuilderMap,
|
||||||
|
rng: &mut rltk::RandomNumberGenerator,
|
||||||
|
to_place: &mut Vec<&str>,
|
||||||
|
avoid_tile: usize,
|
||||||
|
) {
|
||||||
|
for y in building.1..building.1 + building.3 {
|
||||||
|
for x in building.0..building.0 + building.2 {
|
||||||
|
let idx = build_data.map.xy_idx(x, y);
|
||||||
|
if build_data.map.tiles[idx] == TileType::WoodFloor
|
||||||
|
&& idx != avoid_tile
|
||||||
|
&& rng.roll_dice(1, 3) == 1
|
||||||
|
&& !to_place.is_empty()
|
||||||
|
{
|
||||||
|
let entity_tag = to_place[0];
|
||||||
|
to_place.remove(0);
|
||||||
|
build_data.spawn_list.push((idx, entity_tag.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_tavern(
|
||||||
|
&mut self,
|
||||||
|
building: &(i32, i32, i32, i32),
|
||||||
|
build_data: &mut BuilderMap,
|
||||||
|
rng: &mut rltk::RandomNumberGenerator,
|
||||||
|
) {
|
||||||
|
// Place player
|
||||||
|
build_data.starting_position =
|
||||||
|
Some(Position { x: building.0 + (building.2 / 2), y: building.1 + (building.3 / 2) });
|
||||||
|
let player_idx = build_data.map.xy_idx(building.0 + (building.2 / 2), building.1 + (building.3 / 2));
|
||||||
|
|
||||||
|
// Place other items
|
||||||
|
let mut to_place: Vec<&str> = vec![
|
||||||
|
"npc_barkeep",
|
||||||
|
"npc_townsperson",
|
||||||
|
"npc_townsperson",
|
||||||
|
"npc_drunk",
|
||||||
|
"npc_drunk",
|
||||||
|
"npc_guard",
|
||||||
|
"prop_keg",
|
||||||
|
"prop_table",
|
||||||
|
"prop_chair",
|
||||||
|
"prop_chair",
|
||||||
|
];
|
||||||
|
self.random_building_spawn(building, build_data, rng, &mut to_place, player_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_temple(
|
||||||
|
&mut self,
|
||||||
|
building: &(i32, i32, i32, i32),
|
||||||
|
build_data: &mut BuilderMap,
|
||||||
|
rng: &mut rltk::RandomNumberGenerator,
|
||||||
|
) {
|
||||||
|
let mut to_place: Vec<&str> =
|
||||||
|
vec!["npc_priest", "prop_chair", "prop_chair", "prop_table", "prop_candle", "prop_candle"];
|
||||||
|
self.random_building_spawn(building, build_data, rng, &mut to_place, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_mine(
|
||||||
|
&mut self,
|
||||||
|
building: &(i32, i32, i32, i32),
|
||||||
|
build_data: &mut BuilderMap,
|
||||||
|
rng: &mut rltk::RandomNumberGenerator,
|
||||||
|
) {
|
||||||
|
// Place exit
|
||||||
|
let exit_idx = build_data.map.xy_idx(building.0 + (building.2 / 2), building.1 + (building.3 / 2));
|
||||||
build_data.map.tiles[exit_idx] = TileType::DownStair;
|
build_data.map.tiles[exit_idx] = TileType::DownStair;
|
||||||
|
let mut to_place: Vec<&str> = vec!["npc_miner", "npc_miner", "npc_guard"];
|
||||||
|
self.random_building_spawn(building, build_data, rng, &mut to_place, exit_idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_playerhouse(
|
||||||
|
&mut self,
|
||||||
|
building: &(i32, i32, i32, i32),
|
||||||
|
build_data: &mut BuilderMap,
|
||||||
|
rng: &mut rltk::RandomNumberGenerator,
|
||||||
|
) {
|
||||||
|
let mut to_place: Vec<&str> = vec!["prop_bed", "prop_table"];
|
||||||
|
self.random_building_spawn(building, build_data, rng, &mut to_place, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_npchouse(
|
||||||
|
&mut self,
|
||||||
|
building: &(i32, i32, i32, i32),
|
||||||
|
build_data: &mut BuilderMap,
|
||||||
|
rng: &mut rltk::RandomNumberGenerator,
|
||||||
|
) {
|
||||||
|
let mut to_place: Vec<&str> = vec!["prop_bed", "prop_table"];
|
||||||
|
self.random_building_spawn(building, build_data, rng, &mut to_place, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_abandoned(
|
||||||
|
&mut self,
|
||||||
|
building: &(i32, i32, i32, i32),
|
||||||
|
build_data: &mut BuilderMap,
|
||||||
|
rng: &mut rltk::RandomNumberGenerator,
|
||||||
|
) {
|
||||||
|
for y in building.1..building.1 + building.3 {
|
||||||
|
for x in building.0..building.0 + building.2 {
|
||||||
|
let idx = build_data.map.xy_idx(x, y);
|
||||||
|
if build_data.map.tiles[idx] == TileType::WoodFloor && idx != 0 && rng.roll_dice(1, 3) == 1 {
|
||||||
|
build_data.spawn_list.push((idx, "rat".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn grass_layer(&mut self, build_data: &mut BuilderMap) {
|
fn grass_layer(&mut self, build_data: &mut BuilderMap) {
|
||||||
|
|
@ -69,7 +255,7 @@ impl TownBuilder {
|
||||||
let mut n = (rng.roll_dice(1, 65535) as f32) / 65535f32;
|
let mut n = (rng.roll_dice(1, 65535) as f32) / 65535f32;
|
||||||
let mut water_width: Vec<i32> = Vec::new();
|
let mut water_width: Vec<i32> = Vec::new();
|
||||||
let variance = 5;
|
let variance = 5;
|
||||||
let minimum_width = variance + 10;
|
let minimum_width = variance + 5;
|
||||||
let shallow_width = 6;
|
let shallow_width = 6;
|
||||||
let sand_width = shallow_width + 4;
|
let sand_width = shallow_width + 4;
|
||||||
|
|
||||||
|
|
@ -123,9 +309,9 @@ impl TownBuilder {
|
||||||
let idx = build_data.map.xy_idx(x, y);
|
let idx = build_data.map.xy_idx(x, y);
|
||||||
build_data.map.tiles[idx] = TileType::Fence;
|
build_data.map.tiles[idx] = TileType::Fence;
|
||||||
let idx = build_data.map.xy_idx(x, y + 1);
|
let idx = build_data.map.xy_idx(x, y + 1);
|
||||||
build_data.map.tiles[idx] = TileType::WoodFloor;
|
build_data.map.tiles[idx] = TileType::Bridge;
|
||||||
let idx = build_data.map.xy_idx(x, y + 2);
|
let idx = build_data.map.xy_idx(x, y + 2);
|
||||||
build_data.map.tiles[idx] = TileType::WoodFloor;
|
build_data.map.tiles[idx] = TileType::Bridge;
|
||||||
let idx = build_data.map.xy_idx(x, y + 3);
|
let idx = build_data.map.xy_idx(x, y + 3);
|
||||||
build_data.map.tiles[idx] = TileType::Fence;
|
build_data.map.tiles[idx] = TileType::Fence;
|
||||||
}
|
}
|
||||||
|
|
@ -154,7 +340,7 @@ impl TownBuilder {
|
||||||
let mut available_building_tiles: HashSet<usize> = HashSet::new();
|
let mut available_building_tiles: HashSet<usize> = HashSet::new();
|
||||||
|
|
||||||
const BORDER: i32 = 4;
|
const BORDER: i32 = 4;
|
||||||
const OFFSET_FROM_LEFT: i32 = 30 + BORDER;
|
const OFFSET_FROM_LEFT: i32 = 25 + BORDER;
|
||||||
const PATH_OFFSET_FROM_CENTRE: i32 = 4;
|
const PATH_OFFSET_FROM_CENTRE: i32 = 4;
|
||||||
const HALF_PATH_THICKNESS: i32 = 3;
|
const HALF_PATH_THICKNESS: i32 = 3;
|
||||||
|
|
||||||
|
|
@ -216,8 +402,8 @@ impl TownBuilder {
|
||||||
|
|
||||||
const BORDER: i32 = 2;
|
const BORDER: i32 = 2;
|
||||||
const REQUIRED_BUILDINGS: i32 = 8;
|
const REQUIRED_BUILDINGS: i32 = 8;
|
||||||
const OFFSET_FROM_LEFT: i32 = 30;
|
const OFFSET_FROM_LEFT: i32 = 25;
|
||||||
const MIN_BUILDING_SIZE: i32 = 4;
|
const MIN_BUILDING_SIZE: i32 = 6;
|
||||||
const MAX_BUILDING_SIZE: i32 = 10;
|
const MAX_BUILDING_SIZE: i32 = 10;
|
||||||
|
|
||||||
while n_buildings < REQUIRED_BUILDINGS {
|
while n_buildings < REQUIRED_BUILDINGS {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use super::{
|
use super::{
|
||||||
gamelog, BlocksTile, BlocksVisibility, CombatStats, Door, EntityMoved, Hidden, HungerClock, HungerState, Item, Map,
|
gamelog, BlocksTile, BlocksVisibility, Bystander, CombatStats, Door, EntityMoved, Hidden, HungerClock, HungerState,
|
||||||
Monster, Name, ParticleBuilder, Player, Position, Renderable, RunState, State, SufferDamage, Telepath, TileType,
|
Item, Map, Monster, Name, ParticleBuilder, Player, Position, Renderable, RunState, State, SufferDamage, Telepath,
|
||||||
Viewshed, WantsToMelee, WantsToPickupItem,
|
TileType, Viewshed, WantsToMelee, WantsToPickupItem,
|
||||||
};
|
};
|
||||||
use rltk::{Point, RandomNumberGenerator, Rltk, VirtualKeyCode};
|
use rltk::{Point, RandomNumberGenerator, Rltk, VirtualKeyCode};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
@ -272,6 +272,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool {
|
||||||
let mut viewsheds = ecs.write_storage::<Viewshed>();
|
let mut viewsheds = ecs.write_storage::<Viewshed>();
|
||||||
let mut telepaths = ecs.write_storage::<Telepath>();
|
let mut telepaths = ecs.write_storage::<Telepath>();
|
||||||
let mut entity_moved = ecs.write_storage::<EntityMoved>();
|
let mut entity_moved = ecs.write_storage::<EntityMoved>();
|
||||||
|
let friendlies = ecs.read_storage::<Bystander>();
|
||||||
let combat_stats = ecs.read_storage::<CombatStats>();
|
let combat_stats = ecs.read_storage::<CombatStats>();
|
||||||
let map = ecs.fetch::<Map>();
|
let map = ecs.fetch::<Map>();
|
||||||
|
|
||||||
|
|
@ -279,6 +280,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool {
|
||||||
let mut wants_to_melee = ecs.write_storage::<WantsToMelee>();
|
let mut wants_to_melee = ecs.write_storage::<WantsToMelee>();
|
||||||
let mut doors = ecs.write_storage::<Door>();
|
let mut doors = ecs.write_storage::<Door>();
|
||||||
let names = ecs.read_storage::<Name>();
|
let names = ecs.read_storage::<Name>();
|
||||||
|
let mut swap_entities: Vec<(Entity, i32, i32)> = Vec::new();
|
||||||
|
|
||||||
for (entity, _player, pos, viewshed) in (&entities, &mut players, &mut positions, &mut viewsheds).join() {
|
for (entity, _player, pos, viewshed) in (&entities, &mut players, &mut positions, &mut viewsheds).join() {
|
||||||
if pos.x + delta_x < 0
|
if pos.x + delta_x < 0
|
||||||
|
|
@ -291,11 +293,25 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool {
|
||||||
let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y);
|
let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y);
|
||||||
|
|
||||||
for potential_target in map.tile_content[destination_idx].iter() {
|
for potential_target in map.tile_content[destination_idx].iter() {
|
||||||
|
let friendly = friendlies.get(*potential_target);
|
||||||
|
if friendly.is_some() {
|
||||||
|
swap_entities.push((*potential_target, pos.x, pos.y));
|
||||||
|
pos.x = min(map.width - 1, max(0, pos.x + delta_x));
|
||||||
|
pos.y = min(map.height - 1, max(0, pos.y + delta_y));
|
||||||
|
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker");
|
||||||
|
viewshed.dirty = true;
|
||||||
|
let mut ppos = ecs.write_resource::<Point>();
|
||||||
|
ppos.x = pos.x;
|
||||||
|
ppos.y = pos.y;
|
||||||
|
} else {
|
||||||
let target = combat_stats.get(*potential_target);
|
let target = combat_stats.get(*potential_target);
|
||||||
if let Some(_target) = target {
|
if let Some(_target) = target {
|
||||||
wants_to_melee.insert(entity, WantsToMelee { target: *potential_target }).expect("Add target failed.");
|
wants_to_melee
|
||||||
|
.insert(entity, WantsToMelee { target: *potential_target })
|
||||||
|
.expect("Add target failed.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
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 {
|
||||||
|
|
@ -307,6 +323,20 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if swap_entities.len() > 0 {
|
||||||
|
for m in swap_entities.iter() {
|
||||||
|
let their_pos = positions.get_mut(m.0);
|
||||||
|
if let Some(name) = names.get(m.0) {
|
||||||
|
gamelog::Logger::new().append("You swap places with the").npc_name_n(&name.name).period().log();
|
||||||
|
}
|
||||||
|
if let Some(their_pos) = their_pos {
|
||||||
|
their_pos.x = m.1;
|
||||||
|
their_pos.y = m.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if map.blocked[destination_idx] {
|
if map.blocked[destination_idx] {
|
||||||
gamelog::Logger::new().append("You can't move there.").log();
|
gamelog::Logger::new().append("You can't move there.").log();
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ pub struct Mob {
|
||||||
pub flags: Option<Vec<String>>,
|
pub flags: Option<Vec<String>>,
|
||||||
pub stats: MobStats,
|
pub stats: MobStats,
|
||||||
pub vision_range: i32,
|
pub vision_range: i32,
|
||||||
|
pub ai: String,
|
||||||
|
pub quips: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,11 @@ pub fn spawn_named_mob(
|
||||||
let mut eb = new_entity;
|
let mut eb = new_entity;
|
||||||
eb = spawn_position(pos, eb);
|
eb = spawn_position(pos, eb);
|
||||||
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(Monster {});
|
match mob_template.ai.as_ref() {
|
||||||
|
"bystander" => eb = eb.with(Bystander {}),
|
||||||
|
"melee" => eb = eb.with(Monster {}),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
let rolled_hp = roll_hit_dice(rng, 1, mob_template.stats.max_hp);
|
let rolled_hp = roll_hit_dice(rng, 1, mob_template.stats.max_hp);
|
||||||
eb = eb.with(CombatStats {
|
eb = eb.with(CombatStats {
|
||||||
max_hp: rolled_hp,
|
max_hp: rolled_hp,
|
||||||
|
|
@ -172,6 +176,10 @@ pub fn spawn_named_mob(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(quips) = &mob_template.quips {
|
||||||
|
eb = eb.with(Quips { available: quips.clone() });
|
||||||
|
}
|
||||||
|
|
||||||
return Some(eb.build());
|
return Some(eb.build());
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|
@ -207,6 +215,7 @@ pub fn spawn_named_prop(raws: &RawMaster, new_entity: EntityBuilder, key: &str,
|
||||||
"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 }),
|
"DOOR" => eb = eb.with(Door { open: false }),
|
||||||
|
"PROP" => eb = eb.with(Prop {}),
|
||||||
_ => rltk::console::log(format!("Unrecognised flag: {}", flag.as_str())),
|
_ => rltk::console::log(format!("Unrecognised flag: {}", flag.as_str())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ pub fn save_game(ecs: &mut World) {
|
||||||
Attributes,
|
Attributes,
|
||||||
BlocksTile,
|
BlocksTile,
|
||||||
BlocksVisibility,
|
BlocksVisibility,
|
||||||
|
Bystander,
|
||||||
CombatStats,
|
CombatStats,
|
||||||
Confusion,
|
Confusion,
|
||||||
Consumable,
|
Consumable,
|
||||||
|
|
@ -76,8 +77,10 @@ pub fn save_game(ecs: &mut World) {
|
||||||
ParticleLifetime,
|
ParticleLifetime,
|
||||||
Player,
|
Player,
|
||||||
Position,
|
Position,
|
||||||
|
Prop,
|
||||||
ProvidesHealing,
|
ProvidesHealing,
|
||||||
ProvidesNutrition,
|
ProvidesNutrition,
|
||||||
|
Quips,
|
||||||
Ranged,
|
Ranged,
|
||||||
Renderable,
|
Renderable,
|
||||||
SingleActivation,
|
SingleActivation,
|
||||||
|
|
@ -147,6 +150,7 @@ pub fn load_game(ecs: &mut World) {
|
||||||
Attributes,
|
Attributes,
|
||||||
BlocksTile,
|
BlocksTile,
|
||||||
BlocksVisibility,
|
BlocksVisibility,
|
||||||
|
Bystander,
|
||||||
CombatStats,
|
CombatStats,
|
||||||
Confusion,
|
Confusion,
|
||||||
Consumable,
|
Consumable,
|
||||||
|
|
@ -172,8 +176,10 @@ pub fn load_game(ecs: &mut World) {
|
||||||
ParticleLifetime,
|
ParticleLifetime,
|
||||||
Player,
|
Player,
|
||||||
Position,
|
Position,
|
||||||
|
Prop,
|
||||||
ProvidesHealing,
|
ProvidesHealing,
|
||||||
ProvidesNutrition,
|
ProvidesNutrition,
|
||||||
|
Quips,
|
||||||
Ranged,
|
Ranged,
|
||||||
Renderable,
|
Renderable,
|
||||||
SingleActivation,
|
SingleActivation,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue