diff --git a/examples/basic/gfx.gif b/examples/basic/gfx.gif index 47b5ae8..cec71ef 100644 Binary files a/examples/basic/gfx.gif and b/examples/basic/gfx.gif differ diff --git a/src/gsa.rs b/src/gsa.rs index 6daf297..d879895 100644 --- a/src/gsa.rs +++ b/src/gsa.rs @@ -2,6 +2,7 @@ use crate::background::Background; use crate::maps::Maps; 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, @@ -66,21 +67,33 @@ impl Gsa { } } - /// Loads tilemap into backgrounds + /// Loads tilemap into backgrounds from id 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 { + for x in 0..map.size.x as usize { self.bgs[i].tiles[x][y] = map.data[x + y * map.size.x as usize] - } + } } - } + } + } + + /// Stores tilemap from backgrounds into id + pub fn store_map(&mut self, map: u16) { + self.maps.maps.insert( + map, + [ + Tilemap::from_bg(&self.bgs[0]), + Tilemap::from_bg(&self.bgs[1]), + Tilemap::from_bg(&self.bgs[2]), + ], + ); } /// Does a tilemap with this id exist? pub fn map_exists(&mut self, map: u16) -> bool { - self.maps.maps.contains_key(&map) + self.maps.maps.contains_key(&map) } /// Checks if any of given buttons currently held down diff --git a/src/mapedit.rs b/src/mapedit.rs index 3d1b373..ed1fa9b 100644 --- a/src/mapedit.rs +++ b/src/mapedit.rs @@ -11,6 +11,7 @@ use crate::tileset::*; use crate::*; use clap::crate_version; use glam::IVec2; +use winit::event::ElementState; use winit::{ dpi::LogicalSize, event::{Event, VirtualKeyCode, WindowEvent}, @@ -21,14 +22,28 @@ use winit::{ const SPR_CURSOR: usize = 0x01; const TILE_CURSOR: u16 = 0x7110; const TILE_MARKER: u16 = 0x7210; -//const TILE_BG: u16 = 0x730a; +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_LAYER1: usize = 2; -const BUT_LAYER2: usize = 3; -const BUT_LAYER3: usize = 4; -const BUT_LAYERS: usize = 5; -const BUT_EXIT: usize = 7; +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], @@ -49,27 +64,165 @@ impl<'a> Surface<'a> { } } +#[derive(Copy, Clone)] enum State { Edit, SelectTile, + NewMapDialog, } -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; +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.bgs[2].draw_frame(pos, IVec2 { x: 7, y: 5 }); + gsa.bgs[2].tiles[pos.x as usize + 3][pos.y as usize + 1] = TILE_INPUT; + gsa.bgs[2].tiles[pos.x as usize + 4][pos.y as usize + 1] = TILE_INPUT; + gsa.bgs[2].tiles[pos.x as usize + 1][pos.y as usize + 3] = TILE_BUT_YES; + gsa.bgs[2].tiles[pos.x as usize + 5][pos.y as usize + 3] = TILE_BUT_NO; + gsa.write_string( + 3, + IVec2 { + x: pos.x * 2 + 2, + y: pos.y * 2 + 1, + }, + caption, + ); + gsa.write_string( + 3, + 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( + 3, + 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) { + match state { + State::Edit => { + //update editing layers + if all_layers { + gsa1.bgs[0].active = true; + gsa1.bgs[1].active = true; + gsa1.bgs[2].active = true; + } else { + gsa1.bgs[0].active = current_layer == 0; + gsa1.bgs[1].active = current_layer == 1; + gsa1.bgs[2].active = current_layer == 2; + } + gsa2.clear_bg(2); + gsa2.clear_bg(3); + draw_toolbar(gsa1, gsa2, current_layer, all_layers); + + gsa2.bgs[0].active = false; + gsa2.bgs[1].active = false; + gsa1.sprites[SPR_CURSOR].priority = 3; + gsa1.sprites[SPR_CURSOR].tile = TILE_CURSOR; + gsa2.sprites[SPR_CURSOR].tile = EMPTY_TILE; + } + State::SelectTile => { + draw_toolbar(gsa1, gsa2, current_layer, all_layers); + gsa1.bgs[0].active = false; + gsa1.bgs[1].active = false; + gsa1.bgs[2].active = false; + gsa2.bgs[0].active = true; + gsa2.bgs[1].active = true; + gsa1.sprites[SPR_CURSOR].tile = EMPTY_TILE; + gsa2.sprites[SPR_CURSOR].tile = TILE_CURSOR; + } + State::NewMapDialog => { + gsa1.bgs[0].active = false; + gsa1.bgs[1].active = false; + gsa1.bgs[2].active = false; + gsa2.bgs[0].active = false; + gsa2.bgs[1].active = false; + gsa1.sprites[SPR_CURSOR].tile = EMPTY_TILE; + gsa2.sprites[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) { + for y in 0..BACKGROUND_MAX_SIZE { + gsa2.bgs[2].tiles[0][y] = 0x7010; + } + gsa2.bgs[2].tiles[0][BUT_SAVE] = 0x7211; + gsa2.bgs[2].tiles[0][BUT_CREATE] = TILE_BUT_CREATE; + gsa2.bgs[2].tiles[0][BUT_EXIT] = 0x7212; + + //layer buttons + gsa2.bgs[2].tiles[0][BUT_LAYER1] = if current_layer == 0 { 0x7111 } else { 0x7011 }; + gsa2.bgs[2].tiles[0][BUT_LAYER2] = if current_layer == 1 { 0x7112 } else { 0x7012 }; + gsa2.bgs[2].tiles[0][BUT_LAYER3] = if current_layer == 2 { 0x7113 } else { 0x7013 }; + gsa2.bgs[2].tiles[0][BUT_LAYERS] = if all_layers { 0x7114 } else { 0x7014 }; + + gsa2.bgs[2].size = IVec2 { x: 120, y: 68 }; //enough to cover 4k monitors? <_< + gsa2.bgs[3].size = IVec2 { x: 240, y: 136 }; +} + pub(crate) fn run_mapedit() { println!("running map edit"); @@ -83,7 +236,7 @@ pub(crate) fn run_mapedit() { Maps::default() }; - let mut gsa = Gsa { + let mut gsa1 = Gsa { sprites: [Sprite::default(); MAX_SPRITES], palette, bgs: Default::default(), @@ -94,22 +247,20 @@ pub(crate) fn run_mapedit() { maps, }; - gsa.reset_bgs(); - gsa.reset_sprites(); + gsa1.reset_bgs(); + gsa1.reset_sprites(); - if gsa.map_exists(1337) { - gsa.load_map(1337); + if gsa1.map_exists(0) { + gsa1.load_map(0); } for i in 0..3 { - gsa.bgs[i].scroll = IVec2 { + gsa1.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, @@ -134,17 +285,7 @@ pub(crate) fn run_mapedit() { gsa2.bgs[0].tiles[x][y] = (x + (y << 8)) as u16; } } - gsa2.bgs[0].active = false; - gsa2.bgs[1].active = false; - gsa2.bgs[2].active = true; - 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] = 0x7010; - } - gsa2.bgs[2].tiles[0][BUT_SAVE] = 0x7211; - // layer buttons set up later - gsa2.bgs[2].tiles[0][BUT_EXIT] = 0x7212; + //draw_input_window(&mut gsa2, IVec2 { x: 5, y: 3 }, "Create Map"); let event_loop = EventLoop::new(); let size = LogicalSize::new(1280, 720); @@ -168,10 +309,13 @@ 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); + 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); + event_loop.run(move |event, _, control_flow| { let mouse_pos = &mut mouse_pos; let tile_pos = &mut tile_pos; @@ -181,76 +325,183 @@ pub(crate) fn run_mapedit() { 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 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, .. } => { - match ( - input.state, - input.virtual_keycode.unwrap_or(VirtualKeyCode::F24), - ) { - (winit::event::ElementState::Pressed, VirtualKeyCode::Escape) => { - *control_flow = ControlFlow::Exit; + let vk = input.virtual_keycode.unwrap_or(VirtualKeyCode::F24); + match vk { + VirtualKeyCode::Escape => { + //*control_flow = ControlFlow::Exit; } - (winit::event::ElementState::Pressed, VirtualKeyCode::LShift) => { - gsa.bgs[0].active = false; - gsa.bgs[1].active = false; - gsa.bgs[2].active = false; - gsa2.bgs[0].active = true; - gsa2.bgs[1].active = true; - gsa.sprites[SPR_CURSOR].tile = EMPTY_TILE; - gsa2.sprites[SPR_CURSOR].tile = TILE_CURSOR; - *state = State::SelectTile; + VirtualKeyCode::LShift => { + if input.state == ElementState::Pressed { + *state = State::SelectTile; + set_state( + *state, + &mut gsa1, + &mut gsa2, + *current_layer, + *all_layers, + ); + } else { + *state = State::Edit; + set_state( + *state, + &mut gsa1, + &mut gsa2, + *current_layer, + *all_layers, + ); + } } - (winit::event::ElementState::Released, VirtualKeyCode::LShift) => { - gsa.bgs[0].active = true; - gsa.bgs[1].active = true; - gsa.bgs[2].active = true; - gsa2.bgs[0].active = false; - gsa2.bgs[1].active = false; - gsa.sprites[SPR_CURSOR].tile = TILE_CURSOR; - gsa2.sprites[SPR_CURSOR].tile = EMPTY_TILE; - *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 { + *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, button, .. } => match (state, button) { + 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"); - gsa.maps.maps.insert(1337, [Tilemap::from_bg(&gsa.bgs[0]), Tilemap::from_bg(&gsa.bgs[1]), Tilemap::from_bg(&gsa.bgs[2])]); + gsa1.store_map(*current_map); fs::write( maps_path, - postcard::to_allocvec(&gsa.maps).unwrap(), + 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, + ); + 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, + ); } BUT_LAYER2 => { *current_layer = 1; + set_state( + *state, + &mut gsa1, + &mut gsa2, + *current_layer, + *all_layers, + ); } BUT_LAYER3 => { *current_layer = 2; + set_state( + *state, + &mut gsa1, + &mut gsa2, + *current_layer, + *all_layers, + ); + } + BUT_LAYERS => { + *all_layers = !*all_layers; + set_state( + *state, + &mut gsa1, + &mut gsa2, + *current_layer, + *all_layers, + ); } - 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; + 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, + ); + } else if mouse_pos.x / 16 == 20 { + println!("no"); + *state = State::Edit; + set_state( + *state, + &mut gsa1, + &mut gsa2, + *current_layer, + *all_layers, + ); + } + } + } + _ => { + *left_down = true; + } + } } } (winit::event::ElementState::Released, winit::event::MouseButton::Left) => { @@ -280,9 +531,9 @@ pub(crate) fn run_mapedit() { match *state { State::Edit => { // normal mode - gsa.bgs[0].scroll -= delta; - gsa.bgs[1].scroll -= delta; - gsa.bgs[2].scroll -= delta; + gsa1.bgs[0].scroll -= delta; + gsa1.bgs[1].scroll -= delta; + gsa1.bgs[2].scroll -= delta; } State::SelectTile => { // tile select mode @@ -295,16 +546,16 @@ pub(crate) fn run_mapedit() { } *mouse_pos = new_pos; *tile_pos = IVec2 { - x: (new_pos.x + gsa.bgs[0].scroll.x).div_euclid(TILE_SIZE as i32), - y: (new_pos.y + gsa.bgs[0].scroll.y).div_euclid(TILE_SIZE as i32), + x: (new_pos.x + gsa1.bgs[0].scroll.x).div_euclid(TILE_SIZE as i32), + y: (new_pos.y + gsa1.bgs[0].scroll.y).div_euclid(TILE_SIZE as i32), }; *tile_pos2 = IVec2 { x: (new_pos.x + gsa2.bgs[0].scroll.x).div_euclid(TILE_SIZE as i32), y: (new_pos.y + gsa2.bgs[0].scroll.y).div_euclid(TILE_SIZE as i32), }; - let cursor_pos = *tile_pos * TILE_SIZE as i32 - gsa.bgs[0].scroll; + let cursor_pos = *tile_pos * TILE_SIZE as i32 - gsa1.bgs[0].scroll; let cursor_pos2 = *tile_pos2 * TILE_SIZE as i32 - gsa2.bgs[0].scroll; - gsa.sprites[SPR_CURSOR].pos = cursor_pos; + gsa1.sprites[SPR_CURSOR].pos = cursor_pos; gsa2.sprites[SPR_CURSOR].pos = cursor_pos2; } _ => {} @@ -320,7 +571,8 @@ pub(crate) fn run_mapedit() { && 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; + gsa1.bgs[*current_layer].tiles[tile_pos.x as usize] + [tile_pos.y as usize] = tile; } } else if *right_down { if tile_pos.x >= 0 @@ -328,8 +580,8 @@ pub(crate) fn run_mapedit() { && 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; + gsa1.bgs[*current_layer].tiles[tile_pos.x as usize] + [tile_pos.y as usize] = EMPTY_TILE; } } } @@ -348,6 +600,7 @@ pub(crate) fn run_mapedit() { } } } + _ => {} }; // render @@ -365,7 +618,7 @@ pub(crate) fn run_mapedit() { x: size.width as i32 / 2, y: size.height as i32 / 2, }; - render_to_screen(&mut screen_buffer, &gsa, &tileset, screen_size); + 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, @@ -373,10 +626,10 @@ pub(crate) fn run_mapedit() { }; 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; + let xs = -gsa1.bgs[0].scroll.x - 1; + let ys = -gsa1.bgs[0].scroll.y - 1; + let xe = xs + gsa1.bgs[0].size.x * TILE_SIZE as i32 + 1; + let ye = ys + gsa1.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;