Merge remote-tracking branch 'origin/master' into better-backgrounds-gwilym

This commit is contained in:
Gwilym Kuiper 2022-03-07 22:41:11 +00:00
commit cdd77afe22
42 changed files with 2285 additions and 1357 deletions

View file

@ -33,5 +33,9 @@ jobs:
- name: Install gbafix - name: Install gbafix
run: cargo install gbafix run: cargo install gbafix
- uses: extractions/setup-just@v1 - uses: extractions/setup-just@v1
- name: Setup mdBook
uses: peaceiris/actions-mdbook@v1
with:
mdbook-version: '0.4.13'
- name: Build and test all crates - name: Build and test all crates
run: just ci run: just ci

View file

@ -1,38 +0,0 @@
name: Build book
on:
push:
branches: [ master ]
paths:
'book/**'
pull_request:
branches: [ master ]
paths:
'book/**'
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-20.04
steps:
- name: Set CARGO_TARGET_DIR
run: echo "CARGO_TARGET_DIR=$HOME/target" >> $GITHUB_ENV
- name: Install build tools
run: sudo apt-get update && sudo apt-get install build-essential binutils-arm-none-eabi libelf-dev zip -y
- uses: actions/checkout@v3
- name: Cache
uses: actions/cache@v2.1.7
with:
path: |
~/.cargo/registry
~/.cargo/git
~/target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install mdbook
run: cargo install mdbook
- name: Build the book
run: mdbook build
working-directory: book

View file

@ -38,8 +38,10 @@ jobs:
tag: ${{ github.ref }} tag: ${{ github.ref }}
overwrite: true overwrite: true
- name: Install mdbook - name: Setup mdBook
run: cargo install mdbook uses: peaceiris/actions-mdbook@v1
with:
mdbook-version: '0.4.13'
- name: Build the book - name: Build the book
run: just build-book run: just build-book
- name: Deploy the book - name: Deploy the book

66
agb-fixnum/Cargo.lock generated
View file

@ -15,39 +15,9 @@ version = "0.1.0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rand",
"syn", "syn",
] ]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "getrandom"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.36" version = "1.0.36"
@ -66,36 +36,6 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.86" version = "1.0.86"
@ -112,9 +52,3 @@ name = "unicode-xid"
version = "0.2.2" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"

View file

@ -18,6 +18,7 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
name = "agb_image_converter" name = "agb_image_converter"
version = "0.6.0" version = "0.6.0"
dependencies = [ dependencies = [
"asefile",
"image", "image",
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -26,6 +27,20 @@ dependencies = [
"toml", "toml",
] ]
[[package]]
name = "asefile"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0d5f7de918fd4cb18249819fc4bd27f6a5dbfbc9dcb271727f27dacf17ce880"
dependencies = [
"bitflags",
"byteorder",
"flate2",
"image",
"log",
"nohash",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -40,9 +55,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.7.3" version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@ -73,18 +88,31 @@ dependencies = [
[[package]] [[package]]
name = "deflate" name = "deflate"
version = "1.0.0" version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
dependencies = [ dependencies = [
"adler32", "adler32",
"byteorder",
]
[[package]]
name = "flate2"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide 0.4.4",
] ]
[[package]] [[package]]
name = "image" name = "image"
version = "0.24.1" version = "0.23.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db207d030ae38f1eb6f240d5a1c1c88ff422aa005d10f8c6c6fc5e75286ab30e" checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"byteorder", "byteorder",
@ -96,14 +124,45 @@ dependencies = [
] ]
[[package]] [[package]]
name = "miniz_oxide" name = "libc"
version = "0.5.1" version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "miniz_oxide"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [
"adler32",
]
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [ dependencies = [
"adler", "adler",
"autocfg",
] ]
[[package]]
name = "nohash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca"
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.44" version = "0.1.44"
@ -127,9 +186,9 @@ dependencies = [
[[package]] [[package]]
name = "num-rational" name = "num-rational"
version = "0.4.0" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"num-integer", "num-integer",
@ -147,14 +206,14 @@ dependencies = [
[[package]] [[package]]
name = "png" name = "png"
version = "0.17.4" version = "0.16.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02cd7d51cea7e2fa6bbcb8af5fbcad15b871451bfc2d20ed72dff2f4ae072a84" checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crc32fast", "crc32fast",
"deflate", "deflate",
"miniz_oxide", "miniz_oxide 0.3.7",
] ]
[[package]] [[package]]

View file

@ -10,9 +10,10 @@ description = "Library for converting graphics for use on the Game Boy Advance"
proc-macro = true proc-macro = true
[dependencies] [dependencies]
image = { version = "0.24.1", default-features = false, features = [ "png", "bmp" ] } image = { version = "0.23", default-features = false, features = [ "png", "bmp" ] }
toml = "0.5.8" toml = "0.5.8"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
syn = "1.0.86" syn = "1.0.86"
proc-macro2 = "1.0.36" proc-macro2 = "1.0.36"
quote = "1.0.15" quote = "1.0.15"
asefile = "0.3.2"

View file

@ -0,0 +1,23 @@
use std::path::Path;
use asefile::{AsepriteFile, Tag};
use image::DynamicImage;
pub fn generate_from_file(filename: &Path) -> (Vec<DynamicImage>, Vec<Tag>) {
let ase = AsepriteFile::read_file(filename).expect("Aseprite file should exist");
let mut images = Vec::new();
let mut tags = Vec::new();
for frame in 0..ase.num_frames() {
let image = ase.frame(frame).image();
images.push(DynamicImage::ImageRgba8(image))
}
for tag in 0..ase.num_tags() {
tags.push(ase.tag(tag).clone())
}
(images, tags)
}

View file

