From f0ad2726f8f1059f4b57da79e95bcbc2132d2b48 Mon Sep 17 00:00:00 2001 From: dani <> Date: Mon, 21 Aug 2023 23:04:15 +0000 Subject: [PATCH] started refactor of map editor --- CHANGLOG.md | 1 + Cargo.toml | 1 + examples/basic/gfx.gif | Bin 29134 -> 29177 bytes examples/basic/main.rs | 6 +- examples/basic/maps.dat | Bin 0 -> 5672 bytes src/background.rs | 4 +- src/gsa.rs | 3 +- src/gsa_render_to_screen.rs | 108 +++--- src/lib.rs | 10 +- src/mapedit.rs | 730 ------------------------------------ src/mapedit/background.rs | 33 ++ src/mapedit/constants.rs | 25 ++ src/mapedit/mod.rs | 449 ++++++++++++++++++++++ src/mapedit/state.rs | 8 + src/mapedit/stuff.rs | 180 +++++++++ src/mapedit/surface.rs | 20 + src/mapedit/util.rs | 23 ++ src/run.rs | 1 - 18 files changed, 810 insertions(+), 792 deletions(-) create mode 100644 examples/basic/maps.dat delete mode 100644 src/mapedit.rs create mode 100644 src/mapedit/background.rs create mode 100644 src/mapedit/constants.rs create mode 100644 src/mapedit/mod.rs create mode 100644 src/mapedit/state.rs create mode 100644 src/mapedit/stuff.rs create mode 100644 src/mapedit/surface.rs create mode 100644 src/mapedit/util.rs 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 e1d5c1052d1f608eeb0891d63e4995c10dcbac70..6d28de269b055ccb7d928a08cdd0bb6de5e60ed0 100644 GIT binary patch delta 8100 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 delta 8057 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 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 0000000000000000000000000000000000000000..6aea02c7b9d592709bec400ee108e63471d31908 GIT binary patch literal 5672 zcmeH~(FwpH3`8UTb%2bJ{eL?~CsIl~ftZBg1Ajfj`R;= 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,