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,
inflicts_damage,
_ranged,
map,
mut map,
mut suffer_damage,
aoe,
mut confused,

View File

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

View File

@ -1,6 +1,7 @@
use std::cmp::{max, min};
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 specs::prelude::*;
@ -21,6 +22,7 @@ pub const MAX_MONSTER: i32 = 4;
#[derive(Default, Serialize, Deserialize, Clone)]
pub struct Map {
pub tiles: Vec<TileType>,
pub rooms: Vec<Rect>,
pub width: i32,
pub height: i32,
pub revealed_tiles: Vec<bool>,
@ -39,6 +41,33 @@ impl Map {
(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 {
if x < 1 || x > self.width - 1 || y < 1 || y > self.height - 1 {
return false;
@ -59,9 +88,10 @@ impl Map {
}
}
pub fn new(new_depth: i32) -> Map {
Map {
pub fn new_map_rooms_and_corridors(new_depth: i32) -> Map {
let mut map = Map {
tiles: vec![TileType::Wall; MAP_COUNT],
rooms: Vec::new(),
width: MAP_WIDTH as i32,
height: MAP_HEIGHT as i32,
revealed_tiles: vec![false; MAP_COUNT],
@ -70,7 +100,50 @@ impl Map {
tile_content: vec![Vec::new(); MAP_COUNT],
depth: new_depth,
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::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::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 {
ecs.create_entity()

View File

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

View File

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