sprites for entities, with text glyph fallback

This commit is contained in:
Llywelwyn 2023-09-26 17:23:25 +01:00
parent 2c4b4ca143
commit d6ba6c628c
17 changed files with 1486 additions and 1397 deletions

View file

@ -168,7 +168,7 @@ pub fn render_camera(ecs: &World, ctx: &mut BTerm) {
}
}
if draw {
if let Some(sprite) = render.sprite {
/* if let Some(sprite) = render.sprite {
ctx.set_active_console(0);
ctx.add_sprite(
Rect::with_size(
@ -182,7 +182,7 @@ pub fn render_camera(ecs: &World, ctx: &mut BTerm) {
sprite
);
ctx.set_active_console(ENTITY_LAYER);
} else {
} else */ {
ctx.set(
entity_offset_x + bounds.x_offset,
entity_offset_y + bounds.y_offset,

View file

@ -41,7 +41,8 @@ pub struct OtherLevelPosition {
#[derive(Component, ConvertSaveload, Clone)]
pub struct Renderable {
pub glyph: FontCharType,
pub sprite: Option<usize>,
pub sprite: Option<String>,
pub colour_sprite: bool,
pub fg: RGB,
pub bg: RGB,
pub render_order: i32,

View file

@ -18,12 +18,12 @@ pub fn render(draw: bool, gfx: &mut Graphics, font: &notan::draw::Font) {
}
}
/// Render with specific params.
/// Render with specificied params.
pub fn render_log(gfx: &mut Graphics, font: &notan::draw::Font, pos: &(f32, f32), width: f32) {
let mut text = gfx.create_text();
let log = LOG.lock().unwrap();
let latest: Vec<_> = log.iter().rev().take(5).collect();
let mut init = false;
let mut initialised = false;
let mut y = pos.1;
for (_, entries) in latest {
let mut written_on_line = false;
@ -37,14 +37,14 @@ pub fn render_log(gfx: &mut Graphics, font: &notan::draw::Font, pos: &(f32, f32)
.color(Color::from_rgb(frag.colour.r, frag.colour.g, frag.colour.b))
.v_align_bottom();
written_on_line = true;
init = true;
initialised = true;
} else {
text.chain(&frag.text)
.color(Color::from_rgb(frag.colour.r, frag.colour.g, frag.colour.b))
.size(FONTSIZE);
}
}
if init {
if initialised {
y = text.last_bounds().min_y();
}
}

View file

@ -275,6 +275,7 @@ pub fn setup_player_ancestry(ecs: &mut World, ancestry: Ancestry) {
.insert(*player, Renderable {
glyph: to_cp437(DWARF_GLYPH),
sprite: None, // TODO: Dwarf sprite
colour_sprite: true,
fg: RGB::named(DWARF_COLOUR),
bg: RGB::named(BLACK),
render_order: 0,
@ -287,6 +288,7 @@ pub fn setup_player_ancestry(ecs: &mut World, ancestry: Ancestry) {
.insert(*player, Renderable {
glyph: to_cp437(ELF_GLYPH),
sprite: None, // TODO: Elf sprite
colour_sprite: true,
fg: RGB::named(ELF_COLOUR),
bg: RGB::named(BLACK),
render_order: 0,
@ -313,6 +315,7 @@ pub fn setup_player_ancestry(ecs: &mut World, ancestry: Ancestry) {
.insert(*player, Renderable {
glyph: to_cp437(CATFOLK_GLYPH),
sprite: None, // TODO: Catfolk sprite
colour_sprite: true,
fg: RGB::named(CATFOLK_COLOUR),
bg: RGB::named(BLACK),
render_order: 0,

View file

@ -253,34 +253,82 @@ fn draw_camera(
}
// TODO: Use sprites here, not text drawing. Put bitmap font into atlas.
let renderable = renderables.get(entry.1.e).unwrap();
draw.text(
&font,
&format!("{}", bracket_lib::terminal::to_char(renderable.glyph as u8))
)
.position(
((entry.0.x as f32) + 0.5) * TILESIZE,
((entry.0.y as f32) + 0.5) * TILESIZE
if let Some(sprite_id) = &renderable.sprite {
let sprite = if let Some(sprite) = atlas.get(sprite_id) {
sprite
} else {
panic!("No entity sprite found for ID: {}", sprite_id);
};
draw.image(sprite)
.position((entry.0.x as f32) * TILESIZE, (entry.0.y as f32) * TILESIZE)
.color(
if renderable.colour_sprite {
Color::from_rgb(
renderable.fg.r,
renderable.fg.g,
renderable.fg.b
)
} else {
Color::WHITE
}
);
} else {
// Fallback to drawing text.
draw.text(
&font,
&format!("{}", bracket_lib::terminal::to_char(renderable.glyph as u8))
)
.color(Color::from_rgb(renderable.fg.r, renderable.fg.g, renderable.fg.b))
.size(FONTSIZE)
.h_align_center()
.v_align_middle();
// Draw entity
.position(
((entry.0.x as f32) + 0.5) * TILESIZE,
((entry.0.y as f32) + 0.5) * TILESIZE
)
.color(
Color::from_rgb(renderable.fg.r, renderable.fg.g, renderable.fg.b)
)
.size(FONTSIZE)
.h_align_center()
.v_align_middle();
}
}
DrawType::VisibleAndRemember => {
// TODO: PUT THIS INTO A FUNCTION!
let renderable = renderables.get(entry.1.e).unwrap();
draw.text(
&font,
&format!("{}", bracket_lib::terminal::to_char(renderable.glyph as u8))
)
.position(
((entry.0.x as f32) + 0.5) * TILESIZE,
((entry.0.y as f32) + 0.5) * TILESIZE
if let Some(sprite_id) = &renderable.sprite {
let sprite = if let Some(sprite) = atlas.get(sprite_id) {
sprite
} else {
panic!("No entity sprite found for ID: {}", sprite_id);
};
draw.image(sprite)
.position((entry.0.x as f32) * TILESIZE, (entry.0.y as f32) * TILESIZE)
.color(
if renderable.colour_sprite {
Color::from_rgb(
renderable.fg.r,
renderable.fg.g,
renderable.fg.b
)
} else {
Color::WHITE
}
);
} else {
// Fallback to drawing text.
draw.text(
&font,
&format!("{}", bracket_lib::terminal::to_char(renderable.glyph as u8))
)
.color(Color::from_rgb(renderable.fg.r, renderable.fg.g, renderable.fg.b))
.size(FONTSIZE)
.h_align_center()
.v_align_middle();
.position(
((entry.0.x as f32) + 0.5) * TILESIZE,
((entry.0.y as f32) + 0.5) * TILESIZE
)
.color(
Color::from_rgb(renderable.fg.r, renderable.fg.g, renderable.fg.b)
)
.size(FONTSIZE)
.h_align_center()
.v_align_middle();
}
// TODO: Update map memory.
}
_ => {}
@ -318,7 +366,12 @@ fn render_map_in_view(
&*map,
Some(*ecs.fetch::<Point>())
);
draw.image(atlas.get(id).unwrap())
let sprite = if let Some(sprite) = atlas.get(id) {
sprite
} else {
panic!("No sprite found for ID: {}", id);
};
draw.image(sprite)
.position(
((x + bounds.x_offset) as f32) * TILESIZE,
((y + bounds.y_offset) as f32) * TILESIZE

View file

@ -7,14 +7,11 @@ use std::ops::{ Add, Mul };
use notan::prelude::*;
pub fn get_sprite_for_id(idx: usize, map: &Map, other_pos: Option<Point>) -> (&str, Color) {
let x = (idx as i32) % map.width;
let y = (idx as i32) / map.width;
let sprite = map.tiles[idx].sprite();
/*let base = match tile {
TileType::Wall => wall_sprite(tile.sprite(), map, x, y),
_ => tile.sprite(),
let f = map.colour_offset[idx].0.0; // Using offset as a source of random.
let sprite = match map.tiles[idx] {
TileType::Wall => map.tiles[idx].sprite(check_if_base(idx, map), f),
_ => map.tiles[idx].sprite(false, f),
};
let sprite_id = pick_variant(base, tile.variants(), idx, map);*/
let tint = if !map.visible_tiles[idx] {
Color::from_rgb(0.75, 0.75, 0.75)
} else {
@ -159,18 +156,13 @@ fn is_revealed_and_wall(map: &Map, x: i32, y: i32, debug: Option<bool>) -> bool
(if debug.is_none() { map.revealed_tiles[idx] } else { true })
}
fn wall_sprite(id: usize, map: &Map, x: i32, y: i32) -> usize {
if y > map.height - (2 as i32) {
return id;
}
fn check_if_base(idx: usize, map: &Map) -> bool {
let x = (idx as i32) % map.width;
let y = (idx as i32) / map.width;
if is_revealed_and_wall(map, x, y + 1, None) {
return id + 6;
return false;
}
return id;
}
fn pick_variant(base: usize, variants: usize, idx: usize, map: &Map) -> usize {
return base + ((map.colour_offset[idx].0.0 * (variants as f32)) as usize);
return true;
}
fn wall_glyph(map: &Map, x: i32, y: i32, debug: Option<bool>) -> FontCharType {

View file

@ -1,5 +1,5 @@
use serde::{ Deserialize, Serialize };
use crate::consts::sprites::*;
use bracket_lib::random::RandomNumberGenerator;
#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize, Debug)]
pub enum TileType {
@ -30,52 +30,84 @@ pub enum TileType {
}
impl TileType {
pub fn sprite(&self) -> &str {
match self {
TileType::ImpassableMountain => "statue_warrior",
TileType::Wall => "wall_cave_h_a",
TileType::DeepWater => "water",
TileType::Fence => "wall_cave_h_a",
TileType::Bars => "wall_cave_h_a",
TileType::Floor => "floor_cobble_a",
TileType::WoodFloor => "floor_wood_a",
TileType::Gravel => "floor_cobble_b",
TileType::Road => "floor_cobble_c",
TileType::Grass => "floor_grass_a",
TileType::Foliage => "floor_grass_b",
TileType::HeavyFoliage => "floor_grass_c",
TileType::Sand => "floor_cobble_c",
TileType::ShallowWater => "water",
TileType::Bridge => "floor_cobble_a",
TileType::DownStair => "wall_cave_stair_down",
TileType::UpStair => "wall_cave_stair_up",
TileType::ToLocal(_) => "wall_crypt_stair_down",
TileType::ToOvermap(_) => "wall_crypt_stair_up",
pub fn sprite(&self, base: bool, float: f32) -> &str {
if base {
return self.h(float);
}
return self.v(float);
}
pub fn variants(&self) -> usize {
match self {
TileType::ImpassableMountain => 1,
TileType::Wall => 4,
TileType::DeepWater => 2,
TileType::Fence => 1,
TileType::Bars => 1,
TileType::Floor => 6,
TileType::WoodFloor => 3,
TileType::Gravel => 1,
TileType::Road => 4,
TileType::Grass => 6,
TileType::Foliage => 1,
TileType::HeavyFoliage => 1,
TileType::Sand => 1,
TileType::ShallowWater => 2,
TileType::Bridge => 1,
TileType::DownStair => 1,
TileType::UpStair => 1,
TileType::ToLocal(_) => 1,
TileType::ToOvermap(_) => 1,
}
fn h(&self, float: f32) -> &str {
let options = match self {
TileType::Wall =>
vec![
"wall_cave_h_a",
"wall_cave_h_b",
"wall_cave_h_c",
"wall_cave_h_d",
"wall_cave_h_crack"
],
_ => unreachable!("Tried to get a h (base) sprite for a non-wall tile."),
};
return options[(float * (options.len() as f32)) as usize];
}
fn v(&self, float: f32) -> &str {
let options = match self {
TileType::ImpassableMountain => vec!["statue_warrior"],
TileType::Wall =>
vec![
"wall_cave_v_a",
"wall_cave_v_b",
"wall_cave_v_c",
"wall_cave_v_d",
"wall_cave_v_crack"
],
TileType::DeepWater => vec!["water", "water_a1", "water_a2"],
TileType::Fence => vec!["wall_cave_h_a"],
TileType::Bars => vec!["wall_cave_h_a"],
TileType::Floor =>
vec![
"floor_cobble_a",
"floor_cobble_b",
"floor_cobble_c",
"floor_cobble_d",
"floor_cobble_e",
"floor_cobble_f"
],
TileType::WoodFloor =>
vec!["floor_wood_a", "floor_wood_b", "floor_wood_c", "floor_wood_d"],
TileType::Gravel => vec!["floor_cobble_b"],
TileType::Road =>
vec![
"floor_tile_a",
"floor_tile_b",
"floor_tile_c",
"floor_tile_d",
"floor_mossy_a",
"floor_mossy_b",
"floor_mossy_c",
"floor_mossy_d",
"floor_mossy_e"
],
TileType::Grass =>
vec![
"floor_grass_a",
"floor_grass_b",
"floor_grass_c",
"floor_grass_d",
"floor_grass_e",
"floor_grass_f"
],
TileType::Foliage => vec!["floor_grass_b"],
TileType::HeavyFoliage => vec!["floor_grass_c"],
TileType::Sand => vec!["floor_cobble_c"],
TileType::ShallowWater => vec!["water"],
TileType::Bridge => vec!["floor_cobble_a"],
TileType::DownStair => vec!["wall_cave_stair_down"],
TileType::UpStair => vec!["wall_cave_stair_up"],
TileType::ToLocal(_) => vec!["wall_crypt_stair_down"],
TileType::ToOvermap(_) => vec!["wall_crypt_stair_up"],
};
return options[(float * (options.len() as f32)) as usize];
}
}

View file

@ -83,6 +83,7 @@ fn create_delayed_particles(ecs: &mut World, ctx: &App) {
renderables
.insert(p, Renderable {
sprite: None, // TODO: Particle sprite
colour_sprite: false,
fg: handled.fg,
bg: handled.bg,
glyph: handled.glyph,
@ -309,6 +310,7 @@ impl<'a> System<'a> for ParticleSpawnSystem {
renderables
.insert(p, Renderable {
sprite: None, // TODO: Particle sprite
colour_sprite: false,
fg: new_particle.fg,
bg: new_particle.bg,
glyph: new_particle.glyph,

View file

@ -136,7 +136,7 @@ pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState {
let mut renderables = ecs.write_storage::<Renderable>();
let render_data = renderables.get_mut(potential_target).unwrap();
render_data.glyph = to_cp437('+'); // Nethack open door, maybe just use '/' instead.
render_data.sprite = Some(17); // TODO: Enum
render_data.sprite = Some("door_wood_h_closed".to_string()); // TODO: Enum
door_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y));
}
result = RunState::Ticking;
@ -234,7 +234,7 @@ pub fn open(i: i32, j: i32, ecs: &mut World) -> RunState {
let mut renderables = ecs.write_storage::<Renderable>();
let render_data = renderables.get_mut(potential_target).unwrap();
render_data.glyph = to_cp437('▓'); // Nethack open door, maybe just use '/' instead.
render_data.sprite = Some(18); // TODO: Enum
render_data.sprite = Some("door_wood_h_open".to_string()); // TODO: Enum
door_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y));
}
result = RunState::Ticking;

View file

@ -30,7 +30,8 @@ pub struct Equippable {
#[derive(Deserialize, Debug)]
pub struct Renderable {
pub glyph: String,
pub sprite: Option<usize>,
pub sprite: Option<String>,
pub colour_sprite: Option<bool>,
pub fg: String,
pub bg: String,
pub order: i32,

View file

@ -691,10 +691,11 @@ fn get_renderable_component(
) -> crate::components::Renderable {
crate::components::Renderable {
glyph: to_cp437(renderable.glyph.chars().next().unwrap()),
sprite: if let Some(sprite) = &renderable.sprite {
Some(sprite.clone())
sprite: renderable.sprite.clone(),
colour_sprite: if renderable.colour_sprite.is_some() {
renderable.colour_sprite.clone().unwrap()
} else {
None
true
},
fg: RGB::from_hex(&renderable.fg).expect("Invalid RGB"),
bg: RGB::from_hex(&renderable.bg).expect("Invalid RGB"),

View file

@ -57,7 +57,8 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
.with(BlocksTile {})
.with(Renderable {
glyph: to_cp437('@'),
sprite: None, // TODO: Player sprite
sprite: Some("@".to_string()), // TODO: Player sprite
colour_sprite: true,
fg: RGB::named(YELLOW),
bg: RGB::named(BLACK),
render_order: 0,

View file

@ -362,7 +362,10 @@ impl State {
)
);
self.mapgen_timer += ctx.timer.delta_f32();
if self.mapgen_timer > 10.0 / (self.mapgen_history.len() as f32) {
if
self.mapgen_timer > 10.0 / (self.mapgen_history.len() as f32) ||
self.mapgen_timer > 1.0
{
self.mapgen_timer = 0.0;
self.mapgen_index += 1;
if self.mapgen_index >= self.mapgen_history.len() {