diff --git a/raws/mobs.json b/raws/mobs.json index 4151602..69dd27e 100644 --- a/raws/mobs.json +++ b/raws/mobs.json @@ -6,7 +6,8 @@ "flags": ["BLOCKS_TILE"], "stats": { "max_hp": 8, "hp": 8, "defence": 1, "power": 1 }, "vision_range": 4, - "ai": "bystander" + "ai": "bystander", + "quips": ["Drink?"] }, { "id": "npc_townsperson", @@ -16,7 +17,7 @@ "stats": { "max_hp": 8, "hp": 8, "defence": 0, "power": 1 }, "vision_range": 4, "ai": "bystander", - "quips": [ "I don't get paid nearly enough to"] + "quips": ["You won't catch me quipping."] }, { "id": "npc_drunk", @@ -26,7 +27,7 @@ "stats": { "max_hp": 8, "hp": 8, "defence": 0, "power": 1 }, "vision_range": 4, "ai": "bystander", - "quips": [ "Hic!", "H-Hic'."] + "quips": ["Hic!", "H-Hic'."] }, { "id": "npc_fisher", @@ -36,7 +37,7 @@ "stats": { "max_hp": 8, "hp": 8, "defence": 0, "power": 1 }, "vision_range": 4, "ai": "bystander", - "quips": [ "Placeholder."] + "quips": ["Placeholder."] }, { "id": "npc_dockworker", @@ -46,7 +47,7 @@ "stats": { "max_hp": 8, "hp": 8, "defence": 1, "power": 1 }, "vision_range": 4, "ai": "bystander", - "quips": [ "Placeholder."] + "quips": ["Placeholder."] }, { "id": "npc_priest", @@ -74,7 +75,7 @@ "stats": { "max_hp": 12, "hp": 12, "defence": 3, "power": 3 }, "vision_range": 4, "ai": "bystander", - "quips": ["I don't get paid nearly enough to go down the mine."] + "quips": ["You wont catch me down the mine.", "I'm not paid enough for that."] }, { "id": "dog_little", diff --git a/resources/curses_14x16.png b/resources/curses_14x16.png new file mode 100644 index 0000000..a06abb6 Binary files /dev/null and b/resources/curses_14x16.png differ diff --git a/resources/terminal_10x16.png b/resources/terminal_10x16.png new file mode 100644 index 0000000..e40aa99 Binary files /dev/null and b/resources/terminal_10x16.png differ diff --git a/resources/vga8x16.jpg b/resources/vga8x16.jpg deleted file mode 100644 index 291a37c..0000000 Binary files a/resources/vga8x16.jpg and /dev/null differ diff --git a/resources/vga8x16.png b/resources/vga8x16.png new file mode 100644 index 0000000..913e32c Binary files /dev/null and b/resources/vga8x16.png differ diff --git a/src/bystander_ai_system.rs b/src/bystander_ai_system.rs index 1542f5a..15c5610 100644 --- a/src/bystander_ai_system.rs +++ b/src/bystander_ai_system.rs @@ -54,6 +54,7 @@ impl<'a> System<'a> for BystanderAI { (rng.roll_dice(1, quip.available.len() as i32) - 1) as usize }; gamelog::Logger::new() + .append("The") .npc_name(&name.unwrap().name) .append_n("says \"") .append_n(&quip.available[quip_index]) diff --git a/src/camera.rs b/src/camera.rs index 345b905..9f4e59e 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,14 +1,14 @@ use super::{Hidden, Map, Mind, Position, Prop, Renderable}; -use rltk::{Point, Rltk, RGB}; +use rltk::prelude::*; use specs::prelude::*; use std::ops::Mul; const SHOW_BOUNDARIES: bool = false; -pub fn get_screen_bounds(ecs: &World, _ctx: &mut Rltk) -> (i32, i32, i32, i32) { +pub fn get_screen_bounds(ecs: &World, _ctx: &mut Rltk) -> (i32, i32, i32, i32, i32, i32) { let player_pos = ecs.fetch::(); //let (x_chars, y_chars) = ctx.get_char_size(); - let (x_chars, y_chars) = (80, 43); + let (x_chars, y_chars, x_offset, y_offset) = (69, 41, 1, 10); let centre_x = (x_chars / 2) as i32; let centre_y = (y_chars / 2) as i32; @@ -18,30 +18,26 @@ pub fn get_screen_bounds(ecs: &World, _ctx: &mut Rltk) -> (i32, i32, i32, i32) { let max_x = min_x + x_chars as i32; let max_y = min_y + y_chars as i32; - (min_x, max_x, min_y, max_y) + (min_x, max_x, min_y, max_y, x_offset, y_offset) } pub fn render_camera(ecs: &World, ctx: &mut Rltk) { let map = ecs.fetch::(); - let (min_x, max_x, min_y, max_y) = get_screen_bounds(ecs, ctx); - - // Might need to -1 here? - let map_width = map.width; - let map_height = map.height; + let (min_x, max_x, min_y, max_y, x_offset, y_offset) = get_screen_bounds(ecs, ctx); // Render map let mut y = 0; for t_y in min_y..max_y { let mut x = 0; for t_x in min_x..max_x { - if t_x >= 0 && t_x < map.width && t_y >= 0 && t_y < map_height { + if t_x >= 0 && t_x < map.width && t_y >= 0 && t_y < map.height { let idx = map.xy_idx(t_x, t_y); if map.revealed_tiles[idx] { let (glyph, fg, bg) = crate::map::themes::get_tile_glyph(idx, &*map); - ctx.set(x, y, fg, bg, glyph); + ctx.set(x + x_offset, y + y_offset, fg, bg, glyph); } } else if SHOW_BOUNDARIES { - ctx.set(x, y, RGB::named(rltk::DARKSLATEGRAY), RGB::named(rltk::BLACK), rltk::to_cp437('#')); + ctx.set(x + x_offset, y + y_offset, RGB::named(DARKSLATEGRAY), RGB::named(BLACK), rltk::to_cp437('#')); } x += 1; } @@ -64,8 +60,7 @@ pub fn render_camera(ecs: &World, ctx: &mut Rltk) { let idx = map.xy_idx(pos.x, pos.y); let entity_offset_x = pos.x - min_x; let entity_offset_y = pos.y - min_y; - if entity_offset_x > 0 && entity_offset_x < map_width && entity_offset_y > 0 && entity_offset_y < map_height - { + if pos.x < max_x && pos.y < max_y && pos.x >= min_x && pos.y >= min_y { let mut draw = false; let mut fg = render.fg; let (_glyph, _fg, bg) = crate::map::themes::get_tile_glyph(idx, &*map); @@ -90,7 +85,7 @@ pub fn render_camera(ecs: &World, ctx: &mut Rltk) { } } if draw { - ctx.set(entity_offset_x, entity_offset_y, fg, bg, render.glyph); + ctx.set(entity_offset_x + x_offset, entity_offset_y + y_offset, fg, bg, render.glyph); } } } diff --git a/src/gui.rs b/src/gui.rs index ae49a94..f87563c 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -6,55 +6,81 @@ use rltk::{Rltk, VirtualKeyCode, RGB}; use specs::prelude::*; use std::collections::BTreeMap; +pub fn draw_lerping_bar( + ctx: &mut Rltk, + sx: i32, + sy: i32, + width: i32, + n: i32, + max: i32, + full_colour: RGB, + empty_colour: RGB, +) { + let percent = n as f32 / max as f32; + let fill_width = (percent * width as f32) as i32; + let bg = empty_colour.lerp(full_colour, percent); + let fg = RGB::named(rltk::BLACK); + for x in 0..width { + if x <= fill_width { + ctx.print_color(sx + x, sy, fg, bg, " "); + } else { + ctx.print_color(sx + x, sy, RGB::named(rltk::BLACK), RGB::named(rltk::BLACK), " "); + } + } + ctx.print(sx - 1, sy, "["); + let health = format!("{}/{}", n, max); + ctx.print_color(sx + 1, sy, fg, bg, health); + ctx.print(sx + width, sy, "]"); +} + pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { - ctx.draw_box_double(0, 43, 79, 7, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); + ctx.draw_hollow_box(0, 0, 70, 8, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); // Log box + ctx.draw_hollow_box(0, 9, 70, 42, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); // Camera box + ctx.draw_hollow_box(0, 52, 70, 3, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); // Stats box + ctx.draw_hollow_box(71, 0, 28, 55, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); // Side box // Render stats let combat_stats = ecs.read_storage::(); let players = ecs.read_storage::(); let hunger = ecs.read_storage::(); for (_player, stats, hunger) in (&players, &combat_stats, &hunger).join() { - let health = format!(" HP {}/{} ", stats.hp, stats.max_hp); - ctx.print_color_right(36, 43, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &health); - ctx.draw_bar_horizontal(38, 43, 34, stats.hp, stats.max_hp, RGB::named(rltk::RED), RGB::named(rltk::BLACK)); + draw_lerping_bar(ctx, 2, 53, 26, stats.hp, stats.max_hp, RGB::from_u8(0, 255, 0), RGB::from_u8(255, 0, 0)); + //ctx.draw_bar_horizontal(2, 53, 26, stats.hp, stats.max_hp, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK)); + draw_lerping_bar(ctx, 2, 54, 26, stats.hp, stats.max_hp, RGB::named(rltk::BLUE), RGB::named(rltk::BLACK)); match hunger.state { HungerState::Satiated => { - ctx.print_color_right(72, 42, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), "Satiated") + ctx.print_color_right(70, 53, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), "Satiated") } HungerState::Normal => {} HungerState::Hungry => { - ctx.print_color_right(72, 42, RGB::named(rltk::BROWN1), RGB::named(rltk::BLACK), "Hungry") + ctx.print_color_right(70, 53, RGB::named(rltk::BROWN1), RGB::named(rltk::BLACK), "Hungry") } HungerState::Weak => { - ctx.print_color_right(72, 42, RGB::named(rltk::ORANGE), RGB::named(rltk::BLACK), "Weak") + ctx.print_color_right(70, 53, RGB::named(rltk::ORANGE), RGB::named(rltk::BLACK), "Weak") } HungerState::Fainting => { - ctx.print_color_right(72, 42, RGB::named(rltk::RED), RGB::named(rltk::BLACK), "Fainting") + ctx.print_color_right(70, 53, RGB::named(rltk::RED), RGB::named(rltk::BLACK), "Fainting") } } } // Render the message log at [1, 46], descending, with 6 lines. - gamelog::print_log(&mut rltk::BACKEND_INTERNAL.lock().consoles[0].console, Point::new(1, 44), true, 6); + gamelog::print_log(&mut rltk::BACKEND_INTERNAL.lock().consoles[0].console, Point::new(1, 7), false, 7); // Render id let map = ecs.fetch::(); - let id = format!(" D{} ", map.id); - ctx.print_color_right(78, 43, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &id); + let id = format!("D{}", map.id); + ctx.print_color_right(70, 54, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &id); // Render turn ctx.print_color_right( - 78, - 50, + 64, + 54, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), - &format!(" T{} ", crate::gamelog::get_event_count("turns")), + &format!("T{}", crate::gamelog::get_event_count("turns")), ); - // Render mouse cursor - let mouse_pos = ctx.mouse_pos(); - ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::YELLOW)); - draw_tooltips(ecs, ctx); } @@ -63,7 +89,15 @@ pub fn get_input_direction( ctx: &mut Rltk, function: fn(i: i32, j: i32, ecs: &mut World) -> RunState, ) -> RunState { - ctx.print_color(1, 1, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "In what direction? [0-9]/[YUHJKLBN]"); + let (_, _, _, _, x_offset, y_offset) = camera::get_screen_bounds(ecs, ctx); + + ctx.print_color( + 1 + x_offset, + 1 + y_offset, + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + "In what direction? [0-9]/[YUHJKLBN]", + ); match ctx.key { None => return RunState::ActionWithDirection { function }, Some(key) => match key { @@ -83,7 +117,7 @@ pub fn get_input_direction( } fn draw_tooltips(ecs: &World, ctx: &mut Rltk) { - let (min_x, _max_x, min_y, _max_y) = camera::get_screen_bounds(ecs, ctx); + let (min_x, _max_x, min_y, _max_y, x_offset, y_offset) = camera::get_screen_bounds(ecs, ctx); let map = ecs.fetch::(); let names = ecs.read_storage::(); let positions = ecs.read_storage::(); @@ -91,8 +125,8 @@ fn draw_tooltips(ecs: &World, ctx: &mut Rltk) { let mouse_pos = ctx.mouse_pos(); let mut mouse_pos_adjusted = mouse_pos; - mouse_pos_adjusted.0 += min_x; - mouse_pos_adjusted.1 += min_y; + mouse_pos_adjusted.0 += min_x - x_offset; + mouse_pos_adjusted.1 += min_y - y_offset; if mouse_pos_adjusted.0 >= map.width || mouse_pos_adjusted.1 >= map.height || mouse_pos_adjusted.1 < 0 // Might need to be 1, and -1 from map height/width. @@ -120,7 +154,7 @@ fn draw_tooltips(ecs: &World, ctx: &mut Rltk) { for s in tooltip.iter() { for i in 0..2 { ctx.print_color( - arrow_pos.x - i, + arrow_pos.x + i, y, RGB::named(rltk::WHITE), RGB::named(rltk::GREY), @@ -211,7 +245,7 @@ pub fn print_options(inventory: BTreeMap<(String, String), i32>, mut y: i32, ctx pub fn show_help(ctx: &mut Rltk) -> YesNoResult { let mut x = 3; - let mut y = 3; + let mut y = 12; let height = 22; let width = 25; ctx.draw_box(x, y, width, height, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); @@ -379,12 +413,18 @@ pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Opti } pub fn ranged_target(gs: &mut State, ctx: &mut Rltk, range: i32, aoe: i32) -> (ItemMenuResult, Option) { - let (min_x, max_x, min_y, max_y) = camera::get_screen_bounds(&gs.ecs, ctx); + let (min_x, max_x, min_y, max_y, x_offset, y_offset) = camera::get_screen_bounds(&gs.ecs, ctx); let player_entity = gs.ecs.fetch::(); let player_pos = gs.ecs.fetch::(); let viewsheds = gs.ecs.read_storage::(); - ctx.print_color(1, 1, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "Targeting which tile? [mouse input]"); + ctx.print_color( + 1 + x_offset, + 1 + y_offset, + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + "Targeting which tile? [mouse input]", + ); // Highlight available cells let mut available_cells = Vec::new(); @@ -397,7 +437,7 @@ pub fn ranged_target(gs: &mut State, ctx: &mut Rltk, range: i32, aoe: i32) -> (I let screen_x = idx.x - min_x; let screen_y = idx.y - min_y; if screen_x > 0 && screen_x < (max_x - min_x) && screen_y > 0 && screen_y < (max_y - min_y) { - ctx.set_bg(screen_x, screen_y, RGB::named(rltk::BLUE)); + ctx.set_bg(screen_x + x_offset, screen_y + y_offset, RGB::named(rltk::BLUE)); available_cells.push(idx); } } @@ -426,15 +466,15 @@ pub fn ranged_target(gs: &mut State, ctx: &mut Rltk, range: i32, aoe: i32) -> (I rltk::field_of_view(Point::new(mouse_pos_adjusted.0, mouse_pos_adjusted.1), aoe, &*map); blast_tiles.retain(|p| p.x > 0 && p.x < map.width - 1 && p.y > 0 && p.y < map.height - 1); for tile in blast_tiles.iter() { - ctx.set_bg(tile.x - min_x, tile.y - min_y, RGB::named(rltk::DARKCYAN)); + ctx.set_bg(tile.x - min_x + x_offset, tile.y - min_y + y_offset, RGB::named(rltk::DARKCYAN)); } } - ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::CYAN)); + ctx.set_bg(mouse_pos.0 + x_offset, mouse_pos.1 + y_offset, RGB::named(rltk::CYAN)); if ctx.left_click { return (ItemMenuResult::Selected, Some(Point::new(mouse_pos_adjusted.0, mouse_pos_adjusted.1))); } } else { - ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::RED)); + ctx.set_bg(mouse_pos.0 + x_offset, mouse_pos.1 + y_offset, RGB::named(rltk::RED)); if ctx.left_click { return (ItemMenuResult::Cancel, None); } @@ -543,7 +583,7 @@ pub enum YesNoResult { pub fn game_over(ctx: &mut Rltk) -> YesNoResult { let mut x = 3; - let mut y = 3; + let mut y = 12; let width = 45; let height = 20; ctx.draw_box(x, y, width, height, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); diff --git a/src/main.rs b/src/main.rs index dfdf398..42aa92e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,12 +41,10 @@ mod rex_assets; extern crate lazy_static; // Embedded resources for use in wasm build -rltk::embedded_resource!(TERMINAL8X8, "../resources/terminal8x8.jpg"); -rltk::embedded_resource!(SCANLINESFS, "../resources/scanlines.fs"); -rltk::embedded_resource!(SCANLINESVS, "../resources/scanlines.vs"); +rltk::embedded_resource!(CURSES14X16, "../resources/curses_14x16.png"); //Consts -pub const SHOW_MAPGEN: bool = true; +pub const SHOW_MAPGEN: bool = false; #[derive(PartialEq, Copy, Clone)] pub enum RunState { @@ -475,22 +473,26 @@ impl GameState for State { } damage_system::delete_the_dead(&mut self.ecs); + + let _ = rltk::render_draw_buffer(ctx); } } -const DISPLAYWIDTH: i32 = 80; -const DISPLAYHEIGHT: i32 = 51; +const DISPLAYWIDTH: i32 = 100; +const DISPLAYHEIGHT: i32 = 56; fn main() -> rltk::BError { + rltk::link_resource!(CURSES14X16, "../resources/curses_14x16.png"); use rltk::RltkBuilder; - let mut context = RltkBuilder::simple(DISPLAYWIDTH, DISPLAYHEIGHT) - .unwrap() + let context = RltkBuilder::new() .with_title("rust-rl") - .with_tile_dimensions(16, 16) + .with_dimensions(DISPLAYWIDTH, DISPLAYHEIGHT) + .with_fps_cap(60.0) + .with_font("curses_14x16.png", 14, 16) + .with_tile_dimensions(14, 16) + .with_fancy_console(DISPLAYWIDTH, DISPLAYHEIGHT, "curses_14x16.png") //.with_simple_console_no_bg(DISPLAYWIDTH, DISPLAYHEIGHT, "terminal8x8.jpg") .build()?; - context.with_post_scanlines(false); - //context.screen_burn_color(RGB::named((150, 255, 255))); let mut gs = State { ecs: World::new(), diff --git a/src/map/themes.rs b/src/map/themes.rs index 32b5a0b..6a20250 100644 --- a/src/map/themes.rs +++ b/src/map/themes.rs @@ -16,7 +16,7 @@ pub fn get_tile_glyph(idx: usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) { let mut fg: RGB = RGB::new(); let mut bg: RGB; - let default_bg: RGB = RGB::from_u8(39, 67, 67); + let default_bg: RGB = RGB::from_u8(29, 50, 50); match map.tiles[idx] { TileType::Floor => { @@ -26,12 +26,12 @@ pub fn get_tile_glyph(idx: usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) { } TileType::WoodFloor => { glyph = rltk::to_cp437('.'); - bg = RGB::from_u8(48, 43, 40); + bg = RGB::from_u8(41, 30, 20); } TileType::Fence => { glyph = rltk::to_cp437('='); - fg = RGB::from_u8(99, 48, 9); - bg = RGB::from_u8(48, 43, 33); + fg = RGB::from_u8(110, 24, 0); + bg = RGB::from_u8(45, 30, 10); } TileType::Wall => { let x = idx as i32 % map.width; @@ -47,40 +47,40 @@ pub fn get_tile_glyph(idx: usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) { } TileType::Bridge => { glyph = rltk::to_cp437('.'); - bg = RGB::from_u8(59, 49, 43); + bg = RGB::from_u8(42, 48, 37); } TileType::Gravel => { glyph = rltk::to_cp437(';'); - bg = RGB::from_u8(39, 39, 54); + bg = RGB::from_u8(26, 26, 53); } TileType::Road => { glyph = rltk::to_cp437('.'); //fg = RGB::from_u8(112, 105, 94); - bg = RGB::from_u8(29, 45, 46); + bg = RGB::from_u8(8, 38, 40); } TileType::Grass => { glyph = rltk::to_cp437('"'); - bg = RGB::from_u8(39, 67, 39); + bg = RGB::from_u8(9, 65, 6); } TileType::Foliage => { glyph = rltk::to_cp437(':'); - bg = RGB::from_u8(35, 62, 36); + bg = RGB::from_u8(5, 60, 5); } TileType::HeavyFoliage => { glyph = rltk::to_cp437(';'); - bg = RGB::from_u8(32, 62, 32); + bg = RGB::from_u8(5, 55, 5); } TileType::Sand => { glyph = rltk::to_cp437('.'); - bg = RGB::from_u8(92, 92, 66); + bg = RGB::from_u8(70, 70, 21); } TileType::ShallowWater => { glyph = rltk::to_cp437('~'); - bg = RGB::from_u8(51, 63, 91); + bg = RGB::from_u8(24, 47, 99); } TileType::DeepWater => { glyph = rltk::to_cp437('~'); - bg = RGB::from_u8(36, 45, 61); + bg = RGB::from_u8(18, 33, 63); } } if map.bloodstains.contains(&idx) {