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(); } _ => {} } }); }