Add bsp interior

This commit is contained in:
Kasper Juul Hermansen 2022-01-30 14:24:56 +01:00
parent c1893aec74
commit 5a00ebf1f1
Signed by: kjuulh
GPG Key ID: 0F95C140730F2F23
11 changed files with 317 additions and 38 deletions

8
.idea/.gitignore vendored
View File

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

99
.idea/workspace.xml Normal file
View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="CMakeSettings">
<configurations>
<configuration PROFILE_NAME="Debug" ENABLED="true" CONFIG_NAME="Debug" />
</configurations>
</component>
<component name="CargoProjects">
<cargoProject FILE="$PROJECT_DIR$/Cargo.toml" />
</component>
<component name="ChangeListManager">
<list default="true" id="a9f5cad0-d253-42e8-a38a-89dfec417dbc" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/src/map_builders/bsp/mod.rs" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/map_builders/bsp_interior.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.gitignore" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/src/components.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/components.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/map_builders/bsp_dungeon.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/map_builders/bsp_dungeon.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/map_builders/common.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/map_builders/common.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/map_builders/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/map_builders/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/map_builders/simple_map.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/map_builders/simple_map.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/spawner.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/spawner.rs" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ClangdSettings">
<option name="formatViaClangd" value="false" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GitSEFilterConfiguration">
<file-type-list>
<filtered-out-file-type name="LOCAL_BRANCH" />
<filtered-out-file-type name="REMOTE_BRANCH" />
<filtered-out-file-type name="TAG" />
<filtered-out-file-type name="COMMIT_BY_MESSAGE" />
</file-type-list>
</component>
<component name="MacroExpansionManager">
<option name="directoryName" value="7zted9t8" />
</component>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProjectId" id="24PyiDwZbQQQ0gzNvMewyQ4Zryi" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="RunOnceActivity.cidr.known.project.marker" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="cf.first.check.clang-format" value="false" />
<property name="cidr.known.project.marker" value="true" />
<property name="node.js.detected.package.eslint" value="true" />
<property name="node.js.detected.package.tslint" value="true" />
<property name="node.js.selected.package.eslint" value="(autodetect)" />
<property name="node.js.selected.package.tslint" value="(autodetect)" />
<property name="org.rust.cargo.project.model.PROJECT_DISCOVERY" value="true" />
<property name="settings.editor.selected.configurable" value="reference.settingsdialog.project.grazie" />
</component>
<component name="RustProjectSettings">
<option name="toolchainHomeDirectory" value="$USER_HOME$/.cargo/bin" />
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="a9f5cad0-d253-42e8-a38a-89dfec417dbc" name="Changes" comment="" />
<created>1643546643557</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1643546643557</updated>
<workItem from="1643546645546" duration="2136000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
</component>
</project>

File diff suppressed because one or more lines are too long

View File

