Brogue-style tile colour offsets, and main menu
This commit is contained in:
parent
d1b350cdc3
commit
7bf1c0b887
9 changed files with 256 additions and 45 deletions
BIN
resources/cave_tunnel80x50.xp
Normal file
BIN
resources/cave_tunnel80x50.xp
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 25 KiB |
83
src/gui.rs
83
src/gui.rs
|
|
@ -1,4 +1,7 @@
|
|||
use super::{gamelog::GameLog, CombatStats, InBackpack, Map, Name, Player, Point, Position, State};
|
||||
use super::{
|
||||
gamelog::GameLog, rex_assets::RexAssets, CombatStats, InBackpack, Map, Name, Player, Point, Position, RunState,
|
||||
State,
|
||||
};
|
||||
use rltk::{Rltk, VirtualKeyCode, RGB};
|
||||
use specs::prelude::*;
|
||||
|
||||
|
|
@ -201,3 +204,81 @@ pub fn drop_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub enum MainMenuSelection {
|
||||
NewGame,
|
||||
LoadGame,
|
||||
Quit,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub enum MainMenuResult {
|
||||
NoSelection { selected: MainMenuSelection },
|
||||
Selected { selected: MainMenuSelection },
|
||||
}
|
||||
|
||||
pub fn main_menu(gs: &mut State, ctx: &mut Rltk) -> MainMenuResult {
|
||||
let runstate = gs.ecs.fetch::<RunState>();
|
||||
let assets = gs.ecs.fetch::<RexAssets>();
|
||||
|
||||
ctx.render_xp_sprite(&assets.menu, 0, 0);
|
||||
|
||||
ctx.print_color(38, 21, RGB::named(rltk::GREEN), RGB::from_f32(0.11, 0.11, 0.11), "RUST-RL");
|
||||
|
||||
if let RunState::MainMenu { menu_selection: selection } = *runstate {
|
||||
if selection == MainMenuSelection::NewGame {
|
||||
ctx.print_color(34, 24, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "[");
|
||||
ctx.print_color(36, 24, RGB::named(rltk::GREEN), RGB::from_f32(0.11, 0.11, 0.11), "new game");
|
||||
ctx.print_color(45, 24, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "]");
|
||||
} else {
|
||||
ctx.print_color(36, 24, RGB::named(rltk::WHITE), RGB::from_f32(0.11, 0.11, 0.11), "new game");
|
||||
}
|
||||
if selection == MainMenuSelection::LoadGame {
|
||||
ctx.print_color(38, 26, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "[");
|
||||
ctx.print_color(40, 26, RGB::named(rltk::GREEN), RGB::from_f32(0.11, 0.11, 0.11), "load game");
|
||||
ctx.print_color(50, 26, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "]");
|
||||
} else {
|
||||
ctx.print_color(40, 26, RGB::named(rltk::WHITE), RGB::from_f32(0.11, 0.11, 0.11), "load game");
|
||||
}
|
||||
if selection == MainMenuSelection::Quit {
|
||||
ctx.print_color(34, 28, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "[");
|
||||
ctx.print_color(36, 28, RGB::named(rltk::GREEN), RGB::from_f32(0.11, 0.11, 0.11), "goodbye!");
|
||||
ctx.print_color(45, 28, RGB::named(rltk::YELLOW), RGB::from_f32(0.11, 0.11, 0.11), "]");
|
||||
} else {
|
||||
ctx.print_color(36, 28, RGB::named(rltk::WHITE), RGB::from_f32(0.11, 0.11, 0.11), "quit");
|
||||
}
|
||||
|
||||
match ctx.key {
|
||||
None => return MainMenuResult::NoSelection { selected: selection },
|
||||
Some(key) => match key {
|
||||
VirtualKeyCode::Escape | VirtualKeyCode::C => {
|
||||
return MainMenuResult::NoSelection { selected: MainMenuSelection::Quit }
|
||||
}
|
||||
VirtualKeyCode::N => return MainMenuResult::NoSelection { selected: MainMenuSelection::NewGame },
|
||||
VirtualKeyCode::L => return MainMenuResult::NoSelection { selected: MainMenuSelection::LoadGame },
|
||||
VirtualKeyCode::Up => {
|
||||
let new_selection;
|
||||
match selection {
|
||||
MainMenuSelection::NewGame => new_selection = MainMenuSelection::Quit,
|
||||
MainMenuSelection::LoadGame => new_selection = MainMenuSelection::NewGame,
|
||||
MainMenuSelection::Quit => new_selection = MainMenuSelection::LoadGame,
|
||||
}
|
||||
return MainMenuResult::NoSelection { selected: new_selection };
|
||||
}
|
||||
VirtualKeyCode::Down => {
|
||||
let new_selection;
|
||||
match selection {
|
||||
MainMenuSelection::NewGame => new_selection = MainMenuSelection::LoadGame,
|
||||
MainMenuSelection::LoadGame => new_selection = MainMenuSelection::Quit,
|
||||
MainMenuSelection::Quit => new_selection = MainMenuSelection::NewGame,
|
||||
}
|
||||
return MainMenuResult::NoSelection { selected: new_selection };
|
||||
}
|
||||
VirtualKeyCode::Return => return MainMenuResult::Selected { selected: selection },
|
||||
_ => return MainMenuResult::NoSelection { selected: selection },
|
||||
},
|
||||
}
|
||||
}
|
||||
MainMenuResult::NoSelection { selected: MainMenuSelection::NewGame }
|
||||
}
|
||||
|
|
|
|||
62
src/main.rs
62
src/main.rs
|
|
@ -1,5 +1,6 @@
|
|||
use rltk::{GameState, Point, Rltk, RGB};
|
||||
use specs::prelude::*;
|
||||
use std::ops::Add;
|
||||
|
||||
mod components;
|
||||
pub use components::*;
|
||||
|
|
@ -26,6 +27,7 @@ mod inventory_system;
|
|||
use inventory_system::*;
|
||||
mod particle_system;
|
||||
use particle_system::{ParticleBuilder, DEFAULT_PARTICLE_LIFETIME};
|
||||
mod rex_assets;
|
||||
|
||||
// Embedded resources for use in wasm build
|
||||
rltk::embedded_resource!(TERMINAL8X8, "../resources/terminal8x8.jpg");
|
||||
|
|
@ -40,6 +42,7 @@ pub enum RunState {
|
|||
MonsterTurn,
|
||||
ShowInventory,
|
||||
ShowDropItem,
|
||||
MainMenu { menu_selection: gui::MainMenuSelection },
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
|
|
@ -72,10 +75,18 @@ impl State {
|
|||
|
||||
impl GameState for State {
|
||||
fn tick(&mut self, ctx: &mut Rltk) {
|
||||
let mut new_runstate;
|
||||
{
|
||||
let runstate = self.ecs.fetch::<RunState>();
|
||||
new_runstate = *runstate;
|
||||
}
|
||||
// Clear screen
|
||||
ctx.cls();
|
||||
particle_system::cull_dead_particles(&mut self.ecs, ctx);
|
||||
|
||||
match new_runstate {
|
||||
RunState::MainMenu { .. } => {}
|
||||
_ => {
|
||||
// Draw map and ui
|
||||
draw_map(&self.ecs, ctx);
|
||||
{
|
||||
|
|
@ -87,7 +98,9 @@ impl GameState for State {
|
|||
data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order));
|
||||
for (pos, render) in data.iter() {
|
||||
let idx = map.xy_idx(pos.x, pos.y);
|
||||
let mut bg = render.bg;
|
||||
let offsets = RGB::from_u8(map.red_offset[idx], map.green_offset[idx], map.blue_offset[idx]);
|
||||
let mut bg = render.bg.add(RGB::from_u8(26, 45, 45)).add(offsets);
|
||||
//bg = bg.add(offsets);
|
||||
if map.bloodstains.contains(&idx) {
|
||||
bg = RGB::from_f32(0.4, 0., 0.);
|
||||
}
|
||||
|
|
@ -97,11 +110,7 @@ impl GameState for State {
|
|||
}
|
||||
gui::draw_ui(&self.ecs, ctx);
|
||||
}
|
||||
|
||||
let mut new_runstate;
|
||||
{
|
||||
let runstate = self.ecs.fetch::<RunState>();
|
||||
new_runstate = *runstate;
|
||||
}
|
||||
}
|
||||
|
||||
match new_runstate {
|
||||
|
|
@ -153,6 +162,28 @@ impl GameState for State {
|
|||
}
|
||||
}
|
||||
}
|
||||
RunState::MainMenu { .. } => {
|
||||
let result = gui::main_menu(self, ctx);
|
||||
match result {
|
||||
gui::MainMenuResult::NoSelection { selected } => {
|
||||
new_runstate = RunState::MainMenu { menu_selection: selected }
|
||||
}
|
||||
gui::MainMenuResult::Selected { selected } => {
|
||||
match selected {
|
||||
gui::MainMenuSelection::NewGame => new_runstate = RunState::PreRun,
|
||||
gui::MainMenuSelection::LoadGame => {
|
||||
new_runstate = RunState::PreRun;
|
||||
//saveload_system::load_game(&mut self.ecs);
|
||||
//rew_runstate = RunState::AwaitingInput;
|
||||
//saveload_system::delete_save();
|
||||
}
|
||||
gui::MainMenuSelection::Quit => {
|
||||
::std::process::exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -164,14 +195,22 @@ impl GameState for State {
|
|||
}
|
||||
}
|
||||
|
||||
const DISPLAYWIDTH: i32 = 80;
|
||||
const DISPLAYHEIGHT: i32 = 50;
|
||||
|
||||
fn main() -> rltk::BError {
|
||||
use rltk::RltkBuilder;
|
||||
let mut context = RltkBuilder::simple80x50()
|
||||
.with_tile_dimensions(16, 16)
|
||||
//.with_fitscreen(true)
|
||||
let mut context = RltkBuilder::new()
|
||||
.with_title("rust-rl")
|
||||
.with_dimensions(DISPLAYWIDTH, DISPLAYHEIGHT)
|
||||
.with_tile_dimensions(16, 16)
|
||||
.with_resource_path("resources/")
|
||||
.with_font("terminal8x8.jpg", 8, 8)
|
||||
.with_simple_console(DISPLAYWIDTH, DISPLAYHEIGHT, "terminal8x8.jpg")
|
||||
.with_simple_console_no_bg(DISPLAYWIDTH, DISPLAYHEIGHT, "terminal8x8.jpg")
|
||||
.build()?;
|
||||
context.with_post_scanlines(true);
|
||||
context.with_post_scanlines(false);
|
||||
//context.screen_burn_color(RGB::named((150, 255, 255)));
|
||||
let mut gs = State { ecs: World::new() };
|
||||
|
||||
gs.ecs.register::<Position>();
|
||||
|
|
@ -207,8 +246,9 @@ fn main() -> rltk::BError {
|
|||
gs.ecs.insert(Point::new(player_x, player_y));
|
||||
gs.ecs.insert(player_entity);
|
||||
gs.ecs.insert(gamelog::GameLog { entries: vec!["Here's your welcome message.".to_string()] });
|
||||
gs.ecs.insert(RunState::PreRun);
|
||||
gs.ecs.insert(RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame });
|
||||
gs.ecs.insert(particle_system::ParticleBuilder::new());
|
||||
gs.ecs.insert(rex_assets::RexAssets::new());
|
||||
|
||||
rltk::main_loop(context, gs)
|
||||
}
|
||||
|
|
|
|||
40
src/map.rs
40
src/map.rs
|
|
@ -3,6 +3,7 @@ use rltk::{Algorithm2D, BaseMap, Point, RandomNumberGenerator, Rltk, RGB};
|
|||
use specs::prelude::*;
|
||||
use std::cmp::{max, min};
|
||||
use std::collections::HashSet;
|
||||
use std::ops::Add;
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub enum TileType {
|
||||
|
|
@ -12,6 +13,7 @@ pub enum TileType {
|
|||
|
||||
pub const MAPWIDTH: usize = 80;
|
||||
pub const MAPHEIGHT: usize = 43;
|
||||
const MAX_OFFSET: u8 = 32;
|
||||
const MAPCOUNT: usize = MAPHEIGHT * MAPWIDTH;
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -22,6 +24,9 @@ pub struct Map {
|
|||
pub height: i32,
|
||||
pub revealed_tiles: Vec<bool>,
|
||||
pub visible_tiles: Vec<bool>,
|
||||
pub red_offset: Vec<u8>,
|
||||
pub green_offset: Vec<u8>,
|
||||
pub blue_offset: Vec<u8>,
|
||||
pub blocked: Vec<bool>,
|
||||
pub tile_content: Vec<Vec<Entity>>,
|
||||
pub bloodstains: HashSet<usize>,
|
||||
|
|
@ -89,6 +94,9 @@ impl Map {
|
|||
height: MAPHEIGHT as i32,
|
||||
revealed_tiles: vec![false; MAPCOUNT],
|
||||
visible_tiles: vec![false; MAPCOUNT],
|
||||
red_offset: vec![0; MAPCOUNT],
|
||||
green_offset: vec![0; MAPCOUNT],
|
||||
blue_offset: vec![0; MAPCOUNT],
|
||||
blocked: vec![false; MAPCOUNT],
|
||||
tile_content: vec![Vec::new(); MAPCOUNT],
|
||||
bloodstains: HashSet::new(),
|
||||
|
|
@ -100,6 +108,19 @@ impl Map {
|
|||
|
||||
let mut rng = RandomNumberGenerator::new();
|
||||
|
||||
for idx in 0..map.red_offset.len() {
|
||||
let roll = rng.roll_dice(1, MAX_OFFSET as i32);
|
||||
map.red_offset[idx] = roll as u8;
|
||||
}
|
||||
for idx in 0..map.green_offset.len() {
|
||||
let roll = rng.roll_dice(1, MAX_OFFSET as i32);
|
||||
map.green_offset[idx] = roll as u8;
|
||||
}
|
||||
for idx in 0..map.blue_offset.len() {
|
||||
let roll = rng.roll_dice(1, MAX_OFFSET as i32);
|
||||
map.blue_offset[idx] = roll as u8;
|
||||
}
|
||||
|
||||
for _i in 0..MAX_ROOMS {
|
||||
let w = rng.range(MIN_SIZE, MAX_SIZE);
|
||||
let h = rng.range(MIN_SIZE, MAX_SIZE);
|
||||
|
|
@ -198,23 +219,28 @@ pub fn draw_map(ecs: &World, ctx: &mut Rltk) {
|
|||
let mut y = 0;
|
||||
let mut x = 0;
|
||||
for (idx, tile) in map.tiles.iter().enumerate() {
|
||||
// Get our colour offsets. Credit to Brogue for the inspiration here.
|
||||
let offsets = RGB::from_u8(map.red_offset[idx], map.green_offset[idx], map.blue_offset[idx]);
|
||||
if map.revealed_tiles[idx] {
|
||||
let mut fg = offsets;
|
||||
// Right now, everything always has the same background. It's a
|
||||
// very dark green, just to distinguish it slightly from the
|
||||
// black that is tiles we've *never* seen.
|
||||
let mut bg = offsets.add(RGB::from_u8(26, 45, 45));
|
||||
let glyph;
|
||||
let mut fg;
|
||||
let mut bg = RGB::from_f32(0., 0., 0.);
|
||||
match tile {
|
||||
TileType::Floor => {
|
||||
glyph = rltk::to_cp437('.');
|
||||
fg = RGB::from_f32(0.0, 1.0, 0.5);
|
||||
fg = fg.add(RGB::from_f32(0.1, 0.8, 0.5));
|
||||
}
|
||||
TileType::Wall => {
|
||||
glyph = wall_glyph(&*map, x, y);
|
||||
fg = RGB::from_f32(0.0, 1.0, 0.0);
|
||||
fg = fg.add(RGB::from_f32(0.1, 0.8, 0.1));
|
||||
}
|
||||
}
|
||||
let mut bloody = false;
|
||||
if map.bloodstains.contains(&idx) {
|
||||
bg = RGB::from_f32(0.4, 0., 0.);
|
||||
bg = bg.add(RGB::from_f32(0.4, 0., 0.));
|
||||
bloody = true;
|
||||
}
|
||||
if !map.visible_tiles[idx] {
|
||||
|
|
@ -223,7 +249,9 @@ pub fn draw_map(ecs: &World, ctx: &mut Rltk) {
|
|||
// since desaturate is an expensive function. If this stops being the case,
|
||||
// will need to switch to using desaturate
|
||||
if bloody {
|
||||
bg = RGB::from_f32(0.4, 0.4, 0.4)
|
||||
bg = RGB::from_f32(0.4, 0.4, 0.4);
|
||||
} else {
|
||||
bg = bg.to_greyscale();
|
||||
}
|
||||
}
|
||||
ctx.set(x, y, fg, bg, glyph);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use super::{
|
||||
gamelog::GameLog, CombatStats, Item, Map, Player, Position, RunState, State, Viewshed, WantsToMelee,
|
||||
gamelog::GameLog, gui, CombatStats, Item, Map, Player, Position, RunState, State, Viewshed, WantsToMelee,
|
||||
WantsToPickupItem, MAPHEIGHT, MAPWIDTH,
|
||||
};
|
||||
use rltk::{Point, Rltk, VirtualKeyCode};
|
||||
|
|
@ -98,6 +98,7 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
|||
VirtualKeyCode::G => get_item(&mut gs.ecs),
|
||||
VirtualKeyCode::I => return RunState::ShowInventory,
|
||||
VirtualKeyCode::D => return RunState::ShowDropItem,
|
||||
VirtualKeyCode::Escape => return RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame },
|
||||
_ => {
|
||||
return RunState::AwaitingInput;
|
||||
}
|
||||
|
|
@ -105,3 +106,48 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
|||
}
|
||||
RunState::PlayerTurn
|
||||
}
|
||||
|
||||
/* Playing around with autoexplore, without having read how to do it.
|
||||
pub fn auto_explore(ecs: &mut World) {
|
||||
let player_pos = ecs.fetch::<Point>();
|
||||
let positions = ecs.read_storage::<Position>();
|
||||
let entities = ecs.entities();
|
||||
let map = ecs.fetch::<Map>();
|
||||
let mut viewsheds = ecs.write_storage::<Viewshed>();
|
||||
|
||||
let mut unexplored_tiles: Vec<usize> = vec![];
|
||||
for (idx, _tile) in map.tiles.iter().enumerate() {
|
||||
if !map.revealed_tiles[idx] {
|
||||
unexplored_tiles.push(idx);
|
||||
}
|
||||
}
|
||||
let mut unexplored_tile = (0, 0.0f32);
|
||||
|
||||
let flow_map = DijkstraMap::new_empty(MAPWIDTH, MAPHEIGHT, 200.0);
|
||||
|
||||
DijkstraMap::build(&mut flow_map, &unexplored_tiles, &map);
|
||||
for (i, tile) in map.tiles.iter().enumerate() {
|
||||
if *tile == TileType::Floor {
|
||||
let distance_to_start = flow_map.map[i];
|
||||
|
||||
if distance_to_start > unexplored_tile.1 {
|
||||
unexplored_tile.0 = i;
|
||||
unexplored_tile.1 = distance_to_start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let path = rltk::a_star_search(map.xy_idx(player_pos.x, player_pos.y), unexplored_tile.0, &*map);
|
||||
if path.success && path.steps.len() > 1 {
|
||||
let mut idx = map.xy_idx(player_pos.x, player_pos.y);
|
||||
map.blocked[idx] = false;
|
||||
player_pos.x = (path.steps[1] as i32) % map.width;
|
||||
player_pos.y = (path.steps[1] as i32) / map.width;
|
||||
idx = map.xy_idx(player_pos.x, player_pos.y);
|
||||
map.blocked[idx] = true;
|
||||
for (ent, viewshed, pos) in (&entities, &mut viewsheds, &positions).join() {
|
||||
viewshed.dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
16
src/rex_assets.rs
Normal file
16
src/rex_assets.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
use rltk::rex::XpFile;
|
||||
|
||||
rltk::embedded_resource!(CAVE_TUNNEL, "../resources/cave_tunnel80x50.xp");
|
||||
|
||||
pub struct RexAssets {
|
||||
pub menu: XpFile,
|
||||
}
|
||||
|
||||
impl RexAssets {
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> RexAssets {
|
||||
rltk::link_resource!(CAVE_TUNNEL, "../resources/cave_tunnel80x50.xp");
|
||||
|
||||
RexAssets { menu: XpFile::from_resource("../resources/cave_tunnel80x50.xp").unwrap() }
|
||||
}
|
||||
}
|
||||
|
|
@ -801,16 +801,16 @@ function __wbg_get_imports() {
|
|||
const ret = wasm.memory;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper194 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 31, __wbg_adapter_20);
|
||||
imports.wbg.__wbindgen_closure_wrapper187 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 7, __wbg_adapter_20);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper525 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 136, __wbg_adapter_23);
|
||||
imports.wbg.__wbindgen_closure_wrapper648 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 158, __wbg_adapter_23);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper527 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 136, __wbg_adapter_23);
|
||||
imports.wbg.__wbindgen_closure_wrapper650 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 158, __wbg_adapter_23);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue