complete spatial indexing refactor - SpatialMap
This commit is contained in:
parent
2887bb9736
commit
d439ff6d3f
18 changed files with 351 additions and 217 deletions
|
|
@ -79,12 +79,12 @@ fn evaluate(
|
||||||
this_faction: &str,
|
this_faction: &str,
|
||||||
reactions: &mut Vec<(Entity, Reaction)>,
|
reactions: &mut Vec<(Entity, Reaction)>,
|
||||||
) {
|
) {
|
||||||
for other_entity in map.tile_content[idx].iter() {
|
crate::spatial::for_each_tile_content(idx, |other_entity| {
|
||||||
if let Some(faction) = factions.get(*other_entity) {
|
if let Some(faction) = factions.get(other_entity) {
|
||||||
reactions.push((
|
reactions.push((
|
||||||
*other_entity,
|
other_entity,
|
||||||
crate::raws::faction_reaction(this_faction, &faction.name, &crate::raws::RAWS.lock().unwrap()),
|
crate::raws::faction_reaction(this_faction, &faction.name, &crate::raws::RAWS.lock().unwrap()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,13 +39,12 @@ impl<'a> System<'a> for ApproachAI {
|
||||||
&mut *map,
|
&mut *map,
|
||||||
);
|
);
|
||||||
if path.success && path.steps.len() > 1 {
|
if path.success && path.steps.len() > 1 {
|
||||||
let mut idx = map.xy_idx(pos.x, pos.y);
|
let idx = map.xy_idx(pos.x, pos.y);
|
||||||
map.blocked[idx] = false;
|
|
||||||
pos.x = path.steps[1] as i32 % map.width;
|
pos.x = path.steps[1] as i32 % map.width;
|
||||||
pos.y = path.steps[1] as i32 / map.width;
|
pos.y = path.steps[1] as i32 / map.width;
|
||||||
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert EntityMoved");
|
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert EntityMoved");
|
||||||
idx = map.xy_idx(pos.x, pos.y);
|
let new_idx = map.xy_idx(pos.x, pos.y);
|
||||||
map.blocked[idx] = true;
|
crate::spatial::move_entity(entity, idx, new_idx);
|
||||||
viewshed.dirty = true;
|
viewshed.dirty = true;
|
||||||
if let Some(telepath) = telepaths.get_mut(entity) {
|
if let Some(telepath) = telepaths.get_mut(entity) {
|
||||||
telepath.dirty = true;
|
telepath.dirty = true;
|
||||||
|
|
|
||||||
|
|
@ -59,13 +59,12 @@ impl<'a> System<'a> for ChaseAI {
|
||||||
&mut *map,
|
&mut *map,
|
||||||
);
|
);
|
||||||
if path.success && path.steps.len() > 1 && path.steps.len() < MAX_CHASE_DISTANCE {
|
if path.success && path.steps.len() > 1 && path.steps.len() < MAX_CHASE_DISTANCE {
|
||||||
let mut idx = map.xy_idx(pos.x, pos.y);
|
let idx = map.xy_idx(pos.x, pos.y);
|
||||||
map.blocked[idx] = false;
|
|
||||||
pos.x = path.steps[1] as i32 % map.width;
|
pos.x = path.steps[1] as i32 % map.width;
|
||||||
pos.y = path.steps[1] as i32 / map.width;
|
pos.y = path.steps[1] as i32 / map.width;
|
||||||
entity_moved.insert(entity, EntityMoved {}).expect("Failed to insert EntityMoved");
|
entity_moved.insert(entity, EntityMoved {}).expect("Failed to insert EntityMoved");
|
||||||
idx = map.xy_idx(pos.x, pos.y);
|
let new_idx = map.xy_idx(pos.x, pos.y);
|
||||||
map.blocked[idx] = true;
|
crate::spatial::move_entity(entity, idx, new_idx);
|
||||||
viewshed.dirty = true;
|
viewshed.dirty = true;
|
||||||
if let Some(is_telepath) = telepaths.get_mut(entity) {
|
if let Some(is_telepath) = telepaths.get_mut(entity) {
|
||||||
is_telepath.dirty = true;
|
is_telepath.dirty = true;
|
||||||
|
|
|
||||||
|
|
@ -69,13 +69,12 @@ impl<'a> System<'a> for DefaultAI {
|
||||||
|
|
||||||
if x > 0 && x < map.width - 1 && y > 0 && y < map.height - 1 {
|
if x > 0 && x < map.width - 1 && y > 0 && y < map.height - 1 {
|
||||||
let dest_idx = map.xy_idx(x, y);
|
let dest_idx = map.xy_idx(x, y);
|
||||||
if !map.blocked[dest_idx] {
|
if !crate::spatial::is_blocked(dest_idx) {
|
||||||
let idx = map.xy_idx(pos.x, pos.y);
|
let idx = map.xy_idx(pos.x, pos.y);
|
||||||
map.blocked[idx] = false;
|
|
||||||
pos.x = x;
|
pos.x = x;
|
||||||
pos.y = y;
|
pos.y = y;
|
||||||
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert EntityMoved");
|
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert EntityMoved");
|
||||||
map.blocked[dest_idx] = true;
|
crate::spatial::move_entity(entity, idx, dest_idx);
|
||||||
viewshed.dirty = true;
|
viewshed.dirty = true;
|
||||||
if let Some(is_telepath) = telepaths.get_mut(entity) {
|
if let Some(is_telepath) = telepaths.get_mut(entity) {
|
||||||
is_telepath.dirty = true;
|
is_telepath.dirty = true;
|
||||||
|
|
@ -88,13 +87,12 @@ impl<'a> System<'a> for DefaultAI {
|
||||||
// We have a path - follow it
|
// We have a path - follow it
|
||||||
let mut idx = map.xy_idx(pos.x, pos.y);
|
let mut idx = map.xy_idx(pos.x, pos.y);
|
||||||
if path.len() > 1 {
|
if path.len() > 1 {
|
||||||
if !map.blocked[path[1] as usize] {
|
if !crate::spatial::is_blocked(path[1] as usize) {
|
||||||
map.blocked[idx] = false;
|
|
||||||
pos.x = path[1] as i32 % map.width;
|
pos.x = path[1] as i32 % map.width;
|
||||||
pos.y = path[1] as i32 / map.width;
|
pos.y = path[1] as i32 / map.width;
|
||||||
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert EntityMoved");
|
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert EntityMoved");
|
||||||
idx = map.xy_idx(pos.x, pos.y);
|
let new_idx = map.xy_idx(pos.x, pos.y);
|
||||||
map.blocked[idx] = true;
|
crate::spatial::move_entity(entity, idx, new_idx);
|
||||||
viewshed.dirty = true;
|
viewshed.dirty = true;
|
||||||
if let Some(is_telepath) = telepaths.get_mut(entity) {
|
if let Some(is_telepath) = telepaths.get_mut(entity) {
|
||||||
is_telepath.dirty = true;
|
is_telepath.dirty = true;
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,8 @@ impl<'a> System<'a> for FleeAI {
|
||||||
let flee_map = DijkstraMap::new(map.width as usize, map.height as usize, &fleeing.indices, &*map, 100.0);
|
let flee_map = DijkstraMap::new(map.width as usize, map.height as usize, &fleeing.indices, &*map, 100.0);
|
||||||
let flee_target = DijkstraMap::find_highest_exit(&flee_map, my_idx, &*map);
|
let flee_target = DijkstraMap::find_highest_exit(&flee_map, my_idx, &*map);
|
||||||
if let Some(flee_target) = flee_target {
|
if let Some(flee_target) = flee_target {
|
||||||
if !map.blocked[flee_target as usize] {
|
if !crate::spatial::is_blocked(flee_target as usize) {
|
||||||
map.blocked[my_idx] = false;
|
crate::spatial::move_entity(entity, my_idx, flee_target);
|
||||||
map.blocked[flee_target as usize] = true;
|
|
||||||
viewshed.dirty = true;
|
viewshed.dirty = true;
|
||||||
if let Some(is_telepath) = telepaths.get_mut(entity) {
|
if let Some(is_telepath) = telepaths.get_mut(entity) {
|
||||||
is_telepath.dirty = true;
|
is_telepath.dirty = true;
|
||||||
|
|
|
||||||
|
|
@ -96,20 +96,27 @@ fn evaluate(
|
||||||
reactions: &mut Vec<(usize, Reaction, Entity)>,
|
reactions: &mut Vec<(usize, Reaction, Entity)>,
|
||||||
minds: Option<&ReadStorage<Mind>>,
|
minds: Option<&ReadStorage<Mind>>,
|
||||||
) {
|
) {
|
||||||
for other_entity in map.tile_content[idx].iter() {
|
crate::spatial::for_each_tile_content(idx, |other_entity| {
|
||||||
// If minds are passed, we assume we're using telepathy here,
|
// If minds are passed, we assume we're using telepathy here,
|
||||||
// so if the other entity is mindless, we skip it.
|
// so if the other entity is mindless, we skip it.
|
||||||
if minds.is_some() {
|
if minds.is_some() {
|
||||||
if minds.unwrap().get(*other_entity).is_none() {
|
if minds.unwrap().get(other_entity).is_some() {
|
||||||
continue;
|
if let Some(faction) = factions.get(other_entity) {
|
||||||
|
reactions.push((
|
||||||
|
idx,
|
||||||
|
crate::raws::faction_reaction(this_faction, &faction.name, &crate::raws::RAWS.lock().unwrap()),
|
||||||
|
other_entity,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some(faction) = factions.get(other_entity) {
|
||||||
|
reactions.push((
|
||||||
|
idx,
|
||||||
|
crate::raws::faction_reaction(this_faction, &faction.name, &crate::raws::RAWS.lock().unwrap()),
|
||||||
|
other_entity,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(faction) = factions.get(*other_entity) {
|
});
|
||||||
reactions.push((
|
|
||||||
idx,
|
|
||||||
crate::raws::faction_reaction(this_faction, &faction.name, &crate::raws::RAWS.lock().unwrap()),
|
|
||||||
*other_entity,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -200,36 +200,36 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
|
||||||
let mut seen_entities: Vec<(String, RGB, RGB, u16)> = Vec::new();
|
let mut seen_entities: Vec<(String, RGB, RGB, u16)> = Vec::new();
|
||||||
for tile in viewshed.visible_tiles.iter() {
|
for tile in viewshed.visible_tiles.iter() {
|
||||||
let idx = map.xy_idx(tile.x, tile.y);
|
let idx = map.xy_idx(tile.x, tile.y);
|
||||||
for entity in map.tile_content[idx].iter() {
|
crate::spatial::for_each_tile_content(idx, |entity| {
|
||||||
let mut draw = false;
|
let mut draw = false;
|
||||||
if let Some(_) = names.get(*entity) {
|
if let Some(_) = names.get(entity) {
|
||||||
draw = true;
|
draw = true;
|
||||||
}
|
}
|
||||||
let prop = props.get(*entity);
|
let prop = props.get(entity);
|
||||||
if let Some(_) = prop {
|
if let Some(_) = prop {
|
||||||
draw = false;
|
draw = false;
|
||||||
}
|
}
|
||||||
let is_hidden = hidden.get(*entity);
|
let is_hidden = hidden.get(entity);
|
||||||
if let Some(_) = is_hidden {
|
if let Some(_) = is_hidden {
|
||||||
draw = false;
|
draw = false;
|
||||||
}
|
}
|
||||||
if entity == &*player_entity {
|
if entity == *player_entity {
|
||||||
draw = false;
|
draw = false;
|
||||||
}
|
}
|
||||||
if draw {
|
if draw {
|
||||||
let (render_fg, glyph) = if let Some(renderable) = renderables.get(*entity) {
|
let (render_fg, glyph) = if let Some(renderable) = renderables.get(entity) {
|
||||||
(renderable.fg, renderable.glyph)
|
(renderable.fg, renderable.glyph)
|
||||||
} else {
|
} else {
|
||||||
(RGB::named(rltk::WHITE), rltk::to_cp437('-'))
|
(RGB::named(rltk::WHITE), rltk::to_cp437('-'))
|
||||||
};
|
};
|
||||||
seen_entities.push((
|
seen_entities.push((
|
||||||
get_item_display_name(ecs, *entity).0,
|
get_item_display_name(ecs, entity).0,
|
||||||
get_item_colour(ecs, *entity),
|
get_item_colour(ecs, entity),
|
||||||
render_fg,
|
render_fg,
|
||||||
glyph,
|
glyph,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
seen_entities.sort_by(|a, b| b.0.cmp(&a.0));
|
seen_entities.sort_by(|a, b| b.0.cmp(&a.0));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -188,9 +188,7 @@ impl<'a> System<'a> for ItemUseSystem {
|
||||||
// Single target in a tile
|
// Single target in a tile
|
||||||
let idx = map.xy_idx(target.x, target.y);
|
let idx = map.xy_idx(target.x, target.y);
|
||||||
target_idxs.push(idx);
|
target_idxs.push(idx);
|
||||||
for mob in map.tile_content[idx].iter() {
|
crate::spatial::for_each_tile_content(idx, |mob| targets.push(mob));
|
||||||
targets.push(*mob);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Some(area_effect) => {
|
Some(area_effect) => {
|
||||||
// If item with a targeted AOE is cursed, get the position
|
// If item with a targeted AOE is cursed, get the position
|
||||||
|
|
@ -216,9 +214,7 @@ impl<'a> System<'a> for ItemUseSystem {
|
||||||
for tile_idx in blast_tiles.iter() {
|
for tile_idx in blast_tiles.iter() {
|
||||||
let idx = map.xy_idx(tile_idx.x, tile_idx.y);
|
let idx = map.xy_idx(tile_idx.x, tile_idx.y);
|
||||||
target_idxs.push(idx);
|
target_idxs.push(idx);
|
||||||
for mob in map.tile_content[idx].iter() {
|
crate::spatial::for_each_tile_content(idx, |mob| targets.push(mob));
|
||||||
targets.push(*mob);
|
|
||||||
}
|
|
||||||
particle_builder.request(
|
particle_builder.request(
|
||||||
tile_idx.x,
|
tile_idx.x,
|
||||||
tile_idx.y,
|
tile_idx.y,
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ mod ai;
|
||||||
mod gamesystem;
|
mod gamesystem;
|
||||||
mod random_table;
|
mod random_table;
|
||||||
mod rex_assets;
|
mod rex_assets;
|
||||||
|
mod spatial;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ impl MasterDungeonMap {
|
||||||
pub fn get_map(&self, id: i32) -> Option<Map> {
|
pub fn get_map(&self, id: i32) -> Option<Map> {
|
||||||
if self.maps.contains_key(&id) {
|
if self.maps.contains_key(&id) {
|
||||||
let mut result = self.maps[&id].clone();
|
let mut result = self.maps[&id].clone();
|
||||||
result.tile_content = vec![Vec::new(); (result.width * result.height) as usize];
|
|
||||||
return Some(result);
|
return Some(result);
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ fn spawn_random_mob_in_free_nonvisible_tile(ecs: &mut World) {
|
||||||
fn populate_unblocked_nonvisible(map: &Map) -> Vec<usize> {
|
fn populate_unblocked_nonvisible(map: &Map) -> Vec<usize> {
|
||||||
let mut tiles: Vec<usize> = Vec::new();
|
let mut tiles: Vec<usize> = Vec::new();
|
||||||
for (i, _tile) in map.tiles.iter().enumerate() {
|
for (i, _tile) in map.tiles.iter().enumerate() {
|
||||||
if !map.blocked[i] && !map.visible_tiles[i] {
|
if !crate::spatial::is_blocked(i) && !map.visible_tiles[i] {
|
||||||
tiles.push(i);
|
tiles.push(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,16 +28,11 @@ pub struct Map {
|
||||||
// Combine these offsets into one Vec<(u8, u8, u8)>
|
// Combine these offsets into one Vec<(u8, u8, u8)>
|
||||||
pub colour_offset: Vec<(f32, f32, f32)>,
|
pub colour_offset: Vec<(f32, f32, f32)>,
|
||||||
pub additional_fg_offset: rltk::RGB,
|
pub additional_fg_offset: rltk::RGB,
|
||||||
pub blocked: Vec<bool>,
|
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub difficulty: i32,
|
pub difficulty: i32,
|
||||||
pub bloodstains: HashSet<usize>,
|
pub bloodstains: HashSet<usize>,
|
||||||
pub view_blocked: HashSet<usize>,
|
pub view_blocked: HashSet<usize>,
|
||||||
|
|
||||||
#[serde(skip_serializing)]
|
|
||||||
#[serde(skip_deserializing)]
|
|
||||||
pub tile_content: Vec<Vec<Entity>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Map {
|
impl Map {
|
||||||
|
|
@ -47,6 +42,7 @@ impl Map {
|
||||||
|
|
||||||
pub fn new<S: ToString>(new_id: i32, width: i32, height: i32, difficulty: i32, name: S) -> Map {
|
pub fn new<S: ToString>(new_id: i32, width: i32, height: i32, difficulty: i32, name: S) -> Map {
|
||||||
let map_tile_count = (width * height) as usize;
|
let map_tile_count = (width * height) as usize;
|
||||||
|
crate::spatial::set_size(map_tile_count);
|
||||||
let mut map = Map {
|
let mut map = Map {
|
||||||
tiles: vec![TileType::Wall; map_tile_count],
|
tiles: vec![TileType::Wall; map_tile_count],
|
||||||
width: width,
|
width: width,
|
||||||
|
|
@ -57,13 +53,11 @@ impl Map {
|
||||||
telepath_tiles: vec![false; map_tile_count],
|
telepath_tiles: vec![false; map_tile_count],
|
||||||
colour_offset: vec![(1.0, 1.0, 1.0); map_tile_count],
|
colour_offset: vec![(1.0, 1.0, 1.0); map_tile_count],
|
||||||
additional_fg_offset: rltk::RGB::from_u8(OFFSET_PERCENT as u8, OFFSET_PERCENT as u8, OFFSET_PERCENT as u8),
|
additional_fg_offset: rltk::RGB::from_u8(OFFSET_PERCENT as u8, OFFSET_PERCENT as u8, OFFSET_PERCENT as u8),
|
||||||
blocked: vec![false; map_tile_count],
|
|
||||||
id: new_id,
|
id: new_id,
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
difficulty: difficulty,
|
difficulty: difficulty,
|
||||||
bloodstains: HashSet::new(),
|
bloodstains: HashSet::new(),
|
||||||
view_blocked: HashSet::new(),
|
view_blocked: HashSet::new(),
|
||||||
tile_content: vec![Vec::new(); map_tile_count],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const OFFSET_PERCENT: i32 = 10;
|
const OFFSET_PERCENT: i32 = 10;
|
||||||
|
|
@ -86,19 +80,15 @@ impl Map {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let idx = self.xy_idx(x, y);
|
let idx = self.xy_idx(x, y);
|
||||||
!self.blocked[idx]
|
return !crate::spatial::is_blocked(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn populate_blocked(&mut self) {
|
pub fn populate_blocked(&mut self) {
|
||||||
for (i, tile) in self.tiles.iter_mut().enumerate() {
|
crate::spatial::populate_blocked_from_map(self);
|
||||||
self.blocked[i] = !tile_walkable(*tile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_content_index(&mut self) {
|
pub fn clear_content_index(&mut self) {
|
||||||
for content in self.tile_content.iter_mut() {
|
crate::spatial::clear();
|
||||||
content.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use super::{BlocksTile, Map, Position};
|
use super::{spatial, BlocksTile, Map, Position};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
||||||
pub struct MapIndexingSystem {}
|
pub struct MapIndexingSystem {}
|
||||||
|
|
@ -9,18 +9,11 @@ impl<'a> System<'a> for MapIndexingSystem {
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
let (mut map, position, blockers, entities) = data;
|
let (mut map, position, blockers, entities) = data;
|
||||||
|
|
||||||
map.populate_blocked();
|
spatial::clear();
|
||||||
map.clear_content_index();
|
spatial::populate_blocked_from_map(&*map);
|
||||||
for (entity, position) in (&entities, &position).join() {
|
for (entity, position) in (&entities, &position).join() {
|
||||||
let idx = map.xy_idx(position.x, position.y);
|
let idx = map.xy_idx(position.x, position.y);
|
||||||
|
spatial::index_entity(entity, idx, blockers.get(entity).is_some());
|
||||||
let _p: Option<&BlocksTile> = blockers.get(entity);
|
|
||||||
if let Some(_p) = _p {
|
|
||||||
map.blocked[idx] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push the entity to the appropriate index slot.
|
|
||||||
map.tile_content[idx].push(entity);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
256
src/player.rs
256
src/player.rs
|
|
@ -36,31 +36,35 @@ pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
|
||||||
if map.tile_content[destination_idx].len() == 0 {
|
if !crate::spatial::has_tile_content(destination_idx) {
|
||||||
gamelog::Logger::new().append("You see no door there.").log();
|
gamelog::Logger::new().append("You see no door there.").log();
|
||||||
}
|
}
|
||||||
for potential_target in map.tile_content[destination_idx].iter() {
|
let mut multiple_tile_content = false;
|
||||||
let door = doors.get_mut(*potential_target);
|
if crate::spatial::length(destination_idx) > 1 {
|
||||||
|
multiple_tile_content = true;
|
||||||
|
}
|
||||||
|
crate::spatial::for_each_tile_content(destination_idx, |potential_target| {
|
||||||
|
let door = doors.get_mut(potential_target);
|
||||||
if let Some(door) = door {
|
if let Some(door) = door {
|
||||||
if door.open == true {
|
if door.open == true {
|
||||||
if map.tile_content[destination_idx].len() > 1 {
|
if multiple_tile_content {
|
||||||
if let Some(name) = names.get(*potential_target) {
|
if let Some(name) = names.get(potential_target) {
|
||||||
gamelog::Logger::new().append("The").item_name(&name.name).append("is blocked.").log();
|
gamelog::Logger::new().append("The").item_name(&name.name).append("is blocked.").log();
|
||||||
}
|
}
|
||||||
} else if rng.roll_dice(1, 6) + attributes.strength.bonus < 2 {
|
} else if rng.roll_dice(1, 6) + attributes.strength.bonus < 2 {
|
||||||
if let Some(name) = names.get(*potential_target) {
|
if let Some(name) = names.get(potential_target) {
|
||||||
gamelog::Logger::new().append("The").item_name(&name.name).append("resists!").log();
|
gamelog::Logger::new().append("The").item_name(&name.name).append("resists!").log();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
door.open = false;
|
door.open = false;
|
||||||
blocks_visibility
|
blocks_visibility
|
||||||
.insert(*potential_target, BlocksVisibility {})
|
.insert(potential_target, BlocksVisibility {})
|
||||||
.expect("Unable to insert BlocksVisibility.");
|
.expect("Unable to insert BlocksVisibility.");
|
||||||
blocks_movement
|
blocks_movement
|
||||||
.insert(*potential_target, BlocksTile {})
|
.insert(potential_target, BlocksTile {})
|
||||||
.expect("Unable to insert BlocksTile.");
|
.expect("Unable to insert BlocksTile.");
|
||||||
let render_data = renderables.get_mut(*potential_target).unwrap();
|
let render_data = renderables.get_mut(potential_target).unwrap();
|
||||||
if let Some(name) = names.get(*potential_target) {
|
if let Some(name) = names.get(potential_target) {
|
||||||
gamelog::Logger::new().append("You close the").item_name_n(&name.name).period().log();
|
gamelog::Logger::new().append("You close the").item_name_n(&name.name).period().log();
|
||||||
}
|
}
|
||||||
render_data.glyph = rltk::to_cp437('+'); // Nethack open door, maybe just use '/' instead.
|
render_data.glyph = rltk::to_cp437('+'); // Nethack open door, maybe just use '/' instead.
|
||||||
|
|
@ -71,7 +75,7 @@ pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
gamelog::Logger::new().append("It's already closed.").log();
|
gamelog::Logger::new().append("It's already closed.").log();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,23 +121,23 @@ pub fn open(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
|
||||||
if map.tile_content[destination_idx].len() == 0 {
|
if !crate::spatial::has_tile_content(destination_idx) {
|
||||||
gamelog::Logger::new().append("You see no door there.").log();
|
gamelog::Logger::new().append("You see no door there.").log();
|
||||||
}
|
}
|
||||||
for potential_target in map.tile_content[destination_idx].iter() {
|
crate::spatial::for_each_tile_content(destination_idx, |potential_target| {
|
||||||
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 {
|
||||||
if rng.roll_dice(1, 6) + attributes.strength.bonus < 2 {
|
if rng.roll_dice(1, 6) + attributes.strength.bonus < 2 {
|
||||||
if let Some(name) = names.get(*potential_target) {
|
if let Some(name) = names.get(potential_target) {
|
||||||
gamelog::Logger::new().append("The").item_name(&name.name).append("resists!").log();
|
gamelog::Logger::new().append("The").item_name(&name.name).append("resists!").log();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
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);
|
||||||
let render_data = renderables.get_mut(*potential_target).unwrap();
|
let render_data = renderables.get_mut(potential_target).unwrap();
|
||||||
if let Some(name) = names.get(*potential_target) {
|
if let Some(name) = names.get(potential_target) {
|
||||||
gamelog::Logger::new().append("You open the").item_name_n(&name.name).period().log();
|
gamelog::Logger::new().append("You open the").item_name_n(&name.name).period().log();
|
||||||
}
|
}
|
||||||
render_data.glyph = rltk::to_cp437('▓'); // Nethack open door, maybe just use '/' instead.
|
render_data.glyph = rltk::to_cp437('▓'); // Nethack open door, maybe just use '/' instead.
|
||||||
|
|
@ -144,7 +148,7 @@ pub fn open(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
gamelog::Logger::new().append("It's already open.").log();
|
gamelog::Logger::new().append("It's already open.").log();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,7 +191,7 @@ pub fn kick(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
|
||||||
if map.tile_content[destination_idx].len() == 0 {
|
if !crate::spatial::has_tile_content(destination_idx) {
|
||||||
if rng.roll_dice(1, 20) == 20 {
|
if rng.roll_dice(1, 20) == 20 {
|
||||||
let mut suffer_damage = ecs.write_storage::<SufferDamage>();
|
let mut suffer_damage = ecs.write_storage::<SufferDamage>();
|
||||||
SufferDamage::new_damage(&mut suffer_damage, entity, 1, false);
|
SufferDamage::new_damage(&mut suffer_damage, entity, 1, false);
|
||||||
|
|
@ -201,13 +205,13 @@ pub fn kick(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
} else {
|
} else {
|
||||||
let mut last_non_door_target: Option<Entity> = None;
|
let mut last_non_door_target: Option<Entity> = None;
|
||||||
let mut target_name = "thing";
|
let mut target_name = "thing";
|
||||||
for potential_target in map.tile_content[destination_idx].iter() {
|
crate::spatial::for_each_tile_content_with_bool(destination_idx, |potential_target| {
|
||||||
if let Some(name) = names.get(*potential_target) {
|
if let Some(name) = names.get(potential_target) {
|
||||||
target_name = &name.name;
|
target_name = &name.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's a door,
|
// If it's a door,
|
||||||
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 the door is closed,
|
// If the door is closed,
|
||||||
if door.open == false {
|
if door.open == false {
|
||||||
|
|
@ -220,10 +224,10 @@ pub fn kick(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
.item_name_n(target_name)
|
.item_name_n(target_name)
|
||||||
.append(", it crashes open!")
|
.append(", it crashes open!")
|
||||||
.log();
|
.log();
|
||||||
something_was_destroyed = Some(*potential_target);
|
something_was_destroyed = Some(potential_target);
|
||||||
destroyed_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y));
|
destroyed_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y));
|
||||||
gamelog::record_event("broken_doors", 1);
|
gamelog::record_event("broken_doors", 1);
|
||||||
break;
|
return false;
|
||||||
// 66% chance of just kicking it.
|
// 66% chance of just kicking it.
|
||||||
} else {
|
} else {
|
||||||
gamelog::Logger::new()
|
gamelog::Logger::new()
|
||||||
|
|
@ -231,18 +235,19 @@ pub fn kick(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
.item_name_n(target_name)
|
.item_name_n(target_name)
|
||||||
.period()
|
.period()
|
||||||
.log();
|
.log();
|
||||||
break;
|
return false;
|
||||||
}
|
}
|
||||||
// If the door is open and there's nothing else on the tile,
|
// If the door is open and there's nothing else on the tile,
|
||||||
} else if map.tile_content[destination_idx].len() == 1 {
|
} else if crate::spatial::length(destination_idx) == 1 {
|
||||||
// Just kick the air.
|
// Just kick the air.
|
||||||
gamelog::Logger::new().append("You kick the open air.").log();
|
gamelog::Logger::new().append("You kick the open air.").log();
|
||||||
break;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
last_non_door_target = Some(*potential_target);
|
last_non_door_target = Some(potential_target);
|
||||||
}
|
}
|
||||||
}
|
return true;
|
||||||
|
});
|
||||||
if let Some(_) = last_non_door_target {
|
if let Some(_) = last_non_door_target {
|
||||||
gamelog::Logger::new().append("You kick the").item_name_n(target_name).period().log();
|
gamelog::Logger::new().append("You kick the").item_name_n(target_name).period().log();
|
||||||
let mut particle_builder = ecs.write_resource::<ParticleBuilder>();
|
let mut particle_builder = ecs.write_resource::<ParticleBuilder>();
|
||||||
|
|
@ -269,7 +274,7 @@ pub fn kick(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
return RunState::Ticking;
|
return RunState::Ticking;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool {
|
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState {
|
||||||
let mut positions = ecs.write_storage::<Position>();
|
let mut positions = ecs.write_storage::<Position>();
|
||||||
let mut players = ecs.write_storage::<Player>();
|
let mut players = ecs.write_storage::<Player>();
|
||||||
let mut viewsheds = ecs.write_storage::<Viewshed>();
|
let mut viewsheds = ecs.write_storage::<Viewshed>();
|
||||||
|
|
@ -278,12 +283,12 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool {
|
||||||
let factions = ecs.read_storage::<Faction>();
|
let factions = ecs.read_storage::<Faction>();
|
||||||
let pools = ecs.read_storage::<Pools>();
|
let pools = ecs.read_storage::<Pools>();
|
||||||
let map = ecs.fetch::<Map>();
|
let map = ecs.fetch::<Map>();
|
||||||
|
|
||||||
let entities = ecs.entities();
|
let entities = ecs.entities();
|
||||||
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();
|
let mut swap_entities: Vec<(Entity, i32, i32)> = Vec::new();
|
||||||
|
let mut result = RunState::AwaitingInput;
|
||||||
|
|
||||||
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,14 +296,14 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool {
|
||||||
|| pos.y + delta_y < 0
|
|| pos.y + delta_y < 0
|
||||||
|| pos.y + delta_y > map.height - 1
|
|| pos.y + delta_y > map.height - 1
|
||||||
{
|
{
|
||||||
return false;
|
return result;
|
||||||
}
|
}
|
||||||
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() {
|
result = crate::spatial::for_each_tile_content_with_runstate(destination_idx, |potential_target| {
|
||||||
let mut hostile = true;
|
let mut hostile = true;
|
||||||
if pools.get(*potential_target).is_some() {
|
if pools.get(potential_target).is_some() {
|
||||||
if let Some(faction) = factions.get(*potential_target) {
|
if let Some(faction) = factions.get(potential_target) {
|
||||||
let reaction =
|
let reaction =
|
||||||
crate::raws::faction_reaction(&faction.name, "player", &crate::raws::RAWS.lock().unwrap());
|
crate::raws::faction_reaction(&faction.name, "player", &crate::raws::RAWS.lock().unwrap());
|
||||||
if reaction != Reaction::Attack {
|
if reaction != Reaction::Attack {
|
||||||
|
|
@ -307,7 +312,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !hostile {
|
if !hostile {
|
||||||
swap_entities.push((*potential_target, pos.x, pos.y));
|
swap_entities.push((potential_target, pos.x, pos.y));
|
||||||
pos.x = min(map.width - 1, max(0, pos.x + delta_x));
|
pos.x = min(map.width - 1, max(0, pos.x + delta_x));
|
||||||
pos.y = min(map.height - 1, max(0, pos.y + delta_y));
|
pos.y = min(map.height - 1, max(0, pos.y + delta_y));
|
||||||
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker");
|
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker");
|
||||||
|
|
@ -316,90 +321,95 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool {
|
||||||
ppos.x = pos.x;
|
ppos.x = pos.x;
|
||||||
ppos.y = pos.y;
|
ppos.y = pos.y;
|
||||||
} else {
|
} else {
|
||||||
let target = pools.get(*potential_target);
|
let target = pools.get(potential_target);
|
||||||
if let Some(_target) = target {
|
if let Some(_target) = target {
|
||||||
wants_to_melee
|
wants_to_melee
|
||||||
.insert(entity, WantsToMelee { target: *potential_target })
|
.insert(entity, WantsToMelee { target: potential_target })
|
||||||
.expect("Add target failed.");
|
.expect("Add target failed.");
|
||||||
return true;
|
return Some(RunState::Ticking);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 {
|
||||||
if let Some(name) = names.get(*potential_target) {
|
if let Some(name) = names.get(potential_target) {
|
||||||
gamelog::Logger::new().append("The").item_name(&name.name).append("is in your way.").log();
|
gamelog::Logger::new().append("The").item_name(&name.name).append("is in your way.").log();
|
||||||
}
|
}
|
||||||
return false;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return None;
|
||||||
|
});
|
||||||
|
|
||||||
|
if result == RunState::Ticking {
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if swap_entities.len() > 0 {
|
if swap_entities.len() <= 0 {
|
||||||
for m in swap_entities.iter() {
|
if crate::spatial::is_blocked(destination_idx) {
|
||||||
let their_pos = positions.get_mut(m.0);
|
gamelog::Logger::new().append("You can't move there.").log();
|
||||||
if let Some(name) = names.get(m.0) {
|
return RunState::AwaitingInput;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let hidden = ecs.read_storage::<Hidden>();
|
||||||
return true;
|
// Push every entity name in the pile to a vector of strings
|
||||||
}
|
let mut item_names: Vec<String> = Vec::new();
|
||||||
if map.blocked[destination_idx] {
|
let mut some = false;
|
||||||
gamelog::Logger::new().append("You can't move there.").log();
|
crate::spatial::for_each_tile_content(destination_idx, |entity| {
|
||||||
return false;
|
if !hidden.get(entity).is_some() && names.get(entity).is_some() {
|
||||||
}
|
let item_name = get_item_display_name(ecs, entity).0;
|
||||||
let hidden = ecs.read_storage::<Hidden>();
|
item_names.push(item_name);
|
||||||
// Push every entity name in the pile to a vector of strings
|
some = true;
|
||||||
let mut item_names: Vec<String> = Vec::new();
|
|
||||||
let mut some = false;
|
|
||||||
for entity in map.tile_content[destination_idx].iter() {
|
|
||||||
if !hidden.get(*entity).is_some() && names.get(*entity).is_some() {
|
|
||||||
let item_name = get_item_display_name(ecs, *entity).0;
|
|
||||||
item_names.push(item_name);
|
|
||||||
some = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If some names were found, append. Logger = logger is necessary
|
|
||||||
// makes logger called a mutable self. It's not the most efficient
|
|
||||||
// but it happens infrequently enough (once per player turn at most)
|
|
||||||
// that it shouldn't matter.
|
|
||||||
if some {
|
|
||||||
let mut logger = gamelog::Logger::new().append("You see a");
|
|
||||||
for i in 0..item_names.len() {
|
|
||||||
if i > 0 && i < item_names.len() {
|
|
||||||
logger = logger.append(", a");
|
|
||||||
}
|
}
|
||||||
logger = logger.item_name_n(&item_names[i]);
|
});
|
||||||
|
// If some names were found, append. Logger = logger is necessary
|
||||||
|
// makes logger called a mutable self. It's not the most efficient
|
||||||
|
// but it happens infrequently enough (once per player turn at most)
|
||||||
|
// that it shouldn't matter.
|
||||||
|
if some {
|
||||||
|
let mut logger = gamelog::Logger::new().append("You see a");
|
||||||
|
for i in 0..item_names.len() {
|
||||||
|
if i > 0 && i < item_names.len() {
|
||||||
|
logger = logger.append(", a");
|
||||||
|
}
|
||||||
|
logger = logger.item_name_n(&item_names[i]);
|
||||||
|
}
|
||||||
|
logger.period().log();
|
||||||
}
|
}
|
||||||
logger.period().log();
|
let old_idx = map.xy_idx(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));
|
||||||
|
let new_idx = map.xy_idx(pos.x, pos.y);
|
||||||
|
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker");
|
||||||
|
crate::spatial::move_entity(entity, old_idx, new_idx);
|
||||||
|
// Dirty viewsheds, and check only now if telepath viewshed exists
|
||||||
|
viewshed.dirty = true;
|
||||||
|
if let Some(telepathy) = telepaths.get_mut(entity) {
|
||||||
|
telepathy.dirty = true;
|
||||||
|
}
|
||||||
|
let mut ppos = ecs.write_resource::<Point>();
|
||||||
|
ppos.x = pos.x;
|
||||||
|
ppos.y = pos.y;
|
||||||
|
return RunState::Ticking;
|
||||||
}
|
}
|
||||||
pos.x = min(map.width - 1, max(0, pos.x + delta_x));
|
|
||||||
pos.y = min(map.height - 1, max(0, pos.y + delta_y));
|
|
||||||
|
|
||||||
// Dirty viewsheds, and check only now if telepath viewshed exists
|
|
||||||
viewshed.dirty = true;
|
|
||||||
|
|
||||||
let is_telepath = telepaths.get_mut(entity);
|
|
||||||
if let Some(telepathy) = is_telepath {
|
|
||||||
telepathy.dirty = true;
|
|
||||||
}
|
|
||||||
let mut ppos = ecs.write_resource::<Point>();
|
|
||||||
ppos.x = pos.x;
|
|
||||||
ppos.y = pos.y;
|
|
||||||
entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
for m in swap_entities.iter() {
|
||||||
|
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) = positions.get_mut(m.0) {
|
||||||
|
let old_idx = map.xy_idx(their_pos.x, their_pos.y);
|
||||||
|
their_pos.x = m.1;
|
||||||
|
their_pos.y = m.2;
|
||||||
|
let new_idx = map.xy_idx(their_pos.x, their_pos.y);
|
||||||
|
crate::spatial::move_entity(m.0, old_idx, new_idx);
|
||||||
|
return RunState::Ticking;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_item(ecs: &mut World) -> bool {
|
fn get_item(ecs: &mut World) -> RunState {
|
||||||
let player_pos = ecs.fetch::<Point>();
|
let player_pos = ecs.fetch::<Point>();
|
||||||
let player_entity = ecs.fetch::<Entity>();
|
let player_entity = ecs.fetch::<Entity>();
|
||||||
let entities = ecs.entities();
|
let entities = ecs.entities();
|
||||||
|
|
@ -416,42 +426,40 @@ fn get_item(ecs: &mut World) -> bool {
|
||||||
match target_item {
|
match target_item {
|
||||||
None => {
|
None => {
|
||||||
gamelog::Logger::new().append("There is nothing to pick up.").log();
|
gamelog::Logger::new().append("There is nothing to pick up.").log();
|
||||||
return false;
|
return RunState::AwaitingInput;
|
||||||
}
|
}
|
||||||
Some(item) => {
|
Some(item) => {
|
||||||
let mut pickup = ecs.write_storage::<WantsToPickupItem>();
|
let mut pickup = ecs.write_storage::<WantsToPickupItem>();
|
||||||
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.");
|
||||||
return true;
|
return RunState::Ticking;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
||||||
// Player movement
|
|
||||||
let mut result = false;
|
|
||||||
match ctx.key {
|
match ctx.key {
|
||||||
None => return RunState::AwaitingInput,
|
None => return RunState::AwaitingInput,
|
||||||
Some(key) => match key {
|
Some(key) => match key {
|
||||||
// Cardinals
|
// Cardinals
|
||||||
VirtualKeyCode::Left | VirtualKeyCode::Numpad4 | VirtualKeyCode::H => {
|
VirtualKeyCode::Left | VirtualKeyCode::Numpad4 | VirtualKeyCode::H => {
|
||||||
result = try_move_player(-1, 0, &mut gs.ecs);
|
return try_move_player(-1, 0, &mut gs.ecs)
|
||||||
}
|
}
|
||||||
VirtualKeyCode::Right | VirtualKeyCode::Numpad6 | VirtualKeyCode::L => {
|
VirtualKeyCode::Right | VirtualKeyCode::Numpad6 | VirtualKeyCode::L => {
|
||||||
result = try_move_player(1, 0, &mut gs.ecs);
|
return try_move_player(1, 0, &mut gs.ecs);
|
||||||
}
|
}
|
||||||
VirtualKeyCode::Up | VirtualKeyCode::Numpad8 | VirtualKeyCode::K => {
|
VirtualKeyCode::Up | VirtualKeyCode::Numpad8 | VirtualKeyCode::K => {
|
||||||
result = try_move_player(0, -1, &mut gs.ecs);
|
return try_move_player(0, -1, &mut gs.ecs);
|
||||||
}
|
}
|
||||||
VirtualKeyCode::Down | VirtualKeyCode::Numpad2 | VirtualKeyCode::J => {
|
VirtualKeyCode::Down | VirtualKeyCode::Numpad2 | VirtualKeyCode::J => {
|
||||||
result = try_move_player(0, 1, &mut gs.ecs);
|
return try_move_player(0, 1, &mut gs.ecs);
|
||||||
}
|
}
|
||||||
// Diagonals
|
// Diagonals
|
||||||
VirtualKeyCode::Numpad9 | VirtualKeyCode::U => result = try_move_player(1, -1, &mut gs.ecs),
|
VirtualKeyCode::Numpad9 | VirtualKeyCode::U => return try_move_player(1, -1, &mut gs.ecs),
|
||||||
VirtualKeyCode::Numpad7 | VirtualKeyCode::Y => result = try_move_player(-1, -1, &mut gs.ecs),
|
VirtualKeyCode::Numpad7 | VirtualKeyCode::Y => return try_move_player(-1, -1, &mut gs.ecs),
|
||||||
VirtualKeyCode::Numpad3 | VirtualKeyCode::N => result = try_move_player(1, 1, &mut gs.ecs),
|
VirtualKeyCode::Numpad3 | VirtualKeyCode::N => return try_move_player(1, 1, &mut gs.ecs),
|
||||||
VirtualKeyCode::Numpad1 | VirtualKeyCode::B => result = try_move_player(-1, 1, &mut gs.ecs),
|
VirtualKeyCode::Numpad1 | VirtualKeyCode::B => return try_move_player(-1, 1, &mut gs.ecs),
|
||||||
// id
|
// id
|
||||||
VirtualKeyCode::Period => {
|
VirtualKeyCode::Period => {
|
||||||
if ctx.shift {
|
if ctx.shift {
|
||||||
|
|
@ -460,7 +468,7 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
||||||
}
|
}
|
||||||
return RunState::NextLevel; // > to descend
|
return RunState::NextLevel; // > to descend
|
||||||
} else {
|
} else {
|
||||||
result = skip_turn(&mut gs.ecs); // (Wait a turn)
|
return skip_turn(&mut gs.ecs); // (Wait a turn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VirtualKeyCode::Comma => {
|
VirtualKeyCode::Comma => {
|
||||||
|
|
@ -477,7 +485,7 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VirtualKeyCode::NumpadDecimal => {
|
VirtualKeyCode::NumpadDecimal => {
|
||||||
result = skip_turn(&mut gs.ecs);
|
return skip_turn(&mut gs.ecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Items
|
// Items
|
||||||
|
|
@ -485,7 +493,7 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
||||||
VirtualKeyCode::O => return RunState::ActionWithDirection { function: open },
|
VirtualKeyCode::O => return RunState::ActionWithDirection { function: open },
|
||||||
VirtualKeyCode::F => return RunState::ActionWithDirection { function: kick },
|
VirtualKeyCode::F => return RunState::ActionWithDirection { function: kick },
|
||||||
VirtualKeyCode::G => {
|
VirtualKeyCode::G => {
|
||||||
result = get_item(&mut gs.ecs);
|
return get_item(&mut gs.ecs);
|
||||||
}
|
}
|
||||||
VirtualKeyCode::I => return RunState::ShowInventory,
|
VirtualKeyCode::I => return RunState::ShowInventory,
|
||||||
VirtualKeyCode::D => return RunState::ShowDropItem,
|
VirtualKeyCode::D => return RunState::ShowDropItem,
|
||||||
|
|
@ -498,11 +506,7 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if result {
|
return RunState::AwaitingInput;
|
||||||
return RunState::Ticking;
|
|
||||||
} else {
|
|
||||||
return RunState::AwaitingInput;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_next_level(ecs: &mut World) -> bool {
|
pub fn try_next_level(ecs: &mut World) -> bool {
|
||||||
|
|
@ -529,7 +533,7 @@ pub fn try_previous_level(ecs: &mut World) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn skip_turn(ecs: &mut World) -> bool {
|
fn skip_turn(ecs: &mut World) -> RunState {
|
||||||
let player_entity = ecs.fetch::<Entity>();
|
let player_entity = ecs.fetch::<Entity>();
|
||||||
let mut viewsheds = ecs.write_storage::<Viewshed>();
|
let mut viewsheds = ecs.write_storage::<Viewshed>();
|
||||||
let worldmap_resource = ecs.fetch::<Map>();
|
let worldmap_resource = ecs.fetch::<Map>();
|
||||||
|
|
@ -543,8 +547,8 @@ fn skip_turn(ecs: &mut World) -> bool {
|
||||||
let viewshed = viewsheds.get_mut(*player_entity).unwrap();
|
let viewshed = viewsheds.get_mut(*player_entity).unwrap();
|
||||||
for tile in viewshed.visible_tiles.iter() {
|
for tile in viewshed.visible_tiles.iter() {
|
||||||
let idx = worldmap_resource.xy_idx(tile.x, tile.y);
|
let idx = worldmap_resource.xy_idx(tile.x, tile.y);
|
||||||
for entity_id in worldmap_resource.tile_content[idx].iter() {
|
crate::spatial::for_each_tile_content(idx, |entity_id| {
|
||||||
let faction = factions.get(*entity_id);
|
let faction = factions.get(entity_id);
|
||||||
match faction {
|
match faction {
|
||||||
None => {}
|
None => {}
|
||||||
Some(faction) => {
|
Some(faction) => {
|
||||||
|
|
@ -555,7 +559,7 @@ fn skip_turn(ecs: &mut World) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
// Dirty viewshed (so we search for hidden tiles whenever we wait)
|
// Dirty viewshed (so we search for hidden tiles whenever we wait)
|
||||||
viewshed.dirty = true;
|
viewshed.dirty = true;
|
||||||
|
|
@ -583,7 +587,7 @@ fn skip_turn(ecs: &mut World) -> bool {
|
||||||
|
|
||||||
gamelog::Logger::new().append("You wait a turn.").log();
|
gamelog::Logger::new().append("You wait a turn.").log();
|
||||||
|
|
||||||
return true;
|
return RunState::Ticking;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Playing around with autoexplore, without having read how to do it.
|
/* Playing around with autoexplore, without having read how to do it.
|
||||||
|
|
|
||||||
|
|
@ -247,7 +247,7 @@ pub fn load_game(ecs: &mut World) {
|
||||||
for (e, h) in (&entities, &helper).join() {
|
for (e, h) in (&entities, &helper).join() {
|
||||||
let mut worldmap = ecs.write_resource::<super::map::Map>();
|
let mut worldmap = ecs.write_resource::<super::map::Map>();
|
||||||
*worldmap = h.map.clone();
|
*worldmap = h.map.clone();
|
||||||
worldmap.tile_content = vec![Vec::new(); (worldmap.width * worldmap.height) as usize];
|
crate::spatial::set_size((worldmap.width * worldmap.height) as usize);
|
||||||
deleteme = Some(e);
|
deleteme = Some(e);
|
||||||
}
|
}
|
||||||
for (e, h) in (&entities, &helper2).join() {
|
for (e, h) in (&entities, &helper2).join() {
|
||||||
|
|
|
||||||
149
src/spatial/mod.rs
Normal file
149
src/spatial/mod.rs
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
use crate::{tile_walkable, Map, RunState};
|
||||||
|
use specs::prelude::*;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
struct SpatialMap {
|
||||||
|
blocked: Vec<(bool, bool)>,
|
||||||
|
tile_content: Vec<Vec<(Entity, bool)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpatialMap {
|
||||||
|
fn new() -> Self {
|
||||||
|
return Self { blocked: Vec::new(), tile_content: Vec::new() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref SPATIAL_MAP: Mutex<SpatialMap> = Mutex::new(SpatialMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the size of the SpatialMap.
|
||||||
|
pub fn set_size(map_tile_count: usize) {
|
||||||
|
let mut lock = SPATIAL_MAP.lock().unwrap();
|
||||||
|
lock.blocked = vec![(false, false); map_tile_count];
|
||||||
|
lock.tile_content = vec![Vec::new(); map_tile_count];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears the SpatialMap. Blocked is set to (false, false),
|
||||||
|
/// and all tile content is cleared.
|
||||||
|
pub fn clear() {
|
||||||
|
let mut lock = SPATIAL_MAP.lock().unwrap();
|
||||||
|
lock.blocked.iter_mut().for_each(|b| {
|
||||||
|
b.0 = false;
|
||||||
|
b.1 = false;
|
||||||
|
});
|
||||||
|
for content in lock.tile_content.iter_mut() {
|
||||||
|
content.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterates through every tile in the map, setting the SpatialMap's
|
||||||
|
/// blocked-by-map tuple entry to true wherever a tile is impassable.
|
||||||
|
pub fn populate_blocked_from_map(map: &Map) {
|
||||||
|
let mut lock = SPATIAL_MAP.lock().unwrap();
|
||||||
|
for (i, tile) in map.tiles.iter().enumerate() {
|
||||||
|
lock.blocked[i].0 = !tile_walkable(*tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indexes a new entity within the SpatialMap, storing the entity
|
||||||
|
/// and their BlocksTile status.
|
||||||
|
pub fn index_entity(entity: Entity, idx: usize, blocks_tile: bool) {
|
||||||
|
let mut lock = SPATIAL_MAP.lock().unwrap();
|
||||||
|
lock.tile_content[idx].push((entity, blocks_tile));
|
||||||
|
if blocks_tile {
|
||||||
|
lock.blocked[idx].1 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns is_empty on a given tile content idx.
|
||||||
|
pub fn has_tile_content(idx: usize) -> bool {
|
||||||
|
let lock = SPATIAL_MAP.lock().unwrap();
|
||||||
|
if lock.tile_content[idx].is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of entries on a given index.
|
||||||
|
pub fn length(idx: usize) -> usize {
|
||||||
|
let lock = SPATIAL_MAP.lock().unwrap();
|
||||||
|
return lock.tile_content[idx].len();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the idx is blocked by either a map tile or an entity.
|
||||||
|
pub fn is_blocked(idx: usize) -> bool {
|
||||||
|
let lock = SPATIAL_MAP.lock().unwrap();
|
||||||
|
return lock.blocked[idx].0 || lock.blocked[idx].1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls a function on every entity within a given tile idx.
|
||||||
|
pub fn for_each_tile_content<F>(idx: usize, mut f: F)
|
||||||
|
where
|
||||||
|
F: FnMut(Entity),
|
||||||
|
{
|
||||||
|
let lock = SPATIAL_MAP.lock().unwrap();
|
||||||
|
for entity in lock.tile_content[idx].iter() {
|
||||||
|
f(entity.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls a function on every entity within a given tile idx, with the
|
||||||
|
/// added ability to return a RunState mid-calc.
|
||||||
|
pub fn for_each_tile_content_with_runstate<F>(idx: usize, mut f: F) -> RunState
|
||||||
|
where
|
||||||
|
F: FnMut(Entity) -> Option<RunState>,
|
||||||
|
{
|
||||||
|
let lock = SPATIAL_MAP.lock().unwrap();
|
||||||
|
for entity in lock.tile_content[idx].iter() {
|
||||||
|
if let Some(rs) = f(entity.0) {
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RunState::AwaitingInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls a function on every entity within a given tile idx, breaking if
|
||||||
|
/// the closure ever returns false.
|
||||||
|
pub fn for_each_tile_content_with_bool<F>(idx: usize, mut f: F)
|
||||||
|
where
|
||||||
|
F: FnMut(Entity) -> bool,
|
||||||
|
{
|
||||||
|
let lock = SPATIAL_MAP.lock().unwrap();
|
||||||
|
for entity in lock.tile_content[idx].iter() {
|
||||||
|
if !f(entity.0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves an entity from one index to another in the SpatialMap, and
|
||||||
|
/// recalculates blocks for both affected tiles.
|
||||||
|
pub fn move_entity(entity: Entity, moving_from: usize, moving_to: usize) {
|
||||||
|
let mut lock = SPATIAL_MAP.lock().unwrap();
|
||||||
|
let mut entity_blocks = false;
|
||||||
|
lock.tile_content[moving_from].retain(|(e, blocks)| {
|
||||||
|
if *e == entity {
|
||||||
|
entity_blocks = *blocks;
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
lock.tile_content[moving_to].push((entity, entity_blocks));
|
||||||
|
// Recalculate blocks
|
||||||
|
let mut from_blocked = false;
|
||||||
|
let mut to_blocked = false;
|
||||||
|
lock.tile_content[moving_from].iter().for_each(|(_, blocks)| {
|
||||||
|
if *blocks {
|
||||||
|
from_blocked = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
lock.tile_content[moving_to].iter().for_each(|(_, blocks)| {
|
||||||
|
if *blocks {
|
||||||
|
from_blocked = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
lock.blocked[moving_from].1 = from_blocked;
|
||||||
|
lock.blocked[moving_to].1 = to_blocked;
|
||||||
|
}
|
||||||
|
|
@ -45,42 +45,42 @@ impl<'a> System<'a> for TriggerSystem {
|
||||||
let mut remove_entities: Vec<Entity> = Vec::new();
|
let mut remove_entities: Vec<Entity> = Vec::new();
|
||||||
for (entity, mut _entity_moved, pos) in (&entities, &mut entity_moved, &position).join() {
|
for (entity, mut _entity_moved, pos) in (&entities, &mut entity_moved, &position).join() {
|
||||||
let idx = map.xy_idx(pos.x, pos.y);
|
let idx = map.xy_idx(pos.x, pos.y);
|
||||||
for entity_id in map.tile_content[idx].iter() {
|
crate::spatial::for_each_tile_content(idx, |entity_id| {
|
||||||
if entity != *entity_id {
|
if entity != entity_id {
|
||||||
let maybe_trigger = entry_trigger.get(*entity_id);
|
let maybe_trigger = entry_trigger.get(entity_id);
|
||||||
match maybe_trigger {
|
match maybe_trigger {
|
||||||
None => {}
|
None => {}
|
||||||
Some(_trigger) => {
|
Some(_trigger) => {
|
||||||
// Something on this pos had a trigger
|
// Something on this pos had a trigger
|
||||||
let name = names.get(*entity_id);
|
let name = names.get(entity_id);
|
||||||
hidden.remove(*entity_id);
|
hidden.remove(entity_id);
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
particle_builder.trap_triggered(pos.x, pos.y);
|
particle_builder.trap_triggered(pos.x, pos.y);
|
||||||
gamelog::Logger::new().item_name(&name.name).append("triggers!").log();
|
gamelog::Logger::new().item_name(&name.name).append("triggers!").log();
|
||||||
}
|
}
|
||||||
|
|
||||||
let damage = inflicts_damage.get(*entity_id);
|
let damage = inflicts_damage.get(entity_id);
|
||||||
if let Some(damage) = damage {
|
if let Some(damage) = damage {
|
||||||
let damage_roll = rng.roll_dice(damage.n_dice, damage.sides) + damage.modifier;
|
let damage_roll = rng.roll_dice(damage.n_dice, damage.sides) + damage.modifier;
|
||||||
particle_builder.damage_taken(pos.x, pos.y);
|
particle_builder.damage_taken(pos.x, pos.y);
|
||||||
SufferDamage::new_damage(&mut inflict_damage, entity, damage_roll, false);
|
SufferDamage::new_damage(&mut inflict_damage, entity, damage_roll, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let confuses = confusion.get(*entity_id);
|
let confuses = confusion.get(entity_id);
|
||||||
if let Some(confuses) = confuses {
|
if let Some(confuses) = confuses {
|
||||||
confusion
|
confusion
|
||||||
.insert(entity, Confusion { turns: confuses.turns })
|
.insert(entity, Confusion { turns: confuses.turns })
|
||||||
.expect("Unable to insert confusion");
|
.expect("Unable to insert confusion");
|
||||||
}
|
}
|
||||||
|
|
||||||
let sa = single_activation.get(*entity_id);
|
let sa = single_activation.get(entity_id);
|
||||||
if let Some(_sa) = sa {
|
if let Some(_sa) = sa {
|
||||||
remove_entities.push(*entity_id);
|
remove_entities.push(entity_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for trap in remove_entities.iter() {
|
for trap in remove_entities.iter() {
|
||||||
|
|
|
||||||
|
|
@ -54,11 +54,11 @@ impl<'a> System<'a> for VisibilitySystem {
|
||||||
map.visible_tiles[idx] = true;
|
map.visible_tiles[idx] = true;
|
||||||
|
|
||||||
// Reveal hidden things
|
// Reveal hidden things
|
||||||
for thing in map.tile_content[idx].iter() {
|
crate::spatial::for_each_tile_content(idx, |e| {
|
||||||
let is_hidden = hidden.get(*thing);
|
let is_hidden = hidden.get(e);
|
||||||
if let Some(_is_hidden) = is_hidden {
|
if let Some(_is_hidden) = is_hidden {
|
||||||
if rng.roll_dice(1, 12) == 1 {
|
if rng.roll_dice(1, 12) == 1 {
|
||||||
let name = names.get(*thing);
|
let name = names.get(e);
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
gamelog::Logger::new()
|
gamelog::Logger::new()
|
||||||
.append("You spot a")
|
.append("You spot a")
|
||||||
|
|
@ -66,10 +66,10 @@ impl<'a> System<'a> for VisibilitySystem {
|
||||||
.period()
|
.period()
|
||||||
.log();
|
.log();
|
||||||
}
|
}
|
||||||
hidden.remove(*thing);
|
hidden.remove(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue