Began rewriting the game as a wasm4 cartridge
This commit is contained in:
parent
a9378b5785
commit
8f76ba7826
10 changed files with 224 additions and 335 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
|||
[submodule "lib/spoon"]
|
||||
path = lib/spoon
|
||||
url = https://git.sr.ht/~leon_plickat/zig-spoon
|
61
build.zig
61
build.zig
|
@ -1,57 +1,18 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.build.Builder) void {
|
||||
// Standard target options allows the person running `zig build` to choose
|
||||
// what target to build for. Here we do not override the defaults, which
|
||||
// means any target is allowed, and the default is native. Other options
|
||||
// for restricting supported target set are available.
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
// Standard release options allow the person running `zig build` to select
|
||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||
pub fn build(b: *std.build.Builder) !void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
const lib = b.addSharedLibrary("cart", "src/main.zig", .unversioned);
|
||||
|
||||
const exe = b.addExecutable("grenade-brothers", "src/main.zig");
|
||||
exe.addPackagePath("spoon", "lib/spoon/import.zig");
|
||||
exe.setTarget(target);
|
||||
exe.setBuildMode(mode);
|
||||
exe.install();
|
||||
lib.setBuildMode(mode);
|
||||
lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
|
||||
lib.import_memory = true;
|
||||
lib.initial_memory = 65536;
|
||||
lib.max_memory = 65536;
|
||||
lib.stack_size = 14752;
|
||||
|
||||
const run_cmd = exe.run();
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
// Export WASM-4 symbols
|
||||
lib.export_symbol_names = &[_][]const u8{ "start", "update" };
|
||||
|
||||
const coverage = b.option(bool, "test-coverage", "Generate test coverage") orelse false;
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
const exe_tests = b.addTest("src/main.zig");
|
||||
exe_tests.addPackagePath("spoon", "lib/spoon/import.zig");
|
||||
exe_tests.setTarget(target);
|
||||
exe_tests.setBuildMode(mode);
|
||||
|
||||
// Code coverage with kcov, we need an allocator for the setup
|
||||
var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = general_purpose_allocator.deinit();
|
||||
const gpa = general_purpose_allocator.allocator();
|
||||
// We want to exclude the $HOME/.zig path
|
||||
const home = std.process.getEnvVarOwned(gpa, "HOME") catch "";
|
||||
defer gpa.free(home);
|
||||
const exclude = std.fmt.allocPrint(gpa, "--exclude-path={s}/.zig/,/usr", .{home}) catch "";
|
||||
defer gpa.free(exclude);
|
||||
if (coverage) {
|
||||
exe_tests.setExecCmd(&[_]?[]const u8{
|
||||
"kcov",
|
||||
exclude,
|
||||
//"--path-strip-level=3", // any kcov flags can be specified here
|
||||
"kcov-output", // output dir for kcov
|
||||
null, // to get zig to use the --test-cmd-bin flag
|
||||
});
|
||||
}
|
||||
|
||||
const test_step = b.step("test", "Run unit tests");
|
||||
test_step.dependOn(&exe_tests.step);
|
||||
lib.install();
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit c2be37a8087ccb6b01981cb617fef7755dc72b68
|
28
src/ball.zig
Normal file
28
src/ball.zig
Normal file
|
@ -0,0 +1,28 @@
|
|||
const std = @import("std");
|
||||
const utils = @import("utils.zig");
|
||||
const w4 = @import("wasm4.zig");
|
||||
|
||||
pub const Ball = struct {
|
||||
x: f64,
|
||||
y: f64,
|
||||
dx: f64,
|
||||
dy: f64,
|
||||
pub fn draw(self: Ball) void {
|
||||
var y = @floatToInt(u8, std.math.round(self.y));
|
||||
var x = @floatToInt(u8, std.math.round(self.x));
|
||||
w4.DRAW_COLORS.* = 0x4321;
|
||||
w4.blit(&ball, x, y, ball_width, ball_height, ball_flags);
|
||||
}
|
||||
pub fn resetRound(self: *Ball, side: utils.side) void {
|
||||
self.x = @intToFloat(f64, utils.startingX[@enumToInt(side)] + 4);
|
||||
self.y = 160 - 32 - 8;
|
||||
self.dx = 0;
|
||||
self.dy = 0;
|
||||
}
|
||||
};
|
||||
|
||||
//----- Sprite ----------------------------------------------------------------
|
||||
const ball_width = 8;
|
||||
const ball_height = 8;
|
||||
const ball_flags = 1; // BLIT_2BPP
|
||||
const ball = [16]u8{ 0x1a, 0xa4, 0x6f, 0xf9, 0xbf, 0xae, 0xbf, 0xae, 0xbf, 0xfe, 0xbf, 0xfe, 0x6f, 0xf9, 0x1a, 0xa4 };
|
131
src/brothers.zig
131
src/brothers.zig
|
@ -1,128 +1,29 @@
|
|||
const std = @import("std");
|
||||
const spoon = @import("spoon");
|
||||
|
||||
const ball = @import("ball.zig");
|
||||
|
||||
pub const Side = enum(u1) {
|
||||
left,
|
||||
right,
|
||||
};
|
||||
|
||||
const startingX = [2]f64{ 15, 60 };
|
||||
const colors = [2]spoon.Attribute.Colour{ .blue, .red };
|
||||
const leftLimit = [2]f64{ 1, 41 };
|
||||
const rightLimit = [2]f64{ 34, 74 }; // (38, 79) minus a brother's width
|
||||
const utils = @import("utils.zig");
|
||||
const w4 = @import("wasm4.zig");
|
||||
|
||||
pub const Brother = struct {
|
||||
side: Side,
|
||||
side: utils.side,
|
||||
score: u8,
|
||||
x: f64,
|
||||
x: u8,
|
||||
y: f64,
|
||||
dx: f64,
|
||||
dy: f64,
|
||||
moveDuration: u64,
|
||||
pub fn draw(self: Brother, rc: *spoon.Term.RenderContext) !void {
|
||||
try rc.setAttribute(.{ .fg = colors[@enumToInt(self.side)] });
|
||||
var iter = std.mem.split(u8, brother, "\n");
|
||||
var y = @floatToInt(usize, std.math.round(self.y));
|
||||
var x = @floatToInt(usize, std.math.round(self.x));
|
||||
while (iter.next()) |line| : (y += 1) {
|
||||
try rc.moveCursorTo(y, x);
|
||||
_ = try rc.buffer.writer().write(line);
|
||||
}
|
||||
pub fn draw(self: Brother) void {
|
||||
var y = @floatToInt(u8, std.math.round(self.y));
|
||||
w4.DRAW_COLORS.* = 0x30;
|
||||
w4.blit(&brother, self.x, y - brother_height, brother_width, brother_height, brother_flags);
|
||||
}
|
||||
pub fn moveJump(self: *Brother) void {
|
||||
if (self.dy == 0) { // no double jumps! TODO allow kicks off the wall
|
||||
self.dy -= 4 / (1000 / 60.0);
|
||||
}
|
||||
}
|
||||
pub fn moveLeft(self: *Brother) void {
|
||||
self.dx -= 5 / (1000 / 60.0);
|
||||
self.moveDuration = 24;
|
||||
}
|
||||
pub fn moveRight(self: *Brother) void {
|
||||
self.dx += 5 / (1000 / 60.0);
|
||||
self.moveDuration = 24;
|
||||
}
|
||||
pub fn reset(self: *Brother, side: Side) void {
|
||||
pub fn reset(self: *Brother, side: utils.side) void {
|
||||
self.side = side;
|
||||
self.score = 0;
|
||||
}
|
||||
pub fn resetRound(self: *Brother) void {
|
||||
self.x = startingX[@enumToInt(self.side)];
|
||||
self.y = 17;
|
||||
self.dx = 0;
|
||||
self.dy = 0;
|
||||
self.moveDuration = 0;
|
||||
}
|
||||
pub fn step(self: *Brother, b: *ball.Ball) void {
|
||||
// Horizontal movement
|
||||
const x = self.x + self.dx;
|
||||
const ll = leftLimit[@enumToInt(self.side)];
|
||||
const rl = rightLimit[@enumToInt(self.side)];
|
||||
if (x < ll) {
|
||||
self.x = ll;
|
||||
self.dx = 0;
|
||||
self.moveDuration = 0;
|
||||
} else if (x > rl) {
|
||||
self.x = rl;
|
||||
self.dx = 0;
|
||||
self.moveDuration = 0;
|
||||
} else {
|
||||
self.x = x;
|
||||
if (self.moveDuration > 0) {
|
||||
self.moveDuration -= 1;
|
||||
if (self.moveDuration == 0) {
|
||||
self.dx = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Vertical movement
|
||||
const y = self.y + self.dy;
|
||||
if (y < 12) { // jumping
|
||||
self.y = 12;
|
||||
self.dy = -self.dy;
|
||||
} else if (y > 17) { // falling
|
||||
self.y = 17;
|
||||
self.dy = 0;
|
||||
} else {
|
||||
self.y = y;
|
||||
}
|
||||
// Check for ball collisions
|
||||
if (b.y >= y and b.y <= y + 2 and b.x >= x and b.x < x + 5) {
|
||||
if (b.dy > 0) {
|
||||
b.dy = -b.dy / 1.5;
|
||||
}
|
||||
b.dx = b.dx / 2.0;
|
||||
var strength: f64 = 1;
|
||||
if (b.dx > 0 and self.dx < 0)
|
||||
strength *= 2; // moving in opposite directions
|
||||
if (y < 12) { // jumping
|
||||
strength *= 2;
|
||||
}
|
||||
if (b.x < x + 1) {
|
||||
b.dx -= strength * 4 / (1000 / 60.0);
|
||||
} else if (b.x < x + 2) {
|
||||
b.dx -= strength * 2 / (1000 / 60.0);
|
||||
} else if (b.x < x + 3) {
|
||||
var modifier: f64 = 1;
|
||||
if (self.side == .left) modifier = -1;
|
||||
b.dx += modifier * strength * 2 / (1000 / 60.0);
|
||||
} else if (b.x < x + 4) {
|
||||
b.dx += strength * 2 / (1000 / 60.0);
|
||||
} else {
|
||||
b.dx += strength * 4 / (1000 / 60.0);
|
||||
}
|
||||
b.dy = b.dy * strength - 0.04;
|
||||
}
|
||||
self.x = utils.startingX[@enumToInt(self.side)];
|
||||
self.y = 160;
|
||||
}
|
||||
};
|
||||
|
||||
const brother =
|
||||
\\█ █
|
||||
\\█ █ █
|
||||
\\█████
|
||||
\\█████
|
||||
\\█ █
|
||||
\\█ █
|
||||
;
|
||||
//----- Sprite ----------------------------------------------------------------
|
||||
const brother_width = 16;
|
||||
const brother_height = 32;
|
||||
const brother_flags = 0; // BLIT_1BPP
|
||||
const brother = [64]u8{ 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xfb, 0xdf, 0xfb, 0xdf, 0xfb, 0xdf, 0xfb, 0xdf, 0xf9, 0x9f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f };
|
||||
|
|
47
src/game.zig
47
src/game.zig
|
@ -1,50 +1,27 @@
|
|||
const std = @import("std");
|
||||
const spoon = @import("spoon");
|
||||
const utils = @import("utils.zig");
|
||||
const w4 = @import("wasm4.zig");
|
||||
|
||||
const ball = @import("ball.zig");
|
||||
const brothers = @import("brothers.zig");
|
||||
const playfield = @import("playfield.zig");
|
||||
|
||||
pub const Game = struct {
|
||||
ball: ball.Ball = undefined,
|
||||
brothers: [2]brothers.Brother = undefined,
|
||||
side: brothers.Side = undefined,
|
||||
pub fn draw(self: Game, rc: *spoon.Term.RenderContext) !void {
|
||||
try playfield.draw(rc);
|
||||
try self.brothers[0].draw(rc);
|
||||
try self.brothers[1].draw(rc);
|
||||
try self.ball.draw(rc);
|
||||
var score: [1]u8 = undefined;
|
||||
try rc.moveCursorTo(1, 3);
|
||||
score[0] = '0' + self.brothers[0].score;
|
||||
_ = try rc.buffer.writer().write(score[0..]);
|
||||
try rc.moveCursorTo(1, 76);
|
||||
score[0] = '0' + self.brothers[1].score;
|
||||
_ = try rc.buffer.writer().write(score[0..]);
|
||||
playerSide: utils.side = undefined,
|
||||
pub fn draw(self: *Game) void {
|
||||
self.ball.draw();
|
||||
self.brothers[0].draw();
|
||||
self.brothers[1].draw();
|
||||
// draw the net
|
||||
w4.DRAW_COLORS.* = 0x42;
|
||||
w4.rect(78, 100, 4, 61);
|
||||
}
|
||||
pub fn moveJump(self: *Game, side: brothers.Side) void {
|
||||
self.brothers[@enumToInt(side)].moveJump();
|
||||
}
|
||||
pub fn moveLeft(self: *Game, side: brothers.Side) void {
|
||||
self.brothers[@enumToInt(side)].moveLeft();
|
||||
}
|
||||
pub fn moveRight(self: *Game, side: brothers.Side) void {
|
||||
self.brothers[@enumToInt(side)].moveRight();
|
||||
}
|
||||
pub fn reset(self: *Game, side: brothers.Side) void {
|
||||
self.side = side;
|
||||
pub fn reset(self: *Game) void {
|
||||
self.brothers[0].reset(.left);
|
||||
self.brothers[1].reset(.right);
|
||||
self.resetRound();
|
||||
}
|
||||
pub fn resetRound(self: *Game) void {
|
||||
self.ball.reset(.left);
|
||||
self.ball.resetRound(.left);
|
||||
self.brothers[0].resetRound();
|
||||
self.brothers[1].resetRound();
|
||||
}
|
||||
pub fn step(self: *Game) void {
|
||||
self.ball.step();
|
||||
self.brothers[0].step(&self.ball);
|
||||
self.brothers[1].step(&self.ball);
|
||||
}
|
||||
};
|
||||
|
|
102
src/main.zig
102
src/main.zig
|
@ -1,99 +1,15 @@
|
|||
const std = @import("std");
|
||||
const spoon = @import("spoon");
|
||||
|
||||
const game = @import("game.zig");
|
||||
const std = @import("std");
|
||||
const utils = @import("utils.zig");
|
||||
const w4 = @import("wasm4.zig");
|
||||
|
||||
var term: spoon.Term = undefined;
|
||||
var done: bool = false;
|
||||
//----- Globals ---------------------------------------------------------------
|
||||
var Game: game.Game = undefined;
|
||||
|
||||
//----- Game State -----------------------------------------------------------
|
||||
var gs: game.Game = undefined;
|
||||
|
||||
//----- Main -----------------------------------------------------------------
|
||||
pub fn main() !void {
|
||||
try term.init();
|
||||
defer term.deinit();
|
||||
|
||||
std.os.sigaction(std.os.SIG.WINCH, &std.os.Sigaction{
|
||||
.handler = .{ .handler = handleSigWinch },
|
||||
.mask = std.os.empty_sigset,
|
||||
.flags = 0,
|
||||
}, null);
|
||||
|
||||
var fds: [1]std.os.pollfd = undefined;
|
||||
fds[0] = .{
|
||||
.fd = term.tty.handle,
|
||||
.events = std.os.POLL.IN,
|
||||
.revents = undefined,
|
||||
};
|
||||
|
||||
try term.uncook(.{});
|
||||
defer term.cook() catch {};
|
||||
|
||||
try term.fetchSize();
|
||||
try term.setWindowTitle("Grenade Brothers", .{});
|
||||
|
||||
gs.reset(.left);
|
||||
try renderAll();
|
||||
|
||||
var buf: [16]u8 = undefined;
|
||||
while (!done) {
|
||||
// TODO We need to measure how long it took before a key was hit so that we can adjust the timeout on the next loop
|
||||
// otherwise we will get inconsistent ticks for movement steps
|
||||
const timeout = try std.os.poll(&fds, @floatToInt(u64, 1000 / 60.0));
|
||||
|
||||
if (timeout > 0) { // if timeout if not 0 then some fds we are polling have events for us
|
||||
const read = try term.readInput(&buf);
|
||||
var it = spoon.inputParser(buf[0..read]);
|
||||
while (it.next()) |in| {
|
||||
if (in.eqlDescription("escape") or in.eqlDescription("q")) {
|
||||
done = true;
|
||||
break;
|
||||
} else if (in.eqlDescription("arrow-left")) {
|
||||
gs.moveLeft(.right);
|
||||
} else if (in.eqlDescription("arrow-right")) {
|
||||
gs.moveRight(.right);
|
||||
} else if (in.eqlDescription("arrow-up")) {
|
||||
gs.moveJump(.right);
|
||||
} else if (in.eqlDescription("a")) {
|
||||
gs.moveLeft(.left);
|
||||
} else if (in.eqlDescription("d")) {
|
||||
gs.moveRight(.left);
|
||||
} else if (in.eqlDescription("space")) {
|
||||
gs.moveJump(.left);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gs.step();
|
||||
}
|
||||
try renderAll();
|
||||
}
|
||||
export fn start() void {
|
||||
Game.reset();
|
||||
}
|
||||
|
||||
fn renderAll() !void {
|
||||
var rc = try term.getRenderContext();
|
||||
defer rc.done() catch {};
|
||||
|
||||
try rc.clear();
|
||||
|
||||
if (term.width < 80 or term.width < 24) {
|
||||
try rc.setAttribute(.{ .fg = .red, .bold = true });
|
||||
try rc.writeAllWrapping("Terminal too small!");
|
||||
return;
|
||||
}
|
||||
|
||||
try gs.draw(&rc);
|
||||
}
|
||||
|
||||
fn handleSigWinch(_: c_int) callconv(.C) void {
|
||||
term.fetchSize() catch {};
|
||||
renderAll() catch {};
|
||||
}
|
||||
|
||||
/// Custom panic handler, so that we can try to cook the terminal on a crash,
|
||||
/// as otherwise all messages will be mangled.
|
||||
pub fn panic(msg: []const u8, trace: ?*std.builtin.StackTrace) noreturn {
|
||||
@setCold(true);
|
||||
term.cook() catch {};
|
||||
std.builtin.default_panic(msg, trace);
|
||||
export fn update() void {
|
||||
Game.draw();
|
||||
}
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
const std = @import("std");
|
||||
const spoon = @import("spoon");
|
||||
|
||||
pub fn draw(rc: *spoon.Term.RenderContext) !void {
|
||||
var iter = std.mem.split(u8, field, "\n");
|
||||
var y: usize = 0;
|
||||
while (iter.next()) |line| : (y += 1) {
|
||||
try rc.moveCursorTo(y, 0);
|
||||
_ = try rc.buffer.writer().write(line);
|
||||
}
|
||||
}
|
||||
|
||||
const field =
|
||||
\\████████████████████████████████████████████████████████████████████████████████
|
||||
\\█ ██ █
|
||||
\\████████████████████████████████████████████████████████████████████████████████
|
||||
\\█ █
|
||||
\\█ █
|
||||
\\█ █
|
||||
\\█ █
|
||||
\\█ █
|
||||
\\█ █
|
||||
\\█ █
|
||||
\\█ █
|
||||
\\█ █
|
||||
\\█ █
|
||||
\\█ ██ █
|
||||
\\█ ██ █
|
||||
\\█ ██ █
|
||||
\\█ ██ █
|
||||
\\█ ██ █
|
||||
\\█ ██ █
|
||||
\\█ ██ █
|
||||
\\█ ██ █
|
||||
\\█ ██ █
|
||||
\\█ ██ █
|
||||
\\████████████████████████████████████████████████████████████████████████████████
|
||||
;
|
12
src/utils.zig
Normal file
12
src/utils.zig
Normal file
|
@ -0,0 +1,12 @@
|
|||
//----- Physics ---------------------------------------------------------------
|
||||
pub const gravity: f64 = 9.807; // m/s²
|
||||
pub const scale: f64 = 1.0 / 30; // 30 pixels == 1m
|
||||
|
||||
//----- Playground ------------------------------------------------------------
|
||||
pub const side = enum(u1) {
|
||||
left,
|
||||
right,
|
||||
};
|
||||
pub const startingX = [2]u8{ 23, 160 - 23 - 16 };
|
||||
pub const leftLimit = [2]u8{ 0, 81 };
|
||||
pub const rightLimit = [2]u8{ 77, 159 };
|
136
src/wasm4.zig
Normal file
136
src/wasm4.zig
Normal file
|
@ -0,0 +1,136 @@
|
|||
//
|
||||
// WASM-4: https://wasm4.org/docs
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Platform Constants │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
pub const SCREEN_SIZE: u32 = 160;
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Memory Addresses │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
pub const PALETTE: *[4]u32 = @intToPtr(*[4]u32, 0x04);
|
||||
pub const DRAW_COLORS: *u16 = @intToPtr(*u16, 0x14);
|
||||
pub const GAMEPAD1: *const u8 = @intToPtr(*const u8, 0x16);
|
||||
pub const GAMEPAD2: *const u8 = @intToPtr(*const u8, 0x17);
|
||||
pub const GAMEPAD3: *const u8 = @intToPtr(*const u8, 0x18);
|
||||
pub const GAMEPAD4: *const u8 = @intToPtr(*const u8, 0x19);
|
||||
pub const MOUSE_X: *const i16 = @intToPtr(*const i16, 0x1a);
|
||||
pub const MOUSE_Y: *const i16 = @intToPtr(*const i16, 0x1c);
|
||||
pub const MOUSE_BUTTONS: *const u8 = @intToPtr(*const u8, 0x1e);
|
||||
pub const SYSTEM_FLAGS: *u8 = @intToPtr(*u8, 0x1f);
|
||||
pub const NETPLAY: *const u8 = @intToPtr(*const u8, 0x20);
|
||||
pub const FRAMEBUFFER: *[6400]u8 = @intToPtr(*[6400]u8, 0xA0);
|
||||
|
||||
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 extern fn blit(sprite: [*]const u8, x: i32, y: i32, width: u32, height: u32, flags: u32) void;
|
||||
|
||||
/// Copies a subregion within a larger sprite atlas to the framebuffer.
|
||||
pub extern fn blitSub(sprite: [*]const u8, x: i32, y: i32, width: u32, height: u32, src_x: u32, src_y: u32, stride: u32, flags: u32) void;
|
||||
|
||||
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 extern fn line(x1: i32, y1: i32, x2: i32, y2: i32) void;
|
||||
|
||||
/// Draws an oval (or circle).
|
||||
pub extern fn oval(x: i32, y: i32, width: u32, height: u32) void;
|
||||
|
||||
/// Draws a rectangle.
|
||||
pub extern fn rect(x: i32, y: i32, width: u32, height: u32) void;
|
||||
|
||||
/// Draws text using the built-in system font.
|
||||
pub fn text(str: []const u8, x: i32, y: i32) void {
|
||||
textUtf8(str.ptr, str.len, x, y);
|
||||
}
|
||||
extern fn textUtf8(strPtr: [*]const u8, strLen: usize, x: i32, y: i32) void;
|
||||
|
||||
/// Draws a vertical line
|
||||
pub extern fn vline(x: i32, y: i32, len: u32) void;
|
||||
|
||||
/// Draws a horizontal line
|
||||
pub extern fn hline(x: i32, y: i32, len: u32) void;
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Sound Functions │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
/// Plays a sound tone.
|
||||
pub extern fn tone(frequency: u32, duration: u32, volume: u32, flags: u32) void;
|
||||
|
||||
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 │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
/// Reads up to `size` bytes from persistent storage into the pointer `dest`.
|
||||
pub extern fn diskr(dest: [*]u8, size: u32) u32;
|
||||
|
||||
/// Writes up to `size` bytes from the pointer `src` into persistent storage.
|
||||
pub extern fn diskw(src: [*]const u8, size: u32) u32;
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Other Functions │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
/// Prints a message to the debug console.
|
||||
pub fn trace(x: []const u8) void {
|
||||
traceUtf8(x.ptr, x.len);
|
||||
}
|
||||
extern fn traceUtf8(strPtr: [*]const u8, strLen: usize) void;
|
||||
|
||||
/// Use with caution, as there's no compile-time type checking.
|
||||
///
|
||||
/// * %c, %d, and %x expect 32-bit integers.
|
||||
/// * %f expects 64-bit floats.
|
||||
/// * %s expects a *zero-terminated* string pointer.
|
||||
///
|
||||
/// See https://github.com/aduros/wasm4/issues/244 for discussion and type-safe
|
||||
/// alternatives.
|
||||
pub extern fn tracef(x: [*:0]const u8, ...) void;
|
Loading…
Add table
Reference in a new issue