diff --git a/CHANGLOG.md b/CHANGLOG.md index 0eeb789..80c6ee7 100644 --- a/CHANGLOG.md +++ b/CHANGLOG.md @@ -1,5 +1,6 @@ # 0.3.0 - added size to backgrounds +- refactored api, background functions are now methods to background rather than gsa # 0.2.1 - 2023-07-27 - fixed missing dependency in scaffolding diff --git a/Cargo.toml b/Cargo.toml index d5af3a5..20db143 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ dunce = "1.0.4" path-slash = "0.2.1" serde = {version = "1.0.181", features = ["derive"]} postcard = {version = "1.0.6", features = ["alloc"]} +#rayon = "1.7.0" [profile.release-dani] inherits = "release" diff --git a/examples/basic/gfx.gif b/examples/basic/gfx.gif index cec71ef..e1d5c10 100644 Binary files a/examples/basic/gfx.gif and b/examples/basic/gfx.gif differ diff --git a/examples/hello_world/main.rs b/examples/hello_world/main.rs index 720d612..197fc7d 100644 --- a/examples/hello_world/main.rs +++ b/examples/hello_world/main.rs @@ -4,13 +4,13 @@ use gsa::{run, Gsa}; struct Game {} fn init(gsa: &mut Gsa) -> Game { - gsa.sprites[0].tile = 0; + gsa.sprite[0].tile = 0; gsa.write_string(3, IVec2 { x: 10, y: 10 }, "Hello World, nyaa~"); Game {} } fn update(_game: &mut Game, gsa: &mut Gsa) { - gsa.sprites[0].pos += gsa.input_dir(); + gsa.sprite[0].pos += gsa.input_dir(); } run!(init, update); diff --git a/src/background.rs b/src/background.rs index f8bacb5..ed37da0 100644 --- a/src/background.rs +++ b/src/background.rs @@ -1,4 +1,4 @@ -use crate::BACKGROUND_MAX_SIZE; +use crate::{BACKGROUND_MAX_SIZE, EMPTY_TILE}; use glam::IVec2; /// Tilemap which will be rendered on screen @@ -28,3 +28,19 @@ impl Default for Background { } } } + +impl Background { + /// Clears all tiles of given bg + pub fn clear(&mut self) { + self.fill(EMPTY_TILE); + } + + /// Sets all tiles to val + pub fn fill(&mut self, val: u16) { + for x in 0..BACKGROUND_MAX_SIZE { + for y in 0..BACKGROUND_MAX_SIZE { + self.tiles[x][y] = val; + } + } + } +} diff --git a/src/gsa.rs b/src/gsa.rs index d879895..1112322 100644 --- a/src/gsa.rs +++ b/src/gsa.rs @@ -13,19 +13,22 @@ use glam::IVec2; /// Complete state of GSA pub struct Gsa { /// Sprites available - pub sprites: [Sprite; MAX_SPRITES], + pub sprite: [Sprite; MAX_SPRITES], /// Palette used to draw graphics, initially loaded from gfx.gif pub palette: [Rgb; 256], /// Tilemap layers available, layer 0 in the back, layer 3 in front; layer 3 defaults to half-tile mode - pub bgs: [Background; MAX_BACKGROUNDS], + pub bg: [Background; MAX_BACKGROUNDS], /// Currently selected font /// /// Chosen as half-size tile index, extends 16x16 half tiles in x and y pub font: u16, + /// Target BG for string functions, defaults to layer 3 + pub str_bg: usize, + pub(crate) pressed: Buttons, pub(crate) released: Buttons, pub(crate) down: Buttons, @@ -33,47 +36,33 @@ pub struct Gsa { } impl Gsa { - /// Clears all tiles of given bg - pub fn clear_bg(&mut self, bg: usize) { - self.fill_bg(bg, EMPTY_TILE); - } - /// Resets all bg pub fn reset_bgs(&mut self) { for bg in 0..MAX_BACKGROUNDS { - self.clear_bg(bg); - self.bgs[bg].scroll = IVec2::ZERO; - self.bgs[bg].half_tile = false; + self.bg[bg].clear(); + self.bg[bg].scroll = IVec2::ZERO; + self.bg[bg].half_tile = false; } //todo: document ui layer defaulting to half tile - self.bgs[3].half_tile = true; + self.bg[3].half_tile = true; } /// Clears all sprites pub fn reset_sprites(&mut self) { for sprite in 0..MAX_SPRITES { - self.sprites[sprite].pos = IVec2::ZERO; - self.sprites[sprite].tile = EMPTY_TILE; - self.sprites[sprite].priority = 0; - } - } - - /// Sets all tiles of bg to val - pub fn fill_bg(&mut self, bg: usize, val: u16) { - for x in 0..BACKGROUND_MAX_SIZE { - for y in 0..BACKGROUND_MAX_SIZE { - self.bgs[bg].tiles[x][y] = val; - } + self.sprite[sprite].pos = IVec2::ZERO; + self.sprite[sprite].tile = EMPTY_TILE; + self.sprite[sprite].priority = 0; } } /// 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; + self.bg[i].size = map.size; for y in 0..map.size.y 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] + self.bg[i].tiles[x][y] = map.data[x + y * map.size.x as usize] } } } @@ -84,9 +73,9 @@ impl Gsa { self.maps.maps.insert( map, [ - Tilemap::from_bg(&self.bgs[0]), - Tilemap::from_bg(&self.bgs[1]), - Tilemap::from_bg(&self.bgs[2]), + Tilemap::from_bg(&self.bg[0]), + Tilemap::from_bg(&self.bg[1]), + Tilemap::from_bg(&self.bg[2]), ], ); } @@ -122,10 +111,20 @@ impl Gsa { } /// Write given string on given map, at given position, with font [Gsa::font] - pub fn write_string(&mut self, map: usize, pos: IVec2, str: &str) { + pub fn write_string(&mut self, pos: IVec2, str: &str) { let str = AsciiStr::from_ascii(str).unwrap(); for (i, ch) in str.into_iter().enumerate() { - self.bgs[map].tiles[pos.x as usize + i][pos.y as usize] = self.get_char_tile(*ch); + self.bg[self.str_bg].tiles[pos.x as usize + i][pos.y as usize] = + self.get_char_tile(*ch); + } + } + + /// Write given string vertically on given map, at given position, with font [Gsa::font] + pub fn write_string_vertical(&mut self, pos: IVec2, str: &str) { + let str = AsciiStr::from_ascii(str).unwrap(); + for (i, ch) in str.into_iter().enumerate() { + self.bg[self.str_bg].tiles[pos.x as usize][pos.y as usize + i] = + self.get_char_tile(*ch); } } diff --git a/src/mapedit.rs b/src/mapedit.rs index ed1fa9b..294988e 100644 --- a/src/mapedit.rs +++ b/src/mapedit.rs @@ -125,13 +125,12 @@ impl Background { } 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.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( - 3, IVec2 { x: pos.x * 2 + 2, y: pos.y * 2 + 1, @@ -139,7 +138,6 @@ fn draw_input_window(gsa: &mut Gsa, pos: IVec2, caption: &str) { caption, ); gsa.write_string( - 3, IVec2 { x: pos.x * 2 + 3, y: pos.y * 2 + 3, @@ -150,7 +148,6 @@ fn draw_input_window(gsa: &mut Gsa, pos: IVec2, caption: &str) { 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, @@ -159,68 +156,105 @@ fn update_input_window(gsa: &mut Gsa, pos: IVec2, val: u16) { ); } -fn set_state(state: State, gsa1: &mut Gsa, gsa2: &mut Gsa, current_layer: usize, all_layers: bool) { +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.bgs[0].active = true; - gsa1.bgs[1].active = true; - gsa1.bgs[2].active = true; + gsa1.bg[0].active = true; + gsa1.bg[1].active = true; + gsa1.bg[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; + gsa1.bg[0].active = current_layer == 0; + gsa1.bg[1].active = current_layer == 1; + gsa1.bg[2].active = current_layer == 2; } - gsa2.clear_bg(2); - gsa2.clear_bg(3); - draw_toolbar(gsa1, gsa2, current_layer, all_layers); + gsa2.bg[2].clear(); + gsa2.bg[2].clear(); + draw_toolbar(gsa1, gsa2, current_layer, all_layers, current_map); - 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; + 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); - 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; + 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.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; + 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) { +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.bgs[2].tiles[0][y] = 0x7010; + gsa2.bg[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; + 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.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.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.bgs[2].size = IVec2 { x: 120, y: 68 }; //enough to cover 4k monitors? <_< - gsa2.bgs[3].size = IVec2 { x: 240, y: 136 }; + 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() { @@ -237,14 +271,15 @@ pub(crate) fn run_mapedit() { }; let mut gsa1 = Gsa { - sprites: [Sprite::default(); MAX_SPRITES], + sprite: [Sprite::default(); MAX_SPRITES], palette, - bgs: Default::default(), + bg: Default::default(), font: FONT_BOLD, pressed: 0, released: 0, down: 0, maps, + str_bg: 3, }; gsa1.reset_bgs(); @@ -255,26 +290,27 @@ pub(crate) fn run_mapedit() { } for i in 0..3 { - gsa1.bgs[i].scroll = IVec2 { + gsa1.bg[i].scroll = IVec2 { x: -16 - 32, y: -32, }; } let mut gsa2 = Gsa { - sprites: [Sprite::default(); MAX_SPRITES], + sprite: [Sprite::default(); MAX_SPRITES], palette, - bgs: Default::default(), + 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.bgs[i].scroll = IVec2 { + gsa2.bg[i].scroll = IVec2 { x: -16 - 32, y: -32, }; @@ -282,7 +318,7 @@ pub(crate) fn run_mapedit() { for y in 0..TILESET_SIZE { for x in 0..TILESET_SIZE { - gsa2.bgs[0].tiles[x][y] = (x + (y << 8)) as u16; + gsa2.bg[0].tiles[x][y] = (x + (y << 8)) as u16; } } //draw_input_window(&mut gsa2, IVec2 { x: 5, y: 3 }, "Create Map"); @@ -308,13 +344,20 @@ pub(crate) fn run_mapedit() { let mut middle_down = false; let mut right_down = false; let mut selected_tile = IVec2::ZERO; - gsa2.bgs[1].tiles[0][0] = TILE_MARKER; + 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); + 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; @@ -347,6 +390,7 @@ pub(crate) fn run_mapedit() { &mut gsa2, *current_layer, *all_layers, + *current_map, ); } else { *state = State::Edit; @@ -356,6 +400,7 @@ pub(crate) fn run_mapedit() { &mut gsa2, *current_layer, *all_layers, + *current_map, ); } } @@ -409,6 +454,7 @@ pub(crate) fn run_mapedit() { &mut gsa2, *current_layer, *all_layers, + *current_map, ); update_input_window( &mut gsa2, @@ -424,6 +470,7 @@ pub(crate) fn run_mapedit() { &mut gsa2, *current_layer, *all_layers, + *current_map, ); } BUT_LAYER2 => { @@ -434,6 +481,7 @@ pub(crate) fn run_mapedit() { &mut gsa2, *current_layer, *all_layers, + *current_map, ); } BUT_LAYER3 => { @@ -444,6 +492,7 @@ pub(crate) fn run_mapedit() { &mut gsa2, *current_layer, *all_layers, + *current_map, ); } BUT_LAYERS => { @@ -454,6 +503,7 @@ pub(crate) fn run_mapedit() { &mut gsa2, *current_layer, *all_layers, + *current_map, ); } BUT_EXIT => { @@ -484,6 +534,7 @@ pub(crate) fn run_mapedit() { &mut gsa2, *current_layer, *all_layers, + *current_map, ); } else if mouse_pos.x / 16 == 20 { println!("no"); @@ -494,6 +545,7 @@ pub(crate) fn run_mapedit() { &mut gsa2, *current_layer, *all_layers, + *current_map, ); } } @@ -531,14 +583,14 @@ pub(crate) fn run_mapedit() { match *state { State::Edit => { // normal mode - gsa1.bgs[0].scroll -= delta; - gsa1.bgs[1].scroll -= delta; - gsa1.bgs[2].scroll -= delta; + gsa1.bg[0].scroll -= delta; + gsa1.bg[1].scroll -= delta; + gsa1.bg[2].scroll -= delta; } State::SelectTile => { // tile select mode - gsa2.bgs[0].scroll -= delta; - gsa2.bgs[1].scroll -= delta; + gsa2.bg[0].scroll -= delta; + gsa2.bg[1].scroll -= delta; //gsa2.bgs[2].scroll -= delta; } _ => {} @@ -546,17 +598,17 @@ pub(crate) fn run_mapedit() { } *mouse_pos = new_pos; *tile_pos = IVec2 { - 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), + 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.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), + 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.bgs[0].scroll; - let cursor_pos2 = *tile_pos2 * TILE_SIZE as i32 - gsa2.bgs[0].scroll; - gsa1.sprites[SPR_CURSOR].pos = cursor_pos; - gsa2.sprites[SPR_CURSOR].pos = cursor_pos2; + 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; } _ => {} }, @@ -571,7 +623,7 @@ pub(crate) fn run_mapedit() { && tile_pos.y < BACKGROUND_MAX_SIZE as i32 { let tile = (selected_tile.x + (selected_tile.y << 8)) as u16; - gsa1.bgs[*current_layer].tiles[tile_pos.x as usize] + gsa1.bg[*current_layer].tiles[tile_pos.x as usize] [tile_pos.y as usize] = tile; } } else if *right_down { @@ -580,7 +632,7 @@ pub(crate) fn run_mapedit() { && tile_pos.x < BACKGROUND_MAX_SIZE as i32 && tile_pos.y < BACKGROUND_MAX_SIZE as i32 { - gsa1.bgs[*current_layer].tiles[tile_pos.x as usize] + gsa1.bg[*current_layer].tiles[tile_pos.x as usize] [tile_pos.y as usize] = EMPTY_TILE; } } @@ -592,10 +644,10 @@ pub(crate) fn run_mapedit() { && tile_pos2.x < BACKGROUND_MAX_SIZE as i32 && tile_pos2.y < BACKGROUND_MAX_SIZE as i32 { - gsa2.bgs[1].tiles[selected_tile.x as usize] + gsa2.bg[1].tiles[selected_tile.x as usize] [selected_tile.y as usize] = EMPTY_TILE; *selected_tile = *tile_pos2; - gsa2.bgs[1].tiles[selected_tile.x as usize] + gsa2.bg[1].tiles[selected_tile.x as usize] [selected_tile.y as usize] = TILE_MARKER; } } @@ -626,10 +678,10 @@ pub(crate) fn run_mapedit() { }; match *state { State::Edit => { - 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; + 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;