@ -11,6 +11,23 @@ pub struct Position {
pub y: i32, pub y: i32,
} }
impl Position {
pub fn new(x: i32, y: i32) -> Self {
Self {
x, y
}
}
}
impl Default for Position {
fn default() -> Self {
Self {
x: 0,
y: 0
}
}
}
#[derive(Component, ConvertSaveload, Clone)] #[derive(Component, ConvertSaveload, Clone)]
pub struct Renderable { pub struct Renderable {
pub glyph: rltk::FontCharType, pub glyph: rltk::FontCharType,

View File

@ -0,0 +1,22 @@
use crate::{Map, TileType};
#[inline(always)]
pub fn draw_corridor(map: &mut Map, x1: i32, y1: i32, x2: i32, y2: i32) {
let mut x = x1;
let mut y = y1;
while x != x2 || y != y2 {
if x < x2 {
x += 1;
} else if x > x2 {
x -= 1;
} else if y < y2 {
y += 1;
} else if y > y2 {
y -= 1;
}
let idx = map.xy_idx(x, y);
map.tiles[idx] = TileType::Floor;
}
}

View File

@ -1,7 +1,7 @@
use rltk::{Point, RandomNumberGenerator}; use rltk::{Point, RandomNumberGenerator};
use crate::map_builders::common::apply_room_to_map; use crate::map_builders::common::apply_room_to_map;
use crate::map_builders::MapBuilder; use crate::map_builders::{bsp, MapBuilder};
use crate::rect::Rect; use crate::rect::Rect;
use crate::{spawner, Map, Position, TileType, World, SHOW_MAPGEN_VISUALIZER}; use crate::{spawner, Map, Position, TileType, World, SHOW_MAPGEN_VISUALIZER};
@ -175,23 +175,7 @@ impl BspDungeonBuilder {
can_build can_build
} }
fn draw_corridor(&mut self, x1: i32, y1: i32, x2: i32, y2: i32) { fn draw_corridor(&mut self, x1: i32, y1: i32, x2: i32, y2: i32) {
let mut x = x1; bsp::draw_corridor(&mut self.map, x1, y1, x2, y2);
let mut y = y1;
while x != x2 || y != y2 {
if x < x2 {
x += 1;
} else if x > x2 {
x -= 1;
} else if y < y2 {
y += 1;
} else if y > y2 {
y -= 1;
}
let idx = self.map.xy_idx(x, y);
self.map.tiles[idx] = TileType::Floor;
}
} }
} }
@ -202,7 +186,7 @@ impl MapBuilder for BspDungeonBuilder {
fn spawn_entities(&mut self, ecs: &mut World) { fn spawn_entities(&mut self, ecs: &mut World) {
for room in self.rooms.iter().skip(1) { for room in self.rooms.iter().skip(1) {
spawner::spawn_room(ecs, room, self.depth); spawner::spawn_room(ecs, room, self.depth, &self.map);
} }
} }

View File

@ -0,0 +1,157 @@
use rltk::RandomNumberGenerator;
use crate::{Map, Position, SHOW_MAPGEN_VISUALIZER, spawner, TileType, World};
use crate::map_builders::common::reveal_all;
use crate::map_builders::{bsp, MapBuilder};
use crate::rect::Rect;
const MIN_ROOM_SIZE : i32 = 8;
pub struct BspInteriorBuilder {
map: Map,
starting_position: Position,
depth: i32,
rooms: Vec<Rect>,
history: Vec<Map>,
rects: Vec<Rect>,
}
impl BspInteriorBuilder {
pub fn new(new_depth: i32) -> Self{
Self {
map: Map::new(new_depth),
rects: Vec::new(),
history:Vec::new(),
depth: new_depth,
rooms: Vec::new(),
starting_position: Position::default()
}
}
fn build(&mut self) {
let mut rng = RandomNumberGenerator::new();
self.place_rooms(&mut rng);
self.place_corridors(&mut rng);
self.place_stairs();
self.place_start()
}
fn place_start(&mut self) {
let start = self.rooms[0].center();
self.starting_position = Position::new(start.0, start.1)
}
fn place_stairs(&mut self) {
let stairs = self.rooms[self.rooms.len() - 1].center();
let stairs_idx = self.map.xy_idx(stairs.0, stairs.1);
self.map.tiles[stairs_idx] = TileType::DownStairs;
}
fn place_corridors(&mut self, rng: &mut RandomNumberGenerator) {
for i in 0..self.rooms.len() - 1 {
let room = self.rooms[i];
let next_room = self.rooms[i + 1];
let start_x = room.x1 + (rng.roll_dice(1, i32::abs(room.x1 - room.x2)) - 1);
let start_y = room.y1 + (rng.roll_dice(1, i32::abs(room.y1 - room.y2)) - 1);
let end_x = next_room.x1 + (rng.roll_dice(1, i32::abs(next_room.x1 - next_room.x2)) - 1);
let end_y = next_room.y1 + (rng.roll_dice(1, i32::abs(next_room.y1 - next_room.y2)) - 1);
self.draw_corridor(start_x, start_y, end_x, end_y);
self.take_snapshot();
}
}
fn place_rooms(&mut self, mut rng: &mut RandomNumberGenerator) {
self.rects.clear();
self.rects.push(Rect::new(1, 1, self.map.width - 2, self.map.height - 2));
let first_room = self.rects[0];
self.add_subrects(first_room, &mut rng);
let rooms = self.rects.clone();
for r in rooms.iter() {
let room = *r;
self.rooms.push(room);
for y in room.y1..room.y2 {
for x in room.x1..room.x2 {
let idx = self.map.xy_idx(x, y);
if idx > 0 && idx < ((self.map.width * self.map.height) - 1) as usize {
self.map.tiles[idx] = TileType::Floor;
}
}
}
self.take_snapshot();
}
}
fn add_subrects(&mut self, rect: Rect, rng: &mut RandomNumberGenerator) {
if !self.rects.is_empty() {
self.rects.remove(self.rects.len() - 1);
}
let width = rect.x2 - rect.x1;
let height = rect.y2 - rect.y1;
let half_width = width / 2;
let half_height = height / 2;
let split = rng.roll_dice(1, 4);
if split <= 2 {
let h1 = Rect::new(rect.x1, rect.y1, half_width -1, height);
self.rects.push(h1);
if half_width > MIN_ROOM_SIZE {
self.add_subrects(h1, rng);
}
let h2 = Rect::new(rect.x1 + half_width, rect.y1, half_width, height);
self.rects.push(h2);
if half_width > MIN_ROOM_SIZE {
self.add_subrects(h2, rng);
}
}else {
let v1 = Rect::new(rect.x1, rect.y1, width , half_height - 1);
self.rects.push(v1);
if half_height > MIN_ROOM_SIZE {
self.add_subrects(v1, rng);
}
let v2 = Rect::new(rect.x1 , rect.y1 + half_height, width, half_height);
self.rects.push(v2);
if half_height > MIN_ROOM_SIZE {
self.add_subrects(v2, rng);
}
}
}
fn draw_corridor(&mut self, x1: i32, y1: i32, x2: i32, y2: i32) {
bsp::draw_corridor(&mut self.map, x1, y1, x2, y2);
}
}
impl MapBuilder for BspInteriorBuilder {
fn build_map(&mut self) {
self.build();
}
fn spawn_entities(&mut self, ecs: &mut World) {
for room in self.rooms.iter().skip(1) {
spawner::spawn_room(ecs, room, self.depth, &self.map);
}
}
fn get_map(&self) -> Map {
self.map.clone()
}
fn get_starting_position(&self) -> Position {
self.starting_position.clone()
}
fn get_snapshot_history(&self) -> Vec<Map> {
self.history.clone()
}
fn take_snapshot(&mut self) {
if SHOW_MAPGEN_VISUALIZER {
self.history.push(reveal_all(&self.map));
}
}
}

View File

@ -29,3 +29,11 @@ pub fn apply_vertical_tunnel(map: &mut Map, y1: i32, y2: i32, x: i32) {
} }
} }
} }
pub fn reveal_all(map: &Map) -> Map {
let mut snapshot = map.clone();
for v in snapshot.revealed_tiles.iter_mut() {
*v = true;
}
snapshot
}

