From a9ffc9bc6f28ac517a182d7c1b5a22d4e0a6e18b Mon Sep 17 00:00:00 2001 From: dani Date: Sun, 2 Jul 2023 20:16:18 +0000 Subject: [PATCH] added console --- examples/assets/hex2.gif | Bin 0 -> 925 bytes examples/test.rs | 11 ++++++- src/console.rs | 65 +++++++++++++++++++++++++++++++++++++++ src/ega-8x14.gif | Bin 0 -> 3536 bytes src/hexmap.rs | 25 ++++++++++++--- src/image.rs | 32 ++++++++++++++++--- src/lib.rs | 2 ++ src/rect.rs | 5 +++ src/tileset.rs | 2 +- src/window.rs | 34 ++++++++++++++++++-- 10 files changed, 163 insertions(+), 13 deletions(-) create mode 100644 examples/assets/hex2.gif create mode 100644 src/console.rs create mode 100644 src/ega-8x14.gif diff --git a/examples/assets/hex2.gif b/examples/assets/hex2.gif new file mode 100644 index 0000000000000000000000000000000000000000..eedd75634f38bddbbe7b319de6b501fa277a4c1b GIT binary patch literal 925 zcmZXTZD`e10LGu|gfSdDvJ~5dwu`HcP&Yi*RTeb!tvhUK%MP@EY$J_aTxXXH7mlM& zFAxdK94!juh@Dx1%)&c{z03ls|7gb$hDF|GAGT3&T0(|+nwIE zWo!36sKy&WOG^s?l5=x&#bR-Me0+3tbZBU3U|_%)lgVT{Iy%znbSjmqtE;Q5tfcpJ zj>a&GC?$mmKnz$w0}7B!ZAH(o96vPdk9b50F7Cg7PEtrlA~8Wir9xJqB38>GoI+E1CHs1HcS zsJ2+8QK3*K;W_E}iQ$QeS;;oRMuZx~YhhPIuY@XxEcKjrylnVc#50n|1&<>1AbuKl zH*^ZB9&(N6O~>C1e~I{mq!+w|@G0U!*gSLuwGHwubYK6s{5WNfqJwA+csRx;i+YW< z3M-|*Om~LnRYskZ4^rGkEDtD&QDU)7BcYIx*F47@hYXKJ%t*EiK7z0X@dns+&{a?s zkpH`s{Rnx)ov>->%}_Ove|i4tc++q`Vvzh*a1x<__$}DO&}~o~v4muSF3?OfnxdSf zm*Bd7|F%uE0%P@`a5yQAS~PSdgJMd;nHWbxo?`!UTk{dyQ=G@*>yj?*YM`>shRVS z-aaly1VoZFC}oh|!z=KTBZK=Z#M CqOv{! literal 0 HcmV?d00001 diff --git a/examples/test.rs b/examples/test.rs index d2ccec7..a70f6ac 100644 --- a/examples/test.rs +++ b/examples/test.rs @@ -8,6 +8,7 @@ struct World { img: Rc, font: Rc, pos: Vec2, + map: HexMap, } fn main() { @@ -22,11 +23,16 @@ impl Game for World { img: Image::load_data(include_bytes!("assets/test.gif")), font: Tileset::load_data(include_bytes!("assets/ega-8x14.gif"), Vec2 { x: 16, y: 16 }), pos: Vec2::zero(), + map: HexMap::new( + Vec2 { x: 27, y: 13 }, + Tileset::load_data(include_bytes!("assets/hex2.gif"), Vec2 { x: 1, y: 1 }), + ), } } fn update(&mut self, window_state: &mut WindowState) { self.pos = window_state.mouse_pos(); + window_state.log(format!("{}x{}", self.pos.x, self.pos.y).as_str()); } fn on_event(&mut self, window_state: &mut WindowState, event: Event) { @@ -40,7 +46,8 @@ impl Game for World { fn draw(&self, target: &mut Image) { target.clear(); - target.draw_image(Vec2 { x: 200, y: 100 }, &self.img); + target.draw_hexmap(Vec2 { x: 0, y: 0 }, &self.map); + /*target.draw_image(Vec2 { x: 200, y: 100 }, &self.img); target.draw_image(self.pos, self.font.get(68)); target.draw_line( @@ -51,5 +58,7 @@ impl Game for World { self.pos, 10, ); + + */ } } diff --git a/src/console.rs b/src/console.rs new file mode 100644 index 0000000..e00d2a6 --- /dev/null +++ b/src/console.rs @@ -0,0 +1,65 @@ +use crate::{Image, Rect, Tileset, Vec2}; +use std::collections::VecDeque; + +pub(crate) struct Console { + lines: VecDeque, + max_lines: usize, + max_line_length: usize, +} + +impl Console { + pub(crate) fn new(max_lines: i32, max_line_length: i32) -> Self { + Self { + lines: VecDeque::new(), + max_lines: max_lines as usize, + max_line_length: max_line_length as usize, + } + } + + pub(crate) fn add(&mut self, str: &str) { + if str.len() > self.max_line_length { + let (first, rest) = str.split_at(self.max_line_length); + self.add(first); + self.add(rest); + } else { + self.lines.push_back(str.into()); + while self.lines.len() > self.max_lines { + self.lines.pop_front(); + } + } + } + + pub(crate) fn draw_to_image(&self, target: &mut Image, pos: Vec2, font: &Tileset) { + let height = self.max_lines as i32 * font.tile_size().y; + let width = self.max_line_length as i32 * font.tile_size().x; + target.fill_rect( + Rect { + pos, + size: Vec2 { + x: width, + y: height, + }, + }, + 254, + ); + let y = height + pos.y; + target.draw_line( + Vec2 { x: pos.x, y }, + Vec2 { + x: pos.x + width, + y, + }, + 253, + ); + for (i, str) in self.lines.iter().enumerate() { + target.draw_string( + pos + Vec2 { + x: 0, + y: i as i32 * font.tile_size().y, + }, + str, + font, + ); + } + } +} diff --git a/src/ega-8x14.gif b/src/ega-8x14.gif new file mode 100644 index 0000000000000000000000000000000000000000..c4b24c5d0c5e7564d77c23eced1a48cd8618e75c GIT binary patch literal 3536 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 literal 0 HcmV?d00001 diff --git a/src/hexmap.rs b/src/hexmap.rs index 73f73cf..66982f4 100644 --- a/src/hexmap.rs +++ b/src/hexmap.rs @@ -1,19 +1,20 @@ -use crate::Vec2; +use crate::{Image, Tileset, Vec2}; +use std::rc::Rc; //odd-q vertical layout https://www.redblobgames.com/grids/hexagons pub struct HexMap { size: Vec2, data: Vec, - tile_size: Vec2, + tileset: Rc, } impl HexMap { //pub static - pub fn new(size: Vec2, tile_size: Vec2) -> Self { + pub fn new(size: Vec2, tileset: Rc) -> Self { HexMap { size, data: vec![0; size.size()], - tile_size, + tileset, } } @@ -31,6 +32,22 @@ impl HexMap { self.size } + //pubcrate + pub(crate) fn draw_tile_to_image( + &self, + target: &mut Image, + coord: Vec2, + offset: Vec2, + ) { + let x = coord.x * (self.tileset.tile_size().x as f64 * 3.0 / 4.0).ceil() as i32; + let y = + coord.y * self.tileset.tile_size().y + (coord.x % 2) * (self.tileset.tile_size().y / 2); + target.draw_image( + Vec2 { x, y } + offset, + self.tileset.get(self.data[self.coord_to_idx(coord)]), + ) + } + //priv fn coord_to_idx(&self, coord: Vec2) -> usize { (coord.x + coord.y * self.size.x) as usize diff --git a/src/image.rs b/src/image.rs index 936e22d..3c02521 100644 --- a/src/image.rs +++ b/src/image.rs @@ -1,5 +1,5 @@ use crate::vec2::Vec2; -use crate::{HexMap, Rect}; +use crate::{HexMap, Rect, Tileset}; use std::fs; use std::fs::File; use std::rc::Rc; @@ -55,9 +55,13 @@ impl Image { self.data.as_slice() } - //pub fn draw_hexmap(&mut self, pos: Vec2, hexmap: &HexMap) { - // for pos in Vec2::zero()..pos {} - //} + pub fn draw_hexmap(&mut self, pos: Vec2, hexmap: &HexMap) { + for i in hexmap.size().to_rect().iter() { + if i.x % 1 == 0 { + hexmap.draw_tile_to_image(self, i, pos); + } + } + } pub fn draw_image(&mut self, pos: Vec2, image: &Image) { self.draw_image_partial( @@ -110,10 +114,30 @@ impl Image { } } + pub fn draw_string(&mut self, pos: Vec2, str: &str, font: &Tileset) { + assert!(str.is_ascii()); + let array = str.as_bytes(); + for i in 0..array.len() { + self.draw_image( + Vec2 { + x: pos.x + font.tile_size().x * i as i32, + y: pos.y, + }, + font.get(array[i] as i32), + ); + } + } + pub fn fill(&mut self, color: u8) { self.data.fill(color); } + pub fn fill_rect(&mut self, rect: Rect, color: u8) { + for pos in rect.iter() { + self.set_pixel(pos, color); + } + } + pub fn get_pixel(&self, pos: Vec2) -> u8 { self.data[(pos.x + self.size.x * pos.y) as usize] } diff --git a/src/lib.rs b/src/lib.rs index 60d4fd1..f039597 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +mod console; mod hexmap; mod image; mod rect; @@ -5,6 +6,7 @@ mod tileset; mod vec2; mod window; +pub use console::*; pub use hexmap::*; pub use image::*; pub use rect::*; diff --git a/src/rect.rs b/src/rect.rs index cb7fbd0..28fb667 100644 --- a/src/rect.rs +++ b/src/rect.rs @@ -1,4 +1,5 @@ use crate::vec2::Vec2; +use crate::Vec2Iter; use num::{Num, ToPrimitive}; #[derive(Copy, Clone)] @@ -19,6 +20,10 @@ impl Rect { self.pos + self.size } + pub fn iter(&self) -> Vec2Iter { + self.pos.iter_to(self.size) + } + pub fn contains(&self, point: Vec2) -> bool { let p2 = self.pos2(); point.x >= self.pos.x && point.y >= self.pos.y && point.x < p2.x && point.y < p2.y diff --git a/src/tileset.rs b/src/tileset.rs index b267109..c5055d6 100644 --- a/src/tileset.rs +++ b/src/tileset.rs @@ -20,7 +20,7 @@ impl Tileset { x: img.size().x / tile_count.x, y: img.size().y / tile_count.y, }; - for tile in Vec2::zero().iter_to(tile_count) { + for tile in tile_count.to_rect().iter() { let mut image = Image::new(size); image.draw_image_partial( Vec2::zero(), diff --git a/src/window.rs b/src/window.rs index 8d890f3..8d3e4c7 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,11 +1,14 @@ +use crate::console::Console; use crate::image::Image; use crate::vec2::Vec2; +use crate::Tileset; use rand::Rng; use std::cmp::{max, min}; use std::num::NonZeroU32; use std::time::{Duration, Instant}; use winit::dpi::LogicalSize; -use winit::event::VirtualKeyCode::Escape; +use winit::event::ElementState::Pressed; +use winit::event::VirtualKeyCode::{Escape, F12}; use winit::event::{ ElementState, Event as WinitEvent, MouseButton as WinitMouseButton, WindowEvent, }; @@ -37,13 +40,21 @@ pub trait Game { pub struct WindowState { palette: [u32; 256], mouse_pos: Vec2, + console: Console, } impl WindowState { - fn new() -> Self { + pub fn log(&mut self, msg: &str) { + self.console.add(msg); + } +} + +impl WindowState { + fn new(console_size: Vec2) -> Self { WindowState { palette: [0u32; 256], mouse_pos: Vec2::zero(), + console: Console::new(console_size.y, console_size.x), } } pub fn set_palette(&mut self, index: u8, r: u8, g: u8, b: u8) { @@ -84,8 +95,15 @@ pub fn run(width: i32, height: i32) { x: width, y: height, }); - let mut window_state = WindowState::new(); + let internal_font = Tileset::load_data(include_bytes!("ega-8x14.gif"), Vec2 { x: 16, y: 16 }); + let mut window_state = WindowState::new(Vec2 { + x: width / internal_font.tile_size().x, + y: height / internal_font.tile_size().y / 3, + }); let mut game = T::new(&mut window_state); + window_state.console.add("Initialising Skunk2d"); + + let mut display_console = true; let mut frames_since_last_second = 0; let mut last_second = Instant::now(); @@ -129,6 +147,9 @@ pub fn run(width: i32, height: i32) { if k == Escape { *control_flow = ControlFlow::Exit; } + if k == F12 && input.state == Pressed { + display_console = !display_console; + } }; } WindowEvent::ModifiersChanged(_) => {} @@ -181,6 +202,13 @@ pub fn run(width: i32, height: i32) { if Instant::now() - last_frame >= target_frame_duration { game.update(&mut window_state); game.draw(&mut screen); + if display_console { + window_state.console.draw_to_image( + &mut screen, + Vec2::zero(), + &internal_font, + ); + } let size = window.inner_size(); surface