Reorganize

This commit is contained in:
dylan
2023-05-18 19:45:49 -07:00
parent 9b48560cce
commit f652e8d20f
22 changed files with 77 additions and 81 deletions

28
io/cart.ts Normal file
View File

@ -0,0 +1,28 @@
import { path } from "../deps.ts";
import initialCart from "../data/initialCart.json" assert { type: "json" };
import { Sheet } from "./sheet.ts";
const extension = ".faux";
let staticCart = initialCart as Array<Sheet>;
let cart: Array<Sheet> = JSON.parse(JSON.stringify(staticCart));
const virtualPathToRealPath = (virtualFname: string) => {
const vfname = virtualFname + extension;
const realPath = path.join(".", "carts", ...vfname.split("/"));
return realPath;
}
export const saveCart = async (fname: string) => {
await Deno.writeTextFile(virtualPathToRealPath(fname), JSON.stringify(getCart()));
}
export const loadCart = async (fname: string) => {
const json = await Deno.readTextFile(virtualPathToRealPath(fname));
staticCart = JSON.parse(json);
cart = JSON.parse(json);
}
export const getCart = () => {
return cart;
}

174
io/keyboard.ts Normal file
View File

@ -0,0 +1,174 @@
import { font, CHAR } from "../data/font.ts";
const keyboard = new Map<number, {first: boolean, repeat: boolean, held: boolean}>();
export const K = {
ESCAPE: 256,
ENTER: 257,
TAB: 258,
BACKSPACE: 259,
INSERT: 260,
DELETE: 261,
ARROW_RIGHT: 262,
ARROW_LEFT: 263,
ARROW_DOWN: 264,
ARROW_UP: 265,
PAGE_UP: 266,
PAGE_DOWN: 267,
HOME: 268,
END: 269,
CAPS_LOCK: 280,
F1: 290,
F2: 291,
F3: 292,
F4: 293,
F5: 294,
F6: 295,
F7: 296,
F8: 297,
F9: 298,
F10: 299,
F11: 300,
F12: 301,
SHIFT_LEFT: 340,
CTRL_LEFT: 341,
ALT_LEFT: 342,
SHIFT_RIGHT: 344,
CTRL_RIGHT: 345,
ALT_RIGHT: 346,
}
export const shiftMap = {
"1": "!",
"2": "@",
"3": "#",
"4": "$",
"5": "%",
"6": "^",
"7": "&",
"8": "*",
"9": "(",
"0": ")",
"`": "~",
"-": "_",
"=": "+",
"[": "{",
"]": "}",
"\\": "|",
";": ":",
"'": '"',
",": "<",
".": ">",
"/": "?",
}
export const altMap = {
"w": CHAR.UP,
"a": CHAR.LEFT,
"s": CHAR.DOWN,
"d": CHAR.RIGHT,
"p": CHAR.PI,
}
addEventListener("keydown", (evt) => {
// console.log("keydown", evt.key, evt.key.charCodeAt(0));
const key = evt.key.charCodeAt(0);
const isRepeat = keyboard.has(key) && keyboard.get(key)?.held!;
keyboard.set(key, {
first: !isRepeat,
repeat: isRepeat,
held: true,
});
});
addEventListener("keyup", (evt) => {
// console.log("keyup", evt.key, evt.key.charCodeAt(0));
const key = evt.key.charCodeAt(0);
keyboard.set(key, {
first: false,
repeat: false,
held: false,
});
});
export const refreshKeyboard = () => {
keyboard.forEach(({held}, key) => {
if (!held) {
keyboard.delete(key);
} else {
keyboard.set(key, {
first: false,
repeat: false,
held: true,
});
}
})
}
export const keyPressed = (key: string | number) => {
if (typeof key === "string") {
key = key.toUpperCase().charCodeAt(0);
}
return keyboard.has(key) && (keyboard.get(key)?.first! || keyboard.get(key)?.repeat!);
}
export const keyDown = (key: string | number) => {
if (typeof key === "string") {
key = key.toUpperCase().charCodeAt(0);
}
return keyboard.has(key) && keyboard.get(key)?.held!;
}
export const shiftKeyDown = () => {
return keyDown(K.SHIFT_LEFT) || keyDown(K.SHIFT_RIGHT);
}
export const ctrlKeyDown = () => {
return keyDown(K.CTRL_LEFT) || keyDown(K.CTRL_RIGHT);
}
export const altKeyDown = () => {
return keyDown(K.ALT_LEFT) || keyDown(K.ALT_RIGHT);
}
export const keyReleased = (key: string | number) => {
if (typeof key === "string") {
key = key.toUpperCase().charCodeAt(0);
}
return keyboard.has(key) && !keyboard.get(key)?.held!;
}
export const getKeysPressed = () => {
const result = [...keyboard.entries()].filter(([_key, value]) => {
return value.first || value.repeat;
}).map(([key]) => key);
return result;
}
export const getKeyboardString = () => {
let str = "";
if (ctrlKeyDown()) {
return str;
}
for (const key of getKeysPressed()) {
let char = String.fromCharCode(key).toLowerCase();
if (shiftKeyDown()) {
if (char in shiftMap) {
char = shiftMap[char as keyof typeof shiftMap];
} else {
char = char.toUpperCase();
}
}
if (altKeyDown()) {
if (char in altMap) {
char = altMap[char as keyof typeof altMap];
} else {
char = char.toUpperCase();
}
}
if (char in font.chars) {
str += char;
}
}
return str;
}

