less blocking - targets will try to path to any space around their tar
This commit is contained in:
parent
340aefa9e1
commit
64caf0dc1a
16 changed files with 252 additions and 68 deletions
|
|
@ -46,5 +46,6 @@ Complex example, with negative AC:
|
|||
- At worst (AC rolls a 14), the monster must roll less than -1 to hit you. Impossible.
|
||||
- It rolls a 9, and your AC rolls a 2. 9 is less than 11 (10 + 3 - 2), so it hits.
|
||||
- It rolls 1d8 for damage, and gets a 6.
|
||||
bloodstains: if starts on bloodied tile, remove blood + heal, gain xp, grow (little dog -> dog), etc.
|
||||
- You have negative AC, so you roll 1d14 for damage reduction, and get an 8.
|
||||
- The total damage is 6 - 8 = -2, but damage can't be negative, so you take 1 point of damage.
|
||||
|
|
|
|||
|
|
@ -414,7 +414,7 @@
|
|||
{
|
||||
"id": "ogre",
|
||||
"name": "ogre",
|
||||
"renderable": { "glyph": "O", "fg": "#10570d", "bg": "#000000", "order": 1 },
|
||||
"renderable": { "glyph": "O", "fg": "#10A70d", "bg": "#000000", "order": 1 },
|
||||
"flags": ["SMALL_GROUP"],
|
||||
"level": 5,
|
||||
"bac": 5,
|
||||
|
|
@ -426,11 +426,11 @@
|
|||
{
|
||||
"id": "treant_small",
|
||||
"name": "treant sapling",
|
||||
"renderable": { "glyph": "♠️", "fg": "#00FF00", "bg": "#000000", "order": 1 },
|
||||
"flags": ["LARGE_GROUP"],
|
||||
"renderable": { "glyph": "♠️", "fg": "#10570d", "bg": "#000000", "order": 1 },
|
||||
"flags": ["LARGE_GROUP", "GREEN_BLOOD"],
|
||||
"level": 2,
|
||||
"bac": 12,
|
||||
"speed": 4,
|
||||
"speed": 12,
|
||||
"vision_range": 16,
|
||||
"attacks": [{ "name": "lashes", "hit_bonus": 4, "damage": "1d8" }],
|
||||
"loot": { "table": "scrolls", "chance": 0.05 }
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ pub struct ApproachAI {}
|
|||
impl<'a> System<'a> for ApproachAI {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
WriteExpect<'a, RandomNumberGenerator>,
|
||||
WriteStorage<'a, TakingTurn>,
|
||||
WriteStorage<'a, WantsToApproach>,
|
||||
WriteStorage<'a, Position>,
|
||||
|
|
@ -19,6 +20,7 @@ impl<'a> System<'a> for ApproachAI {
|
|||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
let (
|
||||
mut rng,
|
||||
mut turns,
|
||||
mut wants_to_approach,
|
||||
mut positions,
|
||||
|
|
@ -37,11 +39,26 @@ impl<'a> System<'a> for ApproachAI {
|
|||
&turns,
|
||||
).join() {
|
||||
turn_done.push(entity);
|
||||
let path = a_star_search(
|
||||
map.xy_idx(pos.x, pos.y) as i32,
|
||||
map.xy_idx(approach.idx % map.width, approach.idx / map.width) as i32,
|
||||
&mut *map
|
||||
);
|
||||
let target_idxs = if let Some(paths) = get_adjacent_unblocked(&map, approach.idx as usize) {
|
||||
paths
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let mut path: Option<NavigationPath> = None;
|
||||
let idx = map.xy_idx(pos.x, pos.y);
|
||||
for tar_idx in target_idxs {
|
||||
let potential_path = rltk::a_star_search(idx, tar_idx, &mut *map);
|
||||
if potential_path.success && potential_path.steps.len() > 1 {
|
||||
if path.is_none() || potential_path.steps.len() < path.as_ref().unwrap().steps.len() {
|
||||
path = Some(potential_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
let path = if path.is_some() {
|
||||
path.unwrap()
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
if path.success && path.steps.len() > 1 {
|
||||
let idx = map.xy_idx(pos.x, pos.y);
|
||||
pos.x = (path.steps[1] as i32) % map.width;
|
||||
|
|
@ -61,3 +78,25 @@ impl<'a> System<'a> for ApproachAI {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to get an unblocked index within one tile of a given idx, or None.
|
||||
pub fn get_adjacent_unblocked(map: &WriteExpect<Map>, idx: usize) -> Option<Vec<usize>> {
|
||||
let mut adjacent = Vec::new();
|
||||
let x = (idx as i32) % map.width;
|
||||
let y = (idx as i32) / map.width;
|
||||
for i in -1..2 {
|
||||
for j in -1..2 {
|
||||
if i == 0 && j == 0 {
|
||||
continue;
|
||||
}
|
||||
let new_idx = (x + i + (y + j) * map.width) as usize;
|
||||
if !crate::spatial::is_blocked(new_idx) {
|
||||
adjacent.push(new_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
if adjacent.is_empty() {
|
||||
return None;
|
||||
}
|
||||
return Some(adjacent);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use crate::{ Chasing, EntityMoved, Map, Position, TakingTurn, Telepath, Viewshed
|
|||
use rltk::prelude::*;
|
||||
use specs::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use super::approach_ai_system::get_adjacent_unblocked;
|
||||
|
||||
// If the target is beyond this distance, they're no longer being detected,
|
||||
// so stop following them. This is essentially a combined value of the sound
|
||||
|
|
@ -57,11 +58,27 @@ impl<'a> System<'a> for ChaseAI {
|
|||
).join() {
|
||||
turn_done.push(entity);
|
||||
let target_pos = targets[&entity];
|
||||
let path = a_star_search(
|
||||
map.xy_idx(pos.x, pos.y) as i32,
|
||||
map.xy_idx(target_pos.0, target_pos.1) as i32,
|
||||
&mut *map
|
||||
);
|
||||
let target_idx = map.xy_idx(target_pos.0, target_pos.1);
|
||||
let target_idxs = if let Some(paths) = get_adjacent_unblocked(&map, target_idx) {
|
||||
paths
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let mut path: Option<NavigationPath> = None;
|
||||
let idx = map.xy_idx(pos.x, pos.y);
|
||||
for tar_idx in target_idxs {
|
||||
let potential_path = rltk::a_star_search(idx, tar_idx, &mut *map);
|
||||
if potential_path.success && potential_path.steps.len() > 1 {
|
||||
if path.is_none() || potential_path.steps.len() < path.as_ref().unwrap().steps.len() {
|
||||
path = Some(potential_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
let path = if path.is_some() {
|
||||
path.unwrap()
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
if path.success && path.steps.len() > 1 && path.steps.len() < MAX_CHASE_DISTANCE {
|
||||
let idx = map.xy_idx(pos.x, pos.y);
|
||||
pos.x = (path.steps[1] as i32) % map.width;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,11 @@ pub struct Renderable {
|
|||
pub render_order: i32,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Bleeds {
|
||||
pub colour: RGB,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Player {}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use crate::{
|
|||
Blind,
|
||||
HungerClock,
|
||||
HungerState,
|
||||
Bleeds,
|
||||
};
|
||||
use crate::gui::with_article;
|
||||
use crate::data::visuals::{ DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME };
|
||||
|
|
@ -28,7 +29,10 @@ pub fn inflict_damage(ecs: &mut World, damage: &EffectSpawner, target: Entity) {
|
|||
if !target_pool.god {
|
||||
if let EffectType::Damage { amount } = damage.effect_type {
|
||||
target_pool.hit_points.current -= amount;
|
||||
add_effect(None, EffectType::Bloodstain, Targets::Entity { target });
|
||||
let bleeders = ecs.read_storage::<Bleeds>();
|
||||
if let Some(bleeds) = bleeders.get(target) {
|
||||
add_effect(None, EffectType::Bloodstain { colour: bleeds.colour }, Targets::Entity { target });
|
||||
}
|
||||
add_effect(
|
||||
None,
|
||||
EffectType::Particle {
|
||||
|
|
@ -85,13 +89,14 @@ pub fn add_confusion(ecs: &mut World, effect: &EffectSpawner, target: Entity) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn bloodstain(ecs: &mut World, target: usize) {
|
||||
pub fn bloodstain(ecs: &mut World, target: usize, colour: RGB) {
|
||||
let mut map = ecs.fetch_mut::<Map>();
|
||||
// If the current tile isn't bloody, bloody it.
|
||||
if !map.bloodstains.contains(&target) {
|
||||
map.bloodstains.insert(target);
|
||||
if !map.bloodstains.contains_key(&target) {
|
||||
map.bloodstains.insert(target, colour);
|
||||
return;
|
||||
}
|
||||
if map.bloodstains.get(&target).unwrap() == &colour {
|
||||
let mut spread: i32 = target as i32;
|
||||
let mut attempts: i32 = 0;
|
||||
// Otherwise, roll to move one tile in any direction.
|
||||
|
|
@ -113,17 +118,33 @@ pub fn bloodstain(ecs: &mut World, target: usize) {
|
|||
// - If we ever leave bounds, return.
|
||||
// - Roll a dice on each failed attempt, with an increasing change to return (soft-capping max spread)
|
||||
if spread > 0 && spread < map.height * map.width {
|
||||
if !map.bloodstains.contains(&(spread as usize)) {
|
||||
map.bloodstains.insert(spread as usize);
|
||||
if !map.bloodstains.contains_key(&(spread as usize)) {
|
||||
map.bloodstains.insert(spread as usize, colour);
|
||||
return;
|
||||
}
|
||||
// If bloodied with the same colour, return
|
||||
if map.bloodstains.get(&(spread as usize)).unwrap() == &colour {
|
||||
if rng.roll_dice(1, 10 - attempts) == 1 {
|
||||
return;
|
||||
}
|
||||
// If bloodied but a *different* colour, lerp this blood and current blood.
|
||||
} else {
|
||||
let new_col = map.bloodstains
|
||||
.get(&(spread as usize))
|
||||
.unwrap()
|
||||
.lerp(colour, 0.5);
|
||||
map.bloodstains.insert(spread as usize, new_col);
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let curr_blood = map.bloodstains.get(&target).unwrap();
|
||||
let new_colour = curr_blood.lerp(colour, 0.5);
|
||||
map.bloodstains.insert(target, new_colour);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes a level, and returns the total XP required to reach the next level.
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ pub enum EffectType {
|
|||
Confusion {
|
||||
turns: i32,
|
||||
},
|
||||
Bloodstain,
|
||||
Bloodstain {
|
||||
colour: RGB,
|
||||
},
|
||||
Particle {
|
||||
glyph: FontCharType,
|
||||
fg: RGB,
|
||||
|
|
@ -134,7 +136,6 @@ fn affect_tile(ecs: &mut World, effect: &EffectSpawner, target: usize) {
|
|||
}
|
||||
|
||||
match &effect.effect_type {
|
||||
EffectType::Bloodstain => damage::bloodstain(ecs, target),
|
||||
EffectType::Particle { .. } => particles::particle_to_tile(ecs, target as i32, &effect),
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -158,9 +159,9 @@ fn affect_entity(ecs: &mut World, effect: &EffectSpawner, target: Entity) {
|
|||
EffectType::Damage { .. } => damage::inflict_damage(ecs, effect, target),
|
||||
EffectType::Healing { .. } => damage::heal_damage(ecs, effect, target),
|
||||
EffectType::Confusion { .. } => damage::add_confusion(ecs, effect, target),
|
||||
EffectType::Bloodstain { .. } => {
|
||||
EffectType::Bloodstain { colour } => {
|
||||
if let Some(pos) = targeting::entity_position(ecs, target) {
|
||||
damage::bloodstain(ecs, pos)
|
||||
damage::bloodstain(ecs, pos, *colour);
|
||||
}
|
||||
}
|
||||
EffectType::Particle { .. } => {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ use crate::{
|
|||
GrantsSpell,
|
||||
KnownSpell,
|
||||
KnownSpells,
|
||||
Position,
|
||||
Viewshed,
|
||||
};
|
||||
use crate::data::messages::*;
|
||||
use rltk::prelude::*;
|
||||
|
|
@ -219,13 +221,19 @@ fn handle_damage(ecs: &mut World, event: &mut EventInfo, mut logger: gamelog::Lo
|
|||
continue;
|
||||
}
|
||||
let renderables = ecs.read_storage::<Renderable>();
|
||||
let positions = ecs.read_storage::<Position>();
|
||||
let target_pos = positions.get(target).unwrap_or(&(Position { x: 0, y: 0 }));
|
||||
let viewsheds = ecs.read_storage::<Viewshed>();
|
||||
let player_viewshed = viewsheds.get(*ecs.fetch::<Entity>()).unwrap();
|
||||
if ecs.read_storage::<Player>().get(target).is_some() {
|
||||
logger = logger
|
||||
.colour(renderable_colour(&renderables, target))
|
||||
.append("You")
|
||||
.colour(WHITE)
|
||||
.append(DAMAGE_PLAYER_HIT);
|
||||
} else if ecs.read_storage::<Item>().get(target).is_some() {
|
||||
event.log = true;
|
||||
} else if player_viewshed.visible_tiles.contains(&Point::new(target_pos.x, target_pos.y)) {
|
||||
if ecs.read_storage::<Item>().get(target).is_some() {
|
||||
if ecs.read_storage::<Destructible>().get(target).is_some() {
|
||||
logger = logger
|
||||
.append("The")
|
||||
|
|
@ -244,6 +252,7 @@ fn handle_damage(ecs: &mut World, event: &mut EventInfo, mut logger: gamelog::Lo
|
|||
}
|
||||
event.log = true;
|
||||
}
|
||||
}
|
||||
return (logger, true);
|
||||
}
|
||||
return (logger, false);
|
||||
|
|
|
|||
|
|
@ -729,6 +729,7 @@ fn main() -> rltk::BError {
|
|||
gs.ecs.register::<ProvidesIdentify>();
|
||||
gs.ecs.register::<KnownSpells>();
|
||||
gs.ecs.register::<GrantsSpell>();
|
||||
gs.ecs.register::<Bleeds>();
|
||||
gs.ecs.register::<ParticleLifetime>();
|
||||
gs.ecs.register::<SpawnParticleSimple>();
|
||||
gs.ecs.register::<SpawnParticleBurst>();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use rltk::{ Algorithm2D, BaseMap, Point };
|
||||
use rltk::prelude::*;
|
||||
use serde::{ Deserialize, Serialize };
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{ HashSet, HashMap };
|
||||
mod tiletype;
|
||||
pub use tiletype::{ tile_cost, tile_opaque, tile_walkable, TileType, get_dest, Destination };
|
||||
mod interval_spawning_system;
|
||||
|
|
@ -25,13 +25,13 @@ pub struct Map {
|
|||
pub lit_tiles: Vec<bool>,
|
||||
pub telepath_tiles: Vec<bool>,
|
||||
pub colour_offset: Vec<((f32, f32, f32), (f32, f32, f32))>,
|
||||
pub additional_fg_offset: rltk::RGB,
|
||||
pub additional_fg_offset: RGB,
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub short_name: String,
|
||||
pub depth: i32,
|
||||
pub difficulty: i32,
|
||||
pub bloodstains: HashSet<usize>,
|
||||
pub bloodstains: HashMap<usize, RGB>,
|
||||
pub view_blocked: HashSet<usize>,
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ impl Map {
|
|||
short_name: short_name.to_string(),
|
||||
depth: depth,
|
||||
difficulty: difficulty,
|
||||
bloodstains: HashSet::new(),
|
||||
bloodstains: HashMap::new(),
|
||||
view_blocked: HashSet::new(),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -287,8 +287,8 @@ fn apply_colour_offset(mut rgb: RGB, map: &Map, idx: usize, offset: (i32, i32, i
|
|||
}
|
||||
|
||||
fn apply_bloodstain_if_necessary(mut bg: RGB, map: &Map, idx: usize) -> RGB {
|
||||
if map.bloodstains.contains(&idx) {
|
||||
bg = bg.add(RGB::named(BLOODSTAIN_COLOUR));
|
||||
if map.bloodstains.contains_key(&idx) {
|
||||
bg = bg.add(map.bloodstains[&idx]);
|
||||
}
|
||||
return bg;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ mod forest;
|
|||
use forest::forest_builder;
|
||||
mod foliage;
|
||||
use foliage::Foliage;
|
||||
mod room_themer;
|
||||
use room_themer::{ Theme, ThemeRooms };
|
||||
|
||||
// Shared data to be passed around build chain
|
||||
pub struct BuilderMap {
|
||||
|
|
@ -284,6 +286,8 @@ fn random_room_builder(rng: &mut rltk::RandomNumberGenerator, builder: &mut Buil
|
|||
1 => builder.with(RoomBasedSpawner::new()),
|
||||
_ => builder.with(VoronoiSpawning::new()),
|
||||
}
|
||||
|
||||
builder.with(ThemeRooms::grass(10));
|
||||
}
|
||||
|
||||
fn random_shape_builder(rng: &mut rltk::RandomNumberGenerator, builder: &mut BuilderChain, end: bool) -> bool {
|
||||
|
|
|
|||
78
src/map_builders/room_themer.rs
Normal file
78
src/map_builders/room_themer.rs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
use super::{ BuilderMap, MetaMapBuilder, Rect, TileType };
|
||||
use crate::tile_walkable;
|
||||
use rltk::RandomNumberGenerator;
|
||||
|
||||
pub enum Theme {
|
||||
Grass,
|
||||
Forest,
|
||||
}
|
||||
|
||||
pub struct ThemeRooms {
|
||||
pub theme: Theme,
|
||||
pub percent: i32,
|
||||
}
|
||||
|
||||
impl MetaMapBuilder for ThemeRooms {
|
||||
fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) {
|
||||
self.build(rng, build_data);
|
||||
}
|
||||
}
|
||||
|
||||
impl ThemeRooms {
|
||||
#[allow(dead_code)]
|
||||
pub fn grass(percent: i32) -> Box<ThemeRooms> {
|
||||
return Box::new(ThemeRooms { theme: Theme::Grass, percent });
|
||||
}
|
||||
pub fn forest(percent: i32) -> Box<ThemeRooms> {
|
||||
return Box::new(ThemeRooms { theme: Theme::Forest, percent });
|
||||
}
|
||||
|
||||
fn grassify(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap, room: &Rect) {
|
||||
let (var_x, var_y) = (rng.roll_dice(1, 3), rng.roll_dice(1, 3));
|
||||
let x1 = if room.x1 - var_x > 0 { room.x1 - var_x } else { room.x1 };
|
||||
let x2 = if room.x2 + var_x < build_data.map.width - 1 { room.x2 + var_x } else { room.x2 };
|
||||
let y1 = if room.y1 - var_y > 0 { room.y1 - var_y } else { room.y1 };
|
||||
let y2 = if room.y2 + var_y < build_data.map.height - 1 { room.y2 + var_y } else { room.y2 };
|
||||
for x in x1..x2 {
|
||||
for y in y1..y2 {
|
||||
let idx = build_data.map.xy_idx(x, y);
|
||||
if tile_walkable(build_data.map.tiles[idx]) && build_data.map.tiles[idx] != TileType::DownStair {
|
||||
let tar = if x < room.x1 || x > room.x2 || y < room.y1 || y > room.y2 { 45 } else { 90 };
|
||||
if rng.roll_dice(1, 100) <= tar {
|
||||
match rng.roll_dice(1, 6) {
|
||||
1..=4 => {
|
||||
build_data.map.tiles[idx] = TileType::Grass;
|
||||
}
|
||||
5 => {
|
||||
build_data.map.tiles[idx] = TileType::Foliage;
|
||||
}
|
||||
_ => {
|
||||
build_data.map.tiles[idx] = TileType::HeavyFoliage;
|
||||
build_data.spawn_list.push((idx, "treant_small".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) {
|
||||
let rooms: Vec<Rect>;
|
||||
if let Some(rooms_builder) = &build_data.rooms {
|
||||
rooms = rooms_builder.clone();
|
||||
} else {
|
||||
panic!("RoomCornerRounding requires a builder with rooms.");
|
||||
}
|
||||
|
||||
for room in rooms.iter() {
|
||||
if rng.roll_dice(1, 100) < self.percent {
|
||||
match self.theme {
|
||||
Theme::Grass => self.grassify(rng, build_data, room),
|
||||
_ => {}
|
||||
}
|
||||
build_data.take_snapshot();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ use super::{
|
|||
WantsToPickupItem,
|
||||
get_dest,
|
||||
Destination,
|
||||
Bleeds,
|
||||
};
|
||||
use rltk::prelude::*;
|
||||
use rltk::{ Point, RandomNumberGenerator, Rltk, VirtualKeyCode };
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use crate::gamesystem::*;
|
|||
use crate::gui::Ancestry;
|
||||
use crate::random_table::RandomTable;
|
||||
use crate::config::CONFIG;
|
||||
use crate::data::visuals::BLOODSTAIN_COLOUR;
|
||||
use regex::Regex;
|
||||
use rltk::prelude::*;
|
||||
use specs::prelude::*;
|
||||
|
|
@ -91,6 +92,8 @@ macro_rules! apply_flags {
|
|||
"STATIC" => $eb = $eb.with(MoveMode { mode: Movement::Static }),
|
||||
"RANDOM_PATH" => $eb = $eb.with(MoveMode { mode: Movement::RandomWaypoint { path: None } }),
|
||||
// --- RANDOM MOB ATTRIBUTES ---
|
||||
"GREEN_BLOOD" => $eb = $eb.with(Bleeds { colour: RGB::named((0, 153, 0)) }),
|
||||
"BLUE_BLOOD" => $eb = $eb.with(Bleeds { colour: RGB::named((0, 0, 153)) }),
|
||||
"SMALL_GROUP" => {} // These flags are for region spawning,
|
||||
"LARGE_GROUP" => {} // and don't need to apply a component.
|
||||
"MULTIATTACK" => $eb = $eb.with(MultiAttack {}),
|
||||
|
|
@ -386,6 +389,7 @@ pub fn spawn_named_mob(
|
|||
eb = eb.with(BlocksTile {});
|
||||
eb = eb.with(Faction { name: "hostile".to_string() });
|
||||
eb = eb.with(MoveMode { mode: Movement::Random });
|
||||
eb = eb.with(Bleeds { colour: RGB::named(BLOODSTAIN_COLOUR) });
|
||||
let mut xp_value = 1;
|
||||
let mut has_mind = true;
|
||||
if let Some(flags) = &mob_template.flags {
|
||||
|
|
|
|||
|
|
@ -24,8 +24,10 @@ use super::{
|
|||
TileType,
|
||||
Viewshed,
|
||||
BlocksTile,
|
||||
Bleeds,
|
||||
};
|
||||
use crate::data::entity;
|
||||
use crate::data::visuals::BLOODSTAIN_COLOUR;
|
||||
use crate::gamesystem::*;
|
||||
use rltk::{ RandomNumberGenerator, RGB };
|
||||
use specs::prelude::*;
|
||||
|
|
@ -51,6 +53,7 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
|
|||
bg: RGB::named(rltk::BLACK),
|
||||
render_order: 0,
|
||||
})
|
||||
.with(Bleeds { colour: RGB::named(BLOODSTAIN_COLOUR) })
|
||||
.with(Player {})
|
||||
.with(Mind {})
|
||||
.with(Faction { name: "player".to_string() })
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue