Files
picobook/src/pico8-client/renderCart.ts

126 lines
3.7 KiB
TypeScript
Raw Normal View History

2024-03-29 02:02:40 -07:00
import { renderCart as rawRenderCart } from "./rawRenderCart";
type PicoCart = {
name: string;
data: string; // TODO: ideally, accept png data url as well (or even just actual url?)
}
type PlayerButtons = {
left: boolean;
right: boolean;
up: boolean;
down: boolean;
o: boolean;
x: boolean;
menu: boolean;
}
type PicoPlayerHandle = {
// external things
readonly canvas: HTMLCanvasElement;
// i/o
setButtons: (buttons: PlayerButtons[]) => void;
setMouse: (mouse: {
x: number;
y: number;
leftClick: boolean;
rightClick: boolean;
}) => void;
setGamepadCount: (count: number) => void;
readonly gpio: number[]; // read + write (should be 256-tuple)
// audio
setAudioContext: (audioContext: AudioContext) => void;
// state (all communicated out)
readonly state: {
readonly frameNumber: number;
readonly isPaused: boolean;
readonly hasFocus: boolean;
readonly requestPointerLock: boolean;
readonly requirePageNavigateConfirmation: boolean;
readonly showDpad: boolean;
readonly shutdownRequested: boolean;
readonly soundVolume: number;
};
// misc?
setTouchDetected: (touchDetected: boolean) => void;
dropCart: (cart: PicoCart) => void;
}
const imageToNumbers = async (url: string): Promise<number[]> => {
return [];
}
const bitfield = (...args: boolean[]): number => {
if (!args.length) {
return 0;
}
return (args[0]?1:0)+2*bitfield(...args.slice(1));
}
export const makePlayer = async (carts: PicoCart[]): Promise<PicoPlayerHandle> => {
const canvas = document.createElement("canvas");
canvas.style.imageRendering = "pixelated";
const Module = {canvas};
const handle = rawRenderCart(Module, carts.map(cart => cart.name), await Promise.all(carts.map(cart => imageToNumbers(cart.data))));
handle.pico8_state = {};
handle.pico8_buttons = [0,0,0,0,0,0,0,0];
handle.pico8_mouse = [0,0,0];
handle.pico8_gpio = [
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
];
handle.pico8_gamepads = {count: 0};
return {
canvas,
state: {
frameNumber: handle.pico8_state.frame_number!,
isPaused: !!handle.pico8_state.is_paused!,
hasFocus: !!handle.pico8_state.has_focus!,
requestPointerLock: !!handle.pico8_state.request_pointer_lock!,
requirePageNavigateConfirmation: !!handle.pico8_state.require_page_navigate_confirmation!,
showDpad: !!handle.pico8_state.show_dpad!,
shutdownRequested: !!handle.pico8_state.shutdown_requested!,
soundVolume: handle.pico8_state.sound_volume!,
},
gpio: handle.pico8_gpio,
setMouse({x, y, leftClick, rightClick}) {
handle.pico8_mouse = [x, y, bitfield(leftClick, rightClick)];
},
setButtons(buttons) {
// TODO: pad this properly here instead of casting
handle.pico8_buttons = buttons.map(({left, right, up, down, o, x, menu}) => bitfield(left, right, up, down, o, x, menu)) as any;
},
setGamepadCount(count) {
handle.pico8_gamepads = {count};
},
setTouchDetected(touchDetected) {
handle.p8_touch_detected = touchDetected ? 1 : 0;
},
dropCart(cart) {
handle.p8_dropped_cart_name = cart.name;
// TODO: make sure this is a dataURL first, and if not, load it and then pass it in
handle.p8_dropped_cart = cart.data;
},
setAudioContext(audioContext) {
handle.pico8_audio_context = audioContext;
}
}
}