121
io/mouse.ts Normal file
View File

@ -0,0 +1,121 @@
import { WindowMouseEvent } from "https://deno.land/x/dwm@0.3.3/mod.ts";
import { gameWindow } from "./window.ts";
export const M = {
NONE: -1,
LEFT: 0,
RIGHT: 1,
MIDDLE: 2,
}
const mouseButtonsDown = {
[M.LEFT]: false,
[M.RIGHT]: false,
[M.MIDDLE]: false,
};
type MouseEventType = "click" | "down" | "up" | "move" | "dblclick";
const mouseEvents: Array<{
type: MouseEventType,
button: typeof M[keyof typeof M],
x: number,
y: number,
prevX?: number,
prevY?: number
}> = [];
let mouseX = 0;
let mouseY = 0;
const eventPixelCoords = (evt: WindowMouseEvent) => {
const {width, height} = gameWindow.size;
const min = Math.min(width, height);
const pixX = Math.floor(128*(evt.clientX-(width-min)/2)/min);
const pixY = Math.floor(128*(evt.clientY-(height-min)/2)/min);
if (pixX < 0 || pixX > 128 || pixY < 0 || pixY > 128) {
return null;
}
return {
x: pixX,
y: pixY,
}
}
const pushEvent = (type: MouseEventType, evt: WindowMouseEvent, extra?: Partial<typeof mouseEvents[0]>) => {
const coords = eventPixelCoords(evt);
if (!coords) {
return
}
mouseEvents.push({type, button: evt.button, ...coords, ...(extra ?? {})});
}
const evtInBounds = (evt: WindowMouseEvent) => {
return !!eventPixelCoords(evt);
}
addEventListener("dblclick", (evt) => {
pushEvent("dblclick", evt);
});
addEventListener("click", (evt) => {
pushEvent("click", evt);
});
addEventListener("mousedown", (evt) => {
if (evtInBounds(evt)) {
mouseButtonsDown[evt.button] = true;
}
pushEvent("down", evt);
});
addEventListener("mouseup", (evt) => {
if (evtInBounds(evt)) {
mouseButtonsDown[evt.button] = false;
}
pushEvent("up", evt);
});
addEventListener("mousemove", (evt) => {
const coords = eventPixelCoords(evt);
pushEvent("up", evt, {prevX: mouseX, prevY: mouseY});
if (coords) {
mouseX = coords.x;
mouseY = coords.y;
}
});
export const mousePos = () => {
return {
x: mouseX,
y: mouseY,
}
}
export const getMouseX = () => {
return mouseX;
}
export const getMouseY = () => {
return mouseY;
}
export const refreshMouse = () => {
mouseEvents.length = 0;
}
export const mouseDown = (button: number = M.LEFT) => {
return mouseEvents.some(ev => ev.button === button && ev.type === "down");
}
export const mouseClick = (button: number = M.LEFT) => {
return mouseEvents.some(ev => ev.button === button && ev.type === "click");
}
export const mouseDoubleClick = (button: number = M.LEFT) => {
return mouseEvents.some(ev => ev.button === button && ev.type === "dblclick");
}
export const mouseHeld = (button: number = M.LEFT) => {
return mouseButtonsDown[button];
}