@ -13,6 +13,10 @@ pub(crate) struct Image {
impl Image { impl Image {
pub fn load_from_file(image_path: &path::Path) -> Self { pub fn load_from_file(image_path: &path::Path) -> Self {
let img = image::open(image_path).expect("Expected image to exist"); let img = image::open(image_path).expect("Expected image to exist");
Self::load_from_dyn_image(img)
}
pub fn load_from_dyn_image(img: image::DynamicImage) -> Self {
let (width, height) = img.dimensions(); let (width, height) = img.dimensions();
let width = width as usize; let width = width as usize;

View file

@ -1,16 +1,22 @@
use palette16::Palette16OptimisationResults;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use syn::parse_macro_input; use proc_macro2::Literal;
use syn::parse::Parser;
use syn::{parse_macro_input, punctuated::Punctuated, LitStr};
use std::path::Path; use std::path::PathBuf;
use std::{iter, path::Path, str};
use quote::{format_ident, quote}; use quote::{format_ident, quote, ToTokens};
mod aseprite;
mod colour; mod colour;
mod config; mod config;
mod image_loader; mod image_loader;
mod palette16; mod palette16;
mod rust_generator; mod rust_generator;
use image::GenericImageView;
use image_loader::Image; use image_loader::Image;
use colour::Colour; use colour::Colour;
@ -56,7 +62,7 @@ pub fn include_gfx(input: TokenStream) -> TokenStream {
let images = config.images(); let images = config.images();
let image_code = images.iter().map(|(image_name, &image)| { let image_code = images.iter().map(|(image_name, &image)| {
convert_image(image, parent, &image_name, &config.crate_prefix()) convert_image(image, parent, image_name, &config.crate_prefix())
}); });
let module = quote! { let module = quote! {
@ -70,6 +76,126 @@ pub fn include_gfx(input: TokenStream) -> TokenStream {
TokenStream::from(module) TokenStream::from(module)
} }
use quote::TokenStreamExt;
struct ByteString<'a>(&'a [u8]);
impl ToTokens for ByteString<'_> {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.append(Literal::byte_string(self.0));
}
}
#[proc_macro]
pub fn include_aseprite_inner(input: TokenStream) -> TokenStream {
let parser = Punctuated::<LitStr, syn::Token![,]>::parse_separated_nonempty;
let parsed = match parser.parse(input) {
Ok(e) => e,
Err(e) => return e.to_compile_error().into(),
};
let mut optimiser = palette16::Palette16Optimiser::new();
let mut images = Vec::new();
let mut tags = Vec::new();
let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir");
let filenames: Vec<PathBuf> = parsed
.iter()
.map(|s| s.value())
.map(|s| Path::new(&root).join(&*s))
.collect();
for filename in filenames.iter() {
let (frames, tag) = aseprite::generate_from_file(filename);
tags.push((tag, images.len()));
for frame in frames {
let width = frame.width();
assert!(width == frame.height() && width.is_power_of_two() && width <= 32);
let image = Image::load_from_dyn_image(frame);
add_to_optimiser(&mut optimiser, &image, width as usize);
images.push(image);
}
}
let optimised_results = optimiser.optimise_palettes(None);
let (palette_data, tile_data, assignments) = palete_tile_data(&optimised_results, &images);
let palette_data = palette_data.iter().map(|colours| {
quote! {
Palette16::new([
#(#colours),*
])
}
});
let mut pre = 0;
let sprites = images
.iter()
.zip(assignments.iter())
.map(|(f, assignment)| {
let start: usize = pre;
let end: usize = pre + (f.width / 8) * (f.height / 8) * 32;
let data = ByteString(&tile_data[start..end]);
pre = end;
let width = f.width;
let height = f.height;
quote! {
Sprite::new(
&PALETTES[#assignment],
#data,
Size::from_width_height(#width, #height)
)
}
});
let tags = tags.iter().flat_map(|(tag, num_images)| {
tag.iter().map(move |tag| {
let start = tag.from_frame() as usize + num_images;
let end = tag.to_frame() as usize + num_images;
let direction = tag.animation_direction() as usize;
let name = tag.name();
assert!(start <= end, "Tag {} has start > end", name);
quote! {
(#name, Tag::new(SPRITES, #start, #end, #direction))
}
})
});
let include_paths = filenames.iter().map(|s| {
let s = s.as_os_str().to_string_lossy();
quote! {
const _: &[u8] = include_bytes!(#s);
}
});
let module = quote! {
#(#include_paths)*
const PALETTES: &[Palette16] = &[
#(#palette_data),*
];
pub const SPRITES: &[Sprite] = &[
#(#sprites),*
];
const TAGS: &TagMap = &TagMap::new(
&[
#(#tags),*
]
);
};
TokenStream::from(module)
}
fn convert_image( fn convert_image(
settings: &dyn config::Image, settings: &dyn config::Image,
parent: &Path, parent: &Path,
@ -98,11 +224,19 @@ fn convert_image(
} }
fn optimiser_for_image(image: &Image, tile_size: usize) -> palette16::Palette16Optimiser { fn optimiser_for_image(image: &Image, tile_size: usize) -> palette16::Palette16Optimiser {
let mut palette_optimiser = palette16::Palette16Optimiser::new();
add_to_optimiser(&mut palette_optimiser, image, tile_size);
palette_optimiser
}
fn add_to_optimiser(
palette_optimiser: &mut palette16::Palette16Optimiser,
image: &Image,
tile_size: usize,
) {
let tiles_x = image.width / tile_size; let tiles_x = image.width / tile_size;
let tiles_y = image.height / tile_size; let tiles_y = image.height / tile_size;
let mut palette_optimiser = palette16::Palette16Optimiser::new();
for y in 0..tiles_y { for y in 0..tiles_y {
for x in 0..tiles_x { for x in 0..tiles_x {
let mut palette = palette16::Palette16::new(); let mut palette = palette16::Palette16::new();
@ -118,6 +252,74 @@ fn optimiser_for_image(image: &Image, tile_size: usize) -> palette16::Palette16O
palette_optimiser.add_palette(palette); palette_optimiser.add_palette(palette);
} }
} }
}
palette_optimiser
fn palete_tile_data(
optimiser: &Palette16OptimisationResults,
images: &[Image],
) -> (Vec<Vec<u16>>, Vec<u8>, Vec<usize>) {
let palette_data: Vec<Vec<u16>> = optimiser
.optimised_palettes
.iter()
.map(|palette| {
palette
.clone()
.into_iter()
.map(|colour| colour.to_rgb15())
.chain(iter::repeat(0))
.take(16)
.map(|colour| colour as u16)
.collect()
})
.collect();
let mut tile_data = Vec::new();
for image in images {
let tile_size = image.height;
let tiles_x = image.width / tile_size;
let tiles_y = image.height / tile_size;
for y in 0..tiles_y {
for x in 0..tiles_x {
let palette_index = optimiser.assignments[y * tiles_x + x];
let palette = &optimiser.optimised_palettes[palette_index];
for inner_y in 0..tile_size / 8 {
for inner_x in 0..tile_size / 8 {
for j in inner_y * 8..inner_y * 8 + 8 {
for i in inner_x * 8..inner_x * 8 + 8 {
let colour = image.colour(x * tile_size + i, y * tile_size + j);
tile_data.push(palette.colour_index(colour));
}
}
}
}
}
}
}
let tile_data = tile_data
.chunks(2)
.map(|chunk| chunk[0] | (chunk[1] << 4))
.collect();
let assignments = optimiser.assignments.clone();
(palette_data, tile_data, assignments)
}
#[cfg(test)]
mod tests {
use asefile::AnimationDirection;
#[test]
// These directions defined in agb and have these values. This is important
// when outputting code for agb. If more animation directions are added then
// we will have to support them there.
fn directions_to_agb() {
assert_eq!(AnimationDirection::Forward as usize, 0);
assert_eq!(AnimationDirection::Reverse as usize, 1);
assert_eq!(AnimationDirection::PingPong as usize, 2);
}
} }

View file

@ -151,12 +151,12 @@ impl Palette16Optimiser {
let mut a_colour_is_used = false; let mut a_colour_is_used = false;
for current_palette in unsatisfied_palettes { for current_palette in unsatisfied_palettes {
if palette.union_length(&current_palette) > MAX_COLOURS_PER_PALETTE { if palette.union_length(current_palette) > MAX_COLOURS_PER_PALETTE {
continue; continue;
} }
for colour in &current_palette.colours { for colour in &current_palette.colours {
if palette.colours.contains(&colour) { if palette.colours.contains(colour) {
continue; continue;
} }

66
agb-macros/Cargo.lock generated
View file

@ -8,39 +8,9 @@ version = "0.1.0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rand",
"syn", "syn",
] ]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "getrandom"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.36" version = "1.0.36"
@ -59,36 +29,6 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.86" version = "1.0.86"
@ -105,9 +45,3 @@ name = "unicode-xid"
version = "0.2.2" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"

View file

@ -13,4 +13,3 @@ proc-macro = true
syn = { version = "1.0.86", features = ["full", "extra-traits"] } syn = { version = "1.0.86", features = ["full", "extra-traits"] }
proc-macro2 = "1.0.36" proc-macro2 = "1.0.36"
quote = "1.0.15" quote = "1.0.15"
rand = "0.8.5"

View file

@ -3,9 +3,11 @@ use proc_macro::TokenStream;
use proc_macro2::Span; use proc_macro2::Span;
use quote::{quote, ToTokens}; use quote::{quote, ToTokens};
use rand::Rng;
use syn::{FnArg, Ident, ItemFn, Pat, ReturnType, Token, Type, Visibility}; use syn::{FnArg, Ident, ItemFn, Pat, ReturnType, Token, Type, Visibility};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
let f: ItemFn = syn::parse(input).expect("#[agb::entry] must be applied to a function"); let f: ItemFn = syn::parse(input).expect("#[agb::entry] must be applied to a function");
@ -57,7 +59,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
"Must pass no args to #[agb::entry] macro" "Must pass no args to #[agb::entry] macro"
); );
let fn_name = random_ident(); let fn_name = hashed_ident(&f);
let attrs = f.attrs; let attrs = f.attrs;
let stmts = f.block.stmts; let stmts = f.block.stmts;
@ -98,18 +100,13 @@ pub fn num(input: TokenStream) -> TokenStream {
quote!((#integer, #fractional)).into() quote!((#integer, #fractional)).into()
} }
fn random_ident() -> Ident { fn hashed_ident<T: Hash>(f: &T) -> Ident {
let mut rng = rand::thread_rng(); let hash = calculate_hash(f);
Ident::new( Ident::new(&format!("_agb_main_func_{}", hash), Span::call_site())
&(0..16) }
.map(|i| {
if i == 0 || rng.gen() { fn calculate_hash<T: Hash>(t: &T) -> u64 {
(b'a' + rng.gen::<u8>() % 25) as char let mut s = DefaultHasher::new();
} else { t.hash(&mut s);
(b'0' + rng.gen::<u8>() % 10) as char s.finish()
}
})
.collect::<String>(),
Span::call_site(),
)
} }

178
agb/Cargo.lock generated
View file

@ -24,6 +24,9 @@ dependencies = [
"agb_sound_converter", "agb_sound_converter",
"bare-metal", "bare-metal",
"bitflags", "bitflags",
"hashbrown",
"modular-bitfield",
"rustc-hash",
] ]
[[package]] [[package]]
@ -37,6 +40,7 @@ dependencies = [
name = "agb_image_converter" name = "agb_image_converter"
version = "0.6.0" version = "0.6.0"
dependencies = [ dependencies = [
"asefile",
"image", "image",
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -51,7 +55,6 @@ version = "0.1.0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rand",
"syn", "syn",
] ]
@ -65,6 +68,31 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "asefile"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0d5f7de918fd4cb18249819fc4bd27f6a5dbfbc9dcb271727f27dacf17ce880"
dependencies = [
"bitflags",
"byteorder",
"flate2",
"image",
"log",
"nohash",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -118,11 +146,24 @@ dependencies = [
[[package]] [[package]]
name = "deflate" name = "deflate"
version = "1.0.0" version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
dependencies = [ dependencies = [
"adler32", "adler32",
"byteorder",
]
[[package]]
name = "flate2"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide 0.4.4",
] ]
[[package]] [[package]]
@ -136,6 +177,15 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "hashbrown"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758"
dependencies = [
"ahash",
]
[[package]] [[package]]
name = "hound" name = "hound"
version = "3.4.0" version = "3.4.0"
@ -144,9 +194,9 @@ checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
[[package]] [[package]]
name = "image" name = "image"
version = "0.24.1" version = "0.23.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db207d030ae38f1eb6f240d5a1c1c88ff422aa005d10f8c6c6fc5e75286ab30e" checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"byteorder", "byteorder",
@ -164,14 +214,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]] [[package]]
name = "miniz_oxide" name = "log"
version = "0.5.1" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "miniz_oxide"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [
"adler32",
]
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [ dependencies = [
"adler", "adler",
"autocfg",
] ]
[[package]]
name = "modular-bitfield"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74"
dependencies = [
"modular-bitfield-impl",
"static_assertions",
]
[[package]]
name = "modular-bitfield-impl"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "nohash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca"
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.44" version = "0.1.44"
@ -195,9 +291,9 @@ dependencies = [
[[package]] [[package]]
name = "num-rational" name = "num-rational"
version = "0.4.0" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"num-integer", "num-integer",
@ -214,23 +310,23 @@ dependencies = [
] ]
[[package]] [[package]]
name = "png" name = "once_cell"
version = "0.17.5" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
name = "png"
version = "0.16.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crc32fast", "crc32fast",
"deflate", "deflate",
"miniz_oxide", "miniz_oxide 0.3.7",
] ]
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.36" version = "1.0.36"
@ -250,34 +346,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "rand" name = "rustc-hash"
version = "0.8.5" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "serde" name = "serde"
@ -299,6 +371,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.86" version = "1.0.86"
@ -325,6 +403,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.10.2+wasi-snapshot-preview1" version = "0.10.2+wasi-snapshot-preview1"

View file

@ -15,8 +15,7 @@ lto = true
debug = true debug = true
[features] [features]
default = ["alloc"] default = []
alloc = []
freq18157 = ["agb_sound_converter/freq18157"] freq18157 = ["agb_sound_converter/freq18157"]
[dependencies] [dependencies]
@ -26,6 +25,9 @@ agb_sound_converter = { version = "0.1.0", path = "../agb-sound-converter" }
agb_macros = { version = "0.1.0", path = "../agb-macros" } agb_macros = { version = "0.1.0", path = "../agb-macros" }
agb_fixnum = { version = "0.1.0", path = "../agb-fixnum" } agb_fixnum = { version = "0.1.0", path = "../agb-fixnum" }
bare-metal = "1.0" bare-metal = "1.0"
hashbrown = "0.12.0"
modular-bitfield = "0.11.2"
rustc-hash = { version = "1.0", default-features = false }
[package.metadata.docs.rs] [package.metadata.docs.rs]
default-target = "thumbv6m-none-eabi" default-target = "thumbv6m-none-eabi"

View file

@ -3,7 +3,11 @@
use agb::{ use agb::{
display::tiled::{TileFormat, TileSet, TileSetting}, display::tiled::{TileFormat, TileSet, TileSetting},
display::{object::ObjectStandard, HEIGHT, WIDTH}, display::{
object::{Object, ObjectController, Size, Sprite},
palette16::Palette16,
HEIGHT, WIDTH,
},
input::Button, input::Button,
}; };
use core::convert::TryInto; use core::convert::TryInto;
@ -16,7 +20,7 @@ enum State {
} }
struct Character<'a> { struct Character<'a> {
object: ObjectStandard<'a>, object: Object<'a, 'a>,
position: Vector2D, position: Vector2D,
velocity: Vector2D, velocity: Vector2D,
} }
@ -31,8 +35,8 @@ fn tile_is_collidable(tile: u16) -> bool {
masked == 0 || masked == 4 masked == 0 || masked == 4
} }
fn frame_ranger(count: u32, start: u32, end: u32, delay: u32) -> u16 { fn frame_ranger(count: u32, start: u32, end: u32, delay: u32) -> usize {
(((count / delay) % (end + 1 - start)) + start) as u16 (((count / delay) % (end + 1 - start)) + start) as usize
} }
#[agb::entry] #[agb::entry]
@ -66,14 +70,11 @@ fn main(mut gba: agb::Gba) -> ! {
background.show(); background.show();
background.commit(); background.commit();
let mut object = gba.display.object.get(); let object = gba.display.object.get();
object.set_sprite_palette_raw(&CHICKEN_PALETTE); let sprite = object.get_sprite(&ChickenSprites[0]).unwrap();
object.set_sprite_tilemap(&CHICKEN_TILES);
object.enable();
let mut chicken = Character { let mut chicken = Character {
object: object.get_object_standard(), object: object.get_object(sprite).unwrap(),
position: Vector2D { position: Vector2D {
x: (6 * 8) << 8, x: (6 * 8) << 8,
y: ((7 * 8) - 4) << 8, y: ((7 * 8) - 4) << 8,
@ -81,7 +82,6 @@ fn main(mut gba: agb::Gba) -> ! {
velocity: Vector2D { x: 0, y: 0 }, velocity: Vector2D { x: 0, y: 0 },
}; };
chicken.object.set_tile_id(0);
chicken chicken
.object .object
.set_x((chicken.position.x >> 8).try_into().unwrap()); .set_x((chicken.position.x >> 8).try_into().unwrap());
@ -132,14 +132,19 @@ fn main(mut gba: agb::Gba) -> ! {
} }
restrict_to_screen(&mut chicken); restrict_to_screen(&mut chicken);
update_chicken_object(&mut chicken, state, frame_count); update_chicken_object(&mut chicken, &object, state, frame_count);
// Commit the chicken to vram // Commit the chicken to vram
chicken.object.commit(); chicken.object.commit();
} }
} }
fn update_chicken_object(chicken: &mut Character, state: State, frame_count: u32) { fn update_chicken_object<'a>(
chicken: &'_ mut Character<'a>,
object: &'a ObjectController,
state: State,
frame_count: u32,
) {
if chicken.velocity.x > 1 { if chicken.velocity.x > 1 {
chicken.object.set_hflip(false); chicken.object.set_hflip(false);
} else if chicken.velocity.x < -1 { } else if chicken.velocity.x < -1 {
@ -148,18 +153,24 @@ fn update_chicken_object(chicken: &mut Character, state: State, frame_count: u32
match state { match state {
State::Ground => { State::Ground => {
if chicken.velocity.x.abs() > 1 << 4 { if chicken.velocity.x.abs() > 1 << 4 {
chicken.object.set_sprite(
object
.get_sprite(&ChickenSprites[frame_ranger(frame_count, 1, 3, 10)])
.unwrap(),
);
} else {
chicken chicken
.object .object
.set_tile_id(frame_ranger(frame_count, 1, 3, 10)); .set_sprite(object.get_sprite(&ChickenSprites[0]).unwrap());
} else {
chicken.object.set_tile_id(0);
} }
} }
State::Upwards => {} State::Upwards => {}
State::Flapping => { State::Flapping => {
chicken chicken.object.set_sprite(
.object object
.set_tile_id(frame_ranger(frame_count, 4, 5, 5)); .get_sprite(&ChickenSprites[frame_ranger(frame_count, 4, 5, 5)])
.unwrap(),
);
} }
} }
@ -244,16 +255,65 @@ fn handle_collision(
// Below is the data for the sprites // Below is the data for the sprites
static CHICKEN_TILES: [u32; 8 * 6] = [ static ChickenPalette: Palette16 =
0x01100000, 0x11100000, 0x01100010, 0x01111110, 0x01111110, 0x00001000, 0x00001000, 0x00011000, Palette16::new([0x7C1E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
0x01100000, 0x11100000, 0x01100010, 0x01111110, 0x01111110, 0x00010100, 0x00100100, 0x00000010,
0x01100000, 0x11100000, 0x01100010, 0x01111110, 0x01111110, 0x00011000, 0x00100110, 0x00100000,
0x01100000, 0x11100000, 0x01100010, 0x01111110, 0x01111110, 0x00011000, 0x00011100, 0x00001000,
0x01100000, 0x11111100, 0x01111010, 0x01111110, 0x01111110, 0x00011000, 0x00010000, 0x00000000,
0x01100000, 0x11100000, 0x01111110, 0x01111110, 0x01111110, 0x00011000, 0x00010000, 0x00000000,
];
static CHICKEN_PALETTE: [u16; 1] = [0x7C1E]; static ChickenSprites: &[Sprite] = &[
Sprite::new(
&ChickenPalette,
&[
0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11,
0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x10, 0x01, 0x00,
],
Size::S8x8,
),
Sprite::new(
&ChickenPalette,
&[
0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11,
0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x10, 0x00,
0x10, 0x00, 0x00, 0x00,
],
Size::S8x8,
),
Sprite::new(
&ChickenPalette,
&[
0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11,
0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x10, 0x01, 0x10, 0x00,
0x00, 0x00, 0x10, 0x00,
],
Size::S8x8,
),
Sprite::new(
&ChickenPalette,
&[
0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x00, 0x10, 0x01, 0x10, 0x11,
0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00,
0x00, 0x10, 0x00, 0x00,
],
Size::S8x8,
),
Sprite::new(
&ChickenPalette,
&[
0x00, 0x00, 0x10, 0x01, 0x00, 0x11, 0x11, 0x11, 0x10, 0x10, 0x11, 0x01, 0x10, 0x11,
0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00,
],
Size::S8x8,
),
Sprite::new(
&ChickenPalette,
&[
0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x11, 0x10, 0x11, 0x11, 0x01, 0x10, 0x11,
0x11, 0x01, 0x10, 0x11, 0x11, 0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00,
],
Size::S8x8,
),
];
static MAP_TILES: [u32; 8 * 17] = [ static MAP_TILES: [u32; 8 * 17] = [
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,

109
agb/examples/sprites.rs Normal file
View file

@ -0,0 +1,109 @@
#![no_std]
#![no_main]
extern crate alloc;
use agb::display::object::{Graphics, ObjectController, Sprite, TagMap};
use alloc::vec::Vec;
const GRAPHICS: &Graphics = agb::include_aseprite!(
"../examples/the-purple-night/gfx/objects.aseprite",
"../examples/the-purple-night/gfx/boss.aseprite"
);
const SPRITES: &[Sprite] = GRAPHICS.sprites();
const TAG_MAP: &TagMap = GRAPHICS.tags();
fn all_sprites(gfx: &ObjectController) {
let mut input = agb::input::ButtonController::new();
let mut objs = Vec::new();
for y in 0..9 {
for x in 0..14 {
let mut obj = gfx
.get_object(gfx.get_sprite(&SPRITES[0]).unwrap())
.unwrap();
obj.show();
obj.set_position((x * 16 + 8, y * 16 + 8).into());
objs.push(obj);
}
}
let mut count = 0;
let mut image = 0;
let vblank = agb::interrupt::VBlank::get();
loop {
vblank.wait_for_vblank();
input.update();
if input.is_just_pressed(agb::input::Button::A) {
break;
}
count += 1;
if count % 5 == 0 {
image += 1;
image %= SPRITES.len();
let objs_len = objs.len();
for (i, obj) in objs.iter_mut().enumerate() {
let this_image = (image + i * SPRITES.len() / objs_len) % SPRITES.len();
obj.set_sprite(gfx.get_sprite(&SPRITES[this_image]).unwrap());
obj.commit();
}
}
}
}
fn all_tags(gfx: &ObjectController) {
let mut input = agb::input::ButtonController::new();
let mut objs = Vec::new();
for (i, v) in TAG_MAP.values().enumerate() {
let x = (i % 7) as i32;
let y = (i / 7) as i32;
let sprite = v.get_sprite(0);
let (size_x, size_y) = sprite.size().to_width_height();
let (size_x, size_y) = (size_x as i32, size_y as i32);
let mut obj = gfx.get_object(gfx.get_sprite(sprite).unwrap()).unwrap();
obj.show();
obj.set_position((x * 32 + 16 - size_x / 2, y * 32 + 16 - size_y / 2).into());
objs.push((obj, v));
}
let mut count = 0;
let mut image = 0;
let vblank = agb::interrupt::VBlank::get();
loop {
vblank.wait_for_vblank();
input.update();
if input.is_just_pressed(agb::input::Button::A) {
break;
}
count += 1;
if count % 5 == 0 {
image += 1;
for (obj, tag) in objs.iter_mut() {
obj.set_sprite(gfx.get_sprite(tag.get_animation_sprite(image)).unwrap());
obj.commit();
}
}
}
}
#[agb::entry]
fn main(mut gba: agb::Gba) -> ! {
let gfx = gba.display.object.get();
loop {
all_tags(&gfx);
all_sprites(&gfx);
}
}

View file

@ -12,7 +12,7 @@ use core::ptr::NonNull;
use crate::interrupt::free; use crate::interrupt::free;
use bare_metal::{CriticalSection, Mutex}; use bare_metal::{CriticalSection, Mutex};
use super::bump_allocator::BumpAllocator; use super::bump_allocator::{BumpAllocator, StartEnd};
use super::SendNonNull; use super::SendNonNull;
struct Block { struct Block {
@ -49,9 +49,9 @@ pub(crate) struct BlockAllocator {
} }
impl BlockAllocator { impl BlockAllocator {
pub(super) const unsafe fn new() -> Self { pub const unsafe fn new(start: StartEnd) -> Self {
Self { Self {
inner_allocator: BumpAllocator::new(), inner_allocator: BumpAllocator::new(start),
state: Mutex::new(RefCell::new(BlockAllocatorState { state: Mutex::new(RefCell::new(BlockAllocatorState {
first_free_block: None, first_free_block: None,
})), })),
@ -76,7 +76,7 @@ impl BlockAllocator {
} }
/// Requests a brand new block from the inner bump allocator /// Requests a brand new block from the inner bump allocator
fn new_block(&self, layout: Layout, cs: &CriticalSection) -> *mut u8 { fn new_block(&self, layout: Layout, cs: &CriticalSection) -> Option<NonNull<u8>> {
let overall_layout = Block::either_layout(layout); let overall_layout = Block::either_layout(layout);
self.inner_allocator.alloc_critical(overall_layout, cs) self.inner_allocator.alloc_critical(overall_layout, cs)
} }
@ -111,10 +111,8 @@ impl BlockAllocator {
} }
}); });
} }
}
unsafe impl GlobalAlloc for BlockAllocator { pub unsafe fn alloc(&self, layout: Layout) -> Option<NonNull<u8>> {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// find a block that this current request fits in // find a block that this current request fits in
let full_layout = Block::either_layout(layout); let full_layout = Block::either_layout(layout);
@ -133,7 +131,7 @@ unsafe impl GlobalAlloc for BlockAllocator {
let curr_block = curr.as_mut(); let curr_block = curr.as_mut();
if curr_block.size == full_layout.size() { if curr_block.size == full_layout.size() {
*list_ptr = curr_block.next; *list_ptr = curr_block.next;
return curr.as_ptr().cast(); return Some(curr.cast());
} else if curr_block.size >= block_after_layout.size() { } else if curr_block.size >= block_after_layout.size() {
// can split block // can split block
let split_block = Block { let split_block = Block {
@ -148,7 +146,7 @@ unsafe impl GlobalAlloc for BlockAllocator {
*split_ptr = split_block; *split_ptr = split_block;
*list_ptr = NonNull::new(split_ptr).map(SendNonNull); *list_ptr = NonNull::new(split_ptr).map(SendNonNull);
return curr.as_ptr().cast(); return Some(curr.cast());
} }
current_block = curr_block.next; current_block = curr_block.next;
list_ptr = &mut curr_block.next; list_ptr = &mut curr_block.next;
@ -158,7 +156,7 @@ unsafe impl GlobalAlloc for BlockAllocator {
}) })
} }
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { pub unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let new_layout = Block::either_layout(layout).pad_to_align(); let new_layout = Block::either_layout(layout).pad_to_align();
free(|key| { free(|key| {
let mut state = self.state.borrow(*key).borrow_mut(); let mut state = self.state.borrow(*key).borrow_mut();
@ -200,3 +198,16 @@ unsafe impl GlobalAlloc for BlockAllocator {
self.normalise(); self.normalise();
} }
} }
unsafe impl GlobalAlloc for BlockAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
match self.alloc(layout) {
None => core::ptr::null_mut(),
Some(p) => p.as_ptr(),
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
self.dealloc(ptr, layout);
}
}

View file

@ -6,26 +6,33 @@ use super::SendNonNull;
use crate::interrupt::free; use crate::interrupt::free;
use bare_metal::{CriticalSection, Mutex}; use bare_metal::{CriticalSection, Mutex};
pub(crate) struct StartEnd {
pub start: fn() -> usize,
pub end: fn() -> usize,
}
pub(crate) struct BumpAllocator { pub(crate) struct BumpAllocator {
current_ptr: Mutex<RefCell<Option<SendNonNull<u8>>>>, current_ptr: Mutex<RefCell<Option<SendNonNull<u8>>>>,
start_end: Mutex<StartEnd>,
} }
impl BumpAllocator { impl BumpAllocator {
pub const fn new() -> Self { pub const fn new(start_end: StartEnd) -> Self {
Self { Self {
current_ptr: Mutex::new(RefCell::new(None)), current_ptr: Mutex::new(RefCell::new(None)),
start_end: Mutex::new(start_end),
} }
} }
} }
impl BumpAllocator { impl BumpAllocator {
pub fn alloc_critical(&self, layout: Layout, cs: &CriticalSection) -> *mut u8 { pub fn alloc_critical(&self, layout: Layout, cs: &CriticalSection) -> Option<NonNull<u8>> {
let mut current_ptr = self.current_ptr.borrow(*cs).borrow_mut(); let mut current_ptr = self.current_ptr.borrow(*cs).borrow_mut();
let ptr = if let Some(c) = *current_ptr { let ptr = if let Some(c) = *current_ptr {
c.as_ptr() as usize c.as_ptr() as usize
} else { } else {
get_data_end() (self.start_end.borrow(*cs).start)()
}; };
let alignment_bitmask = layout.align() - 1; let alignment_bitmask = layout.align() - 1;
@ -36,53 +43,26 @@ impl BumpAllocator {
let resulting_ptr = ptr + amount_to_add; let resulting_ptr = ptr + amount_to_add;
let new_current_ptr = resulting_ptr + layout.size(); let new_current_ptr = resulting_ptr + layout.size();
if new_current_ptr as usize >= super::EWRAM_END { if new_current_ptr as usize >= (self.start_end.borrow(*cs).end)() {
return core::ptr::null_mut(); return None;
} }
*current_ptr = NonNull::new(new_current_ptr as *mut _).map(SendNonNull); *current_ptr = NonNull::new(new_current_ptr as *mut _).map(SendNonNull);
resulting_ptr as *mut _ NonNull::new(resulting_ptr as *mut _)
} }
pub fn alloc_safe(&self, layout: Layout) -> *mut u8 { pub fn alloc_safe(&self, layout: Layout) -> Option<NonNull<u8>> {
free(|key| self.alloc_critical(layout, key)) free(|key| self.alloc_critical(layout, key))
} }
} }
unsafe impl GlobalAlloc for BumpAllocator { unsafe impl GlobalAlloc for BumpAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 { unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
self.alloc_safe(layout) match self.alloc_safe(layout) {
None => core::ptr::null_mut(),
Some(p) => p.as_ptr(),
}
} }
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
} }
fn get_data_end() -> usize {
extern "C" {
static __ewram_data_end: usize;
}
// TODO: This seems completely wrong, but without the &, rust generates
// a double dereference :/. Maybe a bug in nightly?
(unsafe { &__ewram_data_end }) as *const _ as usize
}
#[cfg(test)]
mod tests {
use super::*;
#[test_case]
fn should_return_data_end_somewhere_in_ewram(_gba: &mut crate::Gba) {
let data_end = get_data_end();
assert!(
0x0200_0000 <= data_end,
"data end should be bigger than 0x0200_0000, got {}",
data_end
);
assert!(
0x0204_0000 > data_end,
"data end should be smaller than 0x0203_0000"
);
}
}

View file

@ -2,11 +2,13 @@ use core::alloc::Layout;
use core::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use core::ptr::NonNull; use core::ptr::NonNull;
mod block_allocator; pub(crate) mod block_allocator;
mod bump_allocator; pub(crate) mod bump_allocator;
use block_allocator::BlockAllocator; use block_allocator::BlockAllocator;
use self::bump_allocator::StartEnd;
struct SendNonNull<T>(NonNull<T>); struct SendNonNull<T>(NonNull<T>);
unsafe impl<T> Send for SendNonNull<T> {} unsafe impl<T> Send for SendNonNull<T> {}
@ -33,7 +35,12 @@ impl<T> DerefMut for SendNonNull<T> {
const EWRAM_END: usize = 0x0204_0000; const EWRAM_END: usize = 0x0204_0000;
#[global_allocator] #[global_allocator]
static GLOBAL_ALLOC: BlockAllocator = unsafe { BlockAllocator::new() }; static GLOBAL_ALLOC: BlockAllocator = unsafe {
BlockAllocator::new(StartEnd {
start: get_data_end,
end: || EWRAM_END,
})
};
#[cfg(test)] #[cfg(test)]
pub unsafe fn number_of_blocks() -> u32 { pub unsafe fn number_of_blocks() -> u32 {
@ -49,6 +56,16 @@ fn alloc_error(layout: Layout) -> ! {
); );
} }
fn get_data_end() -> usize {
extern "C" {
static __ewram_data_end: usize;
}
// TODO: This seems completely wrong, but without the &, rust generates
// a double dereference :/. Maybe a bug in nightly?
(unsafe { &__ewram_data_end }) as *const _ as usize
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
const EWRAM_START: usize = 0x0200_0000; const EWRAM_START: usize = 0x0200_0000;
@ -118,4 +135,19 @@ mod test {
assert_eq!(v1[40], 137); assert_eq!(v1[40], 137);
assert_eq!(v2[78], 1075); assert_eq!(v2[78], 1075);
} }
#[test_case]
fn should_return_data_end_somewhere_in_ewram(_gba: &mut crate::Gba) {
let data_end = get_data_end();
assert!(
0x0200_0000 <= data_end,
"data end should be bigger than 0x0200_0000, got {}",
data_end
);
assert!(
0x0204_0000 > data_end,
"data end should be smaller than 0x0203_0000"
);
}
} }

View file

@ -1,9 +1,10 @@
use crate::memory_mapped::MemoryMapped; use crate::memory_mapped::MemoryMapped;
use bitflags::bitflags; use bitflags::bitflags;
use modular_bitfield::BitfieldSpecifier;
use video::Video; use video::Video;
use self::object::ObjectControl; use self::object::ObjectController;
/// Graphics mode 3. Bitmap mode that provides a 16-bit colour framebuffer. /// Graphics mode 3. Bitmap mode that provides a 16-bit colour framebuffer.
pub mod bitmap3; pub mod bitmap3;
@ -69,8 +70,8 @@ pub struct Display {
pub struct ObjectDistribution {} pub struct ObjectDistribution {}
impl ObjectDistribution { impl ObjectDistribution {
pub fn get(&mut self) -> ObjectControl { pub fn get(&mut self) -> ObjectController {
ObjectControl::new() ObjectController::new()
} }
} }
@ -109,7 +110,7 @@ pub fn busy_wait_for_vblank() {
while VCOUNT.get() < 160 {} while VCOUNT.get() < 160 {}
} }
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(BitfieldSpecifier, Clone, Copy)]
pub enum Priority { pub enum Priority {
P0 = 0, P0 = 0,
P1 = 1, P1 = 1,

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,4 @@
#[repr(C)]
#[derive(Clone)] #[derive(Clone)]
pub struct Palette16 { pub struct Palette16 {
pub(crate) colours: [u16; 16], pub(crate) colours: [u16; 16],

View file

@ -112,6 +112,8 @@
/// ``` /// ```
pub use agb_image_converter::include_gfx; pub use agb_image_converter::include_gfx;
pub use agb_image_converter::include_aseprite_inner;
/// This macro declares the entry point to your game written using `agb`. /// This macro declares the entry point to your game written using `agb`.
/// ///
/// It is already included in the template, but your `main` function must be annotated with `#[agb::entry]`, takes 1 argument and never returns. /// It is already included in the template, but your `main` function must be annotated with `#[agb::entry]`, takes 1 argument and never returns.
@ -133,9 +135,7 @@ pub use agb_macros::entry;
pub use agb_sound_converter::include_wav; pub use agb_sound_converter::include_wav;
#[cfg(feature = "alloc")]
extern crate alloc; extern crate alloc;
#[cfg(feature = "alloc")]
mod agb_alloc; mod agb_alloc;
mod arena; mod arena;

View file

@ -1,6 +1,6 @@
use core::arch::asm; use core::arch::asm;
use crate::display::object::AffineMatrixAttributes; // use crate::display::object::AffineMatrixAttributes;
use crate::fixnum::Num; use crate::fixnum::Num;
#[allow(non_snake_case)] #[allow(non_snake_case)]
@ -113,55 +113,55 @@ pub fn arc_tan2(x: i16, y: i32) -> i16 {
result result
} }
pub fn affine_matrix( // pub fn affine_matrix(
x_scale: Num<i16, 8>, // x_scale: Num<i16, 8>,
y_scale: Num<i16, 8>, // y_scale: Num<i16, 8>,
rotation: u8, // rotation: u8,
) -> AffineMatrixAttributes { // ) -> AffineMatrixAttributes {
let mut result = AffineMatrixAttributes { // let mut result = AffineMatrixAttributes {
p_a: 0, // p_a: 0,
p_b: 0, // p_b: 0,
p_c: 0, // p_c: 0,
p_d: 0, // p_d: 0,
}; // };
#[allow(dead_code)] // #[allow(dead_code)]
#[repr(C, packed)] // #[repr(C, packed)]
struct Input { // struct Input {
x_scale: i16, // x_scale: i16,
y_scale: i16, // y_scale: i16,
rotation: u16, // rotation: u16,
} // }
let input = Input { // let input = Input {
y_scale: x_scale.to_raw(), // y_scale: x_scale.to_raw(),
x_scale: y_scale.to_raw(), // x_scale: y_scale.to_raw(),
rotation: rotation as u16, // rotation: rotation as u16,
}; // };
unsafe { // unsafe {
asm!("swi 0x0F", // asm!("swi 0x0F",
in("r0") &input as *const Input as usize, // in("r0") &input as *const Input as usize,
in("r1") &mut result as *mut AffineMatrixAttributes as usize, // in("r1") &mut result as *mut AffineMatrixAttributes as usize,
in("r2") 1, // in("r2") 1,
in("r3") 2, // in("r3") 2,
) // )
} // }
result // result
} // }
#[cfg(test)] // #[cfg(test)]
mod tests { // mod tests {
use super::*; // use super::*;
#[test_case] // #[test_case]
fn affine(_gba: &mut crate::Gba) { // fn affine(_gba: &mut crate::Gba) {
// expect identity matrix // // expect identity matrix
let one: Num<i16, 8> = 1.into(); // let one: Num<i16, 8> = 1.into();
let aff = affine_matrix(one, one, 0); // let aff = affine_matrix(one, one, 0);
assert_eq!(aff.p_a, one.to_raw()); // assert_eq!(aff.p_a, one.to_raw());
assert_eq!(aff.p_d, one.to_raw()); // assert_eq!(aff.p_d, one.to_raw());
} // }
} // }

View file

@ -24,6 +24,9 @@ dependencies = [
"agb_sound_converter", "agb_sound_converter",
"bare-metal", "bare-metal",
"bitflags", "bitflags",
"hashbrown",
"modular-bitfield",
"rustc-hash",
] ]
[[package]] [[package]]
@ -37,6 +40,7 @@ dependencies = [
name = "agb_image_converter" name = "agb_image_converter"
version = "0.6.0" version = "0.6.0"
dependencies = [ dependencies = [
"asefile",
"image", "image",
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -51,7 +55,6 @@ version = "0.1.0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rand",
"syn", "syn",
] ]
@ -65,6 +68,31 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "asefile"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0d5f7de918fd4cb18249819fc4bd27f6a5dbfbc9dcb271727f27dacf17ce880"
dependencies = [
"bitflags",
"byteorder",
"flate2",
"image",
"log",
"nohash",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -85,9 +113,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.7.3" version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@ -118,11 +146,24 @@ dependencies = [
[[package]] [[package]]
name = "deflate" name = "deflate"
version = "1.0.0" version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
dependencies = [ dependencies = [
"adler32", "adler32",
"byteorder",
]
[[package]]
name = "flate2"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide 0.4.4",
] ]
[[package]] [[package]]
@ -136,6 +177,15 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "hashbrown"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758"
dependencies = [
"ahash",
]
[[package]] [[package]]
name = "hound" name = "hound"
version = "3.4.0" version = "3.4.0"
@ -144,9 +194,9 @@ checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
[[package]] [[package]]
name = "image" name = "image"
version = "0.24.1" version = "0.23.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db207d030ae38f1eb6f240d5a1c1c88ff422aa005d10f8c6c6fc5e75286ab30e" checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"byteorder", "byteorder",
@ -164,14 +214,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]] [[package]]
name = "miniz_oxide" name = "log"
version = "0.5.1" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "miniz_oxide"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [
"adler32",
]
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [ dependencies = [
"adler", "adler",
"autocfg",
] ]
[[package]]
name = "modular-bitfield"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74"
dependencies = [
"modular-bitfield-impl",
"static_assertions",
]
[[package]]
name = "modular-bitfield-impl"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "nohash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca"
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.44" version = "0.1.44"
@ -195,9 +291,9 @@ dependencies = [
[[package]] [[package]]
name = "num-rational" name = "num-rational"
version = "0.4.0" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"num-integer", "num-integer",
@ -214,15 +310,21 @@ dependencies = [
] ]
[[package]] [[package]]
name = "png" name = "once_cell"
version = "0.17.4" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02cd7d51cea7e2fa6bbcb8af5fbcad15b871451bfc2d20ed72dff2f4ae072a84" checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
name = "png"
version = "0.16.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crc32fast", "crc32fast",
"deflate", "deflate",
"miniz_oxide", "miniz_oxide 0.3.7",
] ]
[[package]] [[package]]
@ -232,12 +334,6 @@ dependencies = [
"agb", "agb",
] ]
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.36" version = "1.0.36"
@ -257,34 +353,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "rand" name = "rustc-hash"
version = "0.8.5" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "serde" name = "serde"
@ -306,6 +378,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.86" version = "1.0.86"
@ -332,6 +410,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.10.2+wasi-snapshot-preview1" version = "0.10.2+wasi-snapshot-preview1"

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 B

View file

@ -1,6 +0,0 @@
version = "1.0"
[image.sprites]
filename = "sprites.png"
tile_size = "16x16"
transparent_colour = "ff0044"

View file

@ -10,42 +10,27 @@
// which won't be a particularly clear error message. // which won't be a particularly clear error message.
#![no_main] #![no_main]
use agb::display::object::Size; use agb::display::object::{Graphics, Tag};
use agb::Gba; use agb::Gba;
// Put all the graphics related code in the gfx module const GRAPHICS: &Graphics = agb::include_aseprite!("gfx/sprites.aseprite");
mod gfx {
use agb::display::object::ObjectControl;
// Import the sprites into this module. This will create a `sprites` module
// and within that will be a constant called `sprites` which houses all the
// palette and tile data.
agb::include_gfx!("gfx/sprites.toml");
// Loads the sprites tile data and palette data into VRAM
pub fn load_sprite_data(object: &mut ObjectControl) {
object.set_sprite_palettes(sprites::sprites.palettes);
object.set_sprite_tilemap(sprites::sprites.tiles);
}
}
// The main function must take 0 arguments and never return. The agb::entry decorator // The main function must take 0 arguments and never return. The agb::entry decorator
// ensures that everything is in order. `agb` will call this after setting up the stack // ensures that everything is in order. `agb` will call this after setting up the stack
// and interrupt handlers correctly. // and interrupt handlers correctly.
#[agb::entry] #[agb::entry]
fn main(mut gba: Gba) -> ! { fn main(mut gba: Gba) -> ! {
let _tiled = gba.display.video.tiled0(); let object = gba.display.object.get();
let mut object = gba.display.object.get();
gfx::load_sprite_data(&mut object);
object.enable();
let mut ball = object.get_object_standard(); const BALL: &Tag = GRAPHICS.tags().get("Ball");
let ball_sprite = object
.get_sprite(BALL.get_sprite(0))
.expect("We should be able to load a sprite");
let mut ball = object
.get_object(ball_sprite)
.expect("We should have enoguh space to store an object");
ball.set_x(50) ball.set_x(50).set_y(50).show();
.set_y(50)
.set_sprite_size(Size::S16x16)
.set_tile_id(4 * 2)
.show();
let mut ball_x = 50; let mut ball_x = 50;
let mut ball_y = 50; let mut ball_y = 50;

View file

@ -2,5 +2,10 @@
"files.associations": { "files.associations": {
"*.tsx": "xml", "*.tsx": "xml",
"*.tmx": "xml" "*.tmx": "xml"
} },
"rust-analyzer.checkOnSave.allTargets": false,
"rust-analyzer.checkOnSave.extraArgs": [
"--target",
"thumbv4t-none-eabi"
]
} }

View file

@ -24,6 +24,9 @@ dependencies = [
"agb_sound_converter", "agb_sound_converter",
"bare-metal", "bare-metal",
"bitflags", "bitflags",
"hashbrown",
"modular-bitfield",
"rustc-hash",
] ]
[[package]] [[package]]
@ -37,6 +40,7 @@ dependencies = [
name = "agb_image_converter" name = "agb_image_converter"
version = "0.6.0" version = "0.6.0"
dependencies = [ dependencies = [
"asefile",
"image", "image",
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -51,7 +55,6 @@ version = "0.1.0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rand",
"syn", "syn",
] ]
@ -65,6 +68,31 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "asefile"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0d5f7de918fd4cb18249819fc4bd27f6a5dbfbc9dcb271727f27dacf17ce880"
dependencies = [
"bitflags",
"byteorder",
"flate2",
"image",
"log",
"nohash",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -118,11 +146,24 @@ dependencies = [
[[package]] [[package]]
name = "deflate" name = "deflate"
version = "1.0.0" version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
dependencies = [ dependencies = [
"adler32", "adler32",
"byteorder",
]
[[package]]
name = "flate2"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide 0.4.4",
] ]
[[package]] [[package]]
@ -136,6 +177,15 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "hashbrown"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758"
dependencies = [
"ahash",
]
[[package]] [[package]]
name = "hound" name = "hound"
version = "3.4.0" version = "3.4.0"
@ -144,9 +194,9 @@ checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
[[package]] [[package]]
name = "image" name = "image"
version = "0.24.1" version = "0.23.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db207d030ae38f1eb6f240d5a1c1c88ff422aa005d10f8c6c6fc5e75286ab30e" checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"byteorder", "byteorder",
@ -170,14 +220,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]] [[package]]
name = "miniz_oxide" name = "log"
version = "0.5.1" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "miniz_oxide"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [
"adler32",
]
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [ dependencies = [
"adler", "adler",
"autocfg",
] ]
[[package]]
name = "modular-bitfield"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74"
dependencies = [
"modular-bitfield-impl",
"static_assertions",
]
[[package]]
name = "modular-bitfield-impl"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "nohash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca"
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.44" version = "0.1.44"
@ -201,9 +297,9 @@ dependencies = [
[[package]] [[package]]
name = "num-rational" name = "num-rational"
version = "0.4.0" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"num-integer", "num-integer",
@ -220,23 +316,23 @@ dependencies = [
] ]
[[package]] [[package]]
name = "png" name = "once_cell"
version = "0.17.5" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
name = "png"
version = "0.16.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crc32fast", "crc32fast",
"deflate", "deflate",
"miniz_oxide", "miniz_oxide 0.3.7",
] ]
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.36" version = "1.0.36"
@ -256,34 +352,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "rand" name = "rustc-hash"
version = "0.8.5" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
@ -322,6 +394,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.86" version = "1.0.86"
@ -357,6 +435,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.10.2+wasi-snapshot-preview1" version = "0.10.2+wasi-snapshot-preview1"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -1,6 +0,0 @@
version = "1.0"
[image.object_sheet]
filename = "object_sheet.png"
tile_size = "8x8"
transparent_colour = "2ce8f4"

View file

@ -1,9 +1,20 @@
use super::{object_tiles, sfx::SfxPlayer, Entity, FixedNumberType, HatState, Level}; use crate::TAG_MAP;
use super::{sfx::SfxPlayer, Entity, FixedNumberType, HatState, Level};
use agb::{ use agb::{
display::object::{ObjectControl, Size}, display::object::{ObjectController, Size, Tag},
fixnum::Vector2D, fixnum::Vector2D,
}; };
const SLIME_IDLE: &Tag = TAG_MAP.get("Slime Idle");
const SLIME_JUMP: &Tag = TAG_MAP.get("Slime Jump");
const SLIME_SPLAT: &Tag = TAG_MAP.get("Slime splat");
const SNAIL_EMERGE: &Tag = TAG_MAP.get("Snail Emerge");
const SNAIL_MOVE: &Tag = TAG_MAP.get("Snail Move");
const SNAIL_DEATH: &Tag = TAG_MAP.get("Snail Death");
const SNAIL_IDLE: &Tag = TAG_MAP.get("Snail Idle");
enum UpdateState { enum UpdateState {
Nothing, Nothing,
KillPlayer, KillPlayer,
@ -28,11 +39,11 @@ pub enum EnemyUpdateState {
} }
impl<'a> Enemy<'a> { impl<'a> Enemy<'a> {
pub fn new_slime(object: &'a ObjectControl, start_pos: Vector2D<FixedNumberType>) -> Self { pub fn new_slime(object: &'a ObjectController, start_pos: Vector2D<FixedNumberType>) -> Self {
Enemy::Slime(Slime::new(object, start_pos + (0, 1).into())) Enemy::Slime(Slime::new(object, start_pos + (0, 1).into()))
} }
pub fn new_snail(object: &'a ObjectControl, start_pos: Vector2D<FixedNumberType>) -> Self { pub fn new_snail(object: &'a ObjectController, start_pos: Vector2D<FixedNumberType>) -> Self {
Enemy::Snail(Snail::new(object, start_pos)) Enemy::Snail(Snail::new(object, start_pos))
} }
@ -45,6 +56,7 @@ impl<'a> Enemy<'a> {
pub fn update( pub fn update(
&mut self, &mut self,
controller: &'a ObjectController,
level: &Level, level: &Level,
player_pos: Vector2D<FixedNumberType>, player_pos: Vector2D<FixedNumberType>,
hat_state: HatState, hat_state: HatState,
@ -52,8 +64,12 @@ impl<'a> Enemy<'a> {
sfx_player: &mut SfxPlayer, sfx_player: &mut SfxPlayer,
) -> EnemyUpdateState { ) -> EnemyUpdateState {
let update_state = match self { let update_state = match self {
Enemy::Slime(slime) => slime.update(level, player_pos, hat_state, timer, sfx_player), Enemy::Slime(slime) => {
Enemy::Snail(snail) => snail.update(level, player_pos, hat_state, timer, sfx_player), slime.update(controller, level, player_pos, hat_state, timer, sfx_player)
}
Enemy::Snail(snail) => {
snail.update(controller, level, player_pos, hat_state, timer, sfx_player)
}
Enemy::Empty => UpdateState::Nothing, Enemy::Empty => UpdateState::Nothing,
}; };
@ -82,7 +98,7 @@ struct EnemyInfo<'a> {
impl<'a> EnemyInfo<'a> { impl<'a> EnemyInfo<'a> {
fn new( fn new(
object: &'a ObjectControl, object: &'a ObjectController,
start_pos: Vector2D<FixedNumberType>, start_pos: Vector2D<FixedNumberType>,
collision: Vector2D<u16>, collision: Vector2D<u16>,
) -> Self { ) -> Self {
@ -123,19 +139,18 @@ pub struct Slime<'a> {
} }
impl<'a> Slime<'a> { impl<'a> Slime<'a> {
fn new(object: &'a ObjectControl, start_pos: Vector2D<FixedNumberType>) -> Self { fn new(object: &'a ObjectController, start_pos: Vector2D<FixedNumberType>) -> Self {
let mut slime = Slime { let mut slime = Slime {
enemy_info: EnemyInfo::new(object, start_pos, (14u16, 14u16).into()), enemy_info: EnemyInfo::new(object, start_pos, (14u16, 14u16).into()),
state: SlimeState::Idle, state: SlimeState::Idle,
}; };
slime.enemy_info.entity.sprite.set_sprite_size(Size::S16x16);
slime slime
} }
fn update( fn update(
&mut self, &mut self,
controller: &'a ObjectController,
level: &Level, level: &Level,
player_pos: Vector2D<FixedNumberType>, player_pos: Vector2D<FixedNumberType>,
hat_state: HatState, hat_state: HatState,
@ -147,11 +162,12 @@ impl<'a> Slime<'a> {
match self.state { match self.state {
SlimeState::Idle => { SlimeState::Idle => {
let offset = (timer / 16 % 2) * 4; let offset = (timer / 16) as usize;
self.enemy_info
.entity let frame = SLIME_IDLE.get_animation_sprite(offset);
.sprite let sprite = controller.get_sprite(frame).unwrap();
.set_tile_id(object_tiles::SLIME_IDLE_START + offset as u16);
self.enemy_info.entity.sprite.set_sprite(sprite);
if (self.enemy_info.entity.position - player_pos).magnitude_squared() if (self.enemy_info.entity.position - player_pos).magnitude_squared()
< (64 * 64).into() < (64 * 64).into()
@ -178,7 +194,7 @@ impl<'a> Slime<'a> {
} }
} }
SlimeState::Jumping(jumping_start_frame) => { SlimeState::Jumping(jumping_start_frame) => {
let offset = (timer - jumping_start_frame) / 4; let offset = (timer - jumping_start_frame) as usize / 4;
if timer == jumping_start_frame + 1 { if timer == jumping_start_frame + 1 {
sfx_player.slime_jump(); sfx_player.slime_jump();
@ -188,12 +204,10 @@ impl<'a> Slime<'a> {
self.enemy_info.entity.velocity = (0, 0).into(); self.enemy_info.entity.velocity = (0, 0).into();
self.state = SlimeState::Idle; self.state = SlimeState::Idle;
} else { } else {
let sprite_offset = if offset >= 4 { 7 - offset } else { offset }; let frame = SLIME_JUMP.get_animation_sprite(offset);
let sprite = controller.get_sprite(frame).unwrap();
self.enemy_info self.enemy_info.entity.sprite.set_sprite(sprite);
.entity
.sprite
.set_tile_id(object_tiles::SLIME_JUMP_START + (sprite_offset * 4) as u16);
} }
if player_has_collided { if player_has_collided {
@ -209,17 +223,17 @@ impl<'a> Slime<'a> {
sfx_player.slime_death(); sfx_player.slime_death();
} }
let offset = (timer - dying_start_frame) / 4; let offset = (timer - dying_start_frame) as usize / 4;
self.enemy_info.entity.velocity = (0, 0).into(); self.enemy_info.entity.velocity = (0, 0).into();
if offset >= 4 { if offset >= 4 {
return UpdateState::Remove; return UpdateState::Remove;
} }
self.enemy_info let frame = SLIME_SPLAT.get_animation_sprite(offset);
.entity let sprite = controller.get_sprite(frame).unwrap();
.sprite
.set_tile_id(object_tiles::SLIME_SPLAT_START + (offset * 4) as u16); self.enemy_info.entity.sprite.set_sprite(sprite);
} }
} }
@ -247,14 +261,12 @@ pub struct Snail<'a> {
} }
impl<'a> Snail<'a> { impl<'a> Snail<'a> {
fn new(object: &'a ObjectControl, start_pos: Vector2D<FixedNumberType>) -> Self { fn new(object: &'a ObjectController, start_pos: Vector2D<FixedNumberType>) -> Self {
let mut snail = Snail { let mut snail = Snail {
enemy_info: EnemyInfo::new(object, start_pos, (16u16, 16u16).into()), enemy_info: EnemyInfo::new(object, start_pos, (16u16, 16u16).into()),
state: SnailState::Idle(0), state: SnailState::Idle(0),
}; };
snail.enemy_info.entity.sprite.set_sprite_size(Size::S16x16);
snail snail
} }
@ -264,6 +276,7 @@ impl<'a> Snail<'a> {
fn update( fn update(
&mut self, &mut self,
controller: &'a ObjectController,
level: &Level, level: &Level,
player_pos: Vector2D<FixedNumberType>, player_pos: Vector2D<FixedNumberType>,
hat_state: HatState, hat_state: HatState,
@ -288,10 +301,10 @@ impl<'a> Snail<'a> {
} }
} }
self.enemy_info let frame = SNAIL_IDLE.get_animation_sprite(0);
.entity let sprite = controller.get_sprite(frame).unwrap();
.sprite
.set_tile_id(object_tiles::SNAIL_IDLE_START); self.enemy_info.entity.sprite.set_sprite(sprite);
if player_has_collided { if player_has_collided {
if hat_state != HatState::WizardTowards { if hat_state != HatState::WizardTowards {
return UpdateState::KillPlayer; return UpdateState::KillPlayer;
@ -301,17 +314,17 @@ impl<'a> Snail<'a> {
} }
} }
SnailState::Emerging(time) => { SnailState::Emerging(time) => {
let offset = (timer - time) / 4; let offset = (timer - time) as usize / 4;
if offset >= 5 { if offset >= 5 {
self.state = SnailState::Moving(timer); self.state = SnailState::Moving(timer);
} }
self.enemy_info.entity.velocity = (0, 0).into(); self.enemy_info.entity.velocity = (0, 0).into();
self.enemy_info let frame = SNAIL_EMERGE.get_animation_sprite(offset);
.entity let sprite = controller.get_sprite(frame).unwrap();
.sprite
.set_tile_id(object_tiles::SNAIL_EMERGE_START + (offset * 4) as u16); self.enemy_info.entity.sprite.set_sprite(sprite);
if player_has_collided { if player_has_collided {
if hat_state != HatState::WizardTowards { if hat_state != HatState::WizardTowards {
@ -328,12 +341,12 @@ impl<'a> Snail<'a> {
sfx_player.snail_retreat(); sfx_player.snail_retreat();
} }
let offset = (timer - time) / 8 % 2; let offset = (timer - time) as usize / 8;
self.enemy_info let frame = SNAIL_MOVE.get_animation_sprite(offset);
.entity let sprite = controller.get_sprite(frame).unwrap();
.sprite
.set_tile_id(object_tiles::SNAIL_MOVE + (offset * 4) as u16); self.enemy_info.entity.sprite.set_sprite(sprite);
if timer % 32 == 0 { if timer % 32 == 0 {
let x_vel: FixedNumberType = let x_vel: FixedNumberType =
@ -358,16 +371,16 @@ impl<'a> Snail<'a> {
} }
} }
SnailState::Retreating(time) => { SnailState::Retreating(time) => {
let offset = 5 - (timer - time) / 4; let offset = 5 - (timer - time) as usize / 4;
if offset == 0 { if offset == 0 {
self.state = SnailState::Idle(timer); self.state = SnailState::Idle(timer);
} }
self.enemy_info let frame = SNAIL_EMERGE.get_animation_sprite(offset);
.entity let sprite = controller.get_sprite(frame).unwrap();
.sprite
.set_tile_id(object_tiles::SNAIL_EMERGE_START + (offset * 4) as u16); self.enemy_info.entity.sprite.set_sprite(sprite);
self.enemy_info.entity.velocity = (0, 0).into(); self.enemy_info.entity.velocity = (0, 0).into();
if player_has_collided { if player_has_collided {
@ -383,18 +396,20 @@ impl<'a> Snail<'a> {
sfx_player.snail_death(); sfx_player.snail_death();
} }
let offset = (timer - time) / 4; let offset = (timer - time) as usize / 4;
let tile_id = if offset < 5 { let frame = if offset < 5 {
object_tiles::SNAIL_EMERGE_START + ((5 - offset) * 4) as u16 SNAIL_EMERGE.get_animation_sprite(5 - offset)
} else if offset == 5 { } else if offset == 5 {
object_tiles::SNAIL_IDLE_START SNAIL_IDLE.get_animation_sprite(0)
} else if offset < 5 + 7 { } else if offset < 5 + 7 {
object_tiles::SNAIL_DEATH_START + ((offset - 5) * 4) as u16 SNAIL_DEATH.get_animation_sprite(offset - 5)
} else { } else {
return UpdateState::Remove; return UpdateState::Remove;
}; };
self.enemy_info.entity.sprite.set_tile_id(tile_id); let sprite = controller.get_sprite(frame).unwrap();
self.enemy_info.entity.sprite.set_sprite(sprite);
self.enemy_info.entity.velocity = (0, 0).into(); self.enemy_info.entity.velocity = (0, 0).into();
} }
} }

View file

@ -5,7 +5,7 @@ extern crate alloc;
use agb::{ use agb::{
display::{ display::{
object::{ObjectControl, ObjectStandard, Size}, object::{Graphics, Object, ObjectController, Sprite, Tag, TagMap},
tiled::{ tiled::{
InfiniteScrolledMap, PartialUpdateStatus, TileFormat, TileSet, TileSetting, VRamManager, InfiniteScrolledMap, PartialUpdateStatus, TileFormat, TileSet, TileSetting, VRamManager,
}, },
@ -33,27 +33,6 @@ pub struct Level {
start_pos: (i32, i32), start_pos: (i32, i32),
} }
mod object_tiles {
pub const WIZARD_TILE_START: u16 = 0;
pub const WIZARD_JUMP: u16 = 4 * 4;
pub const WIZARD_FALL_START: u16 = 5 * 4;
pub const HAT_TILE_START: u16 = 9 * 4;
pub const HAT_TILE_START_SECOND: u16 = 28 * 4;
pub const HAT_TILE_START_THIRD: u16 = 38 * 4;
pub const SLIME_IDLE_START: u16 = 19 * 4;
pub const SLIME_JUMP_START: u16 = 20 * 4;
pub const SLIME_SPLAT_START: u16 = 24 * 4;
pub const SNAIL_IDLE_START: u16 = 48 * 4;
pub const SNAIL_EMERGE_START: u16 = 49 * 4;
pub const SNAIL_MOVE: u16 = 54 * 4;
pub const SNAIL_DEATH_START: u16 = 56 * 4;
}
agb::include_gfx!("gfx/object_sheet.toml");
mod map_tiles { mod map_tiles {
use super::Level; use super::Level;
@ -118,18 +97,30 @@ mod map_tiles {
agb::include_gfx!("gfx/tile_sheet.toml"); agb::include_gfx!("gfx/tile_sheet.toml");
const GRAPHICS: &Graphics = agb::include_aseprite!("gfx/sprites.aseprite");
const TAG_MAP: &TagMap = GRAPHICS.tags();
const WALKING: &Tag = TAG_MAP.get("Walking");
const JUMPING: &Tag = TAG_MAP.get("Jumping");
const FALLING: &Tag = TAG_MAP.get("Falling");
const PLAYER_DEATH: &Tag = TAG_MAP.get("Player Death");
const HAT_SPIN_1: &Tag = TAG_MAP.get("HatSpin");
const HAT_SPIN_2: &Tag = TAG_MAP.get("HatSpin2");
const HAT_SPIN_3: &Tag = TAG_MAP.get("HatSpin3");
type FixedNumberType = FixedNum<10>; type FixedNumberType = FixedNum<10>;
pub struct Entity<'a> { pub struct Entity<'a> {
sprite: ObjectStandard<'a>, sprite: Object<'a, 'a>,
position: Vector2D<FixedNumberType>, position: Vector2D<FixedNumberType>,
velocity: Vector2D<FixedNumberType>, velocity: Vector2D<FixedNumberType>,
collision_mask: Vector2D<u16>, collision_mask: Vector2D<u16>,
} }
impl<'a> Entity<'a> { impl<'a> Entity<'a> {
pub fn new(object: &'a ObjectControl, collision_mask: Vector2D<u16>) -> Self { pub fn new(object: &'a ObjectController, collision_mask: Vector2D<u16>) -> Self {
let mut sprite = object.get_object_standard(); let dummy_sprite = object.get_sprite(WALKING.get_sprite(0)).unwrap();
let mut sprite = object.get_object(dummy_sprite).unwrap();
sprite.set_priority(Priority::P1); sprite.set_priority(Priority::P1);
Entity { Entity {
sprite, sprite,
@ -352,14 +343,16 @@ fn ping_pong(i: i32, n: i32) -> i32 {
} }
impl<'a> Player<'a> { impl<'a> Player<'a> {
fn new(controller: &'a ObjectControl, start_position: Vector2D<FixedNumberType>) -> Self { fn new(controller: &'a ObjectController, start_position: Vector2D<FixedNumberType>) -> Self {
let mut hat = Entity::new(controller, (6_u16, 6_u16).into());
let mut wizard = Entity::new(controller, (6_u16, 14_u16).into()); let mut wizard = Entity::new(controller, (6_u16, 14_u16).into());
let mut hat = Entity::new(controller, (6_u16, 6_u16).into());
wizard
.sprite
.set_sprite(controller.get_sprite(HAT_SPIN_1.get_sprite(0)).unwrap());
hat.sprite
.set_sprite(controller.get_sprite(HAT_SPIN_1.get_sprite(0)).unwrap());
wizard.sprite.set_tile_id(object_tiles::WIZARD_TILE_START);
hat.sprite.set_tile_id(object_tiles::HAT_TILE_START);
wizard.sprite.set_sprite_size(Size::S16x16);
hat.sprite.set_sprite_size(Size::S16x16);
wizard.sprite.show(); wizard.sprite.show();
hat.sprite.show(); hat.sprite.show();
@ -385,6 +378,7 @@ impl<'a> Player<'a> {
fn update_frame( fn update_frame(
&mut self, &mut self,
input: &ButtonController, input: &ButtonController,
controller: &'a ObjectController,
timer: i32, timer: i32,
level: &Level, level: &Level,
enemies: &[enemies::Enemy], enemies: &[enemies::Enemy],
@ -459,23 +453,27 @@ impl<'a> Player<'a> {
self.wizard.velocity = self.wizard.update_position(level); self.wizard.velocity = self.wizard.update_position(level);
if self.wizard.velocity.x.abs() > 0.into() { if self.wizard.velocity.x.abs() > 0.into() {
let offset = (ping_pong(timer / 16, 4)) as u16; let offset = (ping_pong(timer / 16, 4)) as usize;
self.wizard_frame = offset as u8; self.wizard_frame = offset as u8;
self.wizard let frame = WALKING.get_animation_sprite(offset);
.sprite let sprite = controller.get_sprite(frame).unwrap();
.set_tile_id(object_tiles::WIZARD_TILE_START + offset * 4);
self.wizard.sprite.set_sprite(sprite);
} }
if self.wizard.velocity.y < -FixedNumberType::new(1) / 16 { if self.wizard.velocity.y < -FixedNumberType::new(1) / 16 {
// going up // going up
self.wizard_frame = 5; self.wizard_frame = 5;
self.wizard.sprite.set_tile_id(object_tiles::WIZARD_JUMP); let frame = JUMPING.get_animation_sprite(0);
let sprite = controller.get_sprite(frame).unwrap();
self.wizard.sprite.set_sprite(sprite);
} else if self.wizard.velocity.y > FixedNumberType::new(1) / 16 { } else if self.wizard.velocity.y > FixedNumberType::new(1) / 16 {
// going down // going down
let offset = if self.wizard.velocity.y * 2 > 3.into() { let offset = if self.wizard.velocity.y * 2 > 3.into() {
((timer / 4) % 4) as u16 (timer / 4) as usize
} else { } else {
// Don't flap beard unless going quickly // Don't flap beard unless going quickly
0 0
@ -483,9 +481,10 @@ impl<'a> Player<'a> {
self.wizard_frame = 0; self.wizard_frame = 0;
self.wizard let frame = FALLING.get_animation_sprite(offset);
.sprite let sprite = controller.get_sprite(frame).unwrap();
.set_tile_id(object_tiles::WIZARD_FALL_START + offset * 4);
self.wizard.sprite.set_sprite(sprite);
} }
if input.x_tri() != agb::input::Tri::Zero { if input.x_tri() != agb::input::Tri::Zero {
@ -494,19 +493,23 @@ impl<'a> Player<'a> {
} }
let hat_base_tile = match self.num_recalls { let hat_base_tile = match self.num_recalls {
0 => object_tiles::HAT_TILE_START, 0 => HAT_SPIN_1,
1 => object_tiles::HAT_TILE_START_SECOND, 1 => HAT_SPIN_2,
_ => object_tiles::HAT_TILE_START_THIRD, _ => HAT_SPIN_3,
}; };
match self.facing { match self.facing {
agb::input::Tri::Negative => { agb::input::Tri::Negative => {
self.wizard.sprite.set_hflip(true); self.wizard.sprite.set_hflip(true);
self.hat.sprite.set_tile_id(hat_base_tile + 4 * 5); self.hat
.sprite
.set_sprite(controller.get_sprite(hat_base_tile.get_sprite(5)).unwrap());
} }
agb::input::Tri::Positive => { agb::input::Tri::Positive => {
self.wizard.sprite.set_hflip(false); self.wizard.sprite.set_hflip(false);
self.hat.sprite.set_tile_id(hat_base_tile); self.hat
.sprite
.set_sprite(controller.get_sprite(hat_base_tile.get_sprite(0)).unwrap());
} }
_ => {} _ => {}
} }
@ -535,11 +538,13 @@ impl<'a> Player<'a> {
_ => 4, _ => 4,
}; };
let hat_sprite_offset = timer / hat_sprite_divider % 10; let hat_sprite_offset = (timer / hat_sprite_divider) as usize;
self.hat self.hat.sprite.set_sprite(
.sprite controller
.set_tile_id(hat_base_tile + (hat_sprite_offset * 4) as u16); .get_sprite(hat_base_tile.get_animation_sprite(hat_sprite_offset))
.unwrap(),
);
if self.hat_slow_counter < 30 && self.hat.velocity.magnitude() < 2.into() { if self.hat_slow_counter < 30 && self.hat.velocity.magnitude() < 2.into() {
self.hat.velocity = (0, 0).into(); self.hat.velocity = (0, 0).into();
@ -570,9 +575,11 @@ impl<'a> Player<'a> {
self.hat.position = self.wizard.position - hat_resting_position; self.hat.position = self.wizard.position - hat_resting_position;
} }
HatState::WizardTowards => { HatState::WizardTowards => {
self.hat self.hat.sprite.set_sprite(
.sprite controller
.set_tile_id(hat_base_tile + 4 * (timer / 2 % 10) as u16); .get_sprite(hat_base_tile.get_animation_sprite(timer as usize / 2))
.unwrap(),
);
let distance_vector = let distance_vector =
self.hat.position - self.wizard.position + hat_resting_position; self.hat.position - self.wizard.position + hat_resting_position;
let distance = distance_vector.magnitude(); let distance = distance_vector.magnitude();
@ -609,7 +616,7 @@ enum UpdateState {
impl<'a, 'b, 'c> PlayingLevel<'a, 'b> { impl<'a, 'b, 'c> PlayingLevel<'a, 'b> {
fn open_level( fn open_level(
level: &'a Level, level: &'a Level,
object_control: &'a ObjectControl, object_control: &'a ObjectController,
background: &'a mut InfiniteScrolledMap<'b>, background: &'a mut InfiniteScrolledMap<'b>,
foreground: &'a mut InfiniteScrolledMap<'b>, foreground: &'a mut InfiniteScrolledMap<'b>,
input: ButtonController, input: ButtonController,
@ -670,15 +677,15 @@ impl<'a, 'b, 'c> PlayingLevel<'a, 'b> {
self.player.wizard.sprite.set_priority(Priority::P0); self.player.wizard.sprite.set_priority(Priority::P0);
} }
fn dead_update(&mut self) -> bool { fn dead_update(&mut self, controller: &'a ObjectController) -> bool {
self.timer += 1; self.timer += 1;
let frame = PLAYER_DEATH.get_animation_sprite(self.timer as usize / 8);
let sprite = controller.get_sprite(frame).unwrap();
self.player.wizard.velocity += (0.into(), FixedNumberType::new(1) / 32).into(); self.player.wizard.velocity += (0.into(), FixedNumberType::new(1) / 32).into();
self.player.wizard.position += self.player.wizard.velocity; self.player.wizard.position += self.player.wizard.velocity;
self.player self.player.wizard.sprite.set_sprite(sprite);
.wizard
.sprite
.set_tile_id((self.timer / 8 % 2 * 4 + 63 * 4) as u16);
self.player.wizard.commit_position(self.background.position); self.player.wizard.commit_position(self.background.position);
@ -689,6 +696,7 @@ impl<'a, 'b, 'c> PlayingLevel<'a, 'b> {
&mut self, &mut self,
sfx_player: &mut sfx::SfxPlayer, sfx_player: &mut sfx::SfxPlayer,
vram: &mut VRamManager, vram: &mut VRamManager,
controller: &'a ObjectController,
) -> UpdateState { ) -> UpdateState {
self.timer += 1; self.timer += 1;
self.input.update(); self.input.update();
@ -697,6 +705,7 @@ impl<'a, 'b, 'c> PlayingLevel<'a, 'b> {
self.player.update_frame( self.player.update_frame(
&self.input, &self.input,
controller,
self.timer, self.timer,
self.background.level, self.background.level,
&self.enemies, &self.enemies,
@ -705,6 +714,7 @@ impl<'a, 'b, 'c> PlayingLevel<'a, 'b> {
for enemy in self.enemies.iter_mut() { for enemy in self.enemies.iter_mut() {
match enemy.update( match enemy.update(
controller,
self.background.level, self.background.level,
self.player.wizard.position, self.player.wizard.position,
self.player.hat_state, self.player.hat_state,
@ -811,11 +821,6 @@ fn main(mut agb: agb::Gba) -> ! {
let mut timer_controller = agb.timers.timers(); let mut timer_controller = agb.timers.timers();
let mut mixer = agb.mixer.mixer(&mut timer_controller.timer0); let mut mixer = agb.mixer.mixer(&mut timer_controller.timer0);
object.set_sprite_palettes(object_sheet::object_sheet.palettes);
object.set_sprite_tilemap(object_sheet::object_sheet.tiles);
object.enable();
mixer.enable(); mixer.enable();
let mut music_box = sfx::MusicBox::new(); let mut music_box = sfx::MusicBox::new();
@ -913,13 +918,15 @@ fn main(mut agb: agb::Gba) -> ! {
world_display.hide(); world_display.hide();
loop { loop {
match level match level.update_frame(
.update_frame(&mut sfx::SfxPlayer::new(&mut mixer, &music_box), &mut vram) &mut sfx::SfxPlayer::new(&mut mixer, &music_box),
{ &mut vram,
&object,
) {
UpdateState::Normal => {} UpdateState::Normal => {}
UpdateState::Dead => { UpdateState::Dead => {
level.dead_start(); level.dead_start();
while level.dead_update() { while level.dead_update(&object) {
music_box.before_frame(&mut mixer); music_box.before_frame(&mut mixer);
mixer.frame(); mixer.frame();
vblank.wait_for_vblank(); vblank.wait_for_vblank();

View file

@ -24,6 +24,9 @@ dependencies = [
"agb_sound_converter", "agb_sound_converter",
"bare-metal", "bare-metal",
"bitflags", "bitflags",
"hashbrown",
"modular-bitfield",
"rustc-hash",
] ]
[[package]] [[package]]
@ -37,6 +40,7 @@ dependencies = [
name = "agb_image_converter" name = "agb_image_converter"
version = "0.6.0" version = "0.6.0"
dependencies = [ dependencies = [
"asefile",
"image", "image",
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -51,7 +55,6 @@ version = "0.1.0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rand",
"syn", "syn",
] ]
@ -65,6 +68,31 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "asefile"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0d5f7de918fd4cb18249819fc4bd27f6a5dbfbc9dcb271727f27dacf17ce880"
dependencies = [
"bitflags",
"byteorder",
"flate2",
"image",
"log",
"nohash",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -133,11 +161,24 @@ dependencies = [
[[package]] [[package]]
name = "deflate" name = "deflate"
version = "1.0.0" version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
dependencies = [ dependencies = [
"adler32", "adler32",
"byteorder",
]
[[package]]
name = "flate2"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if 1.0.0",
"crc32fast",
"libc",
"miniz_oxide 0.4.4",
] ]
[[package]] [[package]]
@ -160,6 +201,15 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "hashbrown"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758"
dependencies = [
"ahash",
]
[[package]] [[package]]
name = "hound" name = "hound"
version = "3.4.0" version = "3.4.0"
@ -168,9 +218,9 @@ checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
[[package]] [[package]]
name = "image" name = "image"
version = "0.24.1" version = "0.23.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db207d030ae38f1eb6f240d5a1c1c88ff422aa005d10f8c6c6fc5e75286ab30e" checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"byteorder", "byteorder",
@ -200,14 +250,60 @@ dependencies = [
] ]
[[package]] [[package]]
name = "miniz_oxide" name = "log"
version = "0.5.1" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "miniz_oxide"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [
"adler32",
]
[[package]]
name = "miniz_oxide"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [ dependencies = [
"adler", "adler",
"autocfg",
] ]
[[package]]
name = "modular-bitfield"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74"
dependencies = [
"modular-bitfield-impl",
"static_assertions",
]
[[package]]
name = "modular-bitfield-impl"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "nohash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca"
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.44" version = "0.1.44"
@ -231,9 +327,9 @@ dependencies = [
[[package]] [[package]]
name = "num-rational" name = "num-rational"
version = "0.4.0" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"num-integer", "num-integer",
@ -250,23 +346,23 @@ dependencies = [
] ]
[[package]] [[package]]
name = "png" name = "once_cell"
version = "0.17.5" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
name = "png"
version = "0.16.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crc32fast", "crc32fast",
"deflate", "deflate",
"miniz_oxide", "miniz_oxide 0.3.7",
] ]
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.36" version = "1.0.36"
@ -285,42 +381,18 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "rle-decode-fast" name = "rle-decode-fast"
version = "1.0.3" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.136" version = "1.0.136"
@ -341,6 +413,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.86" version = "1.0.86"
@ -394,6 +472,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.10.2+wasi-snapshot-preview1" version = "0.10.2+wasi-snapshot-preview1"

View file

@ -14,7 +14,7 @@ use rng::get_random;
use agb::{ use agb::{
display::{ display::{
object::{ObjectControl, ObjectStandard}, object::{Graphics, Object, ObjectController, Sprite, Tag, TagMap},
tiled::{InfiniteScrolledMap, TileFormat, TileSet, TileSetting, VRamManager}, tiled::{InfiniteScrolledMap, TileFormat, TileSet, TileSetting, VRamManager},
Priority, HEIGHT, WIDTH, Priority, HEIGHT, WIDTH,
}, },
@ -25,7 +25,33 @@ use agb::{
use generational_arena::Arena; use generational_arena::Arena;
use sfx::Sfx; use sfx::Sfx;
agb::include_gfx!("gfx/objects.toml"); const GRAPHICS: &Graphics = agb::include_aseprite!("gfx/objects.aseprite", "gfx/boss.aseprite");
const TAG_MAP: &TagMap = GRAPHICS.tags();
const LONGSWORD_IDLE: &Tag = TAG_MAP.get("Idle - longsword");
const LONGSWORD_WALK: &Tag = TAG_MAP.get("Walk - longsword");
const LONGSWORD_JUMP: &Tag = TAG_MAP.get("Jump - longsword");
const LONGSWORD_ATTACK: &Tag = TAG_MAP.get("Attack - longsword");
const LONGSWORD_JUMP_ATTACK: &Tag = TAG_MAP.get("Jump attack - longsword");
const SHORTSWORD_IDLE: &Tag = TAG_MAP.get("Idle - shortsword");
const SHORTSWORD_WALK: &Tag = TAG_MAP.get("Walk - shortsword");
const SHORTSWORD_JUMP: &Tag = TAG_MAP.get("jump - shortsword");
const SHORTSWORD_ATTACK: &Tag = TAG_MAP.get("attack - shortsword");
const SHORTSWORD_JUMP_ATTACK: &Tag = TAG_MAP.get("jump attack - shortsword");
const KNIFE_IDLE: &Tag = TAG_MAP.get("idle - knife");
const KNIFE_WALK: &Tag = TAG_MAP.get("walk - knife");
const KNIFE_JUMP: &Tag = TAG_MAP.get("jump - knife");
const KNIFE_ATTACK: &Tag = TAG_MAP.get("attack - knife");
const KNIFE_JUMP_ATTACK: &Tag = TAG_MAP.get("jump attack - knife");
const SWORDLESS_IDLE: &Tag = TAG_MAP.get("idle swordless");
const SWORDLESS_WALK: &Tag = TAG_MAP.get("walk swordless");
const SWORDLESS_JUMP: &Tag = TAG_MAP.get("jump swordless");
const SWORDLESS_ATTACK: &Tag = KNIFE_ATTACK;
const SWORDLESS_JUMP_ATTACK: &Tag = KNIFE_JUMP_ATTACK;
agb::include_gfx!("gfx/background.toml"); agb::include_gfx!("gfx/background.toml");
type Number = FixedNum<8>; type Number = FixedNum<8>;
@ -126,7 +152,7 @@ impl<'a> Level<'a> {
} }
struct Entity<'a> { struct Entity<'a> {
sprite: ObjectStandard<'a>, sprite: Object<'a, 'a>,
position: Vector2D<Number>, position: Vector2D<Number>,
velocity: Vector2D<Number>, velocity: Vector2D<Number>,
collision_mask: Rect<u16>, collision_mask: Rect<u16>,
@ -134,8 +160,11 @@ struct Entity<'a> {
} }
impl<'a> Entity<'a> { impl<'a> Entity<'a> {
fn new(object_controller: &'a ObjectControl, collision_mask: Rect<u16>) -> Self { fn new(object_controller: &'a ObjectController, collision_mask: Rect<u16>) -> Self {
let mut sprite = object_controller.get_object_standard(); let s = object_controller
.get_sprite(LONGSWORD_IDLE.get_sprite(0))
.unwrap();
let mut sprite = object_controller.get_object(s).unwrap();
sprite.set_priority(Priority::P1); sprite.set_priority(Priority::P1);
Entity { Entity {
sprite, sprite,
@ -320,34 +349,30 @@ impl SwordState {
SwordState::Swordless => Number::new(6) / 256, SwordState::Swordless => Number::new(6) / 256,
} }
} }
fn idle_animation(self, counter: &mut u16) -> u16 { fn idle_animation(self, counter: u16) -> &'static Sprite {
if *counter >= 4 * 8 { let counter = counter as usize;
*counter = 0;
}
match self { match self {
SwordState::LongSword => (*counter / 8) * 4, SwordState::LongSword => LONGSWORD_IDLE.get_animation_sprite(counter / 8),
SwordState::ShortSword => (41 + *counter / 8) * 4, SwordState::ShortSword => SHORTSWORD_IDLE.get_animation_sprite(counter / 8),
SwordState::Dagger => (96 + *counter / 8) * 4, SwordState::Dagger => KNIFE_IDLE.get_animation_sprite(counter / 8),
SwordState::Swordless => (154 + *counter / 8) * 4, SwordState::Swordless => SWORDLESS_IDLE.get_animation_sprite(counter / 8),
} }
} }
fn jump_offset(self) -> u16 { fn jump_tag(self) -> &'static Tag {
match self { match self {
SwordState::LongSword => 10, SwordState::LongSword => LONGSWORD_JUMP,
SwordState::ShortSword => 51, SwordState::ShortSword => SHORTSWORD_JUMP,
SwordState::Dagger => 106, SwordState::Dagger => KNIFE_JUMP,
SwordState::Swordless => 164, SwordState::Swordless => SWORDLESS_JUMP,
} }
} }
fn walk_animation(self, counter: &mut u16) -> u16 { fn walk_animation(self, counter: u16) -> &'static Sprite {
if *counter >= 6 * 4 { let counter = counter as usize;
*counter = 0;
}
match self { match self {
SwordState::LongSword => (4 + *counter / 4) * 4, SwordState::LongSword => LONGSWORD_WALK.get_animation_sprite(counter / 4),
SwordState::ShortSword => (45 + *counter / 4) * 4, SwordState::ShortSword => SHORTSWORD_WALK.get_animation_sprite(counter / 4),
SwordState::Dagger => (100 + *counter / 4) * 4, SwordState::Dagger => KNIFE_WALK.get_animation_sprite(counter / 4),
SwordState::Swordless => (158 + *counter / 4) * 4, SwordState::Swordless => SWORDLESS_WALK.get_animation_sprite(counter / 4),
} }
} }
fn attack_duration(self) -> u16 { fn attack_duration(self) -> u16 {
@ -374,20 +399,20 @@ impl SwordState {
SwordState::Swordless => (self.attack_duration() - timer) / 8, SwordState::Swordless => (self.attack_duration() - timer) / 8,
} }
} }
fn jump_attack_tag(self) -> &'static Tag {
match self {
SwordState::LongSword => LONGSWORD_JUMP_ATTACK,
SwordState::ShortSword => SHORTSWORD_JUMP_ATTACK,
SwordState::Dagger => KNIFE_JUMP_ATTACK,
SwordState::Swordless => SWORDLESS_JUMP_ATTACK,
}
}
fn jump_attack_frame(self, timer: u16) -> u16 { fn jump_attack_frame(self, timer: u16) -> u16 {
(self.jump_attack_duration().saturating_sub(timer)) / 8 (self.jump_attack_duration().saturating_sub(timer)) / 8
} }
fn hold_frame(self) -> u16 { fn hold_frame(self) -> u16 {
7 7
} }
fn jump_attack_hold_frame(self) -> u16 {
match self {
SwordState::LongSword => 13,
SwordState::ShortSword => 54,
SwordState::Dagger => 109,
SwordState::Swordless => 0,
}
}
fn cooldown_time(self) -> u16 { fn cooldown_time(self) -> u16 {
match self { match self {
@ -397,26 +422,15 @@ impl SwordState {
SwordState::Swordless => 0, SwordState::Swordless => 0,
} }
} }
fn to_sprite_id(self, frame: u16) -> u16 { fn attack_tag(self) -> &'static Tag {
match self { match self {
SwordState::LongSword => (16 + frame) * 4, SwordState::LongSword => LONGSWORD_ATTACK,
SwordState::ShortSword => (57 + frame) * 4, SwordState::ShortSword => SHORTSWORD_ATTACK,
SwordState::Dagger => (112 + frame) * 4, SwordState::Dagger => KNIFE_ATTACK,
SwordState::Swordless => 0, SwordState::Swordless => SWORDLESS_ATTACK,
}
}
fn to_jump_sprite_id(self, frame: u16) -> u16 {
if frame == self.jump_attack_hold_frame() {
frame * 4
} else {
match self {
SwordState::LongSword => (24 + frame) * 4,
SwordState::ShortSword => (65 + frame) * 4,
SwordState::Dagger => (120 + frame) * 4,
SwordState::Swordless => 0,
}
} }
} }
fn fudge(self, frame: u16) -> i32 { fn fudge(self, frame: u16) -> i32 {
match self { match self {
SwordState::LongSword => long_sword_fudge(frame), SwordState::LongSword => long_sword_fudge(frame),
@ -519,15 +533,15 @@ struct Player<'a> {
} }
impl<'a> Player<'a> { impl<'a> Player<'a> {
fn new(object_controller: &'a ObjectControl) -> Player { fn new(object_controller: &'a ObjectController) -> Player {
let mut entity = Entity::new( let mut entity = Entity::new(
object_controller, object_controller,
Rect::new((0_u16, 0_u16).into(), (4_u16, 12_u16).into()), Rect::new((0_u16, 0_u16).into(), (4_u16, 12_u16).into()),
); );
entity let s = object_controller
.sprite .get_sprite(LONGSWORD_IDLE.get_sprite(0))
.set_sprite_size(agb::display::object::Size::S16x16); .unwrap();
entity.sprite.set_tile_id(0); entity.sprite.set_sprite(s);
entity.sprite.show(); entity.sprite.show();
entity.position = (144, 0).into(); entity.position = (144, 0).into();
entity.sprite.commit(); entity.sprite.commit();
@ -548,6 +562,7 @@ impl<'a> Player<'a> {
fn update( fn update(
&mut self, &mut self,
controller: &'a ObjectController,
buttons: &ButtonController, buttons: &ButtonController,
level: &Level, level: &Level,
sfx: &mut sfx::Sfx, sfx: &mut sfx::Sfx,
@ -579,13 +594,15 @@ impl<'a> Player<'a> {
self.entity.sprite.set_hflip(self.facing == Tri::Negative); self.entity.sprite.set_hflip(self.facing == Tri::Negative);
self.entity.velocity.x += self.sword.ground_walk_force() * x as i32; self.entity.velocity.x += self.sword.ground_walk_force() * x as i32;
if self.entity.velocity.x.abs() > Number::new(1) / 10 { if self.entity.velocity.x.abs() > Number::new(1) / 10 {
self.entity let sprite = controller
.sprite .get_sprite(self.sword.walk_animation(self.sprite_offset))
.set_tile_id(self.sword.walk_animation(&mut self.sprite_offset)); .unwrap();
self.entity.sprite.set_sprite(sprite);
} else { } else {
self.entity let sprite = controller
.sprite .get_sprite(self.sword.idle_animation(self.sprite_offset))
.set_tile_id(self.sword.idle_animation(&mut self.sprite_offset)); .unwrap();
self.entity.sprite.set_sprite(sprite);
} }
if b_press && self.sword != SwordState::Swordless { if b_press && self.sword != SwordState::Swordless {
@ -603,9 +620,11 @@ impl<'a> Player<'a> {
*a -= 1; *a -= 1;
let frame = self.sword.attack_frame(*a); let frame = self.sword.attack_frame(*a);
self.fudge_factor.x = self.sword.fudge(frame) * self.facing as i32; self.fudge_factor.x = self.sword.fudge(frame) * self.facing as i32;
self.entity let tag = self.sword.attack_tag();
.sprite let sprite = controller
.set_tile_id(self.sword.to_sprite_id(frame)); .get_sprite(tag.get_animation_sprite(frame as usize))
.unwrap();
self.entity.sprite.set_sprite(sprite);
hurtbox = self.sword.ground_attack_hurtbox(frame); hurtbox = self.sword.ground_attack_hurtbox(frame);
@ -617,9 +636,11 @@ impl<'a> Player<'a> {
*a -= 1; *a -= 1;
let frame = self.sword.hold_frame(); let frame = self.sword.hold_frame();
self.fudge_factor.x = self.sword.fudge(frame) * self.facing as i32; self.fudge_factor.x = self.sword.fudge(frame) * self.facing as i32;
self.entity let tag = self.sword.attack_tag();
.sprite let sprite = controller
.set_tile_id(self.sword.to_sprite_id(frame)); .get_sprite(tag.get_animation_sprite(frame as usize))
.unwrap();
self.entity.sprite.set_sprite(sprite);
if *a == 0 { if *a == 0 {
self.attack_timer = AttackTimer::Idle; self.attack_timer = AttackTimer::Idle;
} }
@ -631,7 +652,7 @@ impl<'a> Player<'a> {
match &mut self.attack_timer { match &mut self.attack_timer {
AttackTimer::Idle => { AttackTimer::Idle => {
let sprite = if self.sprite_offset < 3 * 4 { let frame = if self.sprite_offset < 3 * 4 {
self.sprite_offset / 4 self.sprite_offset / 4
} else if self.entity.velocity.y.abs() < Number::new(1) / 5 { } else if self.entity.velocity.y.abs() < Number::new(1) / 5 {
3 3
@ -642,9 +663,11 @@ impl<'a> Player<'a> {
} else { } else {
2 2
}; };
self.entity let tag = self.sword.jump_tag();
.sprite let sprite = controller
.set_tile_id((sprite + self.sword.jump_offset()) * 4); .get_sprite(tag.get_animation_sprite(frame as usize))
.unwrap();
self.entity.sprite.set_sprite(sprite);
if x != Tri::Zero { if x != Tri::Zero {
self.facing = x; self.facing = x;
@ -664,9 +687,11 @@ impl<'a> Player<'a> {
AttackTimer::Attack(a) => { AttackTimer::Attack(a) => {
*a -= 1; *a -= 1;
let frame = self.sword.jump_attack_frame(*a); let frame = self.sword.jump_attack_frame(*a);
self.entity let tag = self.sword.jump_attack_tag();
.sprite let sprite = controller
.set_tile_id(self.sword.to_jump_sprite_id(frame)); .get_sprite(tag.get_animation_sprite(frame as usize))
.unwrap();
self.entity.sprite.set_sprite(sprite);
hurtbox = self.sword.air_attack_hurtbox(frame); hurtbox = self.sword.air_attack_hurtbox(frame);
@ -798,9 +823,10 @@ impl BatData {
} }
} }
fn update( fn update<'a>(
&mut self, &mut self,
entity: &mut Entity, controller: &'a ObjectController,
entity: &mut Entity<'a>,
player: &Player, player: &Player,
level: &Level, level: &Level,
sfx: &mut sfx::Sfx, sfx: &mut sfx::Sfx,
@ -813,6 +839,8 @@ impl BatData {
.unwrap_or(false); .unwrap_or(false);
let should_damage = entity.collider().touches(player.entity.collider()); let should_damage = entity.collider().touches(player.entity.collider());
const BAT_IDLE: &Tag = TAG_MAP.get("bat");
match &mut self.bat_state { match &mut self.bat_state {
BatState::Idle => { BatState::Idle => {
self.sprite_offset += 1; self.sprite_offset += 1;
@ -824,7 +852,10 @@ impl BatData {
sfx.bat_flap(); sfx.bat_flap();
} }
entity.sprite.set_tile_id((78 + self.sprite_offset / 8) * 4); let sprite = BAT_IDLE.get_sprite(self.sprite_offset as usize / 8);
let sprite = controller.get_sprite(sprite).unwrap();
entity.sprite.set_sprite(sprite);
if (entity.position - player.entity.position).manhattan_distance() < 50.into() { if (entity.position - player.entity.position).manhattan_distance() < 50.into() {
self.bat_state = BatState::Chasing(300); self.bat_state = BatState::Chasing(300);
@ -855,7 +886,11 @@ impl BatData {
if self.sprite_offset >= 9 * 2 { if self.sprite_offset >= 9 * 2 {
self.sprite_offset = 0; self.sprite_offset = 0;
} }
entity.sprite.set_tile_id((78 + self.sprite_offset / 2) * 4);
let sprite = BAT_IDLE.get_sprite(self.sprite_offset as usize / 2);
let sprite = controller.get_sprite(sprite).unwrap();
entity.sprite.set_sprite(sprite);
if self.sprite_offset == 2 * 5 { if self.sprite_offset == 2 * 5 {
sfx.bat_flap(); sfx.bat_flap();
@ -878,7 +913,12 @@ impl BatData {
} }
} }
BatState::Dead => { BatState::Dead => {
entity.sprite.set_tile_id(87 * 4); const BAT_DEAD: &Tag = TAG_MAP.get("bat dead");
let sprite = BAT_DEAD.get_sprite(0);
let sprite = controller.get_sprite(sprite).unwrap();
entity.sprite.set_sprite(sprite);
let gravity: Number = 1.into(); let gravity: Number = 1.into();
let gravity = gravity / 16; let gravity = gravity / 16;
entity.velocity.x = 0.into(); entity.velocity.x = 0.into();
@ -916,9 +956,10 @@ impl SlimeData {
} }
} }
fn update( fn update<'a>(
&mut self, &mut self,
entity: &mut Entity, controller: &'a ObjectController,
entity: &mut Entity<'a>,
player: &Player, player: &Player,
level: &Level, level: &Level,
sfx: &mut sfx::Sfx, sfx: &mut sfx::Sfx,
@ -939,9 +980,12 @@ impl SlimeData {
self.sprite_offset = 0; self.sprite_offset = 0;
} }
entity const IDLE: &Tag = TAG_MAP.get("slime idle");
.sprite
.set_tile_id((29 + self.sprite_offset / 16) * 4); let sprite = IDLE.get_sprite(self.sprite_offset as usize / 16);
let sprite = controller.get_sprite(sprite).unwrap();
entity.sprite.set_sprite(sprite);
if (player.entity.position - entity.position).manhattan_distance() < 40.into() { if (player.entity.position - entity.position).manhattan_distance() < 40.into() {
let direction = match player.entity.position.x.cmp(&entity.position.x) { let direction = match player.entity.position.x.cmp(&entity.position.x) {
@ -976,7 +1020,12 @@ impl SlimeData {
sfx.slime_boing(); sfx.slime_boing();
} }
entity.sprite.set_tile_id((frame + 31) * 4); const CHASE: &Tag = TAG_MAP.get("Slime jump");
let sprite = CHASE.get_sprite(frame as usize);
let sprite = controller.get_sprite(sprite).unwrap();
entity.sprite.set_sprite(sprite);
entity.velocity.x = match frame { entity.velocity.x = match frame {
2 | 3 | 4 => (Number::new(1) / 5) * Number::new(*direction as i32), 2 | 3 | 4 => (Number::new(1) / 5) * Number::new(*direction as i32),
@ -1002,7 +1051,11 @@ impl SlimeData {
} }
SlimeState::Dead(count) => { SlimeState::Dead(count) => {
if *count < 5 * 4 { if *count < 5 * 4 {
entity.sprite.set_tile_id((36 + *count / 4) * 4); const DEATH: &Tag = TAG_MAP.get("Slime death");
let sprite = DEATH.get_sprite(*count as usize / 4);
let sprite = controller.get_sprite(sprite).unwrap();
entity.sprite.set_sprite(sprite);
*count += 1; *count += 1;
} else { } else {
return UpdateInstruction::Remove; return UpdateInstruction::Remove;
@ -1032,9 +1085,10 @@ impl MiniFlameData {
} }
} }
fn update( fn update<'a>(
&mut self, &mut self,
entity: &mut Entity, controller: &'a ObjectController,
entity: &mut Entity<'a>,
player: &Player, player: &Player,
_level: &Level, _level: &Level,
sfx: &mut sfx::Sfx, sfx: &mut sfx::Sfx,
@ -1050,6 +1104,8 @@ impl MiniFlameData {
self.sprite_offset += 1; self.sprite_offset += 1;
const ANGRY: &Tag = TAG_MAP.get("angry boss");
match &mut self.state { match &mut self.state {
MiniFlameState::Idle(frames) => { MiniFlameState::Idle(frames) => {
*frames -= 1; *frames -= 1;
@ -1064,13 +1120,9 @@ impl MiniFlameData {
entity.velocity = resulting_direction.normalise() * Number::new(2); entity.velocity = resulting_direction.normalise() * Number::new(2);
} }
} else { } else {
if self.sprite_offset >= 12 * 8 { let sprite = ANGRY.get_animation_sprite(self.sprite_offset as usize / 8);
self.sprite_offset = 0; let sprite = controller.get_sprite(sprite).unwrap();
} entity.sprite.set_sprite(sprite);
entity
.sprite
.set_tile_id((137 + self.sprite_offset / 8) * 4);
entity.velocity = (0.into(), Number::new(-1) / Number::new(4)).into(); entity.velocity = (0.into(), Number::new(-1) / Number::new(4)).into();
} }
@ -1112,17 +1164,13 @@ impl MiniFlameData {
instruction = UpdateInstruction::DamagePlayer; instruction = UpdateInstruction::DamagePlayer;
} }
if self.sprite_offset >= 12 * 2 {
self.sprite_offset = 0;
}
if entity.velocity.manhattan_distance() < Number::new(1) / Number::new(4) { if entity.velocity.manhattan_distance() < Number::new(1) / Number::new(4) {
self.state = MiniFlameState::Idle(90); self.state = MiniFlameState::Idle(90);
} }
entity let sprite = ANGRY.get_animation_sprite(self.sprite_offset as usize / 2);
.sprite let sprite = controller.get_sprite(sprite).unwrap();
.set_tile_id((137 + self.sprite_offset / 2) * 4); entity.sprite.set_sprite(sprite);
} }
MiniFlameState::Dead => { MiniFlameState::Dead => {
entity.velocity = (0, 0).into(); entity.velocity = (0, 0).into();
@ -1130,9 +1178,11 @@ impl MiniFlameData {
instruction = UpdateInstruction::Remove; instruction = UpdateInstruction::Remove;
} }
entity const DEATH: &Tag = TAG_MAP.get("angry boss dead");
.sprite
.set_tile_id((148 + self.sprite_offset / 12) * 4); let sprite = DEATH.get_animation_sprite(self.sprite_offset as usize / 12);
let sprite = controller.get_sprite(sprite).unwrap();
entity.sprite.set_sprite(sprite);
self.sprite_offset += 1; self.sprite_offset += 1;
} }
@ -1164,9 +1214,10 @@ impl EmuData {
} }
} }
fn update( fn update<'a>(
&mut self, &mut self,
entity: &mut Entity, controller: &'a ObjectController,
entity: &mut Entity<'a>,
player: &Player, player: &Player,
level: &Level, level: &Level,
sfx: &mut sfx::Sfx, sfx: &mut sfx::Sfx,
@ -1188,9 +1239,11 @@ impl EmuData {
self.sprite_offset = 0; self.sprite_offset = 0;
} }
entity const IDLE: &Tag = TAG_MAP.get("emu - idle");
.sprite
.set_tile_id((170 + self.sprite_offset / 16) * 4); let sprite = IDLE.get_sprite(self.sprite_offset as usize / 16);
let sprite = controller.get_sprite(sprite).unwrap();
entity.sprite.set_sprite(sprite);
if (entity.position.y - player.entity.position.y).abs() < 10.into() { if (entity.position.y - player.entity.position.y).abs() < 10.into() {
let velocity = Number::new(1) let velocity = Number::new(1)
@ -1233,9 +1286,11 @@ impl EmuData {
sfx.emu_step(); sfx.emu_step();
} }
entity const WALK: &Tag = TAG_MAP.get("emu-walk");
.sprite
.set_tile_id((173 + self.sprite_offset / 2) * 4); let sprite = WALK.get_sprite(self.sprite_offset as usize / 2);
let sprite = controller.get_sprite(sprite).unwrap();
entity.sprite.set_sprite(sprite);
let gravity: Number = 1.into(); let gravity: Number = 1.into();
let gravity = gravity / 16; let gravity = gravity / 16;
@ -1286,9 +1341,12 @@ impl EmuData {
instruction = UpdateInstruction::Remove; instruction = UpdateInstruction::Remove;
} }
entity const DEATH: &Tag = TAG_MAP.get("emu - die");
.sprite
.set_tile_id((177 + self.sprite_offset / 4) * 4); let sprite = DEATH.get_animation_sprite(self.sprite_offset as usize / 4);
let sprite = controller.get_sprite(sprite).unwrap();
entity.sprite.set_sprite(sprite);
self.sprite_offset += 1; self.sprite_offset += 1;
} }
} }
@ -1316,27 +1374,32 @@ impl EnemyData {
} }
} }
fn tile_id(&self) -> u16 { fn sprite(&self) -> &'static Sprite {
const SLIME: &Tag = TAG_MAP.get("slime idle");
const BAT: &Tag = TAG_MAP.get("bat");
const MINI_FLAME: &Tag = TAG_MAP.get("angry boss");
const EMU: &Tag = TAG_MAP.get("emu - idle");
match self { match self {
EnemyData::Slime(_) => 29, EnemyData::Slime(_) => SLIME.get_sprite(0),
EnemyData::Bat(_) => 78, EnemyData::Bat(_) => BAT.get_sprite(0),
EnemyData::MiniFlame(_) => 137, EnemyData::MiniFlame(_) => MINI_FLAME.get_sprite(0),
EnemyData::Emu(_) => 170, EnemyData::Emu(_) => EMU.get_sprite(0),
} }
} }
fn update( fn update<'a>(
&mut self, &mut self,
entity: &mut Entity, controller: &'a ObjectController,
entity: &mut Entity<'a>,
player: &Player, player: &Player,
level: &Level, level: &Level,
sfx: &mut sfx::Sfx, sfx: &mut sfx::Sfx,
) -> UpdateInstruction { ) -> UpdateInstruction {
match self { match self {
EnemyData::Slime(data) => data.update(entity, player, level, sfx), EnemyData::Slime(data) => data.update(controller, entity, player, level, sfx),
EnemyData::Bat(data) => data.update(entity, player, level, sfx), EnemyData::Bat(data) => data.update(controller, entity, player, level, sfx),
EnemyData::MiniFlame(data) => data.update(entity, player, level, sfx), EnemyData::MiniFlame(data) => data.update(controller, entity, player, level, sfx),
EnemyData::Emu(data) => data.update(entity, player, level, sfx), EnemyData::Emu(data) => data.update(controller, entity, player, level, sfx),
} }
} }
} }
@ -1347,13 +1410,13 @@ struct Enemy<'a> {
} }
impl<'a> Enemy<'a> { impl<'a> Enemy<'a> {
fn new(object_controller: &'a ObjectControl, enemy_data: EnemyData) -> Self { fn new(object_controller: &'a ObjectController, enemy_data: EnemyData) -> Self {
let mut entity = Entity::new(object_controller, enemy_data.collision_mask()); let mut entity = Entity::new(object_controller, enemy_data.collision_mask());
entity let sprite = enemy_data.sprite();
.sprite let sprite = object_controller.get_sprite(sprite).unwrap();
.set_sprite_size(agb::display::object::Size::S16x16);
entity.sprite.set_tile_id(enemy_data.tile_id()); entity.sprite.set_sprite(sprite);
entity.sprite.show(); entity.sprite.show();
entity.sprite.commit(); entity.sprite.commit();
@ -1361,8 +1424,15 @@ impl<'a> Enemy<'a> {
Self { entity, enemy_data } Self { entity, enemy_data }
} }
fn update(&mut self, player: &Player, level: &Level, sfx: &mut sfx::Sfx) -> UpdateInstruction { fn update(
self.enemy_data.update(&mut self.entity, player, level, sfx) &mut self,
controller: &'a ObjectController,
player: &Player,
level: &Level,
sfx: &mut sfx::Sfx,
) -> UpdateInstruction {
self.enemy_data
.update(controller, &mut self.entity, player, level, sfx)
} }
} }
@ -1393,9 +1463,10 @@ impl ParticleData {
} }
} }
fn update( fn update<'a>(
&mut self, &mut self,
entity: &mut Entity, controller: &'a ObjectController,
entity: &mut Entity<'a>,
player: &Player, player: &Player,
_level: &Level, _level: &Level,
) -> UpdateInstruction { ) -> UpdateInstruction {
@ -1405,7 +1476,11 @@ impl ParticleData {
return UpdateInstruction::Remove; return UpdateInstruction::Remove;
} }
entity.sprite.set_tile_id((70 + *frame / 3) * 4); const DUST: &Tag = TAG_MAP.get("dust");
let sprite = DUST.get_sprite(*frame as usize / 3);
let sprite = controller.get_sprite(sprite).unwrap();
entity.sprite.set_sprite(sprite);
*frame += 1; *frame += 1;
UpdateInstruction::None UpdateInstruction::None
@ -1415,7 +1490,11 @@ impl ParticleData {
return UpdateInstruction::Remove; // have played the animation 6 times return UpdateInstruction::Remove; // have played the animation 6 times
} }
entity.sprite.set_tile_id((88 + (*frame / 3) % 8) * 4); const HEALTH: &Tag = TAG_MAP.get("Heath");
let sprite = HEALTH.get_animation_sprite(*frame as usize / 3);
let sprite = controller.get_sprite(sprite).unwrap();
entity.sprite.set_sprite(sprite);
if *frame < 8 * 3 * 3 { if *frame < 8 * 3 * 3 {
entity.velocity.y = Number::new(-1) / 2; entity.velocity.y = Number::new(-1) / 2;
@ -1437,7 +1516,11 @@ impl ParticleData {
UpdateInstruction::None UpdateInstruction::None
} }
ParticleData::BossHealer(frame, target) => { ParticleData::BossHealer(frame, target) => {
entity.sprite.set_tile_id((88 + (*frame / 3) % 8) * 4); const HEALTH: &Tag = TAG_MAP.get("Heath");
let sprite = HEALTH.get_animation_sprite(*frame as usize / 3);
let sprite = controller.get_sprite(sprite).unwrap();
entity.sprite.set_sprite(sprite);
if *frame < 8 * 3 * 3 { if *frame < 8 * 3 * 3 {
entity.velocity.y = Number::new(-1) / 2; entity.velocity.y = Number::new(-1) / 2;
@ -1470,7 +1553,7 @@ struct Particle<'a> {
impl<'a> Particle<'a> { impl<'a> Particle<'a> {
fn new( fn new(
object_controller: &'a ObjectControl, object_controller: &'a ObjectController,
particle_data: ParticleData, particle_data: ParticleData,
position: Vector2D<Number>, position: Vector2D<Number>,
) -> Self { ) -> Self {
@ -1479,11 +1562,6 @@ impl<'a> Particle<'a> {
Rect::new((0u16, 0u16).into(), (0u16, 0u16).into()), Rect::new((0u16, 0u16).into(), (0u16, 0u16).into()),
); );
entity
.sprite
.set_sprite_size(agb::display::object::Size::S16x16);
entity.sprite.set_tile_id(particle_data.tile_id() * 4);
entity.sprite.show();
entity.position = position; entity.position = position;
Self { Self {
@ -1492,8 +1570,15 @@ impl<'a> Particle<'a> {
} }
} }
fn update(&mut self, player: &Player, level: &Level) -> UpdateInstruction { fn update(
self.particle_data.update(&mut self.entity, player, level) &mut self,
controller: &'a ObjectController,
player: &Player,
level: &Level,
) -> UpdateInstruction {
self.entity.sprite.show();
self.particle_data
.update(controller, &mut self.entity, player, level)
} }
} }
@ -1514,14 +1599,14 @@ impl<'a> BossState<'a> {
fn update( fn update(
&mut self, &mut self,
enemies: &mut Arena<Enemy<'a>>, enemies: &mut Arena<Enemy<'a>>,
object_controller: &'a ObjectControl, object_controller: &'a ObjectController,
player: &Player, player: &Player,
sfx: &mut sfx::Sfx, sfx: &mut sfx::Sfx,
) -> BossInstruction { ) -> BossInstruction {
match self { match self {
BossState::Active(boss) => boss.update(enemies, object_controller, player, sfx), BossState::Active(boss) => boss.update(enemies, object_controller, player, sfx),
BossState::Following(boss) => { BossState::Following(boss) => {
boss.update(player); boss.update(object_controller, player);
BossInstruction::None BossInstruction::None
} }
BossState::NotSpawned => BossInstruction::None, BossState::NotSpawned => BossInstruction::None,
@ -1549,15 +1634,13 @@ struct FollowingBoss<'a> {
} }
impl<'a> FollowingBoss<'a> { impl<'a> FollowingBoss<'a> {
fn new(object_controller: &'a ObjectControl, position: Vector2D<Number>) -> Self { fn new(object_controller: &'a ObjectController, position: Vector2D<Number>) -> Self {
let mut entity = Entity::new( let mut entity = Entity::new(
object_controller, object_controller,
Rect::new((0_u16, 0_u16).into(), (0_u16, 0_u16).into()), Rect::new((0_u16, 0_u16).into(), (0_u16, 0_u16).into()),
); );
entity.position = position; entity.position = position;
entity
.sprite
.set_sprite_size(agb::display::object::Size::S16x16);
Self { Self {
entity, entity,
following: true, following: true,
@ -1566,11 +1649,11 @@ impl<'a> FollowingBoss<'a> {
gone: false, gone: false,
} }
} }
fn update(&mut self, player: &Player) { fn update(&mut self, controller: &'a ObjectController, player: &Player) {
let difference = player.entity.position - self.entity.position; let difference = player.entity.position - self.entity.position;
self.timer += 1; self.timer += 1;
if self.to_hole { let frame = if self.to_hole {
let target: Vector2D<Number> = (17 * 8, -3 * 8).into(); let target: Vector2D<Number> = (17 * 8, -3 * 8).into();
let difference = target - self.entity.position; let difference = target - self.entity.position;
if difference.manhattan_distance() < 1.into() { if difference.manhattan_distance() < 1.into() {
@ -1579,26 +1662,30 @@ impl<'a> FollowingBoss<'a> {
self.entity.velocity = difference.normalise() * 2; self.entity.velocity = difference.normalise() * 2;
} }
let frame = (self.timer / 8) % 12; self.timer / 8
self.entity.sprite.set_tile_id((125 + frame as u16) * 4);
} else if self.timer < 120 { } else if self.timer < 120 {
let frame = (self.timer / 20) % 12; self.timer / 20
self.entity.sprite.set_tile_id((125 + frame as u16) * 4);
} else if self.following { } else if self.following {
self.entity.velocity = difference / 16; self.entity.velocity = difference / 16;
if difference.manhattan_distance() < 20.into() { if difference.manhattan_distance() < 20.into() {
self.following = false; self.following = false;
} }
let frame = (self.timer / 8) % 12; self.timer / 8
self.entity.sprite.set_tile_id((125 + frame as u16) * 4);
} else { } else {
self.entity.velocity = (0, 0).into(); self.entity.velocity = (0, 0).into();
if difference.manhattan_distance() > 60.into() { if difference.manhattan_distance() > 60.into() {
self.following = true; self.following = true;
} }
let frame = (self.timer / 16) % 12; self.timer / 16
self.entity.sprite.set_tile_id((125 + frame as u16) * 4); };
}
const BOSS: &Tag = TAG_MAP.get("happy boss");
let sprite = BOSS.get_animation_sprite(frame as usize);
let sprite = controller.get_sprite(sprite).unwrap();
self.entity.sprite.set_sprite(sprite);
self.entity.update_position_without_collision(); self.entity.update_position_without_collision();
} }
@ -1631,15 +1718,11 @@ enum BossInstruction {
} }
impl<'a> Boss<'a> { impl<'a> Boss<'a> {
fn new(object_controller: &'a ObjectControl, screen_coords: Vector2D<Number>) -> Self { fn new(object_controller: &'a ObjectController, screen_coords: Vector2D<Number>) -> Self {
let mut entity = Entity::new( let mut entity = Entity::new(
object_controller, object_controller,
Rect::new((0_u16, 0_u16).into(), (28_u16, 28_u16).into()), Rect::new((0_u16, 0_u16).into(), (28_u16, 28_u16).into()),
); );
entity
.sprite
.set_sprite_size(agb::display::object::Size::S32x32);
entity.sprite.set_palette(1);
entity.position = screen_coords + (144, 136).into(); entity.position = screen_coords + (144, 136).into();
Self { Self {
entity, entity,
@ -1654,7 +1737,7 @@ impl<'a> Boss<'a> {
fn update( fn update(
&mut self, &mut self,
enemies: &mut Arena<Enemy<'a>>, enemies: &mut Arena<Enemy<'a>>,
object_controller: &'a ObjectControl, object_controller: &'a ObjectController,
player: &Player, player: &Player,
sfx: &mut sfx::Sfx, sfx: &mut sfx::Sfx,
) -> BossInstruction { ) -> BossInstruction {
@ -1730,8 +1813,14 @@ impl<'a> Boss<'a> {
BossActiveState::WaitUntilKilled => 3.into(), BossActiveState::WaitUntilKilled => 3.into(),
}; };
self.timer += 1; self.timer += 1;
let frame = (self.timer / animation_rate) % 12; let frame = self.timer / animation_rate;
self.entity.sprite.set_tile_id(784 + (frame as u16) * 16);
const BOSS: &Tag = TAG_MAP.get("Boss");
let sprite = BOSS.get_animation_sprite(frame as usize);
let sprite = object_controller.get_sprite(sprite).unwrap();
self.entity.sprite.set_sprite(sprite);
self.entity.update_position_without_collision(); self.entity.update_position_without_collision();
instruction instruction
@ -1752,7 +1841,7 @@ impl<'a> Boss<'a> {
self.entity self.entity
.commit_with_size(offset + shake, (32, 32).into()); .commit_with_size(offset + shake, (32, 32).into());
} }
fn explode(&self, enemies: &mut Arena<Enemy<'a>>, object_controller: &'a ObjectControl) { fn explode(&self, enemies: &mut Arena<Enemy<'a>>, object_controller: &'a ObjectController) {
for _ in 0..(6 - self.health) { for _ in 0..(6 - self.health) {
let x_offset: Number = Number::from_raw(get_random()).rem_euclid(2.into()) - 1; let x_offset: Number = Number::from_raw(get_random()).rem_euclid(2.into()) - 1;
let y_offset: Number = Number::from_raw(get_random()).rem_euclid(2.into()) - 1; let y_offset: Number = Number::from_raw(get_random()).rem_euclid(2.into()) - 1;
@ -1826,7 +1915,7 @@ impl<'a> Game<'a> {
fn advance_frame( fn advance_frame(
&mut self, &mut self,
object_controller: &'a ObjectControl, object_controller: &'a ObjectController,
vram: &mut VRamManager, vram: &mut VRamManager,
sfx: &mut sfx::Sfx, sfx: &mut sfx::Sfx,
) -> GameStatus { ) -> GameStatus {
@ -1923,7 +2012,8 @@ impl<'a> Game<'a> {
self.input.update(); self.input.update();
if let UpdateInstruction::CreateParticle(data, position) = if let UpdateInstruction::CreateParticle(data, position) =
self.player.update(&self.input, &self.level, sfx) self.player
.update(object_controller, &self.input, &self.level, sfx)
{ {
let new_particle = Particle::new(object_controller, data, position); let new_particle = Particle::new(object_controller, data, position);
@ -1937,7 +2027,7 @@ impl<'a> Game<'a> {
continue; continue;
} }
match enemy.update(&self.player, &self.level, sfx) { match enemy.update(object_controller, &self.player, &self.level, sfx) {
UpdateInstruction::Remove => { UpdateInstruction::Remove => {
remove.push(idx); remove.push(idx);
} }
@ -1984,7 +2074,7 @@ impl<'a> Game<'a> {
let mut remove = Vec::with_capacity(10); let mut remove = Vec::with_capacity(10);
for (idx, particle) in self.particles.iter_mut() { for (idx, particle) in self.particles.iter_mut() {
match particle.update(&self.player, &self.level) { match particle.update(object_controller, &self.player, &self.level) {
UpdateInstruction::Remove => remove.push(idx), UpdateInstruction::Remove => remove.push(idx),
UpdateInstruction::HealBossAndRemove => { UpdateInstruction::HealBossAndRemove => {
sfx.sunrise(); sfx.sunrise();
@ -2039,7 +2129,7 @@ impl<'a> Game<'a> {
} }
} }
fn load_enemies(&mut self, object_controller: &'a ObjectControl) { fn load_enemies(&mut self, object_controller: &'a ObjectController) {
if self.slime_load < self.level.slime_spawns.len() { if self.slime_load < self.level.slime_spawns.len() {
for (idx, slime_spawn) in self for (idx, slime_spawn) in self
.level .level
@ -2109,7 +2199,7 @@ impl<'a> Game<'a> {
vram.set_background_palettes(&modified_palettes); vram.set_background_palettes(&modified_palettes);
} }
fn new(object: &'a ObjectControl, level: Level<'a>, start_at_boss: bool) -> Self { fn new(object: &'a ObjectController, level: Level<'a>, start_at_boss: bool) -> Self {
let mut player = Player::new(object); let mut player = Player::new(object);
let mut offset = (8, 8).into(); let mut offset = (8, 8).into();
if start_at_boss { if start_at_boss {
@ -2138,16 +2228,6 @@ impl<'a> Game<'a> {
} }
fn game_with_level(gba: &mut agb::Gba) { fn game_with_level(gba: &mut agb::Gba) {
{
let object = gba.display.object.get();
object.set_sprite_palettes(&[
objects::objects.palettes[0].clone(),
objects::boss.palettes[0].clone(),
]);
object.set_sprite_tilemap(objects::objects.tiles);
object.set_sprite_tilemap_at_idx(8192 - objects::boss.tiles.len(), objects::boss.tiles);
}
let vblank = agb::interrupt::VBlank::get(); let vblank = agb::interrupt::VBlank::get();
vblank.wait_for_vblank(); vblank.wait_for_vblank();
@ -2170,8 +2250,7 @@ fn game_with_level(gba: &mut agb::Gba) {
TileFormat::FourBpp, TileFormat::FourBpp,
)); ));
let mut object = gba.display.object.get(); let object = gba.display.object.get();
object.enable();
let backdrop = InfiniteScrolledMap::new( let backdrop = InfiniteScrolledMap::new(
background.background(Priority::P2), background.background(Priority::P2),

View file

@ -27,6 +27,7 @@ ci: && build-roms
just _all-crates _test-debug just _all-crates _test-debug
just _all-crates _test-release just _all-crates _test-release
just _all-crates _clippy just _all-crates _clippy
just build-book
build-roms: build-roms:
just _build-rom "examples/the-purple-night" "PURPLENIGHT" just _build-rom "examples/the-purple-night" "PURPLENIGHT"

View file

@ -86,9 +86,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.7.3" version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@ -363,9 +363,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]] [[package]]
name = "png" name = "png"
version = "0.17.4" version = "0.17.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02cd7d51cea7e2fa6bbcb8af5fbcad15b871451bfc2d20ed72dff2f4ae072a84" checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crc32fast", "crc32fast",
@ -428,9 +428,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.1.2" version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [ dependencies = [
"winapi-util", "winapi-util",
] ]