initial commit & start building up ecs
This commit is contained in:
commit
681d1b79ac
|
@ -0,0 +1,15 @@
|
|||
[build]
|
||||
target = "wasm32-unknown-unknown"
|
||||
|
||||
[target.wasm32-unknown-unknown]
|
||||
rustflags = [
|
||||
# Import memory from WASM-4
|
||||
"-C", "link-arg=--import-memory",
|
||||
"-C", "link-arg=--initial-memory=65536",
|
||||
"-C", "link-arg=--max-memory=65536",
|
||||
|
||||
# Temporary workaround for #255 issue.
|
||||
# Reserve 8192 bytes of Rust stack space, offset from 6560.
|
||||
# Bump this value, 16-byte aligned, if the framebuffer gets corrupted.
|
||||
"-C", "link-arg=-zstack-size=14752",
|
||||
]
|
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "buddy-alloc"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ff9f338986406db85e2b5deb40a9255b796ca03a194c7457403d215173f3fd5"
|
||||
|
||||
[[package]]
|
||||
name = "cart"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"buddy-alloc",
|
||||
"strum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.24.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "cart"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
buddy-alloc = { version = "0.4.1", optional = true }
|
||||
strum = { version = "0.24", features = ["derive"], default-features = false }
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
|
||||
[features]
|
||||
# use `--no-default-features` or comment out next line to disable allocator
|
||||
default = ["buddy-alloc"]
|
|
@ -0,0 +1,26 @@
|
|||
# thang
|
||||
|
||||
A game written in Rust for the [WASM-4](https://wasm4.org) fantasy console.
|
||||
|
||||
## Building
|
||||
|
||||
Build the cart by running:
|
||||
|
||||
```shell
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
Then run it with:
|
||||
|
||||
```shell
|
||||
w4 run target/wasm32-unknown-unknown/release/cart.wasm
|
||||
```
|
||||
|
||||
For more info about setting up WASM-4, see the [quickstart guide](https://wasm4.org/docs/getting-started/setup?code-lang=rust#quickstart).
|
||||
|
||||
## Links
|
||||
|
||||
- [Documentation](https://wasm4.org/docs): Learn more about WASM-4.
|
||||
- [Snake Tutorial](https://wasm4.org/docs/tutorials/snake/goal): Learn how to build a complete game
|
||||
with a step-by-step tutorial.
|
||||
- [GitHub](https://github.com/aduros/wasm4): Submit an issue or PR. Contributions are welcome!
|
|
@ -0,0 +1,123 @@
|
|||
use strum::{EnumDiscriminants, EnumCount, EnumIter};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::wasm4::trace;
|
||||
|
||||
pub struct Scene {
|
||||
entities: Vec<Option<Entity>>,
|
||||
components: Vec<Option<ComponentRecord>>,
|
||||
|
||||
unused_entity_ids: Vec<EntityId>,
|
||||
unused_component_ids: Vec<ComponentId>
|
||||
}
|
||||
|
||||
struct ComponentRecord {
|
||||
component: Component,
|
||||
owning_entity: EntityId,
|
||||
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
pub fn new() -> Scene {
|
||||
Scene {
|
||||
entities: Vec::with_capacity(32),
|
||||
components: Vec::with_capacity(128),
|
||||
unused_entity_ids: Vec::new(),
|
||||
unused_component_ids: Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_entity(&mut self){
|
||||
|
||||
}
|
||||
|
||||
pub fn query_entities<const N: usize>(&self, query: &[ComponentDiscriminants; N]) -> Vec<(EntityId, [ComponentId; N])> {
|
||||
let mut result = Vec::new();
|
||||
for entity_index in 0..self.entities.len() {
|
||||
if let Some(entity) = &self.entities[entity_index] {
|
||||
if let Some(component_ids) = entity.component_ids(query) {
|
||||
result.push((EntityId(entity_index), component_ids));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn borrow_component_ids<const N: usize>(&mut self, ids: &[ComponentId; N]) -> [Option<&Component>; N] {
|
||||
let mut result = [None; N];
|
||||
for id_index in 0..ids.len() {
|
||||
let ComponentId(id) = ids[id_index];
|
||||
if id >= self.components.len() {
|
||||
trace(format!("tried to borrow a component with id {} when there are {} components", id, self.components.len()));
|
||||
}
|
||||
|
||||
match &self.components[id] {
|
||||
Some(ComponentRecord { component, owning_entity }) => {
|
||||
result[id_index] = Some(component);
|
||||
},
|
||||
None => trace(format!("tried to borrow a component with id {} but it has been removed.", id))
|
||||
}
|
||||
}
|
||||
result.into()
|
||||
}
|
||||
|
||||
pub unsafe fn mut_borrow_component_unsafely(&self, id: &ComponentId) -> Option<&Component> {
|
||||
let ComponentId(id) = *id;
|
||||
if id >= self.components.len(){
|
||||
trace(format!("tried to mutably borrow a component with id {} when there are {} components", id, self.components.len()));
|
||||
return None;
|
||||
}
|
||||
match &mut self.components[id] {
|
||||
Some(ComponentRecord { component, owning_entity }) => {
|
||||
let component_ptr = component as *const Component;
|
||||
let component_mut_ptr = component_ptr as *mut T;
|
||||
return Some(&mut *component_mut_ptr);
|
||||
},
|
||||
None => trace(format!("tried to mutably borrow a component with id {} but it has been removed.", id))
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct EntityId(usize);
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ComponentId(usize);
|
||||
|
||||
pub struct Entity {
|
||||
components: [Option<usize>; Component::COUNT]
|
||||
}
|
||||
|
||||
impl Entity {
|
||||
pub fn new() -> Entity {
|
||||
Entity {
|
||||
components: [None; Component::COUNT]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn component_ids<const N: usize>(&self, query: &[ComponentDiscriminants; N]) -> Option<[ComponentId; N]>{
|
||||
let mut result: [ComponentId; N] = [ComponentId(0); N];
|
||||
for query_index in 0..query.len() {
|
||||
let discriminant = query[query_index];
|
||||
if let Some(id) = self.components[discriminant as usize] {
|
||||
result[query_index] = ComponentId(id);
|
||||
}
|
||||
else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(usize)]
|
||||
#[derive(EnumDiscriminants, EnumCount, EnumIter)]
|
||||
#[strum_discriminants(repr(usize))]
|
||||
pub enum Component {
|
||||
Name(&'static str),
|
||||
Position { x: u8, y: u8 }
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
#![cfg_attr(not(test), no_std)]
|
||||
#[cfg(feature = "buddy-alloc")]
|
||||
mod my_alloc;
|
||||
mod wasm4;
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
|
||||
use wasm4::*;
|
||||
|
||||
mod ecs;
|
||||
|
||||
#[rustfmt::skip]
|
||||
const SMILEY: [u8; 8] = [
|
||||
0b11000011,
|
||||
0b10000001,
|
||||
0b00100100,
|
||||
0b00100100,
|
||||
0b00000000,
|
||||
0b00100100,
|
||||
0b10011001,
|
||||
0b11000011,
|
||||
];
|
||||
|
||||
|
||||
#[cfg_attr(not(test), panic_handler)]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
trace(format!("{}", info));
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn update() {
|
||||
unsafe { *DRAW_COLORS = 2 }
|
||||
text("Hello from Rust!", 10, 10);
|
||||
|
||||
let gamepad = unsafe { *GAMEPAD1 };
|
||||
if gamepad & BUTTON_1 != 0 {
|
||||
unsafe { *DRAW_COLORS = 4 }
|
||||
}
|
||||
|
||||
blit(&SMILEY, 76, 76, 8, 8, BLIT_1BPP);
|
||||
text("Press X to blink", 16, 90);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
use buddy_alloc::{BuddyAllocParam, FastAllocParam, NonThreadsafeAlloc};
|
||||
|
||||
// These values can be tuned
|
||||
const FAST_HEAP_SIZE: usize = 4 * 1024; // 4 KB
|
||||
const HEAP_SIZE: usize = 16 * 1024; // 16 KB
|
||||
const LEAF_SIZE: usize = 16;
|
||||
|
||||
static mut FAST_HEAP: [u8; FAST_HEAP_SIZE] = [0u8; FAST_HEAP_SIZE];
|
||||
static mut HEAP: [u8; HEAP_SIZE] = [0u8; HEAP_SIZE];
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOC: NonThreadsafeAlloc = unsafe {
|
||||
let fast_param = FastAllocParam::new(FAST_HEAP.as_ptr(), FAST_HEAP_SIZE);
|
||||
let buddy_param = BuddyAllocParam::new(HEAP.as_ptr(), HEAP_SIZE, LEAF_SIZE);
|
||||
NonThreadsafeAlloc::new(fast_param, buddy_param)
|
||||
};
|
|
@ -0,0 +1,225 @@
|
|||
//
|
||||
// WASM-4: https://wasm4.org/docs
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Platform Constants │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
pub const SCREEN_SIZE: u32 = 160;
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Memory Addresses │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
pub const PALETTE: *mut [u32; 4] = 0x04 as *mut [u32; 4];
|
||||
pub const DRAW_COLORS: *mut u16 = 0x14 as *mut u16;
|
||||
pub const GAMEPAD1: *const u8 = 0x16 as *const u8;
|
||||
pub const GAMEPAD2: *const u8 = 0x17 as *const u8;
|
||||
pub const GAMEPAD3: *const u8 = 0x18 as *const u8;
|
||||
pub const GAMEPAD4: *const u8 = 0x19 as *const u8;
|
||||
pub const MOUSE_X: *const i16 = 0x1a as *const i16;
|
||||
pub const MOUSE_Y: *const i16 = 0x1c as *const i16;
|
||||
pub const MOUSE_BUTTONS: *const u8 = 0x1e as *const u8;
|
||||
pub const SYSTEM_FLAGS: *mut u8 = 0x1f as *mut u8;
|
||||
pub const NETPLAY: *const u8 = 0x20 as *const u8;
|
||||
pub const FRAMEBUFFER: *mut [u8; 6400] = 0xa0 as *mut [u8; 6400];
|
||||
|
||||
pub const BUTTON_1: u8 = 1;
|
||||
pub const BUTTON_2: u8 = 2;
|
||||
pub const BUTTON_LEFT: u8 = 16;
|
||||
pub const BUTTON_RIGHT: u8 = 32;
|
||||
pub const BUTTON_UP: u8 = 64;
|
||||
pub const BUTTON_DOWN: u8 = 128;
|
||||
|
||||
pub const MOUSE_LEFT: u8 = 1;
|
||||
pub const MOUSE_RIGHT: u8 = 2;
|
||||
pub const MOUSE_MIDDLE: u8 = 4;
|
||||
|
||||
pub const SYSTEM_PRESERVE_FRAMEBUFFER: u8 = 1;
|
||||
pub const SYSTEM_HIDE_GAMEPAD_OVERLAY: u8 = 2;
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Drawing Functions │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
/// Copies pixels to the framebuffer.
|
||||
pub fn blit(sprite: &[u8], x: i32, y: i32, width: u32, height: u32, flags: u32) {
|
||||
unsafe { extern_blit(sprite.as_ptr(), x, y, width, height, flags) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "blit"]
|
||||
fn extern_blit(sprite: *const u8, x: i32, y: i32, width: u32, height: u32, flags: u32);
|
||||
}
|
||||
|
||||
/// Copies a subregion within a larger sprite atlas to the framebuffer.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn blit_sub(
|
||||
sprite: &[u8],
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
src_x: u32,
|
||||
src_y: u32,
|
||||
stride: u32,
|
||||
flags: u32,
|
||||
) {
|
||||
unsafe {
|
||||
extern_blit_sub(
|
||||
sprite.as_ptr(),
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
src_x,
|
||||
src_y,
|
||||
stride,
|
||||
flags,
|
||||
)
|
||||
}
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "blitSub"]
|
||||
fn extern_blit_sub(
|
||||
sprite: *const u8,
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
src_x: u32,
|
||||
src_y: u32,
|
||||
stride: u32,
|
||||
flags: u32,
|
||||
);
|
||||
}
|
||||
|
||||
pub const BLIT_2BPP: u32 = 1;
|
||||
pub const BLIT_1BPP: u32 = 0;
|
||||
pub const BLIT_FLIP_X: u32 = 2;
|
||||
pub const BLIT_FLIP_Y: u32 = 4;
|
||||
pub const BLIT_ROTATE: u32 = 8;
|
||||
|
||||
/// Draws a line between two points.
|
||||
pub fn line(x1: i32, y1: i32, x2: i32, y2: i32) {
|
||||
unsafe { extern_line(x1, y1, x2, y2) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "line"]
|
||||
fn extern_line(x1: i32, y1: i32, x2: i32, y2: i32);
|
||||
}
|
||||
|
||||
/// Draws an oval (or circle).
|
||||
pub fn oval(x: i32, y: i32, width: u32, height: u32) {
|
||||
unsafe { extern_oval(x, y, width, height) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "oval"]
|
||||
fn extern_oval(x: i32, y: i32, width: u32, height: u32);
|
||||
}
|
||||
|
||||
/// Draws a rectangle.
|
||||
pub fn rect(x: i32, y: i32, width: u32, height: u32) {
|
||||
unsafe { extern_rect(x, y, width, height) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "rect"]
|
||||
fn extern_rect(x: i32, y: i32, width: u32, height: u32);
|
||||
}
|
||||
|
||||
/// Draws text using the built-in system font.
|
||||
pub fn text<T: AsRef<[u8]>>(text: T, x: i32, y: i32) {
|
||||
let text_ref = text.as_ref();
|
||||
unsafe { extern_text(text_ref.as_ptr(), text_ref.len(), x, y) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "textUtf8"]
|
||||
fn extern_text(text: *const u8, length: usize, x: i32, y: i32);
|
||||
}
|
||||
|
||||
/// Draws a vertical line
|
||||
pub fn vline(x: i32, y: i32, len: u32) {
|
||||
unsafe {
|
||||
extern_vline(x, y, len);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "vline"]
|
||||
fn extern_vline(x: i32, y: i32, len: u32);
|
||||
}
|
||||
|
||||
/// Draws a horizontal line
|
||||
pub fn hline(x: i32, y: i32, len: u32) {
|
||||
unsafe {
|
||||
extern_hline(x, y, len);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "hline"]
|
||||
fn extern_hline(x: i32, y: i32, len: u32);
|
||||
}
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Sound Functions │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
/// Plays a sound tone.
|
||||
pub fn tone(frequency: u32, duration: u32, volume: u32, flags: u32) {
|
||||
unsafe { extern_tone(frequency, duration, volume, flags) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "tone"]
|
||||
fn extern_tone(frequency: u32, duration: u32, volume: u32, flags: u32);
|
||||
}
|
||||
|
||||
pub const TONE_PULSE1: u32 = 0;
|
||||
pub const TONE_PULSE2: u32 = 1;
|
||||
pub const TONE_TRIANGLE: u32 = 2;
|
||||
pub const TONE_NOISE: u32 = 3;
|
||||
pub const TONE_MODE1: u32 = 0;
|
||||
pub const TONE_MODE2: u32 = 4;
|
||||
pub const TONE_MODE3: u32 = 8;
|
||||
pub const TONE_MODE4: u32 = 12;
|
||||
pub const TONE_PAN_LEFT: u32 = 16;
|
||||
pub const TONE_PAN_RIGHT: u32 = 32;
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Storage Functions │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
extern "C" {
|
||||
/// Reads up to `size` bytes from persistent storage into the pointer `dest`.
|
||||
pub fn diskr(dest: *mut u8, size: u32) -> u32;
|
||||
|
||||
/// Writes up to `size` bytes from the pointer `src` into persistent storage.
|
||||
pub fn diskw(src: *const u8, size: u32) -> u32;
|
||||
}
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Other Functions │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
/// Prints a message to the debug console.
|
||||
pub fn trace<T: AsRef<str>>(text: T) {
|
||||
let text_ref = text.as_ref();
|
||||
unsafe { extern_trace(text_ref.as_ptr(), text_ref.len()) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "traceUtf8"]
|
||||
fn extern_trace(trace: *const u8, length: usize);
|
||||
}
|
Loading…
Reference in New Issue