56
io/sheet.ts Normal file
View File

@ -0,0 +1,56 @@
import { getCart } from "./cart.ts";
import { LinearGrid } from "../util/util.ts";
// import { runCode, addToContext } from "./runcode.ts";
// "code" | "spritesheet" | "map" | "sfx" | "patterns" | "fonts"
export type Sheet = {
sheet_type: "code",
value: string,
} | {
sheet_type: "spritesheet",
value: Array<Array<number>>,
} | {
sheet_type: "map",
value: Array<[number, number]>,
} | {
sheet_type: "none",
value: null,
}
export type SheetType = Sheet["sheet_type"];
export const getSheet = (n: number): Sheet => {
return getCart()[n];
}
// deno-lint-ignore no-explicit-any
export const setSheet = (n: number, type: SheetType, value: any) => {
return getCart()[n] = {sheet_type: type, value};
}
export const getCodeSheet = (sheet: number) => {
const {sheet_type, value} = getSheet(sheet);
if (sheet_type !== "code") {
throw "Trying to use a non-code sheet as code."
}
return value;
}
export const getSpriteSheet = (sheet: number) => {
const {sheet_type, value} = getSheet(sheet);
if (sheet_type !== "spritesheet") {
throw Error("Trying to use a non-sprite sheet as a spritesheet.");
}
while (value.length < 128) {
value.push(Array(64).fill(0));
}
return value;
}
export const getMapSheet = (sheet: number) => {
const {sheet_type, value} = getSheet(sheet);
if (sheet_type !== "map") {
throw "Trying to use a non-map sheet as a map."
}
sheet_type
return LinearGrid(value, 64);
}

256
io/window.ts Normal file
View File

