Allow multi-tile sized entities
This commit is contained in:
parent
140531c601
commit
830b721548
@ -1,6 +1,6 @@
|
|||||||
use ::specs::prelude::*;
|
use ::specs::prelude::*;
|
||||||
|
|
||||||
use crate::components::{Faction, MyTurn, Position, WantsToMelee};
|
use crate::components::{Faction, MyTurn, Position, TileSize, WantsToMelee};
|
||||||
use crate::raws::{self, Reaction, RAWS};
|
use crate::raws::{self, Reaction, RAWS};
|
||||||
use crate::{spatial, Map};
|
use crate::{spatial, Map};
|
||||||
|
|
||||||
@ -16,10 +16,11 @@ impl<'a> System<'a> for AdjacentAI {
|
|||||||
WriteStorage<'a, WantsToMelee>,
|
WriteStorage<'a, WantsToMelee>,
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
ReadExpect<'a, Entity>,
|
ReadExpect<'a, Entity>,
|
||||||
|
ReadStorage<'a, TileSize>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
let (mut turns, factions, positions, map, mut want_melee, entities, player) = data;
|
let (mut turns, factions, positions, map, mut want_melee, entities, player, sizes) = data;
|
||||||
|
|
||||||
let mut turn_done: Vec<Entity> = Vec::new();
|
let mut turn_done: Vec<Entity> = Vec::new();
|
||||||
for (entity, _turn, my_faction, pos) in (&entities, &turns, &factions, &positions).join() {
|
for (entity, _turn, my_faction, pos) in (&entities, &turns, &factions, &positions).join() {
|
||||||
@ -29,66 +30,88 @@ impl<'a> System<'a> for AdjacentAI {
|
|||||||
let w = map.width;
|
let w = map.width;
|
||||||
let h = map.height;
|
let h = map.height;
|
||||||
|
|
||||||
// Add possible reactions to adjacents for each direction
|
if let Some(size) = sizes.get(entity) {
|
||||||
if pos.x > 0 {
|
use crate::rect::Rect;
|
||||||
evaluate(idx - 1, &map, &factions, &my_faction.name, &mut reactions);
|
let mob_rect = Rect::new(pos.x, pos.y, size.x, size.y).get_all_tiles();
|
||||||
}
|
let parent_rect = Rect::new(pos.x - 1, pos.y - 1, size.x + 2, size.y + 2);
|
||||||
if pos.x < w - 1 {
|
parent_rect
|
||||||
evaluate(idx + 1, &map, &factions, &my_faction.name, &mut reactions);
|
.get_all_tiles()
|
||||||
}
|
.iter()
|
||||||
if pos.y > 0 {
|
.filter(|t| !mob_rect.contains(t))
|
||||||
evaluate(
|
.for_each(|t| {
|
||||||
idx - w as usize,
|
if t.0 > 0 && t.0 < w - 1 && t.1 > 0 && t.1 < h - 1 {
|
||||||
&map,
|
let target_idx = map.xy_idx(t.0, t.1);
|
||||||
&factions,
|
evaluate(
|
||||||
&my_faction.name,
|
target_idx,
|
||||||
&mut reactions,
|
&map,
|
||||||
);
|
&factions,
|
||||||
}
|
&my_faction.name,
|
||||||
if pos.y < h - 1 {
|
&mut reactions,
|
||||||
evaluate(
|
);
|
||||||
idx + w as usize,
|
}
|
||||||
&map,
|
})
|
||||||
&factions,
|
} else {
|
||||||
&my_faction.name,
|
// Add possible reactions to adjacents for each direction
|
||||||
&mut reactions,
|
if pos.x > 0 {
|
||||||
);
|
evaluate(idx - 1, &map, &factions, &my_faction.name, &mut reactions);
|
||||||
}
|
}
|
||||||
if pos.y > 0 && pos.x > 0 {
|
if pos.x < w - 1 {
|
||||||
evaluate(
|
evaluate(idx + 1, &map, &factions, &my_faction.name, &mut reactions);
|
||||||
(idx - w as usize) - 1,
|
}
|
||||||
&map,
|
if pos.y > 0 {
|
||||||
&factions,
|
evaluate(
|
||||||
&my_faction.name,
|
idx - w as usize,
|
||||||
&mut reactions,
|
&map,
|
||||||
);
|
&factions,
|
||||||
}
|
&my_faction.name,
|
||||||
if pos.y > 0 && pos.x < w - 1 {
|
&mut reactions,
|
||||||
evaluate(
|
);
|
||||||
(idx - w as usize) + 1,
|
}
|
||||||
&map,
|
if pos.y < h - 1 {
|
||||||
&factions,
|
evaluate(
|
||||||
&my_faction.name,
|
idx + w as usize,
|
||||||
&mut reactions,
|
&map,
|
||||||
);
|
&factions,
|
||||||
}
|
&my_faction.name,
|
||||||
if pos.y < h - 1 && pos.x > 0 {
|
&mut reactions,
|
||||||
evaluate(
|
);
|
||||||
(idx + w as usize) - 1,
|
}
|
||||||
&map,
|
if pos.y > 0 && pos.x > 0 {
|
||||||
&factions,
|
evaluate(
|
||||||
&my_faction.name,
|
(idx - w as usize) - 1,
|
||||||
&mut reactions,
|
&map,
|
||||||
);
|
&factions,
|
||||||
}
|
&my_faction.name,
|
||||||
if pos.y < h - 1 && pos.x < w - 1 {
|
&mut reactions,
|
||||||
evaluate(
|
);
|
||||||
(idx + w as usize) + 1,
|
}
|
||||||
&map,
|
if pos.y > 0 && pos.x < w - 1 {
|
||||||
&factions,
|
evaluate(
|
||||||
&my_faction.name,
|
(idx - w as usize) + 1,
|
||||||
&mut reactions,
|
&map,
|
||||||
);
|
&factions,
|
||||||
|
&my_faction.name,
|
||||||
|
&mut reactions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if pos.y < h - 1 && pos.x > 0 {
|
||||||
|
evaluate(
|
||||||
|
(idx + w as usize) - 1,
|
||||||
|
&map,
|
||||||
|
&factions,
|
||||||
|
&my_faction.name,
|
||||||
|
&mut reactions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if pos.y < h - 1 && pos.x < w - 1 {
|
||||||
|
evaluate(
|
||||||
|
(idx + w as usize) + 1,
|
||||||
|
&map,
|
||||||
|
&factions,
|
||||||
|
&my_faction.name,
|
||||||
|
&mut reactions,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut done = false;
|
let mut done = false;
|
||||||
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use ::specs::prelude::*;
|
use ::specs::prelude::*;
|
||||||
|
|
||||||
use crate::components::{ApplyMove, Chasing, MyTurn, Position};
|
use crate::components::{ApplyMove, Chasing, MyTurn, Position, TileSize};
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
pub struct ChaseAI {}
|
pub struct ChaseAI {}
|
||||||
@ -16,10 +16,13 @@ impl<'a> System<'a> for ChaseAI {
|
|||||||
ReadExpect<'a, Map>,
|
ReadExpect<'a, Map>,
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
WriteStorage<'a, ApplyMove>,
|
WriteStorage<'a, ApplyMove>,
|
||||||
|
ReadStorage<'a, TileSize>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
let (mut turns, mut chasing, positions, map, entities, mut apply_move) = data;
|
use ::rltk::a_star_search;
|
||||||
|
|
||||||
|
let (mut turns, mut chasing, positions, map, entities, mut apply_move, sizes) = data;
|
||||||
|
|
||||||
let mut targets: HashMap<Entity, (i32, i32)> = HashMap::new();
|
let mut targets: HashMap<Entity, (i32, i32)> = HashMap::new();
|
||||||
let mut end_chase: Vec<Entity> = Vec::new();
|
let mut end_chase: Vec<Entity> = Vec::new();
|
||||||
@ -40,11 +43,23 @@ impl<'a> System<'a> for ChaseAI {
|
|||||||
for (entity, pos, _chase, _myturn) in (&entities, &positions, &chasing, &turns).join() {
|
for (entity, pos, _chase, _myturn) in (&entities, &positions, &chasing, &turns).join() {
|
||||||
turn_done.push(entity);
|
turn_done.push(entity);
|
||||||
let target_pos = targets[&entity];
|
let target_pos = targets[&entity];
|
||||||
let path = ::rltk::a_star_search(
|
let path;
|
||||||
map.xy_idx(pos.x, pos.y) as i32,
|
|
||||||
map.xy_idx(target_pos.0, target_pos.1) as i32,
|
if let Some(size) = sizes.get(entity) {
|
||||||
&*map,
|
let mut map_copy = map.clone();
|
||||||
);
|
map_copy.populate_blocked_multi(size.x, size.y);
|
||||||
|
path = a_star_search(
|
||||||
|
map_copy.xy_idx(pos.x, pos.y) as i32,
|
||||||
|
map_copy.xy_idx(target_pos.0, target_pos.1) as i32,
|
||||||
|
&map_copy,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
path = a_star_search(
|
||||||
|
map.xy_idx(pos.x, pos.y) as i32,
|
||||||
|
map.xy_idx(target_pos.0, target_pos.1) as i32,
|
||||||
|
&*map,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if path.success && path.steps.len() > 1 && path.steps.len() < 15 {
|
if path.success && path.steps.len() > 1 && path.steps.len() < 15 {
|
||||||
apply_move
|
apply_move
|
||||||
|
@ -2,8 +2,9 @@
|
|||||||
use ::rltk::{Point, Rltk};
|
use ::rltk::{Point, Rltk};
|
||||||
use ::specs::prelude::*;
|
use ::specs::prelude::*;
|
||||||
|
|
||||||
|
use crate::components::{Hidden, Position, Renderable, TileSize};
|
||||||
use crate::map::tile_glyph;
|
use crate::map::tile_glyph;
|
||||||
use crate::{colors, Hidden, Map, Position, Renderable};
|
use crate::{colors, Map};
|
||||||
|
|
||||||
/// Whether to render an outline of the current map's boundaries
|
/// Whether to render an outline of the current map's boundaries
|
||||||
const SHOW_BOUNDARIES: bool = false;
|
const SHOW_BOUNDARIES: bool = false;
|
||||||
@ -58,28 +59,57 @@ pub fn render_camera(ecs: &World, ctx: &mut Rltk) {
|
|||||||
let renderables = ecs.read_storage::<Renderable>();
|
let renderables = ecs.read_storage::<Renderable>();
|
||||||
let hidden = ecs.read_storage::<Hidden>();
|
let hidden = ecs.read_storage::<Hidden>();
|
||||||
let map = ecs.fetch::<Map>();
|
let map = ecs.fetch::<Map>();
|
||||||
|
let sizes = ecs.read_storage::<TileSize>();
|
||||||
|
let entities = ecs.entities();
|
||||||
|
|
||||||
let mut data = (&positions, &renderables, !&hidden)
|
let mut data = (&positions, &renderables, &entities, !&hidden)
|
||||||
.join()
|
.join()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order));
|
data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order));
|
||||||
for (pos, render, _hidden) in data.iter() {
|
for (pos, render, entity, _hidden) in data.iter() {
|
||||||
let idx = map.xy_idx(pos.x, pos.y);
|
if let Some(size) = sizes.get(*entity) {
|
||||||
if map.visible_tiles[idx] {
|
for cy in 0..size.y {
|
||||||
let entity_screen_x = pos.x - min_x;
|
for cx in 0..size.x {
|
||||||
let entity_screen_y = pos.y - min_y;
|
let tile_x = cx + pos.x;
|
||||||
if entity_screen_x > 0
|
let tile_y = cy + pos.y;
|
||||||
&& entity_screen_x < map_width
|
let idx = map.xy_idx(tile_x, tile_y);
|
||||||
&& entity_screen_y > 0
|
if map.visible_tiles[idx] {
|
||||||
&& entity_screen_y < map_height
|
let entity_screen_x = (cx + pos.x) - min_x;
|
||||||
{
|
let entity_screen_y = (cy + pos.y) - min_y;
|
||||||
ctx.set(
|
if entity_screen_x > 0
|
||||||
entity_screen_x,
|
&& entity_screen_x < map_width
|
||||||
entity_screen_y,
|
&& entity_screen_y > 0
|
||||||
render.fg,
|
&& entity_screen_y < map_height
|
||||||
render.bg,
|
{
|
||||||
render.glyph,
|
ctx.set(
|
||||||
);
|
entity_screen_x + 1,
|
||||||
|
entity_screen_y + 1,
|
||||||
|
render.fg,
|
||||||
|
render.bg,
|
||||||
|
render.glyph,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let idx = map.xy_idx(pos.x, pos.y);
|
||||||
|
if map.visible_tiles[idx] {
|
||||||
|
let entity_screen_x = pos.x - min_x;
|
||||||
|
let entity_screen_y = pos.y - min_y;
|
||||||
|
if entity_screen_x > 0
|
||||||
|
&& entity_screen_x < map_width
|
||||||
|
&& entity_screen_y > 0
|
||||||
|
&& entity_screen_y < map_height
|
||||||
|
{
|
||||||
|
ctx.set(
|
||||||
|
entity_screen_x,
|
||||||
|
entity_screen_y,
|
||||||
|
render.fg,
|
||||||
|
render.bg,
|
||||||
|
render.glyph,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -515,3 +515,9 @@ pub struct SpecialAbility {
|
|||||||
pub struct SpecialAbilities {
|
pub struct SpecialAbilities {
|
||||||
pub abilities: Vec<SpecialAbility>,
|
pub abilities: Vec<SpecialAbility>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Component, ConvertSaveload, Clone)]
|
||||||
|
pub struct TileSize {
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
}
|
||||||
|
@ -5,7 +5,7 @@ mod particles;
|
|||||||
mod targeting;
|
mod targeting;
|
||||||
mod triggers;
|
mod triggers;
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::{HashSet, VecDeque};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use ::rltk::{FontCharType, RGB};
|
use ::rltk::{FontCharType, RGB};
|
||||||
@ -85,6 +85,7 @@ pub struct EffectSpawner {
|
|||||||
pub creator: Option<Entity>,
|
pub creator: Option<Entity>,
|
||||||
pub effect_type: EffectType,
|
pub effect_type: EffectType,
|
||||||
pub targets: Targets,
|
pub targets: Targets,
|
||||||
|
dedupe: HashSet<Entity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds an effect to the queue
|
/// Adds an effect to the queue
|
||||||
@ -93,21 +94,22 @@ pub fn add_effect(creator: Option<Entity>, effect_type: EffectType, targets: Tar
|
|||||||
creator,
|
creator,
|
||||||
effect_type,
|
effect_type,
|
||||||
targets,
|
targets,
|
||||||
|
dedupe: HashSet::new(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_effects_queue(ecs: &mut World) {
|
pub fn run_effects_queue(ecs: &mut World) {
|
||||||
loop {
|
loop {
|
||||||
let effect = EFFECT_QUEUE.lock().unwrap().pop_front();
|
let effect = EFFECT_QUEUE.lock().unwrap().pop_front();
|
||||||
if let Some(effect) = effect {
|
if let Some(mut effect) = effect {
|
||||||
target_applicator(ecs, &effect);
|
target_applicator(ecs, &mut effect);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn target_applicator(ecs: &mut World, effect: &EffectSpawner) {
|
fn target_applicator(ecs: &mut World, effect: &mut EffectSpawner) {
|
||||||
if let EffectType::ItemUse { item } = effect.effect_type {
|
if let EffectType::ItemUse { item } = effect.effect_type {
|
||||||
triggers::item_trigger(effect.creator, item, &effect.targets, ecs);
|
triggers::item_trigger(effect.creator, item, &effect.targets, ecs);
|
||||||
} else if let EffectType::SpellUse { spell } = effect.effect_type {
|
} else if let EffectType::SpellUse { spell } = effect.effect_type {
|
||||||
@ -115,7 +117,7 @@ fn target_applicator(ecs: &mut World, effect: &EffectSpawner) {
|
|||||||
} else if let EffectType::TriggerFire { trigger } = effect.effect_type {
|
} else if let EffectType::TriggerFire { trigger } = effect.effect_type {
|
||||||
triggers::trigger(effect.creator, trigger, &effect.targets, ecs);
|
triggers::trigger(effect.creator, trigger, &effect.targets, ecs);
|
||||||
} else {
|
} else {
|
||||||
match &effect.targets {
|
match &effect.targets.clone() {
|
||||||
Targets::Tile { tile_idx } => affect_tile(ecs, effect, *tile_idx),
|
Targets::Tile { tile_idx } => affect_tile(ecs, effect, *tile_idx),
|
||||||
Targets::Tiles { tiles } => tiles
|
Targets::Tiles { tiles } => tiles
|
||||||
.iter()
|
.iter()
|
||||||
@ -143,7 +145,7 @@ fn tile_effect_hits_entities(effect: &EffectType) -> bool {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn affect_tile(ecs: &mut World, effect: &EffectSpawner, tile_idx: i32) {
|
fn affect_tile(ecs: &mut World, effect: &mut EffectSpawner, tile_idx: i32) {
|
||||||
if tile_effect_hits_entities(&effect.effect_type) {
|
if tile_effect_hits_entities(&effect.effect_type) {
|
||||||
spatial::for_each_tile_content(tile_idx as usize, |entity| {
|
spatial::for_each_tile_content(tile_idx as usize, |entity| {
|
||||||
affect_entity(ecs, effect, entity)
|
affect_entity(ecs, effect, entity)
|
||||||
@ -157,7 +159,11 @@ fn affect_tile(ecs: &mut World, effect: &EffectSpawner, tile_idx: i32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn affect_entity(ecs: &mut World, effect: &EffectSpawner, target: Entity) {
|
fn affect_entity(ecs: &mut World, effect: &mut EffectSpawner, target: Entity) {
|
||||||
|
if effect.dedupe.contains(&target) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
effect.dedupe.insert(target);
|
||||||
match &effect.effect_type {
|
match &effect.effect_type {
|
||||||
EffectType::Damage { .. } => damage::inflict_damage(ecs, effect, target),
|
EffectType::Damage { .. } => damage::inflict_damage(ecs, effect, target),
|
||||||
EffectType::EntityDeath => damage::death(ecs, effect, target),
|
EffectType::EntityDeath => damage::death(ecs, effect, target),
|
||||||
|
@ -13,6 +13,11 @@ pub fn inflict_damage(ecs: &mut World, damage: &EffectSpawner, target: Entity) {
|
|||||||
let mut pools = ecs.write_storage::<Pools>();
|
let mut pools = ecs.write_storage::<Pools>();
|
||||||
if let Some(pool) = pools.get_mut(target) {
|
if let Some(pool) = pools.get_mut(target) {
|
||||||
if !pool.god_mode {
|
if !pool.god_mode {
|
||||||
|
if let Some(creator) = damage.creator {
|
||||||
|
if creator == target {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if let EffectType::Damage { amount } = damage.effect_type {
|
if let EffectType::Damage { amount } = damage.effect_type {
|
||||||
pool.hit_points.current -= amount;
|
pool.hit_points.current -= amount;
|
||||||
add_effect(None, EffectType::Bloodstain, Targets::Single { target });
|
add_effect(None, EffectType::Bloodstain, Targets::Single { target });
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use ::rltk::Rltk;
|
use ::rltk::{Point, Rltk};
|
||||||
use ::specs::prelude::*;
|
use ::specs::prelude::*;
|
||||||
|
|
||||||
use crate::components::{Attributes, Duration, Hidden, Name, Pools, Position, StatusEffect};
|
use crate::components::{Attributes, Duration, Hidden, Name, Pools, StatusEffect};
|
||||||
use crate::{camera, colors, Map};
|
use crate::{camera, colors, Map};
|
||||||
|
|
||||||
struct Tooltip {
|
struct Tooltip {
|
||||||
@ -54,21 +54,23 @@ impl Tooltip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn draw_tooltips(ecs: &World, ctx: &mut Rltk) {
|
pub(super) fn draw_tooltips(ecs: &World, ctx: &mut Rltk) {
|
||||||
use rltk::to_cp437;
|
use ::rltk::{to_cp437, Algorithm2D};
|
||||||
|
|
||||||
let (min_x, _max_x, min_y, _max_y) = camera::get_screen_bounds(ecs, ctx);
|
let (min_x, _max_x, min_y, _max_y) = camera::get_screen_bounds(ecs, ctx);
|
||||||
let map = ecs.fetch::<Map>();
|
let map = ecs.fetch::<Map>();
|
||||||
let positions = ecs.read_storage::<Position>();
|
|
||||||
let hidden = ecs.read_storage::<Hidden>();
|
let hidden = ecs.read_storage::<Hidden>();
|
||||||
let attributes = ecs.read_storage::<Attributes>();
|
let attributes = ecs.read_storage::<Attributes>();
|
||||||
let pools = ecs.read_storage::<Pools>();
|
let pools = ecs.read_storage::<Pools>();
|
||||||
let entities = ecs.entities();
|
|
||||||
|
|
||||||
let mouse_pos = ctx.mouse_pos();
|
let mouse_pos = ctx.mouse_pos();
|
||||||
let mut mouse_map_pos = mouse_pos;
|
let mut mouse_map_pos = mouse_pos;
|
||||||
mouse_map_pos.0 += min_x;
|
mouse_map_pos.0 += min_x;
|
||||||
mouse_map_pos.1 += min_y;
|
mouse_map_pos.1 += min_y;
|
||||||
|
|
||||||
|
if mouse_pos.0 < 1 || mouse_pos.0 > 49 || mouse_pos.1 < 1 || mouse_pos.1 > 40 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if mouse_map_pos.0 >= map.width - 1
|
if mouse_map_pos.0 >= map.width - 1
|
||||||
|| mouse_map_pos.1 >= map.height - 1
|
|| mouse_map_pos.1 >= map.height - 1
|
||||||
|| mouse_map_pos.0 < 1
|
|| mouse_map_pos.0 < 1
|
||||||
@ -77,68 +79,74 @@ pub(super) fn draw_tooltips(ecs: &World, ctx: &mut Rltk) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !map.visible_tiles[map.xy_idx(mouse_map_pos.0, mouse_map_pos.1)] {
|
if !map.in_bounds(Point::new(mouse_map_pos.0, mouse_map_pos.1)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mouse_idx = map.xy_idx(mouse_map_pos.0, mouse_map_pos.1);
|
||||||
|
if !map.visible_tiles[mouse_idx] {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut tip_boxes: Vec<Tooltip> = Vec::new();
|
let mut tip_boxes: Vec<Tooltip> = Vec::new();
|
||||||
for (entity, position, _hidden) in (&entities, &positions, !&hidden).join() {
|
crate::spatial::for_each_tile_content(mouse_idx, |entity| {
|
||||||
if position.x == mouse_map_pos.0 && position.y == mouse_map_pos.1 {
|
if hidden.get(entity).is_some() {
|
||||||
let mut tip = Tooltip::new();
|
return;
|
||||||
tip.add(super::get_item_display_name(ecs, entity));
|
|
||||||
|
|
||||||
// Comment on attributes
|
|
||||||
if let Some(attr) = attributes.get(entity) {
|
|
||||||
let mut s = String::new();
|
|
||||||
if attr.might.bonus < 0 {
|
|
||||||
s += "Weak. "
|
|
||||||
}
|
|
||||||
if attr.might.bonus > 0 {
|
|
||||||
s += "String. "
|
|
||||||
}
|
|
||||||
if attr.quickness.bonus < 0 {
|
|
||||||
s += "Clumsy. "
|
|
||||||
}
|
|
||||||
if attr.quickness.bonus > 0 {
|
|
||||||
s += "Agile. "
|
|
||||||
}
|
|
||||||
if attr.fitness.bonus < 0 {
|
|
||||||
s += "Unhealthy. "
|
|
||||||
}
|
|
||||||
if attr.fitness.bonus > 0 {
|
|
||||||
s += "Healthy. "
|
|
||||||
}
|
|
||||||
if attr.intelligence.bonus < 0 {
|
|
||||||
s += "Unintelligent. "
|
|
||||||
}
|
|
||||||
if attr.intelligence.bonus > 0 {
|
|
||||||
s += "Smart. "
|
|
||||||
}
|
|
||||||
if s.is_empty() {
|
|
||||||
s = "Quite Average".to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
tip.add(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comment on pools
|
|
||||||
if let Some(stat) = pools.get(entity) {
|
|
||||||
tip.add(format!("Level: {}", stat.level));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Status effects
|
|
||||||
let statuses = ecs.read_storage::<StatusEffect>();
|
|
||||||
let durations = ecs.read_storage::<Duration>();
|
|
||||||
let names = ecs.read_storage::<Name>();
|
|
||||||
for (status, duration, name) in (&statuses, &durations, &names).join() {
|
|
||||||
if status.target == entity {
|
|
||||||
tip.add(format!("{} ({})", name.name, duration.turns));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tip_boxes.push(tip);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
let mut tip = Tooltip::new();
|
||||||
|
tip.add(super::get_item_display_name(ecs, entity));
|
||||||
|
|
||||||
|
// Comment on attributes
|
||||||
|
if let Some(attr) = attributes.get(entity) {
|
||||||
|
let mut s = String::new();
|
||||||
|
if attr.might.bonus < 0 {
|
||||||
|
s += "Weak. "
|
||||||
|
}
|
||||||
|
if attr.might.bonus > 0 {
|
||||||
|
s += "String. "
|
||||||
|
}
|
||||||
|
if attr.quickness.bonus < 0 {
|
||||||
|
s += "Clumsy. "
|
||||||
|
}
|
||||||
|
if attr.quickness.bonus > 0 {
|
||||||
|
s += "Agile. "
|
||||||
|
}
|
||||||
|
if attr.fitness.bonus < 0 {
|
||||||
|
s += "Unhealthy. "
|
||||||
|
}
|
||||||
|
if attr.fitness.bonus > 0 {
|
||||||
|
s += "Healthy. "
|
||||||
|
}
|
||||||
|
if attr.intelligence.bonus < 0 {
|
||||||
|
s += "Unintelligent. "
|
||||||
|
}
|
||||||
|
if attr.intelligence.bonus > 0 {
|
||||||
|
s += "Smart. "
|
||||||
|
}
|
||||||
|
if s.is_empty() {
|
||||||
|
s = "Quite Average".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
tip.add(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comment on pools
|
||||||
|
if let Some(stat) = pools.get(entity) {
|
||||||
|
tip.add(format!("Level: {}", stat.level));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status effects
|
||||||
|
let statuses = ecs.read_storage::<StatusEffect>();
|
||||||
|
let durations = ecs.read_storage::<Duration>();
|
||||||
|
let names = ecs.read_storage::<Name>();
|
||||||
|
for (status, duration, name) in (&statuses, &durations, &names).join() {
|
||||||
|
if status.target == entity {
|
||||||
|
tip.add(format!("{} ({})", name.name, duration.turns));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tip_boxes.push(tip);
|
||||||
|
});
|
||||||
|
|
||||||
if tip_boxes.is_empty() {
|
if tip_boxes.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
@ -145,6 +145,7 @@ fn init_state() -> State {
|
|||||||
StatusEffect,
|
StatusEffect,
|
||||||
TeachesSpell,
|
TeachesSpell,
|
||||||
TeleportTo,
|
TeleportTo,
|
||||||
|
TileSize,
|
||||||
TownPortal,
|
TownPortal,
|
||||||
Vendor,
|
Vendor,
|
||||||
Viewshed,
|
Viewshed,
|
||||||
|
25
src/map.rs
25
src/map.rs
@ -50,6 +50,31 @@ impl Map {
|
|||||||
spatial::populate_blocked_from_map(self);
|
spatial::populate_blocked_from_map(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn populate_blocked_multi(&mut self, width: i32, height: i32) {
|
||||||
|
self.populate_blocked();
|
||||||
|
for y in 1..self.height - 1 {
|
||||||
|
for x in 1..self.width - 1 {
|
||||||
|
let idx = self.xy_idx(x, y);
|
||||||
|
if !spatial::is_blocked(idx) {
|
||||||
|
for cy in 0..height {
|
||||||
|
for cx in 0..width {
|
||||||
|
let tx = x + cx;
|
||||||
|
let ty = y + cy;
|
||||||
|
if tx < self.width - 1 && ty < self.height - 1 {
|
||||||
|
let tidx = self.xy_idx(tx, ty);
|
||||||
|
if spatial::is_blocked(tidx) {
|
||||||
|
spatial::set_blocked(idx, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spatial::set_blocked(idx, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clear_content_index(&mut self) {
|
pub fn clear_content_index(&mut self) {
|
||||||
spatial::clear();
|
spatial::clear();
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
use ::specs::prelude::*;
|
use ::specs::prelude::*;
|
||||||
|
|
||||||
use crate::components::{BlocksTile, Pools, Position};
|
use crate::components::{BlocksTile, Pools, Position, TileSize};
|
||||||
use crate::{spatial, Map};
|
use crate::{spatial, Map};
|
||||||
|
|
||||||
pub struct MapIndexingSystem {}
|
pub struct MapIndexingSystem {}
|
||||||
|
|
||||||
impl<'a> System<'a> for MapIndexingSystem {
|
impl<'a> System<'a> for MapIndexingSystem {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
ReadExpect<'a, Map>,
|
ReadExpect<'a, Map>,
|
||||||
ReadStorage<'a, Position>,
|
ReadStorage<'a, Position>,
|
||||||
ReadStorage<'a, BlocksTile>,
|
ReadStorage<'a, BlocksTile>,
|
||||||
ReadStorage<'a, Pools>,
|
ReadStorage<'a, Pools>,
|
||||||
|
ReadStorage<'a, TileSize>,
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
let (map, position, blockers, pools, entities) = data;
|
let (map, position, blockers, pools, sizes, entities) = data;
|
||||||
|
|
||||||
spatial::clear();
|
spatial::clear();
|
||||||
spatial::populate_blocked_from_map(&*map);
|
spatial::populate_blocked_from_map(&*map);
|
||||||
@ -29,8 +31,21 @@ impl<'a> System<'a> for MapIndexingSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if alive {
|
if alive {
|
||||||
let idx = map.xy_idx(position.x, position.y);
|
if let Some(size) = sizes.get(entity) {
|
||||||
spatial::index_entity(entity, idx, blockers.get(entity).is_some());
|
// Multi-tile
|
||||||
|
for y in position.y..position.y + size.y {
|
||||||
|
for x in position.x..position.x + size.x {
|
||||||
|
if x > 0 && x < map.width - 1 && y > 0 && y < map.height - 1 {
|
||||||
|
let idx = map.xy_idx(x, y);
|
||||||
|
spatial::index_entity(entity, idx, blockers.get(entity).is_some());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Single tile
|
||||||
|
let idx = map.xy_idx(position.x, position.y);
|
||||||
|
spatial::index_entity(entity, idx, blockers.get(entity).is_some());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,8 @@ pub struct Renderable {
|
|||||||
pub fg: String,
|
pub fg: String,
|
||||||
pub bg: String,
|
pub bg: String,
|
||||||
pub order: i32,
|
pub order: i32,
|
||||||
|
pub x_size: Option<i32>,
|
||||||
|
pub y_size: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
|
@ -497,6 +497,12 @@ pub fn spawn_named_mob(
|
|||||||
// Renderable
|
// Renderable
|
||||||
if let Some(renderable) = &mob_template.renderable {
|
if let Some(renderable) = &mob_template.renderable {
|
||||||
eb = eb.with(get_renderable_component(renderable));
|
eb = eb.with(get_renderable_component(renderable));
|
||||||
|
if renderable.x_size.is_some() || renderable.y_size.is_some() {
|
||||||
|
eb = eb.with(TileSize {
|
||||||
|
x: renderable.x_size.unwrap_or(1),
|
||||||
|
y: renderable.y_size.unwrap_or(1),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eb = eb.with(Name::from(&mob_template.name));
|
eb = eb.with(Name::from(&mob_template.name));
|
||||||
|
14
src/rect.rs
14
src/rect.rs
@ -1,4 +1,6 @@
|
|||||||
//! Four points, one plane, 90° angles
|
//! Four points, one plane, 90° angles
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use ::serde::{Deserialize, Serialize};
|
use ::serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
|
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||||
@ -27,4 +29,16 @@ impl Rect {
|
|||||||
pub fn center(&self) -> (i32, i32) {
|
pub fn center(&self) -> (i32, i32) {
|
||||||
((self.x1 + self.x2) / 2, (self.y1 + self.y2) / 2)
|
((self.x1 + self.x2) / 2, (self.y1 + self.y2) / 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_all_tiles(&self) -> HashSet<(i32, i32)> {
|
||||||
|
let mut result = HashSet::new();
|
||||||
|
|
||||||
|
for y in self.y1..self.y2 {
|
||||||
|
for x in self.x1..self.x2 {
|
||||||
|
result.insert((x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,7 @@ pub fn save_game(ecs: &mut World) {
|
|||||||
StatusEffect,
|
StatusEffect,
|
||||||
TeachesSpell,
|
TeachesSpell,
|
||||||
TeleportTo,
|
TeleportTo,
|
||||||
|
TileSize,
|
||||||
TownPortal,
|
TownPortal,
|
||||||
Vendor,
|
Vendor,
|
||||||
Viewshed,
|
Viewshed,
|
||||||
@ -254,6 +255,7 @@ pub fn load_game(ecs: &mut World) {
|
|||||||
StatusEffect,
|
StatusEffect,
|
||||||
TeachesSpell,
|
TeachesSpell,
|
||||||
TeleportTo,
|
TeleportTo,
|
||||||
|
TileSize,
|
||||||
TownPortal,
|
TownPortal,
|
||||||
Vendor,
|
Vendor,
|
||||||
Viewshed,
|
Viewshed,
|
||||||
|
@ -60,6 +60,11 @@ pub fn is_blocked(idx: usize) -> bool {
|
|||||||
lock.blocked[idx].0 || lock.blocked[idx].1
|
lock.blocked[idx].0 || lock.blocked[idx].1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_blocked(idx: usize, blocked: bool) {
|
||||||
|
let mut lock = SPATIAL_MAP.lock().unwrap();
|
||||||
|
lock.blocked[idx] = (lock.blocked[idx].0, blocked);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn for_each_tile_content<F>(idx: usize, mut f: F)
|
pub fn for_each_tile_content<F>(idx: usize, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(Entity),
|
F: FnMut(Entity),
|
||||||
@ -84,6 +89,12 @@ where
|
|||||||
RunState::AwaitingInput
|
RunState::AwaitingInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get_tile_content_clone(idx: usize) -> Vec<Entity> {
|
||||||
|
let lock = SPATIAL_MAP.lock().unwrap();
|
||||||
|
lock.tile_content[idx].iter().map(|(e, _)| *e).collect()
|
||||||
|
}
|
||||||
|
|
||||||
/// Move an entity on the map from `moving_from` index to the `moving_to` index.
|
/// Move an entity on the map from `moving_from` index to the `moving_to` index.
|
||||||
/// This also recalculates if these two tiles are blocked based on the entities
|
/// This also recalculates if these two tiles are blocked based on the entities
|
||||||
/// within these tiles.
|
/// within these tiles.
|
||||||
|
Loading…
Reference in New Issue
Block a user