diff --git a/Cargo.toml b/Cargo.toml index 22e3440..d5af3a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["gamedev"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -glam = "0.24.0" +glam = {version = "0.24.0", features = ["serde"]} ascii = "1.1.0" gilrs = "0.10.2" winit = "0.28.6" @@ -22,7 +22,8 @@ gif = "0.12.0" clap = {version = "4.3.8", features = ["derive", "cargo"]} dunce = "1.0.4" path-slash = "0.2.1" -serde = "1.0.181" +serde = {version = "1.0.181", features = ["derive"]} +postcard = {version = "1.0.6", features = ["alloc"]} [profile.release-dani] inherits = "release" diff --git a/examples/basic/gfx.gif b/examples/basic/gfx.gif index 3bb85d3..47b5ae8 100644 Binary files a/examples/basic/gfx.gif and b/examples/basic/gfx.gif differ diff --git a/examples/basic/main.rs b/examples/basic/main.rs index d302b57..d2f7f04 100644 --- a/examples/basic/main.rs +++ b/examples/basic/main.rs @@ -8,6 +8,7 @@ fn init(gsa: &mut Gsa) -> Game { gsa.sprites[1].tile = 0x0200; gsa.bgs[0].tiles[1][1] = 0x0300; gsa.bgs[1].half_tile = true; + gsa.load_map(1337); gsa.write_string(1, IVec2::ONE, "Hello world nyaa~"); Game {} } @@ -15,7 +16,7 @@ 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(); - gsa.bgs[1].scroll.x += 1; + //gsa.bgs[1].scroll.x += 1; if gsa.button_pressed(FACE_DOWN) { gsa.sprites[1].tile += 1; } diff --git a/src/gsa.rs b/src/gsa.rs index 055c66b..6daf297 100644 --- a/src/gsa.rs +++ b/src/gsa.rs @@ -1,4 +1,5 @@ use crate::background::Background; +use crate::maps::Maps; use crate::rgb::Rgb; use crate::sprite::Sprite; use crate::{ @@ -27,6 +28,7 @@ pub struct Gsa { pub(crate) pressed: Buttons, pub(crate) released: Buttons, pub(crate) down: Buttons, + pub(crate) maps: Maps, } impl Gsa { @@ -64,6 +66,23 @@ impl Gsa { } } + /// Loads tilemap into backgrounds + pub fn load_map(&mut self, map: u16) { + for (i, map) in self.maps.maps[&map].iter().enumerate() { + self.bgs[i].size = map.size; + for y in 0..map.size.y as usize { + for x in 0..map.size.x as usize { + self.bgs[i].tiles[x][y] = map.data[x + y * map.size.x as usize] + } + } + } + } + + /// Does a tilemap with this id exist? + pub fn map_exists(&mut self, map: u16) -> bool { + self.maps.maps.contains_key(&map) + } + /// Checks if any of given buttons currently held down pub fn button_down(&self, button: Buttons) -> bool { self.down & button != 0 diff --git a/src/gsa_render_to_screen.rs b/src/gsa_render_to_screen.rs index c550225..9a79a00 100644 --- a/src/gsa_render_to_screen.rs +++ b/src/gsa_render_to_screen.rs @@ -1,8 +1,8 @@ use std::usize; use crate::{ - Background, Gsa, Rgb, BACKGROUND_MAX_SIZE, EMPTY_TILE, HALF_TILE_SIZE, MAX_BACKGROUNDS, - SCREEN_HEIGHT, SCREEN_WIDTH, TILESET_SIZE, TILE_SIZE, TRANSPARENT, + Background, Gsa, Rgb, EMPTY_TILE, HALF_TILE_SIZE, MAX_BACKGROUNDS, TILESET_SIZE, TILE_SIZE, + TRANSPARENT, }; use glam::IVec2; use softbuffer::Buffer; diff --git a/src/lib.rs b/src/lib.rs index b4ba70c..ab0784a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,9 +43,11 @@ mod background; mod buttons; mod gsa; mod gsa_render_to_screen; +mod maps; mod rgb; mod run; mod sprite; +mod tilemap; mod tileset; pub use crate::background::*; diff --git a/src/main.rs b/src/main.rs index 25c933f..4777a3a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,9 +3,11 @@ mod buttons; mod gsa; mod gsa_render_to_screen; mod mapedit; +mod maps; mod rgb; mod run; mod sprite; +mod tilemap; mod tileset; //todo: figure out how to not repeat all the lib.rs stuff :( diff --git a/src/mapedit.rs b/src/mapedit.rs index bf28220..3d1b373 100644 --- a/src/mapedit.rs +++ b/src/mapedit.rs @@ -4,22 +4,31 @@ use std::num::NonZeroU32; use crate::gsa::Gsa; use crate::gsa_render_to_screen::render_to_screen; use crate::gsa_render_to_screen::render_to_window; +use crate::maps::Maps; use crate::sprite::Sprite; +use crate::tilemap::Tilemap; use crate::tileset::*; use crate::*; use clap::crate_version; use glam::IVec2; use winit::{ dpi::LogicalSize, - event::{Event, KeyboardInput, VirtualKeyCode, WindowEvent}, + event::{Event, VirtualKeyCode, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; const SPR_CURSOR: usize = 0x01; -const TILE_CURSOR: u16 = 0x7308; -const TILE_MARKER: u16 = 0x7309; -const TILE_BG: u16 = 0x730a; +const TILE_CURSOR: u16 = 0x7110; +const TILE_MARKER: u16 = 0x7210; +//const TILE_BG: u16 = 0x730a; + +const BUT_SAVE: usize = 0; +const BUT_LAYER1: usize = 2; +const BUT_LAYER2: usize = 3; +const BUT_LAYER3: usize = 4; +const BUT_LAYERS: usize = 5; +const BUT_EXIT: usize = 7; struct Surface<'a> { pub data: &'a mut [u8], @@ -40,12 +49,40 @@ impl<'a> Surface<'a> { } } +enum State { + Edit, + SelectTile, +} + +fn update_layer_state(gsa: &mut Gsa, gsa2: &mut Gsa, layer: usize, all: bool) { + gsa2.bgs[2].tiles[0][BUT_LAYER1] = if layer == 0 {0x7111} else {0x7011}; + gsa2.bgs[2].tiles[0][BUT_LAYER2] = if layer == 1 {0x7112} else {0x7012}; + gsa2.bgs[2].tiles[0][BUT_LAYER3] = if layer == 2 {0x7113} else {0x7013}; + gsa2.bgs[2].tiles[0][BUT_LAYERS] = if all {0x7114} else {0x7014}; + if all { + gsa.bgs[0].active = true; + gsa.bgs[1].active = true; + gsa.bgs[2].active = true; + } else { + gsa.bgs[0].active = layer == 0; + gsa.bgs[1].active = layer == 1; + gsa.bgs[2].active = layer == 2; + } +} + pub(crate) fn run_mapedit() { println!("running map edit"); let tileset_path = "examples/basic/gfx.gif"; let (tileset, palette) = load_tileset(&fs::read(tileset_path).unwrap()); + let maps_path = "examples/basic/maps.dat"; + let maps = if Path::new(maps_path).exists() { + postcard::from_bytes(&fs::read(maps_path).unwrap()).unwrap() + } else { + Maps::default() + }; + let mut gsa = Gsa { sprites: [Sprite::default(); MAX_SPRITES], palette, @@ -54,15 +91,25 @@ pub(crate) fn run_mapedit() { pressed: 0, released: 0, down: 0, + maps, }; gsa.reset_bgs(); gsa.reset_sprites(); + + if gsa.map_exists(1337) { + gsa.load_map(1337); + } + for i in 0..3 { - gsa.bgs[i].scroll = IVec2 { x: -16, y: 0 }; + gsa.bgs[i].scroll = IVec2 { + x: -16 - 32, + y: -32, + }; } gsa.sprites[SPR_CURSOR].tile = TILE_CURSOR; + gsa.sprites[SPR_CURSOR].priority = 3; let mut gsa2 = Gsa { sprites: [Sprite::default(); MAX_SPRITES], palette, @@ -71,11 +118,15 @@ pub(crate) fn run_mapedit() { pressed: 0, released: 0, down: 0, + maps: Maps::default(), }; gsa2.reset_bgs(); gsa2.reset_sprites(); for i in 0..2 { - gsa2.bgs[i].scroll = IVec2 { x: -16, y: 0 }; + gsa2.bgs[i].scroll = IVec2 { + x: -16 - 32, + y: -32, + }; } for y in 0..TILESET_SIZE { @@ -89,10 +140,11 @@ pub(crate) fn run_mapedit() { gsa2.bgs[2].size = IVec2 { x: 120, y: 68 }; //enough to cover 4k monitors? <_< for y in 0..BACKGROUND_MAX_SIZE { - gsa2.bgs[2].tiles[0][y] = 0x7408; + gsa2.bgs[2].tiles[0][y] = 0x7010; } - gsa2.bgs[2].tiles[0][0] = 0x7409; - gsa2.bgs[2].tiles[0][2] = 0x740a; + gsa2.bgs[2].tiles[0][BUT_SAVE] = 0x7211; + // layer buttons set up later + gsa2.bgs[2].tiles[0][BUT_EXIT] = 0x7212; let event_loop = EventLoop::new(); let size = LogicalSize::new(1280, 720); @@ -116,6 +168,10 @@ pub(crate) fn run_mapedit() { let mut right_down = false; let mut selected_tile = IVec2::ZERO; gsa2.bgs[1].tiles[0][0] = TILE_MARKER; + let mut state = State::Edit; + let mut current_layer = 0usize; + let mut all_layers = true; + update_layer_state(&mut gsa, &mut gsa2, current_layer, all_layers); event_loop.run(move |event, _, control_flow| { let mouse_pos = &mut mouse_pos; let tile_pos = &mut tile_pos; @@ -124,6 +180,9 @@ pub(crate) fn run_mapedit() { let middle_down = &mut middle_down; let right_down = &mut right_down; let selected_tile = &mut selected_tile; + let state = &mut state; + let current_layer = &mut current_layer; + let all_layers = &mut all_layers; match event { Event::WindowEvent { event, .. } => match event { @@ -143,6 +202,7 @@ pub(crate) fn run_mapedit() { gsa2.bgs[1].active = true; gsa.sprites[SPR_CURSOR].tile = EMPTY_TILE; gsa2.sprites[SPR_CURSOR].tile = TILE_CURSOR; + *state = State::SelectTile; } (winit::event::ElementState::Released, VirtualKeyCode::LShift) => { gsa.bgs[0].active = true; @@ -152,13 +212,46 @@ pub(crate) fn run_mapedit() { gsa2.bgs[1].active = false; gsa.sprites[SPR_CURSOR].tile = TILE_CURSOR; gsa2.sprites[SPR_CURSOR].tile = EMPTY_TILE; + *state = State::Edit; } _ => {} } } WindowEvent::MouseInput { state, button, .. } => match (state, button) { (winit::event::ElementState::Pressed, winit::event::MouseButton::Left) => { - *left_down = true; + if mouse_pos.x < 16 { + match (mouse_pos.y / 16) as usize { + BUT_SAVE => { + println!("saving"); + gsa.maps.maps.insert(1337, [Tilemap::from_bg(&gsa.bgs[0]), Tilemap::from_bg(&gsa.bgs[1]), Tilemap::from_bg(&gsa.bgs[2])]); + fs::write( + maps_path, + postcard::to_allocvec(&gsa.maps).unwrap(), + ) + .unwrap(); + } + BUT_LAYER1 => { + *current_layer = 0; + } + BUT_LAYER2 => { + *current_layer = 1; + } + BUT_LAYER3 => { + *current_layer = 2; + } + BUT_LAYERS => { + *all_layers = !*all_layers; + } + BUT_EXIT => { + println!("exit"); + *control_flow = ControlFlow::Exit; + } + _ => {} + } + update_layer_state(&mut gsa, &mut gsa2, *current_layer, *all_layers); + } else { + *left_down = true; + } } (winit::event::ElementState::Released, winit::event::MouseButton::Left) => { *left_down = false; @@ -184,16 +277,20 @@ pub(crate) fn run_mapedit() { }; let delta = new_pos - *mouse_pos; if *middle_down { - if gsa.bgs[0].active { - // normal mode - gsa.bgs[0].scroll -= delta; - gsa.bgs[1].scroll -= delta; - gsa.bgs[2].scroll -= delta; - } else { - // tile select mode - gsa2.bgs[0].scroll -= delta; - gsa2.bgs[1].scroll -= delta; - //gsa2.bgs[2].scroll -= delta; + match *state { + State::Edit => { + // normal mode + gsa.bgs[0].scroll -= delta; + gsa.bgs[1].scroll -= delta; + gsa.bgs[2].scroll -= delta; + } + State::SelectTile => { + // tile select mode + gsa2.bgs[0].scroll -= delta; + gsa2.bgs[1].scroll -= delta; + //gsa2.bgs[2].scroll -= delta; + } + _ => {} } } *mouse_pos = new_pos; @@ -214,42 +311,44 @@ pub(crate) fn run_mapedit() { }, Event::MainEventsCleared => { - if gsa.bgs[0].active { - // normal mode - if *left_down { - if tile_pos.x >= 0 - && tile_pos.y >= 0 - && tile_pos.x < BACKGROUND_MAX_SIZE as i32 - && tile_pos.y < BACKGROUND_MAX_SIZE as i32 - { - let tile = (selected_tile.x + (selected_tile.y << 8)) as u16; - gsa.bgs[0].tiles[tile_pos.x as usize][tile_pos.y as usize] = tile; - } - } else if *right_down { - if tile_pos.x >= 0 - && tile_pos.y >= 0 - && tile_pos.x < BACKGROUND_MAX_SIZE as i32 - && tile_pos.y < BACKGROUND_MAX_SIZE as i32 - { - gsa.bgs[0].tiles[tile_pos.x as usize][tile_pos.y as usize] = EMPTY_TILE; + match *state { + State::Edit => { + if *left_down { + if tile_pos.x >= 0 + && tile_pos.y >= 0 + && tile_pos.x < BACKGROUND_MAX_SIZE as i32 + && tile_pos.y < BACKGROUND_MAX_SIZE as i32 + { + let tile = (selected_tile.x + (selected_tile.y << 8)) as u16; + gsa.bgs[*current_layer].tiles[tile_pos.x as usize][tile_pos.y as usize] = tile; + } + } else if *right_down { + if tile_pos.x >= 0 + && tile_pos.y >= 0 + && tile_pos.x < BACKGROUND_MAX_SIZE as i32 + && tile_pos.y < BACKGROUND_MAX_SIZE as i32 + { + gsa.bgs[*current_layer].tiles[tile_pos.x as usize][tile_pos.y as usize] = + EMPTY_TILE; + } } } - } else { - // tile select mode - if *left_down { - if tile_pos2.x >= 0 - && tile_pos2.y >= 0 - && tile_pos2.x < BACKGROUND_MAX_SIZE as i32 - && tile_pos2.y < BACKGROUND_MAX_SIZE as i32 - { - gsa2.bgs[1].tiles[selected_tile.x as usize][selected_tile.y as usize] = - EMPTY_TILE; - *selected_tile = *tile_pos2; - gsa2.bgs[1].tiles[selected_tile.x as usize][selected_tile.y as usize] = - TILE_MARKER; + State::SelectTile => { + if *left_down { + if tile_pos2.x >= 0 + && tile_pos2.y >= 0 + && tile_pos2.x < BACKGROUND_MAX_SIZE as i32 + && tile_pos2.y < BACKGROUND_MAX_SIZE as i32 + { + gsa2.bgs[1].tiles[selected_tile.x as usize] + [selected_tile.y as usize] = EMPTY_TILE; + *selected_tile = *tile_pos2; + gsa2.bgs[1].tiles[selected_tile.x as usize] + [selected_tile.y as usize] = TILE_MARKER; + } } } - } + }; // render let size = window.inner_size(); @@ -272,31 +371,34 @@ pub(crate) fn run_mapedit() { data: &mut screen_buffer, size: screen_size, }; - if gsa.bgs[0].active { - let xs = -gsa.bgs[0].scroll.x - 1; - let ys = -gsa.bgs[0].scroll.y - 1; - let xe = xs + gsa.bgs[0].size.x * TILE_SIZE as i32 + 1; - let ye = ys + gsa.bgs[0].size.y * TILE_SIZE as i32 + 1; - if xs >= 16 && xs < screen_size.x { - let starty = (ys + 1).max(0); - let len = ye.min(screen_size.y) - starty; - screen.draw_vline(IVec2 { x: xs, y: starty }, len, 0xfc); - } - if xe >= 16 && xe < screen_size.x { - let starty = (ys + 1).max(0); - let len = ye.min(screen_size.y) - starty; - screen.draw_vline(IVec2 { x: xe, y: starty }, len, 0xfc); - } - if ys >= 0 && ys < screen_size.y { - let startx = (xs + 1).max(16); - let len = xe.min(screen_size.x) - startx; - screen.draw_hline(IVec2 { x: startx, y: ys }, len, 0xfc); - } - if ye >= 0 && ye < screen_size.y { - let startx = (xs + 1).max(16); - let len = xe.min(screen_size.x) - startx; - screen.draw_hline(IVec2 { x: startx, y: ye }, len, 0xfc); + match *state { + State::Edit => { + let xs = -gsa.bgs[0].scroll.x - 1; + let ys = -gsa.bgs[0].scroll.y - 1; + let xe = xs + gsa.bgs[0].size.x * TILE_SIZE as i32 + 1; + let ye = ys + gsa.bgs[0].size.y * TILE_SIZE as i32 + 1; + if xs >= 16 && xs < screen_size.x { + let starty = (ys + 1).max(0); + let len = ye.min(screen_size.y) - starty; + screen.draw_vline(IVec2 { x: xs, y: starty }, len, 0xfc); + } + if xe >= 16 && xe < screen_size.x { + let starty = (ys + 1).max(0); + let len = ye.min(screen_size.y) - starty; + screen.draw_vline(IVec2 { x: xe, y: starty }, len, 0xfc); + } + if ys >= 0 && ys < screen_size.y { + let startx = (xs + 1).max(16); + let len = xe.min(screen_size.x) - startx; + screen.draw_hline(IVec2 { x: startx, y: ys }, len, 0xfc); + } + if ye >= 0 && ye < screen_size.y { + let startx = (xs + 1).max(16); + let len = xe.min(screen_size.x) - startx; + screen.draw_hline(IVec2 { x: startx, y: ye }, len, 0xfc); + } } + _ => {} } render_to_window( diff --git a/src/maps.rs b/src/maps.rs new file mode 100644 index 0000000..71e8112 --- /dev/null +++ b/src/maps.rs @@ -0,0 +1,10 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +use crate::tilemap::Tilemap; + +#[derive(Default, Deserialize, Serialize)] +pub(crate) struct Maps { + pub(crate) maps: HashMap, +} diff --git a/src/run.rs b/src/run.rs index 33464b1..3ac66dc 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,5 +1,6 @@ use crate::buttons::{button_from_gilrs, button_from_scancode}; use crate::gsa_render_to_screen::{render_to_screen, render_to_window}; +use crate::maps::Maps; use crate::tileset::load_tileset; use crate::{ Buttons, Gsa, Sprite, FONT_BOLD, MAX_SPRITES, SCREEN_HEIGHT, SCREEN_WIDTH, TRANSPARENT, @@ -27,7 +28,12 @@ use winit::window::WindowBuilder; macro_rules! run { ($init: ident, $update: ident) => { fn main() { - run($init, $update, include_bytes!("gfx.gif")); + run( + $init, + $update, + include_bytes!("gfx.gif"), + include_bytes!("maps.dat"), + ); } }; } @@ -37,6 +43,7 @@ pub fn run( init_fn: fn(gsa: &mut Gsa) -> TGame, update_fn: fn(game: &mut TGame, gsa: &mut Gsa), image_data: &[u8], + maps_data: &[u8], ) { let (tileset, palette) = load_tileset(image_data); @@ -48,6 +55,7 @@ pub fn run( pressed: 0, released: 0, down: 0, + maps: postcard::from_bytes(maps_data).unwrap(), }; gsa.reset_bgs(); diff --git a/src/tilemap.rs b/src/tilemap.rs new file mode 100644 index 0000000..705b33d --- /dev/null +++ b/src/tilemap.rs @@ -0,0 +1,26 @@ +use glam::IVec2; +use serde::{Deserialize, Serialize}; + +use crate::Background; + +#[derive(Serialize, Deserialize)] +pub(crate) struct Tilemap { + pub(crate) data: Vec, + pub(crate) size: IVec2, +} + +impl Tilemap { + pub(crate) fn from_bg(bg: &Background) -> Self { + let mut data = vec![0u16; (bg.size.x * bg.size.y) as usize]; + for y in 0..bg.size.y as usize { + for x in 0..bg.size.x as usize { + data[x + y * bg.size.x as usize] = bg.tiles[x][y]; + } + } + + Self { + data, + size: bg.size, + } + } +}