started map editor

This commit is contained in:
dani 2023-08-04 22:41:17 +00:00
parent f3b485f6e2
commit 546fcef57a
5 changed files with 225 additions and 17 deletions

View File

@ -22,6 +22,7 @@ gif = "0.12.0"
clap = {version = "4.3.8", features = ["derive", "cargo"]} clap = {version = "4.3.8", features = ["derive", "cargo"]}
dunce = "1.0.4" dunce = "1.0.4"
path-slash = "0.2.1" path-slash = "0.2.1"
serde = "1.0.181"
[profile.release-dani] [profile.release-dani]
inherits = "release" inherits = "release"
@ -30,4 +31,3 @@ lto = true
codegen-units = 1 codegen-units = 1
strip = true strip = true
panic = "abort" panic = "abort"

View File

@ -5,26 +5,33 @@ use crate::{
use glam::IVec2; use glam::IVec2;
use softbuffer::Buffer; use softbuffer::Buffer;
fn draw_tile(target: &mut [u8], pos: IVec2, tile: u16, tileset: &[u8], half: bool) { fn draw_tile(
target: &mut [u8],
pos: IVec2,
tile: u16,
tileset: &[u8],
half: bool,
screen_size: IVec2,
) {
let tilesize = if half { HALF_TILE_SIZE } else { TILE_SIZE }; let tilesize = if half { HALF_TILE_SIZE } else { TILE_SIZE };
let tx = tile as usize % 0x100 * tilesize; let tx = tile as usize % 0x100 * tilesize;
let ty = tile as usize / 0x100 * tilesize; let ty = tile as usize / 0x100 * tilesize;
let startx = pos.x.max(0).min(SCREEN_WIDTH as i32); let startx = pos.x.max(0).min(screen_size.x);
let starty = pos.y.max(0).min(SCREEN_HEIGHT as i32); let starty = pos.y.max(0).min(screen_size.y);
let endx = (pos.x + tilesize as i32).max(0).min(SCREEN_WIDTH as i32); let endx = (pos.x + tilesize as i32).max(0).min(screen_size.x);
let endy = (pos.y + tilesize as i32).max(0).min(SCREEN_HEIGHT as i32); let endy = (pos.y + tilesize as i32).max(0).min(screen_size.y);
for y in (starty - pos.y)..(endy - pos.y) { for y in (starty - pos.y)..(endy - pos.y) {
for x in (startx - pos.x)..(endx - pos.x) { for x in (startx - pos.x)..(endx - pos.x) {
let p = tileset[(x as usize + tx) + (y as usize + ty) * (TILESET_SIZE * TILE_SIZE)]; let p = tileset[(x as usize + tx) + (y as usize + ty) * (TILESET_SIZE * TILE_SIZE)];
if p != TRANSPARENT { if p != TRANSPARENT {
target[(x as usize + pos.x as usize) target[(x as usize + pos.x as usize)
+ (y as usize + pos.y as usize) * SCREEN_WIDTH] = p; + (y as usize + pos.y as usize) * screen_size.x as usize] = p;
} }
} }
} }
} }
fn render_map(target: &mut [u8], map: &Background, tileset: &[u8]) { fn render_map(target: &mut [u8], map: &Background, tileset: &[u8], screen_size: IVec2) {
let tcmult = if map.half_tile { 2 } else { 1 }; let tcmult = if map.half_tile { 2 } else { 1 };
let tilesize = if map.half_tile { let tilesize = if map.half_tile {
HALF_TILE_SIZE HALF_TILE_SIZE
@ -33,8 +40,10 @@ fn render_map(target: &mut [u8], map: &Background, tileset: &[u8]) {
} as i32; } as i32;
let mut startx = map.scroll.x / tilesize; let mut startx = map.scroll.x / tilesize;
let mut starty = map.scroll.y / tilesize; let mut starty = map.scroll.y / tilesize;
let endx = (BACKGROUND_MAX_SIZE as i32).min(startx + 20 * tcmult); let endx =
let endy = (BACKGROUND_MAX_SIZE as i32).min(starty + 12 * tcmult); (BACKGROUND_MAX_SIZE as i32).min(startx + (screen_size.x / TILE_SIZE as i32 + 1) * tcmult);
let endy =
(BACKGROUND_MAX_SIZE as i32).min(starty + (screen_size.y / TILE_SIZE as i32 + 1) * tcmult);
startx = 0.max(startx); startx = 0.max(startx);
starty = 0.max(starty); starty = 0.max(starty);
for x in startx..endx { for x in startx..endx {
@ -50,18 +59,19 @@ fn render_map(target: &mut [u8], map: &Background, tileset: &[u8]) {
tile, tile,
tileset, tileset,
map.half_tile, map.half_tile,
screen_size,
); );
} }
} }
} }
} }
pub(crate) fn render_to_screen(target: &mut [u8], gsa: &Gsa, tileset: &[u8]) { pub(crate) fn render_to_screen(target: &mut [u8], gsa: &Gsa, tileset: &[u8], screen_size: IVec2) {
for i in 0..MAX_BACKGROUNDS { for i in 0..MAX_BACKGROUNDS {
render_map(target, &gsa.bgs[i], tileset); render_map(target, &gsa.bgs[i], tileset, screen_size);
for sprite in &gsa.sprites { for sprite in &gsa.sprites {
if sprite.tile != EMPTY_TILE && sprite.priority == i as u8 { if sprite.tile != EMPTY_TILE && sprite.priority == i as u8 {
draw_tile(target, sprite.pos, sprite.tile, tileset, false); draw_tile(target, sprite.pos, sprite.tile, tileset, false, screen_size);
} }
} }
} }
@ -77,14 +87,14 @@ pub(crate) fn render_to_window(
off_y: usize, off_y: usize,
) { ) {
let start = window_size.x as usize * off_y; let start = window_size.x as usize * off_y;
let end = start + window_size.x as usize * SCREEN_HEIGHT * scale; let end = start + window_size.x as usize * window_size.y as usize * scale;
for (y, row) in target[start..end] for (y, row) in target[start..end]
.chunks_exact_mut(window_size.x as usize * scale) .chunks_exact_mut(window_size.x as usize * scale)
.enumerate() .enumerate()
{ {
let y = y as i32; let y = y as i32;
for x in 0..SCREEN_WIDTH { for x in 0..window_size.x as usize {
let p = src[x + y as usize * SCREEN_WIDTH]; let p = src[x + y as usize * window_size.x as usize];
for scaley in 0..scale { for scaley in 0..scale {
for scalex in 0..scale { for scalex in 0..scale {

View File

@ -1,5 +1,61 @@
mod background;
mod buttons;
mod gsa;
mod gsa_render_to_screen;
mod mapedit;
mod rgb;
mod run;
mod sprite;
mod tileset;
//todo: figure out how to not repeat all the lib.rs stuff :(
pub use crate::background::*;
pub use crate::buttons::*;
pub use crate::gsa::*;
pub use crate::rgb::*;
pub use crate::run::run;
pub use crate::sprite::*;
/// Amount of sprites in [Gsa::sprites]
pub const MAX_SPRITES: usize = 0xff;
/// Screen Width in pixels
pub const SCREEN_WIDTH: usize = 304;
/// Screen Height in pixels
pub const SCREEN_HEIGHT: usize = 176;
/// X and y dimensions of maps in [Gsa::bgs]
pub const BACKGROUND_MAX_SIZE: usize = 1024;
/// Tile considered empty (never drawn even if has contents)
pub const EMPTY_TILE: u16 = 0xffff;
/// Tile id of bold default font
pub const FONT_BOLD: u16 = 0xf000;
/// Tile id of thin default font
pub const FONT_THIN: u16 = 0xe000;
/// Width and height (in tiles) of tileset
pub const TILESET_SIZE: usize = 0x100;
/// Width and height of a tile
pub const TILE_SIZE: usize = 16;
/// Palette index which is treated as transparent
pub const TRANSPARENT: u8 = 0xff;
/// Width and height of a tile in half-tile mode
pub const HALF_TILE_SIZE: usize = 8;
/// Amount of tile maps in [Gsa::bgs]
pub const MAX_BACKGROUNDS: usize = 4;
use clap::{crate_name, crate_version, Parser, Subcommand}; use clap::{crate_name, crate_version, Parser, Subcommand};
use dunce::canonicalize; use dunce::canonicalize;
use mapedit::run_mapedit;
use path_slash::PathExt; use path_slash::PathExt;
use std::fs; use std::fs;
use std::fs::{write, OpenOptions}; use std::fs::{write, OpenOptions};
@ -26,6 +82,8 @@ enum Cmd {
#[arg(long)] #[arg(long)]
overwrite: bool, overwrite: bool,
}, },
/// Edit map of current project
MapEdit {},
} }
fn main() { fn main() {
@ -102,5 +160,6 @@ fn main() {
) )
.unwrap(); .unwrap();
} }
Cmd::MapEdit {} => run_mapedit(),
} }
} }

131
src/mapedit.rs Normal file
View File

@ -0,0 +1,131 @@
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::sprite::Sprite;
use crate::tileset::*;
use crate::*;
use clap::crate_version;
use glam::IVec2;
use winit::{
dpi::LogicalSize,
event::{Event, KeyboardInput, 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 mut gsa = Gsa {
sprites: [Sprite::default(); MAX_SPRITES],
palette,
bgs: Default::default(),
font: FONT_BOLD,
pressed: 0,
released: 0,
down: 0,
};
gsa.reset_bgs();
gsa.reset_sprites();
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 left_down = false;
event_loop.run(move |event, _, control_flow| {
let mouse_pos = &mut mouse_pos;
let left_down = &mut left_down;
match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Escape, ..),
..
},
..
} => {
*control_flow = ControlFlow::Exit;
}
WindowEvent::MouseInput { state, button, .. } => match (state, button) {
(winit::event::ElementState::Pressed, winit::event::MouseButton::Left) => {
*left_down = true;
}
(winit::event::ElementState::Released, winit::event::MouseButton::Left) => {
*left_down = false;
}
_ => {}
},
WindowEvent::CursorMoved { position, .. } => {
*mouse_pos = IVec2 {
x: position.x as i32,
y: position.y as i32,
};
}
_ => {}
},
Event::MainEventsCleared => {
if *left_down {
let tile_x = mouse_pos.x as usize / TILE_SIZE;
let tile_y = mouse_pos.y as usize / TILE_SIZE;
gsa.bgs[0].tiles[tile_x][tile_y] = 0;
}
// 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];
let mut window_buffer = surface.buffer_mut().unwrap();
render_to_screen(
&mut screen_buffer,
&gsa,
&tileset,
IVec2 {
x: size.width as i32,
y: size.height as i32,
},
);
render_to_window(
&mut window_buffer,
&mut screen_buffer,
&palette,
IVec2 {
x: size.width as i32,
y: size.height as i32,
},
1,
0,
0,
);
window_buffer.present().unwrap();
}
_ => {}
}
});
}

View File

@ -195,7 +195,15 @@ pub fn run<TGame: 'static>(
let mut screen_buffer = [TRANSPARENT; SCREEN_WIDTH * SCREEN_HEIGHT]; let mut screen_buffer = [TRANSPARENT; SCREEN_WIDTH * SCREEN_HEIGHT];
let mut window_buffer = surface.buffer_mut().unwrap(); let mut window_buffer = surface.buffer_mut().unwrap();
render_to_screen(&mut screen_buffer, &gsa, &tileset); render_to_screen(
&mut screen_buffer,
&gsa,
&tileset,
IVec2 {
x: SCREEN_WIDTH as i32,
y: SCREEN_HEIGHT as i32,
},
);
render_to_window( render_to_window(
&mut window_buffer, &mut window_buffer,
&mut screen_buffer, &mut screen_buffer,