Compare commits

..

No commits in common. "fa714104532ac2cf1dac5af9278ab77481bd3c35" and "7c67291031f09406d438c24d0ba2d071e61e38e4" have entirely different histories.

10 changed files with 105 additions and 220 deletions

File diff suppressed because one or more lines are too long

View File

@ -91,7 +91,7 @@ impl<'a> System<'a> for ItemUseSystem {
healing, healing,
inflicts_damage, inflicts_damage,
_ranged, _ranged,
map, mut map,
mut suffer_damage, mut suffer_damage,
aoe, aoe,
mut confused, mut confused,

View File

@ -30,7 +30,6 @@ mod healing_system;
mod hunger_system; mod hunger_system;
mod inventory_system; mod inventory_system;
mod map; mod map;
pub mod map_builders;
mod map_indexing_system; mod map_indexing_system;
mod melee_combat_system; mod melee_combat_system;
mod monster_ai_system; mod monster_ai_system;
@ -41,8 +40,8 @@ mod rect;
mod rex_assets; mod rex_assets;
mod save_load_system; mod save_load_system;
mod spawner; mod spawner;
mod trigger_system;
mod visibility_system; mod visibility_system;
mod trigger_system;
#[derive(PartialEq, Copy, Clone)] #[derive(PartialEq, Copy, Clone)]
pub enum RunState { pub enum RunState {
@ -84,22 +83,20 @@ impl State {
} }
// Build a new map and place the player // Build a new map and place the player
let mut map_builder = map_builders::new_random_builder(1);
let worldmap; let worldmap;
let player_start;
{ {
let mut worldmap_resource = self.ecs.write_resource::<Map>(); let mut worldmap_resource = self.ecs.write_resource::<Map>();
map_builder.build_map(); *worldmap_resource = Map::new_map_rooms_and_corridors(1);
*worldmap_resource = map_builder.get_map();
player_start = map_builder.get_starting_position();
worldmap = worldmap_resource.clone(); worldmap = worldmap_resource.clone();
} }
// Spawn bad guys // Spawn bad guys
map_builder.spawn_entities(&mut self.ecs); for room in worldmap.rooms.iter().skip(1) {
spawner::spawn_room(&mut self.ecs, room, 1);
}
// Place the player and update resources // Place the player and update resources
let (player_x, player_y) = (player_start.x, player_start.y); let (player_x, player_y) = worldmap.rooms[0].center();
let player_entity = spawner::player(&mut self.ecs, player_x, player_y); let player_entity = spawner::player(&mut self.ecs, player_x, player_y);
let mut player_position = self.ecs.write_resource::<Point>(); let mut player_position = self.ecs.write_resource::<Point>();
*player_position = Point::new(player_x, player_y); *player_position = Point::new(player_x, player_y);
@ -167,23 +164,20 @@ impl State {
.expect("Unable to delete entity") .expect("Unable to delete entity")
} }
let mut map_builder;
let worldmap; let worldmap;
let current_depth; let current_depth;
let player_start;
{ {
let mut worldmap_resource = self.ecs.write_resource::<Map>(); let mut worldmap_resource = self.ecs.write_resource::<Map>();
current_depth = worldmap_resource.depth; current_depth = worldmap_resource.depth;
map_builder = map_builders::new_random_builder(current_depth + 1); *worldmap_resource = Map::new_map_rooms_and_corridors(current_depth + 1);
map_builder.build_map();
*worldmap_resource = map_builder.get_map();
player_start = map_builder.get_starting_position();
worldmap = worldmap_resource.clone(); worldmap = worldmap_resource.clone();
} }
map_builder.spawn_entities(&mut self.ecs); for room in worldmap.rooms.iter().skip(1) {
spawner::spawn_room(&mut self.ecs, room, current_depth + 1);
}
let (player_x, player_y) = (player_start.x, player_start.y); let (player_x, player_y) = worldmap.rooms[0].center();
let mut player_position = self.ecs.write_resource::<Point>(); let mut player_position = self.ecs.write_resource::<Point>();
*player_position = Point::new(player_x, player_y); *player_position = Point::new(player_x, player_y);
let mut position_components = self.ecs.write_storage::<Position>(); let mut position_components = self.ecs.write_storage::<Position>();
@ -518,14 +512,14 @@ fn main() -> rltk::BError {
gs.ecs.insert(SimpleMarkerAllocator::<SerializeMe>::new()); gs.ecs.insert(SimpleMarkerAllocator::<SerializeMe>::new());
let mut map_builder = map_builders::new_random_builder(1); let map = Map::new_map_rooms_and_corridors(1);
map_builder.build_map(); let (player_x, player_y) = map.rooms[0].center();
let player_pos = map_builder.get_starting_position();
let mut map = map_builder.get_map();
let (player_x, player_y) = (player_pos.x, player_pos.y);
let player_entity = spawner::player(&mut gs.ecs, player_x, player_y); let player_entity = spawner::player(&mut gs.ecs, player_x, player_y);
map_builder.spawn_entities(&mut gs.ecs);
for room in map.rooms.iter().skip(1) {
spawner::spawn_room(&mut gs.ecs, room, 1);
}
gs.ecs.insert(map); gs.ecs.insert(map);
gs.ecs.insert(Point::new(player_x, player_y)); gs.ecs.insert(Point::new(player_x, player_y));

View File

@ -1,6 +1,7 @@
use std::cmp::{max, min};
use std::collections::HashSet; use std::collections::HashSet;
use rltk::{Algorithm2D, BaseMap, FontCharType, Point, RGB, Rltk}; use rltk::{Algorithm2D, BaseMap, FontCharType, Point, RandomNumberGenerator, Rltk, RGB};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use specs::prelude::*; use specs::prelude::*;
@ -21,6 +22,7 @@ pub const MAX_MONSTER: i32 = 4;
#[derive(Default, Serialize, Deserialize, Clone)] #[derive(Default, Serialize, Deserialize, Clone)]
pub struct Map { pub struct Map {
pub tiles: Vec<TileType>, pub tiles: Vec<TileType>,
pub rooms: Vec<Rect>,
pub width: i32, pub width: i32,
pub height: i32, pub height: i32,
pub revealed_tiles: Vec<bool>, pub revealed_tiles: Vec<bool>,
@ -39,6 +41,33 @@ impl Map {
(y as usize * self.width as usize) + x as usize (y as usize * self.width as usize) + x as usize
} }
fn apply_room_to_map(&mut self, room: &Rect) {
for y in room.y1 + 1..=room.y2 {
for x in room.x1 + 1..=room.x2 {
let idx = self.xy_idx(x, y);
self.tiles[idx] = TileType::Floor;
}
}
}
fn apply_horizontal_tunnel(&mut self, x1: i32, x2: i32, y: i32) {
for x in min(x1, x2)..=max(x1, x2) {
let idx = self.xy_idx(x, y);
if idx > 0 && idx < self.width as usize * self.height as usize {
self.tiles[idx as usize] = TileType::Floor;
}
}
}
fn apply_vertical_tunnel(&mut self, y1: i32, y2: i32, x: i32) {
for y in min(y1, y2)..=max(y1, y2) {
let idx = self.xy_idx(x, y);
if idx > 0 && idx < self.width as usize * self.height as usize {
self.tiles[idx as usize] = TileType::Floor;
}
}
}
fn is_exit_valid(&self, x: i32, y: i32) -> bool { fn is_exit_valid(&self, x: i32, y: i32) -> bool {
if x < 1 || x > self.width - 1 || y < 1 || y > self.height - 1 { if x < 1 || x > self.width - 1 || y < 1 || y > self.height - 1 {
return false; return false;
@ -59,9 +88,10 @@ impl Map {
} }
} }
pub fn new(new_depth: i32) -> Map { pub fn new_map_rooms_and_corridors(new_depth: i32) -> Map {
Map { let mut map = Map {
tiles: vec![TileType::Wall; MAP_COUNT], tiles: vec![TileType::Wall; MAP_COUNT],
rooms: Vec::new(),
width: MAP_WIDTH as i32, width: MAP_WIDTH as i32,
height: MAP_HEIGHT as i32, height: MAP_HEIGHT as i32,
revealed_tiles: vec![false; MAP_COUNT], revealed_tiles: vec![false; MAP_COUNT],
@ -70,7 +100,50 @@ impl Map {
tile_content: vec![Vec::new(); MAP_COUNT], tile_content: vec![Vec::new(); MAP_COUNT],
depth: new_depth, depth: new_depth,
bloodstains: HashSet::new(), bloodstains: HashSet::new(),
};
const MAX_ROOMS: i32 = 30;
const MIN_SIZE: i32 = 6;
const MAX_SIZE: i32 = 10;
let mut rng = RandomNumberGenerator::new();
for _ in 0..MAX_ROOMS {
let w = rng.range(MIN_SIZE, MAX_SIZE);
let h = rng.range(MIN_SIZE, MAX_SIZE);
let x = rng.roll_dice(1, map.width - w - 1) - 1;
let y = rng.roll_dice(1, map.height - h - 1) - 1;
let new_room = Rect::new(x, y, w, h);
let mut ok = true;
for other_room in map.rooms.iter() {
if new_room.intersect(other_room) {
ok = false
};
} }
if ok {
map.apply_room_to_map(&new_room);
if !map.rooms.is_empty() {
let (new_x, new_y) = new_room.center();
let (prev_x, prev_y) = map.rooms[map.rooms.len() - 1].center();
if rng.range(0, 2) == 1 {
map.apply_horizontal_tunnel(prev_x, new_x, prev_y);
map.apply_vertical_tunnel(prev_y, new_y, new_x);
} else {
map.apply_vertical_tunnel(prev_y, new_y, prev_x);
map.apply_horizontal_tunnel(prev_x, new_x, new_y);
}
}
map.rooms.push(new_room);
}
}
let stairs_position = map.rooms[map.rooms.len() - 1].center();
let stairs_idx = map.xy_idx(stairs_position.0, stairs_position.1);
map.tiles[stairs_idx] = TileType::DownStairs;
map
} }
} }

View File

@ -1,31 +0,0 @@
use std::cmp::{max, min};
use crate::rect::Rect;
use crate::{Map, TileType};
pub fn apply_room_to_map(map: &mut Map, room: &Rect) {
for y in room.y1 + 1..=room.y2 {
for x in room.x1 + 1..=room.x2 {
let idx = map.xy_idx(x, y);
map.tiles[idx] = TileType::Floor;
}
}
}
pub fn apply_horizontal_tunnel(map: &mut Map, x1: i32, x2: i32, y: i32) {
for x in min(x1, x2)..=max(x1, x2) {
let idx = map.xy_idx(x, y);
if idx > 0 && idx < map.width as usize * map.height as usize {
map.tiles[idx as usize] = TileType::Floor;
}
}
}
pub fn apply_vertical_tunnel(map: &mut Map, y1: i32, y2: i32, x: i32) {
for y in min(y1, y2)..=max(y1, y2) {
let idx = map.xy_idx(x, y);
if idx > 0 && idx < map.width as usize * map.height as usize {
map.tiles[idx as usize] = TileType::Floor;
}
}
}

View File

@ -1,18 +0,0 @@
use specs::World;
use crate::{Map, Position};
use crate::map_builders::simple_map::SimpleMapBuilder;
pub mod common;
pub mod simple_map;
pub trait MapBuilder {
fn build_map(&mut self);
fn spawn_entities(&mut self, ecs: &mut World);
fn get_map(&self) -> Map;
fn get_starting_position(&self) -> Position;
}
pub fn new_random_builder(new_depth: i32) -> Box<dyn MapBuilder> {
Box::new(SimpleMapBuilder::new(new_depth))
}

View File

@ -1,90 +0,0 @@
use rltk::RandomNumberGenerator;
use specs::prelude::*;
use crate::{Map, Position, spawner, TileType};
use crate::map_builders::{common, MapBuilder};
use crate::rect::Rect;
pub struct SimpleMapBuilder {
map: Map,
starting_position: Position,
depth: i32,
rooms: Vec<Rect>,
}
impl SimpleMapBuilder {
pub fn new(new_depth: i32) -> Self {
Self {
starting_position: Position { x: 0, y: 0 },
map: Map::new(new_depth),
depth: new_depth,
rooms: Vec::new(),
}
}
fn rooms_and_corridors(&mut self) {
const MAX_ROOMS: i32 = 30;
const MIN_SIZE: i32 = 6;
const MAX_SIZE: i32 = 10;
let mut rng = RandomNumberGenerator::new();
for _ in 0..MAX_ROOMS {
let w = rng.range(MIN_SIZE, MAX_SIZE);
let h = rng.range(MIN_SIZE, MAX_SIZE);
let x = rng.roll_dice(1, self.map.width - w - 1) - 1;
let y = rng.roll_dice(1, self.map.height - h - 1) - 1;
let new_room = Rect::new(x, y, w, h);
let mut ok = true;
for other_room in self.rooms.iter() {
if new_room.intersect(other_room) {
ok = false
};
}
if ok {
common::apply_room_to_map(&mut self.map, &new_room);
if !self.rooms.is_empty() {
let (new_x, new_y) = new_room.center();
let (prev_x, prev_y) = self.rooms[self.rooms.len() - 1].center();
if rng.range(0, 2) == 1 {
common::apply_horizontal_tunnel(&mut self.map, prev_x, new_x, prev_y);
common::apply_vertical_tunnel(&mut self.map, prev_y, new_y, new_x);
} else {
common::apply_vertical_tunnel(&mut self.map, prev_y, new_y, prev_x);
common::apply_horizontal_tunnel(&mut self.map, prev_x, new_x, new_y);
}
}
self.rooms.push(new_room);
}
}
let stairs_position = self.rooms[self.rooms.len() - 1].center();
let stairs_idx = self.map.xy_idx(stairs_position.0, stairs_position.1);
self.map.tiles[stairs_idx] = TileType::DownStairs;
let start_pos = self.rooms[0].center();
self.starting_position = Position { x: start_pos.0, y: start_pos.1 }
}
}
impl MapBuilder for SimpleMapBuilder {
fn build_map(&mut self) {
self.rooms_and_corridors();
}
fn spawn_entities(&mut self, ecs: &mut World) {
for room in self.rooms.iter().skip(1) {
spawner::spawn_room(ecs, room, self.depth);
}
}
fn get_map(&self) -> Map {
self.map.clone()
}
fn get_starting_position(&self) -> Position {
self.starting_position.clone()
}
}

View File

@ -4,14 +4,9 @@ use rltk::{FontCharType, RandomNumberGenerator, RGB};
use specs::prelude::*; use specs::prelude::*;
use specs::saveload::{MarkedBuilder, SimpleMarker}; use specs::saveload::{MarkedBuilder, SimpleMarker};
use crate::{AreaOfEffect, BlocksTile, CombatStats, Confusion, Consumable, DefenseBonus, EntryTrigger, EquipmentSlot, Equippable, Hidden, HungerClock, HungerState, InflictsDamage, Item, MagicMapper, MAP_WIDTH, MAX_MONSTER, MeleePowerBonus, Monster, Name, Player, Position, ProvidesFood, ProvidesHealing, Ranged, Renderable, SerializeMe, SingleActivation, Viewshed};
use crate::random_table::RandomTable; use crate::random_table::RandomTable;
use crate::rect::Rect; use crate::rect::Rect;
use crate::{
AreaOfEffect, BlocksTile, CombatStats, Confusion, Consumable, DefenseBonus, EntryTrigger,
EquipmentSlot, Equippable, Hidden, HungerClock, HungerState, InflictsDamage, Item, MagicMapper,
MeleePowerBonus, Monster, Name, Player, Position, ProvidesFood, ProvidesHealing, Ranged,
Renderable, SerializeMe, SingleActivation, Viewshed, MAP_WIDTH, MAX_MONSTER,
};
pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
ecs.create_entity() ecs.create_entity()

View File

@ -1,10 +1,7 @@
use specs::prelude::*; use specs::prelude::*;
use crate::{EntityMoved, EntryTrigger, GameLog, Hidden, InflictsDamage, Map, Name, Position, SingleActivation, SufferDamage};
use crate::particle_system::ParticleBuilder; use crate::particle_system::ParticleBuilder;
use crate::{
EntityMoved, EntryTrigger, GameLog, Hidden, InflictsDamage, Map, Name, Position,
SingleActivation, SufferDamage,
};
pub struct TriggerSystem {} pub struct TriggerSystem {}
@ -21,24 +18,11 @@ impl<'a> System<'a> for TriggerSystem {
ReadStorage<'a, InflictsDamage>, ReadStorage<'a, InflictsDamage>,
WriteExpect<'a, ParticleBuilder>, WriteExpect<'a, ParticleBuilder>,
WriteStorage<'a, SufferDamage>, WriteStorage<'a, SufferDamage>,
WriteStorage<'a, SingleActivation>, WriteStorage<'a, SingleActivation>
); );
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
let ( let (map, mut entity_moved, position, entry_trigger, mut hidden, names, entities, mut game_log, inflicts_damage, mut particle_builder, mut inflict_damage, mut single_activation) = data;
map,
mut entity_moved,
position,
entry_trigger,
mut hidden,
names,
entities,
mut game_log,
inflicts_damage,
mut particle_builder,
mut inflict_damage,
single_activation,
) = data;
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() {
@ -57,14 +41,7 @@ impl<'a> System<'a> for TriggerSystem {
hidden.remove(*entity_id); hidden.remove(*entity_id);
if let Some(damage) = inflicts_damage.get(*entity_id) { if let Some(damage) = inflicts_damage.get(*entity_id) {
particle_builder.request( particle_builder.request(pos.x, pos.y, rltk::RGB::named(rltk::ORANGE), rltk::RGB::named(rltk::BLACK), rltk::to_cp437('‼'), 200.0);
pos.x,
pos.y,
rltk::RGB::named(rltk::ORANGE),
rltk::RGB::named(rltk::BLACK),
rltk::to_cp437('‼'),
200.0,
);
SufferDamage::new_damage(&mut inflict_damage, entity, damage.damage); SufferDamage::new_damage(&mut inflict_damage, entity, damage.damage);
} }
@ -82,3 +59,4 @@ impl<'a> System<'a> for TriggerSystem {
entity_moved.clear(); entity_moved.clear();
} }
} }

View File

@ -1,11 +1,7 @@
use rltk::{field_of_view, Point, RandomNumberGenerator}; use rltk::{field_of_view, Point, RandomNumberGenerator};
use specs::prelude::*; use specs::prelude::*;
use crate::{ use crate::{components::{Player, Position, Viewshed}, GameLog, Hidden, map::Map, Name};
components::{Player, Position, Viewshed},
map::Map,
GameLog, Hidden, Name,
};
pub struct VisibilitySystem {} pub struct VisibilitySystem {}
@ -19,21 +15,11 @@ impl<'a> System<'a> for VisibilitySystem {
WriteStorage<'a, Hidden>, WriteStorage<'a, Hidden>,
WriteExpect<'a, RandomNumberGenerator>, WriteExpect<'a, RandomNumberGenerator>,
ReadStorage<'a, Name>, ReadStorage<'a, Name>,
WriteExpect<'a, GameLog>, WriteExpect<'a, GameLog>
); );
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
let ( let (mut map, entities, mut viewshed, pos, player, mut hidden, mut rng, names, mut game_log) = data;
mut map,
entities,
mut viewshed,
pos,
player,
mut hidden,
mut rng,
names,
mut game_log,
) = data;
for (ent, viewshed, pos) in (&entities, &mut viewshed, &pos).join() { for (ent, viewshed, pos) in (&entities, &mut viewshed, &pos).join() {
if !viewshed.dirty { if !viewshed.dirty {
continue; continue;
@ -59,9 +45,7 @@ impl<'a> System<'a> for VisibilitySystem {
if let Some(_maybe_hidden) = hidden.get(*e) { if let Some(_maybe_hidden) = hidden.get(*e) {
if rng.roll_dice(1, 24) == 1 { if rng.roll_dice(1, 24) == 1 {
if let Some(name) = names.get(*e) { if let Some(name) = names.get(*e) {
game_log game_log.entries.push(format!("You spotted a {}.", &name.name));
.entries
.push(format!("You spotted a {}.", &name.name));
} }
hidden.remove(*e); hidden.remove(*e);
} }