From 41189401ab6a2d3487bc160cccd24c9c7661f397 Mon Sep 17 00:00:00 2001 From: dani Date: Sat, 22 Jul 2023 22:36:53 +0000 Subject: [PATCH] ... did all the things --- Cargo.toml | 2 +- examples/basic.rs | 9 +- examples/gfx.gif | Bin 19809 -> 18072 bytes src/buttons.rs | 111 ++++++++++++++---------- src/gsa.rs | 35 +++++++- src/gsa_render_to_screen.rs | 167 ++++++++++++++++++++++++++++++++---- src/input.rs | 18 ---- src/lib.rs | 22 +++-- src/run.rs | 124 ++++++++++++++++++++++---- src/sprite.rs | 8 +- src/state.rs | 147 ------------------------------- 11 files changed, 381 insertions(+), 262 deletions(-) delete mode 100644 src/input.rs delete mode 100644 src/state.rs diff --git a/Cargo.toml b/Cargo.toml index 986a49b..03b84db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ name = "gsa" version = "0.1.0" edition = "2021" +description = "Game development library modelled after an imaginary console" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -11,7 +12,6 @@ lazy_static = "1.4.0" glam = "0.24.0" ascii = "1.1.0" gilrs = "0.10.2" -cpal = "0.15.2" winit = "0.28.6" softbuffer = "0.3.0" gif = "0.12.0" diff --git a/examples/basic.rs b/examples/basic.rs index 8e71710..9e26568 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,12 +1,12 @@ use glam::IVec2; -use gsa::{run, Gsa}; +use gsa::{run, Gsa, FACE_DOWN}; struct Game {} fn init(gsa: &mut Gsa) -> Game { gsa.sprites[0].tile = 0x0300; gsa.sprites[1].tile = 0x0200; - gsa.maps[0].tiles[0][0] = 0x0300; + gsa.maps[0].tiles[1][1] = 0x0300; gsa.maps[1].half_tile = true; gsa.write_string(1, IVec2::ONE, "Hello world nyaa~"); Game {} @@ -14,8 +14,9 @@ fn init(gsa: &mut Gsa) -> Game { fn update(_game: &mut Game, gsa: &mut Gsa) { gsa.sprites[0].pos.x = (gsa.sprites[0].pos.x + 1) % 300; - gsa.sprites[1].pos += gsa.input.dir; - if gsa.input.pressed.face_down { + gsa.sprites[1].pos += gsa.input_dir(); + gsa.maps[1].scroll.x += 1; + if gsa.button_pressed(FACE_DOWN) { gsa.sprites[1].tile += 1; } } diff --git a/examples/gfx.gif b/examples/gfx.gif index dbd91c9c29fa9a7267c229438187a740147085da..a2f494414c3e49250b051781841c9b3395cce107 100644 GIT binary patch delta 1404 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 diff --git a/src/buttons.rs b/src/buttons.rs index dd89038..0995499 100644 --- a/src/buttons.rs +++ b/src/buttons.rs @@ -1,47 +1,70 @@ -/// State of all GSA buttons -#[derive(Default, Copy, Clone)] -pub struct Buttons { - /// UP on dpad - pub dpad_up: bool, - /// DOWN on dpad - pub dpad_down: bool, - /// LEFT on dpad - pub dpad_left: bool, - /// RIGHT on dpad - pub dpad_right: bool, - /// X on nintendo, Y on microsoft, TRIANGLE on sony - pub face_up: bool, - /// B on nintendo, A on microsoft, CROSS on sony - pub face_down: bool, - /// Y on nintendo, X on microsoft, SQUARE on sony - pub face_left: bool, - /// A on nintendo, B on microsoft, CIRCLE on sony - pub face_right: bool, - /// left shoulder button - pub l: bool, - /// right shoulder button - pub r: bool, - /// start button - pub start: bool, - /// select button - pub select: bool, -} +use gilrs::Button; +use winit::event::ScanCode; -impl Buttons { - pub(crate) fn pressed(old: &Buttons, new: &Buttons) -> Buttons { - Buttons { - dpad_up: !old.dpad_up && new.dpad_up, - dpad_down: !old.dpad_down && new.dpad_down, - dpad_left: !old.dpad_left && new.dpad_left, - dpad_right: !old.dpad_right && new.dpad_right, - face_up: !old.face_up && new.face_up, - face_down: !old.face_down && new.face_down, - face_left: !old.face_left && new.face_left, - face_right: !old.face_right && new.face_right, - l: !old.l && new.l, - r: !old.r && new.r, - start: !old.start && new.start, - select: !old.select && new.select, - } +/// UP on dpad +pub const DPAD_UP: Buttons = 0x0001; +/// DOWN on dpad +pub const DPAD_DOWN: Buttons = 0x0002; +/// LEFT on dpad +pub const DPAD_LEFT: Buttons = 0x0004; +/// RIGHT on dpad +pub const DPAD_RIGHT: Buttons = 0x008; +/// X on nintendo, Y on microsoft, TRIANGLE on sony +pub const FACE_UP: Buttons = 0x0010; +/// B on nintendo, A on microsoft, CROSS on sony +pub const FACE_DOWN: Buttons = 0x0020; +/// Y on nintendo, X on microsoft, SQUARE on sony +pub const FACE_LEFT: Buttons = 0x0040; +/// A on nintendo, B on microsoft, CIRCLE on sony +pub const FACE_RIGHT: Buttons = 0x0080; +/// left shoulder button +pub const L: Buttons = 0x0100; +/// right shoulder button +pub const R: Buttons = 0x0200; +/// start button +pub const START: Buttons = 0x0400; +/// select button +pub const SELECT: Buttons = 0x0800; + +/// Bitmask of button states +/// +/// GSA pretends all input is a snes-like gamepad +/// d-pad, 4 face buttons, l and r shoulder buttons, start and select +pub type Buttons = u16; + +pub(crate) fn button_from_scancode(scancode: ScanCode) -> Buttons { + //todo: currently windows only? check + match scancode { + 0x0010 => L, + 0x0011 => FACE_UP, + 0x0012 => R, + 0x001C => START, + 0x001E => FACE_LEFT, + 0x001F => FACE_DOWN, + 0x0020 => FACE_RIGHT, + 0x0039 => SELECT, + 0xE048 => DPAD_UP, + 0xE04B => DPAD_LEFT, + 0xE04D => DPAD_RIGHT, + 0xE050 => DPAD_DOWN, + _ => 0, + } +} + +pub(crate) fn button_from_gilrs(button: Button) -> Buttons { + match button { + Button::South => FACE_DOWN, + Button::East => FACE_RIGHT, + Button::North => FACE_UP, + Button::West => FACE_LEFT, + Button::LeftTrigger | Button::LeftTrigger2 => L, + Button::RightTrigger | Button::RightTrigger2 => R, + Button::Select => SELECT, + Button::Start => START, + Button::DPadUp => DPAD_UP, + Button::DPadDown => DPAD_DOWN, + Button::DPadLeft => DPAD_LEFT, + Button::DPadRight => DPAD_RIGHT, + _ => 0, } } diff --git a/src/gsa.rs b/src/gsa.rs index afc895d..b1b8995 100644 --- a/src/gsa.rs +++ b/src/gsa.rs @@ -1,8 +1,9 @@ -use crate::input::Input; use crate::rgb::Rgb; use crate::sprite::Sprite; use crate::tilemap::Tilemap; -use crate::{MAX_SPRITES, MAX_TILEMAPS, TILEMAP_MAX_SIZE}; +use crate::{ + Buttons, DPAD_DOWN, DPAD_LEFT, DPAD_RIGHT, DPAD_UP, MAX_SPRITES, MAX_TILEMAPS, TILEMAP_MAX_SIZE, +}; use ascii::{AsciiChar, AsciiStr}; use glam::IVec2; @@ -22,8 +23,9 @@ pub struct Gsa { /// Chosen as half-size tile index, extends 16x16 half tiles in x and y pub font: u16, - /// Current input state - pub input: Input, + pub(crate) pressed: Buttons, + pub(crate) released: Buttons, + pub(crate) down: Buttons, } impl Gsa { @@ -41,6 +43,31 @@ impl Gsa { } } + /// Checks if any of given buttons currently held down + pub fn button_down(&self, button: Buttons) -> bool { + self.down & button != 0 + } + + /// Checks if any of given buttons been pressed in the current frame + pub fn button_pressed(&self, button: Buttons) -> bool { + self.pressed & button != 0 + } + + /// Checks if any of given buttons been released in the current frame + pub fn button_released(&self, button: Buttons) -> bool { + self.released & button != 0 + } + + /// Directional vector (-1 to 1) from current dpad state + pub fn input_dir(&self) -> IVec2 { + IVec2 { + x: if self.button_down(DPAD_LEFT) { -1 } else { 0 } + + if self.button_down(DPAD_RIGHT) { 1 } else { 0 }, + y: if self.button_down(DPAD_UP) { -1 } else { 0 } + + if self.button_down(DPAD_DOWN) { 1 } else { 0 }, + } + } + /// Write given string on given map, at given position, with font [Gsa::font] pub fn write_string(&mut self, map: usize, pos: IVec2, str: &str) { let str = AsciiStr::from_ascii(str).unwrap(); diff --git a/src/gsa_render_to_screen.rs b/src/gsa_render_to_screen.rs index 5f38e91..3856ee3 100644 --- a/src/gsa_render_to_screen.rs +++ b/src/gsa_render_to_screen.rs @@ -1,23 +1,158 @@ -use crate::{Gsa, MAX_SPRITES, TILE_SIZE}; +use crate::{ + Gsa, Rgb, Tilemap, HALF_TILE_SIZE, MAX_TILEMAPS, SCREEN_HEIGHT, SCREEN_WIDTH, TILEMAP_MAX_SIZE, + TILESET_SIZE, TILE_SIZE, +}; use glam::IVec2; use softbuffer::Buffer; -pub(crate) fn render_to_window(target: &mut Buffer, gsa: &Gsa, window_size: IVec2) { - for (y, row) in target.chunks_exact_mut(window_size.x as usize).enumerate() { - let y = y as i32; - for x in 0..window_size.x { - let mut p = 0; - for sprite in &gsa.sprites { - if sprite.tile > 0 - && x >= sprite.pos.x - && y >= sprite.pos.y - && x < sprite.pos.x + TILE_SIZE as i32 - && y < sprite.pos.y + TILE_SIZE as i32 - { - p = 1; - } +fn draw_tile(target: &mut [u8], pos: IVec2, tile: u16, tileset: &[u8], half: bool) { + let tilesize = if half { HALF_TILE_SIZE } else { TILE_SIZE }; + let tx = tile as usize % 0x100 * tilesize; + let ty = tile as usize / 0x100 * tilesize; + let startx = pos.x.max(0).min(SCREEN_WIDTH as i32); + let starty = pos.y.max(0).min(SCREEN_HEIGHT as i32); + let endx = (pos.x + tilesize as i32).max(0).min(SCREEN_WIDTH as i32); + let endy = (pos.y + tilesize as i32).max(0).min(SCREEN_HEIGHT as i32); + for y in (starty - pos.y)..(endy - pos.y) { + for x in (startx - pos.x)..(endx - pos.x) { + let p = tileset[(x as usize + tx) + (y as usize + ty) * (TILESET_SIZE * TILE_SIZE)]; + if p > 0 { + target[(x as usize + pos.x as usize) + + (y as usize + pos.y as usize) * SCREEN_WIDTH] = p; } - row[x as usize] = gsa.palette[p].to_u32(); + } + } +} + +fn render_map(target: &mut [u8], map: &Tilemap, tileset: &[u8]) { + let tcmult = if map.half_tile { 2 } else { 1 }; + let tilesize = if map.half_tile { + HALF_TILE_SIZE + } else { + TILE_SIZE + } as i32; + let mut startx = map.scroll.x / tilesize; + let mut starty = map.scroll.y / tilesize; + let endx = (TILEMAP_MAX_SIZE as i32).min(startx + 20 * tcmult); + let endy = (TILEMAP_MAX_SIZE as i32).min(starty + 12 * tcmult); + startx = 0.max(startx); + starty = 0.max(starty); + for x in startx..endx { + for y in starty..endy { + let tile = map.tiles[x as usize][y as usize]; + if tile > 0 { + draw_tile( + target, + IVec2 { + x: x * tilesize - map.scroll.x, + y: y * tilesize - map.scroll.y, + }, + tile, + tileset, + map.half_tile, + ); + } + } + } +} + +pub(crate) fn render_to_screen(target: &mut [u8], gsa: &Gsa, tileset: &[u8]) { + for i in 0..MAX_TILEMAPS { + render_map(target, &gsa.maps[i], tileset); + for sprite in &gsa.sprites { + if sprite.tile > 0 && sprite.priority == i as u8 { + draw_tile(target, sprite.pos, sprite.tile, tileset, false); + } + } + } +} + +pub(crate) fn render_to_window( + target: &mut Buffer, + src: &mut [u8], + palette: &[Rgb; 256], + window_size: IVec2, + scale: usize, + off_x: usize, + off_y: usize, +) { + let start = window_size.x as usize * off_y; + let end = start + window_size.x as usize * SCREEN_HEIGHT * scale; + for (y, row) in target[start..end] + .chunks_exact_mut(window_size.x as usize * scale) + .enumerate() + { + let y = y as i32; + for x in 0..SCREEN_WIDTH { + let p = src[x + y as usize * SCREEN_WIDTH]; + + for scaley in 0..scale { + for scalex in 0..scale { + let sx = x * scale + scalex; + row[sx + off_x + scaley * window_size.x as usize] = + palette[p as usize].to_u32(); + } + } + + //unrolled is faster benchmarked... <_< + /* + let ww = window_size.x as usize; + match scale { + 1 => { + row[x + off_x] = palette[p as usize].to_u32(); + } + 2 => { + row[x * 2 + off_x] = palette[p as usize].to_u32(); + row[x * 2 + off_x + 1] = palette[p as usize].to_u32(); + row[x * 2 + off_x + ww] = palette[p as usize].to_u32(); + row[x * 2 + off_x + ww + 1] = palette[p as usize].to_u32(); + } + 6 => { + row[x * 6 + off_x + ww * 0] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 0 + 1] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 0 + 2] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 0 + 3] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 0 + 4] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 0 + 5] = palette[p as usize].to_u32(); + + row[x * 6 + off_x + ww * 1] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 1 + 1] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 1 + 2] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 1 + 3] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 1 + 4] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 1 + 5] = palette[p as usize].to_u32(); + + row[x * 6 + off_x + ww * 2] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 2 + 1] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 2 + 2] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 2 + 3] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 2 + 4] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 2 + 5] = palette[p as usize].to_u32(); + + row[x * 6 + off_x + ww * 3] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 3 + 1] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 3 + 2] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 3 + 3] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 3 + 4] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 3 + 5] = palette[p as usize].to_u32(); + + row[x * 6 + off_x + ww * 4] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 4 + 1] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 4 + 2] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 4 + 3] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 4 + 4] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 4 + 5] = palette[p as usize].to_u32(); + + row[x * 6 + off_x + ww * 5] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 5 + 1] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 5 + 2] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 5 + 3] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 5 + 4] = palette[p as usize].to_u32(); + row[x * 6 + off_x + ww * 5 + 5] = palette[p as usize].to_u32(); + } + _ => {} + } + */ } } } diff --git a/src/input.rs b/src/input.rs deleted file mode 100644 index 7d722a8..0000000 --- a/src/input.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::buttons::Buttons; -use glam::IVec2; - -/// Input State -/// -/// GSA pretends all input is a snes-like gamepad -/// d-pad, 4 face buttons, l and r shoulder buttons, start and select -#[derive(Default)] -pub struct Input { - /// Buttons that are currentle held down - pub down: Buttons, - - /// Buttons that have been pressed in the current frame - pub pressed: Buttons, - - /// Directional vector (-1 to 1) from current dpad state - pub dir: IVec2, -} diff --git a/src/lib.rs b/src/lib.rs index 8ddcbd1..b17ab6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,14 @@ //! - Tilemaps: 4 of size 1024x1024, scrollable //! //! ## Features not yet implemented -//! - Sound (no samples +//! - Tilemap effects +//! - Rotation? Scaling? +//! - Mosaic? +//! - Mode7? +//! - Sprite Effects +//! - Rotation? Scaling? +//! - Mosaic? +//! - Sound (no samples) //! - Synth //! - Speech //! - Savegames @@ -23,21 +30,18 @@ mod buttons; mod gsa; mod gsa_render_to_screen; -mod input; mod rgb; mod run; mod sprite; -mod state; mod tilemap; mod tileset; -pub use crate::buttons::Buttons; -pub use crate::gsa::Gsa; -pub use crate::input::Input; -pub use crate::rgb::Rgb; +pub use crate::buttons::*; +pub use crate::gsa::*; +pub use crate::rgb::*; pub use crate::run::run; -pub use crate::sprite::Sprite; -pub use crate::tilemap::Tilemap; +pub use crate::sprite::*; +pub use crate::tilemap::*; /// Amount of sprites in [Gsa::sprites] pub const MAX_SPRITES: usize = 0xff; diff --git a/src/run.rs b/src/run.rs index 433c930..f018b85 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,14 +1,15 @@ -use crate::gsa_render_to_screen::render_to_window; -use crate::state::State; +use crate::buttons::{button_from_gilrs, button_from_scancode}; +use crate::gsa_render_to_screen::{render_to_screen, render_to_window}; use crate::tileset::load_tileset; -use crate::{Gsa, Rgb, Sprite, MAX_SPRITES, SCREEN_HEIGHT, SCREEN_WIDTH}; -use gilrs::Gilrs; +use crate::{Buttons, Gsa, Sprite, MAX_SPRITES, SCREEN_HEIGHT, SCREEN_WIDTH}; +use gilrs::EventType; use glam::IVec2; use std::cmp::min; use std::num::NonZeroU32; use std::time::{Duration, Instant}; -use winit::dpi::LogicalSize; -use winit::event::{Event, VirtualKeyCode, WindowEvent}; +use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize}; +use winit::event; +use winit::event::{ElementState, Event, VirtualKeyCode, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; @@ -41,7 +42,9 @@ pub fn run( maps: Default::default(), palette, font: 0x1010, - input: Default::default(), + pressed: 0, + released: 0, + down: 0, }; let mut game = init_fn(&mut gsa); /*let state = State:: { @@ -64,6 +67,23 @@ pub fn run( .build(&event_loop) .unwrap(); + { + let monitor_size = window.primary_monitor().unwrap().size(); + let scale_factor = 0.9; + let window_scale = ((monitor_size.width as f32 * scale_factor) as u32 + / SCREEN_WIDTH as u32) + .min((monitor_size.height as f32 * scale_factor) as u32 / SCREEN_HEIGHT as u32); + window.set_inner_size(PhysicalSize { + width: window_scale * SCREEN_WIDTH as u32, + height: window_scale * SCREEN_HEIGHT as u32, + }); + let outer_size = window.outer_size(); + window.set_outer_position(PhysicalPosition { + x: (monitor_size.width - outer_size.width) / 2, + y: (monitor_size.height - outer_size.height) / 2, + }); + } + let context = unsafe { softbuffer::Context::new(&window) }.unwrap(); let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap(); @@ -71,34 +91,97 @@ pub fn run( let mut last_second = Instant::now(); let mut last_frame = last_second; - let target_frame_duration = Duration::from_secs(1).div_f64(6000.0); + let target_frame_duration = Duration::from_secs(1).div_f64(60.0); let mut scale = 1usize; let mut off_x = 0usize; let mut off_y = 0usize; + let mut new_buttons = Buttons::default(); + // in case button gets pressed and released within the same frame need to catch them + // seperately from the last-now comparison to catch it + let mut new_pressed = Buttons::default(); + let mut new_released = Buttons::default(); + let mut gilrs = gilrs::Gilrs::new().unwrap(); + event_loop.run(move |event, _, control_flow| { let scale = &mut scale; let off_x = &mut off_x; let off_y = &mut off_y; + let new_buttons = &mut new_buttons; + let new_pressed = &mut new_pressed; + let new_released = &mut new_released; + let gilrs = &mut gilrs; + if Instant::now() - last_second > Duration::from_secs(1) { println!("fps: {}", frames_since_last_second); frames_since_last_second = 0; last_second += Duration::from_secs(1); } + + let mut screen_buffer = [0u8; SCREEN_WIDTH * SCREEN_HEIGHT]; + match event { + Event::WindowEvent { + event: + WindowEvent::KeyboardInput { + input: + event::KeyboardInput { + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + }, + .. + }, + .. + } => { + *control_flow = ControlFlow::Exit; + } + Event::WindowEvent { event: WindowEvent::KeyboardInput { input, .. }, .. - } => match input.virtual_keycode { - Some(VirtualKeyCode::Escape) => { - *control_flow = ControlFlow::Exit; - } - _ => {} - }, + } => { + let button = button_from_scancode(input.scancode); + match input.state { + ElementState::Pressed => { + *new_buttons |= button; + *new_pressed |= button; + } + ElementState::Released => { + *new_buttons &= !button; + *new_released |= button; + } + }; + } + Event::MainEventsCleared {} => { if Instant::now() - last_frame >= target_frame_duration { + //input + while let Some(event) = gilrs.next_event() { + match event.event { + EventType::ButtonPressed(button, ..) => { + let button = button_from_gilrs(button); + *new_buttons |= button; + *new_pressed |= button; + } + EventType::ButtonReleased(button, ..) => { + let button = button_from_gilrs(button); + *new_buttons &= !button; + *new_released |= button; + } + _ => {} + } + } + gsa.down = *new_buttons; + gsa.released = *new_released; + gsa.pressed = *new_pressed; + *new_released = 0; + *new_pressed = 0; + + //update update_fn(&mut game, &mut gsa); + + //graphics let size = window.inner_size(); surface .resize( @@ -114,16 +197,21 @@ pub fn run( *off_x = (size.width as usize - SCREEN_WIDTH * *scale) / 2; *off_y = (size.height as usize - SCREEN_HEIGHT * *scale) / 2; - let mut buf = surface.buffer_mut().unwrap(); + let mut window_buffer = surface.buffer_mut().unwrap(); + render_to_screen(&mut screen_buffer, &gsa, &tileset); render_to_window( - &mut buf, - &gsa, + &mut window_buffer, + &mut screen_buffer, + &palette, IVec2 { x: size.width as i32, y: size.height as i32, }, + *scale, + *off_x, + *off_y, ); - buf.present().unwrap(); + window_buffer.present().unwrap(); frames_since_last_second += 1; last_frame += target_frame_duration; } diff --git a/src/sprite.rs b/src/sprite.rs index c5bb194..b5287ae 100644 --- a/src/sprite.rs +++ b/src/sprite.rs @@ -8,10 +8,16 @@ pub struct Sprite { pub pos: IVec2, /// Tile index pub tile: u16, + /// Priority, this sprite will be drawn above map of idx priority + pub priority: u8, } impl Display for Sprite { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "[pos: {}, tile: {}]", self.pos, self.tile) + write!( + f, + "[pos: {}, tile: {}, priority: {}]", + self.pos, self.tile, self.priority + ) } } diff --git a/src/state.rs b/src/state.rs deleted file mode 100644 index 7c57989..0000000 --- a/src/state.rs +++ /dev/null @@ -1,147 +0,0 @@ -use crate::buttons::Buttons; -use crate::{Gsa, TILEMAP_MAX_SIZE}; -use gilrs::{Button, EventType, Gilrs}; -use glam::IVec2; -use std::rc::Rc; - -pub struct State { - pub(crate) gsa: Gsa, - pub(crate) game: TGame, - pub(crate) update_fn: fn(game: &mut TGame, gsa: &mut Gsa), - pub(crate) tileset: Vec, - pub(crate) first: bool, - pub(crate) gilrs: Gilrs, -} - -impl State { - fn update(&mut self) { - let mut new_buttons = self.gsa.input.down; - while let Some(event) = self.gilrs.next_event() { - match event { - gilrs::Event { - event: EventType::ButtonPressed(button, _), - .. - } => match button { - Button::South => new_buttons.face_down = true, - Button::East => new_buttons.face_left = true, - Button::North => new_buttons.face_up = true, - Button::West => new_buttons.face_right = true, - Button::LeftTrigger | Button::LeftTrigger2 => new_buttons.l = true, - Button::RightTrigger | Button::RightTrigger2 => new_buttons.r = true, - Button::Select => new_buttons.select = true, - Button::Start => new_buttons.start = true, - Button::DPadUp => new_buttons.dpad_up = true, - Button::DPadDown => new_buttons.dpad_down = true, - Button::DPadLeft => new_buttons.dpad_left = true, - Button::DPadRight => new_buttons.dpad_right = true, - _ => {} - }, - gilrs::Event { - event: EventType::ButtonReleased(button, _), - .. - } => match button { - Button::South => new_buttons.face_down = false, - Button::East => new_buttons.face_left = false, - Button::North => new_buttons.face_up = false, - Button::West => new_buttons.face_right = false, - Button::LeftTrigger | Button::LeftTrigger2 => new_buttons.l = false, - Button::RightTrigger | Button::RightTrigger2 => new_buttons.r = false, - Button::Select => new_buttons.select = false, - Button::Start => new_buttons.start = false, - Button::DPadUp => new_buttons.dpad_up = false, - Button::DPadDown => new_buttons.dpad_down = false, - Button::DPadLeft => new_buttons.dpad_left = false, - Button::DPadRight => new_buttons.dpad_right = false, - _ => {} - }, - _ => (), - } - } - self.gsa.input.pressed = Buttons::pressed(&self.gsa.input.down, &new_buttons); - self.gsa.input.down = new_buttons; - self.gsa.input.dir = IVec2 { - x: if self.gsa.input.down.dpad_left { -1 } else { 0 } - + if self.gsa.input.down.dpad_right { 1 } else { 0 }, - y: if self.gsa.input.down.dpad_up { -1 } else { 0 } - + if self.gsa.input.down.dpad_down { 1 } else { 0 }, - }; - - //todo: don't if not updated... how check? <_< - /* - for i in 0..=255 { - window_state.set_palette( - i, - self.gsa.palette[i as usize].r, - self.gsa.palette[i as usize].g, - self.gsa.palette[i as usize].b, - ); - } - */ - (self.update_fn)(&mut self.game, &mut self.gsa); - } - - fn draw(&self) { - //target.clear(); - - for map in &self.gsa.maps { - let tcmult = if map.half_tile { 2 } else { 1 }; - let tilesize = if map.half_tile { 8 } else { 16 }; - let mut startx = map.scroll.x / tilesize; - let mut starty = map.scroll.y / tilesize; - let endx = (TILEMAP_MAX_SIZE as i32).min(startx + 20 * tcmult); - let endy = (TILEMAP_MAX_SIZE as i32).min(starty + 12 * tcmult); - startx = 0.max(startx); - starty = 0.max(starty); - for x in startx..endx { - for y in starty..endy { - let tile = map.tiles[x as usize][y as usize]; - if tile > 0 { - let ty = tile / 0x100; - let tx = tile % 0x100; - /* - target.draw_image_partial( - IVec2 { - x: x * tilesize - map.scroll.x, - y: y * tilesize - map.scroll.y, - }, - &self.tileset, - IRect { - pos: IVec2 { - x: tx as i32 * tilesize, - y: ty as i32 * tilesize, - }, - size: IVec2 { - x: tilesize, - y: tilesize, - }, - }, - ) - - */ - } - } - } - } - - for sprite in &self.gsa.sprites { - if sprite.tile > 0 { - let ty = sprite.tile / 0x100; - let tx = sprite.tile % 0x100; - /* - target.draw_image_partial( - sprite.pos, - &self.tileset, - IRect { - pos: IVec2 { - x: tx as i32 * 16, - y: ty as i32 * 16, - }, - size: IVec2 { x: 16, y: 16 }, - }, - ); - - */ - } - } - } -}