started refactor of map editor

This commit is contained in:
dani 2023-08-21 23:04:15 +00:00
parent f77b66ecc9
commit f0ad2726f8
18 changed files with 810 additions and 792 deletions

View File

@ -1,5 +1,6 @@
# 0.3.0 # 0.3.0
- added size to backgrounds - added size to backgrounds
- added rotation to backgrounds
- refactored api, background functions are now methods to background rather than gsa - refactored api, background functions are now methods to background rather than gsa
# 0.2.1 - 2023-07-27 # 0.2.1 - 2023-07-27

View File

@ -25,6 +25,7 @@ path-slash = "0.2.1"
serde = {version = "1.0.181", features = ["derive"]} serde = {version = "1.0.181", features = ["derive"]}
postcard = {version = "1.0.6", features = ["alloc"]} postcard = {version = "1.0.6", features = ["alloc"]}
rayon = "1.7.0" rayon = "1.7.0"
lazy_static = "1.4.0"
[profile.release-dani] [profile.release-dani]
inherits = "release" inherits = "release"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1,5 +1,5 @@
use glam::IVec2; use glam::IVec2;
use gsa::{run, Gsa, FACE_DOWN, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_MIDDLE, FACE_LEFT, FACE_RIGHT}; use gsa::{run, Gsa, FACE_DOWN, FACE_LEFT, FACE_RIGHT, SCREEN_HEIGHT, SCREEN_MIDDLE, SCREEN_WIDTH};
struct Game {} struct Game {}

BIN
examples/basic/maps.dat Normal file

Binary file not shown.

View File

@ -30,7 +30,7 @@ impl Default for Background {
active: true, active: true,
size: IVec2 { x: 19, y: 11 }, size: IVec2 { x: 19, y: 11 },
origin: IVec2::ZERO, origin: IVec2::ZERO,
rot: 0 rot: 0,
} }
} }
} }

View File

@ -4,8 +4,7 @@ use crate::rgb::Rgb;
use crate::sprite::Sprite; use crate::sprite::Sprite;
use crate::tilemap::Tilemap; use crate::tilemap::Tilemap;
use crate::{ use crate::{
Buttons, BACKGROUND_MAX_SIZE, DPAD_DOWN, DPAD_LEFT, DPAD_RIGHT, DPAD_UP, EMPTY_TILE, Buttons, DPAD_DOWN, DPAD_LEFT, DPAD_RIGHT, DPAD_UP, EMPTY_TILE, MAX_BACKGROUNDS, MAX_SPRITES,
MAX_BACKGROUNDS, MAX_SPRITES,
}; };
use ascii::{AsciiChar, AsciiStr}; use ascii::{AsciiChar, AsciiStr};
use glam::IVec2; use glam::IVec2;

View File