View File

@ -3,8 +3,11 @@ use specs::World;
use crate::map_builders::bsp_dungeon::BspDungeonBuilder; use crate::map_builders::bsp_dungeon::BspDungeonBuilder;
use crate::map_builders::simple_map::SimpleMapBuilder; use crate::map_builders::simple_map::SimpleMapBuilder;
use crate::{Map, Position}; use crate::{Map, Position};
use crate::map_builders::bsp_interior::BspInteriorBuilder;
mod bsp;
mod bsp_dungeon; mod bsp_dungeon;
mod bsp_interior;
mod common; mod common;
mod simple_map; mod simple_map;
@ -19,8 +22,10 @@ pub trait MapBuilder {
pub fn new_random_builder(new_depth: i32) -> Box<dyn MapBuilder> { pub fn new_random_builder(new_depth: i32) -> Box<dyn MapBuilder> {
let mut rng = rltk::RandomNumberGenerator::new(); let mut rng = rltk::RandomNumberGenerator::new();
let builder_choice = rng.roll_dice(1, 2); //let builder_choice = rng.roll_dice(1, 3);
let builder_choice = 2;
match builder_choice { match builder_choice {
2 => Box::new(BspInteriorBuilder::new(new_depth)),
1 => Box::new(SimpleMapBuilder::new(new_depth)), 1 => Box::new(SimpleMapBuilder::new(new_depth)),
_ => Box::new(BspDungeonBuilder::new(new_depth)), _ => Box::new(BspDungeonBuilder::new(new_depth)),
} }

View File

@ -83,7 +83,7 @@ impl MapBuilder for SimpleMapBuilder {
fn spawn_entities(&mut self, ecs: &mut World) { fn spawn_entities(&mut self, ecs: &mut World) {
for room in self.rooms.iter().skip(1) { for room in self.rooms.iter().skip(1) {
spawner::spawn_room(ecs, room, self.depth); spawner::spawn_room(ecs, room, self.depth, &self.map);
} }
} }

View File

@ -6,12 +6,7 @@ use specs::saveload::{MarkedBuilder, SimpleMarker};
use crate::random_table::RandomTable; use crate::random_table::RandomTable;
use crate::rect::Rect; use crate::rect::Rect;
use crate::{ 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, Map, TileType};
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()
@ -86,7 +81,7 @@ fn monster<S: ToString>(ecs: &mut World, x: i32, y: i32, glyph: FontCharType, na
} }
#[allow(clippy::map_entry)] #[allow(clippy::map_entry)]
pub fn spawn_room(ecs: &mut World, room: &Rect, map_depth: i32) { pub fn spawn_room(ecs: &mut World, room: &Rect, map_depth: i32, map: &Map) {
let spawn_table = room_table(map_depth); let spawn_table = room_table(map_depth);
let mut spawn_points: HashMap<usize, String> = HashMap::new(); let mut spawn_points: HashMap<usize, String> = HashMap::new();
@ -101,7 +96,7 @@ pub fn spawn_room(ecs: &mut World, room: &Rect, map_depth: i32) {
let x = (room.x1 + rng.roll_dice(1, i32::abs(room.x2 - room.x1))) as usize; let x = (room.x1 + rng.roll_dice(1, i32::abs(room.x2 - room.x1))) as usize;
let y = (room.y1 + rng.roll_dice(1, i32::abs(room.y2 - room.y1))) as usize; let y = (room.y1 + rng.roll_dice(1, i32::abs(room.y2 - room.y1))) as usize;
let idx = (y * MAP_WIDTH) + x; let idx = (y * MAP_WIDTH) + x;
if !spawn_points.contains_key(&idx) { if !spawn_points.contains_key(&idx) && map.tiles[idx] != TileType::Wall {
spawn_points.insert(idx, spawn_table.roll(&mut rng)); spawn_points.insert(idx, spawn_table.roll(&mut rng));
added = true; added = true;
} else { } else {