started map editor
This commit is contained in:
parent
f3b485f6e2
commit
546fcef57a
|
@ -22,6 +22,7 @@ gif = "0.12.0"
|
|||
clap = {version = "4.3.8", features = ["derive", "cargo"]}
|
||||
dunce = "1.0.4"
|
||||
path-slash = "0.2.1"
|
||||
serde = "1.0.181"
|
||||
|
||||
[profile.release-dani]
|
||||
inherits = "release"
|
||||
|
@ -30,4 +31,3 @@ lto = true
|
|||
codegen-units = 1
|
||||
strip = true
|
||||
panic = "abort"
|
||||
|
||||
|
|
|
@ -5,26 +5,33 @@ use crate::{
|
|||
use glam::IVec2;
|
||||
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 tx = tile as usize % 0x100 * tilesize;
|
||||
let ty = tile as usize / 0x100 * tilesize;
|
||||
let startx = pos.x.max(0).min(SCREEN_WIDTH as i32);
|
||||
let starty = pos.y.max(0).min(SCREEN_HEIGHT as i32);
|
||||
let endx = (pos.x + tilesize as i32).max(0).min(SCREEN_WIDTH as i32);
|
||||
let endy = (pos.y + tilesize as i32).max(0).min(SCREEN_HEIGHT as i32);
|
||||
let startx = pos.x.max(0).min(screen_size.x);
|
||||
let starty = pos.y.max(0).min(screen_size.y);
|
||||
let endx = (pos.x + tilesize as i32).max(0).min(screen_size.x);
|
||||
let endy = (pos.y + tilesize as i32).max(0).min(screen_size.y);
|
||||
for y in (starty - pos.y)..(endy - pos.y) {
|
||||
for x in (startx - pos.x)..(endx - pos.x) {
|
||||
let p = tileset[(x as usize + tx) + (y as usize + ty) * (TILESET_SIZE * TILE_SIZE)];
|
||||
if p != TRANSPARENT {
|
||||
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 tilesize = if map.half_tile {
|
||||
HALF_TILE_SIZE
|
||||
|
@ -33,8 +40,10 @@ fn render_map(target: &mut [u8], map: &Background, tileset: &[u8]) {
|
|||
} as i32;
|
||||
let mut startx = map.scroll.x / tilesize;
|
||||
let mut starty = map.scroll.y / tilesize;
|
||||
let endx = (BACKGROUND_MAX_SIZE as i32).min(startx + 20 * tcmult);
|
||||
let endy = (BACKGROUND_MAX_SIZE as i32).min(starty + 12 * tcmult);
|
||||
let endx =
|
||||
(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);
|
||||
starty = 0.max(starty);
|
||||
for x in startx..endx {
|
||||
|
@ -50,18 +59,19 @@ fn render_map(target: &mut [u8], map: &Background, tileset: &[u8]) {
|
|||
tile,
|
||||
tileset,
|
||||
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 {
|
||||
render_map(target, &gsa.bgs[i], tileset);
|
||||
render_map(target, &gsa.bgs[i], tileset, screen_size);
|
||||
for sprite in &gsa.sprites {
|
||||
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,
|
||||
) {
|
||||
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]
|
||||
.chunks_exact_mut(window_size.x as usize * scale)
|
||||
.enumerate()
|
||||
{
|
||||
let y = y as i32;
|
||||
for x in 0..SCREEN_WIDTH {
|
||||
let p = src[x + y as usize * SCREEN_WIDTH];
|
||||
for x in 0..window_size.x as usize {
|
||||
let p = src[x + y as usize * window_size.x as usize];
|
||||
|
||||
for scaley in 0..scale {
|
||||
for scalex in 0..scale {
|
||||
|
|
59
src/main.rs
59
src/main.rs
|
@ -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 dunce::canonicalize;
|
||||
use mapedit::run_mapedit;
|
||||
use path_slash::PathExt;
|
||||
use std::fs;
|
||||
use std::fs::{write, OpenOptions};
|
||||
|
@ -26,6 +82,8 @@ enum Cmd {
|
|||
#[arg(long)]
|
||||
overwrite: bool,
|
||||
},
|
||||
/// Edit map of current project
|
||||
MapEdit {},
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -102,5 +160,6 @@ fn main() {
|
|||
)
|
||||
.unwrap();
|
||||
}
|
||||
Cmd::MapEdit {} => run_mapedit(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
10
src/run.rs
10
src/run.rs
|
@ -195,7 +195,15 @@ pub fn run<TGame: 'static>(
|
|||
|
||||
let mut screen_buffer = [TRANSPARENT; SCREEN_WIDTH * SCREEN_HEIGHT];
|
||||
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(
|
||||
&mut window_buffer,
|
||||
&mut screen_buffer,
|
||||
|
|
Loading…
Reference in New Issue