@ -0,0 +1,256 @@
import {
createWindow,
getProcAddress,
gl,
} from "../deps.ts";
export {mainloop} from "../deps.ts";
import { COLOR, palette } from "../data/colors.ts";
export const gameWindow = createWindow({
title: "Faux",
width: 512,
height: 512,
resizable: true,
glVersion: [3, 2],
gles: true,
});
gl.load(getProcAddress);
function loadShader(type: number, src: string) {
const shader = gl.CreateShader(type);
gl.ShaderSource(
shader,
1,
new Uint8Array(
new BigUint64Array([
BigInt(
Deno.UnsafePointer.value(
Deno.UnsafePointer.of(new TextEncoder().encode(src)),
),
),
]).buffer,
),
new Int32Array([src.length]),
);
gl.CompileShader(shader);
const status = new Int32Array(1);
gl.GetShaderiv(shader, gl.COMPILE_STATUS, status);
if (status[0] === gl.FALSE) {
const logLength = new Int32Array(1);
gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, logLength);
const log = new Uint8Array(logLength[0]);
gl.GetShaderInfoLog(shader, logLength[0], logLength, log);
console.log(new TextDecoder().decode(log));
gl.DeleteShader(shader);
return 0;
}
return shader;
}
const vShaderSrc = `
attribute vec4 vPosition;
attribute vec4 vCol;
varying vec4 color;
void main() {
gl_Position = vPosition;
color = vCol;
}
`;
const fShaderSrc = `
precision mediump float;
varying vec4 color;
void main() {
gl_FragColor = color;
}
`;
const vShader = loadShader(gl.VERTEX_SHADER, vShaderSrc);
const fShader = loadShader(gl.FRAGMENT_SHADER, fShaderSrc);
const program = gl.CreateProgram();
gl.AttachShader(program, vShader);
gl.AttachShader(program, fShader);
gl.BindAttribLocation(program, 0, new TextEncoder().encode("vPosition\0"));
gl.BindAttribLocation(program, 1, new TextEncoder().encode("vCol\0"));
gl.LinkProgram(program);
const status = new Int32Array(1);
gl.GetProgramiv(program, gl.LINK_STATUS, status);
if (status[0] === gl.FALSE) {
const logLength = new Int32Array(1);
gl.GetProgramiv(program, gl.INFO_LOG_LENGTH, logLength);
const log = new Uint8Array(logLength[0]);
gl.GetProgramInfoLog(program, logLength[0], logLength, log);
console.log(new TextDecoder().decode(log));
gl.DeleteProgram(program);
Deno.exit(1);
}
gl.ClearColor(0.0, 0.0, 0.0, 1.0);
const pixelsPerRow = 128;
const top = 1;
const left = -1;
const cell = 2/pixelsPerRow;
const getHalfAsInt = (n: number) => Number(parseInt(Math.floor(n/2).toString()));
addEventListener("resize", (event) => {
const {width, height} = event;
const min = Math.min(width, height);
gl.Viewport(getHalfAsInt(width-min), getHalfAsInt(height-min), min, min);
});
const px = (x: number, y: number) => {
// deno-fmt-ignore
return [
left + x*cell, top - y*cell, 0,
left + (x+1)*cell, top - y*cell, 0,
left + x*cell, top - (y+1)*cell, 0,
left + (x+1)*cell, top - y*cell, 0,
left + x*cell, top - (y+1)*cell, 0,
left + (x+1)*cell, top - (y+1)*cell, 0,
];
}
const paletteX6 = palette.map(rgba => [...rgba, ...rgba, ...rgba, ...rgba, ...rgba, ...rgba]);
const c = (n: number) => {
return paletteX6[n];
}
const allPixelTriangles = new Float32Array(
Array(pixelsPerRow*pixelsPerRow).fill(null).flatMap((_, i) => px(i%pixelsPerRow,Math.floor(i/pixelsPerRow)))
)
const allPixelColors = new Float32Array(
Array(pixelsPerRow*pixelsPerRow).fill(null).flatMap(() => c(1))
)
export const cameraPos = {
x: 0,
y: 0,
}
export const setPixelColorRaw = (x: number, y: number, color: number) => {
if (x < 0 || y < 0 || x > 127 || y > 127) {
return;
}
if (color !== 0) {
const col = c(color);
allPixelColors.set(col, 4*6*(128*y+x));
}
}
export const setPixelColor = (x: number, y: number, color: number) => {
return setPixelColorRaw(Math.floor(x - cameraPos.x), Math.floor(y - cameraPos.y), color);
}
export const setPixelsInRect = (x: number, y: number, w: number, pixels: Array<number>) => {
for (let i = 0; i < pixels.length; i++) {
setPixelColor(x+i%w, y+Math.floor(i/w), pixels[i]);
}
}
export const setPixelsInRectRaw = (x: number, y: number, w: number, pixels: Array<number>) => {
for (let i = 0; i < pixels.length; i++) {
setPixelColorRaw(x+i%w, y+Math.floor(i/w), pixels[i]);
}
}
export const fillRect = (x: number, y: number, w: number, h: number, color: number) => {
setPixelsInRect(x, y, w, Array(w*h).fill(color));
}
export const fillCircle = (x: number, y: number, r: number, color: number) => {
const left = Math.floor(x-r-1);
const top = Math.floor(y-r-1);
for (let i = left; i <= Math.ceil(x+r+1); i ++) {
for (let j = top; j <= Math.ceil(y+r+1); j ++) {
if (Math.sqrt((x-i)**2 + (y-j)**2) <= r+0.5) {
setPixelColor(i, j, color);
}
}
}
}
export const outlineCircle = (x: number, y: number, r: number, color: number) => {
const left = Math.floor(x-r-1);
const top = Math.floor(y-r-1);
const inR = (d: number) => d <= r+0.5 && d > r-0.5;
for (let i = left; i <= Math.ceil(x+r+1); i ++) {
for (let j = top; j <= Math.ceil(y+r+1); j ++) {
const d = Math.sqrt((x-i)**2 + (y-j)**2);
if (inR(d)) {
const dh = Math.sqrt((x-(i+Math.sign(i-x)))**2 + (y-j)**2);
const dv = Math.sqrt((x-i)**2 + (y-(j+Math.sign(j-y)))**2);
const h = Math.abs(x-i) > Math.abs(y-j);
if (!inR(h ? dh : dv)) {
setPixelColor(i, j, color);
}
}
}
}
}
export const fillEllipse = (x0: number, y0: number, x1: number, y1: number, color: number) => {
const x = 0.5*(x0 + x1);
const y = 0.5*(y0 + y1);
const rx = Math.abs(x0-x1)/2;
const ry = Math.abs(y0-y1)/2;
const left = Math.floor(x-rx-1);
const top = Math.floor(y-ry-1);
for (let i = left; i <= Math.ceil(x+rx+1); i ++) {
for (let j = top; j <= Math.ceil(y+ry+1); j ++) {
if (Math.sqrt(((x-i)/rx)**2 + ((y-j)/ry)**2) <= 1+(1/(rx+ry))) {
setPixelColor(i, j, color);
}
}
}
}
export const outlineEllipse = (x0: number, y0: number, x1: number, y1: number, color: number) => {
const x = 0.5*(x0 + x1);
const y = 0.5*(y0 + y1);
const rx = Math.abs(x0-x1)/2;
const ry = Math.abs(y0-y1)/2;
const left = Math.floor(x-rx-1);
const top = Math.floor(y-ry-1);
const inR = (d: number) => d <= 1+1/(rx+ry);
for (let i = left; i <= Math.ceil(x+rx+1); i ++) {
for (let j = top; j <= Math.ceil(y+ry+1); j ++) {
const d = Math.sqrt(((x-i)/rx)**2 + ((y-j)/ry)**2);
if (inR(d)) {
const dh = Math.sqrt(((x-(i+Math.sign(i-x)))/rx)**2 + ((y-j)/ry)**2);
const dv = Math.sqrt(((x-i)/rx)**2 + ((y-(j+Math.sign(j-y)))/ry)**2);
if (!inR(dh) || !inR(dv)) {
setPixelColor(i, j, color);
}
}
}
}
}
export const fillRectRaw = (x: number, y: number, w: number, h: number, color: number) => {
setPixelsInRectRaw(x, y, w, Array(w*h).fill(color));
}
export const clearScreen = (color?: number) => {
fillRectRaw(0, 0, 128, 128, color ?? COLOR.BLACK);
}
export const frame = () => {
gl.Clear(gl.COLOR_BUFFER_BIT);
gl.UseProgram(program);
gl.VertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 0, allPixelTriangles);
gl.VertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, 0, allPixelColors);
gl.EnableVertexAttribArray(0);
gl.EnableVertexAttribArray(1);
gl.DrawArrays(gl.TRIANGLES, 0, 6*pixelsPerRow*pixelsPerRow);
gameWindow.swapBuffers();
}