diff --git a/CHANGLOG.md b/CHANGLOG.md index 80c6ee7..ba1f2ad 100644 --- a/CHANGLOG.md +++ b/CHANGLOG.md @@ -1,5 +1,6 @@ # 0.3.0 - added size to backgrounds +- added rotation to backgrounds - refactored api, background functions are now methods to background rather than gsa # 0.2.1 - 2023-07-27 diff --git a/Cargo.toml b/Cargo.toml index e7e9311..ec0b64a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ path-slash = "0.2.1" serde = {version = "1.0.181", features = ["derive"]} postcard = {version = "1.0.6", features = ["alloc"]} rayon = "1.7.0" +lazy_static = "1.4.0" [profile.release-dani] inherits = "release" diff --git a/examples/basic/gfx.gif b/examples/basic/gfx.gif index e1d5c10..6d28de2 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 cf54a37..17ebbec 100644 --- a/examples/basic/main.rs +++ b/examples/basic/main.rs @@ -1,5 +1,5 @@ use glam::IVec2; -use gsa::{run, Gsa, FACE_DOWN, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_MIDDLE, FACE_LEFT, FACE_RIGHT}; +use gsa::{run, Gsa, FACE_DOWN, FACE_LEFT, FACE_RIGHT, SCREEN_HEIGHT, SCREEN_MIDDLE, SCREEN_WIDTH}; struct Game {} @@ -14,10 +14,10 @@ fn init(gsa: &mut Gsa) -> Game { fn update(_game: &mut Game, gsa: &mut Gsa) { if gsa.button_down(FACE_LEFT) { - gsa.bg[0].rot = gsa.bg[0].rot.wrapping_add(1); + gsa.bg[0].rot = gsa.bg[0].rot.wrapping_add(1); } if gsa.button_down(FACE_RIGHT) { - gsa.bg[0].rot = gsa.bg[0].rot.wrapping_sub(1); + gsa.bg[0].rot = gsa.bg[0].rot.wrapping_sub(1); } //gsa.bg[0].scroll.x = (gsa.bg[0].scroll.x + 10) % 300; gsa.bg[0].scroll += gsa.input_dir(); diff --git a/examples/basic/maps.dat b/examples/basic/maps.dat new file mode 100644 index 0000000..6aea02c Binary files /dev/null and b/examples/basic/maps.dat differ diff --git a/src/background.rs b/src/background.rs index 3492a3d..a63bd99 100644 --- a/src/background.rs +++ b/src/background.rs @@ -29,8 +29,8 @@ impl Default for Background { half_tile: false, active: true, size: IVec2 { x: 19, y: 11 }, - origin: IVec2::ZERO, - rot: 0 + origin: IVec2::ZERO, + rot: 0, } } } diff --git a/src/gsa.rs b/src/gsa.rs index 1112322..82ff3cd 100644 --- a/src/gsa.rs +++ b/src/gsa.rs @@ -4,8 +4,7 @@ use crate::rgb::Rgb; use crate::sprite::Sprite; use crate::tilemap::Tilemap; use crate::{ - Buttons, BACKGROUND_MAX_SIZE, DPAD_DOWN, DPAD_LEFT, DPAD_RIGHT, DPAD_UP, EMPTY_TILE, - MAX_BACKGROUNDS, MAX_SPRITES, + Buttons, DPAD_DOWN, DPAD_LEFT, DPAD_RIGHT, DPAD_UP, EMPTY_TILE, MAX_BACKGROUNDS, MAX_SPRITES, }; use ascii::{AsciiChar, AsciiStr}; use glam::IVec2; diff --git a/src/gsa_render_to_screen.rs b/src/gsa_render_to_screen.rs index 6da798a..3b5619b 100644 --- a/src/gsa_render_to_screen.rs +++ b/src/gsa_render_to_screen.rs @@ -1,5 +1,5 @@ -use std::{usize, f32::consts::PI}; -use rayon::prelude::*; +use std::{f32::consts::PI, usize}; +//use rayon::prelude::*; use crate::{ Background, Gsa, Rgb, EMPTY_TILE, HALF_TILE_SIZE, MAX_BACKGROUNDS, TILESET_SIZE, TILE_SIZE, @@ -33,46 +33,46 @@ fn draw_tile( } } -fn render_bg(target: &mut [u8], map: &Background, tileset: &[u8], screen_size: IVec2) { - 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 = map - .size - .x - .min(startx + (screen_size.x / TILE_SIZE as i32 + 1) * tcmult); - let endy = map - .size - .y - .min(starty + (screen_size.y / TILE_SIZE as i32 + 1) * 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 != EMPTY_TILE { - draw_tile( - target, - IVec2 { - x: x * tilesize - map.scroll.x, - y: y * tilesize - map.scroll.y, - }, - tile, - tileset, - map.half_tile, - screen_size, - ); - } - } - } -} +// fn render_bg(target: &mut [u8], map: &Background, tileset: &[u8], screen_size: IVec2) { +// 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 = map +// .size +// .x +// .min(startx + (screen_size.x / TILE_SIZE as i32 + 1) * tcmult); +// let endy = map +// .size +// .y +// .min(starty + (screen_size.y / TILE_SIZE as i32 + 1) * 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 != EMPTY_TILE { +// draw_tile( +// target, +// IVec2 { +// x: x * tilesize - map.scroll.x, +// y: y * tilesize - map.scroll.y, +// }, +// tile, +// tileset, +// map.half_tile, +// screen_size, +// ); +// } +// } +// } +// } -fn render_bg2(target: &mut [u8], map: &Background, tileset: &[u8], screen_size: IVec2) { +fn render_bg(target: &mut [u8], map: &Background, tileset: &[u8], screen_size: IVec2) { let tilesize = if map.half_tile { HALF_TILE_SIZE } else { @@ -80,8 +80,7 @@ fn render_bg2(target: &mut [u8], map: &Background, tileset: &[u8], screen_size: } as i32; let a = map.rot as f32 / 256.0 * PI * 2.0; - - + let du = glam::Vec2::new(a.cos(), a.sin()); let dv = glam::Vec2::new(-a.sin(), a.cos()); let duv = Mat2::from_cols(du, dv); @@ -93,17 +92,21 @@ fn render_bg2(target: &mut [u8], map: &Background, tileset: &[u8], screen_size: .for_each({ |(screeny, row)| { for screenx in 0..screen_size.x { - let screen = duv * Vec2::new(screenx as f32 - map.origin.x as f32, screeny as f32 - map.origin.y as f32); + let screen = duv + * Vec2::new( + screenx as f32 - map.origin.x as f32, + screeny as f32 - map.origin.y as f32, + ); let x = screen.x.floor() as i32 + map.scroll.x; let y = screen.y.floor() as i32 + map.scroll.y; let tilex = x.div_euclid(tilesize); let tiley = y.div_euclid(tilesize); - //println!("{},{}", x,tilex); - //assert!(tilex >= 0); - // the addition is there because otherwise x might be below 0 - // which messes with the modulo - let pixx = (x+tilesize) % tilesize; - let pixy = (y+tilesize) % tilesize; + //println!("{},{}", x,tilex); + //assert!(tilex >= 0); + // the addition is there because otherwise x might be below 0 + // which messes with the modulo + let pixx = (x + tilesize) % tilesize; + let pixy = (y + tilesize) % tilesize; let tile = if tilex >= 0 && tiley >= 0 && tilex < map.size.x && tiley < map.size.y { map.tiles[tilex as usize][tiley as usize] @@ -113,7 +116,8 @@ fn render_bg2(target: &mut [u8], map: &Background, tileset: &[u8], screen_size: if tile != EMPTY_TILE { let tilesetx = tile as i32 % 0x100 * tilesize + pixx; let tilesety = tile as i32 / 0x100 * tilesize + pixy; - let p = tileset[tilesetx as usize + tilesety as usize * (TILESET_SIZE * TILE_SIZE)]; + let p = tileset + [tilesetx as usize + tilesety as usize * (TILESET_SIZE * TILE_SIZE)]; if p != TRANSPARENT { row[screenx as usize] = p; } @@ -126,7 +130,7 @@ fn render_bg2(target: &mut [u8], map: &Background, tileset: &[u8], screen_size: pub(crate) fn render_to_screen(target: &mut [u8], gsa: &Gsa, tileset: &[u8], screen_size: IVec2) { for i in 0..MAX_BACKGROUNDS { if gsa.bg[i].active { - render_bg2(target, &gsa.bg[i], tileset, screen_size); + render_bg(target, &gsa.bg[i], tileset, screen_size); } for sprite in &gsa.sprite { if sprite.tile != EMPTY_TILE && sprite.priority == i as u8 { diff --git a/src/lib.rs b/src/lib.rs index 0ceead4..88d189e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,10 +69,16 @@ pub const SCREEN_WIDTH: usize = 304; pub const SCREEN_HEIGHT: usize = 176; /// Screen size as vector (SCREEN_WIDTH,SCREEN_HEIGHT) -pub const SCREEN_SIZE: IVec2 = IVec2{x: SCREEN_WIDTH as i32, y: SCREEN_HEIGHT as i32}; +pub const SCREEN_SIZE: IVec2 = IVec2 { + x: SCREEN_WIDTH as i32, + y: SCREEN_HEIGHT as i32, +}; /// Center of screen as vector -pub const SCREEN_MIDDLE: IVec2 = IVec2{x: SCREEN_WIDTH as i32 / 2, y: SCREEN_HEIGHT as i32 / 2}; +pub const SCREEN_MIDDLE: IVec2 = IVec2 { + x: SCREEN_WIDTH as i32 / 2, + y: SCREEN_HEIGHT as i32 / 2, +}; /// X and y dimensions of maps in [Gsa::bgs] pub const BACKGROUND_MAX_SIZE: usize = 1024; diff --git a/src/mapedit.rs b/src/mapedit.rs deleted file mode 100644 index 294988e..0000000 --- a/src/mapedit.rs +++ /dev/null @@ -1,730 +0,0 @@ -use std::fs; -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::event::ElementState; -use winit::{ - dpi::LogicalSize, - event::{Event, VirtualKeyCode, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, -}; - -const SPR_CURSOR: usize = 0x01; -const TILE_CURSOR: u16 = 0x7110; -const TILE_MARKER: u16 = 0x7210; -const TILE_FRT: u16 = 0x7018; -const TILE_FRB: u16 = 0x7118; -const TILE_FRL: u16 = 0x7218; -const TILE_FRR: u16 = 0x7219; -const TILE_FRTL: u16 = 0x7019; -const TILE_FRTR: u16 = 0x701A; -const TILE_FRBL: u16 = 0x7119; -const TILE_FRBR: u16 = 0x711A; -const TILE_FRBG: u16 = 0x721A; -const TILE_INPUT: u16 = 0x7318; - -const TILE_BUT_YES: u16 = 0x7311; -const TILE_BUT_NO: u16 = 0x7312; -const TILE_BUT_CREATE: u16 = 0x7213; - -const BUT_SAVE: usize = 0; -const BUT_CREATE: usize = 1; -const BUT_LAYER1: usize = 3; -const BUT_LAYER2: usize = 4; -const BUT_LAYER3: usize = 5; -const BUT_LAYERS: usize = 6; -const BUT_EXIT: usize = 8; - -struct Surface<'a> { - pub data: &'a mut [u8], - pub size: IVec2, -} - -impl<'a> Surface<'a> { - fn draw_vline(&mut self, pos: IVec2, len: i32, color: u8) { - for i in 0..len { - self.data[(pos.x + (pos.y + i) * self.size.x) as usize] = color; - } - } - - fn draw_hline(&mut self, pos: IVec2, len: i32, color: u8) { - for i in 0..len { - self.data[(pos.x + i + pos.y * self.size.x) as usize] = color; - } - } -} - -#[derive(Copy, Clone)] -enum State { - Edit, - SelectTile, - NewMapDialog, -} - -fn key_to_num(key: VirtualKeyCode) -> u16 { - match key { - VirtualKeyCode::Key0 => 0x0, - VirtualKeyCode::Key1 => 0x1, - VirtualKeyCode::Key2 => 0x2, - VirtualKeyCode::Key3 => 0x3, - VirtualKeyCode::Key4 => 0x4, - VirtualKeyCode::Key5 => 0x5, - VirtualKeyCode::Key6 => 0x6, - VirtualKeyCode::Key7 => 0x7, - VirtualKeyCode::Key8 => 0x8, - VirtualKeyCode::Key9 => 0x9, - VirtualKeyCode::A => 0xa, - VirtualKeyCode::B => 0xb, - VirtualKeyCode::C => 0xc, - VirtualKeyCode::D => 0xd, - VirtualKeyCode::E => 0xe, - VirtualKeyCode::F => 0xf, - _ => 0x0, - } -} - -impl Background { - fn draw_frame(&mut self, pos: IVec2, size: IVec2) { - let px1 = pos.x as usize; - let py1 = pos.y as usize; - let sx = size.x as usize; - let sy = size.y as usize; - let px2 = px1 + sx - 1; - let py2 = py1 + sy - 1; - - self.tiles[px1][py1] = TILE_FRTL; - self.tiles[px1][py2] = TILE_FRBL; - self.tiles[px2][py1] = TILE_FRTR; - self.tiles[px2][py2] = TILE_FRBR; - - for x in (px1 + 1)..px2 { - self.tiles[x][py1] = TILE_FRT; - self.tiles[x][py2] = TILE_FRB; - } - - for y in (py1 + 1)..py2 { - self.tiles[px1][y] = TILE_FRL; - self.tiles[px2][y] = TILE_FRR; - } - for x in (px1 + 1)..px2 { - for y in (py1 + 1)..py2 { - self.tiles[x][y] = TILE_FRBG; - } - } - } -} - -fn draw_input_window(gsa: &mut Gsa, pos: IVec2, caption: &str) { - gsa.bg[2].draw_frame(pos, IVec2 { x: 7, y: 5 }); - gsa.bg[2].tiles[pos.x as usize + 3][pos.y as usize + 1] = TILE_INPUT; - gsa.bg[2].tiles[pos.x as usize + 4][pos.y as usize + 1] = TILE_INPUT; - gsa.bg[2].tiles[pos.x as usize + 1][pos.y as usize + 3] = TILE_BUT_YES; - gsa.bg[2].tiles[pos.x as usize + 5][pos.y as usize + 3] = TILE_BUT_NO; - gsa.write_string( - IVec2 { - x: pos.x * 2 + 2, - y: pos.y * 2 + 1, - }, - caption, - ); - gsa.write_string( - IVec2 { - x: pos.x * 2 + 3, - y: pos.y * 2 + 3, - }, - "ID:0000", - ); -} - -fn update_input_window(gsa: &mut Gsa, pos: IVec2, val: u16) { - gsa.write_string( - IVec2 { - x: pos.x * 2 + 6, - y: pos.y * 2 + 3, - }, - &format!("{:04x}", val), - ); -} - -fn set_state( - state: State, - gsa1: &mut Gsa, - gsa2: &mut Gsa, - current_layer: usize, - all_layers: bool, - current_map: u16, -) { - match state { - State::Edit => { - //update editing layers - if all_layers { - gsa1.bg[0].active = true; - gsa1.bg[1].active = true; - gsa1.bg[2].active = true; - } else { - gsa1.bg[0].active = current_layer == 0; - gsa1.bg[1].active = current_layer == 1; - gsa1.bg[2].active = current_layer == 2; - } - gsa2.bg[2].clear(); - gsa2.bg[2].clear(); - draw_toolbar(gsa1, gsa2, current_layer, all_layers, current_map); - - gsa2.bg[0].active = false; - gsa2.bg[1].active = false; - gsa1.sprite[SPR_CURSOR].priority = 3; - gsa1.sprite[SPR_CURSOR].tile = TILE_CURSOR; - gsa2.sprite[SPR_CURSOR].tile = EMPTY_TILE; - } - State::SelectTile => { - draw_toolbar(gsa1, gsa2, current_layer, all_layers, current_map); - gsa1.bg[0].active = false; - gsa1.bg[1].active = false; - gsa1.bg[2].active = false; - gsa2.bg[0].active = true; - gsa2.bg[1].active = true; - gsa1.sprite[SPR_CURSOR].tile = EMPTY_TILE; - gsa2.sprite[SPR_CURSOR].tile = TILE_CURSOR; - } - State::NewMapDialog => { - gsa1.bg[0].active = false; - gsa1.bg[1].active = false; - gsa1.bg[2].active = false; - gsa2.bg[0].active = false; - gsa2.bg[1].active = false; - gsa1.sprite[SPR_CURSOR].tile = EMPTY_TILE; - gsa2.sprite[SPR_CURSOR].tile = EMPTY_TILE; - draw_input_window(gsa2, IVec2 { x: 15, y: 9 }, "Create Map"); - } - } -} - -fn draw_toolbar( - gsa1: &mut Gsa, - gsa2: &mut Gsa, - current_layer: usize, - all_layers: bool, - current_map: u16, -) { - for y in 0..BACKGROUND_MAX_SIZE { - gsa2.bg[2].tiles[0][y] = 0x7010; - } - gsa2.bg[2].tiles[0][BUT_SAVE] = 0x7211; - gsa2.bg[2].tiles[0][BUT_CREATE] = TILE_BUT_CREATE; - gsa2.bg[2].tiles[0][BUT_EXIT] = 0x7212; - - //layer buttons - gsa2.bg[2].tiles[0][BUT_LAYER1] = if current_layer == 0 { 0x7111 } else { 0x7011 }; - gsa2.bg[2].tiles[0][BUT_LAYER2] = if current_layer == 1 { 0x7112 } else { 0x7012 }; - gsa2.bg[2].tiles[0][BUT_LAYER3] = if current_layer == 2 { 0x7113 } else { 0x7013 }; - gsa2.bg[2].tiles[0][BUT_LAYERS] = if all_layers { 0x7114 } else { 0x7014 }; - - gsa2.bg[2].size = IVec2 { x: 120, y: 68 }; //enough to cover 4k monitors? <_< - gsa2.bg[3].size = IVec2 { x: 240, y: 136 }; - - gsa2.write_string_vertical( - IVec2 { - x: 0, - y: BUT_EXIT as i32 * 2 + 3, - }, - &format!("MAP-{:04X}", current_map), - ); - - gsa2.write_string_vertical( - IVec2 { - x: 0, - y: BUT_EXIT as i32 * 2 + 12, - }, - &format!("W-{}", gsa1.bg[0].size.x), - ); - - gsa2.write_string_vertical( - IVec2 { - x: 1, - y: BUT_EXIT as i32 * 2 + 12, - }, - &format!("H-{}", gsa1.bg[0].size.y), - ); -} - -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 gsa1 = Gsa { - sprite: [Sprite::default(); MAX_SPRITES], - palette, - bg: Default::default(), - font: FONT_BOLD, - pressed: 0, - released: 0, - down: 0, - maps, - str_bg: 3, - }; - - gsa1.reset_bgs(); - gsa1.reset_sprites(); - - if gsa1.map_exists(0) { - gsa1.load_map(0); - } - - for i in 0..3 { - gsa1.bg[i].scroll = IVec2 { - x: -16 - 32, - y: -32, - }; - } - - let mut gsa2 = Gsa { - sprite: [Sprite::default(); MAX_SPRITES], - palette, - bg: Default::default(), - font: FONT_BOLD, - pressed: 0, - released: 0, - down: 0, - maps: Maps::default(), - str_bg: 3, - }; - gsa2.reset_bgs(); - gsa2.reset_sprites(); - for i in 0..2 { - gsa2.bg[i].scroll = IVec2 { - x: -16 - 32, - y: -32, - }; - } - - for y in 0..TILESET_SIZE { - for x in 0..TILESET_SIZE { - gsa2.bg[0].tiles[x][y] = (x + (y << 8)) as u16; - } - } - //draw_input_window(&mut gsa2, IVec2 { x: 5, y: 3 }, "Create Map"); - - let event_loop = EventLoop::new(); - let size = LogicalSize::new(1280, 720); - let window = WindowBuilder::new() - .with_title(format!( - "Game Skunk Advance Map Editor v{}", - crate_version!() - )) - .with_inner_size(size) - .build(&event_loop) - .unwrap(); - - let context = unsafe { softbuffer::Context::new(&window) }.unwrap(); - let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap(); - window.request_redraw(); - let mut mouse_pos = IVec2::ZERO; - let mut tile_pos = IVec2::ZERO; - let mut tile_pos2 = IVec2::ZERO; - let mut left_down = false; - let mut middle_down = false; - let mut right_down = false; - let mut selected_tile = IVec2::ZERO; - gsa2.bg[1].tiles[0][0] = TILE_MARKER; - let mut current_layer = 0usize; - let mut all_layers = true; - let mut state = State::Edit; - let mut input_buf = 0u16; - let mut current_map = 0; - set_state( - State::Edit, - &mut gsa1, - &mut gsa2, - current_layer, - all_layers, - current_map, - ); - - event_loop.run(move |event, _, control_flow| { - let mouse_pos = &mut mouse_pos; - let tile_pos = &mut tile_pos; - let tile_pos2 = &mut tile_pos2; - let left_down = &mut left_down; - 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; - let input_buf = &mut input_buf; - let current_map = &mut current_map; - - match event { - Event::WindowEvent { event, .. } => match event { - WindowEvent::KeyboardInput { input, .. } => { - let vk = input.virtual_keycode.unwrap_or(VirtualKeyCode::F24); - match vk { - VirtualKeyCode::Escape => { - //*control_flow = ControlFlow::Exit; - } - VirtualKeyCode::LShift => { - if input.state == ElementState::Pressed { - *state = State::SelectTile; - set_state( - *state, - &mut gsa1, - &mut gsa2, - *current_layer, - *all_layers, - *current_map, - ); - } else { - *state = State::Edit; - set_state( - *state, - &mut gsa1, - &mut gsa2, - *current_layer, - *all_layers, - *current_map, - ); - } - } - VirtualKeyCode::Key0 - | VirtualKeyCode::Key1 - | VirtualKeyCode::Key2 - | VirtualKeyCode::Key3 - | VirtualKeyCode::Key4 - | VirtualKeyCode::Key5 - | VirtualKeyCode::Key6 - | VirtualKeyCode::Key7 - | VirtualKeyCode::Key8 - | VirtualKeyCode::Key9 - | VirtualKeyCode::A - | VirtualKeyCode::B - | VirtualKeyCode::C - | VirtualKeyCode::D - | VirtualKeyCode::E - | VirtualKeyCode::F => { - if input.state == ElementState::Pressed { - *input_buf = ((*input_buf & 0xFFF) << 4) | key_to_num(vk); - update_input_window(&mut gsa2, IVec2 { x: 15, y: 9 }, *input_buf); - } - } - _ => {} - } - } - WindowEvent::MouseInput { - state: but_state, - button, - .. - } => match (but_state, button) { - (winit::event::ElementState::Pressed, winit::event::MouseButton::Left) => { - if mouse_pos.x < 16 { - match (mouse_pos.y / 16) as usize { - BUT_SAVE => { - println!("saving"); - gsa1.store_map(*current_map); - fs::write( - maps_path, - postcard::to_allocvec(&gsa1.maps).unwrap(), - ) - .unwrap(); - } - BUT_CREATE => { - *state = State::NewMapDialog; - *input_buf = *current_map; - set_state( - *state, - &mut gsa1, - &mut gsa2, - *current_layer, - *all_layers, - *current_map, - ); - update_input_window( - &mut gsa2, - IVec2 { x: 15, y: 9 }, - *input_buf, - ); - } - BUT_LAYER1 => { - *current_layer = 0; - set_state( - *state, - &mut gsa1, - &mut gsa2, - *current_layer, - *all_layers, - *current_map, - ); - } - BUT_LAYER2 => { - *current_layer = 1; - set_state( - *state, - &mut gsa1, - &mut gsa2, - *current_layer, - *all_layers, - *current_map, - ); - } - BUT_LAYER3 => { - *current_layer = 2; - set_state( - *state, - &mut gsa1, - &mut gsa2, - *current_layer, - *all_layers, - *current_map, - ); - } - BUT_LAYERS => { - *all_layers = !*all_layers; - set_state( - *state, - &mut gsa1, - &mut gsa2, - *current_layer, - *all_layers, - *current_map, - ); - } - BUT_EXIT => { - println!("exit"); - *control_flow = ControlFlow::Exit; - } - _ => {} - } - } else { - match *state { - State::NewMapDialog => { - //todo: not hardcode? - if mouse_pos.y / 16 == 12 { - if mouse_pos.x / 16 == 16 { - println!("yes"); - gsa1.store_map(*current_map); - *current_map = *input_buf; - if gsa1.map_exists(*current_map) { - gsa1.load_map(*current_map); - } else { - //todo: not reset scrolling - gsa1.reset_bgs(); - } - *state = State::Edit; - set_state( - *state, - &mut gsa1, - &mut gsa2, - *current_layer, - *all_layers, - *current_map, - ); - } else if mouse_pos.x / 16 == 20 { - println!("no"); - *state = State::Edit; - set_state( - *state, - &mut gsa1, - &mut gsa2, - *current_layer, - *all_layers, - *current_map, - ); - } - } - } - _ => { - *left_down = true; - } - } - } - } - (winit::event::ElementState::Released, winit::event::MouseButton::Left) => { - *left_down = false; - } - (winit::event::ElementState::Pressed, winit::event::MouseButton::Middle) => { - *middle_down = true; - } - (winit::event::ElementState::Released, winit::event::MouseButton::Middle) => { - *middle_down = false; - } - (winit::event::ElementState::Pressed, winit::event::MouseButton::Right) => { - *right_down = true; - } - (winit::event::ElementState::Released, winit::event::MouseButton::Right) => { - *right_down = false; - } - _ => {} - }, - WindowEvent::CursorMoved { position, .. } => { - let new_pos = IVec2 { - x: position.x as i32 / 2, - y: position.y as i32 / 2, - }; - let delta = new_pos - *mouse_pos; - if *middle_down { - match *state { - State::Edit => { - // normal mode - gsa1.bg[0].scroll -= delta; - gsa1.bg[1].scroll -= delta; - gsa1.bg[2].scroll -= delta; - } - State::SelectTile => { - // tile select mode - gsa2.bg[0].scroll -= delta; - gsa2.bg[1].scroll -= delta; - //gsa2.bgs[2].scroll -= delta; - } - _ => {} - } - } - *mouse_pos = new_pos; - *tile_pos = IVec2 { - x: (new_pos.x + gsa1.bg[0].scroll.x).div_euclid(TILE_SIZE as i32), - y: (new_pos.y + gsa1.bg[0].scroll.y).div_euclid(TILE_SIZE as i32), - }; - *tile_pos2 = IVec2 { - x: (new_pos.x + gsa2.bg[0].scroll.x).div_euclid(TILE_SIZE as i32), - y: (new_pos.y + gsa2.bg[0].scroll.y).div_euclid(TILE_SIZE as i32), - }; - let cursor_pos = *tile_pos * TILE_SIZE as i32 - gsa1.bg[0].scroll; - let cursor_pos2 = *tile_pos2 * TILE_SIZE as i32 - gsa2.bg[0].scroll; - gsa1.sprite[SPR_CURSOR].pos = cursor_pos; - gsa2.sprite[SPR_CURSOR].pos = cursor_pos2; - } - _ => {} - }, - - Event::MainEventsCleared => { - 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; - gsa1.bg[*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 - { - gsa1.bg[*current_layer].tiles[tile_pos.x as usize] - [tile_pos.y as usize] = EMPTY_TILE; - } - } - } - 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.bg[1].tiles[selected_tile.x as usize] - [selected_tile.y as usize] = EMPTY_TILE; - *selected_tile = *tile_pos2; - gsa2.bg[1].tiles[selected_tile.x as usize] - [selected_tile.y as usize] = TILE_MARKER; - } - } - } - _ => {} - }; - - // render - let size = window.inner_size(); - surface - .resize( - NonZeroU32::new(size.width).unwrap(), - NonZeroU32::new(size.height).unwrap(), - ) - .unwrap(); - let mut screen_buffer = - vec![TRANSPARENT; size.width as usize * size.height as usize / 4]; - let mut window_buffer = surface.buffer_mut().unwrap(); - let screen_size = IVec2 { - x: size.width as i32 / 2, - y: size.height as i32 / 2, - }; - render_to_screen(&mut screen_buffer, &gsa1, &tileset, screen_size); - render_to_screen(&mut screen_buffer, &gsa2, &tileset, screen_size); - let mut screen = Surface { - data: &mut screen_buffer, - size: screen_size, - }; - match *state { - State::Edit => { - let xs = -gsa1.bg[0].scroll.x - 1; - let ys = -gsa1.bg[0].scroll.y - 1; - let xe = xs + gsa1.bg[0].size.x * TILE_SIZE as i32 + 1; - let ye = ys + gsa1.bg[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( - &mut window_buffer, - &mut screen_buffer, - &palette, - IVec2 { - x: size.width as i32, - y: size.height as i32, - }, - IVec2 { - x: size.width as i32 / 2, - y: size.height as i32 / 2, - }, - 2, - 0, - 0, - ); - window_buffer.present().unwrap(); - } - _ => {} - } - }); -} diff --git a/src/mapedit/background.rs b/src/mapedit/background.rs new file mode 100644 index 0000000..55a76d9 --- /dev/null +++ b/src/mapedit/background.rs @@ -0,0 +1,33 @@ +use crate::mapedit::*; +use crate::Background; + +impl Background { + pub(crate) fn draw_frame(&mut self, pos: IVec2, size: IVec2) { + let px1 = pos.x as usize; + let py1 = pos.y as usize; + let sx = size.x as usize; + let sy = size.y as usize; + let px2 = px1 + sx - 1; + let py2 = py1 + sy - 1; + + self.tiles[px1][py1] = TILE_FRTL; + self.tiles[px1][py2] = TILE_FRBL; + self.tiles[px2][py1] = TILE_FRTR; + self.tiles[px2][py2] = TILE_FRBR; + + for x in (px1 + 1)..px2 { + self.tiles[x][py1] = TILE_FRT; + self.tiles[x][py2] = TILE_FRB; + } + + for y in (py1 + 1)..py2 { + self.tiles[px1][y] = TILE_FRL; + self.tiles[px2][y] = TILE_FRR; + } + for x in (px1 + 1)..px2 { + for y in (py1 + 1)..py2 { + self.tiles[x][y] = TILE_FRBG; + } + } + } +} diff --git a/src/mapedit/constants.rs b/src/mapedit/constants.rs new file mode 100644 index 0000000..b4bf9b7 --- /dev/null +++ b/src/mapedit/constants.rs @@ -0,0 +1,25 @@ +pub const SPR_CURSOR: usize = 0x01; +pub const TILE_CURSOR: u16 = 0x7110; +pub const TILE_MARKER: u16 = 0x7210; +pub const TILE_FRT: u16 = 0x7018; +pub const TILE_FRB: u16 = 0x7118; +pub const TILE_FRL: u16 = 0x7218; +pub const TILE_FRR: u16 = 0x7219; +pub const TILE_FRTL: u16 = 0x7019; +pub const TILE_FRTR: u16 = 0x701A; +pub const TILE_FRBL: u16 = 0x7119; +pub const TILE_FRBR: u16 = 0x711A; +pub const TILE_FRBG: u16 = 0x721A; +pub const TILE_INPUT: u16 = 0x7318; + +pub const TILE_BUT_YES: u16 = 0x7311; +pub const TILE_BUT_NO: u16 = 0x7312; +pub const TILE_BUT_CREATE: u16 = 0x7213; + +pub const BUT_SAVE: usize = 0; +pub const BUT_CREATE: usize = 1; +pub const BUT_LAYER1: usize = 3; +pub const BUT_LAYER2: usize = 4; +pub const BUT_LAYER3: usize = 5; +pub const BUT_LAYERS: usize = 6; +pub const BUT_EXIT: usize = 8; diff --git a/src/mapedit/mod.rs b/src/mapedit/mod.rs new file mode 100644 index 0000000..b9354ff --- /dev/null +++ b/src/mapedit/mod.rs @@ -0,0 +1,449 @@ +mod background; +mod state; +use state::State; +mod stuff; +use stuff::Stuff; +mod constants; +use constants::*; +mod surface; +use surface::Surface; +mod util; +use util::*; + +use std::fs; +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::tileset::*; +use crate::*; +use clap::crate_version; +use glam::IVec2; +use winit::dpi::PhysicalSize; +use winit::event::ElementState; +use winit::{ + dpi::LogicalSize, + event::{Event, VirtualKeyCode, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; + + +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 gsa1 = Gsa { + sprite: [Sprite::default(); MAX_SPRITES], + palette, + bg: Default::default(), + font: FONT_BOLD, + pressed: 0, + released: 0, + down: 0, + maps, + str_bg: 3, + }; + + gsa1.reset_bgs(); + gsa1.reset_sprites(); + + if gsa1.map_exists(0) { + gsa1.load_map(0); + } + + for i in 0..3 { + gsa1.bg[i].scroll = IVec2 { + x: -16 - 32, + y: -32, + }; + } + + let mut gsa2 = Gsa { + sprite: [Sprite::default(); MAX_SPRITES], + palette, + bg: Default::default(), + font: FONT_BOLD, + pressed: 0, + released: 0, + down: 0, + maps: Maps::default(), + str_bg: 3, + }; + gsa2.reset_bgs(); + gsa2.reset_sprites(); + for i in 0..2 { + gsa2.bg[i].scroll = IVec2 { + x: -16 - 32, + y: -32, + }; + } + + for y in 0..TILESET_SIZE { + for x in 0..TILESET_SIZE { + gsa2.bg[0].tiles[x][y] = (x + (y << 8)) as u16; + } + } + //draw_input_window(&mut gsa2, IVec2 { x: 5, y: 3 }, "Create Map"); + + let mut stuff = Stuff { + gsa1, + gsa2, + mouse_pos: IVec2::ZERO, + tile_pos: IVec2::ZERO, + tile_pos2: IVec2::ZERO, + left_down: false, + middle_down: false, + right_down: false, + selected_tile: IVec2::ZERO, + current_layer: 0, + all_layers: true, + state: State::Edit, + input_buf: 0, + current_map: 0, + window_tile_size: IVec2::new(1280 / 32, 640 / 32), + }; + + let event_loop = EventLoop::new(); + let size = LogicalSize::new(1280, 640); + let window = WindowBuilder::new() + .with_title(format!( + "Game Skunk Advance Map Editor v{}", + crate_version!() + )) + .with_inner_size(size) + .with_resize_increments(LogicalSize::new(32, 32)) + .build(&event_loop) + .unwrap(); + + let context = unsafe { softbuffer::Context::new(&window) }.unwrap(); + let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap(); + window.request_redraw(); + + stuff.gsa2.bg[1].tiles[0][0] = TILE_MARKER; + stuff.set_state(State::Edit); + + event_loop.run(move |event, _, control_flow| { + //let stuff = &mut stuff; + + match event { + Event::WindowEvent { event, .. } => match event { + WindowEvent::Resized(size) => { + if size.width % 32 != 0 || size.height % 32 != 0 { + let tw = (size.width as f32 / 32.0).round(); + let th = (size.height as f32 / 32.0).round(); + let w = (tw * 32.0) as u32; + let h = (th * 32.0) as u32; + stuff.window_tile_size.x = tw as i32; + stuff.window_tile_size.y = th as i32; + stuff.draw_toolbar(); + let pos = window.outer_position().unwrap(); + window.set_inner_size(PhysicalSize::new(w, h)); + window.set_outer_position(pos); + } + } + WindowEvent::KeyboardInput { input, .. } => { + let vk = input.virtual_keycode.unwrap_or(VirtualKeyCode::F24); + match vk { + VirtualKeyCode::Escape => { + //*control_flow = ControlFlow::Exit; + } + VirtualKeyCode::LShift => { + if input.state == ElementState::Pressed { + stuff.set_state(State::SelectTile); + } else { + stuff.set_state(State::Edit); + } + } + VirtualKeyCode::Key0 + | VirtualKeyCode::Key1 + | VirtualKeyCode::Key2 + | VirtualKeyCode::Key3 + | VirtualKeyCode::Key4 + | VirtualKeyCode::Key5 + | VirtualKeyCode::Key6 + | VirtualKeyCode::Key7 + | VirtualKeyCode::Key8 + | VirtualKeyCode::Key9 + | VirtualKeyCode::A + | VirtualKeyCode::B + | VirtualKeyCode::C + | VirtualKeyCode::D + | VirtualKeyCode::E + | VirtualKeyCode::F => { + if input.state == ElementState::Pressed { + stuff.input_buf = ((stuff.input_buf & 0xFFF) << 4) | key_to_num(vk); + stuff.update_input_window(IVec2 { x: 15, y: 9 }, stuff.input_buf); + } + } + _ => {} + } + } + WindowEvent::MouseInput { + state: but_state, + button, + .. + } => match (but_state, button) { + (winit::event::ElementState::Pressed, winit::event::MouseButton::Left) => { + if stuff.mouse_pos.x < 16 { + match (stuff.mouse_pos.y / 16) as usize { + BUT_SAVE => { + println!("saving"); + stuff.gsa1.store_map(stuff.current_map); + fs::write( + maps_path, + postcard::to_allocvec(&stuff.gsa1.maps).unwrap(), + ) + .unwrap(); + } + BUT_CREATE => { + stuff.input_buf = stuff.current_map; + stuff.set_state(State::NewMapDialog); + stuff.update_input_window( + IVec2 { x: 15, y: 9 }, + stuff.input_buf, + ); + } + BUT_LAYER1 => { + stuff.current_layer = 0; + stuff.set_state(stuff.state); + } + BUT_LAYER2 => { + stuff.current_layer = 1; + stuff.set_state(stuff.state); + } + BUT_LAYER3 => { + stuff.current_layer = 2; + stuff.set_state(stuff.state); + } + BUT_LAYERS => { + stuff.all_layers = !stuff.all_layers; + stuff.set_state(stuff.state); + } + BUT_EXIT => { + println!("exit"); + *control_flow = ControlFlow::Exit; + } + _ => {} + } + } else { + match stuff.state { + State::NewMapDialog => { + //todo: not hardcode? + if stuff.mouse_pos.y / 16 == 12 { + if stuff.mouse_pos.x / 16 == 16 { + println!("yes"); + stuff.gsa1.store_map(stuff.current_map); + stuff.current_map = stuff.input_buf; + if stuff.gsa1.map_exists(stuff.current_map) { + stuff.gsa1.load_map(stuff.current_map); + } else { + //todo: not reset scrolling + stuff.gsa1.reset_bgs(); + } + stuff.set_state(State::Edit); + } else if stuff.mouse_pos.x / 16 == 20 { + println!("no"); + stuff.set_state(State::Edit); + } + } + } + _ => { + stuff.left_down = true; + } + } + } + } + (winit::event::ElementState::Released, winit::event::MouseButton::Left) => { + stuff.left_down = false; + } + (winit::event::ElementState::Pressed, winit::event::MouseButton::Middle) => { + stuff.middle_down = true; + } + (winit::event::ElementState::Released, winit::event::MouseButton::Middle) => { + stuff.middle_down = false; + } + (winit::event::ElementState::Pressed, winit::event::MouseButton::Right) => { + stuff.right_down = true; + } + (winit::event::ElementState::Released, winit::event::MouseButton::Right) => { + stuff.right_down = false; + } + _ => {} + }, + WindowEvent::CursorMoved { position, .. } => { + let new_pos = IVec2 { + x: position.x as i32 / 2, + y: position.y as i32 / 2, + }; + let delta = new_pos - stuff.mouse_pos; + if stuff.middle_down { + match stuff.state { + State::Edit => { + // normal mode + stuff.gsa1.bg[0].scroll -= delta; + stuff.gsa1.bg[1].scroll -= delta; + stuff.gsa1.bg[2].scroll -= delta; + } + State::SelectTile => { + // tile select mode + stuff.gsa2.bg[0].scroll -= delta; + stuff.gsa2.bg[1].scroll -= delta; + //gsa2.bgs[2].scroll -= delta; + } + _ => {} + } + } + stuff.mouse_pos = new_pos; + let new_tile_pos = + IVec2 { + x: (new_pos.x + stuff.gsa1.bg[0].scroll.x).div_euclid(TILE_SIZE as i32), + y: (new_pos.y + stuff.gsa1.bg[0].scroll.y).div_euclid(TILE_SIZE as i32), + }; + let tile_pos_changed = stuff.tile_pos != new_tile_pos; + stuff.tile_pos = new_tile_pos; + stuff.tile_pos2 = IVec2 { + x: (new_pos.x + stuff.gsa2.bg[0].scroll.x).div_euclid(TILE_SIZE as i32), + y: (new_pos.y + stuff.gsa2.bg[0].scroll.y).div_euclid(TILE_SIZE as i32), + }; + let cursor_pos = stuff.tile_pos * TILE_SIZE as i32 - stuff.gsa1.bg[0].scroll; + let cursor_pos2 = stuff.tile_pos2 * TILE_SIZE as i32 - stuff.gsa2.bg[0].scroll; + stuff.gsa1.sprite[SPR_CURSOR].pos = cursor_pos; + stuff.gsa2.sprite[SPR_CURSOR].pos = cursor_pos2; + if tile_pos_changed { + stuff.update_status_bar(); + } + } + _ => {} + }, + + Event::MainEventsCleared => { + match stuff.state { + State::Edit => { + if stuff.left_down { + if stuff.tile_pos.x >= 0 + && stuff.tile_pos.y >= 0 + && stuff.tile_pos.x < BACKGROUND_MAX_SIZE as i32 + && stuff.tile_pos.y < BACKGROUND_MAX_SIZE as i32 + { + let tile = + (stuff.selected_tile.x + (stuff.selected_tile.y << 8)) as u16; + stuff.gsa1.bg[stuff.current_layer].tiles + [stuff.tile_pos.x as usize] + [stuff.tile_pos.y as usize] = tile; + } + } else if stuff.right_down { + if stuff.tile_pos.x >= 0 + && stuff.tile_pos.y >= 0 + && stuff.tile_pos.x < BACKGROUND_MAX_SIZE as i32 + && stuff.tile_pos.y < BACKGROUND_MAX_SIZE as i32 + { + stuff.gsa1.bg[stuff.current_layer].tiles + [stuff.tile_pos.x as usize] + [stuff.tile_pos.y as usize] = EMPTY_TILE; + } + } + } + State::SelectTile => { + if stuff.left_down { + if stuff.tile_pos2.x >= 0 + && stuff.tile_pos2.y >= 0 + && stuff.tile_pos2.x < BACKGROUND_MAX_SIZE as i32 + && stuff.tile_pos2.y < BACKGROUND_MAX_SIZE as i32 + { + stuff.gsa2.bg[1].tiles[stuff.selected_tile.x as usize] + [stuff.selected_tile.y as usize] = EMPTY_TILE; + stuff.selected_tile = stuff.tile_pos2; + stuff.gsa2.bg[1].tiles[stuff.selected_tile.x as usize] + [stuff.selected_tile.y as usize] = TILE_MARKER; + } + } + } + _ => {} + }; + + // render + let size = window.inner_size(); + surface + .resize( + NonZeroU32::new(size.width).unwrap(), + NonZeroU32::new(size.height).unwrap(), + ) + .unwrap(); + let mut screen_buffer = + vec![TRANSPARENT; size.width as usize * size.height as usize / 4]; + let mut window_buffer = surface.buffer_mut().unwrap(); + let screen_size = IVec2 { + x: size.width as i32 / 2, + y: size.height as i32 / 2, + }; + render_to_screen(&mut screen_buffer, &stuff.gsa1, &tileset, screen_size); + render_to_screen(&mut screen_buffer, &stuff.gsa2, &tileset, screen_size); + let mut screen = Surface { + data: &mut screen_buffer, + size: screen_size, + }; + match stuff.state { + State::Edit => { + let xs = -stuff.gsa1.bg[0].scroll.x - 1; + let ys = -stuff.gsa1.bg[0].scroll.y - 1; + let xe = xs + stuff.gsa1.bg[0].size.x * TILE_SIZE as i32 + 1; + let ye = ys + stuff.gsa1.bg[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( + &mut window_buffer, + &mut screen_buffer, + &palette, + IVec2 { + x: size.width as i32, + y: size.height as i32, + }, + IVec2 { + x: size.width as i32 / 2, + y: size.height as i32 / 2, + }, + 2, + 0, + 0, + ); + window_buffer.present().unwrap(); + } + _ => {} + } + }); +} diff --git a/src/mapedit/state.rs b/src/mapedit/state.rs new file mode 100644 index 0000000..c605fb5 --- /dev/null +++ b/src/mapedit/state.rs @@ -0,0 +1,8 @@ +use super::stuff::Stuff; + +#[derive(Copy, Clone)] +pub enum State { + Edit, + SelectTile, + NewMapDialog, +} diff --git a/src/mapedit/stuff.rs b/src/mapedit/stuff.rs new file mode 100644 index 0000000..cff7b85 --- /dev/null +++ b/src/mapedit/stuff.rs @@ -0,0 +1,180 @@ +use super::constants::*; +use super::state::State; +use crate::gsa::Gsa; +use crate::*; +use glam::IVec2; + +pub struct Stuff { + pub gsa1: Gsa, + pub gsa2: Gsa, + pub mouse_pos: IVec2, + pub tile_pos: IVec2, + pub tile_pos2: IVec2, + pub left_down: bool, + pub middle_down: bool, + pub right_down: bool, + pub selected_tile: IVec2, + pub current_layer: usize, + pub all_layers: bool, + pub state: State, + pub input_buf: u16, + pub current_map: u16, + pub window_tile_size: IVec2, +} + +impl Stuff { + pub fn draw_input_window(&mut self, pos: IVec2, caption: &str) { + self.gsa2.bg[2].draw_frame(pos, IVec2 { x: 7, y: 5 }); + self.gsa2.bg[2].tiles[pos.x as usize + 3][pos.y as usize + 1] = TILE_INPUT; + self.gsa2.bg[2].tiles[pos.x as usize + 4][pos.y as usize + 1] = TILE_INPUT; + self.gsa2.bg[2].tiles[pos.x as usize + 1][pos.y as usize + 3] = TILE_BUT_YES; + self.gsa2.bg[2].tiles[pos.x as usize + 5][pos.y as usize + 3] = TILE_BUT_NO; + self.gsa2.write_string( + IVec2 { + x: pos.x * 2 + 2, + y: pos.y * 2 + 1, + }, + caption, + ); + self.gsa2.write_string( + IVec2 { + x: pos.x * 2 + 3, + y: pos.y * 2 + 3, + }, + "ID:0000", + ); + } + + pub fn draw_toolbar(&mut self) { + self.gsa2.bg[2].clear(); + self.gsa2.bg[3].clear(); + + //vertical + for y in 0..(self.window_tile_size.y - 1) { + self.gsa2.bg[2].tiles[0][y as usize] = 0x7010; + } + self.gsa2.bg[2].tiles[0][BUT_SAVE] = 0x7211; + self.gsa2.bg[2].tiles[0][BUT_CREATE] = TILE_BUT_CREATE; + self.gsa2.bg[2].tiles[0][BUT_EXIT] = 0x7212; + + //layer buttons + self.gsa2.bg[2].tiles[0][BUT_LAYER1] = if self.current_layer == 0 { + 0x7111 + } else { + 0x7011 + }; + self.gsa2.bg[2].tiles[0][BUT_LAYER2] = if self.current_layer == 1 { + 0x7112 + } else { + 0x7012 + }; + self.gsa2.bg[2].tiles[0][BUT_LAYER3] = if self.current_layer == 2 { + 0x7113 + } else { + 0x7013 + }; + self.gsa2.bg[2].tiles[0][BUT_LAYERS] = if self.all_layers { 0x7114 } else { 0x7014 }; + + self.gsa2.bg[2].size = IVec2 { x: 120, y: 68 }; //enough to cover 4k monitors? <_< + self.gsa2.bg[3].size = IVec2 { x: 240, y: 136 }; + + //horizontal + let bottom = self.window_tile_size.y as usize - 1; + self.gsa2.bg[2].tiles[0][bottom] = 0x7410; + for x in 1..self.window_tile_size.x { + self.gsa2.bg[2].tiles[x as usize][bottom] = 0x7310; + } + self.update_status_bar(); + } + + pub fn set_state(&mut self, state: State) { + self.state = state; + match state { + State::Edit => { + //update editing layers + if self.all_layers { + self.gsa1.bg[0].active = true; + self.gsa1.bg[1].active = true; + self.gsa1.bg[2].active = true; + } else { + self.gsa1.bg[0].active = self.current_layer == 0; + self.gsa1.bg[1].active = self.current_layer == 1; + self.gsa1.bg[2].active = self.current_layer == 2; + } + self.draw_toolbar(); + + self.gsa2.bg[0].active = false; + self.gsa2.bg[1].active = false; + self.gsa1.sprite[SPR_CURSOR].priority = 3; + self.gsa1.sprite[SPR_CURSOR].tile = TILE_CURSOR; + self.gsa2.sprite[SPR_CURSOR].tile = EMPTY_TILE; + } + State::SelectTile => { + self.draw_toolbar(); + self.gsa1.bg[0].active = false; + self.gsa1.bg[1].active = false; + self.gsa1.bg[2].active = false; + self.gsa2.bg[0].active = true; + self.gsa2.bg[1].active = true; + self.gsa1.sprite[SPR_CURSOR].tile = EMPTY_TILE; + self.gsa2.sprite[SPR_CURSOR].tile = TILE_CURSOR; + } + State::NewMapDialog => { + self.gsa1.bg[0].active = false; + self.gsa1.bg[1].active = false; + self.gsa1.bg[2].active = false; + self.gsa2.bg[0].active = false; + self.gsa2.bg[1].active = false; + self.gsa1.sprite[SPR_CURSOR].tile = EMPTY_TILE; + self.gsa2.sprite[SPR_CURSOR].tile = EMPTY_TILE; + self.draw_input_window(IVec2 { x: 15, y: 9 }, "Create Map"); + } + } + } + + pub fn update_input_window(&mut self, pos: IVec2, val: u16) { + self.gsa2.write_string( + IVec2 { + x: pos.x * 2 + 6, + y: pos.y * 2 + 3, + }, + &format!("{:04x}", val), + ); + } + + pub fn update_status_bar(&mut self) { + let bottom = self.window_tile_size.y as usize - 1; + let text_bottom = (bottom * 2 + 1) as i32; + + self.gsa2.write_string( + IVec2 { + x: 0, + y: text_bottom, + }, + &" ".repeat(self.window_tile_size.x as usize * 2), + ); + self.gsa2.write_string( + IVec2 { + x: 1, + y: text_bottom, + }, + &format!("Map:{:04X}", self.current_map), + ); + + self.gsa2.write_string( + IVec2 { + x: 17, + y: text_bottom, + }, + &format!("Size:{}*{}", self.gsa1.bg[0].size.x, self.gsa1.bg[0].size.y), + ); + + self.gsa2.write_string( + IVec2 { + x: 33, + y: text_bottom, + }, + &format!("Cursor:{}*{}", self.tile_pos.x, self.tile_pos.y), + ); + } +} diff --git a/src/mapedit/surface.rs b/src/mapedit/surface.rs new file mode 100644 index 0000000..99a32a5 --- /dev/null +++ b/src/mapedit/surface.rs @@ -0,0 +1,20 @@ +use glam::IVec2; + +pub struct Surface<'a> { + pub data: &'a mut [u8], + pub size: IVec2, +} + +impl<'a> Surface<'a> { + pub fn draw_vline(&mut self, pos: IVec2, len: i32, color: u8) { + for i in 0..len { + self.data[(pos.x + (pos.y + i) * self.size.x) as usize] = color; + } + } + + pub fn draw_hline(&mut self, pos: IVec2, len: i32, color: u8) { + for i in 0..len { + self.data[(pos.x + i + pos.y * self.size.x) as usize] = color; + } + } +} diff --git a/src/mapedit/util.rs b/src/mapedit/util.rs new file mode 100644 index 0000000..b4358ef --- /dev/null +++ b/src/mapedit/util.rs @@ -0,0 +1,23 @@ +use winit::event::VirtualKeyCode; + +pub fn key_to_num(key: VirtualKeyCode) -> u16 { + match key { + VirtualKeyCode::Key0 => 0x0, + VirtualKeyCode::Key1 => 0x1, + VirtualKeyCode::Key2 => 0x2, + VirtualKeyCode::Key3 => 0x3, + VirtualKeyCode::Key4 => 0x4, + VirtualKeyCode::Key5 => 0x5, + VirtualKeyCode::Key6 => 0x6, + VirtualKeyCode::Key7 => 0x7, + VirtualKeyCode::Key8 => 0x8, + VirtualKeyCode::Key9 => 0x9, + VirtualKeyCode::A => 0xa, + VirtualKeyCode::B => 0xb, + VirtualKeyCode::C => 0xc, + VirtualKeyCode::D => 0xd, + VirtualKeyCode::E => 0xe, + VirtualKeyCode::F => 0xf, + _ => 0x0, + } +} diff --git a/src/run.rs b/src/run.rs index cc0599f..f851dff 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,6 +1,5 @@ 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,