@ -1,5 +1,5 @@
use std::{usize, f32::consts::PI}; use std::{f32::consts::PI, usize};
use rayon::prelude::*; //use rayon::prelude::*;
use crate::{ use crate::{
Background, Gsa, Rgb, EMPTY_TILE, HALF_TILE_SIZE, MAX_BACKGROUNDS, TILESET_SIZE, TILE_SIZE, Background, Gsa, Rgb, EMPTY_TILE, HALF_TILE_SIZE, MAX_BACKGROUNDS, TILESET_SIZE, TILE_SIZE,
@ -33,46 +33,46 @@ fn draw_tile(
} }
} }
fn render_bg(target: &mut [u8], map: &Background, tileset: &[u8], screen_size: IVec2) { // fn render_bg(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
} else { // } else {
TILE_SIZE // TILE_SIZE
} 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 = map // let endx = map
.size // .size
.x // .x
.min(startx + (screen_size.x / TILE_SIZE as i32 + 1) * tcmult); // .min(startx + (screen_size.x / TILE_SIZE as i32 + 1) * tcmult);
let endy = map // let endy = map
.size // .size
.y // .y
.min(starty + (screen_size.y / TILE_SIZE as i32 + 1) * tcmult); // .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 {
for y in starty..endy { // for y in starty..endy {
let tile = map.tiles[x as usize][y as usize]; // let tile = map.tiles[x as usize][y as usize];
if tile != EMPTY_TILE { // if tile != EMPTY_TILE {
draw_tile( // draw_tile(
target, // target,
IVec2 { // IVec2 {
x: x * tilesize - map.scroll.x, // x: x * tilesize - map.scroll.x,
y: y * tilesize - map.scroll.y, // y: y * tilesize - map.scroll.y,
}, // },
tile, // tile,
tileset, // tileset,
map.half_tile, // map.half_tile,
screen_size, // screen_size,
); // );
} // }
} // }
} // }
} // }
fn render_bg2(target: &mut [u8], map: &Background, tileset: &[u8], screen_size: IVec2) { fn render_bg(target: &mut [u8], map: &Background, tileset: &[u8], screen_size: IVec2) {
let tilesize = if map.half_tile { let tilesize = if map.half_tile {
HALF_TILE_SIZE HALF_TILE_SIZE
} else { } else {
@ -81,7 +81,6 @@ fn render_bg2(target: &mut [u8], map: &Background, tileset: &[u8], screen_size:
let a = map.rot as f32 / 256.0 * PI * 2.0; let a = map.rot as f32 / 256.0 * PI * 2.0;
let du = glam::Vec2::new(a.cos(), a.sin()); let du = glam::Vec2::new(a.cos(), a.sin());
let dv = glam::Vec2::new(-a.sin(), a.cos()); let dv = glam::Vec2::new(-a.sin(), a.cos());
let duv = Mat2::from_cols(du, dv); let duv = Mat2::from_cols(du, dv);
@ -93,7 +92,11 @@ fn render_bg2(target: &mut [u8], map: &Background, tileset: &[u8], screen_size:
.for_each({ .for_each({
|(screeny, row)| { |(screeny, row)| {
for screenx in 0..screen_size.x { for screenx in 0..screen_size.x {
let screen = duv * Vec2::new(screenx as f32 - map.origin.x as f32, screeny as f32 - map.origin.y as f32); let screen = duv
* Vec2::new(
screenx as f32 - map.origin.x as f32,
screeny as f32 - map.origin.y as f32,
);
let x = screen.x.floor() as i32 + map.scroll.x; let x = screen.x.floor() as i32 + map.scroll.x;
let y = screen.y.floor() as i32 + map.scroll.y; let y = screen.y.floor() as i32 + map.scroll.y;
let tilex = x.div_euclid(tilesize); let tilex = x.div_euclid(tilesize);
@ -102,8 +105,8 @@ fn render_bg2(target: &mut [u8], map: &Background, tileset: &[u8], screen_size:
//assert!(tilex >= 0); //assert!(tilex >= 0);
// the addition is there because otherwise x might be below 0 // the addition is there because otherwise x might be below 0
// which messes with the modulo // which messes with the modulo
let pixx = (x+tilesize) % tilesize; let pixx = (x + tilesize) % tilesize;
let pixy = (y+tilesize) % tilesize; let pixy = (y + tilesize) % tilesize;
let tile = let tile =
if tilex >= 0 && tiley >= 0 && tilex < map.size.x && tiley < map.size.y { if tilex >= 0 && tiley >= 0 && tilex < map.size.x && tiley < map.size.y {
map.tiles[tilex as usize][tiley as usize] 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 { if tile != EMPTY_TILE {
let tilesetx = tile as i32 % 0x100 * tilesize + pixx; let tilesetx = tile as i32 % 0x100 * tilesize + pixx;
let tilesety = tile as i32 / 0x100 * tilesize + pixy; 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 { if p != TRANSPARENT {
row[screenx as usize] = p; 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) { 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 {
if gsa.bg[i].active { 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 { for sprite in &gsa.sprite {
if sprite.tile != EMPTY_TILE && sprite.priority == i as u8 { if sprite.tile != EMPTY_TILE && sprite.priority == i as u8 {

View File

@ -69,10 +69,16 @@ pub const SCREEN_WIDTH: usize = 304;
pub const SCREEN_HEIGHT: usize = 176; pub const SCREEN_HEIGHT: usize = 176;
/// Screen size as vector (SCREEN_WIDTH,SCREEN_HEIGHT) /// 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 /// 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] /// X and y dimensions of maps in [Gsa::bgs]
pub const BACKGROUND_MAX_SIZE: usize = 1024; pub const BACKGROUND_MAX_SIZE: usize = 1024;

View File

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

33
src/mapedit/background.rs Normal file
View File

@ -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;
}
}
}
}

25
src/mapedit/constants.rs Normal file
View File

@ -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;

449
src/mapedit/mod.rs Normal file
View File

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

8
src/mapedit/state.rs Normal file
View File

@ -0,0 +1,8 @@
use super::stuff::Stuff;
#[derive(Copy, Clone)]
pub enum State {
Edit,
SelectTile,
NewMapDialog,
}

180
src/mapedit/stuff.rs Normal file
View File

@ -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),
);
}
}

20
src/mapedit/surface.rs Normal file
View File

@ -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;
}
}
}

23
src/mapedit/util.rs Normal file
View File

@ -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,
}
}

View File

@ -1,6 +1,5 @@
use crate::buttons::{button_from_gilrs, button_from_scancode}; use crate::buttons::{button_from_gilrs, button_from_scancode};
use crate::gsa_render_to_screen::{render_to_screen, render_to_window}; use crate::gsa_render_to_screen::{render_to_screen, render_to_window};
use crate::maps::Maps;
use crate::tileset::load_tileset; use crate::tileset::load_tileset;
use crate::{ use crate::{
Buttons, Gsa, Sprite, FONT_BOLD, MAX_SPRITES, SCREEN_HEIGHT, SCREEN_WIDTH, TRANSPARENT, Buttons, Gsa, Sprite, FONT_BOLD, MAX_SPRITES, SCREEN_HEIGHT, SCREEN_WIDTH, TRANSPARENT,