all the things and even documentation... <_<
This commit is contained in:
commit
a370ec7fd0
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
/Cargo.lock
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="CPP_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||||
|
<option name="processCode" value="true" />
|
||||||
|
<option name="processLiterals" value="true" />
|
||||||
|
<option name="processComments" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/gsa.iml" filepath="$PROJECT_DIR$/.idea/gsa.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "gsa"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
glam = "0.24.0"
|
||||||
|
ascii = "1.1.0"
|
||||||
|
gilrs = "0.10.2"
|
||||||
|
|
||||||
|
[dependencies.skunk2d]
|
||||||
|
path = "../skunk2d"
|
||||||
|
|
||||||
|
[profile.release-dani]
|
||||||
|
inherits = "release"
|
||||||
|
opt-level = "z"
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
strip = true
|
||||||
|
panic = "abort"
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
use glam::IVec2;
|
||||||
|
use gsa::{run, Gsa};
|
||||||
|
|
||||||
|
struct Game {}
|
||||||
|
|
||||||
|
fn init(gsa: &mut Gsa) -> Game {
|
||||||
|
gsa.sprites[0].tile = 0x0300;
|
||||||
|
gsa.sprites[1].tile = 0x0200;
|
||||||
|
gsa.maps[0].tiles[0][0] = 0x0300;
|
||||||
|
gsa.maps[1].half_tile = true;
|
||||||
|
gsa.write_string(1, IVec2::ONE, "Hello world nyaa~");
|
||||||
|
Game {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(_game: &mut Game, gsa: &mut Gsa) {
|
||||||
|
gsa.sprites[0].pos.x = (gsa.sprites[0].pos.x + 1) % 300;
|
||||||
|
gsa.sprites[1].pos += gsa.input.dir;
|
||||||
|
if gsa.input.pressed.face_down {
|
||||||
|
gsa.sprites[1].tile += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run!(init, update);
|
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
|
@ -0,0 +1,47 @@
|
||||||
|
/// State of all GSA buttons
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
pub struct Buttons {
|
||||||
|
/// UP on dpad
|
||||||
|
pub dpad_up: bool,
|
||||||
|
/// DOWN on dpad
|
||||||
|
pub dpad_down: bool,
|
||||||
|
/// LEFT on dpad
|
||||||
|
pub dpad_left: bool,
|
||||||
|
/// RIGHT on dpad
|
||||||
|
pub dpad_right: bool,
|
||||||
|
/// X on nintendo, Y on microsoft, TRIANGLE on sony
|
||||||
|
pub face_up: bool,
|
||||||
|
/// B on nintendo, A on microsoft, CROSS on sony
|
||||||
|
pub face_down: bool,
|
||||||
|
/// Y on nintendo, X on microsoft, SQUARE on sony
|
||||||
|
pub face_left: bool,
|
||||||
|
/// A on nintendo, B on microsoft, CIRCLE on sony
|
||||||
|
pub face_right: bool,
|
||||||
|
/// left shoulder button
|
||||||
|
pub l: bool,
|
||||||
|
/// right shoulder button
|
||||||
|
pub r: bool,
|
||||||
|
/// start button
|
||||||
|
pub start: bool,
|
||||||
|
/// select button
|
||||||
|
pub select: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Buttons {
|
||||||
|
pub(crate) fn pressed(old: &Buttons, new: &Buttons) -> Buttons {
|
||||||
|
Buttons {
|
||||||
|
dpad_up: !old.dpad_up && new.dpad_up,
|
||||||
|
dpad_down: !old.dpad_down && new.dpad_down,
|
||||||
|
dpad_left: !old.dpad_left && new.dpad_left,
|
||||||
|
dpad_right: !old.dpad_right && new.dpad_right,
|
||||||
|
face_up: !old.face_up && new.face_up,
|
||||||
|
face_down: !old.face_down && new.face_down,
|
||||||
|
face_left: !old.face_left && new.face_left,
|
||||||
|
face_right: !old.face_right && new.face_right,
|
||||||
|
l: !old.l && new.l,
|
||||||
|
r: !old.r && new.r,
|
||||||
|
start: !old.start && new.start,
|
||||||
|
select: !old.select && new.select,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
use crate::input::Input;
|
||||||
|
use crate::rgb::Rgb;
|
||||||
|
use crate::sprite::Sprite;
|
||||||
|
use crate::tilemap::Tilemap;
|
||||||
|
use crate::{MAX_SPRITES, MAX_TILEMAPS, TILEMAP_MAX_SIZE};
|
||||||
|
use ascii::{AsciiChar, AsciiStr};
|
||||||
|
use glam::IVec2;
|
||||||
|
|
||||||
|
/// Complete state of GSA
|
||||||
|
pub struct Gsa {
|
||||||
|
/// Sprites available
|
||||||
|
pub sprites: [Sprite; MAX_SPRITES],
|
||||||
|
|
||||||
|
/// Palette used to draw graphics, initially loaded from gfx.gif
|
||||||
|
pub palette: [Rgb; 256],
|
||||||
|
|
||||||
|
/// Tilemap layers available
|
||||||
|
pub maps: [Tilemap; MAX_TILEMAPS],
|
||||||
|
|
||||||
|
/// Currently selected font
|
||||||
|
///
|
||||||
|
/// Chosen as half-size tile index, extends 16x16 half tiles in x and y
|
||||||
|
pub font: u16,
|
||||||
|
|
||||||
|
/// Current input state
|
||||||
|
pub input: Input,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gsa {
|
||||||
|
/// Clears all tiles of given map to 0
|
||||||
|
pub fn clear_map(&mut self, map: usize) {
|
||||||
|
self.fill_map(map, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets all tiles of map to val
|
||||||
|
pub fn fill_map(&mut self, map: usize, val: u16) {
|
||||||
|
for x in 0..TILEMAP_MAX_SIZE {
|
||||||
|
for y in 0..TILEMAP_MAX_SIZE {
|
||||||
|
self.maps[map].tiles[x][y] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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) {
|
||||||
|
let str = AsciiStr::from_ascii(str).unwrap();
|
||||||
|
for (i, ch) in str.into_iter().enumerate() {
|
||||||
|
self.maps[map].tiles[pos.x as usize + i][pos.y as usize] = self.get_char_tile(*ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_char_tile(&self, ch: AsciiChar) -> u16 {
|
||||||
|
let ch = ch as u16;
|
||||||
|
self.font + (ch % 0x10) + (ch / 0x10) * 0x100
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
use crate::buttons::Buttons;
|
||||||
|
use glam::IVec2;
|
||||||
|
|
||||||
|
/// Input State
|
||||||
|
///
|
||||||
|
/// GSA pretends all input is a snes-like gamepad
|
||||||
|
/// d-pad, 4 face buttons, l and r shoulder buttons, start and select
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Input {
|
||||||
|
/// Buttons that are currentle held down
|
||||||
|
pub down: Buttons,
|
||||||
|
|
||||||
|
/// Buttons that have been pressed in the current frame
|
||||||
|
pub pressed: Buttons,
|
||||||
|
|
||||||
|
/// Directional vector (-1 to 1) from current dpad state
|
||||||
|
pub dir: IVec2,
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
//! # Game Skunk Advance
|
||||||
|
//! Game development library modelled after an imaginary console
|
||||||
|
//!
|
||||||
|
//! ## Specs
|
||||||
|
//! - Resolution: 304x176 (19x11 tiles)
|
||||||
|
//! - Colors: 256 (indexed out of a possible 24-bit)
|
||||||
|
//! - Tilesize: 16x16 (or 8x8 for half-tiles)
|
||||||
|
//! - Tileset: 65536 tiles, indexed via 0xYYXX
|
||||||
|
//! - Sprites: 256 of size 16x16 (pondering allowing larger sprites)
|
||||||
|
//! - Tilemaps: 4 of size 1024x1024, scrollable
|
||||||
|
//!
|
||||||
|
//! ## Features not yet implemented
|
||||||
|
//! - Sound (no samples
|
||||||
|
//! - Synth
|
||||||
|
//! - Speech
|
||||||
|
//! - Savegames
|
||||||
|
//! - Helpers
|
||||||
|
//! - Gamepad text keyboard input
|
||||||
|
//! - Menus
|
||||||
|
|
||||||
|
mod buttons;
|
||||||
|
mod gsa;
|
||||||
|
mod input;
|
||||||
|
mod rgb;
|
||||||
|
mod run;
|
||||||
|
mod sprite;
|
||||||
|
mod state;
|
||||||
|
mod tilemap;
|
||||||
|
|
||||||
|
pub use crate::buttons::Buttons;
|
||||||
|
pub use crate::gsa::Gsa;
|
||||||
|
pub use crate::input::Input;
|
||||||
|
pub use crate::rgb::Rgb;
|
||||||
|
pub use crate::run::run;
|
||||||
|
pub use crate::sprite::Sprite;
|
||||||
|
pub use crate::tilemap::Tilemap;
|
||||||
|
|
||||||
|
/// Amount of sprites in [Gsa::sprites]
|
||||||
|
pub const MAX_SPRITES: usize = 0xff;
|
||||||
|
|
||||||
|
/// X and y dimensions of maps in [Gsa::maps]
|
||||||
|
pub const TILEMAP_MAX_SIZE: usize = 1024;
|
||||||
|
|
||||||
|
/// Amount of tile maps in [Gsa::maps]
|
||||||
|
pub const MAX_TILEMAPS: usize = 4;
|
|
@ -0,0 +1,10 @@
|
||||||
|
/// RGB Color
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Rgb {
|
||||||
|
/// Red component 0-255
|
||||||
|
pub r: u8,
|
||||||
|
/// Green component 0-255
|
||||||
|
pub g: u8,
|
||||||
|
/// Blue component 0-255
|
||||||
|
pub b: u8,
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
use crate::state::State;
|
||||||
|
use crate::{Gsa, Rgb, Sprite, MAX_SPRITES};
|
||||||
|
use gilrs::Gilrs;
|
||||||
|
use skunk2d::Image8;
|
||||||
|
|
||||||
|
/// Creates main function, includes gfx.gif, and calls run
|
||||||
|
///
|
||||||
|
/// Pass the following two functions to it:
|
||||||
|
/// - fn init(gsa: &mut Gsa) -> TGame
|
||||||
|
/// - fn update(game: &mut Game, gsa: &mut Gsa)
|
||||||
|
///
|
||||||
|
/// TGame can be any type, usually a struct that contains your game's state
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! run {
|
||||||
|
($init: ident, $update: ident) => {
|
||||||
|
fn main() {
|
||||||
|
run($init, $update, include_bytes!("gfx.gif"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is called by [run!]
|
||||||
|
pub fn run<TGame: 'static>(
|
||||||
|
init_fn: fn(gsa: &mut Gsa) -> TGame,
|
||||||
|
update_fn: fn(game: &mut TGame, gsa: &mut Gsa),
|
||||||
|
image_data: &[u8],
|
||||||
|
) {
|
||||||
|
let tileset = Image8::load_data(image_data);
|
||||||
|
let pal = Image8::load_data_palette(image_data);
|
||||||
|
let mut palette = [Rgb { r: 0, g: 0, b: 0 }; 256];
|
||||||
|
|
||||||
|
for i in 0..pal.len() / 3 {
|
||||||
|
palette[i] = Rgb {
|
||||||
|
r: pal[i * 3 + 2],
|
||||||
|
g: pal[i * 3 + 1],
|
||||||
|
b: pal[i * 3],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut gsa = Gsa {
|
||||||
|
sprites: [Sprite::default(); MAX_SPRITES],
|
||||||
|
maps: Default::default(),
|
||||||
|
palette,
|
||||||
|
font: 0x1010,
|
||||||
|
input: Default::default(),
|
||||||
|
};
|
||||||
|
let game = init_fn(&mut gsa);
|
||||||
|
let state = State::<TGame> {
|
||||||
|
gsa,
|
||||||
|
game,
|
||||||
|
tileset,
|
||||||
|
update_fn,
|
||||||
|
first: true,
|
||||||
|
gilrs: Gilrs::new().unwrap(),
|
||||||
|
};
|
||||||
|
skunk2d::run_with::<Image8, State<TGame>>(304, 176, 60, state);
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
use glam::IVec2;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
|
/// Sprite which will be displayed on screen, unless tile=0
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
pub struct Sprite {
|
||||||
|
/// Position on screen
|
||||||
|
pub pos: IVec2,
|
||||||
|
/// Tile index
|
||||||
|
pub tile: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Sprite {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "[pos: {}, tile: {}]", self.pos, self.tile)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
use crate::buttons::Buttons;
|
||||||
|
use crate::{Gsa, TILEMAP_MAX_SIZE};
|
||||||
|
use gilrs::{Button, EventType, Gilrs};
|
||||||
|
use glam::IVec2;
|
||||||
|
use skunk2d::{Event, IRect, Image8, WindowState};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub struct State<TGame> {
|
||||||
|
pub(crate) gsa: Gsa,
|
||||||
|
pub(crate) game: TGame,
|
||||||
|
pub(crate) update_fn: fn(game: &mut TGame, gsa: &mut Gsa),
|
||||||
|
pub(crate) tileset: Rc<Image8>,
|
||||||
|
pub(crate) first: bool,
|
||||||
|
pub(crate) gilrs: Gilrs,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TGame> skunk2d::Game<Image8> for State<TGame> {
|
||||||
|
fn update(&mut self, window_state: &mut WindowState) {
|
||||||
|
if self.first {
|
||||||
|
window_state.toggle_fullscreen();
|
||||||
|
self.first = false;
|
||||||
|
}
|
||||||
|
let mut new_buttons = self.gsa.input.down;
|
||||||
|
while let Some(event) = self.gilrs.next_event() {
|
||||||
|
match event {
|
||||||
|
gilrs::Event {
|
||||||
|
event: EventType::ButtonPressed(button, _),
|
||||||
|
..
|
||||||
|
} => match button {
|
||||||
|
Button::South => new_buttons.face_down = true,
|
||||||
|
Button::East => new_buttons.face_left = true,
|
||||||
|
Button::North => new_buttons.face_up = true,
|
||||||
|
Button::West => new_buttons.face_right = true,
|
||||||
|
Button::LeftTrigger | Button::LeftTrigger2 => new_buttons.l = true,
|
||||||
|
Button::RightTrigger | Button::RightTrigger2 => new_buttons.r = true,
|
||||||
|
Button::Select => new_buttons.select = true,
|
||||||
|
Button::Start => new_buttons.start = true,
|
||||||
|
Button::DPadUp => new_buttons.dpad_up = true,
|
||||||
|
Button::DPadDown => new_buttons.dpad_down = true,
|
||||||
|
Button::DPadLeft => new_buttons.dpad_left = true,
|
||||||
|
Button::DPadRight => new_buttons.dpad_right = true,
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
gilrs::Event {
|
||||||
|
event: EventType::ButtonReleased(button, _),
|
||||||
|
..
|
||||||
|
} => match button {
|
||||||
|
Button::South => new_buttons.face_down = false,
|
||||||
|
Button::East => new_buttons.face_left = false,
|
||||||
|
Button::North => new_buttons.face_up = false,
|
||||||
|
Button::West => new_buttons.face_right = false,
|
||||||
|
Button::LeftTrigger | Button::LeftTrigger2 => new_buttons.l = false,
|
||||||
|
Button::RightTrigger | Button::RightTrigger2 => new_buttons.r = false,
|
||||||
|
Button::Select => new_buttons.select = false,
|
||||||
|
Button::Start => new_buttons.start = false,
|
||||||
|
Button::DPadUp => new_buttons.dpad_up = false,
|
||||||
|
Button::DPadDown => new_buttons.dpad_down = false,
|
||||||
|
Button::DPadLeft => new_buttons.dpad_left = false,
|
||||||
|
Button::DPadRight => new_buttons.dpad_right = false,
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.gsa.input.pressed = Buttons::pressed(&self.gsa.input.down, &new_buttons);
|
||||||
|
self.gsa.input.down = new_buttons;
|
||||||
|
self.gsa.input.dir = IVec2 {
|
||||||
|
x: if self.gsa.input.down.dpad_left { -1 } else { 0 }
|
||||||
|
+ if self.gsa.input.down.dpad_right { 1 } else { 0 },
|
||||||
|
y: if self.gsa.input.down.dpad_up { -1 } else { 0 }
|
||||||
|
+ if self.gsa.input.down.dpad_down { 1 } else { 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
//todo: don't if not updated... how check? <_<
|
||||||
|
for i in 0..=255 {
|
||||||
|
window_state.set_palette(
|
||||||
|
i,
|
||||||
|
self.gsa.palette[i as usize].r,
|
||||||
|
self.gsa.palette[i as usize].g,
|
||||||
|
self.gsa.palette[i as usize].b,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(self.update_fn)(&mut self.game, &mut self.gsa);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_event(&mut self, _window_state: &mut WindowState, _event: Event) {}
|
||||||
|
|
||||||
|
fn draw(&self, target: &mut Image8) {
|
||||||
|
target.clear();
|
||||||
|
|
||||||
|
for map in &self.gsa.maps {
|
||||||
|
let tcmult = if map.half_tile { 2 } else { 1 };
|
||||||
|
let tilesize = if map.half_tile { 8 } else { 16 };
|
||||||
|
let mut startx = map.scroll.x / tilesize;
|
||||||
|
let mut starty = map.scroll.y / tilesize;
|
||||||
|
let endx = (TILEMAP_MAX_SIZE as i32).min(startx + 20 * tcmult);
|
||||||
|
let endy = (TILEMAP_MAX_SIZE as i32).min(starty + 12 * tcmult);
|
||||||
|
startx = 0.max(startx);
|
||||||
|
starty = 0.max(starty);
|
||||||
|
for x in startx..endx {
|
||||||
|
for y in starty..endy {
|
||||||
|
let tile = map.tiles[x as usize][y as usize];
|
||||||
|
if tile > 0 {
|
||||||
|
let ty = tile / 0x100;
|
||||||
|
let tx = tile % 0x100;
|
||||||
|
target.draw_image_partial(
|
||||||
|
IVec2 {
|
||||||
|
x: x * tilesize - map.scroll.x,
|
||||||
|
y: y * tilesize - map.scroll.y,
|
||||||
|
},
|
||||||
|
&self.tileset,
|
||||||
|
IRect {
|
||||||
|
pos: IVec2 {
|
||||||
|
x: tx as i32 * tilesize,
|
||||||
|
y: ty as i32 * tilesize,
|
||||||
|
},
|
||||||
|
size: IVec2 {
|
||||||
|
x: tilesize,
|
||||||
|
y: tilesize,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for sprite in &self.gsa.sprites {
|
||||||
|
if sprite.tile > 0 {
|
||||||
|
let ty = sprite.tile / 0x100;
|
||||||
|
let tx = sprite.tile % 0x100;
|
||||||
|
target.draw_image_partial(
|
||||||
|
sprite.pos,
|
||||||
|
&self.tileset,
|
||||||
|
IRect {
|
||||||
|
pos: IVec2 {
|
||||||
|
x: tx as i32 * 16,
|
||||||
|
y: ty as i32 * 16,
|
||||||
|
},
|
||||||
|
size: IVec2 { x: 16, y: 16 },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
use crate::TILEMAP_MAX_SIZE;
|
||||||
|
use glam::IVec2;
|
||||||
|
|
||||||
|
/// Tilemap which will be rendered on screen
|
||||||
|
pub struct Tilemap {
|
||||||
|
/// Tiles in idx, accessible via \[x\]\[y\], x and y 0..[TILEMAP_MAX_SIZE]
|
||||||
|
pub tiles: Box<[[u16; TILEMAP_MAX_SIZE]; TILEMAP_MAX_SIZE]>,
|
||||||
|
/// Camera scroll (negative draw offset) for rendering
|
||||||
|
pub scroll: IVec2,
|
||||||
|
/// Are tiles indices half-tile indices?
|
||||||
|
pub half_tile: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Tilemap {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
tiles: [[0u16; TILEMAP_MAX_SIZE]; TILEMAP_MAX_SIZE].into(),
|
||||||
|
scroll: IVec2::ZERO,
|
||||||
|
half_tile: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue